BootChannelDisk.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Taft, July 21, 1983 5:09 pm
Sturgis, July 23, 1983 4:33 pm
Andrew Birrell, October 31, 1983 3:33 pm
Russ Atkinson, February 28, 1985 0:06:35 am PST
Doug Wyatt, February 26, 1985 6:05:55 pm PST
NOTE: If you change the imports, you will also have to change the Germ*.config files!!
DIRECTORY
Basics USING [DivMod, LongDiv, LongDivMod, LongMult],
BootFile USING [Location, nullLink],
BootChannel USING [Create, Operation, Handle, transferCleanup, transferWait],
DiskFace USING[ Label ],
GermPrivate USING [ Error ],
MPCodes USING [germDeviceError, germLabelCheck],
PrincOps USING [PageCount, PageNumber],
PrincOpsUtils USING [AddressForPageNumber, LowHalf, VERSION],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses, SetMP],
SA4000Face USING [Command, DeviceHandle, DiskAddress, GetDeviceAttributes, GetNextDevice, globalStateSize, Initialize, Initiate, nullDeviceHandle, Operation, operationSize, Poll, Recalibrate];
BootChannelDisk: PROGRAM
IMPORTS Basics, RemainingChannels: BootChannel, GermPrivate, PrincOpsUtils, ProcessorFace, SA4000Face
EXPORTS BootChannel = {
Implementation of BootChannel for Cedar volume on SA4000.
A single, serially reusable, channel is supported.
label1, label2: DiskFace.Label; -- HOW SHOULD THIS BE ALIGNED FOR FUTURE DISKS?
pAllocateNext: LONG POINTER; -- allocator for first 64K storage
pLoc: POINTER TO BootFile.Location;
tries: [0..triesMax];
triesMax: CARDINAL = 8;
movingHeads, sectorsPerTrack: CARDINAL;
doubleBuffer: BOOLEAN;
currentDiskAddress: SA4000Face.DiskAddress;
currentFilePage: LONG CARDINAL;
Operation: TYPE = RECORD [
pOp: LONG POINTER TO SA4000Face.Operation,
state: {idle, busy}];
current, alternate: Operation;
Create: PUBLIC PROC [ pLocation: POINTER TO BootFile.Location, operation: BootChannel.Operation, dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [BootChannel.Handle] = {
pAllocateNext ← BASE[dFirst64KStorage]; -- reset first 64K allocator
SELECT (pLoc ← pLocation).deviceType FROM
= sa1000, sa4000 => {
da: SA4000Face.DiskAddress = LOOPHOLE[pLocation.diskFileID.firstLink];
device: SA4000Face.DeviceHandle;
Disable double buffering of commands on the Dandelion, because there seems to
be some bug in the head or microcode that prevents it from working.
doubleBuffer ← PrincOpsUtils.VERSION[].machineType # dandelion;
current.pOp ← Allocate[SA4000Face.operationSize];
current.state ← idle;
alternate.pOp ← Allocate[SA4000Face.operationSize];
alternate.state ← idle;
SA4000Face.Initialize[0,
LOOPHOLE[PrincOpsUtils.LowHalf[Allocate[SA4000Face.globalStateSize]]]];
device ← SA4000Face.nullDeviceHandle;
THROUGH [0..pLocation.deviceOrdinal] DO
device ← SA4000Face.GetNextDevice[device] ENDLOOP;
[movingHeads: movingHeads, sectorsPerTrack: sectorsPerTrack] ←
SA4000Face.GetDeviceAttributes[device];
Initialize label
currentDiskAddress ← da;
current.pOp.labelPtr ← @label1;
dataPtr, incrementDataPtr irrelevant
current.pOp.command ← vr; -- read label (and ignore data)
pageCount set in Transfer
current.pOp.device ← device;
TransferSA4000[page: NULL, count: 1]; -- fetch type, attribute fields
TransferSA4000[page: NULL, count: BootChannel.transferWait];
label1.fileID ← pLoc.diskFileID.fID;
SetLabelFilePage[@label1, pLoc.diskFileID.firstPage];
label1.filePage ← pLoc.diskFileID.firstPage;
label1.dontCare ← BootFile.nullLink; --- so TransferSA4000 will use pOp.clientHeader
label2 ← label1;
currentFilePage ← pLoc.diskFileID.firstPage;
Initialize remaining operation fields
currentDiskAddress ← da; -- since transfer above incremented it
labelPtr set above
dataPtr set in Transfer
current.pOp.incrementDataPtr ← TRUE;
current.pOp.command ← SELECT operation FROM
read => vvr,
write => vvw,
ENDCASE --rawRead-- => vrr;
pageCount set in Transfer
device set above
alternate.pOp^ ← current.pOp^;
alternate.pOp.labelPtr ← @label2;
RETURN[TransferSA4000];
};
ENDCASE => -- not anything I implement. Pass it on.
RETURN[RemainingChannels.Create[pLocation, operation, dFirst64KStorage]];
};
TransferSA4000: PROC [page: PrincOps.PageNumber, count: PrincOps.PageCount] = {
IF current.state=busy THEN Cleanup[];
SELECT count FROM
BootChannel.transferWait, BootChannel.transferCleanup => {
Pause[100];
IF doubleBuffer THEN {
ExchangeCurAlt[];
IF current.state=busy THEN {Cleanup[count = BootChannel.transferCleanup]};
};
};
ENDCASE => {
current.pOp.dataPtr ← PrincOpsUtils.AddressForPageNumber[page];
current.pOp.pageCount ← count;
Initiate[];
IF current.state=busy THEN Cleanup[];--wait for previous transfer to complete
IF ~doubleBuffer THEN {ExchangeCurAlt[]; Cleanup[]};
};
};
Initiate: PROC = {
current.pOp.clientHeader ← currentDiskAddress;
SetLabelFilePage[current.pOp.labelPtr, currentFilePage];
current.pOp.labelPtr.filePage ← currentFilePage;
current.pOp.labelPtr.dontCare ← BootFile.nullLink;
IncrementDiskAddressAndPage[current.pOp.pageCount];
SA4000Face.Initiate[current.pOp];
current.state ← busy;
ExchangeCurAlt[];
};
pulsesPerMilli: CARDINAL =
Basics.LongDiv[100000, ProcessorFace.microsecondsPerHundredPulses];
Pause: PROC [msecs: CARDINAL ← 10] = {
A simple processor-independent pausing routine.
THROUGH [0..msecs) DO
init: LONG CARDINAL = ProcessorFace.GetClockPulses[];
DO
current: LONG CARDINAL = ProcessorFace.GetClockPulses[];
IF current-init > pulsesPerMilli THEN EXIT;
ENDLOOP;
ENDLOOP;
};
Cleanup: PROC [ignoreCheck: BOOLFALSE] = {
initialCount: CARDINALLAST[CARDINAL];
extraPause: BOOL ← ignoreCheck;
DO -- until count pages transferred
DO -- until status~=inProgress
extraPause ← ignoreCheck;
SELECT SA4000Face.Poll[current.pOp] FROM
inProgress => NULL;
goodCompletion => GOTO TransferComplete;
labelCheck =>
SELECT TRUE FROM
current.pOp.labelPtr.dontCare~= BootFile.nullLink => {
end of run on disk; chain to next run and resume transfer
current.pOp.clientHeader ← LOOPHOLE[current.pOp.labelPtr.dontCare];
extraPause ← TRUE;
EXIT;
};
ignoreCheck => {
bogus test here for debugging purposes
current.state ← idle;
ProcessorFace.SetMP[809];
Pause[500];
RETURN;
};
ENDCASE => {
initialCount ← current.pOp.pageCount;
ProcessorFace.SetMP[100+initialCount];
Pause[10000];
GermPrivate.Error[MPCodes.germLabelCheck];
};
notReady => {
IF (tries ← tries - 1) = 0 THEN GermPrivate.Error[MPCodes.germDeviceError];
Wait ~ 10 millisecond to see if disk becomes ready
Pause[10];
initialCount ← current.pOp.pageCount;
};
ENDCASE -- other error -- => {
IF current.pOp.pageCount ~= initialCount THEN tries ← triesMax - 1
ELSE
SELECT tries ← tries - 1 FROM
0 => GermPrivate.Error[MPCodes.germDeviceError];
triesMax/2 => SA4000Face.Recalibrate[current.pOp.device];
ENDCASE;
initialCount ← current.pOp.pageCount;
EXIT;
};
ENDLOOP;
At this point we know the disk controller is dormant, since errors cause all pending transfers to be abandoned. Therefore it is safe to fool with the label and the operation.
currentDiskAddress ← current.pOp.clientHeader;
currentFilePage ← GetLabelFilePage[current.pOp.labelPtr];
currentFilePage ← current.pOp.labelPtr.filePage;
Initiate[];
IF extraPause THEN Pause[40];
if we are chaining, then we pause more after this operation
Restart the alternate operation if there is one, since the head has forgotten it.
IF current.state=busy THEN Initiate[] ELSE ExchangeCurAlt[];
REPEAT TransferComplete => {
current.state ← idle;
If this operation happened to finish at the end of a run of pages, we must manually follow the bootChainLink. If the next operation has already been initiated, it will surely suffer a check error; wait for it to terminate, and then restart it at the correct place.
IF current.pOp.labelPtr.dontCare # BootFile.nullLink AND
current.pOp.labelPtr.dontCare # LOOPHOLE[current.pOp.clientHeader] THEN {
currentDiskAddress ← LOOPHOLE[current.pOp.labelPtr.dontCare];
currentFilePage ← GetLabelFilePage[current.pOp.labelPtr];
currentFilePage ← current.pOp.labelPtr.filePage;
IF alternate.state=busy THEN {
ExchangeCurAlt[];
WHILE SA4000Face.Poll[current.pOp] = inProgress DO ENDLOOP;
Initiate[];
};
};
};
ENDLOOP;
};
IncrementDiskAddressAndPage: PROC [count: CARDINAL] = INLINE {
virtualDA: LONG CARDINAL = count + currentDiskAddress.sector +
Basics.LongMult[sectorsPerTrack,
currentDiskAddress.head + movingHeads*currentDiskAddress.cylinder];
c: CARDINAL;
[c, currentDiskAddress.sector] ← Basics.LongDivMod[virtualDA, sectorsPerTrack];
[currentDiskAddress.cylinder, currentDiskAddress.head] ← Basics.DivMod[c, movingHeads];
currentFilePage ← currentFilePage+count;
};
ExchangeCurAlt: PROC = INLINE {
t: Operation ← current; current ← alternate; alternate ← t;
};
Allocator for 16-word aligned storage in first64K
Allocate: PROC [size: CARDINAL] RETURNS [lp: LONG POINTER] = {
pAllocateNext ← (lp ← pAllocateNext) + LONG[((size + 15)/16)*16];
};
}.
(For earlier log entries see Pilot 4.0 archive version.)
June 23, 1980 2:31 PM McJones OISDisk,FilePageLabel=>PilotDisk
August 13, 1980 6:02 PM McJones Read first label to determine attributes
August 23, 1980 1:21 PM McJones Don't report labelCheck at end of run
January 4, 1981 3:03 PM Taft Queue up to 2 operations at a time
June 18, 1981 2:04 PM Taft Bug in pathological case of boot file chaining
July 21, 1983 2:10 pm Taft Disable double-buffering of commands if running on Dandelion
July 23, 1983 4:32 pm: Sturgis also handle sa1000 device type
July 28, 1983 2:54 pm: Andrew Birrell wait for previous transfer after initiating second transfer
October 31, 1983 2:45 pm: Andrew Birrell convert to 5.0