<> <> DIRECTORY BcdDefs USING [GFTIndex, MTIndex, NullVersion, VersionStamp], BcdOps USING [BcdBase, MTHandle, ProcessModules], Boot USING [Location, LP], BootSwap USING [mdsiGerm, Mon, OutLoad, sPilotSwitches], BootSwapCross USING [pMon], CachedSpace USING [Desc, Handle, Level], CedarInitOps USING [EnsureUsableMicrocode], CedarSnapshot USING [After, CheckpointProc, Outcome, RollbackProc], CedarSnapshotPrivate USING [ CorkHandle, CorkRestOfWorld, EnsureSnapshotFileSize, EnumerateCheckpointProcs, EnumerateRollbackProcs, FinalizeFeedback, FinalizeSnapshotFile, initialClientScriptPages, InitializeFeedback, InitializeSnapshotFile, initialLinkScriptPages, initialVMScriptPages, InstallSnapshotFile, maxReasonableSwapUnit, PrepareForOutload, SetPhase, SnapshotFile, snapshotLock, Twiddle, UncorkRestOfWorld], DeviceCleanup USING [Perform], DiskChannel USING [Idle, Restart], DirectoryFiles USING [DCEntry, --directoryCache,-- maxDCache], DirectoryFilesImpl USING [directoryCache], DirectoryInternal USING [DirectoryPageHandle, leaderPageSize], Environment USING [wordsPerPage], File USING [ Capability, Create, GetAttributes, GetSize, ID, nullID, PageCount, PageNumber, Permissions, read, Unknown, write], Heap USING [systemZone], Hierarchy USING [GetDescriptor], Inline USING [BITAND, BITOR, LowHalf], LogicalVolume USING [CloseLogicalVolume, OpenLogicalVolume], MapLog USING [WriteLog], PilotFileTypes USING [PilotFileType, tAnonymousFile, tVMBackingFile], PilotLoaderOps USING [ CloseLinkSpace, LinkSegmentLength, OpenLinkSpace, ReadLink, WriteLink], PilotLoadStateOps USING [ AcquireBcd, ConfigIndex, EnumerateBcds, GetMap, GetModule, InputLoadState, Map, NullConfig, ReleaseBcd, ReleaseLoadState, ReleaseMap], PilotMP USING [cClient], PilotSwitches USING [switches --.m--], PrincOps USING [ControlLink, GFTIndex, GlobalFrameHandle], PrincOpsRuntime USING [GetFrame, GFT], ProcessInternal USING [DisableInterrupts, EnableInterrupts], ProcessOperations USING [ReadPSB, ReadPTC, ReadWDC, WritePSB, WritePTC, WriteWDC], ProcessorFace USING [SetMP], PSB USING [PsbHandle], RuntimeInternal USING [Codebase, WorryCallDebugger], SDDefs USING [SD], Space USING [ CopyIn, CopyOut, Create, Deactivate, defaultWindow, Delete, GetAttributes, GetHandle, GetWindow, Handle, LongPointer, MakeReadOnly, MakeWritable, Map, nullHandle, PageCount, PageFromLongPointer, PageOffset, Unmap, virtualMemory, WindowOrigin], SpaceImplInternal USING [EnterSpace], SpecialSpace USING [MakeResident, MakeSwappable], SpecialTerminal USING [TurnOff, TurnOn], SubVolumeImpl USING [CacheEntry, ceFirst, ceLast, CePtr], System USING [GetUniversalID, VolumeID], TemporaryBooting USING [Switches], TemporarySetGMT USING [SetGMT], TerminalMultiplex USING [PermitDebuggerSwaps, PreventDebuggerSwaps], TransactionExtras USING [DoCrashRecovery, TransactionsInProgress], UserCredentialsUnsafe USING [GetProc, Login, PutProc], VM USING [PageNumber], Volume USING [ID, InsufficientSpace, nullID, systemID, Unknown], VolumeImplInterface USING [SubvolumeOffline, SubvolumeOnline]; CedarSnapshotCheckpoint: MONITOR LOCKS CedarSnapshotPrivate.snapshotLock IMPORTS BcdOps, BootSwap, BootSwapCross, CedarInitOps, CedarSnapshotPrivate, DeviceCleanup, DirectoryFilesImpl, DiskChannel, File, Heap, Hierarchy, Inline, LogicalVolume, MapLog, PilotLoaderOps, PilotLoadStateOps, PilotSwitches, PrincOpsRuntime, ProcessInternal, ProcessOperations, ProcessorFace, RuntimeInternal, Space, SpaceImplInternal, SpecialSpace, SpecialTerminal, SubVolumeImpl, System, TemporarySetGMT, TerminalMultiplex, TransactionExtras, UserCredentialsUnsafe, Volume, VolumeImplInterface EXPORTS CedarSnapshot, BootSwap -- hack!! SHARES BootSwap, BootSwapCross, File, SubVolumeImpl = BEGIN OPEN CedarSnapshotPrivate; -- Hack export to BootSwap (needed by Outload) -- pMon: PUBLIC LONG POINTER TO BootSwap.Mon _ BootSwapCross.pMon; -- Client Errors -- UnacceptableSpace: ERROR [reason: Reason] = CODE; Reason: TYPE = {nullHandle, dataSpace, mappedToPilotFile, insufficientPermissions}; SnapshotBug: ERROR = CODE; -- Checkpoint -- Checkpoint: PUBLIC ENTRY PROC [volume: Volume.ID _ Volume.nullID] RETURNS [outcome: CedarSnapshot.Outcome] = BEGIN systemVolume: Volume.ID _ Volume.systemID; snapshotFile: SnapshotFile _ NIL; outloadLocation: disk Boot.Location; <> Disposition: TYPE = {keep, delete}; SpaceScriptEntryRP: TYPE = SpaceScriptBase RELATIVE ORDERED POINTER [0..LAST[CARDINAL]] TO SpaceScriptEntry; SpaceScriptEntryPtr: TYPE = LONG POINTER TO SpaceScriptEntry; SpaceScriptEntryType: TYPE = {space, subSpace, mappingSpace, end}; SpaceScriptEntry: TYPE = RECORD [ entry: SELECT type: SpaceScriptEntryType FROM space => [ readOnly, ensureBackingStorage: BOOLEAN, level: CachedSpace.Level, page: VM.PageNumber, size: Space.PageCount], subSpace => [ disposition: Disposition, level: CachedSpace.Level, page: VM.PageNumber, size: Space.PageCount], mappingSpace => [ readOnly, ensureBackingStorage: BOOLEAN, level: CachedSpace.Level, page: VM.PageNumber], end => NULL, ENDCASE]; SpaceScriptBase: TYPE = LONG BASE POINTER; SpaceScript: TYPE = LONG POINTER TO SpaceScriptDescriptor; SpaceScriptDescriptor: TYPE = RECORD [ base: SpaceScriptBase _ NIL, end: SpaceScriptEntryRP _ FIRST[SpaceScriptEntryRP], pages: Space.PageCount _ NULL, limit: SpaceScriptEntryRP _ FIRST[SpaceScriptEntryRP], space: Space.Handle _ Space.nullHandle, pinned: BOOLEAN _ FALSE, baseOfSpaces: File.PageNumber _ NULL, pagesForSpaces: File.PageCount _ 0]; entrySizes: ARRAY SpaceScriptEntryType OF CARDINAL = [ SIZE[space SpaceScriptEntry], SIZE[subSpace SpaceScriptEntry], SIZE[mappingSpace SpaceScriptEntry], SIZE[end SpaceScriptEntry]]; InitializeSpaceScript: PROC [script: SpaceScript, pages: Space.PageCount] = BEGIN PrepareSpaceScript[script, pages]; END; ExtendSpaceScript: PROC [script: SpaceScript, increment: Space.PageCount _ 1] = BEGIN <> Space.Delete[script.space]; PrepareSpaceScript[script, script.pages + increment]; END; FlushSpaceScript: PROC [script: SpaceScript] = BEGIN <> <> <> <> <> <> <> <> <> <> <> <> IF script.space ~= Space.nullHandle THEN { IF script.pinned THEN SpecialSpace.MakeSwappable[script.space]; -- is this necessary? Space.Delete[script.space]; }; END; PrepareSpaceScript: PROC [script: SpaceScript, pages: Space.PageCount] = BEGIN script.space _ Space.Create[pages, Space.virtualMemory]; EnsureSnapshotFileSize[snapshotFile, snapshotFile.firstFree + pages]; Space.Map[script.space, [snapshotFile.cap, snapshotFile.firstFree]]; script.base _ Space.LongPointer[script.space]; script.limit _ LOOPHOLE[pages*Environment.wordsPerPage]; script.pages _ pages; END; InstallSpaceScript: PROC [script: SpaceScript] = BEGIN AllocateEntry[script, end]^ _ [end[]]; <> <> snapshotFile.firstFree _ snapshotFile.firstFree + script.pages; END; AddSpaceOrChildren: PROC [ script: SpaceScript, parent: Space.Handle, ensureBackingStorage: BOOLEAN] = BEGIN child: Space.Handle; parentSize: Space.PageCount; readOnly, pinned: BOOLEAN; <> <> <> <> [pinned, readOnly] _ GetPinnedAndReadOnly[parent]; IF pinned THEN RETURN; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> [size: parentSize, lowestChild: child] _ Space.GetAttributes[parent]; IF parentSize <= maxReasonableSwapUnit OR child = Space.nullHandle THEN BEGIN sH: CachedSpace.Handle = LOOPHOLE[parent]; AllocateEntry[script, space]^ _ [space[ readOnly: readOnly, ensureBackingStorage: ensureBackingStorage, level: sH.level, page: sH.page, size: parentSize]]; script.pagesForSpaces _ script.pagesForSpaces + parentSize; END ELSE BEGIN <> <> <> <> <> expectedBase: Space.PageOffset _ 0; AddSubSpace: PROC [space: Space.Handle, size: Space.PageCount, disposition: Disposition] = BEGIN sH: CachedSpace.Handle = LOOPHOLE[space]; AllocateEntry[script, subSpace]^ _ [subSpace[ disposition: disposition, level: sH.level, page: sH.page, size: size]]; script.pagesForSpaces _ script.pagesForSpaces + size; END; FillTo: PROC [nextBase: Space.PageOffset] = BEGIN fillerSize: Space.PageCount = nextBase - expectedBase; filler: Space.Handle = Space.Create[size: fillerSize, parent: parent, base: expectedBase]; AddSubSpace[filler, fillerSize, delete]; END; sH: CachedSpace.Handle = LOOPHOLE[parent]; AllocateEntry[script, mappingSpace]^ _ [mappingSpace[ readOnly: readOnly, ensureBackingStorage: ensureBackingStorage, level: sH.level, page: sH.page]]; DO base: Space.PageOffset; size: Space.PageCount; nextSibling: Space.Handle; [base: base, size: size, nextSibling: nextSibling] _ Space.GetAttributes[child]; IF base ~= expectedBase THEN FillTo[base]; AddSubSpace[child, size, keep]; expectedBase _ base + size; IF nextSibling = Space.nullHandle THEN EXIT; child _ nextSibling; ENDLOOP; IF expectedBase ~= parentSize THEN FillTo[parentSize]; END; END; AllocateEntry: PROC [script: SpaceScript, type: SpaceScriptEntryType] RETURNS [next: SpaceScriptEntryPtr] = BEGIN WHILE script.end + entrySizes[type] >= script.limit DO ExtendSpaceScript[script]; ENDLOOP; next _ @script.base[script.end]; script.end _ script.end + entrySizes[type]; Twiddle[]; END; SpaceFromScriptEntry: PROC [entry: SpaceScriptEntryPtr] RETURNS [space: Space.Handle] = BEGIN WITH e: entry SELECT FROM space => space _ LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; subSpace => space _ LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; mappingSpace => space _ LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; ENDCASE => ERROR SnapshotBug; END; EnumerateSpaceScript: PROC [script: SpaceScript, proc: PROC [SpaceScriptEntryPtr]] = BEGIN next: SpaceScriptEntryRP _ FIRST[SpaceScriptEntryRP]; UNTIL next >= script.end DO proc[@script.base[next]]; next _ next + entrySizes[script.base[next].type]; ENDLOOP; END; EnumerateChildren: PROC [space: Space.Handle, proc: PROC [Space.Handle]] = BEGIN FOR child: Space.Handle _ Space.GetAttributes[space].lowestChild, Space.GetAttributes[child].nextSibling UNTIL child = Space.nullHandle DO proc[child]; ENDLOOP; END; <> <> clientScript: SpaceScript _ Heap.systemZone.NEW[SpaceScriptDescriptor _ []]; ProcessSpaceForClientScript: PROC [space: Space.Handle] = BEGIN CheckPotentiallyUnmappedSpace: PROC [space: Space.Handle] = BEGIN IF Space.GetAttributes[space].mapped THEN CheckAndAddSpace[space, space] ELSE EnumerateChildren[space, CheckPotentiallyUnmappedSpace]; END; CheckAndAddSpace: PROC [space, mappingSpace: Space.Handle] = BEGIN window: Space.WindowOrigin = Space.GetWindow[mappingSpace]; permissions: File.Permissions = File.read + File.write; SELECT TRUE FROM window = Space.defaultWindow => ERROR UnacceptableSpace[dataSpace]; File.GetAttributes[window.file].type IN PilotFileTypes.PilotFileType => ERROR UnacceptableSpace[mappedToPilotFile]; Inline.BITAND[window.file.permissions, permissions] ~= permissions => ERROR UnacceptableSpace[insufficientPermissions]; ENDCASE; AddSpaceOrChildren[clientScript, space, FALSE]; END; mapped: BOOLEAN _ FALSE; mappingSpace: Space.Handle _ space; IF space = Space.nullHandle THEN ERROR UnacceptableSpace[nullHandle]; UNTIL mappingSpace = Space.nullHandle DO parent: Space.Handle; [mapped: mapped, parent: parent] _ Space.GetAttributes[mappingSpace]; IF mapped THEN EXIT; mappingSpace _ parent; ENDLOOP; IF mapped THEN CheckAndAddSpace[space, mappingSpace] ELSE EnumerateChildren[space, CheckPotentiallyUnmappedSpace]; END; BuildClientSpaceScript: PROC = BEGIN DoOneCheckpointProc: PROC [proc: CedarSnapshot.CheckpointProc] = {proc[ProcessSpaceForClientScript]}; InitializeSpaceScript[clientScript, initialClientScriptPages]; EnumerateCheckpointProcs[DoOneCheckpointProc]; InstallSpaceScript[clientScript]; END; CheckpointClientSpaces: PROC = BEGIN window: Space.WindowOrigin; CheckpointOneClientSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN space: Space.Handle; size: Space.PageCount; deactivate: BOOLEAN; WITH e: entry SELECT FROM space => {size _ e.size; deactivate _ FALSE}; subSpace => {size _ e.size; deactivate _ TRUE}; ENDCASE => RETURN; space _ SpaceFromScriptEntry[entry]; Space.CopyOut[space: space, window: window]; <> <> <<(see comments in AddSpaceOrChildren). We would prefer not to have this>> <> IF deactivate THEN Space.Deactivate[space]; window.base _ window.base + size; Twiddle[]; END; clientScript.baseOfSpaces _ snapshotFile.firstFree; EnsureSnapshotFileSize[snapshotFile, clientScript.baseOfSpaces + clientScript.pagesForSpaces]; window _ [snapshotFile.cap, clientScript.baseOfSpaces]; EnumerateSpaceScript[clientScript, CheckpointOneClientSpace]; snapshotFile.firstFree _ snapshotFile.firstFree + clientScript.pagesForSpaces; END; RollbackClientSpaces: PROC = BEGIN window: Space.WindowOrigin _ [snapshotFile.cap, clientScript.baseOfSpaces]; RollbackOneClientSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN space: Space.Handle; size: Space.PageCount; disposition: Disposition _ keep; WITH e: entry SELECT FROM space => size _ e.size; subSpace => {size _ e.size; disposition _ e.disposition}; ENDCASE => RETURN; space _ SpaceFromScriptEntry[entry]; Space.CopyIn[space: space, window: window]; IF disposition = delete THEN Space.Delete[space]; window.base _ window.base + size; Twiddle[]; END; EnumerateSpaceScript[clientScript, RollbackOneClientSpace]; END; CleanupClientSpaces: PROC [after: CedarSnapshot.After] = BEGIN <> <> IF after = checkpoint THEN BEGIN CleanupOneClientSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN WITH e: entry SELECT FROM subSpace => IF e.disposition = delete THEN Space.Delete[SpaceFromScriptEntry[entry]]; ENDCASE; END; IF clientScript = NIL THEN RETURN; EnumerateSpaceScript[clientScript, CleanupOneClientSpace]; END; FlushSpaceScript[clientScript]; Heap.systemZone.FREE[@clientScript]; END; <> nSpecials: CARDINAL = IF PilotSwitches.switches.m = down THEN 1 ELSE 2; <> vmScript: SpaceScript _ Heap.systemZone.NEW[SpaceScriptDescriptor _ []]; ProcessSpaceForVMScript: PROC [space: Space.Handle] = BEGIN size: Space.PageCount; mapped: BOOLEAN; [size: size, mapped: mapped] _ Space.GetAttributes[space]; IF mapped THEN BEGIN window: Space.WindowOrigin _ Space.GetWindow[space]; SELECT TRUE FROM window = Space.defaultWindow => BEGIN FindBackingFile: PROC = BEGIN desc: CachedSpace.Desc; validSpace, validSwapUnit: BOOLEAN; [validSpace, validSwapUnit] _ Hierarchy.GetDescriptor[@desc, LOOPHOLE[space]]; IF ~(validSpace OR validSwapUnit) OR desc.dataOrFile ~= data THEN ERROR SnapshotBug; window _ desc.window; END; SpaceImplInternal.EnterSpace[FindBackingFile]; AddSpaceOrChildren[vmScript, space, File.GetAttributes[window.file].type ~= PilotFileTypes.tVMBackingFile]; END; window.file.fID = File.nullID => NULL; -- initial, pinned space File.GetAttributes[window.file ! File.Unknown => CONTINUE].type = PilotFileTypes.tVMBackingFile => BEGIN <> <> <> <> sH: CachedSpace.Handle = LOOPHOLE[space]; entry: SpaceScriptEntryRP = IF window.base = 0 THEN IF PilotSwitches.switches.m = down THEN ERROR SnapshotBug ELSE LOOPHOLE[SIZE[space SpaceScriptEntry]] ELSE FIRST[SpaceScriptEntryRP]; vmScript.base[entry] _ [space[ readOnly: FALSE, ensureBackingStorage: FALSE, level: sH.level, page: sH.page, size: size]]; vmScript.pagesForSpaces _ vmScript.pagesForSpaces + size; END; ENDCASE => <> <> <> <> <> <> <> Space.Deactivate[space]; END ELSE EnumerateChildren[space, ProcessSpaceForVMScript]; END; BuildVMSpaceScript: PROC = BEGIN InitializeSpaceScript[vmScript, initialVMScriptPages]; THROUGH [0..nSpecials) DO [] _ AllocateEntry[vmScript, space] ENDLOOP; EnumerateChildren[Space.virtualMemory, ProcessSpaceForVMScript]; InstallSpaceScript[vmScript]; END; CheckpointVMSpaces: PROC = BEGIN window: Space.WindowOrigin; dbSpaceEntry: SpaceScriptEntryPtr = @vmScript.base[FIRST[SpaceScriptEntryRP]]; deactivate: BOOLEAN; CheckpointOneVMSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN size: Space.PageCount; WITH e: entry SELECT FROM space => {size _ e.size; deactivate _ e.ensureBackingStorage}; subSpace => size _ e.size; mappingSpace => {deactivate _ e.ensureBackingStorage; RETURN}; ENDCASE => RETURN; <> <> IF entry ~= dbSpaceEntry THEN BEGIN space: Space.Handle = SpaceFromScriptEntry[entry]; Space.CopyOut[space: space, window: window]; <> <> <<(or subSpace of a mappingSpace) with ensureBackingStorage = TRUE,>> <> <> <> <> <> IF deactivate THEN Space.Deactivate[space]; END; window.base _ window.base + size; Twiddle[]; END; SpecialSpace.MakeResident[vmScript.space]; vmScript.pinned _ TRUE; vmScript.baseOfSpaces _ snapshotFile.firstFree; EnsureSnapshotFileSize[snapshotFile, vmScript.baseOfSpaces + vmScript.pagesForSpaces]; window _ [snapshotFile.cap, vmScript.baseOfSpaces]; EnumerateSpaceScript[vmScript, CheckpointOneVMSpace]; <> <> <> <> <> <> <> <> <> <> <> <> window _ [snapshotFile.cap, vmScript.baseOfSpaces]; Space.CopyOut[space: SpaceFromScriptEntry[dbSpaceEntry], window: window]; Space.CopyOut[space: SpaceFromScriptEntry[dbSpaceEntry], window: window]; -- no, you aren't seeing double... snapshotFile.firstFree _ snapshotFile.firstFree + vmScript.pagesForSpaces; END; RollbackVMSpaces: PROC = BEGIN window: Space.WindowOrigin _ [snapshotFile.cap, vmScript.baseOfSpaces]; mappingSpace: Space.Handle _ Space.nullHandle; readOnly: BOOLEAN _ FALSE; EnsureWritable: PROC = BEGIN window: Space.WindowOrigin _ Space.GetWindow[mappingSpace]; window.file.permissions _ Inline.BITOR[window.file.permissions, File.write]; Space.MakeWritable[mappingSpace, window.file]; END; EnsureBackingStorage: PROC [space: Space.Handle] = BEGIN <> <> <> <> <> needFile: BOOLEAN _ FALSE; desc: CachedSpace.Desc; EnsureBackingStorageInternal: PROC = BEGIN validSpace, validSwapUnit: BOOLEAN; [validSpace, validSwapUnit] _ Hierarchy.GetDescriptor[@desc, LOOPHOLE[space]]; IF ~(validSpace AND ~validSwapUnit) THEN ERROR SnapshotBug; END; SpaceImplInternal.EnterSpace[EnsureBackingStorageInternal]; [] _ File.GetSize[desc.window.file ! File.Unknown => {needFile _ TRUE; CONTINUE}]; IF ~needFile THEN RETURN; Space.Unmap[mappingSpace ! File.Unknown => -- the maplog fixup wasn't done {MapLog.WriteLog[interval: desc.interval, pSpaceD: NIL]; CONTINUE}]; Space.Map[mappingSpace]; END; RollbackOneVMSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN IF entry.type ~= subSpace AND readOnly THEN <> <> Space.MakeReadOnly[mappingSpace]; WITH e: entry SELECT FROM space => BEGIN space: Space.Handle = LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; IF e.ensureBackingStorage THEN EnsureBackingStorage[space]; IF (readOnly _ e.readOnly) THEN EnsureWritable[]; Space.CopyIn[space: space, window: window]; window.base _ window.base + e.size; END; mappingSpace => BEGIN mappingSpace _ LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; IF e.ensureBackingStorage THEN EnsureBackingStorage[mappingSpace]; IF (readOnly _ e.readOnly) THEN EnsureWritable[]; END; subSpace => BEGIN space: Space.Handle = LOOPHOLE[CachedSpace.Handle[level: e.level, page: e.page]]; Space.CopyIn[space: space, window: window]; SELECT e.disposition FROM <> <> keep => Space.Deactivate[space]; delete => Space.Delete[space]; ENDCASE; window.base _ window.base + e.size; END; end => NULL; ENDCASE; Twiddle[]; END; EnumerateSpaceScript[vmScript, RollbackOneVMSpace]; END; CleanupVMSpaces: PROC [after: CedarSnapshot.After] = BEGIN <> <> IF after = checkpoint THEN BEGIN CleanupOneVMSpace: PROC [entry: SpaceScriptEntryPtr] = BEGIN WITH e: entry SELECT FROM subSpace => IF e.disposition = delete THEN Space.Delete[SpaceFromScriptEntry[entry]]; ENDCASE; END; IF vmScript = NIL THEN RETURN; EnumerateSpaceScript[vmScript, CleanupOneVMSpace]; END; FlushSpaceScript[vmScript]; Heap.systemZone.FREE[@vmScript]; END; <> PLinkScriptEntry: TYPE = LONG ORDERED POINTER TO LinkScriptEntry; LinkScriptEntry: TYPE = RECORD [ frame: PrincOps.GlobalFrameHandle, mth: BcdOps.MTHandle, config: PilotLoadStateOps.ConfigIndex, nLinks: CARDINAL, links: ARRAY [0..0) OF PrincOps.ControlLink]; linkScriptBase, linkScriptNext, linkScriptLimit: PLinkScriptEntry _ NIL; linkScriptSpace: Space.Handle _ Space.nullHandle; nLinkScriptPages: Space.PageCount; nLinkScriptEntries: CARDINAL _ 0; UnSavedLinks: SIGNAL = CODE; InitializeLinkScript: PROC [pages: Space.PageCount] = BEGIN linkScriptSpace _ Space.Create[pages, Space.virtualMemory]; EnsureSnapshotFileSize[snapshotFile, snapshotFile.firstFree + pages]; Space.Map[linkScriptSpace, [snapshotFile.cap, snapshotFile.firstFree]]; linkScriptBase _ linkScriptNext _ LOOPHOLE[Space.LongPointer[linkScriptSpace]]; linkScriptLimit _ linkScriptBase + pages*Environment.wordsPerPage; nLinkScriptPages _ pages; END; ExtendLinkScript: PROC [increment: Space.PageCount _ 1] = BEGIN offset: LONG CARDINAL = linkScriptNext - linkScriptBase; <> Space.Delete[linkScriptSpace]; InitializeLinkScript[nLinkScriptPages + increment]; linkScriptNext _ linkScriptBase + offset; END; FlushLinkScript: PROC = BEGIN <> <> <> <> <> <> <> <> <> <> <> <> IF linkScriptSpace ~= Space.nullHandle THEN Space.Delete[linkScriptSpace]; END; InstallLinkScript: PROC = BEGIN <> <> snapshotFile.firstFree _ snapshotFile.firstFree + nLinkScriptPages; END; CheckpointLinks: PROC = BEGIN OPEN PilotLoadStateOps; ProcessBcd: PROC [config: ConfigIndex] RETURNS [BOOLEAN] = BEGIN bcd: BcdOps.BcdBase _ AcquireBcd[config]; map: Map _ GetMap[config]; ProcessLinks: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOLEAN] = BEGIN OPEN PrincOps, PrincOpsRuntime; rgfi: GFTIndex = map[mth.gfi]; gf: GlobalFrameHandle = GetFrame[GFT[rgfi]]; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> IF ~GetModule[rgfi].resolved AND gf.codelinks THEN BEGIN space: Space.Handle = Space.GetHandle[Space.PageFromLongPointer[ RuntimeInternal.Codebase[LOOPHOLE[gf]]-1]]; <> <> <> <> <> <> IF GetPinnedAndReadOnly[space].pinned THEN SIGNAL UnSavedLinks ELSE BEGIN OPEN PilotLoaderOps; nLinks: CARDINAL _ LinkSegmentLength[mth, bcd]; thisEntrySize: CARDINAL _ SIZE[LinkScriptEntry] + nLinks*SIZE[ControlLink]; UNTIL linkScriptNext + thisEntrySize <= linkScriptLimit DO ExtendLinkScript[ (thisEntrySize + Environment.wordsPerPage - 1)/Environment.wordsPerPage ! UNWIND => {ReleaseMap[map]; ReleaseBcd[bcd]}]; ENDLOOP; linkScriptNext^ _ [frame: gf, mth: mth, config: config, nLinks: nLinks, links: ]; OpenLinkSpace[gf, mth, bcd]; FOR i: CARDINAL IN [0..nLinks) DO linkScriptNext.links[i] _ ReadLink[i]; ENDLOOP; CloseLinkSpace[gf]; linkScriptNext _ linkScriptNext + thisEntrySize; nLinkScriptEntries _ nLinkScriptEntries + 1; Twiddle[]; END; END; RETURN [FALSE] END; [] _ BcdOps.ProcessModules[bcd, ProcessLinks]; ReleaseMap[map]; ReleaseBcd[bcd]; RETURN[FALSE] END; [] _ InputLoadState[]; BEGIN ENABLE UNWIND => ReleaseLoadState[]; InitializeLinkScript[initialLinkScriptPages]; [] _ EnumerateBcds[recentlast, ProcessBcd]; InstallLinkScript[]; END; ReleaseLoadState[]; END; RollbackLinks: PROC = BEGIN OPEN PilotLoadStateOps; lSE: PLinkScriptEntry _ linkScriptBase; config: ConfigIndex _ NullConfig; bcd: BcdOps.BcdBase; THROUGH [0..nLinkScriptEntries) DO OPEN PilotLoaderOps; nLinks: CARDINAL = lSE.nLinks; IF lSE.config ~= config THEN BEGIN IF config ~= NullConfig THEN ReleaseBcd[bcd]; bcd _ PilotLoadStateOps.AcquireBcd[config_lSE.config]; END; IF bcdVersion[config] = bcd.version THEN BEGIN <> OpenLinkSpace[lSE.frame, lSE.mth, bcd]; FOR i: CARDINAL IN [0..nLinks) DO WriteLink[i, lSE.links[i]]; ENDLOOP; CloseLinkSpace[lSE.frame]; END; lSE _ lSE + SIZE[LinkScriptEntry] + nLinks*SIZE[PrincOps.ControlLink]; Twiddle[]; ENDLOOP; IF config ~= NullConfig THEN ReleaseBcd[bcd]; END; CleanupLinks: PROC [after: CedarSnapshot.After] = BEGIN FlushLinkScript[]; END; <> BcdVersionTable: TYPE = ARRAY PilotLoadStateOps.ConfigIndex OF BcdDefs.VersionStamp; bcdVersionSpace: Space.Handle _ Space.nullHandle; bcdVersion: LONG POINTER TO BcdVersionTable _ NIL; BcdOverwritten: SIGNAL = CODE; BuildBcdTable: PROC = BEGIN ProcessBcd: PROC [config: PilotLoadStateOps.ConfigIndex] RETURNS [BOOLEAN] = BEGIN bcd: BcdOps.BcdBase = PilotLoadStateOps.AcquireBcd[config]; bcdVersion[config] _ bcd.version; PilotLoadStateOps.ReleaseBcd[bcd]; <> <> Space.Deactivate[Space.GetHandle[Space.PageFromLongPointer[bcd]]]; RETURN[FALSE] END; pagesForBcdTable: Space.PageCount = (SIZE[BcdVersionTable]+Environment.wordsPerPage-1)/Environment.wordsPerPage; EnsureSnapshotFileSize[snapshotFile, snapshotFile.firstFree + pagesForBcdTable]; bcdVersionSpace _ Space.Create[pagesForBcdTable, Space.virtualMemory]; Space.Map[bcdVersionSpace, [snapshotFile.cap, snapshotFile.firstFree]]; snapshotFile.firstFree _ snapshotFile.firstFree + pagesForBcdTable; bcdVersion _ Space.LongPointer[bcdVersionSpace]; bcdVersion^ _ ALL[BcdDefs.NullVersion]; [] _ PilotLoadStateOps.InputLoadState[]; [] _ PilotLoadStateOps.EnumerateBcds[recentlast, ProcessBcd]; PilotLoadStateOps.ReleaseLoadState[]; END; ValidateBcds: PROC = BEGIN CheckBcd: PROC [config: PilotLoadStateOps.ConfigIndex] RETURNS [BOOLEAN] = BEGIN bcd: BcdOps.BcdBase = PilotLoadStateOps.AcquireBcd[config]; -- If the BCD has completely vanished, touching bcd.version will produce an address fault that can't be satisfied in the File Helper (it will get File.Unknown during GetFileDescriptor). Accordingly, we must test for this case ourselves by hand. file: File.Capability = Space.GetWindow[Space.GetHandle[Space.PageFromLongPointer[bcd]]].file; {ENABLE File.Unknown => GO TO overWritten; [] _ File.GetSize[file]; -- dummy operation; triggers File.Unknown if BCD is gone IF bcdVersion[config] ~= bcd.version THEN GO TO overWritten; EXITS overWritten => SIGNAL BcdOverwritten; }; PilotLoadStateOps.ReleaseBcd[bcd]; RETURN[FALSE] END; [] _ PilotLoadStateOps.InputLoadState[]; [] _ PilotLoadStateOps.EnumerateBcds[recentlast, CheckBcd]; PilotLoadStateOps.ReleaseLoadState[]; END; CleanupBcdTable: PROC [after: CedarSnapshot.After] = BEGIN IF bcdVersionSpace ~= Space.nullHandle THEN Space.Delete[bcdVersionSpace]; END; <> CheckpointSystemVolume: PROC = BEGIN <> <> <> <> <> <> <> <> FlushTempFileListCache: PROC = BEGIN <> <> <> <> <> bogusVolume: System.VolumeID = [System.GetUniversalID[]]; -- can't have been used yet [] _ File.Create[volume: bogusVolume, initialSize: 1, type: PilotFileTypes.tAnonymousFile ! Volume.Unknown => GO TO flushed]; ERROR SnapshotBug; EXITS flushed => NULL; END; WaitForDiskToIdle: PROC = BEGIN <> BEGIN OPEN SubVolumeImpl; FOR cePtr: CePtr _ ceFirst, cePtr + SIZE[CacheEntry] WHILE cePtr <= ceLast DO IF cePtr.occupied THEN BEGIN DiskChannel.Idle[cePtr.svDesc.channel]; DiskChannel.Restart[cePtr.svDesc.channel]; END; ENDLOOP; END; END; FlushTempFileListCache[]; WaitForDiskToIdle[]; <> <> <> <> <> <> <> <> <> <> VolumeImplInterface.SubvolumeOnline[lvID: systemVolume, root: TRUE]; LogicalVolume.CloseLogicalVolume[@systemVolume]; VolumeImplInterface.SubvolumeOffline[lvID: systemVolume, root: TRUE]; END; RollbackSystemVolume: PROC = BEGIN [] _ LogicalVolume.OpenLogicalVolume[@systemVolume]; END; CleanupSystemVolume: PROC = BEGIN [] _ LogicalVolume.OpenLogicalVolume[@systemVolume]; END; <> OutLoad: PROC RETURNS [inLoaded: BOOLEAN] = BEGIN <> <> <> psb: PSB.PsbHandle; ptc: CARDINAL; wdc: CARDINAL; <> ProcessInternal.DisableInterrupts[]; -- make it hold still first psb _ ProcessOperations.ReadPSB[]; ptc _ ProcessOperations.ReadPTC[]; wdc _ ProcessOperations.ReadWDC[]; DeviceCleanup.Perform[turnOff]; -- turn all devices off. <> <> inLoaded _ BootSwap.OutLoad[@outloadLocation, restore] ~= outLoaded; IF inLoaded THEN BEGIN <> ProcessOperations.WriteWDC[wdc]; ProcessOperations.WritePTC[ptc]; ProcessOperations.WritePSB[psb]; <> -- We must do it with interrupts off or Communication will be using the Ethernet-- <> [] _ TemporarySetGMT.SetGMT[]; END; DeviceCleanup.Perform[turnOn]; -- turn devices back on ProcessorFace.SetMP[PilotMP.cClient]; -- announce our return ProcessInternal.EnableInterrupts[]; END; <> RationalizeDirectory: PROC = BEGIN FOR i: CARDINAL IN [1 .. DirectoryFiles.maxDCache] DO dCE: LONG POINTER TO DirectoryFiles.DCEntry = @DirectoryFilesImpl.directoryCache[i]; IF dCE.refCount > 0 THEN BEGIN <> <> <> window: Space.WindowOrigin = Space.GetWindow[dCE.dir.space]; dirFileSize: Space.PageCount = Inline.LowHalf[File.GetSize[window.file]]; directoryOrigin: DirectoryInternal.DirectoryPageHandle = Space.LongPointer[dCE.dir.space]; Space.Unmap[dCE.dir.space]; Space.Map[dCE.dir.space, window]; dCE.dir.top _ directoryOrigin.top; dCE.dir.size _ dirFileSize - DirectoryInternal.leaderPageSize; Twiddle[]; END; ENDLOOP; END; <> GetPinnedAndReadOnly: PROC [space: Space.Handle] RETURNS [pinned, readOnly: BOOLEAN] = BEGIN GetPinnedAndReadOnlyInternal: PROC = BEGIN desc: CachedSpace.Desc; validSpace, validSwapUnit: BOOLEAN; [validSpace, validSwapUnit] _ Hierarchy.GetDescriptor[@desc, LOOPHOLE[space]]; IF ~(validSpace OR validSwapUnit) THEN ERROR SnapshotBug; <> pinned _ desc.pinned; readOnly _ desc.writeProtected; END; SpaceImplInternal.EnterSpace[GetPinnedAndReadOnlyInternal]; END; Cleanup: PROC [after: CedarSnapshot.After] = BEGIN UncorkRestOfWorld[corkHandle]; CleanupBcdTable[after]; CleanupLinks[after]; CleanupVMSpaces[after]; CleanupClientSpaces[after]; END; GetGermSwitches: PROC RETURNS [TemporaryBooting.Switches] = BEGIN OPEN BootSwap; RETURN[LOOPHOLE[Boot.LP[highbits: mdsiGerm, lowbits: @SDDefs.SD[sPilotSwitches]], LONG POINTER TO TemporaryBooting.Switches]^] END; ValidateMicrocodeAndUser: PROC = BEGIN OPEN UserCredentialsUnsafe; TurnOnInitialTerminal: PROC RETURNS [g: GetProc, p: PutProc] = { [g, p] _ SpecialTerminal.TurnOn[]; }; TurnOffInitialTerminal: PROC = {SpecialTerminal.TurnOff[]}; CedarInitOps.EnsureUsableMicrocode[]; Login[TurnOnInitialTerminal, TurnOffInitialTerminal]; END; CallRollbackProcs: PROC [after: CedarSnapshot.After] = BEGIN DoOneRollbackProc: PROC [proc: CedarSnapshot.RollbackProc] = {proc[after]}; EnumerateRollbackProcs[DoOneRollbackProc]; END; <<*** Here's the real work ***>> corkHandle: CorkHandle _ NIL; checkpointProcsCalled: BOOL _ FALSE; switches: TemporaryBooting.Switches; InitializeFeedback[]; outcome _ checkpointed; IF volume = Volume.nullID THEN volume _ systemVolume; BEGIN ENABLE Volume.InsufficientSpace => {outcome _ insufficientDiskSpace; GO TO cantCheckpoint}; SetPhase[0, checkpoint]; snapshotFile _ InitializeSnapshotFile[volume]; <> SetPhase[1, checkpoint]; BuildClientSpaceScript[]; checkpointProcsCalled _ TRUE; <> corkHandle _ CorkRestOfWorld[]; <> SetPhase[2, checkpoint]; IF TransactionExtras.TransactionsInProgress[] THEN {outcome _ transactionsInProgress; GO TO cantCheckpoint}; <> SetPhase[3, checkpoint]; CheckpointLinks[ ! UnSavedLinks => {outcome _ potentiallyDangerousCodeLinks; RESUME}]; <> SetPhase[4, checkpoint]; BuildVMSpaceScript[]; <> SetPhase[5, checkpoint]; CheckpointClientSpaces[]; <> SetPhase[6, checkpoint]; BuildBcdTable[]; <> SetPhase[7, checkpoint]; CheckpointVMSpaces[]; <> SetPhase[8, checkpoint]; outloadLocation _ PrepareForOutload[snapshotFile]; EXITS cantCheckpoint => { Cleanup[checkpoint]; FinalizeSnapshotFile[snapshotFile, clear]; IF checkpointProcsCalled THEN CallRollbackProcs[checkpoint]; FinalizeFeedback[]; RETURN}; END; <> SetPhase[9, checkpoint]; CheckpointSystemVolume[]; <> SetPhase[10, checkpoint]; TerminalMultiplex.PreventDebuggerSwaps[]; IF ~OutLoad[].inLoaded THEN BEGIN TerminalMultiplex.PermitDebuggerSwaps[]; SetPhase[11, checkpoint]; CleanupSystemVolume[]; SetPhase[12, checkpoint]; InstallSnapshotFile[snapshotFile, outloadLocation]; -- now Rollback will work SetPhase[13, checkpoint]; Cleanup[checkpoint]; FinalizeSnapshotFile[snapshotFile, keep]; SetPhase[14, checkpoint]; CallRollbackProcs[checkpoint]; FinalizeFeedback[]; RETURN END; <> outcome _ rolledBack; <> switches _ GetGermSwitches[]; IF switches.i = down THEN RuntimeInternal.WorryCallDebugger["Rollback KeyStop"L]; <> SetPhase[0, rollback]; RollbackSystemVolume[]; <> SetPhase[1, rollback]; RollbackVMSpaces[]; <> SetPhase[2, rollback]; ValidateBcds[ ! BcdOverwritten => {outcome _ overwrittenBcd; RESUME}]; <> SetPhase[3, rollback]; RollbackLinks[]; <> SetPhase[4, rollback]; RollbackClientSpaces[]; <> <> SetPhase[5, rollback]; RationalizeDirectory[]; <> SetPhase[6, rollback]; TransactionExtras.DoCrashRecovery[]; <> SetPhase[7, rollback]; Cleanup[rollback]; FinalizeSnapshotFile[snapshotFile, keep]; <> SetPhase[8, rollback]; ValidateMicrocodeAndUser[]; TerminalMultiplex.PermitDebuggerSwaps[]; <> SetPhase[9, rollback]; CallRollbackProcs[rollback]; FinalizeFeedback[]; END; END.