-- 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.