DIRECTORY BasicTime USING [ FromPupTime, Now, ToPupTime ], BootFile USING [Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, TrailerArray, TrailerStart ], Booting USING [ Boot, Switches, switches ], DebuggerFormat USING [ExternalStateVector, LabelChecksum, Run, SwapInfo, VersionID, VMRunTable ], Disk USING [ Channel, DoIO, DriveAttributes, Label, NextChannel, ok, Request, Status ], DiskFace USING [ Type ], File USING [Error, FindVM, FP, Handle, Info, Open, PageCount, PageNumber, Read, SetSize, SystemVolume, wordsPerPage, Write ], FileBackdoor USING [GetRoot], IO USING [ GetChar, Flush, PutChar, PutF, PutRope, STREAM ], PrincOps USING [ flagsVacant, PageCount, PageNumber, PageValue, PDA, wordsPerPage ], PrincOpsUtils USING [ LongCopy ], ProcessorFace USING [ GetNextAvailableVM ], Rope USING [ ROPE ], SimpleTerminal USING [ TurnOn ], SystemVersion USING [ bootFileDate, Date, release, ReleaseNumber ], VM USING [AddressForPageNumber, CantAllocate, Interval, PageCount, PageNumber, PagesForWords, Pin, SimpleAllocate], VMBacking USING [StateFromPageValue]; RollbackImpl: MONITOR IMPORTS BasicTime, BootFile, Booting, DebuggerFormat, Disk, File, FileBackdoor, IO, PrincOpsUtils, ProcessorFace, SimpleTerminal, SystemVersion, VM, VMBacking = { vmPagesPerFilePage: VM.PageCount = VM.PagesForWords[File.wordsPerPage]; valuesPerPage: CARDINAL = PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]; PageValueArray: TYPE = ARRAY [0..valuesPerPage) OF PrincOps.PageValue; mapEntriesPerPage: CARDINAL = valuesPerPage; outLdFile: File.Handle _ NIL; outLdFirstPage: File.PageNumber; outLdSpace: VM.Interval = VM.SimpleAllocate[vmPagesPerFilePage]; outLdPage: File.PageNumber _ [LAST[INT]]; outLdAddress: LONG POINTER TO ARRAY [0..PrincOps.wordsPerPage) OF WORD = VM.AddressForPageNumber[outLdSpace.page]; ReadOutLdPage: PROC [page: File.PageNumber] = { IF outLdPage # page THEN File.Read[outLdFile, outLdPage_page, 1, outLdAddress]; }; BFMEntryIndex: TYPE = [0..MAX[BootFile.maxEntriesPerHeader, BootFile.maxEntriesPerTrailer]]; BootFileMapItem: TYPE = RECORD [ link: BFMPointer _ NIL, lastMemPage: PrincOps.PageNumber, filePageOffset: File.PageNumber, entryCount: BFMEntryIndex, entries: EntrySeq]; EntrySeq: TYPE = LONG POINTER TO BootFile.TrailerArray; BFMPointer: TYPE = REF BootFileMapItem; bfmHead: BFMPointer _ NIL; OpenOutLdFile: 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.SimpleAllocate[vmPagesPerFilePage]; vm _ VM.AddressForPageNumber[space.page]; File.Read[outLdFile, curbase, 1, vm]; }; 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; }; PageNotFound: ERROR[mempage: PrincOps.PageNumber] = CODE; MalformedOutload: ERROR = CODE; SearchOutLdFile: PROCEDURE [mempage: PrincOps.PageNumber] = { FOR bfm: BFMPointer _ bfmHead, bfm.link UNTIL bfm = NIL DO IF mempage <= PrincOps.PageNumber[bfm.lastMemPage] THEN { FOR i: BFMEntryIndex IN [0..bfm.entryCount] DO SELECT PrincOps.PageNumber[bfm.entries[i].page] FROM < mempage => NULL; > mempage => ERROR PageNotFound[mempage]; ENDCASE => { ReadOutLdPage[[bfm.filePageOffset+i]]; RETURN }; REPEAT FINISHED => ERROR MalformedOutload[]; ENDLOOP; }; REPEAT FINISHED => ERROR PageNotFound[mempage] ENDLOOP; }; CopyRead: PUBLIC PROC [from: INT, nwords: INT, to: LONG POINTER] = { n: PrincOps.PageNumber; w: [0..PrincOps.wordsPerPage); WHILE nwords > 0 DO amount: (0..PrincOps.wordsPerPage]; n _ from / PrincOps.wordsPerPage; w _ from - n * LONG[PrincOps.wordsPerPage]; SearchOutLdFile[n]; amount _ MIN[nwords,PrincOps.wordsPerPage-w]; PrincOpsUtils.LongCopy[from: @outLdAddress[w], to: to, nwords: amount]; nwords _ nwords - amount; from _ from + amount; to _ to + amount; ENDLOOP; }; UnknownDrive: ERROR = CODE; BadTransfer: ERROR = CODE; DiskAddress: TYPE = RECORD[ deviceType: DiskFace.Type, deviceOrdinal: CARDINAL, diskPage: LONG CARDINAL, -- device-relative page number of start of run offset: CARDINAL, -- page number from start of run, for label checksum calculation labelCheck: CARDINAL -- label checksum of start of run ]; lastChannel: Disk.Channel _ NIL; lastType: DiskFace.Type; lastOrdinal: CARDINAL; TransferBackingRun: PUBLIC ENTRY PROC [ data: LONG POINTER, label: LONG POINTER TO Disk.Label, which: { readAndChecksum, readAndVerify, write }, count: INT, addr: DiskAddress] = { ENABLE UNWIND => NULL; req: Disk.Request; status: Disk.Status; countDone: INT; IF lastChannel = NIL OR addr.deviceType # lastType OR addr.deviceOrdinal # lastOrdinal THEN { FOR lastChannel _ Disk.NextChannel[NIL], Disk.NextChannel[lastChannel] UNTIL lastChannel = NIL DO [type: lastType, ordinal: lastOrdinal] _ Disk.DriveAttributes[lastChannel]; IF addr.deviceType = lastType AND addr.deviceOrdinal = lastOrdinal THEN EXIT; REPEAT FINISHED => ERROR UnknownDrive[]; ENDLOOP; }; req _ [ diskPage: [addr.diskPage], data: data, command: SELECT which FROM readAndChecksum => [header: verify, label: read, data: read], readAndVerify => [header: verify, label: verify, data: read], write => [header: verify, label: verify, data: write], ENDCASE => ERROR, count: count ]; [status, countDone] _ Disk.DoIO[lastChannel, label, @req]; IF status # Disk.ok THEN ERROR BadTransfer[]; IF which = readAndChecksum AND DebuggerFormat.LabelChecksum[label^, addr.offset + count-1] # addr.labelCheck THEN ERROR BadTransfer[]; label.filePage _ label.filePage - (count-1); }; ReadBackingMap: PROC RETURNS [table: REF DebuggerFormat.VMRunTable] = { swapInfo: DebuggerFormat.SwapInfo; swapInfoAddr: INT = LOOPHOLE[@PrincOps.PDA.available]; esv: DebuggerFormat.ExternalStateVector; esvAddr: INT; CopyRead[from: swapInfoAddr, nwords: SIZE[DebuggerFormat.SwapInfo], to: @swapInfo]; esvAddr _ LOOPHOLE[swapInfo.externalStateVector]; CopyRead[esvAddr, SIZE[DebuggerFormat.ExternalStateVector], @esv]; SELECT esv.versionident FROM DebuggerFormat.VersionID => { IF esv.vmRunTable = NIL THEN table _ NIL ELSE { temp: ARRAY [0..SIZE[DebuggerFormat.VMRunTable[0]]) OF WORD; cheat: POINTER TO DebuggerFormat.VMRunTable = LOOPHOLE[@temp]; CopyRead[ LOOPHOLE[esv.vmRunTable, INT], SIZE[DebuggerFormat.VMRunTable[0]], @temp ]; table _ NEW[DebuggerFormat.VMRunTable[cheat.length]]; CopyRead[ LOOPHOLE[esv.vmRunTable, INT], SIZE[DebuggerFormat.VMRunTable[cheat.length]], LOOPHOLE[table, LONG POINTER] ]; }; }; ENDCASE => table _ NIL; }; in, out: IO.STREAM; vmImageVersion: CARDINAL = 1; VMImageHeader: TYPE = MACHINE DEPENDENT RECORD[ version(0): CARDINAL _ vmImageVersion, runs(1): INT, pages(3): INT, bootVersion(5): SystemVersion.ReleaseNumber, bootFileDate(8): SystemVersion.Date, checkpointDate(10): SystemVersion.Date ]; VMImageRun: TYPE = MACHINE DEPENDENT RECORD[ pages: CARDINAL, deviceType: DiskFace.Type, deviceOrdinal: CARDINAL, diskPage: LONG CARDINAL, label: Disk.Label]; vmImageRunsPerPage: CARDINAL = File.wordsPerPage / SIZE[VMImageRun]; VMImageRunTable: TYPE = ARRAY [0..vmImageRunsPerPage) OF VMImageRun; vmImageBufferPages: NAT _ 256; NoBackingStore: ERROR[PrincOps.PageNumber] = CODE; ShortFile: ERROR = CODE; WrongPageCount: ERROR = CODE; which: { checkpoint, rollback }; Complain: PROC [msg: Rope.ROPE] = { out.PutRope[msg]; out.PutRope["\nType any character to "]; out.PutRope[IF which = checkpoint THEN "resume the outloaded session: " ELSE "re-boot with the \"f\" switch: "]; in.Flush[]; [] _ in.GetChar[]; IF which = checkpoint THEN [] _ Booting.Boot[boot: [file[outLdFile, outLdFirstPage]], switches: ALL[FALSE] ] ELSE [] _ Booting.Boot[boot: [self[]], switches: [f: TRUE] ]; ERROR }; DoIt: PROC = TRUSTED { ENABLE { File.Error => Complain[SELECT why FROM wentOffline => "System volume has gone offline", nonCedarVolume => "System volume is not a cedar volume", inconsistent => "System volume or checkpoint file is inconsistent - scavenge", software => "Label-check when accessing the checkpoint file", hardware => "Hard disk error when accessing the checkpoint file", unknownFile => "The checkpoint file does not exist", unknownPage => "Bogus checkpoint file (unknownPage)", volumeFull => "Can't checkpoint (system volume full)", fragmented => "Can't checkpoint (system volume fragmented)", ENDCASE => "Unexpected file error - consult expert"]; PageNotFound => Complain["Can't find required swapped-in page in outload"]; MalformedOutload => Complain["The outload appears to be malformed"]; UnknownDrive => Complain["Can't find the disk drive for a swapped-out page"]; BadTransfer => Complain["Disk error when accessing a swapped out page"]; NoBackingStore => Complain["Can't find the backing store for a swapped out page"]; ShortFile => Complain["The checkpoint file appears to be truncated"]; WrongPageCount => Complain["I'm confused about how many pages to transfer"]; }; outloadDataPages: PrincOps.PageCount; countVM: INT _ 0; mapPages: CARDINAL; mapBufferSpace: VM.Interval = VM.SimpleAllocate[VM.PagesForWords[File.wordsPerPage]]; mapBuffer: LONG POINTER TO PageValueArray = VM.AddressForPageNumber[mapBufferSpace.page]; mapStart: File.PageNumber; -- start of map dump in checkpoint file vmImagePos: File.PageNumber; -- start of vmImage in checkpoint file vmImageBufferSpace: VM.Interval = AllocateBuffer[]; vmImageBufferAddr: LONG POINTER = VM.AddressForPageNumber[vmImageBufferSpace.page]; vmImageHeader: LONG POINTER TO VMImageHeader = vmImageBufferAddr; vmImageRunTable: REF VMImageRunTable _ NEW[VMImageRunTable]; AllocateBuffer: PROC RETURNS [interval: VM.Interval] = { DO RETURN [VM.SimpleAllocate[vmImageBufferPages ! VM.CantAllocate => IF vmImageBufferPages > 4 THEN {vmImageBufferPages _ vmImageBufferPages / 2; LOOP} ELSE REJECT; ]]; ENDLOOP; }; GetMapStart: PROC RETURNS [File.PageNumber] = TRUSTED { fp: File.FP; header: LONG POINTER TO BootFile.Header = LOOPHOLE[mapBuffer]; [fp: fp, page: outLdFirstPage] _ FileBackdoor.GetRoot[File.SystemVolume[], checkpoint]; outLdFile _ File.Open[File.SystemVolume[], fp]; File.Read[file: outLdFile, from: outLdFirstPage, nPages: 1, to: header]; outloadDataPages _ header.countData; RETURN [[outLdFirstPage+BootFile.MemorySizeToFileSize[outloadDataPages]]] }; [in: in, out: out] _ SimpleTerminal.TurnOn[]; VM.Pin[vmImageBufferSpace]; out.PutRope[IF which = checkpoint THEN "\nCheckpoint." ELSE "\nRollback."]; { count: PrincOps.PageCount; page: PrincOps.PageNumber; FOR countVM _ 0, page + count DO [firstPage: page, count: count] _ ProcessorFace.GetNextAvailableVM[countVM]; IF count = 0 THEN EXIT; ENDLOOP; }; mapPages _ (countVM + mapEntriesPerPage - 1) / mapEntriesPerPage; mapStart _ GetMapStart[]; vmImagePos _ [mapStart+mapPages]; IF which = checkpoint THEN { GetMapPage: PROC [p: CARDINAL] = TRUSTED { File.Read[file: outLdFile, from: [mapStart+p], nPages: 1, to: mapBuffer] }; PutMapPage: PROC [p: CARDINAL] = TRUSTED { File.Write[file: outLdFile, to: [mapStart+p], nPages: 1, from: mapBuffer] }; firstEnumerate: BOOL _ TRUE; InterestingPage: PROC [e: [0..mapEntriesPerPage)] RETURNS [yes: BOOL] = TRUSTED { mapEntry: PrincOps.PageValue = mapBuffer[e]; IF mapEntry.state.flags = PrincOps.flagsVacant THEN yes _ VMBacking.StateFromPageValue[mapEntry] = active ELSE { yes _ FALSE; IF firstEnumerate THEN { mapBuffer[e].state.flags.referenced _ TRUE; mapBuffer[e].state.flags.dirty _ TRUE; }; }; }; firstPassCount: INT _ 0; -- consistency check between passes of EnumerateRuns table: REF DebuggerFormat.VMRunTable; runSize: CARDINAL; -- pages in current run; runStart: VM.PageNumber; -- page number of first page in run backingRun: LONG POINTER TO DebuggerFormat.Run _ NIL; -- covers entire run EnumerateRuns: PROC [work: PROC] = { pageNumber: VM.PageNumber _ 0; runSize _ 0; FOR p: CARDINAL IN [0..mapPages) DO GetMapPage[p]; FOR e: [0..mapEntriesPerPage) IN [0..mapEntriesPerPage) DO interesting: BOOL = InterestingPage[e]; IF runSize > 0 AND (NOT interesting OR pageNumber NOT IN [backingRun.page..backingRun.page+backingRun.count) ) THEN { -- end of current run work[]; runSize _ 0; }; IF interesting THEN { IF runSize = 0 THEN { IF backingRun = NIL OR pageNumber NOT IN [backingRun.page..backingRun.page+backingRun.count) THEN { -- find entry in backing run table IF table = NIL THEN ERROR NoBackingStore[pageNumber]; FOR i: CARDINAL IN [0..table.nRuns) DO backingRun _ @table[i]; IF pageNumber IN [backingRun.page..backingRun.page+backingRun.count) THEN EXIT; REPEAT FINISHED => ERROR NoBackingStore[pageNumber] ENDLOOP; }; runStart _ pageNumber; }; runSize _ runSize+1; }; pageNumber _ pageNumber+1; ENDLOOP; IF firstEnumerate THEN PutMapPage[p]; ENDLOOP; IF runSize > 0 THEN work[]; firstEnumerate _ FALSE; }; OpenOutLdFile[]; table _ ReadBackingMap[]; vmImageHeader^ _ [ runs: 0, pages: 0, bootVersion: SystemVersion.release, bootFileDate: SystemVersion.bootFileDate, checkpointDate: BasicTime.ToPupTime[BasicTime.Now[]] ]; { -- First pass: count runs to determine file size needed fileSize: File.PageCount; -- required checkpoint file size CountRuns: PROC = { vmImageHeader.runs _ vmImageHeader.runs+1; vmImageHeader.pages _ vmImageHeader.pages + runSize; }; out.PutRope["\nCounting swapped-out VM pages ... "]; EnumerateRuns[CountRuns]; firstPassCount _ vmImageHeader.pages; fileSize _ vmImagePos+1+vmImageHeader.pages+ (vmImageHeader.runs +vmImageRunsPerPage-1) / vmImageRunsPerPage; out.PutF["\nThe VM has %g pages, of which %g are swapped-in and %g are swapped-out (in %g runs).\nEnsuring checkpoint file length is at least %g ... ", [integer[countVM]], [integer[outloadDataPages]], [integer[vmImageHeader.pages]], [integer[vmImageHeader.runs]], [integer[fileSize]] ]; IF File.Info[outLdFile].size < fileSize THEN File.SetSize[outLdFile, fileSize]; }; -- first pass File.Write[outLdFile, vmImagePos, 1, vmImageHeader]; vmImagePos _ [vmImagePos+1]; { -- Second pass: transfer the pages vmImageRunTablePos: [0..vmImageRunsPerPage] _ 0; pages: CARDINAL _ 0; -- pages used in vmImageBuffer Copier: PROC = -- Add run to the vmImageRuntable we're building { offset: CARDINAL = runStart-backingRun.page; vmImageRunTable[vmImageRunTablePos] _ [ pages: runSize, deviceType: backingRun.deviceType, deviceOrdinal: backingRun.deviceOrdinal, diskPage: backingRun.diskPage + offset, label: NULL ]; TransferBackingRun[ data: vmImageBufferAddr + pages * PrincOps.wordsPerPage, label: @vmImageRunTable[vmImageRunTablePos].label, which: readAndChecksum, count: 1, addr: [ deviceType: vmImageRunTable[vmImageRunTablePos].deviceType, deviceOrdinal: vmImageRunTable[vmImageRunTablePos].deviceOrdinal, diskPage: vmImageRunTable[vmImageRunTablePos].diskPage, offset: offset, labelCheck: backingRun.labelCheck] ]; vmImageRunTablePos _ vmImageRunTablePos+1; IF vmImageRunTablePos = vmImageRunsPerPage THEN CopyToBuffer[]; }; CopyToBuffer: PROC = { LOOPHOLE[vmImageBufferAddr + pages * PrincOps.wordsPerPage, LONG POINTER TO VMImageRunTable]^ _ vmImageRunTable^; pages _ pages + 1; firstPassCount _ firstPassCount + 1; IF pages = vmImageBufferPages THEN WriteBuffer[]; FOR r: CARDINAL IN [0..vmImageRunTablePos) DO total: CARDINAL = vmImageRunTable[r].pages; reqd: CARDINAL _ total; WHILE reqd > 0 DO amount: CARDINAL = MIN[vmImageBufferPages-pages, reqd]; label: Disk.Label _ vmImageRunTable[r].label; label.filePage _ label.filePage + (total-reqd); TransferBackingRun[ data: vmImageBufferAddr + pages * PrincOps.wordsPerPage, label: @label, which: readAndVerify, count: amount, addr: [ deviceType: vmImageRunTable[r].deviceType, deviceOrdinal: vmImageRunTable[r].deviceOrdinal, diskPage: vmImageRunTable[r].diskPage + (total-reqd), offset: 0, labelCheck: 0 ] ]; pages _ pages + amount; IF pages = vmImageBufferPages THEN WriteBuffer[]; reqd _ reqd - amount; ENDLOOP; ENDLOOP; vmImageRunTablePos _ 0; }; WriteBuffer: PROC = { File.Write[outLdFile, vmImagePos, pages, vmImageBufferAddr]; firstPassCount _ firstPassCount - pages; vmImagePos _ [vmImagePos+pages]; out.PutChar['.]; pages _ 0; }; out.PutF["ok\nCopying VM into checkpoint file (each dot = %g pages transferred) ", [integer[vmImageBufferPages]]]; EnumerateRuns[Copier]; IF vmImageRunTablePos # 0 THEN CopyToBuffer[]; WriteBuffer[]; IF firstPassCount # 0 THEN ERROR WrongPageCount[]; }; -- second pass } ELSE { remainingFile: File.PageCount; -- amount of file that should remain remainingRuns: INT; pages: CARDINAL _ 0; -- position in vmImageBuffer ReadBuffer: PROC = { amount: File.PageCount = MIN[vmImageBufferPages, remainingFile]; File.Read[outLdFile, vmImagePos, amount, vmImageBufferAddr]; vmImagePos _ [vmImagePos+amount]; remainingFile _ remainingFile - amount; pages _ 0; out.PutChar['.]; }; ReadImageRunTable: PROC = { vmImageRunTable^ _ LOOPHOLE[vmImageBufferAddr + pages * PrincOps.wordsPerPage, LONG POINTER TO VMImageRunTable]^; pages _ pages + 1; IF pages = vmImageBufferPages THEN ReadBuffer[]; }; File.Read[outLdFile, vmImagePos, 1, vmImageHeader]; vmImagePos _ [vmImagePos+1]; out.PutF["\nThis checkpoint was created on %g by boot file version %g.%g.%g of %g", [time[BasicTime.FromPupTime[vmImageHeader.checkpointDate]]], [integer[vmImageHeader.bootVersion.major]], [integer[vmImageHeader.bootVersion.minor]], [integer[vmImageHeader.bootVersion.patch]], [time[BasicTime.FromPupTime[vmImageHeader.bootFileDate]]] ]; out.PutF["\nThere were %g swapped-in pages, and %g swapped-out pages in %g runs", [integer[outloadDataPages]], [integer[vmImageHeader.pages]], [integer[vmImageHeader.runs]] ]; remainingFile _ vmImageHeader.pages+ (vmImageHeader.runs +vmImageRunsPerPage-1) / vmImageRunsPerPage; IF File.Info[outLdFile].size < vmImagePos+remainingFile THEN ERROR ShortFile[]; remainingRuns _ vmImageHeader.runs; out.PutF["\nCopying VM from checkpoint file (each dot = %g pages transferred) ", [integer[vmImageBufferPages]]]; ReadBuffer[]; WHILE remainingRuns > 0 DO runsThisTime: CARDINAL = MIN[remainingRuns, vmImageRunsPerPage]; remainingRuns _ remainingRuns - runsThisTime; ReadImageRunTable[]; FOR r: CARDINAL IN [0 .. runsThisTime) DO total: CARDINAL = vmImageRunTable[r].pages; reqd: CARDINAL _ total; WHILE reqd > 0 DO amount: CARDINAL = MIN[vmImageBufferPages-pages, reqd]; label: Disk.Label _ vmImageRunTable[r].label; label.filePage _ label.filePage + (total-reqd); TransferBackingRun[ data: vmImageBufferAddr + pages * PrincOps.wordsPerPage, label: @label, which: write, count: amount, addr: [ deviceType: vmImageRunTable[r].deviceType, deviceOrdinal: vmImageRunTable[r].deviceOrdinal, diskPage: vmImageRunTable[r].diskPage + (total-reqd), offset: 0, labelCheck: 0 ] ]; pages _ pages + amount; IF pages = vmImageBufferPages THEN ReadBuffer[]; reqd _ reqd - amount; ENDLOOP; ENDLOOP; ENDLOOP; }; out.PutRope[" ok\nInloading real memory from checkpoint file ... "]; IF Booting.switches[l] THEN { out.PutRope["\nPausing (\"L\" switch). Type any character to continue: "]; [] _ in.GetChar[]; }; [] _ Booting.Boot[boot: [file[outLdFile, outLdFirstPage]], switches: ALL[FALSE] ]; ERROR }; IF Booting.switches[v] THEN { which _ checkpoint; DoIt[] }; IF Booting.switches[r] THEN { which _ rollback; DoIt[] }; [] _ File.FindVM[]; -- in this case we're continuing with the full boot sequence }. RollbackImpl.mesa (Checkpoint and Rollback: VM copying) Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell December 7, 1983 1:22 pm Levin, September 22, 1983 1:48 pm Russ Atkinson (RRA) June 5, 1985 3:33:47 pm PDT Accessing outload image on checkpoint file. (Mainly stolen from WVMCache.mesa) Layout of boot and outload files returns pointer into vm containing data from outload file map page Leaves result in outLdAddress it's in this page of the bootFileMap Reads from address space of outload file (cf. WorldVM.CopyRead) Transfers from backing store (also stolen from WVMCache.mesa) Assumes data is pinned and VM page aligned VM backing data structure (ReadBackingMap stolen from WVMBackingMap.mesa) Top level procedure Just in case we are running tight enough to fall back to a smaller interval Copy from VM backing storage to checkpoint file, then inload from checkpoint file Mark it dirty because we won't restore the backing disk. And mark it referenced so it won't look vacant! ( "firstEnumerate" optimizes so we only update the entry once ) The following are set by EnumerateRuns before calling "work": call "work" for each run of interesting pages start of a new run Write the header to the checkpoint file Read first page of run to get its label (sigh!) Read the runs into the buffer Write from buffer into checkpoint file Copy from checkpoint file to VM backing storage, then inload from checkpoint file Beyond here, we should not access vmImageHeader (it's overlaid on the buffer!) Pause so user can read the screen ΚL˜codešœ7™7Kšœ Οmœ1™Kšœ ˜ Kšžœžœ˜Kšžœ˜#K˜Kšœ žœ*˜6˜ Kšžœžœ˜Kšžœ*˜.Kšžœžœžœ˜ —Kšœ˜——Kšœ˜—Kšžœ žœ˜—Kšœ˜—K˜K˜—Kšœ™˜Kšœ žœžœ˜K˜Kšœžœ˜K˜š œžœžœž œžœ˜/Kšœ žœ˜&Kšœ žœ˜ Kšœ žœ˜Kšœ,˜,Kšœ$˜$Kšœ&˜&K˜—K˜š œ žœžœž œžœ˜,Kšœžœ˜K˜Kšœžœ˜Kšœ žœžœ˜K˜—K˜Kšœžœžœ ˜DK˜Kšœžœžœžœ ˜DK˜Kšœžœ˜K˜Kšœžœžœ˜2Kšœ žœžœ˜Kšœžœžœ˜K˜Kšœ ˜ K˜šŸœžœ žœ˜#K˜K˜(šœ žœ˜!Kšžœ!˜%Kšžœ$˜(—K˜ K˜šžœ˜KšžœFžœžœ˜VKšžœ1žœ˜=Kšž˜—Kšœ˜—K˜šŸœžœžœ˜šžœ˜šœ ˜ šœ žœž˜Kšœ0˜0Kšœ8˜8KšœN˜NKšœ=˜=KšœA˜AKšœ4˜4Kšœ5˜5Kšœ6˜6Kšœ<˜KšœW˜WKšœ/˜/KšœH˜HKšœ$˜$KšžœC˜IKšœ˜—K˜-Kšžœ˜Kšœ žœžœžœ˜Kšœ˜Kšœ˜Kšœ˜šžœž˜ K˜LKšžœ žœžœ˜Kšžœ˜—Kšœ˜—KšœA˜AKšœ˜Kšœ!˜!šžœžœ˜KšœQ™QšŸ œžœžœž˜(KšœM˜M—šŸ œžœžœž˜(KšœN˜N—Kšœžœžœ˜š Ÿœžœžœžœžœ˜QKšœ,˜,šžœ,˜.Kšžœ6˜:šžœ˜Kšœžœ˜ Kšœ8™8Kšœ/™/K™?šžœžœ˜Kšœ&žœ˜+Kšœ!žœ˜&Kšœ˜—Kšœ˜——Kšœ˜—Kšœžœ 4˜MKšœžœ˜%šœ=™=Kšœ žœ ˜+Kšœ žœ  #˜