-- OthelloDeviceImplDorado.mesa
-- Contains Othello commands and operations that are specific to disks connected to Dorados.
-- Last Edited by: Taft, February 15, 1983 11:06 am

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
        ! FT.BadPage => 
          BEGIN
          WriteString["Warning: page "L];
          WriteLongNumber[p];
          WriteLine[" is bad (will be skipped). "L];
          RESUME;
          END];
      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;
  NoteBad: PROC [p: PhysicalVolume.PageNumber] =
    BEGIN
    IF badSpots=0 THEN WriteLine["Bad pages found:"L];
    IF badSpots IN [0..LENGTH[bs↑]) THEN bs[badSpots] ← p;
    WriteFixedWidthNumber[p, 11];
    badSpots ← badSpots + 1;
    IF (badSpots MOD 6)=0 THEN NewLine[];
    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;
  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];
      proc ← IF op=format THEN Format ELSE Scan;
      IF driveNumber=0 THEN
        proc[h, 0, pagesReservedInPartition1
          ! BadPage => { NoteBad[p]; RESUME }];
      FOR r: CARDINAL IN [0..layout.nDiskPageRuns) DO
        proc[h, layout.diskPageRuns[r].base, layout.diskPageRuns[r].size
          ! BadPage => { NoteBad[p]; 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