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 ];
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 {
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 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[];
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[];
};
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]];
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.
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];
};
}.