<> <> <> <> <> DIRECTORY AMProcessBasic USING [ Faulted, GFTable, GFTableObject, State ], Basics USING [ LongNumber], DebuggerSwap USING [ Frozen, freezeesTable, freezer], Loader USING [ MakeProcedureResident ], PrincOps --USING just about everything--, PrincOpsUtils USING [Alloc, DisableInterrupts, EnableAndRequeue, EnableInterrupts, Free, GetReturnFrame, LongCopy, MyGlobalFrame, MyLocalFrame, PsbHandleToIndex, PsbIndexToHandle, ReadPSB, Requeue], Process USING [ Pause ], TrapSupport USING [ opTrapTable ], VM USING [ PageNumberForAddress, PageNumber, Pin, Unpin], VMInternal USING [IsVacant], WorldVM USING [Address, AddressFault, CopyRead, CopyWrite, LocalWorld, Lock, Long, LongRead, Read, Unlock, World, Write], WVMPrivate USING [ Patch ]; AMProcessBasicImpl: MONITOR IMPORTS DebuggerSwap, Loader, PrincOpsUtils, Process, VM, VMInternal, WorldVM, WVMPrivate EXPORTS AMProcessBasic SHARES DebuggerSwap = BEGIN <> <> <> <> PSBI: TYPE = PrincOps.PsbIndex; <<******** Part 1: Aborting ********>> zTrapME: PrincOps.op = 176B; zTrapMRE: PrincOps.op = 177B; --AMProcessBasic.--Abort: PUBLIC PROC[world: WorldVM.World, psbi: PSBI] = BEGIN state: AMProcessBasic.State; frozen: BOOL; processFrame: PrincOps.FrameHandle; trapFrame: POINTER TO local PrincOps.Frame = AbortFrame[]; -- lock out anyone else from modifying victim process -- Set[psbi]; [frozen: frozen, state: state, processFrame: processFrame] _ InfoFromPSB[world, psbi, FALSE ! UNWIND => PrincOpsUtils.Free[trapFrame]]; IF state = dead THEN { PrincOpsUtils.Free[trapFrame]; RETURN }; -- now, world and psbi are locked -- [] _ GetPSB[psbi, world, TRUE]; IF frozen OR world # local THEN { Unlock[world]; PrincOpsUtils.Free[trapFrame] } ELSE BEGIN ENABLE UNWIND => Clear[psbi]; SetPsbFrame[world, psbi, trapFrame]; trapFrame.returnlink.frame _ processFrame; <> PrincOps.PDA[PrincOps.PDA.timeout][psbi] _ PrincOps.NoTimeout; <> PrincOps.PDA.block[psbi].--timeout--mds _ PrincOps.NoTimeout; Unlock[world]; IF (state = waitingCV OR state = waitingML) AND NOT frozen THEN Dequeue[world, psbi, processFrame]; END; -- world is unlocked and process has been requeued from any CV or ML queue -- Clear[psbi]; END--DoAbort--; Dequeue: PROC[world: WorldVM.World, psbi: PSBI, frame: PrincOps.FrameHandle] = BEGIN -- Only implemented for world = local -- <> <> <> <> style: { ME, MRE }; wordAddr: LONG POINTER; offset: [0..1]; op, trap: PrincOps.op; queue: LONG POINTER TO PrincOps.Queue; <> DO code: LONG POINTER = frame.accesslink.code.longbase; pc: PrincOps.BytePC _ [frame.pc-1]; Fetch: PROC = BEGIN word: PrincOps.InstWord; wordAddr _ code + pc / 2; offset _ pc MOD 2; word _ wordAddr^; op _ IF offset = 0 THEN word.evenbyte ELSE word.oddbyte; END; Fetch[]; SELECT op FROM PrincOps.zME => { style _ ME; trap _ zTrapME }; PrincOps.zMRE => { style _ MRE; trap _ zTrapMRE }; PrincOps.zMXW => BEGIN style _ MRE; trap _ zTrapMRE; THROUGH [1..1000] DO pc _ [pc + OpcodeLengths[op]]; Fetch[]; IF op = PrincOps.zMRE THEN EXIT; REPEAT FINISHED => ERROR ENDLOOP; END; zTrapME, zTrapMRE => { Process.Pause[1]; LOOP --obscure: some other process being patched at same place--}; ENDCASE => { Clear[psbi]; RETURN }; -- process will become ready by itself -- IF WVMPrivate.Patch[world: world, addr: LOOPHOLE[wordAddr, WorldVM.Address], offset: offset, byte: trap] = op THEN EXIT; -- otherwise, someone else is patching, so look again! -- ENDLOOP; -- call copy of victim's frame, to evaluate queue pointers -- BEGIN newFrame: PrincOps.FrameHandle; state: PrincOps.StateVector; fsi: CARDINAL _ LOOPHOLE[frame-1, POINTER TO CARDINAL]^; size: CARDINAL; IF fsi >= PrincOps.LargeReturnSlot THEN BEGIN size _ LOOPHOLE[frame-2, POINTER TO CARDINAL]^; FOR i: PrincOps.FrameSizeIndex IN PrincOps.FrameSizeIndex DO IF size <= PrincOps.FrameVec[i] THEN { newFrame _ PrincOpsUtils.Alloc[fsi _ i]; EXIT }; REPEAT FINISHED => ERROR ENDLOOP; END ELSE { size _ PrincOps.FrameVec[fsi]; newFrame _ PrincOpsUtils.Alloc[fsi] }; PrincOpsUtils.LongCopy[from: frame, to: newFrame, nwords: PrincOps.FrameVec[fsi]]; newFrame.returnlink.frame _ PrincOpsUtils.MyLocalFrame[]; IF style = ME THEN LOOPHOLE[newFrame, PROC[BOOL]][FALSE] ELSE LOOPHOLE[newFrame, PROC][]; state _ STATE; SELECT TRUE FROM (style = ME AND state.stkptr = 1) => queue _ LOOPHOLE[state.stk[0],POINTER]; (style = MRE AND state.stkptr = 2) => queue _ LOOPHOLE[state.stk[1],POINTER]; (style = ME AND state.stkptr = 2) => queue _ LOOPHOLE[Basics.LongNumber[num[lowbits: state.stk[0], highbits: state.stk[1]]]]; (style = MRE AND state.stkptr = 4) => queue _ LOOPHOLE[Basics.LongNumber[num[lowbits: state.stk[2], highbits: state.stk[3]]]]; ENDCASE => ERROR; -- Beware if the queue should be in the victim's local frame! -- BEGIN queueCard: LONG CARDINAL = LOOPHOLE[queue]; frameBase: LONG CARDINAL = LOOPHOLE[LONG[newFrame]]; IF queueCard > frameBase AND queueCard <= frameBase + size THEN queue _ queue - frameBase + LOOPHOLE[LONG[frame],LONG CARDINAL]; END; END; -- remove the tangle trap -- IF WVMPrivate.Patch[world: world, addr: LOOPHOLE[wordAddr, WorldVM.Address], offset: offset, byte: op] # trap THEN ERROR; -- Transfer the process to the ready list if it's still on the queue -- BEGIN handle: PrincOps.PsbHandle = PrincOpsUtils.PsbIndexToHandle[psbi]; queuePage: PrincOps.PageNumber = LOOPHOLE[queue, LONG CARDINAL] / PrincOps.wordsPerPage; THROUGH [1..50] -- garbage check -- DO [] _ queue^; -- queue may be non-resident -- PrincOpsUtils.DisableInterrupts[]; IF VMInternal.IsVacant[queuePage] THEN EXIT; PrincOpsUtils.EnableInterrupts[]; REPEAT FINISHED => ERROR ENDLOOP; -- now disabled, with queue swapped in -- IF OnQueue[world, psbi, queue] THEN PrincOpsUtils.EnableAndRequeue[queue, @PrincOps.PDA.ready, handle] ELSE PrincOpsUtils.EnableInterrupts[]; END; END--Dequeue--; AbortFrame: PROC RETURNS[ POINTER TO local PrincOps.Frame ] = BEGIN state: RECORD[a, b: UNSPECIFIED, v: PrincOps.StateVector]; psbi: PSBI; Caller: PROC[PrincOps.FrameHandle] = LOOPHOLE[PrincOpsUtils.GetReturnFrame[]]; Caller[PrincOpsUtils.MyLocalFrame[]]; -- execution resumes in the victim process -- state.v _ STATE; psbi _ PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]]; Wait[psbi]; -- now the initiating process has finished with the victim's frames -- IF PrincOps.PDA.block[psbi].flags.abort THEN BEGIN -- now read trapee's op-code, to distinguish ME from MRE -- caller: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; code: LONG POINTER = caller.accesslink.code.longbase; pc: PrincOps.BytePC = [caller.pc-1]; wordAddr: LONG POINTER = code + pc / 2; offset: [0..1] = pc MOD 2; word: PrincOps.InstWord = wordAddr^; op: PrincOps.op = IF offset = 0 THEN word.evenbyte ELSE word.oddbyte; -- In the ME case, we handle aborted; otherwise MRE handles it -- IF op = PrincOps.zME OR op = zTrapME THEN { PrincOps.PDA.block[psbi].flags.abort _ FALSE; ERROR ABORTED }; END; state.v.dest.frame _ PrincOpsUtils.GetReturnFrame[]; state.v.source _ PrincOps.NullLink; RETURN WITH state.v; END; TrapME: PROC = BEGIN state: RECORD[a, b: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.instbyte _ PrincOps.zME; Trap[@state.v]; RETURN WITH state.v; END; TrapMRE: PROC = BEGIN state: RECORD[a, b: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.instbyte _ PrincOps.zMRE; Trap[@state.v]; RETURN WITH state.v END; Trap: PROC[v: PrincOps.SVPointer] = INLINE -- MUST be INLINE, since it assumes it's in the trap handler's local frame -- BEGIN trapee: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; IF trapee.returnlink.frame.accesslink = PrincOpsUtils.MyGlobalFrame[] THEN BEGIN v.instbyte _ 0; v.dest _ trapee.returnlink; PrincOpsUtils.Free[trapee]; END ELSE v.dest.frame _ trapee; END; OpcodeLengths: PACKED ARRAY [0..255] OF [0..3] = [ -- copied from Traps.mesa 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 3, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 3, 2, 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2, 0, 2, 2, 1, 0]; <<******** Part 2: Freezing and process information ********>> qFreeze: PrincOps.FaultIndex = 3; freezeQueue: LONG POINTER TO PrincOps.Queue = @PrincOps.PDA.fault[qFreeze].queue; GFTable: TYPE = AMProcessBasic.GFTable; --AMProcessBasic.--Info: PUBLIC PROC[world: WorldVM.World, psbi: PSBI, freeze: BOOL, -- freeze at boundary of filter thaw: BOOL, -- unfreeze if not in filter fullStatus: BOOL, -- whether to distinguish fault and ready queues -- filter: GFTable, -- interesting global frames -- wantedStates: PACKED ARRAY AMProcessBasic.State OF BOOL -- interesting states --] RETURNS[ state: AMProcessBasic.State _ dead, faultData: LONG CARDINAL _ 0, priority: PrincOps.Priority _ 0, frame: PrincOps.FrameHandle _ NIL, frozenFrame: PrincOps.FrameHandle _ NIL, topFrame: PrincOps.FrameHandle _ NIL -- current frame of process --] = BEGIN <> <> <> <> <> <> <> <> frozen: BOOL; processFrame, prevFrame, freezeTrap: PrincOps.FrameHandle; restOfStack: PrincOps.ControlLink; LockFilter[filter]; -- lock out anyone else from modifying victim process -- Set[psbi]; restOfStack _ GetFreezee[world, psbi]; freezeTrap _ Freezer[world]; [priority, frozen, state, processFrame, faultData] _ InfoFromPSB[world, psbi, fullStatus]; IF state = dead THEN RETURN; -- now, world and psbi are locked -- IF NOT wantedStates[state] THEN { Unlock[world]; Clear[psbi]; UnlockFilter[filter]; RETURN}; [frame, prevFrame] _ ApplyFilter[world, [frame[processFrame]], filter, [frame[freezeTrap]] ! UNWIND => { Unlock[world]; Clear[psbi]; UnlockFilter[filter] } ]; frozenFrame _ IF restOfStack = PrincOps.NullLink THEN NIL ELSE IF frozen THEN GetLink[world, restOfStack.frame].frame ELSE restOfStack.frame; topFrame _ SELECT TRUE FROM frozen, freezeTrap=processFrame => frozenFrame, ENDCASE => processFrame; SELECT TRUE FROM frame # freezeTrap AND frame # NIL => BEGIN <> IF freeze THEN BEGIN -- freeze here -- IF frame = processFrame THEN SetPsbFrame[world, psbi, freezeTrap] ELSE SetLink[world, prevFrame, [frame[freezeTrap]]]; Unlock[world]; IF restOfStack # PrincOps.NullLink THEN BEGIN tail: PrincOps.FrameHandle _ frame; DO next: PrincOps.FrameHandle = GetLink[world, tail].frame; IF next = freezeTrap THEN EXIT; tail _ next; ENDLOOP; SetLink[world, tail, restOfStack]; END; SetFreezee[world, psbi, [frame[frame]]]; frozenFrame _ frame; END ELSE Unlock[world]; END; frame = freezeTrap => BEGIN <> [frame, prevFrame] _ ApplyFilter[world, restOfStack, filter, PrincOps.NullLink ! UNWIND => { Unlock[world]; Clear[psbi]; UnlockFilter[filter] } ]; IF frame # NIL AND frame # restOfStack.frame AND (NOT frozen OR frame # frozenFrame) AND freeze AND thaw THEN BEGIN <> SetLink[world, prevFrame, [frame[freezeTrap]]]; Unfreeze[world, psbi, processFrame, restOfStack]; SetFreezee[world, psbi, [frame[frame]]]; frozenFrame _ frame; END ELSE Unlock[world]; END; ENDCASE => Unlock[world]; -- clean up -- Clear[psbi]; UnlockFilter[filter] END--Info--; Thaw: PUBLIC PROC[world: WorldVM.World, psbi: PrincOps.PsbIndex] = BEGIN processFrame: PrincOps.FrameHandle; restOfStack: PrincOps.ControlLink; Set[psbi]; restOfStack _ GetFreezee[world, psbi]; IF restOfStack # PrincOps.NullLink THEN BEGIN processFrame _ InfoFromPSB[world, psbi, FALSE].processFrame; -- locks world -- Unfreeze[world, psbi, processFrame, restOfStack]; -- unlocks world -- END; Clear[psbi]; END; Unfreeze: PROC[world: WorldVM.World, psbi: PrincOps.PsbIndex, processFrame: PrincOps.FrameHandle, restOfStack: PrincOps.ControlLink] = BEGIN -- On entry process and world are locked. On exit, only process is locked. -- freezeTrap: PrincOps.FrameHandle = Freezer[world]; IF processFrame = freezeTrap THEN BEGIN SetPsbFrame[world, psbi, restOfStack.frame]; [] _ OnQueue[world, psbi, freezeQueue, TRUE]; END ELSE BEGIN tail: PrincOps.FrameHandle _ processFrame; DO next: PrincOps.FrameHandle = GetLink[world, tail].frame; IF next = freezeTrap THEN EXIT; tail _ next; ENDLOOP; SetLink[world, tail, restOfStack]; END; Unlock[world]; SetFreezee[world, psbi, PrincOps.NullLink]; END; LockFilter: PROC[filter: GFTable] = BEGIN IF filter # NIL THEN BEGIN base: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[filter]]; top: VM.PageNumber = VM.PageNumberForAddress[ LOOPHOLE[filter,LONG POINTER] + SIZE[AMProcessBasic.GFTableObject[filter.length]]-1 ]; VM.Pin[[page: base, count: top-base+1]]; END; END; UnlockFilter: PROC[filter: GFTable] = BEGIN IF filter # NIL THEN BEGIN base: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[filter]]; top: VM.PageNumber = VM.PageNumberForAddress[ LOOPHOLE[filter,LONG POINTER] + SIZE[AMProcessBasic.GFTableObject[filter.length]]-1 ]; VM.Unpin[[page: base, count: top-base+1]]; END; END; ApplyFilter: PROC[world: WorldVM.World, link: PrincOps.ControlLink, filter: GFTable, stopAt: PrincOps.ControlLink ] RETURNS[ frame, prevFrame: PrincOps.FrameHandle] = INLINE BEGIN -- "filter" is resident; world is locked. -- <<"frame" _ the first frame whose global frame inside the filter (or NIL if no such frame).>> <> frame _ link.frame; IF filter = NIL THEN RETURN; IF world = local THEN DO IF stopAt = [frame[frame]] THEN EXIT; IF AddrBad[frame] OR AddrBad[frame.accesslink] THEN { frame _ NIL; EXIT }; FOR i: CARDINAL IN [0..filter.count) DO IF frame.accesslink = filter[i] THEN GOTO found ENDLOOP; IF frame.returnlink = PrincOps.NullLink OR frame.returnlink.proc THEN { frame _ NIL; EXIT }; prevFrame _ frame; frame _ frame.returnlink.frame; REPEAT found => NULL ENDLOOP ELSE [frame, prevFrame] _ ApplyRemoteFilter[world, link, filter, stopAt] END; ApplyRemoteFilter: PROC[world: WorldVM.World, link: PrincOps.ControlLink, filter: GFTable, stopAt: PrincOps.ControlLink ] RETURNS[ frame, prevFrame: PrincOps.FrameHandle] = BEGIN frame _ link.frame; DO ENABLE WorldVM.AddressFault => { frame _ NIL; EXIT }; fHeader: PrincOps.Frame; IF stopAt = [frame[frame]] THEN EXIT; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, LOOPHOLE[frame] ], to: @fHeader, nwords: SIZE[PrincOps.Frame] ]; FOR i: CARDINAL IN [0..filter.count) DO IF fHeader.accesslink = filter[i] THEN GOTO found ENDLOOP; IF fHeader.returnlink = PrincOps.NullLink OR fHeader.returnlink.proc THEN { frame _ NIL; EXIT }; prevFrame _ frame; frame _ fHeader.returnlink.frame; REPEAT found => NULL ENDLOOP; END; ReturnLink: PUBLIC PROC[world: WorldVM.World, frame: PrincOps.FrameHandle, psbi: PrincOps.PsbIndex _ PrincOps.PsbNull] RETURNS[link: PrincOps.ControlLink] = -- This returns the "virtual" return link: the one that would be there if the process was not frozen. -- BEGIN freezer: POINTER TO local PrincOps.Frame = Freezer[world]; link _ GetLink[world, frame]; IF link # [frame[freezer]] THEN RETURN; IF psbi # PrincOps.PsbNull THEN { Set[psbi]; link _ GetFreezee[world, psbi] } ELSE FOR psbi IN PrincOps.PsbIndex DO Set[psbi]; IF (link _ GetFreezee[world, psbi]) # PrincOps.NullLink THEN BEGIN state: AMProcessBasic.State; processFrame: PrincOps.FrameHandle; [state: state, processFrame: processFrame] _ InfoFromPSB[world, psbi, FALSE]; IF state # dead THEN BEGIN ENABLE UNWIND => Clear[psbi]; other: PrincOps.ControlLink _ [frame[processFrame]]; Unlock[world]; UNTIL other = [frame[freezer]] OR other.indirect OR other.proc DO IF other.frame = frame THEN GOTO found; other _ GetLink[world, frame]; ENDLOOP; END; END; Clear[psbi]; REPEAT found => NULL; FINISHED => ERROR ENDLOOP; Clear[psbi]; END; freezerAddr: POINTER TO POINTER TO local PrincOps.Frame = LOOPHOLE[@(PrincOps.SD[205B])]; freezeesAddr: POINTER TO LONG POINTER TO DebuggerSwap.Frozen = LOOPHOLE[@(PrincOps.SD[206B])]; GetFreezee: PROC[world: WorldVM.World, psbi: PSBI] RETURNS[PrincOps.ControlLink] = { RETURN[ IF world = local THEN DebuggerSwap.freezeesTable[psbi] ELSE LOOPHOLE[WorldVM.Read[world, Freezees[world] + psbi]] ] }; SetFreezee: PROC[world: WorldVM.World, psbi: PSBI, freezee: PrincOps.ControlLink] = { WorldVM.Write[world, Freezees[world] + psbi, LOOPHOLE[freezee] ] }; Freezees: PROC[world: WorldVM.World] RETURNS[ WorldVM.Address ] = { RETURN[ LOOPHOLE[WorldVM.LongRead[world, WorldVM.Long[world, LOOPHOLE[freezeesAddr]]]] ] }; Freezer: PROC[world: WorldVM.World] RETURNS[POINTER TO local PrincOps.Frame] = { RETURN[ IF world = local THEN DebuggerSwap.freezer ELSE LOOPHOLE[WorldVM.Read[world, WorldVM.Long[world, LOOPHOLE[freezerAddr]]]] ] }; <<******** Part 3: common subroutines ********>> GetLink: PROC[world: WorldVM.World, frame: PrincOps.FrameHandle] RETURNS[PrincOps.ControlLink] = INLINE { RETURN[IF world = local THEN frame.returnlink ELSE GetRemoteLink[world, frame] ] }; GetRemoteLink: PROC[world: WorldVM.World, frame: PrincOps.FrameHandle] RETURNS[PrincOps.ControlLink] = BEGIN fHeader: PrincOps.Frame; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, LOOPHOLE[frame] ], to: @fHeader, nwords: SIZE[PrincOps.Frame] ]; RETURN[fHeader.returnlink] END; SetLink: PROC[world: WorldVM.World, frame: PrincOps.FrameHandle, link: PrincOps.ControlLink] = INLINE { IF world = local THEN frame.returnlink _ link ELSE SetRemoteLink[world, frame, link] }; SetRemoteLink: PROC[world: WorldVM.World, frame: PrincOps.FrameHandle, link: PrincOps.ControlLink] = BEGIN fHeader: PrincOps.Frame; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, LOOPHOLE[frame] ], to: @fHeader, nwords: SIZE[PrincOps.Frame] ]; fHeader.returnlink _ link; WorldVM.CopyWrite[world: world, from: @fHeader, to: WorldVM.Long[world, LOOPHOLE[frame] ], nwords: SIZE[PrincOps.Frame] ]; END; SetPsbFrame: PROC[world: WorldVM.World, psbi: PSBI, trapFrame: PrincOps.FrameHandle] = INLINE BEGIN <> IF world = local THEN BEGIN IF PrincOps.PDA.block[psbi].link.vector THEN PrincOps.PDA[PrincOps.PDA.block[psbi].context.state].frame _ LOOPHOLE[trapFrame] ELSE PrincOps.PDA.block[psbi].context.frame _ trapFrame; END ELSE SetRemotePsbFrame[world, psbi, trapFrame]; END; SetRemotePsbFrame: PROC[world: WorldVM.World, psbi: PSBI, trapFrame: PrincOps.FrameHandle] = BEGIN <> psb: PrincOps.ProcessStateBlock _ GetPSB[psbi, world]; IF psb.link.vector THEN BEGIN stateAddr: WorldVM.Address = LOOPHOLE[PrincOps.PDA, WorldVM.Address] + LOOPHOLE[psb.context.state, CARDINAL]; state: PrincOps.StateVector; WorldVM.CopyRead[world: world, from: stateAddr, to: @state, nwords: SIZE[PrincOps.StateVector] ]; state.frame _ LOOPHOLE[trapFrame]; WorldVM.CopyWrite[world: world, from: @state, to: stateAddr, nwords: SIZE[PrincOps.StateVector] ]; END ELSE BEGIN psb.context.frame _ trapFrame; WorldVM.CopyWrite[world: world, from: @psb, to: PsbAddr[world, psbi], nwords: SIZE[PrincOps.ProcessStateBlock] ]; END; END; InfoFromPSB: PROC[world: WorldVM.World, psbi: PSBI, fullStatus: BOOL] RETURNS[ priority: PrincOps.Priority, frozen: BOOL, state: AMProcessBasic.State, processFrame: PrincOps.FrameHandle, faultData: LONG CARDINAL _ 0 ] = BEGIN psb: PrincOps.ProcessStateBlock; -- prevent victim from proceeding -- Lock[world]; -- Also disables interrupts and stops processor timeout scan if world = local -- BEGIN ENABLE UNWIND => { Unlock[world]; Clear[psbi] }; psb _ GetPSB[psbi, world]; priority _ psb.link.priority; -- get status -- IF psb.flags.processState.state = dead THEN { Unlock[world]; Clear[psbi]; state _ dead; RETURN }; frozen _ OnQueue[world, psbi, freezeQueue]; SELECT TRUE FROM psb.flags.waiting => state _ waitingCV; fullStatus => state _ GetQueueState[psbi, world, psb.link.failed]; ENDCASE => state _ unknown; -- get context -- IF psb.link.vector THEN BEGIN sv: PrincOps.StateVector; stateAddr: WorldVM.Address = LOOPHOLE[PrincOps.PDA, WorldVM.Address] + LOOPHOLE[psb.context.state, CARDINAL]; IF world = local -- avoid making a procedure call to non-resident code! -- THEN PrincOpsUtils.LongCopy[from: LOOPHOLE[stateAddr], to: @sv, nwords: SIZE[PrincOps.StateVector] ] ELSE WorldVM.CopyRead[world: world, from: stateAddr, to: @sv, nwords: SIZE[PrincOps.StateVector] ]; processFrame _ sv.frame; faultData _ SELECT state FROM frameFault => sv.fsi, IN AMProcessBasic.Faulted => LOOPHOLE[sv.memPointer], ENDCASE => 0; END ELSE processFrame _ psb.context.frame; END--UNWIND catch--; END; GetQueueState: PROC[psbi: CARDINAL, world: WorldVM.World, enterFailed: BOOL] RETURNS[state: AMProcessBasic.State] = INLINE BEGIN IF OnQueue[world, psbi, @PrincOps.PDA.ready] THEN RETURN[ready]; FOR q: PrincOps.FaultIndex IN PrincOps.FaultIndex DO IF OnQueue[world, psbi, @(PrincOps.PDA.fault[q].queue)] THEN SELECT q FROM PrincOps.qFrameFault => RETURN[frameFault]; PrincOps.qPageFault, PrincOps.qPageFault+4 => RETURN[pageFault]; -- "+4" for software queues PrincOps.qWriteProtectFault, PrincOps.qWriteProtectFault+4 => RETURN[writeProtectFault]; qFreeze => RETURN[ready]; ENDCASE => RETURN[unknownFault]; ENDLOOP; RETURN[IF enterFailed THEN waitingML ELSE unknown] END; OnQueue: PROC[world: WorldVM.World, psbi: PSBI, queueHandle: PrincOps.QueueHandle, remove: BOOL _ FALSE] RETURNS[BOOL] = INLINE BEGIN IF world = local THEN BEGIN tail, prev: PSBI; IF queueHandle^ = PrincOps.QueueEmpty THEN RETURN[FALSE]; prev _ tail _ queueHandle.tail; THROUGH [FIRST[PSBI]..LAST[PSBI]+1] -- garbage protection -- DO next: PSBI = PrincOps.PDA.block[prev].link.next; IF next = psbi THEN BEGIN IF remove THEN PrincOpsUtils.Requeue[queueHandle, @PrincOps.PDA.ready, PrincOpsUtils.PsbIndexToHandle[psbi]]; RETURN[TRUE] END; prev _ next; IF prev = tail THEN RETURN[FALSE]; ENDLOOP; RETURN[FALSE] -- actually, the queue is thoroughly mangled! -- END ELSE RETURN[ OnRemoteQueue[world, psbi, LOOPHOLE[queueHandle], remove] ]; END; OnRemoteQueue: PROC[world: WorldVM.World, psbi: PSBI, queueHandle: WorldVM.Address, remove: BOOL] RETURNS[BOOL] = BEGIN dest: LONG POINTER TO PrincOps.Queue = IF remove THEN @PrincOps.PDA.ready ELSE NIL; queue: PrincOps.Queue; tail, prev: PSBI; WorldVM.CopyRead[world: world, from: LOOPHOLE[queueHandle, WorldVM.Address], to: @queue, nwords: SIZE[PrincOps.Queue]]; IF queue = PrincOps.QueueEmpty THEN RETURN[FALSE]; prev _ tail _ queue.tail; THROUGH [FIRST[PSBI]..LAST[PSBI]+1] -- garbage protection -- DO psb: PrincOps.ProcessStateBlock _ GetPSB[prev, world]; IF psb.link.next = psbi THEN BEGIN IF dest # NIL THEN BEGIN this: PrincOps.ProcessStateBlock _ GetPSB[psbi, world]; newQueue: PrincOps.Queue; WorldVM.CopyRead[world: world, from: LOOPHOLE[dest, WorldVM.Address], to: @newQueue, nwords: SIZE[PrincOps.Queue]]; -- dequeue -- psb.link.next _ this.link.next; IF queue.tail = psbi THEN queue.tail _ IF prev = psbi THEN PrincOps.PsbNull ELSE prev; -- enqueue -- IF newQueue.tail = PrincOps.PsbNull THEN this.link.next _ newQueue.tail _ psbi ELSE BEGIN -- scan queue for correct priority position -- tempPSBI: PSBI _ newQueue.tail; temp: PrincOps.ProcessStateBlock _ GetPSB[tempPSBI, world]; IF temp.link.priority >= this.link.priority THEN newQueue.tail _ psbi ELSE -- search until we find the correct priority -- DO next: PrincOps.ProcessStateBlock _ GetPSB[temp.link.next, world]; IF this.link.priority > next.link.priority THEN EXIT; tempPSBI _ temp.link.next; temp _ next; ENDLOOP; this.link.next _ temp.link.next; temp.link.next _ psbi; WorldVM.CopyWrite[world: world, from: @temp, to: PsbAddr[world, tempPSBI], nwords: SIZE[PrincOps.ProcessStateBlock]]; END; -- copy back -- WorldVM.CopyWrite[world: world, from: @psb, to: PsbAddr[world, prev], nwords: SIZE[PrincOps.ProcessStateBlock]]; WorldVM.CopyWrite[world: world, from: @this, to: PsbAddr[world, psbi], nwords: SIZE[PrincOps.ProcessStateBlock]]; WorldVM.CopyWrite[world: world, from: @queue, to: LOOPHOLE[queueHandle, WorldVM.Address], nwords: SIZE[PrincOps.Queue]]; WorldVM.CopyWrite[world: world, from: @newQueue, to: LOOPHOLE[dest, WorldVM.Address], nwords: SIZE[PrincOps.Queue]]; END; RETURN[TRUE] END; prev _ psb.link.next; IF prev = tail THEN RETURN[FALSE]; ENDLOOP; RETURN[FALSE] -- actually, the queue is thoroughly mangled! -- END; GetPSB: PROC[psbi: PSBI, world: WorldVM.World, abort: BOOL _ FALSE] RETURNS[PrincOps.ProcessStateBlock] = INLINE BEGIN IF world = local THEN BEGIN IF abort THEN PrincOps.PDA.block[psbi].flags.abort _ TRUE; RETURN[ PrincOps.PDA.block[psbi] ] END ELSE RETURN[ GetRemotePSB[psbi, world, abort] ] END; GetRemotePSB: PROC[psbi: PSBI, world: WorldVM.World, abort: BOOL] RETURNS[psb: PrincOps.ProcessStateBlock] = BEGIN psbAddr: WorldVM.Address = PsbAddr[world, psbi]; WorldVM.CopyRead[world: world, from: psbAddr, to: @psb, nwords: SIZE[PrincOps.ProcessStateBlock]]; IF abort THEN BEGIN psb.flags.abort _ TRUE; WorldVM.CopyWrite[world: world, from: @psb, to: psbAddr, nwords: SIZE[PrincOps.ProcessStateBlock]]; END; END; PsbAddr: PROC[world: WorldVM.World, psbi: PSBI] RETURNS[ WorldVM.Address ] = { RETURN[ LOOPHOLE[PrincOps.PDA, WorldVM.Address] + psbi*SIZE[PrincOps.ProcessStateBlock] ] }; -- Synchronisation -- untangling: REF PACKED ARRAY PSBI OF BOOL = NEW[PACKED ARRAY PSBI OF BOOL _ ALL[FALSE] ]; finishedUntangling: CONDITION _ [timeout:0]; Set: ENTRY PROC[psbi: PSBI] = { WHILE untangling[psbi] DO WAIT finishedUntangling ENDLOOP; untangling[psbi] _ TRUE }; Wait: ENTRY PROC[psbi: PSBI] = INLINE { WHILE untangling[psbi] DO WAIT finishedUntangling ENDLOOP }; Clear: ENTRY PROC[psbi: PSBI] = { untangling[psbi] _ FALSE; BROADCAST finishedUntangling }; -- Access to client memory with client stopped -- local: WorldVM.World = WorldVM.LocalWorld[]; Lock: PROC[world: WorldVM.World] = { IF world = local THEN PrincOpsUtils.DisableInterrupts[] ELSE WorldVM.Lock[world] }; Unlock: PROC[world: WorldVM.World] = INLINE { IF world = local THEN PrincOpsUtils.EnableInterrupts[] ELSE WorldVM.Unlock[world] }; AddrBad: PROC[addr: LONG POINTER] RETURNS[BOOL] = INLINE { OPEN a: LOOPHOLE[addr, num Basics.LongNumber]; IF a.highbits >= 256 THEN RETURN[TRUE]; RETURN[ VMInternal.IsVacant[a.highbits*256 + a.lowbits/256]] }; Loader.MakeProcedureResident[Info]; Loader.MakeProcedureResident[Dequeue]; TrapSupport.opTrapTable.main[zTrapME] _ LOOPHOLE[TrapME]; TrapSupport.opTrapTable.main[zTrapMRE] _ LOOPHOLE[TrapMRE]; END.