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; 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]; 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, 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. πAMProcessBasicImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Russ Atkinson, February 20, 1985 8:12:38 pm PST 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. Κm˜codešœ™Kšœ Οmœ1™Kšœ)™)Kšœ žœ Ÿ œ˜=K˜Kšžœžœžœžœ˜;Kšžœ$˜(Kšžœ˜—KšŸM˜MK˜K˜ K˜KšžŸ œ˜K˜K˜—š‘œžœžœ ˜NKšž˜KšŸ(˜(KšœZ™ZKšœS™SKšœ™Kšœ*™*Kšœ žœžœ˜Kšœ žœžœ˜K˜Kšœ˜Kšœžœžœžœ˜&K˜Kšœ™šžœžœžœ"˜7K˜#š‘œžœ˜ Kšž˜K˜K˜Kšœ žœ˜K˜Kšœžœ žœžœ˜8Kšžœ˜—K˜šžœž˜Kšœžœ˜/Kšœžœ˜2˜Kšž˜Kšœžœ˜ K˜Kšžœ ˜Kšžœ)žœžœžœ˜KKšžœžœž˜Kšžœ˜Kšžœ˜—˜KšœžœŸ;œ˜V——KšžœžœŸ)˜Mšžœ&žœ˜LKšœ ˜ —Kšžœžœ˜ KšŸ9˜9—Kšžœ˜K˜KšŸ=˜=šž˜K˜K˜Kš œžœžœ žœžœžœ˜8Kšœžœ˜Kšžœ ˜"šžœž˜ Kš œžœ žœžœžœ˜/Kšžœžœ˜9Kšžœžœžœ,žœ˜ZKšžœžœž˜Kšžœ˜Kšž˜—KšžœH˜LKšœR˜RK˜9Kšžœ ž˜ Kš žœžœ žœžœžœ˜*Kšžœžœ žœ˜ Kšœžœ˜šžœžœž˜šœ žœžœ˜$Kšœžœžœ˜'—šœ žœžœ˜%Kšœžœžœ˜'—šœ žœžœ˜$šœ˜KšœžœH˜R——šœ žœžœ˜%šœ˜KšœžœH˜R———Kšžœžœ˜KšŸ@˜@šž˜Kšœ žœžœžœ˜+Kš œ žœžœžœžœ ˜4Kšžœžœ˜:Kš žœžœžœžœžœ˜E—Kšžœ˜—Kšžœ˜K˜KšŸ˜šžœ&žœ˜LKšœ ˜ —Kšžœžœ˜ K˜KšŸG˜Gšž˜KšœB˜B˜ Kšžœžœžœ˜7—Kšžœ Ÿ˜#šžœŸ˜/K˜"Kšžœ žœžœ˜,K˜!—Kšžœžœž˜Kšžœ˜KšŸ)˜)Kšžœ˜Kšžœ1žœ˜GKšžœ"˜&—Kšžœ˜K˜KšžŸ œ˜K˜K˜—š ‘ œžœžœžœžœ˜=Kšž˜Kšœžœž œ˜:Kšœžœ˜ Kš‘œžœžœ!˜NK˜%KšŸ-˜-Kšœ žœ˜K˜?K˜ KšŸF˜FKšžœ žœ˜'šžœž˜ KšŸ;˜;K˜>Kšœžœžœ#˜5K˜$Kšœ žœžœ˜'Kšœžœ˜K˜$Kšœžœ žœžœ˜EKšŸA˜AKšžœžœ ˜$Kš žœ žœžœžœžœ˜EKšžœ˜—K˜4K˜#Kšžœžœ ˜Kšžœ˜K˜K˜—š‘œžœ˜Kšž˜Kšœžœž œ˜:Kšœ žœ˜K˜ K˜Kšžœžœ ˜Kšžœ˜K˜—š‘œžœ˜Kšž˜Kšœžœž œ˜:Kšœ žœ˜K˜!K˜Kšžœžœ˜Kšžœ˜—K˜š‘œžœž˜*KšŸM˜MKšž˜K˜>KšžœC˜Ešžœž˜ K˜K˜K˜Kšž˜—Kšžœ˜Kšžœ˜—K˜šœžœžœ žœ Ÿ˜LK˜MK˜JK˜JK˜JK˜JK˜JK˜JK˜JK˜JK˜JK˜—K˜K˜K˜—Kšœ:™:˜Kšœ!˜!Kš œ žœžœžœžœ˜QK˜Kšœ žœ˜'K˜šŸ‘œžœžœ˜:Kšœžœ˜ KšœžœŸ˜-KšœžœŸ˜(Kšœ žœŸ3˜EKšœŸ˜0Kš œžœžœžœžœŸœ˜Qšžœ˜K˜#Kšœ žœžœ˜Kšœ ˜ Kšœžœ˜"Kšœ$žœ˜(Kšœ!žœŸœ˜F—Kšž˜Kšœ ™ Kšœ*™*KšœR™RKšœE™EKšœ`™`Kšœ#™#Kšœ^™^KšœX™XKšœžœ˜ K˜:K˜"K˜K˜K˜KšŸ8˜8K˜ K˜K˜&K˜KšœZ˜ZKšžœžœžœ˜KšŸ$˜$Kšžœžœžœ5žœ˜\K˜˜\Kšžœ;˜AK˜—šœžœ ˜0Kšžœž˜šžœžœ˜Kšžœ(˜,Kšžœ˜——šœ žœžœž˜K˜/Kšžœ˜K˜—šžœžœž˜šœžœ žœ˜%Kšž˜Kšœ9™9Kšžœ˜ šžœž˜ KšŸ˜Kšžœ˜Kšžœ%˜)Kšžœ0˜4K˜Kšžœ ˜"šžœž˜ K˜#šžœ9˜;Kšžœžœžœ˜K˜ —Kšžœ˜K˜"Kšžœ˜—K˜(K˜Kšž˜—Kšžœ˜Kšžœ˜—˜Kšž˜Kšœ=™=˜PKšžœ;˜A—Kšžœ ž˜Kšžœ˜Kšžœžœžœ˜'Kšžœžœ˜šžœž˜ Kšœ>™>K˜/K˜1K˜(K˜Kšž˜—Kšžœ˜Kšžœ˜——Kšžœ˜K˜KšŸ˜K˜ Kšœ˜K˜KšžŸœ˜ K˜K˜—š‘œžœžœ1˜BKšž˜K˜#K˜"K˜ K˜&Kšžœ ˜"šžœž˜ Kšœ(žœŸ˜NKšœ2Ÿ˜EKšžœ˜—K˜ Kšžœ˜K˜K˜—š‘œžœ/˜=K˜#K˜$Kšž˜KšŸN˜NK˜2Kšžœ˜šžœž˜ K˜,Kšœ'žœ˜-Kšž˜—šžœž˜ K˜*šžœ9˜;Kšžœžœžœ˜K˜ —Kšžœ˜K˜"Kšžœ˜—K˜K˜+Kšžœ˜K˜—K˜š‘ œžœ˜#Kšž˜Kšžœ ž˜šžœž˜ Kšœžœžœžœ ˜@šœžœžœ˜-Kšžœžœžœžœ/˜SKšœ˜—Kšžœ&˜(Kšžœ˜—šžœ˜K˜——š‘ œžœ˜%Kšž˜Kšžœ ž˜šžœž˜ Kšœžœžœžœ ˜@šœžœžœ˜-Kšžœžœžœžœ/˜SKšœ˜—Kšžœ(˜*Kšžœ˜—šžœ˜K˜——š‘ œžœ˜'K˜K˜K˜Kšžœ,ž˜9Kšž˜KšŸ,˜,KšœY™YKšœX™XK˜Kšžœ žœžœžœ˜Kšžœ˜šžœž˜Kšžœžœžœ˜%Kš žœžœžœ žœžœ˜JKšžœžœžœ˜$Kš žœžœžœžœžœ˜;Kšžœ&žœ˜@Kšžœ žœžœ˜K˜2šž˜Kšœ ž˜ —Kšž˜—KšžœD˜HKšžœ˜K˜—š‘œžœ˜-K˜K˜K˜Kšžœ+˜2Kšž˜K˜šžœžœ#žœžœ˜8K˜Kšžœžœžœ˜%˜Kšœžœ ˜,Kšœžœ˜-—Kšžœžœžœ˜$Kš žœžœ žœžœžœ˜=Kšžœ(žœ˜DKšžœ žœžœ˜K˜4—šž˜Kšœ ž˜ —Kšžœ˜Kšžœ˜K˜K˜—š‘ œžœžœ3˜JKšœ+˜+Kšžœ˜%KšŸh˜hKšž˜Kšœ žœžœ'˜:K˜Kšžœžœžœ˜'Kšžœ˜Kšžœ.˜2šžœžœžœ˜"šžœ ˜ Kšžœ5˜7šžœž˜ K˜K˜#KšœFžœ˜MKšžœ ˜šžœž˜ Kšžœžœ˜K˜4K˜Kšžœžœžœ ˜>šžœžœžœžœ˜*K˜—Kšžœ˜Kšžœ˜—Kšžœ˜—K˜ —šž˜Kšœ žœ˜Kšžœž˜—Kšžœ˜—K˜ Kšžœ˜K˜K˜—š œ žœžœžœžœ˜9Kšžœ žœ ˜—š œžœžœžœžœžœ˜>Kšžœ žœ ˜K˜—š‘ œžœžœžœ˜Ršœžœžœ˜Kšžœ!˜%Kšžœžœ2˜?K˜——š‘ œžœžœ"˜SKšœ/žœ˜EK˜—š‘œžœžœ˜Ašœžœžœ˜*Kšœžœ˜2K˜——š ‘œžœžœžœžœ˜Nšœžœžœ˜Kšžœ˜Kšžœžœ)žœ˜S——K˜K˜K˜—Kšœ,™,˜š‘œžœ3˜@Kšžœž˜&Kš œžœžœžœžœ!˜UK˜—š‘ œžœ3˜FKšžœ˜Kšž˜K˜˜Kšœžœ ˜,K˜ Kšœžœ˜—Kšžœ˜Kšžœ˜K˜—š‘œžœ3˜@Kšœž˜$Kšœžœžœžœ%˜YK˜—š‘ œžœ3˜FK˜Kšž˜K˜˜Kšœžœ ˜,K˜ Kšœžœ˜—K˜˜K˜Kšœžœ ˜*Kšœžœ˜—Kšžœ˜K˜—š‘ œžœžœ˜3Kšœ#ž˜)Kšž˜Kšœ-™-Kšžœ˜šžœž˜ Kšžœ žœ˜'Kšžœ žœ žœ$žœ ˜UKšžœ žœ'˜8Kšž˜—Kšžœ+˜/Kšžœ˜K˜—š‘œžœžœ˜9K˜"Kšž˜Kšœ-™-Kšœ6˜6Kšžœ˜šžœž˜ ˜Kšžœ žœžœžœ˜P—K˜˜;Kšœžœ˜%—Kšœžœ ˜"˜Kšž˜—Kšžœžœžœ˜IKšžœ˜K˜—š‘ œžœžœ˜SKšœžœ˜ Kšžœžœ˜Kšž˜Kšœžœžœžœžœžœ žœžœžœ˜SKšœ˜Kšœ žœ˜šœ%žœ˜LKšœžœ˜*—Kšžœžœžœžœ˜2K˜Kš žœžœžœžœžœŸ˜<šžœ7˜9Kšžœ˜šžœž˜ Kšžœž˜ šžœž˜ Kšœ7˜7Kšœ˜˜Kšœžœ˜&Kšœžœ˜-—KšŸ ˜ K˜Kšžœ˜Kšžœžœ žœžœ˜AKšŸ ˜ Kšžœ!˜#Kšžœ&˜*šžœž˜ KšŸ.˜.Kšœ žœ˜Kšœ;˜;Kšžœ)˜+Kšžœ˜šžœŸ/˜4šžœB˜DKšžœ)žœžœ˜5K˜'—Kšžœ˜—K˜ K˜˜JKšœžœ˜*—Kšžœ˜—KšŸ˜˜EKšœžœ˜*—˜FKšœžœ˜*—˜-Kšœžœ˜+Kšœžœ˜—˜0Kšœžœ˜$Kšœžœ˜—Kšžœ˜—Kšžœžœ˜ Kšžœ˜—K˜Kšžœ žœžœžœ˜"—Kšžœ˜KšžœžœŸ0˜>Kšžœ˜K˜—š ‘œžœžœžœžœ˜CKšžœž˜,Kšž˜Kšžœ˜šžœž˜ Kšžœžœ žœžœ˜:Kšžœ žœ˜"Kšž˜—Kšžœžœ$˜/Kšžœ˜K˜—š‘ œžœžœžœ˜AKšžœ#˜*Kšž˜K˜0˜K˜K˜ Kšœžœ˜*—Kšžœ˜šžœž˜ Kšœžœ˜˜K˜ K˜ Kšœžœ˜*—Kšžœ˜—Kšžœ˜K˜—š‘œžœžœžœ˜LKš œžœžœ žœžœ!˜^K˜K˜K˜—KšŸ˜K˜š œ žœžœžœžœžœžœ˜+Kšžœžœžœžœžœžœžœžœ˜-K˜—Kšœž œ˜,K˜š‘œžœžœžœ˜Kš œžœžœžœžœžœ˜WK˜—š ‘œžœžœžœž˜%Kš œžœžœžœžœ˜>K˜—š‘œžœžœžœ˜Kšœžœž œ˜;K˜K˜K˜—KšŸ1˜1K˜K˜,K˜š‘œžœ˜"šœžœ˜Kšžœ"˜&Kšžœ˜K˜——š‘œžœž˜+šœžœ˜Kšžœ!˜%Kšžœ˜K˜——š ‘œžœžœžœžœžœž˜8šœžœžœ˜0Kšžœžœžœžœ˜'Kšžœ9˜?K˜K˜K˜———K˜#K˜&Kšœ(žœ ˜9Kšœ)žœ ˜;K˜Kšžœ˜K˜K˜—…—eκG