<> <> <> <> <> <> <<>> DIRECTORY BootFile USING [ Location, MemorySizeToFileSize ], Booting USING [ Bootee, BootFileNumber, CheckpointProc, MicrocodeType, RollbackProc, Switches ], DeviceCleanup USING [ Perform ], File USING [ Create, Error, Info, FP, Handle, Open, nullFP, nullVolumeID, PageCount, SetSize, SystemVolume, Volume, VolumeFile, VolumeID ], FileBackdoor USING [ GetRoot, SetRoot], FileInternal USING [ GetFileLocation, GetLogicalLocation, GetPhysicalLocation ], GermSwap USING [ bootedFrom, InLoad, OutLoad, switches ], MicrocodeBooting USING [ GetBootFileNumber, BootSpecificMicrocode ], MPCodes USING [ emptyMP ], PhysicalVolume USING [ GetPhysical, NextPhysical, Physical, PhysicalInfo ], ProcessorFace USING [ BootButton, SetMP ], PrincOps USING [ maxPagesInVM, PageValue, PsbHandle, wordsPerPage ], PrincOpsUtils USING [ DisableInterrupts, EnableInterrupts, ReadPSB, ReadPTC, ReadWDC, WritePSB, WritePTC, WriteWDC ], Rope USING [ Length, ROPE ], VM USING [ VMPartition ], VMBacking USING [ RecoverRealMemory ], VMSideDoor USING [ rmPages ], VMStatistics USING [ VirtualAllocation ], VolumeFormat USING [ nullDiskFileID ]; BootingImpl: CEDAR MONITOR IMPORTS BootFile, DeviceCleanup, File, FileBackdoor, FileInternal, GermSwap, MicrocodeBooting, PhysicalVolume, ProcessorFace, PrincOpsUtils, Rope, VMBacking, VMSideDoor, VMStatistics EXPORTS Booting = { pagesInMap: INT = PrincOps.maxPagesInVM / (PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]); <<# of pages in the VM map that exists inside of the inload/outload file>> <<******** Exports to Booting interface ********>> switches: PUBLIC Booting.Switches _ GermSwap.switches; GetBootFileNumber: PUBLIC PROC [type: Booting.MicrocodeType] RETURNS [Booting.BootFileNumber] = TRUSTED { RETURN[ MicrocodeBooting.GetBootFileNumber[type] ]; }; Boot: PUBLIC PROC [boot: Booting.Bootee, switches: Booting.Switches, outload: Booting.Bootee _ [noOp[]] ] RETURNS [rejection: Rope.ROPE _ NIL] = TRUSTED { sys: File.Volume = File.SystemVolume[]; GetLocation: PROC [from: Booting.Bootee] RETURNS [loc: BootFile.Location] = TRUSTED { WITH b: from SELECT FROM noOp => NULL; file => loc _ FileInternal.GetFileLocation[b.file, b.firstPage]; logical => { v: File.Volume = IF b.volume = NIL THEN sys ELSE b.volume; IF v = NIL THEN ERROR File.Error[unknownFile]; loc _ FileInternal.GetLogicalLocation[v, b.root]; IF loc.diskFileID = VolumeFormat.nullDiskFileID THEN ERROR File.Error[unknownFile]; }; physical => { id: File.VolumeID = IF b.volume = File.nullVolumeID AND sys # NIL THEN PhysicalVolume.PhysicalInfo[PhysicalVolume.GetPhysical[sys]].id ELSE b.volume; FOR p: PhysicalVolume.Physical _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO IF PhysicalVolume.PhysicalInfo[p].id = id THEN { loc _ FileInternal.GetPhysicalLocation[p, b.root]; IF loc.diskFileID = VolumeFormat.nullDiskFileID THEN ERROR File.Error[unknownFile]; EXIT }; REPEAT FINISHED => ERROR File.Error[unknownFile] ENDLOOP; }; microcode => NULL; bootButton => NULL; self => loc _ GermSwap.bootedFrom.location; ENDCASE; }; outloadWanted: BOOL _ outload.type # noOp; outloadLoc, inloadLoc: BootFile.Location; IF (rejection _ CallBootingProcs[]) # NIL THEN RETURN; IF NOT outloadWanted AND sys # NIL AND FileBackdoor.GetRoot[sys, debugger].fp # File.nullFP THEN { outloadWanted _ TRUE; outload _ [logical[sys, debugger]]; }; IF outloadWanted THEN outloadLoc _ GetLocation[outload]; inloadLoc _ GetLocation[boot]; rejection _ BootFromLocation[ outloadLoc: IF outloadWanted THEN @outloadLoc ELSE NIL, boot: WITH b: boot SELECT FROM noOp => [noOp[]], bootButton => [bootButton[]], microcode => [microcode[b.bfn]], ENDCASE => [inload[@inloadLoc]], switches: switches]; }; CallBootingProcs: PUBLIC ENTRY PROC RETURNS [rejection: Rope.ROPE _ NIL] = TRUSTED { <> FOR this: REF ProcsRec _ checkpoints, this.nextC UNTIL this = NIL DO IF this.b # NIL THEN { rejection _ (this.b)[this.clientData]; IF rejection.Length[] # 0 THEN RETURN; }; ENDLOOP; rejection _ NIL; }; inloadedOnce: PUBLIC BOOL _ FALSE; BootRecord: TYPE = RECORD[SELECT t: * FROM noOp => NULL, inload => [inloadLoc: POINTER TO BootFile.Location], bootButton => NULL, microcode => [bfn: Booting.BootFileNumber], ENDCASE]; BootFromLocation: ENTRY PROC [outloadLoc: POINTER TO BootFile.Location _ NIL, boot: BootRecord, switches: Booting.Switches] RETURNS [rejection: Rope.ROPE _ NIL] = TRUSTED { ENABLE UNWIND => NULL; self: BootFile.Location _ GermSwap.bootedFrom.location; inloaded: BOOL _ FALSE; IF outloadLoc # NIL THEN { rejection _ CallCheckpointProcs[]; IF rejection # NIL THEN RETURN }; inloadedOnce _ FALSE; PrincOpsUtils.DisableInterrupts[]; -- make it hold still first DeviceCleanup.Perform[turnOff]; IF outloadLoc # NIL THEN { psb: PrincOps.PsbHandle; ptc: CARDINAL; wdc: CARDINAL; -- should be in outload file? psb _ PrincOpsUtils.ReadPSB[]; ptc _ PrincOpsUtils.ReadPTC[]; wdc _ PrincOpsUtils.ReadWDC[]; <> inloaded _ GermSwap.OutLoad[outloadLoc, restore] # outLoaded; <> PrincOpsUtils.WriteWDC[wdc]; PrincOpsUtils.WritePTC[ptc]; PrincOpsUtils.WritePSB[psb]; }; IF NOT inloaded THEN { WITH b: boot SELECT FROM noOp => NULL; bootButton => ProcessorFace.BootButton[--switches--]; microcode => MicrocodeBooting.BootSpecificMicrocode[b.bfn--, switches--]; inload => { VMBacking.RecoverRealMemory[]; -- so that the germ can find it for the next guy. DeviceCleanup.Perform[disconnect]; GermSwap.InLoad[NIL, NIL, 0, b.inloadLoc, switches]; }; ENDCASE => ERROR; <> }; IF inloadedOnce THEN { <> newSwitches: Booting.Switches _ ALL[FALSE]; newSwitches[w] _ TRUE; GermSwap.InLoad[NIL, NIL, 0,@self, newSwitches]; }; DeviceCleanup.Perform[turnOn]; ProcessorFace.SetMP[MPCodes.emptyMP]; PrincOpsUtils.EnableInterrupts[]; IF outloadLoc # NIL THEN CallRollbackProcs[]; }; <<******** Checkpoint facilities ********>> <<>> ProcsRec: TYPE = RECORD[ c: Booting.CheckpointProc, r: Booting.RollbackProc, b: Booting.CheckpointProc, clientData: REF ANY, nextC: REF ProcsRec _ NIL, nextR: REF ProcsRec _ NIL]; checkpoints: REF ProcsRec _ NIL; -- one end of a two-way linked list rollbacks: REF ProcsRec _ NIL; -- other end of that list RegisterProcs: PUBLIC ENTRY PROC [c: Booting.CheckpointProc _ NIL, r: Booting.RollbackProc _ NIL, b: Booting.CheckpointProc _ NIL, clientData: REF ANY _ NIL] = { ENABLE UNWIND => NULL; new: REF ProcsRec = NEW[ProcsRec _ [c: c, r: r, b: b, clientData: clientData]]; <> IF rollbacks = NIL THEN rollbacks _ new; IF checkpoints # NIL THEN checkpoints.nextR _ new; new.nextC _ checkpoints; checkpoints _ new; }; Deregister: PUBLIC ENTRY PROC [c: Booting.CheckpointProc _ NIL, r: Booting.RollbackProc _ NIL, b: Booting.CheckpointProc _ NIL] = { ENABLE UNWIND => NULL; prev: REF ProcsRec _ NIL; FOR this: REF ProcsRec _ checkpoints, this.nextC UNTIL this = NIL DO IF this.c = c AND this.r = r AND this.b = b THEN { IF this.nextR = NIL THEN checkpoints _ this.nextC ELSE this.nextR.nextC _ this.nextC; IF this.nextC = NIL THEN rollbacks _ this.nextR ELSE this.nextC.nextR _ this.nextR; EXIT }; prev _ this; ENDLOOP; }; CallCheckpointProcs: INTERNAL PROC RETURNS [rejection: Rope.ROPE _ NIL] = TRUSTED { FOR this: REF ProcsRec _ checkpoints, this.nextC UNTIL this = NIL DO IF this.c # NIL THEN { rejection _ (this.c)[this.clientData]; IF rejection.Length[] = 0 THEN rejection _ NIL ELSE { -- undo the damage caused so far ... FOR undo: REF ProcsRec _ this.nextR, undo.nextR UNTIL undo = NIL DO IF undo.c # NIL AND undo.r # NIL THEN (undo.r)[this.clientData] ENDLOOP; RETURN }; }; ENDLOOP; }; CallRollbackProcs: INTERNAL PROC = TRUSTED { FOR this: REF ProcsRec _ rollbacks, this.nextR UNTIL this = NIL DO IF this.r # NIL THEN (this.r)[this.clientData]; ENDLOOP; }; Checkpoint: PUBLIC PROC RETURNS [rejection: Rope.ROPE _ NIL] = { mySwitches: Booting.Switches _ ALL[FALSE]; mySwitches[v] _ TRUE; -- => copy VM for checkpoint VerifyFile[File.SystemVolume[], checkpoint]; RETURN[Boot[ boot: [self[]], switches: mySwitches, outload: [logical[root: checkpoint]] ]] }; VerifyFile: PROC [volume: File.Volume, which: File.VolumeFile] = TRUSTED { size: File.PageCount _ BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+256; oldFP: File.FP = FileBackdoor.GetRoot[volume, which].fp; new: File.Handle; { -- try to guess the correct size, so our FS will do volume flushing size _ size - VMSideDoor.rmPages; -- size _ boot file overhead FOR p: VM.VMPartition IN VM.VMPartition DO pagesAllocated, pagesFreed, pagesInPartition: INT; [pagesAllocated, pagesFreed, pagesInPartition] _ VMStatistics.VirtualAllocation[p]; size _ size + (pagesAllocated-pagesFreed); ENDLOOP; size _ size + pagesInMap + 2; -- guess for checkpoint file overhead }; new _ File.Open[volume, oldFP ! File.Error => { new _ NIL; CONTINUE} ]; IF new = NIL THEN new _ File.Create[volume, size]; IF File.Info[new].size < size THEN File.SetSize[new, size]; FileBackdoor.SetRoot[which, new]; }; }.