-- OthelloDeviceImplDorado.mesa
-- Contains Othello commands and operations that are specific to disks connected to Dorados.
-- Last Edited by: Taft, May 22, 1983 1:26 pm
DIRECTORY
Device,
DeviceTypes USING [sa4000],
FormatTrident,
OthelloDefs,
OthelloDevice,
OthelloOps,
PhysicalVolume,
String USING [AppendDecimal, AppendString, EquivalentStrings],
System USING [GreenwichMeanTime],
Volume;
OthelloDeviceImplDorado: PROGRAM
IMPORTS FormatTrident, OthelloDefs, OthelloOps, PhysicalVolume, String, Volume
EXPORTS OthelloDevice =
BEGIN OPEN OthelloDefs, OthelloDevice;
printNameSA4000: PUBLIC STRING ← "Trident";
logicalVolumeOverhead: CARDINAL = 1;
minLogicalVolumeSize: CARDINAL = 50; -- fudge + 1+1+6;
maxNameLength: CARDINAL = PhysicalVolume.maxNameLength;
-- BUG! The following statement is wrong! physicalVolumeOverhead should be set based upon device type. This will be necessary once Pilot knows how to create bad page tables based upon device types. For the time bbeing, we just KNOW that all bad page tables are one page long, and that as a result there are always two pages of physical volume overhead.
physicalVolumeOverhead: CARDINAL = 2;
partitionsForPilot: PACKED ARRAY FormatTrident.Partition OF BOOLEAN;
partitionsSpecified: BOOLEAN ← FALSE;
DiskLayout: TYPE = RECORD [
nDiskPageRuns: CARDINAL,
diskPageRuns: ARRAY [0..LAST[FormatTrident.Partition]/2] OF RECORD [
base: PhysicalVolume.PageNumber,
size, remaining: Volume.PageCount]];
systemDiskLayout: DiskLayout; -- for drive 0
LvRecord: TYPE = MACHINE DEPENDENT RECORD[
base: PhysicalVolume.PageNumber ← NULL,
size: Volume.PageCount ← NULL,
type: Volume.Type ← NULL,
fill: [0..16000] ← 0,
name: StringBody ← [maxlength: maxNameLength, length: 0, text: ],
txt: PACKED ARRAY [0..maxNameLength) OF CHARACTER ← NULL];
CallCommandProc: PUBLIC PROC [proc: PROC] =
BEGIN
proc[
-- All Dorado device-specific SIGNALs are caught here, remaining ones by caller.
! FormatTrident.MicrocodeInstallFailure =>
BEGIN
NewLine[];
ReportError[SELECT m FROM
emptyFile => "\rThat remote file is empty!"L,
firstPageBad => "\rFirst microcode page of this disk is bad."L,
flakeyPageFound => "\rIntermittent page in microcode area."L,
microcodeTooBig => "\rMicrocode too large."L,
ENDCASE => "\rUnknown Install microcode error."L];
RESUME;
END;
FormatTrident.BadPage =>
{WriteString["\rBadPage?"L]; DebugAsk[]; CONTINUE};
FormatTrident.NotTrident =>
{WriteString["\rNotTrident?"L]; DebugAsk[]; CONTINUE}
];
END;
CreateVolume: PUBLIC PROC=
BEGIN
h: PhysicalVolume.Handle = GetDriveFromUser[];
driveNumber: CARDINAL = GetDriveNumber[h];
layout: DiskLayout;
badTable: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber;
bad: CARDINAL ← 0;
broughtOnLine: BOOLEAN ← FALSE;
nSubVols: CARDINAL;
lvID: Volume.ID;
lvsize: Volume.PageCount;
lvTable: ARRAY [0..10) OF LvRecord ← ALL[[]];
pvID: PhysicalVolume.ID ← PhysicalVolume.nullID;
pvName: STRING ← [maxNameLength];
volumeFound: BOOLEAN ← TRUE;
run: CARDINAL;
currentBase: PhysicalVolume.PageNumber;
DO
pvID ← PhysicalVolume.GetNext[pvID];
IF pvID=PhysicalVolume.nullID THEN
BEGIN
volumeFound ← Yes["Shall I try to find an old bad page Table? "L];
IF volumeFound THEN
BEGIN
broughtOnLine ← TRUE;
pvID ← PhysicalVolume.AssertPilotVolume[h !
PhysicalVolume.Error, PhysicalVolume.CanNotScavenge =>
{ volumeFound ← broughtOnLine ← FALSE; CONTINUE }]
END;
EXIT;
END;
IF h=PhysicalVolume.GetAttributes[pvID].instance THEN EXIT;
ENDLOOP;
IF volumeFound THEN
BEGIN OPEN PhysicalVolume;
badTable[bad] ← GetNextBadPage[pvID, nullBadPage];
WHILE badTable[bad]#nullBadPage DO
badTable[bad+1] ← GetNextBadPage[pvID, badTable[bad]];
bad ← bad+1;
ENDLOOP;
volumeFound ← PhysicalVolume.GetNextLogicalVolume[
pvID, Volume.nullID]#Volume.nullID;
END;
IF driveNumber=0 AND ~partitionsSpecified THEN PartitionCmd[];
IF driveNumber=0 THEN layout ← systemDiskLayout
ELSE BEGIN
layout.nDiskPageRuns ← 1;
layout.diskPageRuns[0] ← [
base: physicalVolumeOverhead,
size: OthelloOps.GetDriveSize[h]-physicalVolumeOverhead, remaining: 0];
END;
GetName["New physical volume name: "L, pvName];
nSubVols ← ReadShortNumber[
"Number of logical volumes: "L, 1,
PhysicalVolume.maxSubvolumesOnPhysicalVolume, 3];
FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
layout.diskPageRuns[r].remaining ← layout.diskPageRuns[r].size; ENDLOOP;
FOR i: CARDINAL IN [0..nSubVols) DO
OPEN lvTable[i];
IF layout.nDiskPageRuns>1 THEN
BEGIN
WriteString["Remaining space is in "L];
WriteLongNumber[layout.nDiskPageRuns];
WriteString[" runs of "L];
FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
IF r#0 THEN WriteString[", "];
WriteLongNumber[layout.diskPageRuns[r].remaining];
ENDLOOP;
WriteLine[" pages."L];
END;
WriteString["Logical volume "L];
WriteLongNumber[LONG[i]];
NewLine[];
DO
duplicate: BOOLEAN ← FALSE;
GetName[" Name: "L, @name];
FOR j: CARDINAL IN [0..i) WHILE ~duplicate DO
duplicate ← String.EquivalentStrings[@name, @lvTable[j].name];
ENDLOOP;
IF ~duplicate THEN EXIT;
ReportError["Name is already in use; please choose another"L];
ENDLOOP;
size ← 0;
FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
size ← MAX[size, layout.diskPageRuns[r].remaining]; ENDLOOP;
IF size<minLogicalVolumeSize THEN
BEGIN
ReportError["Insufficient space for any more volumes"L];
ERROR OthelloDefs.TryAgain;
END;
size ← ReadNumber[" Pages: "L, minLogicalVolumeSize, size, size/(nSubVols-i)];
-- "best-fit" run assignment
run ← LAST[CARDINAL];
FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
IF size<=layout.diskPageRuns[r].remaining AND
(run=LAST[CARDINAL] OR
layout.diskPageRuns[r].remaining < layout.diskPageRuns[run].remaining) THEN
run ← r;
ENDLOOP;
base ← layout.diskPageRuns[run].base +
layout.diskPageRuns[run].size - layout.diskPageRuns[run].remaining;
layout.diskPageRuns[run].remaining ← layout.diskPageRuns[run].remaining-size;
type ← GetLvTypeFromUser[" Type: "L];
ENDLOOP;
IF broughtOnLine THEN PhysicalVolume.Offline[pvID];
Confirm[IF volumeFound THEN twice ELSE once];
PhysicalVolume.Offline[pvID ! ANY => CONTINUE];
pvID ← PhysicalVolume.CreatePhysicalVolume[h, pvName];
FOR i: CARDINAL IN [0..bad) DO
PhysicalVolume.MarkPageBad[pvID, badTable[i]]; ENDLOOP;
currentBase ← 0;
THROUGH [0..nSubVols) DO
-- Create logical volumes in increasing order of base disk address.
-- This is required due to peculiar behavior of CreateLogicalVolume.
leastBase: PhysicalVolume.PageNumber ← LAST[PhysicalVolume.PageNumber];
lvIndex: CARDINAL;
FOR i: CARDINAL IN [0..nSubVols) DO
IF lvTable[i].base > currentBase AND lvTable[i].base < leastBase THEN
BEGIN lvIndex ← i; leastBase ← lvTable[i].base; END;
ENDLOOP;
-- Now lvIndex denotes the lowest logical volume not already created.
BEGIN OPEN lvTable[lvIndex];
currentBase ← base;
lvID ← PhysicalVolume.CreateLogicalVolume[
pvID, size-logicalVolumeOverhead, @name, type, base];
IF (lvsize←Volume.GetAttributes[lvID].volumeSize)#size-logicalVolumeOverhead THEN
BEGIN
WriteString[@name];
WriteString["'s size decreased (because of bad pages) to "L];
WriteLongNumber[lvsize];
NewLine[];
END;
END;
ENDLOOP;
IF driveNumber=0 THEN InstallUserCredentials[];
END;
FetchInitialMicrocode: PUBLIC PROC =
BEGIN
h: PhysicalVolume.Handle ← GetDriveFromUser[];
msg: STRING ← [100];
t: Device.Type;
IF ~ftpOpen THEN {ReportError["Please open a connection first"L]; RETURN};
IF GetDriveNumber[h]#0 THEN {
ReportError["Initial microcode can be put on drive 0 only"L]; RETURN};
GetName["File name: "L, file];
Confirm[];
SELECT (t ← GetDriveType[h]) FROM
DeviceTypes.sa4000 =>
BEGIN OPEN FT: FormatTrident;
P: PROC [getPage: PROC RETURNS[LONG POINTER]] =
{FT.InstallBootMicrocode[h, getPage]};
FT.Format[h, FT.firstHardUCodePage,
FT.pagesReservedInPartition1 - FT.firstHardUCodePage, 1 !
FT.BadPage =>
BEGIN
WriteString["Warning: page "L];
WriteLongNumber[p];
WriteLine[" is bad (will be skipped). "L];
RESUME;
END;
FT.PassStarting => RESUME];
WriteString["Fetching..."L];
[] ← Retrieve[remoteFile: file, msg: msg, destination: [rawWrite[P]] !
CredentialError => { PromptForCredentials[ftpError, message]; RESUME }];
WriteLine["Done"L];
END;
ENDCASE => ReportError["Othello can't install microcode on that device."L];
IF msg.length#0 THEN ReportError[msg];
END;
FormatCheckDrive: PUBLIC PROC [
bs: POINTER TO ARRAY[0..badTableSize) OF PhysicalVolume.PageNumber,
op: FormatCheckOp]
RETURNS [couldDo: BOOLEAN, badSpots: CARDINAL, h: PhysicalVolume.Handle] =
BEGIN
driveNumber: CARDINAL;
t: Device.Type;
layout: DiskLayout;
badSpotsThisPass: CARDINAL ← 0;
passes: CARDINAL;
NoteBad: PROC [p: PhysicalVolume.PageNumber] =
BEGIN
FOR i: CARDINAL IN [0..badSpots) DO
IF p = bs[i] THEN RETURN;
ENDLOOP;
IF badSpotsThisPass=0 THEN WriteString[" Bad pages found:"L];
IF (badSpotsThisPass MOD 6)=0 THEN NewLine[];
WriteFixedWidthNumber[p, 11];
IF badSpots IN [0..LENGTH[bs↑]) THEN bs[badSpots] ← p;
badSpotsThisPass ← badSpotsThisPass + 1;
badSpots ← badSpots+1;
END;
NotePassStarting: PROC [pass: CARDINAL, operation: FormatTrident.Operation] =
BEGIN
WriteString["\rPass "L];
WriteFixedWidthNumber[pass, 3];
WriteString[IF operation=write THEN ", writing."L ELSE ", reading."L];
badSpotsThisPass ← 0;
END;
h ← GetDriveFromUser[];
driveNumber ← GetDriveNumber[h];
IF driveNumber=0 AND ~partitionsSpecified THEN PartitionCmd[];
IF driveNumber=0 THEN layout ← systemDiskLayout
ELSE BEGIN
layout.nDiskPageRuns ← 1;
layout.diskPageRuns[0] ← [
base: 0,
size: OthelloOps.GetDriveSize[h], remaining: 0];
END;
badSpots ← 0;
passes ← ReadShortNumber["Number of passes: "L, 1, 1000, (IF op=format THEN 10 ELSE 1)];
IF op=check THEN Confirm[once]
ELSE -- op=format
BEGIN
p: PhysicalVolume.ID ← PhysicalVolume.nullID;
Confirm[twice];
DO
p ← PhysicalVolume.GetNext[p];
IF p=PhysicalVolume.nullID THEN EXIT;
IF h = PhysicalVolume.GetAttributes[p].instance THEN
{ PhysicalVolume.Offline[p]; EXIT };
ENDLOOP;
END;
SELECT (t ← GetDriveType[h]) FROM
DeviceTypes.sa4000 => -- kludge, really Trident
BEGIN OPEN FormatTrident;
proc: PROC[
h: PhysicalVolume.Handle, firstPage: PhysicalVolume.PageNumber,
count: LONG CARDINAL, passes: CARDINAL];
proc ← IF op=format THEN Format ELSE Scan;
IF driveNumber=0 THEN
proc[h, 0, pagesReservedInPartition1, passes !
BadPage => { NoteBad[p]; RESUME };
PassStarting => RESUME];
FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
proc[h, layout.diskPageRuns[r].base, layout.diskPageRuns[r].size, passes !
BadPage => { NoteBad[p]; RESUME };
PassStarting => {NotePassStarting[pass, operation]; RESUME}];
ENDLOOP;
END;
ENDCASE => RETURN[FALSE, badSpots, h];
RETURN[TRUE, badSpots, h];
END;
IdentifyInitialMicrocode: PUBLIC PROCEDURE [h: PhysicalVolume.Handle, s: STRING]
RETURNS [microcodeInstalled: BOOLEAN ← FALSE, time: System.GreenwichMeanTime] =
BEGIN
SELECT GetDriveType[h] FROM
DeviceTypes.sa4000 => -- kludge, really Trident
[microcodeInstalled, time] ← FormatTrident.IdentifyInitialMicrocode[h, s];
ENDCASE => NULL;
END;
IndicateAltoness: PUBLIC PROC = {ReportError["Command not applicable to Dorado."L]};
PartitionCmd: PUBLIC PROC =
BEGIN OPEN FormatTrident;
first: Partition;
partitionsSpecified ← FALSE;
WHILE ~partitionsSpecified DO
FOR partition: Partition IN Partition DO
prompt: STRING ← [50];
String.AppendString[prompt, "Include partition "L];
String.AppendDecimal[prompt, partition];
String.AppendString[prompt, " in the Pilot file system? "L];
partitionsForPilot[partition] ← Yes[prompt];
ENDLOOP;
systemDiskLayout.nDiskPageRuns ← 0;
first ← FIRST[Partition];
WHILE first IN Partition DO
IF partitionsForPilot[first] THEN
BEGIN OPEN run: systemDiskLayout.diskPageRuns[systemDiskLayout.nDiskPageRuns];
length: CARDINAL ← 1;
overhead: Volume.PageCount ←
IF first=FIRST[Partition] THEN pagesReservedInPartition1 ELSE 0;
WHILE first+length IN Partition AND partitionsForPilot[first+length] DO
length ← length+1; ENDLOOP;
run.base ← (first-FIRST[Partition])*pagesInPartition + overhead;
run.size ← length*pagesInPartition - overhead;
systemDiskLayout.nDiskPageRuns ← systemDiskLayout.nDiskPageRuns+1;
first ← first+length;
END
ELSE first ← first+1;
ENDLOOP;
partitionsSpecified ← systemDiskLayout.nDiskPageRuns>0;
ENDLOOP;
END;
END.
12-Jun-81 16:15:16 Taft Created file using excerpts from VolumeInitImplA.mesa
17-Jun-82 14:36:08 Taft Re-implement FetchInitialMicrocode
20-Jun-82 16:10:45 Taft Add IdentifyInitialMicrocode
September 10, 1982 11:34 am Taft Mods for new Cedar Login
May 22, 1983 1:21 pm Taft Modify FormatCheckDrive for multi-pass Format and Scan.