<> <> <> <> DIRECTORY BootFile USING[Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, TrailerStart ], Booting USING[ Boot, CheckpointProc, inloadedOnce, RegisterProcs, RollbackProc, switches ], DebuggerFormat USING[ LabelChecksum ], Disk USING[ Channel, DoIO, DriveAttributes, Label, NextChannel, ok, Request, Status ], DiskFace USING[ Type ], File USING[Create, Error, FP, Handle, Info, Open, PageCount, PageNumber, Read, SetSize, SystemVolume, Volume, VolumeFile, wordsPerPage, Write ], FileBackdoor USING[GetRoot, IsDebugger, SetRoot], PrincOps USING[ maxPagesInVM, PageValue, wordsPerPage ], VM USING[PageNumberForAddress, Allocate, Free, Interval, MakeUnchanged, PageNumber, AddressForPageNumber, Pin, State, Unpin, PagesForWords ], VMInternal USING[ IsVacant ], VMSideDoor USING[ rmPages ], WorldVM USING[ AddressFault, BadWorld ], WVMPrivate; WVMOutLd: MONITOR IMPORTS BootFile, Booting, DebuggerFormat, Disk, File, FileBackdoor, VM, VMInternal, VMSideDoor, WorldVM, WVMPrivate EXPORTS WVMPrivate = { EntrySeq: TYPE = LONG POINTER TO EntrySeqRep; EntrySeqRep: TYPE = RECORD [SEQUENCE COMPUTED CARDINAL OF BootFile.Entry]; -- Management of outload files -- debugger: File.Handle; debuggee: File.Handle; pagesInMap: INT = PrincOps.maxPagesInVM / (PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]); SetupFiles: ENTRY PROC RETURNS[ok: BOOL] = { ENABLE UNWIND => NULL; sysVol: File.Volume = File.SystemVolume[]; size: File.PageCount = BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+pagesInMap+2; changed: BOOLEAN _ FALSE; IF NOT FileBackdoor.IsDebugger[sysVol] THEN RETURN[FALSE]; ok _ TRUE; debugger _ VerifyFile[sysVol, debugger, size]; debuggee _ VerifyFile[sysVol, debuggee, size]; }; VerifyFile: INTERNAL PROC[sysVol: File.Volume, which: File.VolumeFile, size: File.PageCount] RETURNS[ new: File.Handle ] = { changed: BOOL _ FALSE; oldFP: File.FP = FileBackdoor.GetRoot[sysVol, which].fp; new _ File.Open[sysVol, oldFP ! File.Error => { new _ NIL; CONTINUE} ]; IF new = NIL THEN { changed _ TRUE; new _ File.Create[sysVol, size] }; IF File.Info[new].size < size THEN { changed _ TRUE; File.SetSize[new, size] }; IF changed THEN FileBackdoor.SetRoot[which, new]; }; vmPagesPerFilePage: INT = VM.PagesForWords[File.wordsPerPage]; outLdSpace: VM.Interval = VM.Allocate[vmPagesPerFilePage]; outLdPage: File.PageNumber _ [LAST[INT]]; outLdAddress: LONG POINTER = VM.AddressForPageNumber[outLdSpace.page]; ReadOutLdPage: INTERNAL PROC[page: File.PageNumber] = { IF outLdPage # page THEN File.Read[debuggee, outLdPage_page, 1, outLdAddress] }; WriteOutLdPage: INTERNAL PROC = { File.Write[debuggee, outLdPage, 1, outLdAddress] }; InvalidateOutLdPage: INTERNAL PROC = { outLdPage _ [LAST[INT]] }; -- Layout of boot and outload files -- BFMEntryIndex: TYPE = [0..MAX[BootFile.maxEntriesPerHeader, BootFile.maxEntriesPerTrailer]]; BootFileMapItem: TYPE = RECORD [ link: BFMPointer _ NIL, lastMemPage: WVMPrivate.PageNumber, filePageOffset: File.PageNumber, entryCount: BFMEntryIndex, entries: EntrySeq]; BFMPointer: TYPE = REF BootFileMapItem; bfmHead: BFMPointer _ NIL; OpenOutLdFile: INTERNAL PROC = { bfm: BFMPointer; bfh: LONG POINTER TO BootFile.Header; entries: EntrySeq; curmax: CARDINAL _ BootFile.maxEntriesPerHeader; curbase: File.PageNumber _ [0]; OutLdPageSpace: PROC RETURNS[vm: LONG POINTER] = { <> space: VM.Interval = VM.Allocate[vmPagesPerFilePage]; vm _ VM.AddressForPageNumber[space.page]; File.Read[debuggee, curbase, 1, vm]; VM.MakeUnchanged[space]; }; pagesremaining: CARDINAL; bfmHead _ NIL; bfh _ OutLdPageSpace[]; entries _ LOOPHOLE[@bfh.entries]; pagesremaining _ bfh.countData; bfmHead _ bfm _ NEW[BootFileMapItem]; DO bfm.entries _ entries; bfm.entryCount _ MIN[curmax, pagesremaining]; bfm.lastMemPage _ bfm.entries[bfm.entryCount-1].page; bfm.filePageOffset _ [curbase+1]; pagesremaining _ pagesremaining - bfm.entryCount; IF pagesremaining = 0 THEN EXIT; curbase _ [bfm.filePageOffset+bfm.entryCount]; curmax _ BootFile.maxEntriesPerTrailer; entries _ OutLdPageSpace[] + BootFile.TrailerStart; bfm.link _ NEW[BootFileMapItem]; bfm _ bfm.link; ENDLOOP; }; CloseOutLdFile: INTERNAL PROC = { FOR bfmPtr: BFMPointer _ bfmHead, bfmPtr.link UNTIL bfmPtr = NIL DO space: VM.Interval = [page: VM.PageNumberForAddress[bfmPtr.entries], count: vmPagesPerFilePage]; changed: BOOL _ FALSE; FOR i: NAT IN NAT[0..space.count) DO IF VM.State[space.page+i].dataState = changed THEN changed _ TRUE ENDLOOP; IF changed THEN File.Write[debuggee, [bfmPtr.filePageOffset-1], 1, VM.AddressForPageNumber[space.page]]; VM.Free[space]; ENDLOOP; bfmHead _ NIL; }; SearchOutLdFile: INTERNAL PROCEDURE [mempage: WVMPrivate.PageNumber] RETURNS[ entry: LONG POINTER TO BootFile.Entry ] = { <> FOR bfm: BFMPointer _ bfmHead, bfm.link UNTIL bfm = NIL DO IF mempage <= bfm.lastMemPage THEN { -- it's in this page of the bootFileMap -- FOR i: BFMEntryIndex IN [0..bfm.entryCount] DO SELECT LONG[bfm.entries[i].page] FROM < mempage => NULL; > mempage => RETURN[NIL]; ENDCASE => { ReadOutLdPage[[bfm.filePageOffset+i]]; RETURN[ @bfm.entries[i] ]; } REPEAT FINISHED => ERROR ENDLOOP; }; REPEAT FINISHED => RETURN[NIL] ENDLOOP; }; -- Transfers to/from outload file -- ReadOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber] RETURNS[ok: BOOLEAN] = { ENABLE UNWIND => NULL; entry: LONG POINTER TO BootFile.Entry = SearchOutLdFile[mempage]; IF entry = NIL THEN IF mempage IN [376B..377B] THEN -- kludge: Germ doesn't write io pages to outload file, so read ours instead -- { IF VMInternal.IsVacant[mempage] THEN ERROR WorldVM.AddressFault[mempage]; data^ _ LOOPHOLE[WVMPrivate.PageAddress[mempage], LONG POINTER TO WVMPrivate.PageData]^; ok _ TRUE; } ELSE RETURN[FALSE] ELSE { data^ _ LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^; ok _ TRUE; }; }; WriteOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber] RETURNS[readOnly: BOOL] = { ENABLE UNWIND => NULL; entry: LONG POINTER TO BootFile.Entry = SearchOutLdFile[mempage]; IF entry = NIL THEN ERROR WorldVM.AddressFault[mempage]; LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^ _ data^; WriteOutLdPage[]; readOnly _ entry.value.state.flags.readonly; IF NOT readOnly THEN entry.value.state.flags.dirty _ TRUE; }; -- Transfers to/from backing store -- bufferSpace: VM.Interval = VM.Allocate[1]; bufferData: LONG POINTER TO WVMPrivate.PageData = VM.AddressForPageNumber[bufferSpace.page]; UnknownDrive: ERROR = CODE; BadTransfer: ERROR = CODE; MoveLocalDiskPage: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, direction: WVMPrivate.ChannelDirection, addr: WVMPrivate.DiskAddress] = { ENABLE UNWIND => NULL; req: Disk.Request; label: Disk.Label; FOR channel: Disk.Channel _ Disk.NextChannel[NIL], Disk.NextChannel[channel] UNTIL channel = NIL DO type: DiskFace.Type; ordinal: INT; status: Disk.Status; countDone: INT; [type: type, ordinal: ordinal] _ Disk.DriveAttributes[channel]; IF type # addr.deviceType OR ordinal # addr.deviceOrdinal THEN LOOP; req _ [ diskPage: [addr.diskPage + addr.offset], data: bufferData, command: [header: verify, label: read, data: read], count: 1 ]; VM.Pin[bufferSpace]; [status, countDone] _ Disk.DoIO[channel, @label, @req]; VM.Unpin[bufferSpace]; IF status # Disk.ok OR DebuggerFormat.LabelChecksum[label, addr.offset] # addr.labelCheck THEN ERROR BadTransfer[]; IF direction = read THEN { data^ _ bufferData^; EXIT }; IF direction # write THEN ERROR; bufferData^ _ data^; req _ [ diskPage: [addr.diskPage + addr.offset], data: bufferData, command: [header: verify, label: verify, data: write], count: 1 ]; VM.Pin[bufferSpace]; [status, countDone] _ Disk.DoIO[channel, @label, @req]; -- write the data VM.Unpin[bufferSpace]; IF status # Disk.ok THEN ERROR BadTransfer[]; EXIT REPEAT FINISHED => ERROR UnknownDrive[]; ENDLOOP; }; -- Control transfers -- <> <> <> -- switch indicating that the client outload file should be believed. -- CantGetHere: ERROR = CODE; CantFindInloadFlag: ERROR = CODE; MyCheckpoint: ENTRY Booting.CheckpointProc = TRUSTED { ENABLE UNWIND => NULL; CloseOutLdFile[]; InvalidateOutLdPage[]; }; MyRollback: ENTRY Booting.RollbackProc = TRUSTED { ENABLE UNWIND => NULL; { -- patch the inloadedOnce boolean in the outload file! mempage: VM.PageNumber = VM.PageNumberForAddress[@Booting.inloadedOnce]; bfh: LONG POINTER TO BootFile.Header; entries: EntrySeq _ NIL; curmax: CARDINAL _ BootFile.maxEntriesPerHeader; curbase: File.PageNumber _ [0]; pagesremaining: CARDINAL; File.Read[debugger, curbase, 1, outLdAddress]; bfh _ outLdAddress; entries _ LOOPHOLE[@bfh.entries]; pagesremaining _ bfh.countData; DO filePageOffset: File.PageNumber = [curbase+1]; entryCount: CARDINAL = MIN[curmax, pagesremaining]; IF mempage <= entries[entryCount-1].page THEN -- it's in this page of the bootFileMap -- { FOR i: BFMEntryIndex IN [0..entryCount] DO SELECT LONG[entries[i].page] FROM < mempage => NULL; > mempage => ERROR CantFindInloadFlag[]; ENDCASE => { diskInLoaded: LONG POINTER TO BOOL; File.Read[debugger, [filePageOffset+i], 1, outLdAddress]; diskInLoaded _ LOOPHOLE[outLdAddress + (LONG[@Booting.inloadedOnce]-VM.AddressForPageNumber[mempage])]; diskInLoaded^ _ TRUE; File.Write[debugger, [filePageOffset+i], 1, outLdAddress]; GOTO done; } REPEAT FINISHED => ERROR ENDLOOP; }; pagesremaining _ pagesremaining - entryCount; IF pagesremaining = 0 THEN ERROR CantFindInloadFlag[]; curbase _ [filePageOffset+entryCount]; curmax _ BootFile.maxEntriesPerTrailer; File.Read[debugger, curbase, 1, outLdAddress]; entries _ outLdAddress + BootFile.TrailerStart; ENDLOOP; EXITS done => InvalidateOutLdPage[]; }; OpenOutLdFile[]; }; LocateOther: PUBLIC PROC = { ENABLE UNWIND => NULL; -- This is called successfully only once per run -- IF NOT SetupFiles[] THEN RETURN WITH ERROR WorldVM.BadWorld[]; Booting.RegisterProcs[c: MyCheckpoint, r: MyRollback]; [] _ Booting.Boot[ boot: IF Booting.switches[w] THEN [noOp[]] ELSE [physical[]], switches: ALL[FALSE] ] }; GoOther: PUBLIC PROC = <> { [] _ Booting.Boot[ boot: [logical[root: debuggee]], switches: ALL[FALSE] ] }; }.