<> <> <> <> <> <<>> DIRECTORY Basics USING [ BITNOT, BYTE, BytePair, DivMod, LongDivMod, LowHalf ], BasicTime USING [ FromPupTime, GetClockPulses, GMT, nullGMT, OutOfRange ], BootFile USING [MemorySizeToFileSize], Booting USING [ Boot, Switches, switches ], DebuggerSwap USING [EnableSwatWatcher], DefaultRemoteNames USING [Get], Disk USING [Channel, DriveAttributes, GetDeviceFromChannel, Label, NextChannel, PageCount, PageNumber], DiskFace USING [DeviceHandle, GetDeviceAttributes, GetTrueDeviceAttributes], File USING [Error, FindVolumeFromName, FP, GetVolumeID, GetVolumeName, Handle, Info, LogicalInfo, NextVolume, nullFP, Open, RC, SetSize, SystemVolume, Volume, VolumeID, VolumeFile, wordsPerPage], FileBackdoor USING [CreateLogicalVolume, CreatePhysicalVolume, EraseVolume, GetRoot, GetVolumePages, IsDebugger, MarkDebugger, PhysicalPageBad, SetRoot], FormatDisk USING [BadPage, IdentifyInitialMicrocode, InstallInitialMicrocode, MicrocodeInstallFailure, Operation, Sweep], FS USING [ Close, ComponentPositions, Copy, Delete, EnumerateForNames, Error, ExpandName, FileInfo, NameProc, Open, OpenFile, SetByteCountAndCreatedTime, StreamOpen ], FSBackdoor USING [ CloseVolume, CreateVMBacking, FNameFromHandle, GetFileHandle ], FSRemoteFile USING [Retrieve], GermSwap USING [ Switch ], IagoCommands, IagoOps --USING everything--, IO USING [ Close, EndOfStream, Error, GetChar, GetLength, PutChar, PutF, PutF1, PutRope, RopeFromROS, RIS, ROS, SetIndex, STREAM ], PhysicalVolume USING [ GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SetPhysicalRoot, SubVolumes ], Rope USING [ Cat, Concat, Find, ROPE, Substr ], SystemVersion USING [ machineType, uCodeCedar, uCodeDate, uCodeFloatingPoint, uCodeVersion ], ThisMachine USING [ Name, ProcessorID ], UserCredentials USING [ChangeState], VM USING [AddressForPageNumber, Allocate, Free, Interval, PagesForBytes, PagesForWords, Pin, wordsPerPage], VMSideDoor USING [rmPages]; IagoCommands1Impl: CEDAR PROGRAM IMPORTS Basics, BasicTime, BootFile, Booting, DebuggerSwap, DefaultRemoteNames, Disk, DiskFace, File, FileBackdoor, FormatDisk, FS, FSBackdoor, FSRemoteFile, IagoCommands, IagoOps, IO, PhysicalVolume, Rope, SystemVersion, ThisMachine, UserCredentials, VM, VMSideDoor EXPORTS IagoCommands = { ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; <<******** The command subroutines (alphabetic order) ********>> Attach: PUBLIC PROC [in, out: STREAM] = { from: ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "From ", check: TRUE]; to: ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "To "]; IO.PutRope[out, " ... "]; [] _ FS.Copy[from: from, to: to, remoteCheck: FALSE, attach: TRUE]; IO.PutRope[out, "done"]; }; BootLogical: PUBLIC PROC [in, out: STREAM] = { v: File.Volume = IagoOps.GetLogical[in, out]; reject: ROPE = Booting.Boot[[logical[v]], IagoOps.GetSwitches[in, out]]; IO.PutF1[out, " ... couldn't: %g\n", [rope[reject]] ]; }; Copy: PUBLIC PROC [in, out: STREAM] = { from: ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "From ", check: TRUE]; to: ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "To "]; IO.PutRope[out, " ... "]; [] _ FS.Copy[from: from, to: to]; IO.PutRope[out, "done"]; }; CreateLogical: PUBLIC PROC [in, out: STREAM] = { name: ROPE _ IF File.FindVolumeFromName[IagoOps.clientVolName] = NIL THEN IagoOps.clientVolName ELSE NIL; size: INT; p: PhysicalVolume.Physical; free: INT; HelpCreation: PROC = { IO.PutRope[out, "? type a string which is not the same as the name of any existing volume"]; }; DO name _ IagoOps.GetArg[ in: in, out: out, prompt: "\nNew volume name: ", default: name, help: HelpCreation]; IF File.FindVolumeFromName[name] # NIL THEN IO.PutRope[out, " ... already exists"] ELSE EXIT ENDLOOP; p _ IagoOps.GetPhysical[in, out]; IagoOps.ReservePages[in, out, p]; free _ PhysicalVolume.PhysicalInfo[p].free; size _ IagoOps.GetSize[in: in, out: out, default: free, max: free]; IO.PutRope[out, "\nAre you sure? "]; IF IagoOps.Confirm[in, out] THEN { new: File.Volume _ NIL; IO.PutRope[out, " ... "]; new _ FileBackdoor.CreateLogicalVolume[ id: IagoOps.NewID[], where: LIST[p], size: size, name: name]; IO.PutRope[out, " done"]; IagoOps.clientVolName _ name; IO.PutRope[out, "\nDo you want to use this as the debugger volume? "]; IF IagoOps.Confirm[in, out] THEN FileBackdoor.MarkDebugger[new, TRUE]; }; }; CloseFSVolumes: PROC = { FOR v: File.Volume _ File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO FSBackdoor.CloseVolume[v]; ENDLOOP; }; CreatePhysical: PUBLIC PROC [in, out: STREAM] RETURNS[done: BOOL _ FALSE] = { HelpCreation: PROC = { IO.PutRope[out, "? type a name not the same as that of any existing volume"]; }; d: Disk.Channel = IagoOps.GetDrive[in, out]; alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND IagoOps.CheckAltoRegions[in, out]; name: ROPE = IagoOps.GetArg[ in: in, out: out, prompt: "\nNew volume name: ", default: ThisMachine.Name[], help: HelpCreation]; IF IagoOps.ConfirmDestruction[in, out, Rope.Concat[ "the entire disk drive", IF alto THEN " (excluding Alto regions)" ELSE NIL ] ] THEN { new: PhysicalVolume.Physical; CloseFSVolumes[]; new _ FileBackdoor.CreatePhysicalVolume[where: d, name: name, id: IagoOps.NewID[]]; IagoOps.ReservePages[in, out, new]; done _ TRUE; IagoCommands.InstallCredentials[in, out]; DebuggerSwap.EnableSwatWatcher[TRUE]; }; }; CreateUserWorld: PUBLIC PROC [in, out: STREAM] = { Install: PROC [where: File.Volume, which: File.VolumeFile[microcode..bootFile]] = { wDir: ROPE = Rope.Cat["[]<", File.GetVolumeName[where], ">"]; remote: ROPE = IagoOps.RemoteRootFileName[which]; local: ROPE = IagoOps.LocalRootFileName[which]; fsFile: FS.OpenFile; file: File.Handle; createTime: BasicTime.GMT; IO.PutF[out, "\nInstalling %g as %g%g ... ", [rope[remote]], [rope[wDir]], [rope[local]] ]; [] _ FS.Copy[from: remote, to: local, wDir: wDir]; createTime _ FS.FileInfo[name: local, wDir: wDir].created; fsFile _ FS.Open[name: local, lock: write, wDir: wDir]; file _ FSBackdoor.GetFileHandle[fsFile]; FileBackdoor.SetRoot[which, file]; FS.SetByteCountAndCreatedTime[file: fsFile, created: createTime]; FS.Close[fsFile]; IO.PutRope[out, "done"]; }; format, protect: BOOL; altoText: ROPE _ NIL; myName: ROPE = ThisMachine.Name[]; systemHost: ROPE = DefaultRemoteNames.Get[].systemHost; userHost: ROPE = DefaultRemoteNames.Get[].userHost; d: Disk.Channel = Disk.NextChannel[NIL]; newP: PhysicalVolume.Physical; newC: File.Volume; memImagePages: INT; <> cedarVMPages: INT _ 0; TRUSTED { memImagePages _ BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+24; <> }; IO.PutChar[out, '\n]; IagoOps.ReserveAltoRegions[in, out]; IF IagoOps.CheckAltoRegions[in, out] THEN altoText _ " (excluding the Alto disk regions)"; IO.PutRope[out, "\n\nHas your disk already been formatted? (Confirm if the disk has previously been used for Cedar or Pilot, AND you have not reduced the amount of disk reserved for Alto volumes) "]; format _ NOT IagoOps.Confirm[in, out]; IO.PutRope[out, "\n\nDo you want to password-protect this disk? "]; protect _ IagoOps.Confirm[in, out]; WHILE cedarVMPages < 8000 DO cedarVMPages _ IagoOps.GetNumber[ in: in, out: out, default: DefaultVMSize[], max: 64000, prompt: "\n\nSpecify VM size (in pages): ", help: "? give number of VM pages in decimal (max: 64000)" ]; ENDLOOP; IO.PutRope[out, "\n\nI intend to perform the following operations:"]; IF format THEN IO.PutF1[out, "\n - format the entire disk%g;", [rope[altoText]] ]; IO.PutF1[out, "\n - create a new physical volume named \"%g\";", [rope[myName]] ]; IO.PutF1[out, "\n - create a client volume named \"Cedar\" with a %g page VM;\n - install microcode, germ and boot files(s) from the release directory;\n - make the \"Cedar\" volume be the physical boot volume;\n - boot the volume(s) to initialize the software.\n", [integer[cedarVMPages]] ]; IF NOT IagoOps.ConfirmDestruction[ in, out, Rope.Concat["the entire disk on drive RD0", altoText]] THEN RETURN; CloseFSVolumes[]; IO.PutChar[out, '\n]; IF format THEN TRUSTED { DoFormatOrScan[in: in, out: out, d: d, format: TRUE, passes: 1] }; IO.PutRope[out, "\nCreating physical volume ... "]; newP _ FileBackdoor.CreatePhysicalVolume[where: d, name: myName, id: IagoOps.NewID[]]; IagoOps.ReservePages[in, out, newP]; [] _ UserCredentials.ChangeState[IF protect THEN name ELSE nameHint]; IO.PutRope[out, "done"]; IO.PutRope[out, "\nCreating logical volume \"Cedar\" ... "]; newC _ FileBackdoor.CreateLogicalVolume[ id: IagoOps.NewID[], where: LIST[newP], size: PhysicalVolume.PhysicalInfo[newP].free, name: "Cedar"]; IO.PutRope[out, "done. Erasing it ... "]; FileBackdoor.EraseVolume[newC]; IO.PutRope[out, "done"]; CreateVMFile[out, newC, cedarVMPages]; Install[where: newC, which: microcode]; Install[where: newC, which: germ]; Install[where: newC, which: bootFile]; PhysicalVolume.SetPhysicalRoot[newC, microcode]; PhysicalVolume.SetPhysicalRoot[newC, germ]; PhysicalVolume.SetPhysicalRoot[newC, bootFile]; { init: ROPE = IagoOps.InitialMicrocodeFileName[]; IO.PutRope[out, "\nInstalling "]; IO.PutRope[out, init]; IF DoInitialMicrocodeInstallation[out, d, init, "[]Temp>Initial.Microcode"] THEN [] _ Booting.Boot[ [logical[newC]], [d: Booting.switches[d]] ]; }; }; CreateVM: PUBLIC PROC [in, out: STREAM] = { v: File.Volume = IagoOps.GetLogical[in, out, "On "]; size: INT = IagoOps.GetSize[ in: in, out: out, default: DefaultVMSize[], max: FileBackdoor.GetVolumePages[v].size]; oldFP: File.FP = FileBackdoor.GetRoot[v, VM].fp; oldFile: File.Handle _ NIL; IF oldFP # File.nullFP THEN oldFile _ File.Open[v, oldFP ! File.Error => IF why = unknownFile THEN CONTINUE]; IF oldFile # NIL THEN { IO.PutF[out, "\nThat volume already has a VM backing file of size %g. Do you want me to set its size to %g? ", [integer[File.Info[oldFile].size]], [integer[size]] ]; IF IagoOps.Confirm[in, out] THEN { IO.PutRope[out, " ... "]; File.SetSize[oldFile, size]; IO.PutRope[out, "done"] }; } ELSE CreateVMFile[out, v, size]; -- Create the file ourselves }; DefaultVMSize: PROC RETURNS [pages: INT _ 16000] = { SELECT VMSideDoor.rmPages FROM <= 4*1024 => {}; <= 8*1024 => pages _ 20000; <= 12*1024 => pages _ 24000; ENDCASE => pages _ 32000; }; CreateVMFile: PROC [out: STREAM, v: File.Volume, size: INT] = { newFile: FS.OpenFile; volName: ROPE = File.GetVolumeName[v]; vmName: ROPE = Rope.Cat["[]<", volName, ">", IagoOps.LocalRootFileName[VM]]; IO.PutF1[out, "\nCreating %g ... ", [rope[vmName]] ]; newFile _ FSBackdoor.CreateVMBacking[name: vmName, pages: size]; FileBackdoor.SetRoot[VM, FSBackdoor.GetFileHandle[newFile]]; FS.Close[newFile]; IO.PutRope[out, "done"]; [] _ IagoOps.WriteRemoteNames[volName, out]; }; Delete: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; DoName: FS.NameProc = { count _ count+1; IO.PutF1[out, "\nDeleting %g ... ", [rope[fullFName]]]; { ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE }; FS.Delete[fullFName]; IO.PutRope[out, "done"]; }; RETURN[TRUE]; }; pattern: ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; IF Rope.Find[pattern, "!"] < 0 THEN pattern _ pattern.Concat["!L"]; IO.PutRope[out, " ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; DescribeDrives: PUBLIC PROC [in, out: STREAM] = { FOR c: Disk.Channel _ Disk.NextChannel[NIL], Disk.NextChannel[c] UNTIL c = NIL DO ordinal: INT; nPages: INT; [ordinal: ordinal, nPages: nPages] _ Disk.DriveAttributes[c]; IO.PutF[out, "\nRD%g, size: %g pages", [integer[ordinal]], [integer[nPages]] ]; ENDLOOP; }; DescribeLV: PUBLIC PROC [in, out: STREAM] = { FOR v: File.Volume _ File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO { ENABLE File.Error => IO.PutRope[out, "\nUnexpected File.Error"]--and let it propagate--; id: File.VolumeID; rootStatus, vamStatus: File.RC; name: ROPE; size, free, freeboard: INT; [id: id, size: size, rootStatus: rootStatus, name: name, vamStatus: vamStatus, free: free, freeboard: freeboard] _ File.LogicalInfo[v]; IO.PutRope[out, "\n\nID: "]; IagoOps.PutID[out, File.GetVolumeID[v]]; IF v = File.SystemVolume[] THEN IO.PutRope[out, " (the system volume)"]; SELECT rootStatus FROM ok, nonCedarVolume => NULL; wentOffline => { IO.PutRope[out, "\nVolume went offline"]; LOOP; }; inconsistent => { IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"]; LOOP; }; software => { IO.PutRope[out, "\nCan't find the volume root page (label-check)"]; LOOP; }; hardware => { IO.PutRope[out, "\nHard disk error reading the volume root page"]; LOOP; }; ENDCASE => ERROR File.Error[rootStatus]; IO.PutF1[out, "\nName: %g", [rope[name]] ]; IF FileBackdoor.IsDebugger[v] THEN IO.PutRope[out, "\nThis is a debugger volume"]; IO.PutF1[out, "\nSize: %g", [integer[size]] ]; IF rootStatus = nonCedarVolume THEN { IO.PutRope[out, "\nNot a Cedar volume"]; LOOP }; SELECT vamStatus FROM ok => IO.PutF1[out, ", free pages: %g", [integer[free]] ]; wentOffline => IO.PutRope[out, "\nVolume went offline during VAM operation"]; inconsistent => IO.PutRope[out, "\nDon't understand the VAM"]; software => IO.PutRope[out, "\nCan't find the VAM (label-check)"]; hardware => IO.PutRope[out, "\nHard disk error reading the VAM"]; unknownFile => IO.PutRope[out, "\nCan't find the VAM (unknown-file)"]; unknownPage => IO.PutRope[out, "\nCan't find the VAM (unknown-page)"]; ENDCASE => ERROR File.Error[vamStatus]; FOR vf: File.VolumeFile IN File.VolumeFile DO PutVolumeFile[out, v, vf]; ENDLOOP; }; ENDLOOP; }; PutVolumeFile: PROC [out: STREAM, v: File.Volume, vf: File.VolumeFile] = { fp: File.FP _ FileBackdoor.GetRoot[v, vf].fp; file: File.Handle; name: ROPE; IF fp = File.nullFP THEN RETURN; IO.PutF[out, "\n%g fp: [id:%bB,da:%bB]", [rope[SELECT vf FROM checkpoint => "Checkpoint", microcode => "Microcode", germ => "Germ", bootFile => "BootFile", debugger => "Debugger", debuggee => "Debuggee", VM => "VM", VAM => "VAM", client => "FS-root", alpine => "Alpine-root", ENDCASE => "Other-root"]], [cardinal[LOOPHOLE[fp.id]]], [cardinal[LOOPHOLE[fp.da]]] ]; file _ File.Open[v, fp ! File.Error => { IO.PutRope[out, " ... "]; IO.PutRope[out, IagoOps.FileError[why]]; file _ NIL; CONTINUE } ]; IF file # NIL THEN { ENABLE FS.Error => { IO.PutRope[out, " ... "]; IF error.code = $invalidPropertyPage THEN IO.PutRope[out, "not an FS file"] ELSE IO.PutRope[out, error.explanation]; CONTINUE }; IO.PutF1[out, ", size: %g", [integer[File.Info[file].size]] ]; name _ FSBackdoor.FNameFromHandle[file]; IO.PutRope[out, ", name: "]; IO.PutRope[out, name]; }; }; DescribeMachine: PUBLIC PROC [in, out: STREAM] = { IO.PutF[out, "\nThis processor is a %g, id = %g, %g, %g, available real memory: %g pages", [rope[SELECT SystemVersion.machineType FROM dolphin => "Dolphin", dorado => "Dorado", dandelion => "Dandelion", dicentra => "Dicentra", ENDCASE => "{unknown processor type!}"]], [rope[ThisMachine.ProcessorID[$Octal]]], [rope[ThisMachine.ProcessorID[$ProductSoftware]]], [rope[ThisMachine.ProcessorID[$Hex]]], [integer[VMSideDoor.rmPages]] ]; IO.PutF1[out, "\nThe microcode is version %g of ", [integer[SystemVersion.uCodeVersion]] ]; { ENABLE BasicTime.OutOfRange => { IO.PutRope[out, "unknown date."]; CONTINUE; }; IO.PutF1[out, "%g.", [time[BasicTime.FromPupTime[SystemVersion.uCodeDate]]] ]; }; IF NOT SystemVersion.uCodeCedar THEN IO.PutRope[out, " No Cedar microcode."]; IF NOT SystemVersion.uCodeFloatingPoint THEN IO.PutRope[out, " No floating-point microcode."]; }; DescribePV: PUBLIC PROC [in, out: STREAM] = { FOR p: PhysicalVolume.Physical _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO IagoOps.ReservePages[in, out, p]; ENDLOOP; FOR p: PhysicalVolume.Physical _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO channel: Disk.Channel; rootStatus: PhysicalVolume.PhysicalRC; id: File.VolumeID; name: ROPE; size: Disk.PageCount; free: Disk.PageCount; [channel: channel, rootStatus: rootStatus, id: id, name: name, size: size, free: free] _ PhysicalVolume.PhysicalInfo[p]; IO.PutRope[out, "\n\nID: "]; IagoOps.PutID[out, id]; IO.PutF1[out, "\nOn drive RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ]; SELECT rootStatus FROM ok => NULL--drop through to after ENDCASE--; wentOffline => { IO.PutRope[out, "\nVolume went offline"]; LOOP }; inconsistent => { IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"]; LOOP }; software => { IO.PutRope[out, "\nCan't find the volume root page (label-check)"]; LOOP }; hardware => { IO.PutRope[out, "\nHard disk error reading the volume root page"]; LOOP }; ENDCASE => ERROR File.Error[rootStatus]; TRUSTED { microcodeInstalled: BOOL; time: BasicTime.GMT; name: ROPE; [microcodeInstalled, time, name] _ FormatDisk.IdentifyInitialMicrocode[channel]; IF microcodeInstalled THEN IO.PutF[out, "\nContains initial microcode %g of %g", [rope[name]], [time[time]] ]; }; IO.PutF[out, "\nName: %g\nSize: %g, free pages: %g\nLogical sub-volumes:", [rope[name]], [integer[size]], [integer[free]] ]; { sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO IO.PutF1[out, "\n At physical page %g, logical id: ", [integer[sv[i].address]] ]; IagoOps.PutID[out, sv[i].id]; IO.PutF[out, ", pages [%g..%g)", [integer[sv[i].start]], [integer[sv[i].start+sv[i].size]] ]; ENDLOOP; }; ENDLOOP; }; EraseLogical: PUBLIC PROC [in, out: STREAM] = { v: File.Volume = IagoOps.GetLogical[in, out]; IF IagoOps.ConfirmDestruction[in, out, "the entire logical volume"] THEN { IO.PutRope[out, "\nErasing ... "]; FileBackdoor.EraseVolume[v]; IO.PutRope[out, "done"]; FSBackdoor.CloseVolume[v]; }; }; FormatOrScan: PUBLIC PROC [in, out: STREAM, format: BOOL] = TRUSTED { d: Disk.Channel = IagoOps.GetDrive[in, out]; alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND IagoOps.CheckAltoRegions[in, out]; passes: INT = IagoOps.GetNumber[in, out, 10, 999, "\nHow many check passes should I make? ", "? Type how often I should make transfers over the entire disk trying to find bad spots"]; IF NOT format OR IagoOps.ConfirmDestruction[in, out, Rope.Concat[ "the entire disk drive", IF alto THEN " (excluding Alto regions)" ELSE NIL ] ] THEN DoFormatOrScan[in, out, d, format, passes]; }; DoFormatOrScan: UNSAFE PROC [in, out: STREAM, d: Disk.Channel, format: BOOL, passes: INT] = UNCHECKED { initLabel: Disk.Label; pageSpace: VM.Interval = VM.Allocate[VM.PagesForWords[File.wordsPerPage]]; data: LONG POINTER = VM.AddressForPageNumber[pageSpace.page]; soft, hard: INT _ 0; BadList: TYPE = LIST OF RECORD[ page: Disk.PageNumber, correctable: BOOL, dontReport: BOOL _ FALSE]; badPages: BadList _ NIL; errorsThisPass: BOOL; SweepDisk: UNSAFE PROC [out: STREAM, d: Disk.Channel, operation: FormatDisk.Operation] = UNCHECKED { origin: Disk.PageNumber _ [0]; label: Disk.Label _ initLabel; IO.PutRope[out, SELECT operation FROM format => "\nFormatting ... ", write => "\nWriting ... ", read => "\nReading ... ", verify => "\nVerifying ... " ENDCASE => ERROR]; errorsThisPass _ FALSE; DO firstPage: Disk.PageNumber; pageCount: Disk.PageCount; [firstPage, pageCount] _ IagoOps.NextRun[d, origin]; IF pageCount < 0 THEN EXIT; FormatDisk.Sweep[d, operation, firstPage, pageCount, @label, data]; origin _ [firstPage+pageCount]; ENDLOOP; IF NOT errorsThisPass THEN IO.PutRope[out, "no errors"]; }; IF format THEN CloseFSVolumes[]; { ENABLE { UNWIND => VM.Free[pageSpace]; FormatDisk.BadPage => { <<[page: Disk.PageNumber, correctable: BOOL]>> b: BadList; FOR b _ badPages, b.rest UNTIL b = NIL DO IF b.first.page = page THEN { IF b.first.correctable THEN { IF NOT correctable THEN { b.first.correctable _ FALSE; soft _ soft-1; hard _ hard+1 }; }; EXIT }; REPEAT FINISHED => { badPages _ b _ CONS[first: [page: page, correctable: correctable], rest: badPages]; IF correctable THEN soft _ soft+1 ELSE hard _ hard+1; } ENDLOOP; IF hard > 100 THEN { IO.PutRope[out, "\nThere are over 100 hard disk errors. Continue? "]; IF NOT IagoOps.Confirm[in, out] THEN CONTINUE; hard _ 0; }; IF NOT b.first.dontReport THEN { logicalDiskCyl: CARDINAL; physicalDiskCyl, physicalDiskTrack, physicalDiskSector: CARDINAL; cylinder, track, sector, temp: CARDINAL; device: DiskFace.DeviceHandle _ Disk.GetDeviceFromChannel[d]; IO.PutRope[out, IF errorsThisPass THEN ", " ELSE "\nDisk error(s): "]; errorsThisPass _ TRUE; [cylinders: physicalDiskCyl, movingHeads: physicalDiskTrack, sectorsPerTrack: physicalDiskSector] _ DiskFace.GetTrueDeviceAttributes[device]; [cylinders: logicalDiskCyl] _ DiskFace.GetDeviceAttributes[device]; IF logicalDiskCyl # physicalDiskCyl THEN { <> [quotient: temp, remainder: sector] _ Basics.LongDivMod[ num: page, den: physicalDiskSector ]; [quotient: track, remainder: cylinder] _ Basics.DivMod[ num: temp, den: physicalDiskCyl]; } ELSE { <> [quotient: cylinder, remainder: temp] _ Basics.LongDivMod[ num: page, den: physicalDiskSector * physicalDiskTrack]; [quotient: track, remainder: sector] _ Basics.DivMod[ num: temp, den: physicalDiskSector]; }; IO.PutF[out, "%bB[c:%g, t:%g, s:%g](%g)", [integer[page]], [cardinal[cylinder]], [cardinal[track]], [cardinal[sector]], [rope[IF correctable THEN "soft" ELSE "hard"]] ]; b.first.dontReport _ NOT correctable; -- suppress messages on subsequent passes }; RESUME }; }; VM.Pin[pageSpace]; IF format THEN { init: BOOL _ TRUE; Randomize: UNSAFE PROC = UNCHECKED { RandomizeOne[LOOPHOLE[LONG[@initLabel]], SIZE[Disk.Label]]; RandomizeOne[data, File.wordsPerPage]; init _ NOT init; }; RandomizeOne: UNSAFE PROC [where: LONG POINTER, nwords: CARDINAL] = UNCHECKED { array: LONG POINTER TO ARRAY OF CARDINAL = LOOPHOLE[where]; FOR i: CARDINAL IN [0..nwords) DO array[i] _ IF init THEN Basics.LowHalf[BasicTime.GetClockPulses[]] ELSE Basics.BITNOT[array[i]]; ENDLOOP; }; Randomize[]; SweepDisk[out, d, format]; SweepDisk[out, d, verify]; THROUGH [1..passes] DO <> Randomize[]; SweepDisk[out, d, write]; SweepDisk[out, d, verify]; ENDLOOP; } ELSE THROUGH [1..passes] DO SweepDisk[out, d, read] ENDLOOP; <> { p: PhysicalVolume.Physical _ NIL; IF NOT format THEN FOR p _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] DO IF p = NIL OR PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT; ENDLOOP; IF p = NIL THEN { IO.PutRope[out, "\nCreating a physical volume to hold the bad page table ..."]; p _ FileBackdoor.CreatePhysicalVolume[ where: d, name: "JustForTheBadPageTable", id: IagoOps.NewID[]]; IO.PutRope[out, "done"]; }; IF badPages # NIL THEN { rc: PhysicalVolume.PhysicalRC _ ok; IO.PutRope[out, "\nRecording bad pages in physical bad page table ... "]; FOR b: BadList _ badPages, b.rest UNTIL b = NIL OR rc # ok DO full: BOOL _ FALSE; IF NOT b.first.correctable THEN { [full, rc] _ FileBackdoor.PhysicalPageBad[p, b.first.page]; IF full THEN { IO.PutRope[out, "\nOops, bad page table full. Aborting."]; GO TO tableFull; }; }; ENDLOOP; IF rc = ok THEN IO.PutRope[out, " done" ] ELSE ERROR File.Error[rc]; EXITS tableFull => {}; }; }; }; VM.Free[pageSpace]; }; InstallInitial: PUBLIC PROC [in, out: STREAM] = { d: Disk.Channel = IagoOps.GetDrive[in, out]; name: ROPE = IagoOps.GetFile[ in: in, out: out, extension: ".eb", default: IagoOps.InitialMicrocodeFileName[], check: TRUE ]; IO.PutRope[out, " ... installing"]; [] _ DoInitialMicrocodeInstallation[out, d, name]; }; DoInitialMicrocodeInstallation: PROC [out: STREAM, d: Disk.Channel, name: ROPE, temp: ROPE _ NIL] RETURNS [done: BOOL _ FALSE] = { cp: FS.ComponentPositions; fullFName: ROPE; pages: INT _ 0; strm: STREAM; ProduceStream: PROC [fullGName: ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [STREAM] = { pages_ VM.PagesForBytes[bytes]; RETURN[strm_ IO.ROS[]]; }; { [fullFName, cp, ]_ FS.ExpandName[name]; IF cp.server.length # 0 THEN { FSRemoteFile.Retrieve[fullFName.Substr[cp.server.start, cp.server.length], fullFName.Substr[cp.dir.start-1], BasicTime.nullGMT, ProduceStream ! FS.Error => { out.PutChar['\n]; out.PutRope[error.explanation]; GO TO finish}]; IF strm = NIL THEN GOTO noFile; strm_ IO.RIS[IO.RopeFromROS[strm], strm]; } ELSE { strm _ FS.StreamOpen[fullFName ! FS.Error => { out.PutChar['\n]; out.PutRope[error.explanation]; GO TO finish}]; pages_ VM.PagesForBytes[strm.GetLength[]]; }; strm.SetIndex[0]; TRUSTED { VMReader: UNSAFE PROC [ ptr: LONG POINTER]RETURNS [ok: BOOL _ TRUE] = { index: INT; { IF pages <= 0 THEN RETURN[FALSE]; FOR i: INT IN [0 .. VM.wordsPerPage) DO ENABLE IO.EndOfStream => {index_ i; GOTO eos}; val: Basics.BytePair; val.high_ LOOPHOLE[strm.GetChar[], Basics.BYTE]; val.low_ LOOPHOLE[strm.GetChar[], Basics.BYTE]; (ptr + i)^_ LOOPHOLE[val, WORD]; ENDLOOP; pages _ pages-1; EXITS eos => { FOR j: INT IN [index .. VM.wordsPerPage) DO (ptr + j)^_ 0; ENDLOOP; pages_ 0; }; }; }; FormatDisk.InstallInitialMicrocode[d, VMReader ! FormatDisk.MicrocodeInstallFailure => { IO.PutRope[out, SELECT why FROM emptyFile => "empty microcode file", firstPageBad => "first page of microcode area of disk is bad", flakeyPageFound => "bad page in microcode area of disk", microcodeTooBig => "microcode too big for fixed area of disk", other => "other error during installation", ENDCASE => "unknown error during installation"]; GO TO finish; } ]; }; done _ TRUE; IO.PutRope[out, " done."]; GO TO finish; EXITS noFile => {}; finish => {IF strm # NIL THEN strm.Close[ ! IO.Error => CONTINUE]}; }; }; }.