CedarInitBasement.mesa
last edited by Levin on February 18, 1983 2:34 pm
DIRECTORY
CedarInitPrivate USING [ErrorCode],
CedarVersion USING [MachineType],
Directory USING [CreateSystemDirectory, Error, InsertFile, Lookup],
File USING [Capability, MakePermanent, nullCapability, PageCount, Type],
FileInternal USING [Descriptor, maxPermissions, PageGroup],
FileImpl USING [TmpsEnter, LOCK],
Format USING [Date, LongString, StringProc],
Heap USING [systemZone],
KernelFile USING [Pin],
LogicalVolume USING [Handle, PageNumber, VolumeAccess, VolumeAccessProc],
LongString USING [AppendString],
MicrocodeVersion USING [MachineType, VERSION, VersionResult],
Process USING [Pause, Ticks],
ProcessOperations USING [Enter, Exit],
ProcessorFace USING [SetMP],
PupDefs USING [PupPackageMake],
System USING [GetUniversalID, gmtEpoch, GreenwichMeanTime, SecondsSinceEpoch],
VolAllocMap USING [AccessVAM, AllocPageGroup],
VolFileMap USING [InsertPageGroup],
Volume USING [ID, PageCount, systemID];
CedarInitBasement: PROGRAM
IMPORTS
Directory, File, FileImpl, Format, Heap, KernelFile, LogicalVolume,
LongString, MicrocodeVersion, Process, ProcessOperations, ProcessorFace,
PupDefs, System, VolAllocMap, VolFileMap, Volume
EXPORTS CedarInitPrivate, CedarVersion
SHARES File, FileImpl =
BEGIN
This module must execute before initialization of safe storage occurs.
-- Exports to CedarVersion --
machineType: PUBLIC CedarVersion.MachineType;
uCodeDate: PUBLIC System.GreenwichMeanTime;
uCodeVersion: PUBLIC CARDINAL;
uCodeFloatingPoint: PUBLIC BOOLEAN;
uCodeCedar: PUBLIC BOOLEAN;
-- Exports to CedarInitPrivate --
FatalError: PUBLIC PROC [code: CedarInitPrivate.ErrorCode] = {
ProcessorFace.SetMP[LOOPHOLE[code]];
DO Process.Pause[LAST[Process.Ticks]] ENDLOOP;
watchers in TerminalMultiplexImpl can still get us to the debugger...
};
ValidateMicrocode: PUBLIC PROC RETURNS [error: LONG STRINGNIL] = {
DMachines: TYPE = MicrocodeVersion.MachineType[dolphin..dicentra];
Append: Format.StringProc = {
IF error = NIL OR error.length + s.length > error.maxlength THEN {
oldSize: CARDINAL = IF error = NIL THEN 0 ELSE error.length;
newError: LONG STRING ← Heap.systemZone.NEW[StringBody[3*(oldSize+s.length)/2]];
IF error ~= NIL THEN {
LongString.AppendString[newError, error];
Heap.systemZone.FREE[@error];
};
error ← newError;
};
LongString.AppendString[error, s];
};
The following must be altered when incompatible microcode changes occur:
expectedVersion: CARDINAL = 0;
The release date returned by the microcode is relative to the local time zone in effect at the point of release. If we blindly converted it to GMT, subsequent conversions back to local time would cause the date to be decremented by 1, assuming that the microcode was released somewhere in the western hemisphere. To eliminate this, we "define" microcode releases to occur at Greenwich noon, which will force the same apparent release date anywhere in the world.
The following limit the compatible versions (warning: can't call DateAndTime yet):
vintages: ARRAY DMachines OF System.GreenwichMeanTime =
[[23102203300B], -- 12-May-82 1200 GMT
[23234170100B], -- 9-Feb-83 1200 GMT
System.gmtEpoch,
System.gmtEpoch];
v: MicrocodeVersion.VersionResult = MicrocodeVersion.VERSION[];
machineType ← LOOPHOLE[v.machineType];
uCodeVersion ← v.majorVersion;
uCodeDate ← [(LONG[v.releaseDate]*24+--noon--12)*60*60];
uCodeFloatingPoint ← v.floatingPoint;
uCodeCedar ← v.cedar;
SELECT TRUE FROM
~(v.machineType IN DMachines) =>
Format.LongString["This isn't a D-machine."L, Append];
uCodeVersion ~= expectedVersion =>
Format.LongString["The microcode and boot file are incompatible."L, Append];
System.SecondsSinceEpoch[vintages[v.machineType]] > System.SecondsSinceEpoch[uCodeDate] => {
Format.LongString["The microcode (of "L, Append];
Format.Date[uCodeDate, dateOnly, Append];
Format.LongString[") is too old ("L, Append];
Format.Date[vintages[v.machineType], dateOnly, Append];
Format.LongString[" is the earliest acceptable version)."L, Append];
};
ENDCASE => NULL;
};
-- VM backing file logic --
EnsureCedarVMBackingFile: PROC = {
vmFileName: STRING = "CedarVM.DontDeleteMe"L;
vmFile: File.Capability ← File.nullCapability;
filePages: File.PageCount;
volID: Volume.ID ← Volume.systemID;
CreateVMBackingFile: PROC [filePages: File.PageCount] RETURNS [file: File.Capability] = {
backingFileType: File.Type = [500];
fileD: FileInternal.Descriptor;
MakeVMBackingFile: LogicalVolume.VolumeAccessProc = {
runThreshold: Volume.PageCount = 128;
group: FileInternal.PageGroup ← [filePage: 0, volumePage: , nextFilePage: ];
WHILE filePages > 0 DO
size: Volume.PageCount;
[group.volumePage, size] ← FindDiskHole[volume, filePages];
IF size < runThreshold THEN EXIT; -- terminal fragmentation has set in; give up
group.nextFilePage ← group.filePage + size;
VolAllocMap.AllocPageGroup[volume, @fileD, @group, group.filePage = 0];
IF group.filePage + size ~= group.nextFilePage THEN FatalError[implementationBug];
VolFileMap.InsertPageGroup[volume, @fileD, @group];
group.filePage ← group.nextFilePage;
filePages ← filePages - size;
ENDLOOP;
RETURN[FALSE]
};
file ← [[System.GetUniversalID[]], FileInternal.maxPermissions];
FileImpl.TmpsEnter[@file, @volID];
fileD ← [file.fID, Volume.systemID, local[FALSE, TRUE, 0, backingFileType]];
WHILE ~ProcessOperations.Enter[@FileImpl.LOCK] DO NULL ENDLOOP;
IF LogicalVolume.VolumeAccess[@volID, MakeVMBackingFile, TRUE] ~= ok THEN
FatalError[implementationBug];
ProcessOperations.Exit[@FileImpl.LOCK];
};
SELECT machineType FROM
dorado => filePages ← 6000;
dolphin => filePages ← 5000; -- is this plausible?
dandelion => filePages ← 5000; -- is this plausible?
ENDCASE => FatalError[implementationBug];
vmFile ← Directory.Lookup[vmFileName ! Directory.Error => CONTINUE];
IF vmFile = File.nullCapability AND
(vmFile ← CreateVMBackingFile[filePages]) ~= File.nullCapability THEN {
Directory.InsertFile[vmFileName, vmFile];
File.MakePermanent[vmFile];
};
IF vmFile ~= File.nullCapability THEN KernelFile.Pin[vmFile];
};
FindDiskHole: PROC [volume: LogicalVolume.Handle, nPages: Volume.PageCount]
RETURNS [page: LogicalVolume.PageNumber, size: Volume.PageCount] = {
This procedure is expected to be invoked from a LogicalVolume.VolumeAccessProc, to ensure appropriate mutual exclusion in access to the volume Handle. It never alters anything, either in the volume Handle or in the VAM. FindDiskHole attempts to find a (contiguous) run of free disk pages of length nPages. It returns the largest run of size less than or equal to nPages that it actually found. (This code was adapted from DiskFindHole.bcpl(!))
State: TYPE = --LONG-- POINTER TO StateObject;
StateObject: TYPE = RECORD [
base: LogicalVolume.PageNumber,
largestHoleStart: LogicalVolume.PageNumber,
largestHoleSize: Volume.PageCount,
maxPages: Volume.PageCount];
PageInUse: PROC [page: LogicalVolume.PageNumber] RETURNS [BOOL] = INLINE
{RETURN[VolAllocMap.AccessVAM[volume, page, FALSE, FALSE]]};
FindNextHole: PROC [nPages: File.PageCount, state: State]
RETURNS [found: BOOL] = {
Attempts to find a contiguous hole nPages long beginning at a page greater than or equal to state.base. If successful, returns TRUE and updates state.base to point to the start of the hole. If unsuccessful, returns FALSE, leaves state.base unchanges, and sets state.largestHoleStart and state.largestHoleSize to indicate the largest hole actually encountered during the scan (which, however, may not be the largest hole present in the region scanned).
IF nPages <= state.maxPages THEN {
base, exBase: LogicalVolume.PageNumber ← state.base;
lastPage: LogicalVolume.PageNumber ← state.maxPages - nPages;
UNTIL base > lastPage DO
Invariants:
exBase >= base
[base..exBase) have been searched exhaustively and are known to be free
top: LogicalVolume.PageNumber = base + nPages - 1;
FOR page: LogicalVolume.PageNumber DECREASING IN [exBase..top] DO
IF PageInUse[page] THEN {
IF top - page > state.largestHoleSize THEN {
state.largestHoleSize ← top - page; state.largestHoleStart ← page + 1};
Try now for a new hole starting at page+1. (page..top] are known to be free.
base ← page + 1;
exBase ← top + 1;
EXIT
};
REPEAT
FINISHED => {state.base ← base; RETURN [TRUE]};
ENDLOOP;
ENDLOOP;
};
RETURN[FALSE]
};
state: StateObject ← [0, 0, , volume.volumeSize];
IF FindNextHole[nPages, @state] THEN RETURN[state.base, nPages];
IF state.largestHoleSize = 0 THEN
FOR page: LogicalVolume.PageNumber IN [0..state.maxPages) DO
IF ~PageInUse[page] THEN {state.base ← page; state.largestHoleSize ← 1; EXIT};
REPEAT
FINISHED => RETURN[0, 0]; -- disk is completely full
ENDLOOP;
WHILE FindNextHole[state.largestHoleSize + 1, @state] DO
page: LogicalVolume.PageNumber ← state.base + state.largestHoleSize + 1;
WHILE page < state.maxPages AND ~PageInUse[page] DO
page ← page + 1;
ENDLOOP;
state.largestHoleSize ← page - state.base;
state.largestHoleStart ← state.base;
state.base ← page + 1;
ENDLOOP;
RETURN [state.largestHoleStart, state.largestHoleSize]
};
{
ENABLE ANY => FatalError[implementationBug];
ucodeError: LONG STRING ← ValidateMicrocode[];
IF ucodeError ~= NIL THEN {
Heap.systemZone.FREE[@ucodeError];
ProcessorFace.SetMP[LOOPHOLE[CedarInitPrivate.ErrorCode[unsuitableMachine]]];
forge ahead and hope for the best
};
Directory.CreateSystemDirectory[Volume.systemID ! Directory.Error => CONTINUE];
EnsureCedarVMBackingFile[];
PupDefs.PupPackageMake[];
};
END.
Edited on December 15, 1982 5:39 pm, by Levin
Set style to "Cedar"
changes to: , END.