DIRECTORY AMProcessBasic USING[ Faulted, GFTable, GFTableObject, State ], Basics USING[ LongNumber], BootFileChanges USING[ IsVacant], 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], WorldVM USING[ Address, AddressFault, CopyRead, CopyWrite, LocalWorld, Lock, Long, LongRead, Read, Unlock, World, Write], WVMPrivate USING[ Patch ]; AMProcessBasicImpl: MONITOR IMPORTS BootFileChanges, DebuggerSwap, Loader, PrincOpsUtils, Process, VM, WorldVM, WVMPrivate EXPORTS AMProcessBasic SHARES DebuggerSwap = BEGIN PSBI: TYPE = PrincOps.PsbIndex; 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 BootFileChanges.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]; 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 _ 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]]]] ] }; 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, pageFault, writeProtectFault => LOOPHOLE[sv.memPointer], IN AMProcessBasic.Faulted => LOOPHOLE[@sv.dataArray, POINTER TO LONG CARDINAL]^ 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[ BootFileChanges.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. ìAMProcessBasicImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Russ Atkinson, July 19, 1984 5:32:00 pm PDT Andrew Birrell, September 16, 1983 11:29 am Paul Rovner, August 10, 1983 12:04 pm Innocent reader beware! This module contains very delicate code. Much of it is executed with interrupts disabled. When disabled, the following constraints are obeyed: all code is resident, all data is resident, no procedure calls (except to INLINEs). Consider very carefully before modifying any of this module. ******** Part 1: Aborting ******** disable process's timeout so it doesn't change queues during Dequeue. TEMP until microcode uses timeout vector: This procedure will remove a process from a CV or ML queue and requeue it onto ready list. The process must already have bee forced into a trap so it will not return from the frame where it was waiting. World is not frozen at entry, nor at exit. set the tangle-trap ******** Part 2: Freezing and process information ******** Delicate! If process is dead, returns [dead, 0, NIL] Returns state, but if not "fullStatus" treats fault and ready queues as "unknown". If "fullStatus", returns "faultData" from fault state vector, if any. Returns "frame" as first frame in stack scan that's inside the filter (or first, if filter=NIL); If no such frame, then frame _ NIL. If "freeze", freezes process at "frame", unless earlier frame in stack scan is already frozen; If "freeze" and "thaw", freezes process at "frame", then thaws any earlier frozen frame. A victim frame was found, and it's in unfrozen territory. If there is a victim, it must be inside the frozen territory. A victim was found, and we must contract the frozen territory. "frame" _ the first frame whose global frame inside the filter (or NIL if no such frame). If "frame" is not NIL and not "processFrame", prevFrame _ frame with returnlink "frame". ******** Part 3: common subroutines ******** on entry and exit, world and psbi are locked. on entry and exit, world and psbi are locked. Ê£˜šœ™Jšœ Ïmœ1™Jšœ)™)Jšœ žœ Ÿ œ˜=J˜Jšžœžœžœžœ˜;Jšžœ$˜(Jšžœ˜—JšŸM˜MJ˜J˜ J˜JšžŸ œ˜J˜J˜—š¡œžœžœ ˜NJšž˜JšŸ(˜(JšœZ™ZJšœS™SJšœ™Jšœ*™*Jšœ žœžœ˜Jšœ žœžœ˜J˜Jšœ˜Jšœžœžœžœ˜&J˜Jšœ™šžœžœžœ"˜7J˜#š¡œžœ˜ Jšž˜J˜J˜Jšœ žœ˜J˜Jšœžœ žœžœ˜8Jšžœ˜—J˜šžœž˜Jšœžœ˜/Jšœžœ˜2˜Jšž˜Jšœžœ˜ J˜Jšžœ ˜Jšžœ)žœžœžœ˜KJšžœžœž˜Jšžœ˜Jšžœ˜—˜JšœžœŸ;œ˜V——JšžœžœŸ)˜Mšžœ&žœ˜LJšœ ˜ —Jšžœžœ˜ JšŸ9˜9—Jšžœ˜J˜JšŸ=˜=šž˜J˜J˜Jš œžœžœ žœžœžœ˜8Jšœžœ˜Jšžœ ˜"šžœž˜ Jš œžœ žœžœžœ˜/Jšžœžœ˜9Jšžœžœžœ,žœ˜ZJšžœžœž˜Jšžœ˜Jšž˜—JšžœH˜LJ˜RJ˜9Jšžœ ž˜ Jš žœžœ žœžœžœ˜*Jšžœžœ žœ˜ Jšœžœ˜šžœžœž˜šœ žœžœ˜$Jšœžœžœ˜'—šœ žœžœ˜%Jšœžœžœ˜'—šœ žœžœ˜$šœ˜JšœžœH˜R——šœ žœžœ˜%šœ˜JšœžœH˜R———Jšžœžœ˜JšŸ@˜@šž˜Jšœ žœžœžœ˜+Jš œ žœžœžœžœ ˜4Jšžœžœ˜:Jš žœžœžœžœžœ˜E—Jšžœ˜—Jšžœ˜J˜JšŸ˜šžœ&žœ˜LJšœ ˜ —Jšžœžœ˜ J˜JšŸG˜Gšž˜JšœB˜B˜ Jšžœžœžœ˜7—Jšžœ Ÿ˜#šžœŸ˜/J˜"Jšžœ%žœžœ˜1J˜!—Jšžœžœž˜Jšžœ˜JšŸ)˜)Jšžœ˜Jšžœ1žœ˜GJšžœ"˜&—Jšžœ˜J˜JšžŸ œ˜J˜J˜—š ¡ œžœžœžœžœ˜=Jšž˜Jšœžœž œ˜:Jšœžœ˜ Jš¡œžœžœ!˜NJ˜%JšŸ-˜-Jšœ žœ˜J˜?J˜ JšŸF˜FJšžœ žœ˜'šžœž˜ JšŸ;˜;J˜>Jšœžœžœ#˜5J˜$Jšœ žœžœ˜'Jšœžœ˜J˜$Jšœžœ žœžœ˜EJšŸA˜AJšžœžœ ˜$Jš žœ žœžœžœžœ˜EJšžœ˜—J˜4J˜#Jšžœžœ ˜Jšžœ˜J˜J˜—š¡œžœ˜Jšž˜Jšœžœž œ˜:Jšœ žœ˜J˜ J˜Jšžœžœ ˜Jšžœ˜J˜—š¡œžœ˜Jšž˜Jšœžœž œ˜:Jšœ žœ˜J˜!J˜Jšžœžœ˜Jšžœ˜—J˜š¡œžœž˜*JšŸM˜MJšž˜J˜>JšžœC˜Ešžœž˜ J˜J˜J˜Jšž˜—Jšžœ˜Jšžœ˜—J˜šœžœžœ žœ Ÿ˜LJ˜MJ˜JJ˜JJ˜JJ˜JJ˜JJ˜JJ˜JJ˜JJ˜JJ˜—J˜J˜J˜—Jšœ:™:˜Jšœ!˜!Jš œ žœžœžœžœ˜QJ˜Jšœ žœ˜'J˜šŸ¡œžœžœ˜:Jšœžœ˜ JšœžœŸ˜-JšœžœŸ˜(Jšœ žœŸ3˜EJšœŸ˜0Jš œžœžœžœžœŸœ˜Qšžœ˜J˜#Jšœ žœžœ˜Jšœ ˜ Jšœžœ˜"Jšœ$žœ˜(Jšœ!žœŸœ˜F—Jšž˜Jšœ ™ Jšœ*™*JšœR™RJšœE™EJšœ`™`Jšœ#™#Jšœ^™^JšœX™XJšœžœ˜ J˜:J˜"J˜J˜J˜JšŸ8˜8J˜ J˜J˜&J˜JšœZ˜ZJšžœžœžœ˜JšŸ$˜$Jšžœžœžœ5žœ˜\J˜˜\Jšžœ;˜AJ˜—šœžœ ˜0Jšžœž˜šžœžœ˜Jšžœ(˜,Jšžœ˜——šœ žœžœž˜J˜/Jšžœ˜J˜—šžœžœž˜šœžœ žœ˜%Jšž˜Jšœ9™9Jšžœ˜ šžœž˜ JšŸ˜Jšžœ˜Jšžœ%˜)Jšžœ0˜4J˜Jšžœ ˜"šžœž˜ J˜#šžœ9˜;Jšžœžœžœ˜J˜ —Jšžœ˜J˜"Jšžœ˜—J˜(J˜Jšž˜—Jšžœ˜Jšžœ˜—˜Jšž˜Jšœ=™=˜PJšžœ;˜A—Jšžœ ž˜Jšžœ˜Jšžœžœžœ˜'Jšžœžœ˜šžœž˜ Jšœ>™>J˜/J˜1J˜(J˜Jšž˜—Jšžœ˜Jšžœ˜——Jšžœ˜J˜JšŸ˜J˜ Jšœ˜J˜JšžŸœ˜ J˜J˜—š¡œžœžœ1˜BJšž˜J˜#J˜"J˜ J˜&Jšžœ ˜"šžœž˜ Jšœ(žœŸ˜NJšœ2Ÿ˜EJšžœ˜—J˜ Jšžœ˜J˜J˜—š¡œžœ/˜=J˜#J˜$Jšž˜JšŸN˜NJ˜2Jšžœ˜šžœž˜ J˜,Jšœ'žœ˜-Jšž˜—šžœž˜ J˜*šžœ9˜;Jšžœžœžœ˜J˜ —Jšžœ˜J˜"Jšžœ˜—J˜J˜+Jšžœ˜J˜—J˜š¡ œžœ˜#Jšž˜Jšžœ ž˜šžœž˜ Jšœžœžœžœ ˜@šœžœžœ˜-Jšžœžœžœžœ/˜SJšœ˜—Jšžœ&˜(Jšžœ˜—šžœ˜J˜——š¡ œžœ˜%Jšž˜Jšžœ ž˜šžœž˜ Jšœžœžœžœ ˜@šœžœžœ˜-Jšžœžœžœžœ/˜SJšœ˜—Jšžœ(˜*Jšžœ˜—šžœ˜J˜——š¡ œžœ˜'J˜J˜J˜Jšžœ,ž˜9Jšž˜JšŸ,˜,JšœY™YJšœX™XJ˜Jšžœ žœžœžœ˜Jšžœ˜šžœž˜Jšžœžœžœ˜%Jš žœžœžœ žœžœ˜JJšžœžœžœ˜$Jš žœžœžœžœžœ˜;Jšžœ&žœ˜@Jšžœ žœžœ˜J˜2šž˜Jšœ ž˜ —Jšž˜—JšžœD˜HJšžœ˜J˜—š¡œžœ˜-J˜J˜J˜Jšžœ+˜2Jšž˜J˜šžœžœ#žœžœ˜8J˜Jšžœžœžœ˜%˜Jšœžœ ˜,Jšœžœ˜-—Jšžœžœžœ˜$Jš žœžœ žœžœžœ˜=Jšžœ(žœ˜DJšžœ žœžœ˜J˜4—šž˜Jšœ ž˜ —Jšžœ˜Jšžœ˜J˜J˜—š¡ œžœžœ3˜JJšœ+˜+Jšžœ˜%JšŸh˜hJšž˜Jšœ žœžœ'˜:J˜Jšžœžœžœ˜'Jšžœ˜Jšžœ.˜2šžœžœžœ˜"šžœ ˜ Jšžœ5˜7šžœž˜ J˜J˜#JšœFžœ˜MJšžœ ˜šžœž˜ Jšžœžœ˜J˜4J˜Jšžœžœžœ ˜>šžœžœžœžœ˜*J˜—Jšžœ˜Jšžœ˜—Jšžœ˜—J˜ —šž˜Jšœ žœ˜Jšžœž˜—Jšžœ˜—J˜ Jšžœ˜J˜J˜—š œ žœžœžœžœ˜9Jšžœ žœ ˜—š œžœžœžœžœžœ˜>Jšžœ žœ ˜J˜—š¡ œžœžœžœ˜Ršœžœžœ˜Jšžœ!˜%Jšžœžœ2˜?J˜——š¡ œžœžœ"˜SJšœ/žœ˜EJ˜—š¡œžœžœ˜Ašœžœžœ˜*Jšœžœ˜2J˜——š ¡œžœžœžœžœ˜Nšœžœžœ˜Jšžœ˜Jšžœžœ)žœ˜S——J˜J˜J˜—Jšœ,™,˜š¡œžœ3˜@Jšžœž˜&Jš œžœžœžœžœ!˜UJ˜—š¡ œžœ3˜FJšžœ˜Jšž˜J˜˜Jšœžœ ˜,J˜ Jšœžœ˜—Jšžœ˜Jšžœ˜J˜—š¡œžœ3˜@Jšœž˜$Jšœžœžœžœ%˜YJ˜—š¡ œžœ3˜FJ˜Jšž˜J˜˜Jšœžœ ˜,J˜ Jšœžœ˜—J˜˜J˜Jšœžœ ˜*Jšœžœ˜—Jšžœ˜J˜—š¡ œžœžœ˜3Jšœ#ž˜)Jšž˜Jšœ-™-Jšžœ˜šžœž˜ Jšžœ žœ˜'Jšžœ žœ žœ$žœ ˜UJšžœ žœ'˜8Jšž˜—Jšžœ+˜/Jšžœ˜J˜—š¡œžœžœ˜9J˜"Jšž˜Jšœ-™-Jšœ6˜6Jšžœ˜šžœž˜ ˜Jšžœ žœžœžœ˜P—J˜˜;Jšœžœ˜%—Jšœžœ ˜"˜Jšž˜—Jšžœžœžœ˜IJšžœ˜J˜—š¡ œžœžœ˜SJšœžœ˜ Jšžœžœ˜Jšž˜Jšœžœžœžœžœžœ žœžœžœ˜SJšœ˜Jšœ žœ˜šœ%žœ˜LJšœžœ˜*—Jšžœžœžœžœ˜2J˜Jš žœžœžœžœžœŸ˜<šžœ7˜9Jšžœ˜šžœž˜ Jšžœž˜ šžœž˜ Jšœ7˜7Jšœ˜˜Jšœžœ˜&Jšœžœ˜-—JšŸ ˜ J˜Jšžœ˜Jšžœžœ žœžœ˜AJšŸ ˜ Jšžœ!˜#Jšžœ&˜*šžœž˜ JšŸ.˜.Jšœ žœ˜Jšœ;˜;Jšžœ)˜+Jšžœ˜šžœŸ/˜4šžœB˜DJšžœ)žœžœ˜5J˜'—Jšžœ˜—J˜ J˜˜JJšœžœ˜*—Jšžœ˜—JšŸ˜˜EJšœžœ˜*—˜FJšœžœ˜*—˜-Jšœžœ˜+Jšœžœ˜—˜0Jšœžœ˜$Jšœžœ˜—Jšžœ˜—Jšžœžœ˜ Jšžœ˜—J˜Jšžœ žœžœžœ˜"—Jšžœ˜JšžœžœŸ0˜>Jšžœ˜J˜—š ¡œžœžœžœžœ˜CJšžœž˜,Jšž˜Jšžœ˜šžœž˜ Jšžœžœ žœžœ˜:Jšžœ žœ˜"Jšž˜—Jšžœžœ$˜/Jšžœ˜J˜—š¡ œžœžœžœ˜AJšžœ#˜*Jšž˜J˜0˜J˜J˜ Jšœžœ˜*—Jšžœ˜šžœž˜ Jšœžœ˜˜J˜ J˜ Jšœžœ˜*—Jšžœ˜—Jšžœ˜J˜—š¡œžœžœžœ˜LJš œžœžœ žœžœ!˜^J˜J˜J˜—JšŸ˜J˜š œ žœžœžœžœžœžœ˜+Jšžœžœžœžœžœžœžœžœ˜-J˜—Jšœž œ˜,J˜š¡œžœžœžœ˜Jš œžœžœžœžœžœ˜WJ˜—š ¡œžœžœžœž˜%Jš œžœžœžœžœ˜>J˜—š¡œžœžœžœ˜Jšœžœž œ˜;J˜J˜J˜—JšŸ1˜1J˜J˜,J˜š¡œžœ˜"šœžœ˜Jšžœ"˜&Jšžœ˜J˜——š¡œžœž˜+šœžœ˜Jšžœ!˜%Jšžœ˜J˜——š ¡œžœžœžœžœžœž˜8šœžœžœ˜0Jšžœžœžœžœ˜'Jšžœ>˜DJ˜J˜J˜———J˜#J˜&Jšœ(žœ ˜9Jšœ)žœ ˜;J˜Jšžœ˜J˜J˜—…—fJÙ