-- OthelloOpsImpl.mesa 13-Jan-84 11:13:05 by Keith -- Public procs are first, utility procedures in a later separate section. DIRECTORY Boot USING [BootFileType, Location, LVBootFiles, PVBootFiles], BootFile USING [ currentVersion, Header, MapEntry, maxEntriesPerHeader, maxEntriesPerTrailer, Trailer], Buffer USING [Buffer, NSBuffer], Device USING [Type], DiskChannel USING [ Drive, GetDriveAttributes, GetNextDrive, GetPageNumber, nullDrive], Environment USING [ LongNumber, PageFromLongPointer, PageCount, PageNumber, wordsPerPage], File USING [ File, GetSize, ID, MissingPages, nullFile, nullID, PageCount, PageNumber, Unknown], Inline USING [LongDivMod], KernelFile USING [eofLink, GetBootLocation, Link, MakeBootable, MakeUnbootable], KernelPhysicalVolume USING [PVHandle], NSConstants USING [timeServerSocket], NSTypes USING [], OthelloOps USING [ BootFileType, nullSubVolume, SetDebuggerSuccess, SetExpirationDateSuccess, SetGetSwitchesSuccess, SubVolume, TimeServerErrorType], PhysicalVolume USING [ GetAttributes, GetContainingPhysicalVolume, Handle, ID, InterpretHandle, PageNumber], PilotDisk USING [Address, FileID, Handle], ProcessorFace USING [GreenwichMeanTime, SetGreenwichMeanTime], Runtime USING [IsBound], Socket USING [ AssignNetworkAddress, BroadcastAddressFromSocket, BroadcastPacketToAllConnectedNets, ChannelHandle, Create, Delete, GetPacket, GetPacketBytes, GetSendBuffer, NetworkAddress, ReturnBuffer, SetDestination, SetPacketWords, SetWaitTime, TimeOut], Space USING [Access, Interval, Map, PagesFromWords, Unmap], SpecialVolume USING [ GetLogicalVolumeBootFiles, GetNextSubVolume, GetPhysicalVolumeBootFiles, nullSubVolume, SetLogicalVolumeBootFiles, SetPhysicalVolumeBootFiles, SubVolume, SubVolumeUnknown], StartList USING [BootLocation, Header, Switches, VersionID], System USING [ defaultSwitches, GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime, LocalTimeParameters, NetworkAddress, nullID, Switches, UpDown, WestEast], TemporaryBooting USING [MakeBootable, MakeUnbootable], TemporarySetGMT USING [GetNetworkGMT, TimeZoneDirection], Volume USING [ Close, GetStatus, ID, NotOnline, NotOpen, nullID, Open, ReadOnly, Status, Unknown]; OthelloOpsImpl: PROGRAM IMPORTS DiskChannel, Environment, File, Inline, KernelFile, PhysicalVolume, ProcessorFace, Runtime, Socket, Space, SpecialVolume, System, TemporaryBooting, TemporarySetGMT, Volume EXPORTS OthelloOps, PhysicalVolume --[Handle]-- SHARES Buffer, File = BEGIN OPEN OthelloOps; -- Time Server Constants: THESE SHOULD BE IN NSTypes, etc.! TimeHeader: TYPE = MACHINE DEPENDENT RECORD [ id1, id2: CARDINAL, clientType: CARDINAL, version: CARDINAL, type: CARDINAL]; timeRequest: CARDINAL = 1; -- for TimeHeader.type timeResponse: CARDINAL = 2; -- for TimeHeader.type timeProtocolVersion: CARDINAL = 2; -- for TimeHeader.version packetExchangeClient: CARDINAL = 1; -- for TimeHeader.clientType WireTimeFormat: TYPE = MACHINE DEPENDENT RECORD [ timeH(0), timeL(1): CARDINAL, zoneS(2): System.WestEast, zoneH(3): [0..177B], zoneM(4): [0..377B], beginDST(5), endDST(6): WORD, errorAccurate(7): BOOLEAN, errorLow(8), errorHigh(9): CARDINAL]; Handle: PUBLIC --PhysicalVolume.-- TYPE = KernelPhysicalVolume.PVHandle; BadSwitches: PUBLIC ERROR = CODE; SubVolumeUnknown: PUBLIC ERROR [sv: SubVolume] = CODE; TimeServerError: PUBLIC ERROR [error: TimeServerErrorType] = CODE; StartListHeadPtr: TYPE = LONG POINTER TO StartList.Header; Bug: ERROR [bugType: BugType] = CODE; BugType: TYPE = { impossibleBootFileType, startListOffEndOfBootFile, whereDidTheDriveGo, whereDidTheLogicalVolumeGo}; --======================================== -- Public procedures: --======================================== DecodeSwitches: PUBLIC PROC [switchString: LONG STRING] RETURNS [switches: System.Switches ← System.defaultSwitches] = { escapeCount: CARDINAL ← 0; setUpDown: System.UpDown ← down; escapeChar: CHARACTER ← 0C; FOR i: CARDINAL IN [0..switchString.length) DO c: CHARACTER = switchString[i]; SELECT TRUE FROM c = '-, c = '~ => { IF setUpDown = up THEN ERROR BadSwitches ELSE {setUpDown ← up; LOOP}}; c = '\\ => { IF escapeCount # 0 THEN ERROR BadSwitches ELSE {escapeCount ← 1; LOOP}}; escapeCount = 1 => { SELECT c FROM 'n, 'N, 'r, 'R => {switches['\n] ← setUpDown; escapeCount ← 0}; 't, 'T => {switches['\t] ← setUpDown; escapeCount ← 0}; 'b, 'B => {switches['\b] ← setUpDown; escapeCount ← 0}; 'f, 'F => {switches['\f] ← setUpDown; escapeCount ← 0}; 'l, 'L => {switches['\l] ← setUpDown; escapeCount ← 0}; '\\ => {switches['\\] ← setUpDown; escapeCount ← 0}; IN ['0..'7] => { escapeChar ← c - ('0 - 0C); escapeCount ← escapeCount + 1}; ENDCASE => ERROR BadSwitches; LOOP}; escapeCount # 0 => { IF c NOT IN ['0..'7] THEN ERROR BadSwitches; escapeChar ← (c - '0) + (escapeChar - 0C) * 8 + 0C; IF escapeChar > 377C THEN ERROR BadSwitches; IF (escapeCount ← escapeCount + 1) = 4 THEN { switches[escapeChar] ← setUpDown; escapeCount ← 0}; LOOP}; ENDCASE => switches[c] ← setUpDown; -- fall through to here if normal set or escape set -- but not on seeing or while collecting escape setUpDown ← down; ENDLOOP; IF escapeCount # 0 THEN ERROR BadSwitches; RETURN}; VolumeNotClosed: PUBLIC ERROR = CODE; DeleteTempFiles: PUBLIC PROC [lvID: Volume.ID] = BEGIN status: Volume.Status = Volume.GetStatus[lvID]; IF status = openRead OR status = openReadWrite THEN ERROR VolumeNotClosed; Volume.Open[lvID]; -- Deletes any temporary files. Volume.Close[lvID]; END; GetDriveSize: PUBLIC PROC [h: Handle] RETURNS [nPages: LONG CARDINAL] = { RETURN[DiskChannel.GetDriveAttributes[h.drive].nPages]}; GetNextSubVolume: PUBLIC PROC [pvID: PhysicalVolume.ID, thisSv: SubVolume] RETURNS [SubVolume] = BEGIN sv: SpecialVolume.SubVolume = SpecialVolume.GetNextSubVolume[ pvID, IF thisSv = nullSubVolume THEN SpecialVolume.nullSubVolume ELSE [ lvID: thisSv.lvID, firstLVPageNumber: thisSv.firstLVPageNumber, firstPVPageNumber: thisSv.firstPVPageNumber, subVolumeSize: thisSv.subVolumeSize] ! SpecialVolume.SubVolumeUnknown => GOTO error]; RETURN[ IF sv = SpecialVolume.nullSubVolume THEN nullSubVolume ELSE [ lvID: sv.lvID, subVolumeSize: sv.subVolumeSize, firstLVPageNumber: sv.firstLVPageNumber, firstPVPageNumber: sv.firstPVPageNumber]]; EXITS error => ERROR SubVolumeUnknown[thisSv]; END; GetTimeFromTimeServer: PUBLIC PROC RETURNS [ serverTime: System.GreenwichMeanTime, serverLTPs: System.LocalTimeParameters] = BEGIN pfGMT: ProcessorFace.GreenwichMeanTime; isValid: BOOLEAN; IF Runtime.IsBound[LOOPHOLE[Socket.Create]] THEN [isValid, serverTime, serverLTPs] ← GetOISCPTime[] ELSE { zoneDirection: TemporarySetGMT.TimeZoneDirection; [networkTimeFound: isValid, timeFromNetwork: pfGMT, zoneDirection: zoneDirection, zone: serverLTPs.zone, zoneMinutes: serverLTPs.zoneMinutes, beginDST: serverLTPs.beginDST, endDST: serverLTPs.endDST] ← TemporarySetGMT.GetNetworkGMT[]; serverTime ← [pfGMT]; serverLTPs.direction ← IF zoneDirection = east THEN east ELSE west}; IF ~isValid THEN ERROR TimeServerError[noResponse]; END; IsTimeValid: PUBLIC PROC RETURNS [valid: BOOLEAN] = { RETURN[System.GetGreenwichMeanTime[] # System.gmtEpoch]}; MakeBootable: PUBLIC PROC [ file: File.File, type: BootFileType, firstPage: File.PageNumber] = BEGIN SELECT type FROM pilot => TemporaryBooting.MakeBootable[file, firstPage]; ENDCASE => BEGIN lvID: Volume.ID = file.volumeID; l: KernelFile.Link = KernelFile.MakeBootable[ file: file, firstPage: firstPage, count: File.GetSize[file] - firstPage, lastLink: KernelFile.eofLink]; END; END; MakeUnbootable: PUBLIC PROC [ file: File.File, type: BootFileType, firstPage: File.PageNumber] = BEGIN SELECT type FROM pilot => TemporaryBooting.MakeUnbootable[file, firstPage]; ENDCASE => KernelFile.MakeUnbootable[ file: file, firstPage: firstPage, count: File.GetSize[file] - firstPage]; END; SetProcessorTime: PUBLIC PROC [time: System.GreenwichMeanTime] = { ProcessorFace.SetGreenwichMeanTime[time]}; SetVolumeBootFile: PUBLIC PROC [ file: File.File, type: BootFileType, firstPage: File.PageNumber] = BEGIN bootFiles: Boot.LVBootFiles; lvID: Volume.ID = file.volumeID; pilotDiskFileID: PilotDisk.FileID ← [volumeRelative[file.fileID]]; addr: PilotDisk.Address = KernelFile.GetBootLocation[ file, firstPage].diskAddress; SpecialVolume.GetLogicalVolumeBootFiles[lvID, @bootFiles]; bootFiles[ConvertBootFileType[type]] ← [pilotDiskFileID, firstPage, addr]; SpecialVolume.SetLogicalVolumeBootFiles[lvID, @bootFiles]; END; SetPhysicalVolumeBootFile: PUBLIC PROC [ file: File.File, type: BootFileType, firstPage: File.PageNumber] = BEGIN pilotDiskFileID: PilotDisk.FileID ← [volumeRelative[file.fileID]]; pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[ file.volumeID]; addr: PilotDisk.Address = KernelFile.GetBootLocation[ file, firstPage].diskAddress; pBootFiles: Boot.PVBootFiles; SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @pBootFiles]; pBootFiles[ConvertBootFileType[type]] ← [pilotDiskFileID, firstPage, addr]; SpecialVolume.SetPhysicalVolumeBootFiles[pvID, @pBootFiles]; END; GetVolumeBootFile: PUBLIC PROC [lvID: Volume.ID, type: BootFileType] RETURNS [file: File.File, firstPage: File.PageNumber] = BEGIN bootFiles: Boot.LVBootFiles; cType: Boot.BootFileType = ConvertBootFileType[type]; SpecialVolume.GetLogicalVolumeBootFiles[lvID, @bootFiles]; IF bootFiles[cType].fID.fileID = File.nullID THEN RETURN[File.nullFile, 0] ELSE { file ← [bootFiles[cType].fID.fileID, lvID]; RETURN[file, bootFiles[cType].firstPage]}; END; GetPhysicalVolumeBootFile: PUBLIC PROC [ pvID: PhysicalVolume.ID, type: BootFileType] RETURNS [file: File.File, firstPage: File.PageNumber] = BEGIN cType: Boot.BootFileType = ConvertBootFileType[type]; bootFiles: Boot.PVBootFiles; lvID: Volume.ID; -- Begin main text of GetPhysicalVolumeBootFile: SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @bootFiles]; IF bootFiles[cType].fID.fileID = File.nullID THEN RETURN[File.nullFile, 0] ELSE BEGIN -- determine what logical volume we are on given pvID and bootFiles[cType].da bootFilePage: PhysicalVolume.PageNumber; drive: DiskChannel.Drive; subVolume: SpecialVolume.SubVolume; instance: PhysicalVolume.Handle = PhysicalVolume.GetAttributes[ pvID].instance; index: CARDINAL ← PhysicalVolume.InterpretHandle[instance].index; -- Find drive that the index corresponds to: FOR drive ← DiskChannel.GetNextDrive[DiskChannel.nullDrive], DiskChannel.GetNextDrive[drive] WHILE drive # DiskChannel.nullDrive DO IF DiskChannel.GetDriveAttributes[drive].deviceOrdinal = index THEN EXIT; REPEAT FINISHED => Bug[whereDidTheDriveGo]; ENDLOOP; -- Find volume the boot file page lives in: bootFilePage ← DiskChannel.GetPageNumber[drive, bootFiles[cType].da]; FOR subVolume ← SpecialVolume.GetNextSubVolume[ pvID, SpecialVolume.nullSubVolume], SpecialVolume.GetNextSubVolume[ pvID, subVolume] UNTIL subVolume = SpecialVolume.nullSubVolume DO IF bootFilePage IN [subVolume.firstPVPageNumber..subVolume.firstPVPageNumber + subVolume.subVolumeSize) THEN { lvID ← subVolume.lvID; EXIT}; REPEAT FINISHED => Bug[whereDidTheLogicalVolumeGo]; ENDLOOP; file ← [bootFiles[cType].fID.fileID, lvID]; RETURN[file, bootFiles[cType].firstPage]; END; END; --GetPhysicalVolumeBootFile-- VoidVolumeBootFile: PUBLIC PROC [lvID: Volume.ID, type: BootFileType] = BEGIN pBootFiles: Boot.LVBootFiles; SpecialVolume.GetLogicalVolumeBootFiles[lvID, @pBootFiles]; pBootFiles[ConvertBootFileType[type]].fID.fileID ← File.nullID; SpecialVolume.SetLogicalVolumeBootFiles[lvID, @pBootFiles]; END; VoidPhysicalVolumeBootFile: PUBLIC PROC [ pvID: PhysicalVolume.ID, type: BootFileType] = BEGIN pBootFiles: Boot.PVBootFiles; SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @pBootFiles]; pBootFiles[ConvertBootFileType[type]].fID.fileID ← File.nullID; SpecialVolume.SetPhysicalVolumeBootFiles[pvID, @pBootFiles]; END; SetDebugger: PUBLIC PROC [ debuggeeFile: File.File, debuggeeFirstPage: File.PageNumber, debugger: Volume.ID, debuggerType: Device.Type, debuggerOrdinal: CARDINAL] RETURNS [outcome: SetDebuggerSuccess] = BEGIN ENABLE NoStartListHeader --[code]-- => {outcome ← code; CONTINUE}; nullID: PilotDisk.FileID ← [unique[System.nullID]]; debuggerBootFiles: Boot.LVBootFiles; pStartListHeader: StartListHeadPtr; debuggerBootFiles ← ALL[[fID: nullID, firstPage:, da:]]; IF debugger # Volume.nullID THEN { SpecialVolume.GetLogicalVolumeBootFiles[debugger, @debuggerBootFiles]; IF debuggerBootFiles[pilot].fID = nullID OR debuggerBootFiles[debugger].fID = nullID OR debuggerBootFiles[debuggee].fID = nullID THEN RETURN[noDebugger]} ELSE {debuggerOrdinal ← 0; debuggerType ← LOOPHOLE[0]}; [outcome, pStartListHeader] ← MapStartListHeader[ debuggeeFile, debuggeeFirstPage]; IF outcome = success THEN BEGIN pStartListHeader.locDebuggerMicrocode ← LOOPHOLE[Boot.Location[ deviceType: debuggerType, deviceOrdinal: debuggerOrdinal, vp: disk[debuggerBootFiles[softMicrocode]]]]; pStartListHeader.locDebuggerGerm ← LOOPHOLE[Boot.Location[ deviceType: debuggerType, deviceOrdinal: debuggerOrdinal, vp: disk[debuggerBootFiles[germ]]]]; pStartListHeader.locDebugger ← LOOPHOLE[Boot.Location[ deviceType: debuggerType, deviceOrdinal: debuggerOrdinal, vp: disk[debuggerBootFiles[debugger]]]]; pStartListHeader.locDebuggee ← LOOPHOLE[Boot.Location[ deviceType: debuggerType, deviceOrdinal: debuggerOrdinal, vp: disk[debuggerBootFiles[debuggee]]]]; END; [] ← Space.Unmap[pStartListHeader]; END; --SetDebugger-- SetExpirationDate: PUBLIC PROC [ file: File.File, firstPage: File.PageNumber, expirationDate: System.GreenwichMeanTime] RETURNS [outcome: SetExpirationDateSuccess] = BEGIN ENABLE NoStartListHeader --[code]-- => {outcome ← code; CONTINUE}; pStartListHeader: StartListHeadPtr; [outcome, pStartListHeader] ← MapStartListHeader[file, firstPage]; IF outcome = success THEN pStartListHeader.expirationDate ← expirationDate; [] ← Space.Unmap[pStartListHeader]; END; SetSwitches: PUBLIC PROC [ file: File.File, firstPage: File.PageNumber, switches: System.Switches] RETURNS [outcome: SetGetSwitchesSuccess] = BEGIN ENABLE NoStartListHeader --[code]-- => {outcome ← code; CONTINUE}; pStartListHeader: StartListHeadPtr; [outcome, pStartListHeader] ← MapStartListHeader[file, firstPage]; IF outcome = success THEN BEGIN pBootHeader: LONG POINTER TO BootFile.Header; pStartListHeader.switches ← LOOPHOLE[switches]; -- Note that MapStartListHeader already mapped the header once, so this -- Map of the bootfile header will succeed again pBootHeader ← Space.Map[ window: [file, firstPage, 1], swapUnits: [unitary[]]].pointer; pBootHeader.switches ← LOOPHOLE[switches]; [] ← Space.Unmap[pBootHeader]; END; [] ← Space.Unmap[pStartListHeader]; END; GetSwitches: PUBLIC PROC [file: File.File, firstPage: File.PageNumber] RETURNS [outcome: SetGetSwitchesSuccess, switches: System.Switches] = BEGIN ENABLE NoStartListHeader --[code]-- => {outcome ← code; CONTINUE}; pStartListHeader: StartListHeadPtr; [outcome, pStartListHeader] ← MapStartListHeader[file, firstPage, readOnly]; IF outcome = success THEN switches ← LOOPHOLE[pStartListHeader.switches]; [] ← Space.Unmap[pStartListHeader]; END; --======================================== -- Private, utility procedures: --======================================== ConvertBootFileType: PROC [x: BootFileType] RETURNS [Boot.BootFileType] = BEGIN SELECT x FROM hardMicrocode => RETURN[hardMicrocode]; softMicrocode => RETURN[softMicrocode]; germ => RETURN[germ]; pilot => RETURN[pilot]; ENDCASE => ERROR Bug[impossibleBootFileType]; END; GetOISCPTime: PROC RETURNS [ valid: BOOLEAN ← FALSE, time: System.GreenwichMeanTime, ltp: System.LocalTimeParameters] = BEGIN cH: Socket.ChannelHandle; id1: CARDINAL = 12345; id2: CARDINAL = 6789; target: System.NetworkAddress = Socket.BroadcastAddressFromSocket[ NSConstants.timeServerSocket]; timeHeader: LONG POINTER TO TimeHeader; cH ← Socket.Create[local: Socket.AssignNetworkAddress[], receive: 1]; Socket.SetWaitTime[cH, 700]; -- milliseconds THROUGH [0..3) DO sendBuf: Buffer.NSBuffer ← Socket.GetSendBuffer[cH]; Socket.SetPacketWords[sendBuf, SIZE[TimeHeader]]; sendBuf.ns.packetType ← packetExchange; Socket.SetDestination[sendBuf, target]; timeHeader ← LOOPHOLE[@sendBuf.ns.nsWords]; timeHeader↑ ← [ id1, id2, packetExchangeClient, timeProtocolVersion, timeRequest]; Socket.BroadcastPacketToAllConnectedNets[cH, sendBuf]; DO recBuf: Buffer.NSBuffer; recBuf ← Socket.GetPacket[cH ! Socket.TimeOut => EXIT]; timeHeader ← LOOPHOLE[@recBuf.ns.nsWords]; SELECT TRUE FROM Socket.GetPacketBytes[recBuf] < 2 * (SIZE[WireTimeFormat] + 1) OR (timeHeader.id1 # id1) OR (timeHeader.id2 # id2) OR (timeHeader.type # timeResponse) => LOOP; ENDCASE => BEGIN wt: LONG POINTER TO WireTimeFormat = LOOPHOLE[@recBuf.ns.nsWords[ SIZE[TimeHeader]]]; valid ← TRUE; time ← LOOPHOLE[Environment.LongNumber[ num[lowbits: wt.timeL, highbits: wt.timeH]]]; ltp ← [ zone: wt.zoneH, direction: wt.zoneS, zoneMinutes: wt.zoneM, beginDST: wt.beginDST, endDST: wt.endDST]; Socket.ReturnBuffer[recBuf]; GOTO done; END; ENDLOOP; REPEAT done => NULL; ENDLOOP; Socket.Delete[cH]; END; --GetOISCPTime-- NoStartListHeader: PRIVATE ERROR [code: SetGetSwitchesSuccess] = CODE; MapStartListHeader: PROC [ file: File.File, firstPage: File.PageNumber, access: Space.Access ← readWrite] RETURNS [outcome: SetGetSwitchesSuccess, pStartListHeader: StartListHeadPtr] = << Maps space (readWrite or readOnly) to the page containing the start list header in the boot file starting at firstPage of file, and returns a pointer to it. If any trouble, NoStartListHeader is raised (and the space is not mapped).>> BEGIN shortBootFile: SetGetSwitchesSuccess = SetGetSwitchesSuccess[other]; pBootHeader: LONG POINTER TO BootFile.Header ← NIL; -- and space. startListPage: Environment.PageNumber; offsetStartListInPage: CARDINAL; remainingBootFilePages: Environment.PageCount; headerPage: File.PageNumber; nEntries: CARDINAL; entries: LONG POINTER TO ARRAY [0..0) OF BootFile.MapEntry; bootFileSize: File.PageCount; BEGIN ENABLE UNWIND => IF pBootHeader # NIL THEN pBootHeader ← Space.Unmap[pBootHeader]; bootFileSize ← File.GetSize[ file ! File.Unknown, Volume.Unknown, Volume.NotOnline, Volume.NotOpen => ERROR NoStartListHeader[nullBootFile]]; -- Map space to boot file header, get location of start list out of it: IF firstPage > bootFileSize THEN ERROR NoStartListHeader[shortBootFile]; pBootHeader ← Space.Map[ -- map to header page window: [file, firstPage, 1], swapUnits: [unitary[]], access: access ! Volume.ReadOnly => ERROR NoStartListHeader[cantWriteBootFile]; File.MissingPages => ERROR NoStartListHeader[other]].mapUnit.pointer; IF pBootHeader.version # BootFile.currentVersion THEN ERROR NoStartListHeader[other]; startListPage ← Environment.PageFromLongPointer[pBootHeader.pStartListHeader]; offsetStartListInPage ← Inline.LongDivMod[ num: LOOPHOLE[pBootHeader.pStartListHeader], den: Environment.wordsPerPage].remainder; -- Set up loop variables: remainingBootFilePages ← pBootHeader.countData; entries ← @pBootHeader.entries; nEntries ← BootFile.maxEntriesPerHeader; headerPage ← firstPage; DO --until get to header page which describes page containing start list-- -- At this point we have either a boot file Header or Trailer page, -- and are looking at the map entries in it, -- searching for the vm page containing the start list. nEntries ← CARDINAL[MIN[remainingBootFilePages, nEntries]]; FOR k: CARDINAL IN [0..nEntries) DO IF entries[k].virtual = startListPage THEN BEGIN -- found file page containing start list. startListHeaderPages: Environment.PageCount = Space.PagesFromWords[ StartList.Header.SIZE + Environment.wordsPerPage - 1]; -- can start anywhere in page. pBootHeader ← Space.Unmap[pBootHeader]; -- unmap the header/trailer page. -- Map the start list header: IF headerPage + 1 + k + startListHeaderPages > bootFileSize THEN ERROR NoStartListHeader[shortBootFile]; pStartListHeader ← Space.Map[ window: [file, headerPage + 1 + k, startListHeaderPages], swapUnits: [unitary[]], access: access ! Volume.ReadOnly => ERROR NoStartListHeader[cantWriteBootFile]; File.MissingPages => ERROR NoStartListHeader[other]].mapUnit.pointer + offsetStartListInPage; RETURN[ outcome: IF pStartListHeader.version # StartList.VersionID THEN startListHeaderHasBadVersion ELSE success, pStartListHeader: pStartListHeader]; END; ENDLOOP; -- It's not described in this Header or Trailer page; go for the next: headerPage ← headerPage + 1 + nEntries; -- step past header and data pages. remainingBootFilePages ← remainingBootFilePages - nEntries; pBootHeader ← Space.Unmap[pBootHeader]; IF remainingBootFilePages = 0 THEN ERROR Bug[startListOffEndOfBootFile]; IF headerPage > bootFileSize THEN ERROR NoStartListHeader[shortBootFile]; pBootHeader ← Space.Map[ -- map to trailer page window: [file, headerPage, 1], swapUnits: [unitary[]], access: access ! Volume.ReadOnly => ERROR NoStartListHeader[cantWriteBootFile]; File.MissingPages => ERROR NoStartListHeader[other]].mapUnit.pointer; entries ← @LOOPHOLE[pBootHeader, LONG POINTER TO BootFile.Trailer].entries; nEntries ← BootFile.maxEntriesPerTrailer; ENDLOOP; END; --ENABLE UNWIND-- END; --GetStartListHeader-- END. LOG May 31, 80 9:54 PM Forrest Re-created file from combines of old VolumeImplA & B. Jul 13, 80 8:10 PM Forrest Another run at the old fence. 18-Aug-81 14:36:15 Forrest new switches. 16-Nov-81 11:38:25 pasqua fold in bootstrap changes made by Knutsen 17-Nov-81 17:33:25 Forrest change GetOISCP time to not reference SocketInternal; implement SetExpirationDate 26-Jan-83 18:18:31 Johnsson only one \ for excapes. 15-Mar-83 14:11:25 Thiara Update To Klamath (space stuff). Added to GetPhysicalVolumeBootFile to find logical volume. Changed DeleteTempFiles. 31-Mar-83 10:51:36 Luniewski Fold in Sierra changes: 13-Apr-83 13:34:43 pasqua OIS => NS. Some things in SpecialFile moved to KernelFile. 14-Apr-83 11:20:09 pasqua SetDebugger should be PUBLIC. 22-Apr-83 14:29:12 DKnutsen MapStartListHeader sometimes didn't map space; and clients wrongly always Deallocated it. Reorganized MapStartListHeader. It now checks for short boot file, version mismatches, etc. and handles own error cleanups; now handles case of Header overlapping page boundary. Use Map rather than MapAt. Rearranged to public first, private later. 12-May-83 10:22:10 DKnutsen Allow GetSwitches on readOnly volume 13-May-83 12:49:47 DKnutsen Prev edit didn't fix second Map. 13-Jul-83 13:49:40 Luniewski Convert to 11.0b. 16-Aug-83 16:39:38 pasqua DeleteTempFiles did too much work. 16-Sep-83 12:54:12 marzullo SetSwitches did not set the switches into the bootfile header. 29-Nov-83 12:13:55 DKnutsen FindLogicalVolume looped for multiple drives. Uninitialized variable in DeleteTempFiles. Shoot loopholes and uninitialized warnings. 13-Jan-84 10:50:34 marzullo GetOISCPTime changed to new protocol.