FormatDiskDLion.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Birrell December 6, 1983 1:15 pm
Taft, January 26, 1984 4:40:40 pm PST
DIRECTORY
BasicTime USING [GMT],
Disk USING [Channel, Command, defaultTries, DriveAttributes, DoIO, GetBootChainLink, Label, PageCount, PageNumber, Request, Status],
DiskFace USING [DeviceHandle, DontCare, GetDeviceAttributes, GetNextDevice, nullDeviceHandle],
FormatDisk,
Rope USING [ROPE],
VM USING [AddressForPageNumber, Allocate, Free, Interval, Pin, Unpin];
NOTE: At the time this module is started, the disk head must already have been initialized so that the result returned by DiskFace.GetDeviceAttributes is defined. This code assumes that all attached disks are of the same shape.
FormatDiskDLion:
MONITOR
-- only one guy in here at a time!
IMPORTS Disk, DiskFace, VM
EXPORTS FormatDisk = { OPEN FormatDisk;
Disk formatting and checking
BadPage: PUBLIC SIGNAL [page: Disk.PageNumber, correctable: BOOLEAN] = CODE;
Error: PUBLIC ERROR [page: Disk.PageNumber, status: Disk.Status] = CODE;
Sweep:
PUBLIC ENTRY
PROC [channel: Disk.Channel, operation: Operation, firstPage: Disk.PageNumber, pageCount: Disk.PageCount, label:
LONG
POINTER
TO Disk.Label, data:
LONG
POINTER] = {
IF operation=format
THEN {
initialLabel: Disk.Label ← label^;
p: CARDINAL;
[sectorsPerTrack: p] ← DeviceFromChannel[channel].GetDeviceAttributes[];
IF (firstPage MOD p) # 0 OR (pageCount MOD p) # 0 THEN ERROR; -- can only format entire tracks
WHILE pageCount#0
DO
Transfer[channel: channel, firstPage: firstPage, pageCount: p, command: [write, write, write], label: label, data: data, reportSoftErrors: FALSE];
firstPage ← [firstPage+p];
pageCount ← pageCount-p;
ENDLOOP;
label^ ← initialLabel;
}
ELSE Transfer[channel: channel, firstPage: firstPage, pageCount: pageCount, command: commandTable[operation], label: label, data: data, reportSoftErrors: TRUE];
};
commandTable:
ARRAY Operation
OF Disk.Command = [
format: [write, write, write],
write: [verify, write, write],
read: [verify, read, read],
verify: [verify, verify, verify]];
Initial microcode
MicrocodeInstallFailure: PUBLIC SIGNAL [why: FailureType] = CODE;
InstallInitialMicrocode:
PUBLIC ENTRY
PROC [channel: Disk.Channel, getPage:
PROC [ptr:
LONG
POINTER]
RETURNS [ok:
BOOLEAN]] = {
label: Disk.Label;
vmInterval: VM.Interval ← VM.Allocate[count: 1];
pageBuffer: LONG POINTER ← VM.AddressForPageNumber[vmInterval.page];
thisPage: Disk.PageNumber ← hardUCodeStart;
prevPage: Disk.PageNumber ← [hardUCodeStart-1];
VM.Pin[vmInterval];
{ ENABLE UNWIND => {VM.Unpin[vmInterval]; VM.Free[vmInterval]};
DO
ok: BOOLEAN;
IF (ok ← getPage[pageBuffer])
THEN
{
ENABLE BadPage, Error =>
IF thisPage=hardUCodeStart THEN GOTO firstPageBad
ELSE {thisPage ← [thisPage+1]; RETRY};
IF thisPage>=hardUCodeStart+hardUCodeSize THEN ERROR MicrocodeInstallFailure[microcodeTooBig];
label ← initialMicrocodeLabel;
label.dontCare ← channel.GetBootChainLink[[thisPage+1]];
Transfer[channel: channel, firstPage: thisPage, pageCount: 1, command: [verify, write, write], label: @label, data: pageBuffer];
label ← initialMicrocodeLabel;
Transfer[channel: channel, firstPage: thisPage, pageCount: 1, command: [verify, verify, verify], label: @label, data: pageBuffer];
};
IF thisPage#Disk.PageNumber[prevPage+1]
OR ~ok
THEN
{ ENABLE BadPage, Error => GOTO flakeyPageFound;
label ← initialMicrocodeLabel;
Transfer[channel: channel, firstPage: prevPage, pageCount: 1, command: [verify, verify, read], label: @label, data: pageBuffer];
label ← initialMicrocodeLabel;
label.dontCare ← IF ok THEN channel.GetBootChainLink[thisPage] ELSE eofLink;
Transfer[channel: channel, firstPage: prevPage, pageCount: 1, command: [verify, write, write], label: @label, data: pageBuffer];
};
IF ~ok THEN EXIT;
thisPage ← [(prevPage ← thisPage)+1];
REPEAT
firstPageBad => ERROR MicrocodeInstallFailure[firstPageBad];
flakeyPageFound => ERROR MicrocodeInstallFailure[flakeyPageFound];
ENDLOOP;
};
VM.Unpin[vmInterval];
VM.Free[vmInterval];
};
IdentifyInitialMicrocode:
PUBLIC
ENTRY
PROC [channel: Disk.Channel]
RETURNS [microcodeInstalled:
BOOLEAN, time: BasicTime.GMT, name: Rope.
ROPE] = {
microcodeInstalled ← FALSE; -- DLion microcode does not follow this convention
};
hardUCodeFileID: ARRAY [0..5) OF CARDINAL = [0, 0, 0, 0, 0]; -- boot microcode knows this ID
initialMicrocodeLabel: Disk.Label = [fileID: [abs[LOOPHOLE[hardUCodeFileID]]], filePage: 0, attributes: LOOPHOLE[0], dontCare: LOOPHOLE[LONG[0]]]; -- boot microcode knows this label. All pages of the initial microcode have the same label, except for the boot chain link which is filled in for every page. That is, the initial microcode is not a well-formed file.
nullLink: DiskFace.DontCare = LOOPHOLE[LONG[0]];
eofLink: DiskFace.DontCare = LOOPHOLE[LONG[-1]];
Reserved disk areas
hardUCodeStart: PUBLIC Disk.PageNumber; -- computed by Start
hardUCodeSize: PUBLIC Disk.PageCount; -- computed by Start
altoRegionJargon: PUBLIC Rope.ROPE ← NIL;
altoRegionsMax: PUBLIC INT ← 0;
altoRegionsStart: PUBLIC Disk.PageNumber ← [0];
altoRegionsSize: PUBLIC Disk.PageCount ← 0;
altoRegionsBottomUp: PUBLIC BOOL ← FALSE;
Initialization
Start:
PUBLIC
PROC = {
sectors, heads: CARDINAL;
device: DiskFace.DeviceHandle = DiskFace.GetNextDevice[DiskFace.nullDeviceHandle];
IF device=DiskFace.nullDeviceHandle THEN ERROR;
[sectorsPerTrack: sectors, movingHeads: heads] ← DiskFace.GetDeviceAttributes[device];
hardUCodeStart ← [sectors]; -- one track reserved before start of microcode
hardUCodeSize ← (heads-1)*sectors; -- microcode fills rest of first cylinder
};
Private
Transfer:
PROC [channel: Disk.Channel, firstPage: Disk.PageNumber, pageCount: Disk.PageCount, command: Disk.Command, label:
LONG
POINTER
TO Disk.Label, data:
LONG
POINTER, reportSoftErrors:
BOOLEAN ←
FALSE] = {
! BadPage, Error;
retrying: BOOLEAN ← FALSE;
WHILE pageCount>0
DO
The idea here is as follows. If reportSoftErrors=FALSE then we just make a normal call and let the disk channel (and head) do all the error recovery. But if reportSoftErrors=TRUE, we initially request the disk channel (and head) to perform only one try so that we get to find out the exact address of any error. When an error occurs, we then retry just that one page with full error recovery enabled in order to discover whether the error is correctable.
request: Disk.Request ← [diskPage: firstPage, data: data, incrementDataPtr: FALSE, command: command, tries: IF reportSoftErrors AND ~retrying THEN 1 ELSE Disk.defaultTries, count: IF retrying THEN 1 ELSE pageCount];
countDone: Disk.PageCount;
status: Disk.Status;
[status: status, countDone: countDone] ← channel.DoIO[label: label, request: @request];
WITH s: status
SELECT
FROM
changed => ERROR Error[firstPage, status];
unchanged =>
SELECT s.status
FROM
goodCompletion =>
IF retrying THEN {retrying ← FALSE; SIGNAL BadPage[firstPage, TRUE]};
notReady, recalibrateError, seekTimeout, memoryError, memoryFault, clientError, operationReset => ERROR Error[firstPage, status];
ENDCASE => {
IF ~retrying THEN retrying ← TRUE
ELSE {
retrying ← FALSE;
SIGNAL BadPage[firstPage, FALSE];
countDone ← countDone+1; -- skip over the bad page
label.filePage ← label.filePage+1;
};
};
ENDCASE => ERROR;
pageCount ← pageCount-countDone;
firstPage ← [firstPage+countDone];
ENDLOOP;
};
DeviceFromChannel:
PROC [channel: Disk.Channel]
RETURNS [device: DiskFace.DeviceHandle] = {
device ← DiskFace.nullDeviceHandle;
FOR ordinal:
CARDINAL
IN [0..
CARDINAL[channel.DriveAttributes[].ordinal]]
DO
device ← device.GetNextDevice[];
IF device=DiskFace.nullDeviceHandle THEN ERROR; -- bogus ordinal
ENDLOOP;
};
}.