Cedar Nucleus (Files): per-file operations, locking file header data structures
BootingImpl.mesa
Andrew Birrell September 20, 1983 3:35 pm
DIRECTORY
BootFile USING[ Location, MemorySizeToFileSize ],
Booting USING[ Bootee, BootFileNumber, CheckpointProc, MicrocodeType, RollbackProc, Switches ],
DeviceCleanup USING[ Perform ],
File USING[ Create, Error, Info, FP, GetRoot, Handle, Open, nullFP, nullVolumeID, PageCount, SetRoot, SetSize, SystemVolume, Volume, VolumeFile, VolumeID ],
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[ PsbHandle ],
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, FileInternal, GermSwap, MicrocodeBooting, PhysicalVolume, ProcessorFace, PrincOpsUtils, Rope, VMBacking, VMSideDoor, VMStatistics
EXPORTS Booting =
BEGIN
******** Exports to Booting interface ********
switches: PUBLIC Booting.Switches ← GermSwap.switches;
--Booting.--GetBootFileNumber: PUBLIC PROC[type: Booting.MicrocodeType] RETURNS[Booting.BootFileNumber] = TRUSTED
{ RETURN[ MicrocodeBooting.GetBootFileNumber[type] ] };
--Booting.--Boot: PUBLIC PROC[
boot: Booting.Bootee,
switches: Booting.Switches,
outload: Booting.Bootee ← [noOp[]] ]
RETURNS[rejection: Rope.ROPENIL] = TRUSTED
BEGIN
sys: File.Volume = File.SystemVolume[];
GetLocation: PROC[from: Booting.Bootee] RETURNS[loc: BootFile.Location] = TRUSTED
BEGIN
WITH b: from SELECT FROM
noOp => NULL;
file =>
loc ← FileInternal.GetFileLocation[b.file, b.firstPage];
logical =>
BEGIN
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];
END;
physical =>
BEGIN
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 BEGIN
loc ← FileInternal.GetPhysicalLocation[p, b.root];
IF loc.diskFileID = VolumeFormat.nullDiskFileID
THEN ERROR File.Error[unknownFile];
EXIT
END;
REPEAT FINISHED => ERROR File.Error[unknownFile]
ENDLOOP;
END;
microcode => NULL;
bootButton => NULL;
self =>
loc ← GermSwap.bootedFrom.location;
ENDCASE;
END;
outloadWanted: BOOL ← outload.type # noOp;
outloadLoc, inloadLoc: BootFile.Location;
IF NOT outloadWanted AND sys # NIL AND File.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];
END;
inloadedOnce: PUBLIC BOOLFALSE;
BootFromLocation: ENTRY PROC[
outloadLoc: POINTER TO BootFile.Location ← NIL,
boot: RECORD[SELECT t: * FROM
noOp => NULL,
inload => [inloadLoc: POINTER TO BootFile.Location],
bootButton => NULL,
microcode => [bfn: Booting.BootFileNumber],
ENDCASE],
switches: Booting.Switches
] RETURNS[rejection: Rope.ROPENIL] = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
self: BootFile.Location ← GermSwap.bootedFrom.location;
inloaded: BOOLFALSE;
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 BEGIN
psb: PrincOps.PsbHandle; ptc: CARDINAL; wdc: CARDINAL; -- should be in outload file?
psb ← PrincOpsUtils.ReadPSB[];
ptc ← PrincOpsUtils.ReadPTC[];
wdc ← PrincOpsUtils.ReadWDC[];
nww ← PrincOpsUtils.ReadNWW[];
inloaded ← GermSwap.OutLoad[outloadLoc, restore] # outLoaded;
PrincOpsUtils.WriteNWW[Basics.BitOR[nww, PrincOpsUtils.ReadNWW[]]];
PrincOpsUtils.WriteWDC[wdc];
PrincOpsUtils.WritePTC[ptc];
PrincOpsUtils.WritePSB[psb];
END;
IF NOT inloaded
THEN BEGIN
WITH b: boot SELECT FROM
noOp => NULL;
bootButton => ProcessorFace.BootButton[--switches--];
microcode => MicrocodeBooting.BootSpecificMicrocode[b.bfn--, switches--];
inload =>
BEGIN
VMBacking.RecoverRealMemory[]; -- so that the germ can find it for the next guy.
DeviceCleanup.Perform[disconnect];
GermSwap.InLoad[NIL, NIL, 0, b.inloadLoc, switches];
END;
ENDCASE => ERROR;
Can't get here
END;
IF inloadedOnce
THEN BEGIN
Inloaded twice: VM backing file has been disturbed by a previous inload, so re-boot
newSwitches: Booting.Switches ← ALL[FALSE];
newSwitches[w] ← TRUE;
GermSwap.InLoad[NIL, NIL, 0,@self, newSwitches];
END;
DeviceCleanup.Perform[turnOn];
ProcessorFace.SetMP[MPCodes.emptyMP];
PrincOpsUtils.EnableInterrupts[];
IF outloadLoc # NIL
THEN CallRollbackProcs[];
END;
******** Checkpoint facilities ********
ProcsRec: TYPE = RECORD[
c: Booting.CheckpointProc,
r: Booting.RollbackProc,
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, clientData: REF ANYNIL] =
BEGIN
ENABLE UNWIND => NULL;
new: REF ProcsRec = NEW[ProcsRec ← [c: c, r: r, clientData: clientData]];
Add to head of CheckpointProcs and tail of RollbackProcs
IF rollbacks = NIL THEN rollbacks ← new;
IF checkpoints # NIL THEN checkpoints.nextR ← new;
new.nextC ← checkpoints; checkpoints ← new;
END;
Deregister: PUBLIC ENTRY PROC[c: Booting.CheckpointProc ← NIL, r: Booting.RollbackProc ← NIL] =
BEGIN
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
THEN BEGIN
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
END;
prev ← this;
ENDLOOP;
END;
CallCheckpointProcs: INTERNAL PROC RETURNS[rejection: Rope.ROPENIL] = TRUSTED
BEGIN
FOR this: REF ProcsRec ← checkpoints, this.nextC UNTIL this = NIL
DO IF this.c # NIL
THEN BEGIN
rejection ← (this.c)[this.clientData];
IF rejection.Length[] = 0
THEN rejection ← NIL
ELSE BEGIN -- 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
END;
END;
ENDLOOP;
END;
CallRollbackProcs: INTERNAL PROC = TRUSTED
BEGIN
FOR this: REF ProcsRec ← rollbacks, this.nextR UNTIL this = NIL
DO IF this.r # NIL THEN (this.r)[this.clientData] ENDLOOP;
END;
Checkpoint: PUBLIC PROC RETURNS[rejection: Rope.ROPENIL] =
BEGIN
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]]
]]
END;
VerifyFile: PROC[volume: File.Volume, which: File.VolumeFile] = TRUSTED
BEGIN
size: File.PageCount ← BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+256;
oldFP: File.FP = File.GetRoot[volume, which].fp;
new: File.Handle;
BEGIN -- 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 + 256; -- guess for checkpoint file overhead
END;
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];
File.SetRoot[which, new];
END;
END.