-- Cedar Remote Debugging: access to world-swap client memory -- WVMOutLd.mesa -- Andrew Birrell February 14, 1983 4:40 pm DIRECTORY Boot USING[ DiskFileID, LVBootFiles ], BootFile USING[ Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, Trailer ], Booting USING[ Boot, Bootee, defaultSwitches, nullBootFile, Switches ], DiskChannel USING[ CompletionHandle, Create, CreateCompletionObject, Delete, Drive, GetDriveTag, GetNextDrive, Handle, IORequest, InitiateIO, nullDrive, WaitAny], File USING[ Capability, Create, Delete, delete, GetSize, MakePermanent, nullID, PageNumber, read, write ], FileTypes USING[ tUntypedFile ], Inline USING[ BITAND ], PageMap USING[ GetF, flagsVacant, Value ], PilotDisk USING[ Label, LabelCheckSum ], PilotSwitches USING[ switches ], SpecialFile USING[ MakeBootable, SetDebuggerFiles ], SpecialSpace USING[ MakeResident, MakeSwappable, realMemorySize ], SpecialVolume USING[ GetLogicalVolumeBootFiles ], Space USING[ CopyIn, CopyOut, Create, defaultWindow, Delete, GetHandle, Handle, LongPointer, LongPointerFromPage, Map, PageFromLongPointer, PageNumber, virtualMemory, VMPageNumber], Volume USING[ GetType, SystemID ], WorldVM USING[ AddressFault, BadWorld ], WVMPrivate; WVMOutLd: MONITOR IMPORTS BootFile, Booting, DiskChannel, File, Inline, PageMap, PilotDisk, PilotSwitches, SpecialFile, SpecialSpace, SpecialVolume, Space, Volume, WorldVM, WVMPrivate EXPORTS WVMPrivate SHARES File-- to create a capability inside VerifyFile --, PageMap -- GetF -- = BEGIN -- Management of outload files -- debugger: File.Capability; debuggee: File.Capability; thisIsADebugger: BOOL _ FALSE; SetupFiles: INTERNAL PROC RETURNS[ok: BOOL] = BEGIN size: CARDINAL = BootFile.MemorySizeToFileSize[SpecialSpace.realMemorySize]; bootFiles: Boot.LVBootFiles; changed: BOOLEAN _ FALSE; IF Volume.GetType[Volume.SystemID[]] # debugger AND Volume.GetType[Volume.SystemID[]] # debuggerDebugger THEN RETURN[FALSE] ELSE ok _ TRUE; SpecialVolume.GetLogicalVolumeBootFiles[Volume.SystemID[], @bootFiles]; debugger _ VerifyFile[bootFiles[debugger], size, @changed]; debuggee _ VerifyFile[bootFiles[debuggee], size, @changed]; IF changed THEN BEGIN SpecialFile.SetDebuggerFiles[debugger: debugger, debuggee: debuggee]; File.MakePermanent[debugger]; File.MakePermanent[debuggee]; END; thisIsADebugger _ TRUE; END; VerifyFile: INTERNAL PROC[old: Boot.DiskFileID, size: CARDINAL, changed: POINTER TO BOOLEAN] RETURNS[ new: File.Capability ] = BEGIN new _ [fID: old.fID, permissions: File.read+File.write+File.delete]; IF old.fID = File.nullID OR File.GetSize[new] < size THEN -- need to create -- BEGIN IF old.fID # File.nullID THEN File.Delete[new]; new _ File.Create[Volume.SystemID[], size, FileTypes.tUntypedFile]; [] _ SpecialFile.MakeBootable[file: new, firstPage: FIRST[File.PageNumber], count: size, lastLink: [0]--cf. CoPilot!--]; changed^ _ TRUE; END; END; outLdSpace: Space.Handle = Space.Create[1, Space.virtualMemory]; outLdPage: File.PageNumber _ LAST[File.PageNumber]; outLdAddress: LONG POINTER = Space.LongPointer[outLdSpace]; ReadOutLdPage: INTERNAL PROC[page: File.PageNumber] = { IF outLdPage # page THEN Space.CopyIn[outLdSpace, [debuggee,outLdPage_page]] }; WriteOutLdPage: INTERNAL PROC = { Space.CopyOut[outLdSpace, [debuggee,outLdPage]] }; InvalidateOutLdPage: INTERNAL PROC = { outLdPage _ LAST[File.PageNumber] }; -- Layout of boot and outload files -- BFMEntryIndex: TYPE = [0..MAX[BootFile.maxEntriesPerHeader, BootFile.maxEntriesPerTrailer]]; BootFileMapItem: TYPE = RECORD [ link: BFMPointer _ NIL, lastMemPage: WVMPrivate.PageNumber, filePageOffset: File.PageNumber, entryCount: BFMEntryIndex, entries: LONG POINTER TO ARRAY [0..0) OF BootFile.Entry]; BFMPointer: TYPE = REF BootFileMapItem; bfmHead: BFMPointer _ NIL; OpenOutLdFile: INTERNAL PROC = BEGIN bfm: BFMPointer; bfh: LONG POINTER TO BootFile.Header; entries: LONG POINTER TO ARRAY [0..0) OF BootFile.Entry; curmax: CARDINAL _ BootFile.maxEntriesPerHeader; curbase: File.PageNumber _ 0; OutLdPageSpace: PROC RETURNS[LONG POINTER] = -- returns pointer into space mapped to outload file map page -- BEGIN space: Space.Handle = Space.Create[1, Space.virtualMemory]; Space.Map[space, [debuggee, curbase]]; RETURN[Space.LongPointer[space]] END; pagesremaining: CARDINAL; bfmHead _ NIL; bfh _ OutLdPageSpace[]; entries _ @bfh.entries; pagesremaining _ bfh.countData; bfmHead _ bfm _ NEW[BootFileMapItem]; DO bfm.entries _ entries; bfm.entryCount _ MIN[curmax, pagesremaining]; bfm.lastMemPage _ bfm.entries[bfm.entryCount-1].page; bfm.filePageOffset _ curbase+1; pagesremaining _ pagesremaining - bfm.entryCount; IF pagesremaining = 0 THEN EXIT; curbase _ bfm.filePageOffset+bfm.entryCount; curmax _ BootFile.maxEntriesPerTrailer; entries _ OutLdPageSpace[] + SIZE[BootFile.Trailer]; bfm.link _ NEW[BootFileMapItem]; bfm _ bfm.link; ENDLOOP; END; CloseOutLdFile: INTERNAL PROC = BEGIN FOR bfmPtr: BFMPointer _ bfmHead, bfmPtr.link UNTIL bfmPtr = NIL DO Space.Delete[Space.GetHandle[Space.PageFromLongPointer[bfmPtr.entries]]] ENDLOOP; bfmHead _ NIL; END; SearchOutLdFile: INTERNAL PROCEDURE [mempage: WVMPrivate.PageNumber] RETURNS[ entry: LONG POINTER TO BootFile.Entry ] = BEGIN -- Returns [NIL,NIL] if page isn't in file -- FOR bfm: BFMPointer _ bfmHead, bfm.link UNTIL bfm = NIL DO IF mempage <= bfm.lastMemPage THEN BEGIN -- it's in this page of the bootFileMap -- FOR i: BFMEntryIndex IN [0..bfm.entryCount] DO SELECT LONG[bfm.entries[i].page] FROM < mempage => NULL; > mempage => RETURN[NIL]; ENDCASE => BEGIN ReadOutLdPage[bfm.filePageOffset+i]; RETURN[ @bfm.entries[i] ]; END REPEAT FINISHED => ERROR ENDLOOP; END; REPEAT FINISHED => RETURN[NIL] ENDLOOP; END; -- Transfers to/from outload file -- ReadOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber] RETURNS[map: WVMPrivate.MapEntry, ok: BOOLEAN] = BEGIN ENABLE UNWIND => NULL; entry: LONG POINTER TO BootFile.Entry = SearchOutLdFile[mempage]; IF entry = NIL THEN IF mempage IN [376B..377B] THEN -- kludge: Germ doesn't write io pages to outload file, so read ours instead -- BEGIN value: PageMap.Value = PageMap.GetF[mempage]; IF value.flags = PageMap.flagsVacant THEN ERROR WorldVM.AddressFault[mempage]; data^ _ LOOPHOLE[WVMPrivate.PageAddress[mempage], LONG POINTER TO WVMPrivate.PageData]^; map _ [flags: [logSE: value.logSingleError, W: TRUE, D: FALSE, R: TRUE], page: value.realPage]; ok _ TRUE; END ELSE RETURN[,FALSE] ELSE BEGIN data^ _ LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^; map _ LOOPHOLE[entry.value]; ok _ TRUE; END; END; WriteOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber, map: WVMPrivate.MapEntry] = BEGIN ENABLE UNWIND => NULL; entry: LONG POINTER TO BootFile.Entry = SearchOutLdFile[mempage]; IF entry = NIL THEN ERROR WorldVM.AddressFault[mempage]; LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^ _ data^; WriteOutLdPage[]; IF NOT entry.value.flags.writeProtected THEN entry.value.flags.dirty _ TRUE; END; -- Transfers to/from backing store -- VolumeObject: TYPE = RECORD [ link: VHandle, driveTag: CARDINAL, handle: DiskChannel.Handle]; completion: DiskChannel.CompletionHandle _ DiskChannel.CreateCompletionObject[]; VHandle: TYPE = REF VolumeObject; vHead: VHandle _ NIL; DiscoverVolumes: INTERNAL PROCEDURE = BEGIN drive: DiskChannel.Drive _ DiskChannel.nullDrive; FOR vh: VHandle _ vHead, vh.link UNTIL vh = NIL DO DiskChannel.Delete[vHead.handle] ENDLOOP; vHead _ NIL; UNTIL (drive _ DiskChannel.GetNextDrive[drive]) = DiskChannel.nullDrive DO handle: DiskChannel.Handle _ DiskChannel.Create[drive: drive, completion: completion]; vHead _ NEW[VolumeObject _ [ link: vHead, driveTag: DiskChannel.GetDriveTag[drive], handle: DiskChannel.Create[drive: drive, completion: completion]]]; ENDLOOP; END; bufferSpace: Space.Handle = Space.Create[1, Space.virtualMemory]; bufferData: LONG POINTER TO WVMPrivate.PageData = Space.LongPointer[bufferSpace]; BadDriveTag: ERROR = CODE; BadTransfer: ERROR = CODE; MoveLocalDiskPage: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, direction: WVMPrivate.ChannelDirection, addr: WVMPrivate.DiskAddress] = BEGIN ENABLE UNWIND => NULL; req: DiskChannel.IORequest; label: ARRAY [0..SIZE[PilotDisk.Label]+3) OF WORD; pLabel: POINTER = Inline.BITAND[@label+3,-4]; FOR vh: VHandle _ vHead, vh.link UNTIL vh = NIL DO IF vh.driveTag # addr.driveTag THEN LOOP; req.dontIncrement _ FALSE; req.channel _ vh.handle; req.diskPage _ addr.diskPage + addr.offset; req.memoryPage _ Space.VMPageNumber[bufferSpace]; req.count _ 1; req.label _ pLabel; req.command _ vrr; SpecialSpace.MakeResident[bufferSpace]; DiskChannel.InitiateIO[@req]; UNTIL DiskChannel.WaitAny[completion] = @req DO NULL ENDLOOP; SpecialSpace.MakeSwappable[bufferSpace]; IF req.status # goodCompletion OR PilotDisk.LabelCheckSum[pLabel, addr.offset] # addr.labelCheck THEN ERROR BadTransfer[]; IF direction = read THEN { data^ _ bufferData^; EXIT }; IF direction # write THEN ERROR; bufferData^ _ data^; req.memoryPage _ Space.VMPageNumber[bufferSpace]; req.command _ vvw; SpecialSpace.MakeResident[bufferSpace]; DiskChannel.InitiateIO[@req]; -- check the label and write the data UNTIL DiskChannel.WaitAny[completion] = @req DO NULL ENDLOOP; SpecialSpace.MakeSwappable[bufferSpace]; IF req.status # goodCompletion THEN ERROR BadTransfer[]; EXIT REPEAT FINISHED => ERROR BadDriveTag[]; ENDLOOP; END; -- Control transfers -- -- Mechanism for handling "inloaded twice". If we're inloaded a second time -- we cannot proceed, because various pilot caches may not reflect the correct -- state of the disk(s). In that case, we re-boot our logical volume with a -- switch indicating that the client outload file should be believed. -- CantGetHere: ERROR = CODE; CantFindInloadFlag: ERROR = CODE; mySwitches: Booting.Switches _ PilotSwitches.switches; WorldSwap: INTERNAL PROC[ to: { install, run }, boot: Booting.Bootee, switches: Booting.Switches _ Booting.defaultSwitches ] = BEGIN -- to=boot => outload then boot physical volume, unless switches.w = down; -- to=client => outload then inload client. -- returns after we are inloaded again. inLoaded: BOOL _ FALSE; -- "inloaded twice" boolean -- CloseOutLdFile[]; InvalidateOutLdPage[]; IF to = install AND mySwitches.w = down THEN NULL -- assume client outload is acceptable and interesting -- ELSE Booting.Boot[boot: boot, switches: switches, outload: IF thisIsADebugger THEN [debugger] ELSE Booting.nullBootFile ]; IF inLoaded THEN -- inloaded twice! -- BEGIN mySwitches.w _ down; Booting.Boot[[logical[]], mySwitches]; ERROR CantGetHere[] END ELSE BEGIN mempage: Space.PageNumber = Space.PageFromLongPointer[@inLoaded]; -- now search "debugger" outload file for that page, then set bool TRUE in outload file -- bfh: LONG POINTER TO BootFile.Header; entries: LONG POINTER TO ARRAY [0..0) OF BootFile.Entry; curmax: CARDINAL _ BootFile.maxEntriesPerHeader; curbase: File.PageNumber _ 0; pagesremaining: CARDINAL; Space.CopyIn[outLdSpace, [debugger, curbase]]; bfh _ outLdAddress; entries _ @bfh.entries; pagesremaining _ bfh.countData; DO filePageOffset: File.PageNumber = curbase+1; entryCount: CARDINAL = MIN[curmax, pagesremaining]; IF mempage <= entries[entryCount-1].page THEN -- it's in this page of the bootFileMap -- BEGIN FOR i: BFMEntryIndex IN [0..entryCount] DO SELECT LONG[entries[i].page] FROM < mempage => NULL; > mempage => ERROR CantFindInloadFlag[]; ENDCASE => BEGIN diskInLoaded: LONG POINTER TO BOOL; Space.CopyIn[outLdSpace, [debugger, filePageOffset+i]]; diskInLoaded _ LOOPHOLE[ outLdAddress + (LONG[@inLoaded]-Space.LongPointerFromPage[mempage])]; diskInLoaded^ _ TRUE; Space.CopyOut[outLdSpace, [debugger, filePageOffset+i]]; GOTO done; END REPEAT FINISHED => ERROR ENDLOOP; END; pagesremaining _ pagesremaining - entryCount; IF pagesremaining = 0 THEN ERROR CantFindInloadFlag[]; curbase _ filePageOffset+entryCount; curmax _ BootFile.maxEntriesPerTrailer; Space.CopyIn[outLdSpace, [debugger, curbase]]; entries _ outLdAddress + SIZE[BootFile.Trailer]; ENDLOOP; EXITS done => InvalidateOutLdPage[]; END; OpenOutLdFile[]; END; LocateOther: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; -- This is called successfully only once per run -- IF NOT SetupFiles[] THEN RETURN WITH ERROR WorldVM.BadWorld[]; DiscoverVolumes[]; Space.Map[bufferSpace]; Space.Map[outLdSpace]; WorldSwap[install, [physical[]]]; END; GoOther: PUBLIC ENTRY PROC = { WorldSwap[run, [inload[[debuggee]]]] }; ReallySwapAndBoot: PUBLIC ENTRY SAFE PROC[boot: Booting.Bootee, switches: Booting.Switches] = TRUSTED { WorldSwap[run, boot, switches] }; END.