WVMOutLd.mesa - access to world-swap client memory
Copyright © 1985 by Xerox Corporation. All rights reserved.
Andrew Birrell September 20, 1983 3:24 pm
Russ Atkinson, February 6, 1985 9:33:47 pm PST
DIRECTORY
BootFile USING[Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, MemorySizeToFileSize, 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, Handle, Info, Open, PageCount, PageNumber, Read, SetSize, SystemVolume, Volume, VolumeFile, wordsPerPage, Write ],
FileBackdoor USING[GetRoot, IsDebugger, SetRoot],
PrincOps USING[ maxPagesInVM, PageValue, wordsPerPage ],
VM USING[PageNumberForAddress, Allocate, Free, Interval, MakeUnchanged, PageNumber, AddressForPageNumber, Pin, State, Unpin, PagesForWords ],
VMInternal USING[ IsVacant ],
VMSideDoor USING[ rmPages ],
WorldVM USING[ AddressFault, BadWorld ],
WVMPrivate;
WVMOutLd: MONITOR
IMPORTS BootFile, Booting, DebuggerFormat, Disk, File, FileBackdoor, VM, VMInternal, VMSideDoor, WorldVM, WVMPrivate
EXPORTS WVMPrivate = {
EntrySeq: TYPE = LONG POINTER TO EntrySeqRep;
EntrySeqRep: TYPE = RECORD [SEQUENCE COMPUTED CARDINAL OF BootFile.Entry];
-- Management of outload files --
debugger: File.Handle;
debuggee: File.Handle;
pagesInMap: INT =
PrincOps.maxPagesInVM / (PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]);
SetupFiles: ENTRY PROC RETURNS[ok: BOOL] = {
ENABLE UNWIND => NULL;
sysVol: File.Volume = File.SystemVolume[];
size: File.PageCount =
BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+pagesInMap+2;
changed: BOOLEANFALSE;
IF NOT FileBackdoor.IsDebugger[sysVol] THEN RETURN[FALSE];
ok ← TRUE;
debugger ← VerifyFile[sysVol, debugger, size];
debuggee ← VerifyFile[sysVol, debuggee, size];
};
VerifyFile: INTERNAL PROC[sysVol: File.Volume, which: File.VolumeFile, size: File.PageCount] RETURNS[ new: File.Handle ] = {
changed: BOOLFALSE;
oldFP: File.FP = FileBackdoor.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 FileBackdoor.SetRoot[which, new];
};
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[BootFile.maxEntriesPerHeader,
BootFile.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 = {
bfm: BFMPointer;
bfh: LONG POINTER TO BootFile.Header;
entries: EntrySeq;
curmax: CARDINAL ← BootFile.maxEntriesPerHeader;
curbase: File.PageNumber ← [0];
OutLdPageSpace: PROC RETURNS[vm: LONG POINTER] = {
returns pointer into vm containing data from outload file map page
space: VM.Interval = VM.Allocate[vmPagesPerFilePage];
vm ← VM.AddressForPageNumber[space.page];
File.Read[debuggee, curbase, 1, vm];
VM.MakeUnchanged[space];
};
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 ← BootFile.maxEntriesPerTrailer;
entries ← OutLdPageSpace[] + BootFile.TrailerStart;
bfm.link ← NEW[BootFileMapItem];
bfm ← bfm.link;
ENDLOOP;
};
CloseOutLdFile: INTERNAL PROC = {
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;
};
SearchOutLdFile: INTERNAL PROCEDURE [mempage: WVMPrivate.PageNumber] RETURNS[ entry: LONG POINTER TO BootFile.Entry ] = {
Returns [NIL,NIL] if page isn't in file
FOR bfm: BFMPointer ← bfmHead, bfm.link UNTIL bfm = NIL DO
IF mempage <= bfm.lastMemPage
THEN { -- 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 => {
ReadOutLdPage[[bfm.filePageOffset+i]];
RETURN[ @bfm.entries[i] ];
}
REPEAT FINISHED => ERROR
ENDLOOP;
};
REPEAT FINISHED => RETURN[NIL]
ENDLOOP;
};
-- Transfers to/from outload file --
ReadOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber] RETURNS[ok: BOOLEAN] = {
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 --
{
IF VMInternal.IsVacant[mempage] THEN ERROR WorldVM.AddressFault[mempage];
data^ ← LOOPHOLE[WVMPrivate.PageAddress[mempage],
LONG POINTER TO WVMPrivate.PageData]^;
ok ← TRUE;
}
ELSE RETURN[FALSE]
ELSE {
data^ ← LOOPHOLE[outLdAddress, LONG POINTER TO WVMPrivate.PageData]^;
ok ← TRUE;
};
};
WriteOtherCore: PUBLIC ENTRY PROC[data: REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber] RETURNS[readOnly: BOOL] = {
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[];
readOnly ← entry.value.state.flags.readonly;
IF NOT readOnly THEN entry.value.state.flags.dirty ← TRUE;
};
-- 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] = {
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;
};
-- 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 {
ENABLE UNWIND => NULL;
CloseOutLdFile[];
InvalidateOutLdPage[];
};
MyRollback: ENTRY Booting.RollbackProc = TRUSTED {
ENABLE UNWIND => NULL;
{ -- patch the inloadedOnce boolean in the outload file!
mempage: VM.PageNumber = VM.PageNumberForAddress[@Booting.inloadedOnce];
bfh: LONG POINTER TO BootFile.Header;
entries: EntrySeq ← NIL;
curmax: CARDINAL ← BootFile.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 --
{
FOR i: BFMEntryIndex IN [0..entryCount]
DO SELECT LONG[entries[i].page] FROM
< mempage => NULL;
> mempage => ERROR CantFindInloadFlag[];
ENDCASE =>
{
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;
}
REPEAT FINISHED => ERROR
ENDLOOP;
};
pagesremaining ← pagesremaining - entryCount;
IF pagesremaining = 0 THEN ERROR CantFindInloadFlag[];
curbase ← [filePageOffset+entryCount];
curmax ← BootFile.maxEntriesPerTrailer;
File.Read[debugger, curbase, 1, outLdAddress];
entries ← outLdAddress + BootFile.TrailerStart;
ENDLOOP;
EXITS done => InvalidateOutLdPage[];
};
OpenOutLdFile[];
};
LocateOther: PUBLIC PROC = {
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] ]
};
GoOther: PUBLIC PROC =
Easy! All the hard stuff is in our Checkpoint and Rollback procs
{ [] ← Booting.Boot[ boot: [logical[root: debuggee]], switches: ALL[FALSE] ] };
}.