<> <> <> <> <<>> DIRECTORY Basics USING[ BITNOT, LowHalf ], BasicTime USING[ FromPupTime, GetClockPulses, GMT, TimeNotKnown ], Booting USING[ Boot, Switches, switches ], DebuggerSwap USING [EnableSwatWatcher], Disk USING [Channel, DriveAttributes, Label, NextChannel, PageCount, PageNumber], File USING [EraseVolume, Error, FindVolumeFromName, FindVM, FP, GetRoot, GetVolumeID, GetVolumeName, GetVolumePages, Handle, Info, IsDebugger, LogicalInfo, MarkDebugger, NextVolume, nullFP, Open, RC, SetRoot, SetSize, SystemVolume, Volume, VolumeID, VolumeFile, wordsPerPage], FileExtra USING [CreateLogicalVolume, CreatePhysicalVolume, PhysicalPageBad], FormatDisk USING [BadPage, IdentifyInitialMicrocode, InstallInitialMicrocode, MicrocodeInstallFailure, Operation, Sweep], FS USING[ Close, ComponentPositions, Copy, Create, Delete, EnumerateForInfo, EnumerateForNames, Error, ExpandName, GetInfo, InfoProc, NameProc, Open, OpenFile, Read, Rename, SetKeep, SetDefaultWDir ], FSBackdoor USING[ CloseVolume, EnumerateCacheForNames, Flush, FNameFromHandle, GetFileHandle, NameProc, ScavengeDirectoryAndCache ], GermSwap USING[ bootedFrom, Switch ], IagoOps --USING everything--, IO USING[ PutChar, PutF, PutRope, STREAM ], PhysicalVolume USING[ GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SetPhysicalRoot, SubVolumes ], ProcessorFace USING[ processorID ], PupDefs USING[ GetMyName ], Rope USING [ Cat, Equal, Fetch, Find, Index, Length, ROPE, Run, Substr ], SimpleTerminal USING [TurnOff, TurnOn], SystemVersion USING[ machineType, uCodeCedar, uCodeDate, uCodeFloatingPoint, uCodeVersion ], UserCredentials USING [ChangeState, Login, LoginOptions], VM USING[ AddressForPageNumber, Allocate, Free, Interval, PagesForWords, Pin], VMSideDoor USING [rmPages]; IagoCommandsImpl: CEDAR PROGRAM IMPORTS Basics, BasicTime, Booting, DebuggerSwap, Disk, File, FileExtra, FormatDisk, FS, FSBackdoor, GermSwap, IagoOps, IO, PhysicalVolume, ProcessorFace, PupDefs, Rope, SimpleTerminal, SystemVersion, UserCredentials, VM, VMSideDoor = BEGIN <<******** The command subroutines (alphabetic order) ********>> Attach: PROC[in, out: IO.STREAM] = BEGIN from: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "From ", check: TRUE]; to: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "To "]; out.PutRope[" ... "]; FS.Copy[from: from, to: to, remoteCheck: FALSE, attach: TRUE]; out.PutRope["done"]; END; BootLogical: PROC[in, out: IO.STREAM] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out]; reject: Rope.ROPE = Booting.Boot[[logical[v]], IagoOps.GetSwitches[in, out]]; IF reject # NIL THEN { out.PutRope[" ... couldn't: "]; out.PutRope[reject] }; END; Copy: PROC[in, out: IO.STREAM] = BEGIN from: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "From ", check: TRUE]; to: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "To "]; out.PutRope[" ... "]; FS.Copy[from: from, to: to]; out.PutRope["done"]; END; CreateLogical: PROC[in, out: IO.STREAM] = BEGIN name: Rope.ROPE _ IF File.FindVolumeFromName[IagoOps.clientVolName] = NIL THEN IagoOps.clientVolName ELSE NIL; size: INT; p: PhysicalVolume.Physical; free: INT; HelpCreation: PROC = BEGIN out.PutRope["? type a string which is not the same as the name of any existing volume"]; END; DO name _ IagoOps.GetArg[ in: in, out: out, prompt: Rope.Cat["\nNew volume name: "], default: name, help: HelpCreation]; IF File.FindVolumeFromName[name] # NIL THEN out.PutRope[" ... 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]; out.PutRope["\nAre you sure? "]; IF IagoOps.Confirm[in, out] THEN BEGIN new: File.Volume _ NIL; out.PutRope[" ... "]; new _ FileExtra.CreateLogicalVolume[ id: IagoOps.NewID[], where: LIST[p], size: size, name: name]; out.PutRope["done"]; IagoOps.clientVolName _ name; out.PutRope["\nDo you want to use this as the system volume for a disk world-swap debugger (\"CoCedar\")? "]; IF IagoOps.Confirm[in, out] THEN File.MarkDebugger[new, TRUE]; END; END; CloseFSVolumes: PROC = BEGIN FOR v: File.Volume _ File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO FSBackdoor.CloseVolume[v] ENDLOOP; END; CreatePhysical: PROC[in, out: IO.STREAM] RETURNS[done: BOOL _ FALSE] = BEGIN HelpCreation: PROC = BEGIN out.PutRope["? type a string which is not the same as the name of any existing volume"]; END; d: Disk.Channel = IagoOps.GetDrive[in, out]; alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND IagoOps.CheckAltoRegions[in, out]; name: Rope.ROPE = IagoOps.GetArg[ in: in, out: out, prompt: Rope.Cat["\nNew volume name: "], default: PupDefs.GetMyName[], help: HelpCreation]; IF IagoOps.ConfirmDestruction[in, out, Rope.Cat[ "the entire disk drive", IF alto THEN " (excluding Alto regions)" ELSE NIL ] ] THEN BEGIN new: PhysicalVolume.Physical; CloseFSVolumes[]; new _ FileExtra.CreatePhysicalVolume[where: d, name: name, id: IagoOps.NewID[]]; IagoOps.ReservePages[in, out, new]; done _ TRUE; InstallCredentials[in, out]; DebuggerSwap.EnableSwatWatcher[TRUE]; END; END; CreateUserWorld: PROC[in, out: IO.STREAM] = BEGIN Install: PROC[where: File.Volume, which: File.VolumeFile[microcode..bootFile]] = BEGIN wDir: Rope.ROPE = Rope.Cat["[]<", File.GetVolumeName[where], ">"]; remote: Rope.ROPE = IagoOps.RemoteRootFileName[which]; local: Rope.ROPE = IagoOps.LocalRootFileName[which]; fsFile: FS.OpenFile; file: File.Handle; out.PutF["\nInstalling %g as %g%g ... ", [rope[remote]], [rope[wDir]], [rope[local]] ]; FS.Copy[from: remote, to: local, wDir: wDir]; fsFile _ FS.Open[name: local, lock: write, wDir: wDir]; file _ FSBackdoor.GetFileHandle[fsFile]; File.SetRoot[which, file]; FS.Close[fsFile]; out.PutRope["done"]; END; format, coCedar, protect: BOOL; altoText: Rope.ROPE _ NIL; myName: Rope.ROPE = PupDefs.GetMyName[]; d: Disk.Channel = Disk.NextChannel[NIL]; newP: PhysicalVolume.Physical; newC, newD: File.Volume; out.PutChar['\n]; IagoOps.ReserveAltoRegions[in, out]; IF IagoOps.CheckAltoRegions[in, out] THEN altoText _ " (excluding the Alto disk regions)"; out.PutRope["\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]; out.PutRope["\n\nDo you want to password-protect this disk so that only you can access it? "]; protect _ IagoOps.Confirm[in, out]; out.PutRope["\n\nDo you want to have a disk world-swap debugger (CoCedar) on this disk? "]; coCedar _ IagoOps.Confirm[in, out]; IF NOT Booting.switches[d] THEN BEGIN out.PutRope["\n\nDo you want me to use software from the PreCedar directory instead of the Cedar directory? "]; IF IagoOps.Confirm[in, out] THEN Booting.switches[d] _ TRUE; END; out.PutRope["\n\nI intend to perform the following operations:"]; IF format THEN BEGIN out.PutRope["\n - format the entire disk"]; out.PutRope[altoText]; out.PutChar[';]; END; out.PutF["\n - create a new physical volume named \"%g\";", [rope[myName]] ]; IF coCedar THEN out.PutRope["\n - create a 35000 page debugger logical volume named \"Debugger\" with a 16000 page virtual memory;"]; out.PutF["\n - create a maximum-sized logical volume named \"Cedar\" with a 16000 page virtual memory;\n - install microcode, germ and boot files(s) from the \"%g\" directory;\n - make the \"Cedar\" volume be your physical boot volume;\n - boot the volume(s) to initialize the software.\n", [rope[IF Booting.switches[d] THEN "PreCedar" ELSE "Cedar"]] ]; IF NOT IagoOps.ConfirmDestruction[in, out, Rope.Cat["the entire disk on drive RD0", altoText]] THEN RETURN; CloseFSVolumes[]; out.PutChar['\n]; IF format THEN TRUSTED{ DoFormatOrScan[in: in, out: out, d: d, format: TRUE, passes: 1] }; out.PutRope["\nCreating physical volume ... "]; newP _ FileExtra.CreatePhysicalVolume[where: d, name: myName, id: IagoOps.NewID[]]; IagoOps.ReservePages[in, out, newP]; [] _ UserCredentials.ChangeState[IF protect THEN name ELSE nameHint]; out.PutRope["done"]; IF coCedar THEN BEGIN out.PutRope["\nCreating logical volume \"Debugger\" ... "]; newD _ FileExtra.CreateLogicalVolume[id: IagoOps.NewID[], where: LIST[newP], size: 35000, name: "Debugger"]; File.MarkDebugger[newD, TRUE]; out.PutRope["done. Erasing it ... "]; File.EraseVolume[newD]; out.PutRope["done"]; CreateVMFile[out, newD, 16000]; Install[where: newD, which: bootFile]; END; out.PutRope["\nCreating logical volume \"Cedar\" ... "]; newC _ FileExtra.CreateLogicalVolume[ id: IagoOps.NewID[], where: LIST[newP], size: PhysicalVolume.PhysicalInfo[newP].free, name: "Cedar"]; out.PutRope["done. Erasing it ... "]; File.EraseVolume[newC]; out.PutRope["done"]; CreateVMFile[out, newC, 16000]; 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]; BEGIN init: Rope.ROPE = IagoOps.InitialMicrocodeFileName[]; out.PutRope["\nInstalling "]; out.PutRope[init]; IF DoInstallation[out, d, init] THEN [] _ Booting.Boot[[logical[IF coCedar THEN newD ELSE newC]], [d: Booting.switches[d]] ]; END; END; CreateVM: PROC[in, out: IO.STREAM] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out, "On "]; size: INT = IagoOps.GetSize[in: in, out: out, default: 16000, max: File.GetVolumePages[v].size]; oldFP: File.FP = File.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 BEGIN out.PutF["\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 { out.PutRope[" ... "]; File.SetSize[oldFile, size]; out.PutRope["done"] }; END ELSE CreateVMFile[out, v, size]; -- Create the file ourselves END; CreateVMFile: PROC[out: IO.STREAM, v: File.Volume, size: INT] = BEGIN newFile: FS.OpenFile; vmName: Rope.ROPE = Rope.Cat["[]<", File.GetVolumeName[v], ">", IagoOps.LocalRootFileName[VM]]; out.PutF["\nCreating %g ... ", [rope[vmName]] ]; newFile _ FS.Create[name: vmName, pages: size]; File.SetRoot[VM, FSBackdoor.GetFileHandle[newFile]]; FS.Close[newFile]; out.PutRope["done"]; END; Delete: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; DoName: FS.NameProc = BEGIN count _ count+1; out.PutF["\nDeleting %g ... ", [rope[fullFName]]]; BEGIN ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE }; FS.Delete[fullFName]; out.PutRope["done"]; END; RETURN[TRUE]; END; pattern: Rope.ROPE _ IagoOps.GetFile[ in: in, out: out, pattern: TRUE]; IF Rope.Find[pattern, "!"] < 0 THEN pattern _ pattern.Cat["!L"]; out.PutRope[" ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; DescribeDrives: PROC[in, out: IO.STREAM] = BEGIN 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]; out.PutF["\nRD%g, size: %g pages", [integer[ordinal]], [integer[nPages]], ]; ENDLOOP; END; DescribeLV: PROC[in, out: IO.STREAM] = BEGIN FOR v: File.Volume _ File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO BEGIN ENABLE File.Error => out.PutRope["\nUnexpected File.Error"]--and let it propagate--; id: File.VolumeID; rootStatus, vamStatus: File.RC; name: Rope.ROPE; size, free, freeboard: INT; [id: id, size: size, rootStatus: rootStatus, name: name, vamStatus: vamStatus, free: free, freeboard: freeboard] _ File.LogicalInfo[v]; out.PutRope["\n\nID: "]; IagoOps.PutID[out, File.GetVolumeID[v]]; IF v = File.SystemVolume[] THEN out.PutRope[" (the system volume)"]; SELECT rootStatus FROM ok, nonCedarVolume => NULL; wentOffline => { out.PutRope["\nVolume went offline"]; LOOP }; inconsistent => { out.PutRope["\nRoot page is inconsistent (wrong version?)"]; LOOP }; software => { out.PutRope["\nCan't find the volume root page (label-check)"]; LOOP }; hardware => { out.PutRope["\nHard disk error reading the volume root page"]; LOOP }; ENDCASE => ERROR File.Error[rootStatus]; out.PutF["\nName: %g", [rope[name]] ]; IF File.IsDebugger[v] THEN out.PutRope["\nThis is a debugger volume"]; out.PutF["\nSize: %g", [integer[size]] ]; IF rootStatus = nonCedarVolume THEN { out.PutRope["\nNot a Cedar volume"]; LOOP }; SELECT vamStatus FROM ok => out.PutF[", free pages: %g", [integer[free]] ]; wentOffline => out.PutRope["\nVolume went offline during VAM operation"]; inconsistent => out.PutRope["\nDon't understand the VAM"]; software => out.PutRope["\nCan't find the VAM (label-check)"]; hardware => out.PutRope["\nHard disk error reading the VAM"]; unknownFile => out.PutRope["\nCan't find the VAM (unknown-file)"]; unknownPage => out.PutRope["\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; END; ENDLOOP; END; PutVolumeFile: PROC[out: IO.STREAM, v: File.Volume, vf: File.VolumeFile] = BEGIN fp: File.FP _ File.GetRoot[v, vf].fp; file: File.Handle; name: Rope.ROPE; IF fp = File.nullFP THEN RETURN; out.PutF["\n%g fp: [id:%b,da:%b]", [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 => { out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; file _ NIL; CONTINUE } ]; IF file # NIL THEN BEGIN ENABLE FS.Error => BEGIN out.PutRope[" ... "]; IF error.code = $invalidPropertyPage THEN out.PutRope["not an FS file"] ELSE out.PutRope[error.explanation]; CONTINUE END; out.PutF[", size: %g", [integer[File.Info[file].size]] ]; name _ FSBackdoor.FNameFromHandle[file]; out.PutRope[", name: "]; out.PutRope[name]; END; END; DescribeMachine: PROC[in, out: IO.STREAM] = BEGIN id: RECORD[a,b,c: CARDINAL] = LOOPHOLE[ProcessorFace.processorID]; out.PutF["\nThis processor is a %g, id = [%x,%x,%x], available real memory: %g pages", [rope[SELECT SystemVersion.machineType FROM dolphin => "Dolphin", dorado => "Dorado", dandelion => "Dandelion", dicentra => "Dicentra", ENDCASE => "{unknown processor type!}"]], [integer[id.a]], [integer[id.b]], [integer[id.c]], [integer[VMSideDoor.rmPages]] ]; out.PutF["\nThe microcode is version %g of %g.", [integer[SystemVersion.uCodeVersion]], [time[BasicTime.FromPupTime[SystemVersion.uCodeDate]]] ]; IF NOT SystemVersion.uCodeCedar THEN out.PutRope[" No Cedar microcode."]; IF NOT SystemVersion.uCodeFloatingPoint THEN out.PutRope[" No floating-point microcode."]; END; DescribePV: PROC[in, out: IO.STREAM] = BEGIN 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.ROPE; size: Disk.PageCount; free: Disk.PageCount; [channel: channel, rootStatus: rootStatus, id: id, name: name, size: size, free: free] _ PhysicalVolume.PhysicalInfo[p]; out.PutF["\n\nID: "]; IagoOps.PutID[out, id]; out.PutF["\nOn drive RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ]; SELECT rootStatus FROM ok => NULL--drop through to after ENDCASE--; wentOffline => { out.PutRope["\nVolume went offline"]; LOOP }; inconsistent => { out.PutRope["\nRoot page is inconsistent (wrong version?)"]; LOOP }; software => { out.PutRope["\nCan't find the volume root page (label-check)"]; LOOP }; hardware => { out.PutRope["\nHard disk error reading the volume root page"]; LOOP }; ENDCASE => ERROR File.Error[rootStatus]; TRUSTED BEGIN microcodeInstalled: BOOL; time: BasicTime.GMT; name: Rope.ROPE; [microcodeInstalled, time, name] _ FormatDisk.IdentifyInitialMicrocode[channel]; IF microcodeInstalled THEN BEGIN out.PutF["\nContains initial microcode %g of %g", [rope[name]], [time[time]] ]; END; END; out.PutF["\nName: %g\nSize: %g, free pages: %g\nLogical sub-volumes:", [rope[name]], [integer[size]], [integer[free]] ]; BEGIN sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO out.PutF["\n At physical page %g, logical id: ", [integer[sv[i].address]] ]; IagoOps.PutID[out, sv[i].id]; out.PutF[", pages [%g..%g)", [integer[sv[i].start]], [integer[sv[i].start+sv[i].size]] ]; ENDLOOP; END; ENDLOOP; END; EraseLogical: PROC[in, out: IO.STREAM] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out]; IF IagoOps.ConfirmDestruction[in, out, "the entire logical volume"] THEN BEGIN out.PutRope["\nErasing ... "]; File.EraseVolume[v]; out.PutRope["done"]; FSBackdoor.CloseVolume[v]; END; END; FormatOrScan: UNSAFE PROC[in, out: IO.STREAM, format: BOOL] = UNCHECKED BEGIN 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.Cat[ "the entire disk drive", IF alto THEN " (excluding Alto regions)" ELSE NIL ] ] THEN DoFormatOrScan[in, out, d, format, passes]; END; DoFormatOrScan: UNSAFE PROC[in, out: IO.STREAM, d: Disk.Channel, format: BOOL, passes: INT] = UNCHECKED BEGIN 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: BOOLEAN, reported: BOOL _ FALSE]; badPages: BadList _ NIL; SweepDisk: UNSAFE PROC[out: IO.STREAM, d: Disk.Channel, operation: FormatDisk.Operation] = UNCHECKED BEGIN origin: Disk.PageNumber _ [0]; label: Disk.Label _ initLabel; correctableOnes: BOOL _ FALSE; out.PutRope[SELECT operation FROM format => "\nFormatting ... ", write => "\nWriting ... ", read => "\nReading ... ", verify => "\nVerifying ... " ENDCASE => ERROR]; 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; DO someBad: BOOL _ FALSE; FOR b: BadList _ badPages, b.rest UNTIL b = NIL DO IF (b.first.correctable = correctableOnes) AND NOT b.first.reported THEN BEGIN out.PutF[IF someBad THEN ", %b" ELSE IF correctableOnes THEN "\nSoft errors: %b" ELSE "\nHard errors: %b", [integer[b.first.page]] ]; someBad _ TRUE; b.first.reported _ TRUE; END; ENDLOOP; IF NOT correctableOnes AND NOT someBad THEN out.PutRope["no hard errors"]; IF correctableOnes THEN EXIT ELSE correctableOnes _ TRUE; ENDLOOP; END; IF format THEN CloseFSVolumes[]; BEGIN ENABLE BEGIN UNWIND => VM.Free[pageSpace]; FormatDisk.BadPage--[page: Disk.PageNumber, correctable: BOOLEAN]-- => BEGIN FOR b: BadList _ badPages, b.rest UNTIL b = NIL DO IF b.first.page = page THEN BEGIN IF b.first.correctable THEN BEGIN b.first.reported _ FALSE; IF NOT correctable THEN { b.first.correctable _ FALSE; soft _ soft-1; hard _ hard+1 }; END; RESUME; END; ENDLOOP; badPages _ CONS[first: [page: page, correctable: correctable], rest: badPages]; IF correctable THEN soft _ soft+1 ELSE hard _ hard+1; IF hard > 100 THEN BEGIN out.PutRope["\nThere have been more than 100 hard disk errors. Do you want me to continue? "]; IF NOT IagoOps.Confirm[in, out] THEN CONTINUE; hard _ 0; END; RESUME END; END; VM.Pin[pageSpace]; IF format THEN BEGIN init: BOOL _ TRUE; Randomize: UNSAFE PROC = UNCHECKED BEGIN RandomizeOne[@initLabel, SIZE[Disk.Label]]; RandomizeOne[data, VM.PagesForWords[File.wordsPerPage]]; init _ NOT init; END; RandomizeOne: UNSAFE PROC[where: LONG POINTER, nwords: CARDINAL] = UNCHECKED BEGIN 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; END; Randomize[]; SweepDisk[out, d, format]; SweepDisk[out, d, verify]; THROUGH [1..passes] DO -- First, set label and data to random values Randomize[]; SweepDisk[out, d, write]; SweepDisk[out, d, verify]; ENDLOOP; END ELSE THROUGH [1..passes] DO SweepDisk[out, d, read] ENDLOOP; <> BEGIN p: PhysicalVolume.Physical; FOR p _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO IF PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT ENDLOOP; IF p = NIL THEN BEGIN out.PutRope["\nCreating a physical volume to hold the bad page table ..."]; p _ FileExtra.CreatePhysicalVolume[ where: d, name: "JustForTheBadPageTable", id: IagoOps.NewID[]]; out.PutRope["done"]; END; IF badPages # NIL THEN BEGIN rc: PhysicalVolume.PhysicalRC _ ok; out.PutRope["\nRecording bad pages in physical bad page table ... "]; FOR b: BadList _ badPages, b.rest UNTIL b = NIL OR rc # ok DO IF NOT b.first.correctable THEN rc _ FileExtra.PhysicalPageBad[p, b.first.page]; ENDLOOP; out.PutRope[IF rc = ok THEN "done" ELSE IagoOps.FileError[rc]]; END; END; END; VM.Free[pageSpace]; END; FlushCache: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; DoName: FSBackdoor.NameProc = BEGIN count _ count+1; out.PutF["\nFlushing %g ... ", [rope[fullGName]]]; BEGIN ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE }; FSBackdoor.Flush[fullGName: fullGName, volName: volName]; out.PutRope["done"]; END; RETURN[TRUE]; END; v: File.Volume = IagoOps.GetLogical[in, out, "On "]; volName: Rope.ROPE = File.GetVolumeName[v]; pattern: Rope.ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; out.PutRope[" ... "]; FSBackdoor.EnumerateCacheForNames[DoName, volName, pattern]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; InstallCredentials: PROC[in, out: IO.STREAM] = BEGIN out.PutRope["\nDo you want to password-protect this disk so that only you can access it? "]; [] _ UserCredentials.ChangeState[IF IagoOps.Confirm[in, out] THEN name ELSE nameHint]; END; InstallInitial: PROC[in, out: IO.STREAM] = BEGIN d: Disk.Channel = IagoOps.GetDrive[in, out]; name: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, extension: ".eb", default: IagoOps.InitialMicrocodeFileName[], check: TRUE ]; out.PutRope[" ... installing"]; [] _ DoInstallation[out, d, name]; END; DoInstallation: PROC[out: IO.STREAM, d: Disk.Channel, name: Rope.ROPE] RETURNS[done: BOOL _ TRUE] = BEGIN dest: Rope.ROPE = "InitialMicrocode.eb"; wDir: Rope.ROPE _ NIL; fsFile: FS.OpenFile; pages: INT _ 0; v: File.Volume; FOR v _ File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO IF File.LogicalInfo[v].vamStatus = ok AND File.GetVolumePages[v].free >= 100 THEN EXIT; REPEAT FINISHED => BEGIN out.PutRope[" ... I can't find a suitable logical volume for buffering the file. Create one then try again"]; RETURN END ENDLOOP; wDir _ Rope.Cat["[]<", File.GetVolumeName[v], ">"]; out.PutF[" as %g%g ... ", [rope[wDir]], [rope[dest]] ]; FS.Copy[from: name, to: dest, wDir: wDir]; fsFile _ FS.Open[name: dest, lock: read, wDir: wDir]; pages _ FS.GetInfo[fsFile].pages; TRUSTED BEGIN pos: INT _ 0; Reader: UNSAFE PROC[ ptr: LONG POINTER]RETURNS [ok: BOOL _ TRUE] = BEGIN IF pages <= 0 THEN RETURN[FALSE]; FS.Read[file: fsFile, from: pos, nPages: 1, to: ptr]; pages _ pages-1; pos _ pos+1; END; FormatDisk.InstallInitialMicrocode[d, Reader ! FormatDisk.MicrocodeInstallFailure => BEGIN out.PutRope[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"]; done _ FALSE; CONTINUE END ]; END; FS.Close[fsFile]; IF done THEN out.PutRope["done"]; END; InstallLogicalFile: PROC[in, out: IO.STREAM, which: File.VolumeFile[checkpoint..bootFile]] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out, "For "]; localVolume: Rope.ROPE = Rope.Cat["[]<", File.GetVolumeName[v], ">"]; name: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, extension: IagoOps.ext[which], default: IagoOps.RemoteRootFileName[which], wDir: localVolume, check: TRUE ]; fsFile: FS.OpenFile; file: File.Handle; PhysicalToo: PROC RETURNS[BOOL] = BEGIN IF which # bootFile THEN { out.PutRope["\nInstalling on the physical volume"]; RETURN[TRUE] }; IF File.IsDebugger[v] THEN RETURN[FALSE]; out.PutRope["\nDo you want to use this file when you boot the physical volume? "]; RETURN[IagoOps.Confirm[in, out]] END; <<"name" is a canonicalized FS name.>> out.PutRope[" ... "]; IF name.Length[] # 0 AND name.Fetch[0] = '[ AND Rope.Run[s1: name, s2: localVolume, case: FALSE] # localVolume.Length[] AND ( v # File.SystemVolume[] OR Rope.Run[s1: name, s2: "[]<>"] # 4 ) THEN BEGIN localName: Rope.ROPE _ IagoOps.LocalRootFileName[which]; Help: PROC = { out.PutRope["? Please type the name of an FS local file such as \"a.b\""] }; out.PutRope[IF name.Length[] >=1 AND name.Fetch[1] = '] THEN "that file is on another volume ..." ELSE "that file is on a server ..."]; <> DO localName _ IagoOps.GetArg[ in: in, out: out, prompt: "\nCopy to local file name (please confirm or alter): ", default: localName, help: Help]; IF localName.Length[] = 0 OR localName.Fetch[0] = '[ THEN Help[] ELSE EXIT; ENDLOOP; out.PutRope[" ... copying ... "]; FS.Copy[from: name, to: localName, wDir: localVolume]; fsFile _ FS.Open[name: localName, lock: write, wDir: localVolume]; END ELSE fsFile _ FS.Open[name: name, lock: write, wDir: localVolume]; file _ FSBackdoor.GetFileHandle[fsFile]; IF File.Info[file].volume # v THEN out.PutRope["I'm confused: the file is on the wrong volume"] ELSE { out.PutRope["installing ... "]; File.SetRoot[which, file]; out.PutRope["done"] }; FS.Close[fsFile]; IF PhysicalToo[] THEN { PhysicalVolume.SetPhysicalRoot[v, which]; out.PutRope[" ... done"] }; END; ListCache: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; DoName: FSBackdoor.NameProc = BEGIN count _ count+1; out.PutF["\n %g", [rope[fullGName]]]; RETURN[TRUE]; END; v: File.Volume = IagoOps.GetLogical[in, out, "On "]; pattern: Rope.ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; out.PutRope[" ... "]; FSBackdoor.EnumerateCacheForNames[DoName, File.GetVolumeName[v], pattern]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; ListFileInfo: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; DoName: FS.InfoProc = BEGIN count _ count+1; out.PutF["\n %g, keep: %g, bytes: %g, created: %g", [rope[fullFName]], [cardinal[keep]], [integer[bytes]], [time[created]] ]; IF attachedTo # NIL THEN out.PutF[", attached to %g", [rope[attachedTo]] ]; RETURN[TRUE]; END; pattern: Rope.ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; out.PutRope[" ... "]; FS.EnumerateForInfo[pattern, DoName]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; ListNames: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; prevFile: Rope.ROPE _ NIL; DoName: FS.NameProc = BEGIN newFile, newPrefix: Rope.ROPE; newPos: FS.ComponentPositions; count _ count+1; [fullFName: newFile, cp: newPos] _ FS.ExpandName[fullFName]; newPrefix _ fullFName.Substr[len: newPos.ver.start]; IF prevFile # NIL AND newPos.ver.length # 0 AND prevFile.Equal[newPrefix, FALSE] THEN out.PutF[", %g", [rope[fullFName.Substr[start: newPos.ver.start, len: newPos.ver.length]]] ] ELSE BEGIN prevFile _ newPrefix; out.PutF["\n%g", [rope[fullFName]]]; END; RETURN[TRUE]; END; pattern: Rope.ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; out.PutRope[" ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; Login: PROC[options: UserCredentials.LoginOptions] = BEGIN TurnOnProc: PROC RETURNS [in, out: IO.STREAM] = { [in: in, out: out] _ SimpleTerminal.TurnOn[]; out.PutRope["\nPlease login ...\n"]; }; TurnOffProc: PROC [in, out: IO.STREAM] = { SimpleTerminal.TurnOff[] }; UserCredentials.Login[startInteraction: TurnOnProc, endInteraction: TurnOffProc, options: options]; END; Rename: PROC[in, out: IO.STREAM] = BEGIN from: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "From ", check: TRUE]; to: Rope.ROPE = IagoOps.GetFile[ in: in, out: out, prompt: "To "]; out.PutRope[" ... "]; FS.Rename[from: from, to: to]; out.PutRope["done"]; END; Rollback: PROC[in, out: IO.STREAM] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out]; reject: Rope.ROPE; mySwitches: Booting.Switches _ ALL[FALSE]; mySwitches[r] _ TRUE; reject _ Booting.Boot[[logical[v]], mySwitches]; IF reject # NIL THEN { out.PutRope[" ... couldn't: "]; out.PutRope[reject] }; END; Scavenge: PROC[in, out: IO.STREAM] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out]; out.PutRope[" ... "]; [] _ File.FindVM[]; FSBackdoor.ScavengeDirectoryAndCache[File.GetVolumeName[v]]; out.PutRope["done"]; END; SetKeep: PROC[in, out: IO.STREAM] = BEGIN count: INT _ 0; DoName: FS.NameProc = BEGIN count _ count+1; fullFName _ fullFName.Substr[start: 0, len: fullFName.Index[0, "!"]]; out.PutF["\nSetting %g ... ", [rope[fullFName]]]; BEGIN ENABLE FS.Error => { out.PutRope[error.explanation]; CONTINUE }; FS.SetKeep[fullFName, k]; out.PutRope["done"]; END; RETURN[TRUE]; END; pattern: Rope.ROPE _ IagoOps.GetFile[ in: in, out: out, pattern: TRUE, prompt: "For "]; k: INT = IagoOps.GetNumber[ in, out, 1, LAST[CARDINAL]-1, "\nNumber of versions to keep: ", "? Type how many versions of each file should be kept (excess versions will be deleted)"]; IF Rope.Find[pattern, "!"] < 0 THEN pattern _ pattern.Cat["!H"]; out.PutRope[" ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN out.PutRope["not found"] ELSE out.PutF["\n%g files", [integer[count]] ]; END; SetPhysicalFile: PROC[in, out: IO.STREAM, which: File.VolumeFile[checkpoint..bootFile]] = BEGIN v: File.Volume = IagoOps.GetLogical[in, out, "From "]; out.PutRope[" ... "]; PhysicalVolume.SetPhysicalRoot[v, which]; out.PutRope["done"]; END; SetWDir: PROC[in, out: IO.STREAM] = BEGIN Help: PROC = { out.PutRope["? Type the prefix of a file name, such as \"[]\""] }; name: Rope.ROPE = IagoOps.GetArg[ in: in, out: out, prompt: "\nWorking directory: ", default: IF IagoOps.clientVolName.Length[] # 0 THEN Rope.Cat["[]<", IagoOps.clientVolName, ">"] ELSE NIL, help: Help]; FS.SetDefaultWDir[name]; END; <<******** Main program ********>> DoCommand: PROC[in, out: IO.STREAM, c: IagoOps.Command] RETURNS[exit: BOOL _ FALSE, overwritten: BOOL _ FALSE] = BEGIN ENABLE BEGIN IagoOps.Rubout => { out.PutRope[" XXX"]; CONTINUE }; File.Error => BEGIN out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; CONTINUE END; FS.Error => BEGIN out.PutRope[" ... "]; out.PutRope[ IF error.explanation.Length[] # 0 THEN error.explanation ELSE "FS.Error without a message: consult an expert"]; CONTINUE END; BasicTime.TimeNotKnown => BEGIN out.PutRope[" ... I can't do that without knowing the current time."]; CONTINUE END; END; SELECT c FROM attach => Attach[in, out]; bootLogical => BootLogical[in, out]; checkDrive => TRUSTED{ FormatOrScan[in: in, out: out, format: FALSE] }; copy => Copy[in, out]; createLogical => CreateLogical[in, out]; createPhysical => overwritten _ CreatePhysical[in, out]; createUserWorld => CreateUserWorld[in, out]; createVM => CreateVM[in, out]; delete => Delete[in, out]; describeDrives => DescribeDrives[in, out]; describeLV => DescribeLV[in, out]; describeMachine => DescribeMachine[in, out]; describePV => DescribePV[in, out]; eraseLogical => EraseLogical[in, out]; format => TRUSTED{ FormatOrScan[in: in, out: out, format: TRUE] }; flushCache => FlushCache[in, out]; installBoot => InstallLogicalFile[in, out, bootFile]; installCredentials => InstallCredentials[in, out]; installGerm => InstallLogicalFile[in, out, germ]; installInitial => InstallInitial[in, out]; installMicrocode => InstallLogicalFile[in, out, microcode]; listCache => ListCache[in, out]; listFileInfo => ListFileInfo[in, out]; listNames => ListNames[in, out]; login => Login[[alwaysInteract: TRUE, ignoreDiskEntirely: TRUE]]; quit => { out.PutRope["\nAre you sure? "]; exit _ IagoOps.Confirm[in, out] }; rename => Rename[in, out]; rollbackLogical => Rollback[in, out]; scavenge => Scavenge[in, out]; setKeep => SetKeep[in, out]; setPhysicalBoot => SetPhysicalFile[in, out, bootFile]; setPhysicalGerm => SetPhysicalFile[in, out, germ]; setPhysicalMicrocode => SetPhysicalFile[in, out, microcode]; setWDir => SetWDir[in, out]; ENDCASE => ERROR; END; Main: PROC[in, out: IO.STREAM, mode: { normal, nSwitch, createWorld }] = BEGIN out.PutRope["\n\nType \"?\" at any time if you need help."]; IF Booting.switches[d] THEN out.PutRope["\nBooted with the \"D\" switch: defaults come from PreCedar directory instead of Cedar."]; DO BEGIN ENABLE BEGIN ABORTED => EXIT; IagoOps.Rubout => { out.PutRope[" XXX"]; CONTINUE }; END; c: IagoOps.Command = IagoOps.GetCommand[in: in, out: out, diskReadable: mode = normal]; exit, overwritten: BOOL; [exit, overwritten] _ DoCommand[in, out, c]; IF overwritten THEN mode _ normal; IF exit THEN EXIT; END; ENDLOOP; out.PutRope["\nLeaving Iago ...\n"]; END; DoIt: PROC = BEGIN in, out: IO.STREAM; etherBoot: BOOL = GermSwap.bootedFrom.location.deviceType = ethernet; Login[[ignoreDiskEntirely: Booting.switches[n]]]; IF NOT Booting.switches[n] THEN DebuggerSwap.EnableSwatWatcher[TRUE] -- ELSE it may be enabled by the "I" switch--; [in: in, out: out] _ SimpleTerminal.TurnOn[]; IF etherBoot THEN BEGIN DO out.PutRope["\nDo you want to initialize your disk from scratch for use by Cedar (selecting from the standard options I will offer you)? "]; IF IagoOps.Confirm[in, out ! IagoOps.Rubout => CONTINUE] THEN [] _ DoCommand[in, out, createUserWorld] ELSE EXIT; ENDLOOP; END; SELECT TRUE FROM Booting.switches[n] => BEGIN out.PutRope["\nBooted with the \"N\" switch. Invoking Iago, with restricted command set"]; Main[in: in, out: out, mode: nSwitch]; END; Booting.switches[l] OR etherBoot => BEGIN out.PutRope["\nDo you want to use \"Iago\", the Cedar disk utility program? "]; IF IagoOps.Confirm[in, out ! IagoOps.Rubout => CONTINUE] THEN Main[in: in, out: out, mode: normal]; END; ENDCASE => NULL; SimpleTerminal.TurnOff[]; END; DoIt[]; END.