-- Swapper>PageFaultImpl.mesa (December 8, 1982 10:10 pm by Levin)
DIRECTORY
Environment USING [PageNumber],
Frame USING [GetReturnFrame, MyLocalFrame, SetReturnFrame],
PageFault USING [],
PrincOps USING [BytePC, Frame, GlobalFrameHandle, StateVector],
ProcessOperations USING
[IndexToHandle, LongReEnter, LongWait, ReadPSB, Requeue],
PSB USING [
FaultIndex, NoTimeout, NullPsbHandle, PDA, PDABase,
ProcessStateBlock, PsbHandle, PsbIndex, PsbNull, qPageFault,
qWriteProtectFault, Queue, QueueEmpty],
Runtime USING [CallDebugger],
SwapperPrograms USING [],
Utilities USING [PageFromLongPointer],
VM USING [Interval, PageNumber],
WriteFault USING [];
--SwapperPrograms.--PageFaultImpl: MONITOR LOCKS pageFaultLock
IMPORTS Frame, ProcessOperations, Runtime, Utilities
EXPORTS PageFault, SwapperPrograms, WriteFault =
BEGIN
pageFaultLock: MONITORLOCK; -- in principle, two locks are appropriate, but one will do.
pda: PSB.PDABase = PSB.PDA;
qPageFault: PSB.FaultIndex = PSB.qPageFault;
qPageFaultsBeingProcessed: PSB.FaultIndex = PSB.qPageFault+4;
qWriteFault: PSB.FaultIndex = PSB.qWriteProtectFault;
qWriteFaultsBeingProcessed: PSB.FaultIndex = PSB.qWriteProtectFault+4;
pPageFaultCondition: LONG POINTER TO CONDITION =
LOOPHOLE[@pda.fault[qPageFault].condition];
pWriteFaultCondition: LONG POINTER TO CONDITION =
LOOPHOLE[@pda.fault[qWriteFault].condition];
beingProcessedAF: LONG POINTER TO PSB.Queue =
-- contains processes for whom page fault processing has been initiated.
@pda.fault[qPageFaultsBeingProcessed].queue;
beingProcessedWP: LONG POINTER TO PSB.Queue =
-- contains processes for whom write fault processing has been initiated.
@pda.fault[qWriteFaultsBeingProcessed].queue;
-- Exports to PageFault --
AwaitPageFault: PUBLIC ENTRY PROCEDURE RETURNS [page: VM.PageNumber] =
BEGIN
process: PSB.PsbHandle;
WHILE pda.fault[qPageFault].queue = PSB.QueueEmpty DO
ProcessOperations.LongWait[@pageFaultLock, pPageFaultCondition, PSB.NoTimeout];
UNTIL ProcessOperations.LongReEnter[
@pageFaultLock, pPageFaultCondition] DO NULL ENDLOOP;
ENDLOOP;
process ← ProcessOperations.IndexToHandle[
pda.block[pda.fault[qPageFault].queue.tail]
.link.next]; -- walk to tail, then to head.
ProcessOperations.Requeue[
@pda.fault[qPageFault].queue, beingProcessedAF, process];
page ← Utilities.PageFromLongPointer[
pda[pda[process].context.state].memPointer];
Log[page, pda[pda[process].context.state].frame]; -- REMOVE THIS SOMEDAY!
END;
RestartPageFault: PUBLIC ENTRY PROCEDURE [interval: VM.Interval] =
BEGIN
process, pNext: PSB.PsbHandle;
pLast: PSB.PsbHandle = ProcessOperations.IndexToHandle[beingProcessedAF.tail];
IF beingProcessedAF↑ ~= PSB.QueueEmpty THEN
FOR process ← ProcessOperations.IndexToHandle[
pda.block[beingProcessedAF.tail].link.next], pNext DO
pNext ← ProcessOperations.IndexToHandle[pda[process].link.next];
IF Utilities.PageFromLongPointer[
pda[pda[process].context.state].memPointer]
IN [interval.page..interval.page+interval.count) THEN
ProcessOperations.Requeue[beingProcessedAF, @pda.ready, process]; -- wake him up.
IF process = pLast THEN EXIT;
ENDLOOP;
END;
AddressFault: PUBLIC ERROR [address: LONG POINTER] = CODE;
ReportAddressFault: PUBLIC PROC [page: VM.PageNumber] =
{ReportFault[page, beingProcessedAF, AddressFault]};
-- Exports to WriteFault --
AwaitWriteFault: PUBLIC ENTRY PROCEDURE RETURNS [page: VM.PageNumber] =
BEGIN
process: PSB.PsbHandle;
WHILE pda.fault[qWriteFault].queue = PSB.QueueEmpty DO
ProcessOperations.LongWait[@pageFaultLock, pWriteFaultCondition, PSB.NoTimeout];
UNTIL ProcessOperations.LongReEnter[
@pageFaultLock, pWriteFaultCondition] DO NULL ENDLOOP;
ENDLOOP;
process ← ProcessOperations.IndexToHandle[
pda.block[pda.fault[qWriteFault].queue.tail].link.next]; -- walk to tail, then to head.
ProcessOperations.Requeue[
@pda.fault[qWriteFault].queue, beingProcessedWP, process];
page ← Utilities.PageFromLongPointer[
pda[pda[process].context.state].memPointer];
END;
RestartWriteFault: PUBLIC ENTRY PROCEDURE [interval: VM.Interval] =
BEGIN
process, pNext: PSB.PsbHandle;
pLast: PSB.PsbHandle = ProcessOperations.IndexToHandle[beingProcessedWP.tail];
IF beingProcessedWP↑ ~= PSB.QueueEmpty THEN
FOR process ← ProcessOperations.IndexToHandle[
pda.block[beingProcessedWP.tail].link.next], pNext DO
pNext ← ProcessOperations.IndexToHandle[pda[process].link.next];
IF Utilities.PageFromLongPointer[
pda[pda[process].context.state].memPointer]
IN [interval.page..interval.page+interval.count) THEN
ProcessOperations.Requeue[beingProcessedWP, @pda.ready, process]; -- wake him up.
IF process = pLast THEN EXIT;
ENDLOOP;
END;
WriteProtectFault: PUBLIC ERROR [address: LONG POINTER] = CODE;
ReportWriteProtectFault: PUBLIC PROC [page: VM.PageNumber] =
{ReportFault[page, beingProcessedWP, WriteProtectFault]};
-- Internal procedures --
ReportFault: PROC [
page: VM.PageNumber, queue: LONG POINTER TO PSB.Queue, error: ERROR [LONG POINTER]] = {
faultees: PSB.Queue ← PSB.QueueEmpty;
RemoveFaultees: ENTRY PROC = --INLINE-- {
qEnd: PSB.PsbIndex ← queue.tail;
IF qEnd ~= PSB.PsbNull THEN {
psbi: PSB.PsbIndex ← pda.block[qEnd].link.next;
DO
psb: PSB.ProcessStateBlock ← pda.block[psbi]; -- note: a copy of the psb
IF ~psb.link.vector THEN Runtime.CallDebugger["Bug in fault handling"L];
IF Utilities.PageFromLongPointer[pda[psb.context.state].memPointer] = page THEN
ProcessOperations.Requeue[
from: queue, to: @faultees, p: ProcessOperations.IndexToHandle[psbi]];
IF psbi = qEnd THEN EXIT;
psbi ← psb.link.next; -- relies on psb being a copy
ENDLOOP;
};
};
RemoveFaultees[];
UNTIL faultees = PSB.QueueEmpty DO
psb: PSB.PsbHandle ← ProcessOperations.IndexToHandle[faultees.tail];
MemoryFault[psb, error];
ProcessOperations.Requeue[from: @faultees, to: @pda.ready, p: psb];
ENDLOOP;
};
MemoryFault: PROC [psb: PSB.PsbHandle, error: ERROR [LONG POINTER]] = {
Return: PROC ← LOOPHOLE[Frame.GetReturnFrame[]];
state: PrincOps.StateVector ← pda[pda[psb].context.state];
-- Splice me in as top-of-stack of psb.
pda[pda[psb].context.state].stkptr ← pda[pda[psb].context.state].instbyte ← 0;
Frame.SetReturnFrame[state.frame];
pda[pda[psb].context.state].frame ← LOOPHOLE[Frame.MyLocalFrame[]];
-- Return to ReportFault without disturbing this frame.
Return[];
-- Control returns here in the context of the faulting process. If it has
-- a dirty cleanup link, disaster may ensue if a signal is raised, since it is
-- almost certain to be uncaught and the AMEvents stuff will doubtless do a WAIT.
-- If someone caught it and did a wait, similar bad things would happen. So, we
-- call the world-swap debugger instead.
IF pda[psb].flags.cleanup ~= PSB.PsbNull THEN
Runtime.CallDebugger["Address fault with dirty cleanup link (see a wizard)"L];
ERROR error[state.memPointer];
};
-- Event log (for debugging)
Event: TYPE = RECORD [
page: VM.PageNumber,
process: PSB.PsbHandle,
local: POINTER TO local PrincOps.Frame,
global: PrincOps.GlobalFrameHandle,
pc: PrincOps.BytePC];
IEvent: TYPE = [0..3);
rgEvent: ARRAY IEvent OF Event ← ALL[[0, PSB.NullPsbHandle, NIL, NIL, [0]]];
pEvent: POINTER TO Event ← @rgEvent[FIRST[IEvent]];
Log: INTERNAL PROC [
page: VM.PageNumber, trapee: POINTER TO local PrincOps.Frame] = INLINE
BEGIN
pEvent↑ ←
[page, ProcessOperations.ReadPSB[], trapee, trapee.accesslink, trapee.pc];
pEvent ←
IF pEvent = @rgEvent[LAST[IEvent]] THEN @rgEvent[FIRST[IEvent]]
ELSE pEvent + SIZE[Event];
END;
END.
June 12, 1978 9:46 AM McJones Created file.
June 20, 1978 10:53 AM McJones Replaced "mark bit" with two queues; add correct simulation of naked notify.
June 23, 1978 1:30 PM McJones Removed RestartQuantifier.
July 25, 1978 5:53 PM McJones Trap didn't set state.dest (or .source); Restart traversed queue incorrectly.
September 19, 1978 12:03 PM McJones Added event log.
March 28, 1979 10:10 AM McJones Initialization of rgEvent clobbered following words.
April 28, 1980 10:59 AM Forrest FrameOps=>Frame, ControlDefs=>PrincOps, TrapOps=>Trap, PSBDefs=>PSB, ProcessOps=>ProcessOperations; rearrange logging code a bit using an INLINE; use ALL construct to initialize log table.
June 10, 1980 8:27 AM Forrest Detect -1 parameter to PageFaultTrap.
August 26, 1980 7:07 PM McJones New PSB, ProcessOperation.
January 16, 1981 1:18 PM Knutsen New PSB names. Fault Notification. CallDebugger, not worryCall.
February 4, 1981 11:22 AM Knutsen Remove LOOPHOLEs to access fault parameter.
February 14, 1981 5:54 PM Knutsen Move processes being worked on to unused PDA fault queue instead of private queue.
August 4, 1982 12:07 pm Levin Cleanup USING lists.
September 15, 1982 8:32 pm Levin Add address/write protect fault stuff.
December 8, 1982 10:04 pm Levin Detect fault with dirty cleanup link and world-swap.