BootingImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Cedar Nucleus (Files): per-file operations, locking file header data structures
Andrew Birrell September 20, 1983 3:35 pm
Russ Atkinson February 20, 1985 2:58:08 pm PST
Bob Hagmann January 31, 1985 11:26:52 am PST
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.ROPENIL] = 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.ROPENIL] = TRUSTED {
Calls the booting procs to determine if there is any reason to NOT boot. NIL is returned if it is OK to boot. The booting procs should perform no significant state changes (forcing flushes of files is OK, for example).
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 BOOLFALSE;
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.ROPENIL] = TRUSTED {
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 {
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];
};
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;
Can't get here
};
IF inloadedOnce THEN {
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];
};
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 ANYNIL] = {
ENABLE UNWIND => NULL;
new: REF ProcsRec = NEW[ProcsRec ← [c: c, r: r, b: b, 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;
};
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.ROPENIL] = 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.ROPENIL] = {
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];
};
}.