WVMOutLd.mesa
Cedar Remote Debugging: access to world-swap client memory
Andrew Birrell September 20, 1983 3:24 pm
Russ Atkinson, April 26, 1984 1:58:38 pm PST
DIRECTORY
BootFileChanges USING[
Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, PageValue, TrailerStart ],
Booting USING[ Boot, CheckpointProc, inloadedOnce, RegisterProcs, RollbackProc, switches ],
DebuggerFormat USING[ LabelChecksum ],
Disk USING[ Channel, DoIO, DriveAttributes, Label, NextChannel, ok, Request, Status ],
DiskFace USING[ Type ],
File USING[
Create, Error, FP, GetRoot, Handle, Info, IsDebugger, Open, PageCount, PageNumber, Read, SetRoot, SetSize, SystemVolume, Volume, VolumeFile, wordsPerPage, Write ],
PrincOps USING[ maxPagesInVM, wordsPerPage ],
PrincOpsUtils USING[ IsVacant ],
VM USING[
PageNumberForAddress, Allocate, Free, Interval, MakeUnchanged, PageNumber, AddressForPageNumber, Pin, State, Unpin, PagesForWords ],
VMSideDoor USING[ rmPages ],
WorldVM USING[ AddressFault, BadWorld ],
WVMPrivate;
WVMOutLd: MONITOR
IMPORTS
BootFileChanges, Booting, DebuggerFormat, Disk, File, PrincOpsUtils, VM, VMSideDoor, WorldVM, WVMPrivate
EXPORTS WVMPrivate =
BEGIN
EntrySeq: TYPE = LONG POINTER TO EntrySeqRep;
EntrySeqRep: TYPE = RECORD [SEQUENCE COMPUTED CARDINAL OF BootFileChanges.Entry];
-- Management of outload files --
debugger: File.Handle;
debuggee: File.Handle;
pagesInMap: INT =
PrincOps.maxPagesInVM / (PrincOps.wordsPerPage / SIZE[BootFileChanges.PageValue]);
SetupFiles: ENTRY PROC RETURNS[ok: BOOL] =
BEGIN
ENABLE UNWIND => NULL;
sysVol: File.Volume = File.SystemVolume[];
size: File.PageCount =
BootFileChanges.MemorySizeToFileSize[VMSideDoor.rmPages]+pagesInMap+2;
changed: BOOLEANFALSE;
IF NOT File.IsDebugger[sysVol] THEN RETURN[FALSE];
ok ← TRUE;
debugger ← VerifyFile[sysVol, debugger, size];
debuggee ← VerifyFile[sysVol, debuggee, size];
END;
VerifyFile: INTERNAL PROC[sysVol: File.Volume, which: File.VolumeFile, size: File.PageCount]
RETURNS[ new: File.Handle ] =
BEGIN
changed: BOOLFALSE;
oldFP: File.FP = File.GetRoot[sysVol, which].fp;
new ← File.Open[sysVol, oldFP ! File.Error => { new ← NIL; CONTINUE} ];
IF new = NIL THEN { changed ← TRUE; new ← File.Create[sysVol, size] };
IF File.Info[new].size < size THEN { changed ← TRUE; File.SetSize[new, size] };
IF changed THEN File.SetRoot[which, new];
END;
vmPagesPerFilePage: INT = VM.PagesForWords[File.wordsPerPage];
outLdSpace: VM.Interval = VM.Allocate[vmPagesPerFilePage];
outLdPage: File.PageNumber ← [LAST[INT]];
outLdAddress: LONG POINTER = VM.AddressForPageNumber[outLdSpace.page];
ReadOutLdPage: INTERNAL PROC[page: File.PageNumber] =
{ IF outLdPage # page THEN File.Read[debuggee, outLdPage←page, 1, outLdAddress] };
WriteOutLdPage: INTERNAL PROC =
{ File.Write[debuggee, outLdPage, 1, outLdAddress] };
InvalidateOutLdPage: INTERNAL PROC =
{ outLdPage ← [LAST[INT]] };
-- Layout of boot and outload files --
BFMEntryIndex: TYPE = [0..MAX[BootFileChanges.maxEntriesPerHeader,
BootFileChanges.maxEntriesPerTrailer]];
BootFileMapItem: TYPE = RECORD [
link: BFMPointer ← NIL,
lastMemPage: WVMPrivate.PageNumber,
filePageOffset: File.PageNumber,
entryCount: BFMEntryIndex,
entries: EntrySeq];
BFMPointer: TYPE = REF BootFileMapItem;
bfmHead: BFMPointer ← NIL;
OpenOutLdFile: INTERNAL PROC =
BEGIN
bfm: BFMPointer;
bfh: LONG POINTER TO BootFileChanges.Header;
entries: EntrySeq;
curmax: CARDINAL ← BootFileChanges.maxEntriesPerHeader;
curbase: File.PageNumber ← [0];
OutLdPageSpace: PROC RETURNS[vm: LONG POINTER] =
-- returns pointer into vm containing data from outload file map page --
BEGIN
space: VM.Interval = VM.Allocate[vmPagesPerFilePage];
vm ← VM.AddressForPageNumber[space.page];
File.Read[debuggee, curbase, 1, vm];
VM.MakeUnchanged[space];
END;
pagesremaining: CARDINAL;
bfmHead ← NIL;
bfh ← OutLdPageSpace[];
entries ← LOOPHOLE[@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 ← BootFileChanges.maxEntriesPerTrailer;
entries ← OutLdPageSpace[] + BootFileChanges.TrailerStart;
bfm.link ← NEW[BootFileMapItem];
bfm ← bfm.link;
ENDLOOP;
END;
CloseOutLdFile: INTERNAL PROC =
BEGIN
FOR bfmPtr: BFMPointer ← bfmHead, bfmPtr.link UNTIL bfmPtr = NIL
DO
space: VM.Interval =
[page: VM.PageNumberForAddress[bfmPtr.entries], count: vmPagesPerFilePage];
changed: BOOLFALSE;
FOR i: NAT IN NAT[0..space.count)
DO IF VM.State[space.page+i].dataState = changed THEN changed ← TRUE ENDLOOP;
IF changed THEN
File.Write[debuggee, [bfmPtr.filePageOffset-1], 1, VM.AddressForPageNumber[space.page]];
VM.Free[space];
ENDLOOP;
bfmHead ← NIL;
END;
SearchOutLdFile: INTERNAL PROCEDURE [mempage: WVMPrivate.PageNumber]
RETURNS[ entry: LONG POINTER TO BootFileChanges.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[ok: BOOLEAN] =
BEGIN
ENABLE UNWIND => NULL;
entry: LONG POINTER TO BootFileChanges.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
IF PrincOpsUtils.IsVacant[mempage] THEN ERROR WorldVM.AddressFault[mempage];
data^ ← LOOPHOLE[WVMPrivate.PageAddress[mempage],
LONG POINTER TO WVMPrivate.PageData]^;
ok ← TRUE;
END
ELSE RETURN[FALSE]
ELSE BEGIN
data^ ← LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^;
ok ← TRUE;
END;
END;
WriteOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData,
mempage: WVMPrivate.PageNumber] RETURNS[readOnly: BOOL] =
BEGIN
ENABLE UNWIND => NULL;
entry: LONG POINTER TO BootFileChanges.Entry = SearchOutLdFile[mempage];
IF entry = NIL THEN ERROR WorldVM.AddressFault[mempage];
LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^ ← data^;
WriteOutLdPage[];
readOnly ← entry.value.state.flags.readonly;
IF NOT readOnly THEN entry.value.state.flags.dirty ← TRUE;
END;
-- Transfers to/from backing store --
bufferSpace: VM.Interval = VM.Allocate[1];
bufferData: LONG POINTER TO WVMPrivate.PageData =
VM.AddressForPageNumber[bufferSpace.page];
UnknownDrive: ERROR = CODE;
BadTransfer: ERROR = CODE;
MoveLocalDiskPage: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData,
direction: WVMPrivate.ChannelDirection,
addr: WVMPrivate.DiskAddress] =
BEGIN
ENABLE UNWIND => NULL;
req: Disk.Request;
label: Disk.Label;
FOR channel: Disk.Channel ← Disk.NextChannel[NIL], Disk.NextChannel[channel]
UNTIL channel = NIL
DO type: DiskFace.Type;
ordinal: INT;
status: Disk.Status;
countDone: INT;
[type: type, ordinal: ordinal] ← Disk.DriveAttributes[channel];
IF type # addr.deviceType OR ordinal # addr.deviceOrdinal THEN LOOP;
req ← [
diskPage: [addr.diskPage + addr.offset],
data: bufferData,
command: [header: verify, label: read, data: read],
count: 1 ];
VM.Pin[bufferSpace];
[status, countDone] ← Disk.DoIO[channel, @label, @req];
VM.Unpin[bufferSpace];
IF status # Disk.ok
OR DebuggerFormat.LabelChecksum[label, addr.offset] # addr.labelCheck
THEN ERROR BadTransfer[];
IF direction = read THEN { data^ ← bufferData^; EXIT };
IF direction # write THEN ERROR;
bufferData^ ← data^;
req ← [
diskPage: [addr.diskPage + addr.offset],
data: bufferData,
command: [header: verify, label: verify, data: write],
count: 1 ];
VM.Pin[bufferSpace];
[status, countDone] ← Disk.DoIO[channel, @label, @req]; -- write the data
VM.Unpin[bufferSpace];
IF status # Disk.ok THEN ERROR BadTransfer[];
EXIT
REPEAT FINISHED => ERROR UnknownDrive[];
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;
MyCheckpoint: ENTRY Booting.CheckpointProc = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
CloseOutLdFile[];
InvalidateOutLdPage[];
END;
MyRollback: ENTRY Booting.RollbackProc = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
BEGIN -- patch the inloadedOnce boolean in the outload file!
mempage: VM.PageNumber = VM.PageNumberForAddress[@Booting.inloadedOnce];
bfh: LONG POINTER TO BootFileChanges.Header;
entries: EntrySeq ← NIL;
curmax: CARDINAL ← BootFileChanges.maxEntriesPerHeader;
curbase: File.PageNumber ← [0];
pagesremaining: CARDINAL;
File.Read[debugger, curbase, 1, outLdAddress];
bfh ← outLdAddress;
entries ← LOOPHOLE[@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;
File.Read[debugger, [filePageOffset+i], 1, outLdAddress];
diskInLoaded ← LOOPHOLE[outLdAddress +
(LONG[@Booting.inloadedOnce]-VM.AddressForPageNumber[mempage])];
diskInLoaded^ ← TRUE;
File.Write[debugger, [filePageOffset+i], 1, outLdAddress];
GOTO done;
END
REPEAT FINISHED => ERROR
ENDLOOP;
END;
pagesremaining ← pagesremaining - entryCount;
IF pagesremaining = 0 THEN ERROR CantFindInloadFlag[];
curbase ← [filePageOffset+entryCount];
curmax ← BootFileChanges.maxEntriesPerTrailer;
File.Read[debugger, curbase, 1, outLdAddress];
entries ← outLdAddress + BootFileChanges.TrailerStart;
ENDLOOP;
EXITS done => InvalidateOutLdPage[];
END;
OpenOutLdFile[];
END;
LocateOther: PUBLIC PROC =
BEGIN
ENABLE UNWIND => NULL;
-- This is called successfully only once per run --
IF NOT SetupFiles[] THEN RETURN WITH ERROR WorldVM.BadWorld[];
Booting.RegisterProcs[c: MyCheckpoint, r: MyRollback];
[] ← Booting.Boot[ boot: IF Booting.switches[w] THEN [noOp[]] ELSE [physical[]],
switches: ALL[FALSE] ]
END;
GoOther: PUBLIC PROC =
Easy! All the hard stuff is in our Checkpoint and Rollback procs
{ [] ← Booting.Boot[ boot: [logical[root: debuggee]], switches: ALL[FALSE] ] };
END.