DIRECTORY BasicTime USING[ FromPupTime, Now, ToPupTime ], BootFileChanges USING [ Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, PageValue, 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, GetRoot, Handle, Info, Open, PageCount, PageNumber, Read, SetSize, SystemVolume, wordsPerPage, Write ], IO USING[ GetChar, Flush, PutChar, PutF, PutRope, STREAM ], PrincOps USING[ flagsVacant, PageCount, PageNumber, PDA, wordsPerPage ], PrincOpsUtils USING[ LongCOPY ], ProcessorFace USING[ GetNextAvailableVM ], Rope USING[ ROPE ], SimpleTerminal USING[ TurnOn ], SystemVersion USING[ bootFileDate, Date, release, ReleaseNumber ], VM USING[ AddressForPageNumber, Allocate, Interval, PageCount, PageNumber, PagesForWords, Pin ], VMBackingChanges USING [StateFromPageValue]; RollbackImpl: MONITOR IMPORTS BasicTime, BootFileChanges, Booting, DebuggerFormat, Disk, File, IO, PrincOpsUtils, ProcessorFace, SimpleTerminal, SystemVersion, VM, VMBackingChanges = BEGIN vmPagesPerFilePage: VM.PageCount = VM.PagesForWords[File.wordsPerPage]; valuesPerPage: CARDINAL = PrincOps.wordsPerPage / SIZE[BootFileChanges.PageValue]; PageValueArray: TYPE = ARRAY [0..valuesPerPage) OF BootFileChanges.PageValue; mapEntriesPerPage: CARDINAL = valuesPerPage; outLdFile: File.Handle _ NIL; outLdFirstPage: File.PageNumber; outLdSpace: VM.Interval = VM.Allocate[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[BootFileChanges.maxEntriesPerHeader, BootFileChanges.maxEntriesPerTrailer]]; BootFileMapItem: TYPE = RECORD [ link: BFMPointer _ NIL, lastMemPage: PrincOps.PageNumber, filePageOffset: File.PageNumber, entryCount: BFMEntryIndex, entries: EntrySeq]; EntrySeq: TYPE = LONG POINTER TO BootFileChanges.TrailerArray; BFMPointer: TYPE = REF BootFileMapItem; bfmHead: BFMPointer _ NIL; OpenOutLdFile: PROC = BEGIN bfm: BFMPointer; bfh: LONG POINTER TO BootFileChanges.Header; entries: EntrySeq; curmax: CARDINAL _ BootFileChanges.maxEntriesPerHeader; curbase: File.PageNumber _ [0]; OutLdPageSpace: PROC RETURNS[vm: LONG POINTER] = BEGIN space: VM.Interval = VM.Allocate[vmPagesPerFilePage]; vm _ VM.AddressForPageNumber[space.page]; File.Read[outLdFile, curbase, 1, vm]; END; 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 _ BootFileChanges.maxEntriesPerTrailer; entries _ OutLdPageSpace[] + BootFileChanges.TrailerStart; bfm.link _ NEW[BootFileMapItem]; bfm _ bfm.link; ENDLOOP; END; PageNotFound: ERROR[mempage: PrincOps.PageNumber] = CODE; MalformedOutload: ERROR = CODE; SearchOutLdFile: PROCEDURE [mempage: PrincOps.PageNumber] = BEGIN FOR bfm: BFMPointer _ bfmHead, bfm.link UNTIL bfm = NIL DO IF mempage <= PrincOps.PageNumber[bfm.lastMemPage] THEN BEGIN -- it's in this page of the bootFileMap -- FOR i: BFMEntryIndex IN [0..bfm.entryCount] DO SELECT PrincOps.PageNumber[bfm.entries[i].page] FROM < mempage => NULL; > mempage => ERROR PageNotFound[mempage]; ENDCASE => BEGIN ReadOutLdPage[[bfm.filePageOffset+i]]; RETURN END REPEAT FINISHED => ERROR MalformedOutload[]; ENDLOOP; END; REPEAT FINISHED => ERROR PageNotFound[mempage] ENDLOOP; END; CopyRead: PUBLIC PROC[from: INT, nwords: INT, to: LONG POINTER] = BEGIN 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; END; 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] = BEGIN ENABLE UNWIND => NULL; req: Disk.Request; status: Disk.Status; countDone: INT; IF lastChannel = NIL OR addr.deviceType # lastType OR addr.deviceOrdinal # lastOrdinal THEN BEGIN 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; END; 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); END; ReadBackingMap: PROC RETURNS[table: REF DebuggerFormat.VMRunTable] = BEGIN 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 => BEGIN IF esv.vmRunTable = NIL THEN table _ NIL ELSE BEGIN 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] ]; END; END; ENDCASE => table _ NIL; END; 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: VM.PageCount = 200; NoBackingStore: ERROR[PrincOps.PageNumber] = CODE; ShortFile: ERROR = CODE; WrongPageCount: ERROR = CODE; which: { checkpoint, rollback }; Complain: PROC[msg: Rope.ROPE] = BEGIN 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 END; DoIt: PROC = TRUSTED BEGIN ENABLE BEGIN File.Error => Complain[SELECT why FROM wentOffline => "The system volume has gone offline", nonCedarVolume => "The system volume is not a cedar volume", inconsistent => "The 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 => "I don't understand the checkpoint file (unknownPage)", volumeFull => "The system volume is full - can't create checkpoint", fragmented => "The system volume is too fragmented - can't create checkpoint", 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"]; END; outloadDataPages: PrincOps.PageCount; countVM: INT _ 0; mapPages: CARDINAL; mapBufferSpace: VM.Interval = VM.Allocate[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 = VM.Allocate[vmImageBufferPages]; vmImageBufferAddr: LONG POINTER = VM.AddressForPageNumber[vmImageBufferSpace.page]; vmImageHeader: LONG POINTER TO VMImageHeader = vmImageBufferAddr; vmImageRunTable: REF VMImageRunTable _ NEW[VMImageRunTable]; GetMapStart: PROC RETURNS[File.PageNumber] = TRUSTED BEGIN fp: File.FP; header: LONG POINTER TO BootFileChanges.Header = LOOPHOLE[mapBuffer]; [fp: fp, page: outLdFirstPage] _ File.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+BootFileChanges.MemorySizeToFileSize[outloadDataPages]]] END; [in: in, out: out] _ SimpleTerminal.TurnOn[]; VM.Pin[vmImageBufferSpace]; out.PutRope[IF which = checkpoint THEN "\nCheckpoint." ELSE "\nRollback."]; BEGIN 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; END; mapPages _ (countVM + mapEntriesPerPage - 1) / mapEntriesPerPage; mapStart _ GetMapStart[]; vmImagePos _ [mapStart+mapPages]; IF which = checkpoint THEN BEGIN 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 BEGIN mapEntry: BootFileChanges.PageValue = mapBuffer[e]; IF mapEntry.state.flags = PrincOps.flagsVacant THEN yes _ VMBackingChanges.StateFromPageValue[mapEntry] = active ELSE BEGIN yes _ FALSE; IF firstEnumerate THEN { mapBuffer[e].state.flags.referenced _ TRUE; mapBuffer[e].state.flags.dirty _ TRUE; }; END; END; 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] = -- call "work" for each run of interesting pages BEGIN 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 BEGIN -- end of current run work[]; runSize _ 0; END; IF interesting THEN BEGIN IF runSize = 0 THEN BEGIN -- start of a new run IF backingRun = NIL OR pageNumber NOT IN [backingRun.page..backingRun.page+backingRun.count) THEN BEGIN -- 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; END; runStart _ pageNumber; END; runSize _ runSize+1; END; pageNumber _ pageNumber+1; ENDLOOP; IF firstEnumerate THEN PutMapPage[p]; ENDLOOP; IF runSize > 0 THEN work[]; firstEnumerate _ FALSE; END; OpenOutLdFile[]; table _ ReadBackingMap[]; vmImageHeader^ _ [ runs: 0, pages: 0, bootVersion: SystemVersion.release, bootFileDate: SystemVersion.bootFileDate, checkpointDate: BasicTime.ToPupTime[BasicTime.Now[]] ]; BEGIN -- First pass: count runs to determine file size needed fileSize: File.PageCount; -- required checkpoint file size CountRuns: PROC = BEGIN vmImageHeader.runs _ vmImageHeader.runs+1; vmImageHeader.pages _ vmImageHeader.pages + runSize; END; 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]; END; -- first pass File.Write[outLdFile, vmImagePos, 1, vmImageHeader]; vmImagePos _ [vmImagePos+1]; BEGIN -- 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 BEGIN 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[]; END; CopyToBuffer: PROC = -- Read the runs into the buffer BEGIN 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; END; WriteBuffer: PROC = -- Write from buffer into checkpoint file BEGIN File.Write[outLdFile, vmImagePos, pages, vmImageBufferAddr]; firstPassCount _ firstPassCount - pages; vmImagePos _ [vmImagePos+pages]; out.PutChar['.]; pages _ 0; END; 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[]; END; -- second pass END ELSE BEGIN remainingFile: File.PageCount; -- amount of file that should remain remainingRuns: INT; pages: CARDINAL _ 0; -- position in vmImageBuffer ReadBuffer: PROC = BEGIN amount: File.PageCount = MIN[vmImageBufferPages, remainingFile]; File.Read[outLdFile, vmImagePos, amount, vmImageBufferAddr]; vmImagePos _ [vmImagePos+amount]; remainingFile _ remainingFile - amount; pages _ 0; out.PutChar['.]; END; ReadImageRunTable: PROC = BEGIN vmImageRunTable^ _ LOOPHOLE[vmImageBufferAddr + pages * PrincOps.wordsPerPage, LONG POINTER TO VMImageRunTable]^; pages _ pages + 1; IF pages = vmImageBufferPages THEN ReadBuffer[]; END; 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; END; out.PutRope[" ok\nInloading real memory from checkpoint file ... "]; IF Booting.switches[l] THEN BEGIN -- Pause so user can read the screen out.PutRope["\nPausing (\"L\" switch). Type any character to continue: "]; [] _ in.GetChar[]; END; [] _ Booting.Boot[boot: [file[outLdFile, outLdFirstPage]], switches: ALL[FALSE] ]; ERROR END; 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 END. ÐRollbackImpl.mesa Cedar Nucleus: Checkpoint and Rollback: VM copying Andrew Birrell December 7, 1983 1:22 pm Levin, September 22, 1983 1:48 pm Russ Atkinson, April 26, 1984 2:10:27 am PST 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 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 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": Write the header to the checkpoint file Read first page of run to get its label (sigh!) 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!) Êõ˜šœ™Jšœ3™3Jšœ(™(J™!J™,J™—šÏk ˜ Jšœ œ ˜/šœœ˜Jšœx˜x—Jšœœ˜*šœœ˜JšœK˜K—JšœœL˜VJšœ œ ˜šœœ˜ Jšœœi˜z—Jšœœ*œ˜;Jšœ œ&œ˜HJšœœ ˜ Jšœœ˜*Jšœœœ˜Jšœœ ˜Jšœœ/˜Bšœœ˜ JšœV˜V—Jšœœ˜,—J˜šœ˜JšœBœ?œ˜ —J˜Jš˜˜Jšœœ œ"˜GJšœœœ˜RJšœœœœ˜MJšœœ˜,J˜—JšœN™N˜Jšœœ˜J˜ Jšœ œ œ˜:Jšœœœ˜)š œœœœœœœ˜HJšœ'˜)—J˜šÏn œœ˜,Jšœœœ9˜S—J˜Jšœ ™ J˜šœœœ%˜BJšœ'˜'—J˜šœœœ˜ Jšœœ˜Jšœ!˜!J˜ J˜Jšœ˜J˜—Jš œ œœœœ˜>J˜Jšœ œœ˜'J˜Jšœœ˜J˜šž œœ˜Jš˜J˜Jšœœœœ˜,Jšœ˜Jšœœ'˜7J˜š žœœœœœ˜0JšœB™BJš˜J˜5Jšœ)˜)Jšœ%˜%Jšœ˜—Jšœœ˜Jšœ œ˜J˜Jšœ œ˜!J˜Jšœœ˜%šœ˜Jšœœ˜-J˜5J˜!J˜1Jšœœœ˜ J˜.Jšœ.˜.Jšœ:˜:Jšœ œ˜ J˜—Jšœ˜Jšœ˜—J˜Jšœœ!œ˜9Jšœœœ˜J˜šžœ œ!˜;Jš˜Jšœ™Jšœ%œ˜7šœœ0˜5šœœÏc*˜5Jšœœ˜+šœœ*˜7Jšœ œ˜Jšœ œ˜)šœ˜ Jš˜J˜&Jš˜Jš˜——Jšœœœ˜,Jšœ˜Jšœ˜——Jšœœœ˜.Jšœ˜Jšœ˜—J˜šžœœœœ œœœ˜AJš˜Jšœ?™?J˜J˜Jšœ ˜šœ$˜&Jšœ!˜!Jšœœ˜+Jšœ˜Jšœ œ!˜-JšœG˜GJ˜A—Jšœ˜Jšœ˜—J˜J˜J˜—Jšœ=™=˜Jšœœœ˜Jšœ œœ˜J˜šœ œœ˜J˜Jšœœ˜Jšœ œœŸ.˜GJšœœŸ@˜RJšœ œŸ!˜6J˜—J˜Jšœœ˜ J˜Jšœ œ˜J˜šžœœœœ˜&Jšœœœ˜Jšœ œœ ˜"J˜1Jšœœ˜ J˜Jš˜Jšœ*™*Jšœœœ˜J˜J˜Jšœ œ˜Jšœœœœ!˜Všœ˜ Jšœ œ ˜FJšœ˜šœL˜NJšœœ"œœ˜M—Jšœœœ˜(Jšœ˜Jšœ˜—˜Jšœ˜Jšœ ˜ šœ œ˜Jšœ=˜=Jšœ=˜=Jšœ6˜6Jšœœ˜—Jšœ˜—Jšœ:˜:Jšœœœ˜-Jšœ˜JšœN˜QJšœœ˜Jšœ,˜,Jšœ˜—J˜J˜—JšœI™I˜šžœœœœ˜DJš˜Jšœ"˜"Jšœœœ œ ˜6J˜(Jšœ œ˜ Jšœ%œ*˜SJšœ œ˜1Jšœœ,˜Bšœ˜˜Jš˜Jšœ˜Jšœ ˜šœ˜ Jš œœœ œœ˜Jšœ ˜ Jšœœ˜Jšœ˜#J˜Jšœ œ*˜6˜ Jšœœ˜Jšœ*˜.Jšœœœ˜ —Jšœ˜——Jšœ˜—Jšœ œ˜Jšœ˜—J˜J˜—Jšœ™˜Jšœ œœ˜J˜Jšœœ˜J˜š œœœ œœ˜/Jšœ œ˜&Jšœ œ˜ Jšœ œ˜Jšœ,˜,Jšœ$˜$Jšœ&˜&J˜—J˜š œ œœ œœ˜,Jšœœ˜J˜Jšœœ˜Jšœ œœ˜J˜—J˜Jšœœœ ˜DJ˜Jšœœœœ ˜DJ˜Jšœœ˜'J˜J˜Jšœœœ˜2Jšœ œœ˜Jšœœœ˜J˜Jšœ ˜ J˜šžœœ œ˜ Jš˜J˜J˜(šœ œ˜!Jšœ!˜%Jšœ$˜(—J˜ J˜Jšœ˜JšœFœœ˜VJšœ1œ˜=Jš˜Jšœ˜—J˜šžœœ˜Jš˜š˜Jš˜šœ ˜ šœ œ˜Jšœ4˜4Jšœ<˜