2943 lines
98 KiB
ArmAsm
2943 lines
98 KiB
ArmAsm
// TITLE("Context Swap")
|
|
|
|
|
|
// Copyright (c) 1994 IBM Corporation
|
|
|
|
// Module Name:
|
|
|
|
// ctxswap.s
|
|
|
|
// Abstract:
|
|
|
|
// This module implements the PowerPC machine dependent code necessary to
|
|
// field the dispatch interrupt and to perform kernel initiated context
|
|
// switching.
|
|
|
|
// Author:
|
|
|
|
// Peter L. Johnston (plj@vnet.ibm.com) August 1993
|
|
// Adapted from code by David N. Cutler (davec) 1-Apr-1991
|
|
|
|
// Environment:
|
|
|
|
// Kernel mode only.
|
|
|
|
// Revision History:
|
|
|
|
// plj Apr-94 Upgraded to NT 3.5.
|
|
|
|
|
|
|
|
#include "ksppc.h"
|
|
|
|
// Module Constants
|
|
|
|
#define rPrcb r.29
|
|
#define OTH r.30
|
|
#define NTH r.31
|
|
|
|
// Global externals
|
|
|
|
.extern ..KdPollBreakIn
|
|
.extern ..KeFlushCurrentTb
|
|
.extern ..KiActivateWaiterQueue
|
|
.extern ..KiContinueClientWait
|
|
.extern ..KiDeliverApc
|
|
.extern ..KiQuantumEnd
|
|
.extern ..KiReadyThread
|
|
.extern ..KiWaitTest
|
|
|
|
.extern KdDebuggerEnabled
|
|
.extern KeTickCount
|
|
.extern KiDispatcherReadyListHead
|
|
.extern KiIdleSummary
|
|
.extern KiReadySummary
|
|
.extern KiWaitInListHead
|
|
.extern KiWaitOutListHead
|
|
.extern __imp_HalProcessorIdle
|
|
.extern __imp_KeLowerIrql
|
|
#if DBG
|
|
.extern ..DbgBreakPoint
|
|
.extern ..DbgBreakPointWithStatus
|
|
#endif
|
|
#if !defined(NT_UP)
|
|
|
|
.extern KeNumberProcessors
|
|
.extern KiBarrierWait
|
|
.extern KiContextSwapLock
|
|
.extern KiDispatcherLock
|
|
.extern KiProcessorBlock
|
|
|
|
#if SPINDBG
|
|
.extern ..KiAcquireSpinLockDbg
|
|
.extern ..KiTryToAcquireSpinLockDbg
|
|
#endif
|
|
|
|
#endif
|
|
.extern KiMasterSequence
|
|
.extern KiMasterPid
|
|
|
|
.globl KiScheduleCount
|
|
.data
|
|
KiScheduleCount:
|
|
.long 0
|
|
|
|
#if COLLECT_PAGING_DATA
|
|
.globl KiFlushOnProcessSwap
|
|
.data
|
|
KiFlushOnProcessSwap:
|
|
.long 0
|
|
#endif
|
|
|
|
|
|
|
|
// ThreadSwitchFrame
|
|
|
|
// This is the layout of the beginning of the stack frame that must be
|
|
// established by any routine that calls SwapContext.
|
|
|
|
// The caller of SwapContext must have saved r.14 and 26 thru 31.
|
|
// SwapContext will take care of r.15 thru r.25, f.14 thru f.31 and
|
|
// the condition register.
|
|
|
|
// Note: this is not a complete stack frame, the caller must allocate
|
|
// additional space for any additional registers it needs to
|
|
// save (eg Link Register). (Also, the following has not been
|
|
// padded to 8 bytes).
|
|
|
|
// WARNING: KiInitializeContextThread() is aware of the layout of
|
|
// a ThreadSwitchFrame.
|
|
|
|
|
|
.struct 0
|
|
.space StackFrameHeaderLength
|
|
swFrame:.space SwapFrameLength
|
|
swFrameLength:
|
|
|
|
|
|
// SBTTL("Switch To Thread")
|
|
|
|
|
|
// NTSTATUS
|
|
// KiSwitchToThread (
|
|
// IN PKTHREAD NextThread,
|
|
// IN ULONG WaitReason,
|
|
// IN ULONG WaitMode,
|
|
// IN PKEVENT WaitObject
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This function performs an optimal switch to the specified target thread
|
|
// if possible. No timeout is associated with the wait, thus the issuing
|
|
// thread will wait until the wait event is signaled or an APC is deliverd.
|
|
|
|
// N.B. This routine is called with the dispatcher database locked.
|
|
|
|
// N.B. The wait IRQL is assumed to be set for the current thread and the
|
|
// wait status is assumed to be set for the target thread.
|
|
|
|
// N.B. It is assumed that if a queue is associated with the target thread,
|
|
// then the concurrency count has been incremented.
|
|
|
|
// N.B. Control is returned from this function with the dispatcher database
|
|
// unlocked.
|
|
|
|
// Arguments:
|
|
|
|
// NextThread - Supplies a pointer to a dispatcher object of type thread.
|
|
|
|
// WaitReason - supplies the reason for the wait operation.
|
|
|
|
// WaitMode - Supplies the processor wait mode.
|
|
|
|
// WaitObject - Supplies a pointer to a dispatcher object of type event
|
|
// or semaphore.
|
|
|
|
// Return Value:
|
|
|
|
// The wait completion status. A value of STATUS_SUCCESS is returned if
|
|
// the specified object satisfied the wait. A value of STATUS_USER_APC is
|
|
// returned if the wait was aborted to deliver a user APC to the current
|
|
// thread.
|
|
|
|
|
|
.struct 0
|
|
.space swFrameLength
|
|
sttLR: .space 4
|
|
sttReason: .space 4
|
|
sttMode: .space 4
|
|
sttObject: .space 4
|
|
.align 3 // ensure 8 byte alignment
|
|
sttFrameLength:
|
|
|
|
SPECIAL_ENTRY_S(KiSwitchToThread,_TEXT$00)
|
|
|
|
mflr r.0 // get return address
|
|
stwu r.sp, -sttFrameLength(r.sp) // buy stack frame
|
|
stw r.14, swFrame + ExGpr14(r.sp) // save gpr 14
|
|
stw r.26, swFrame + ExGpr26(r.sp) // save gprs 26 through 31
|
|
stw r.27, swFrame + ExGpr27(r.sp)
|
|
stw r.28, swFrame + ExGpr28(r.sp)
|
|
stw r.29, swFrame + ExGpr29(r.sp)
|
|
stw r.30, swFrame + ExGpr30(r.sp)
|
|
stw r.31, swFrame + ExGpr31(r.sp)
|
|
ori NTH, r.3, 0
|
|
stw r.0, sttLR(r.sp) // save return address
|
|
li r.0, 0
|
|
|
|
PROLOGUE_END(KiSwitchToThread)
|
|
|
|
|
|
// Save the wait reason, the wait mode, and the wait object address.
|
|
|
|
|
|
stw r.4, sttReason(r.sp) // save wait reason
|
|
stw r.5, sttMode(r.sp) // save wait mode
|
|
stw r.6, sttObject(r.sp) // save wait object address
|
|
|
|
|
|
// If the target thread's kernel stack is resident, the target thread's
|
|
// process is in the balance set, the target thread can run on the
|
|
// current processor, and another thread has not already been selected
|
|
// to run on the current processor, then do a direct dispatch to the
|
|
// target thread bypassing all the general wait logic, thread priorities
|
|
// permiting.
|
|
|
|
|
|
lwz r.7, ThApcState + AsProcess(NTH) // get target process address
|
|
lbz r.8, ThKernelStackResident(NTH) // get kernel stack resident
|
|
lwz rPrcb, KiPcr + PcPrcb(r.0) // get address of PRCB
|
|
lbz r.10, PrState(r.7) // get target process state
|
|
lwz OTH, KiPcr + PcCurrentThread(r.0) // get current thread address
|
|
cmpwi r.8, 0 // kernel stack resident?
|
|
beq LongWay // if eq, kernel stack not resident
|
|
cmpwi r.10, ProcessInMemory // process in memory?
|
|
bne LongWay // if ne, process not in memory
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.8, PbNextThread(rPrcb) // get address of next thread
|
|
lbz r.10, ThNextProcessor(OTH) // get current processor number
|
|
lwz r.14, ThAffinity(NTH) // get target thread affinity
|
|
lwz r.26, KiPcr + PcSetMember(r.0) // get processor set member
|
|
cmpwi r.8, 0 // next thread selected?
|
|
bne LongWay // if ne, next thread selected
|
|
and. r.26, r.26, r.14 // check for compatible affinity
|
|
beq LongWay // if eq, affinity not compatible
|
|
|
|
#endif
|
|
|
|
|
|
// Compute the new thread priority.
|
|
|
|
|
|
lbz r.14, ThPriority(OTH) // get client thread priority
|
|
lbz r.26, ThPriority(NTH) // get server thread priority
|
|
cmpwi r.14, LOW_REALTIME_PRIORITY // check if realtime client
|
|
cmpwi cr.7, r.26, LOW_REALTIME_PRIORITY // check if realtime server
|
|
bge stt60 // if ge, realtime client
|
|
lbz r.27, ThPriorityDecrement(NTH) // get priority decrement value
|
|
lbz r.28, ThBasePriority(NTH) // get server base priority
|
|
bge cr.7, stt50 // if ge, realtime server
|
|
addi r.9, r.28, 1 // computed boosted priority
|
|
cmpwi r.27, 0 // server boot active?
|
|
bne stt30 // if ne, server boost active
|
|
|
|
|
|
// Both the client and the server are not realtime and a priority boost
|
|
// is not currently active for the server. Under these conditions an
|
|
// optimal switch to the server can be performed if the base priority
|
|
// of the server is above a minimum threshold or the boosted priority
|
|
// of the server is not less than the client priority.
|
|
|
|
|
|
cmpw r.9, r.14 // check if high enough boost
|
|
cmpwi cr.7, r.9, LOW_REALTIME_PRIORITY // check if less than realtime
|
|
blt stt20 // if lt, boosted priority less
|
|
stb r.9, ThPriority(NTH) // asssume boosted priority is okay
|
|
blt cr.7, stt70 // if lt, less than realtime
|
|
li r.9, LOW_REALTIME_PRIORITY - 1 // set high server priority
|
|
stb r.9, ThPriority(NTH) //
|
|
b stt70
|
|
|
|
stt20:
|
|
|
|
|
|
// The boosted priority of the server is less than the current priority of
|
|
// the client. If the server base priority is above the required threshold,
|
|
// then a optimal switch to the server can be performed by temporarily
|
|
// raising the priority of the server to that of the client.
|
|
|
|
|
|
cmpwi r.28, BASE_PRIORITY_THRESHOLD // check if above threshold
|
|
sub r.9, r.14, r.28 // compute priority decrement value
|
|
blt LongWay // if lt, priority below threshold
|
|
li r.28, ROUND_TRIP_DECREMENT_COUNT // get system decrement count value
|
|
stb r.9, ThPriorityDecrement(NTH) // set priority decrement value
|
|
stb r.14, ThPriority(NTH) // set current server priority
|
|
stb r.28, ThDecrementCount(NTH) // set server decrement count
|
|
b stt70
|
|
|
|
stt30:
|
|
|
|
|
|
// A server boost has previously been applied to the server thread. Count
|
|
// down the decrement count to determine if another optimal server switch
|
|
// is allowed.
|
|
|
|
|
|
|
|
lbz r.9, ThDecrementCount(NTH) // decrement server count value
|
|
subic. r.9, r.9, 1 //
|
|
stb r.9, ThDecrementCount(NTH) // store updated decrement count
|
|
beq stt40 // if eq, no more switches allowed
|
|
|
|
|
|
// Another optimal switch to the server is allowed provided that the
|
|
// server priority is not less than the client priority.
|
|
|
|
|
|
cmpw r.26, r.14 // check if server lower priority
|
|
bge stt70 // if ge, server not lower priority
|
|
b LongWay
|
|
|
|
stt40:
|
|
|
|
|
|
// The server has exhausted the number of times an optimal switch may
|
|
// be performed without reducing its priority. Reduce the priority of
|
|
// the server to its original unboosted value minus one.
|
|
|
|
|
|
stb r.0, ThPriorityDecrement(NTH) // clear server priority decrement
|
|
stb r.28, ThPriority(NTH) // set server priority to base
|
|
b LongWay
|
|
|
|
stt50:
|
|
|
|
|
|
// The client is not realtime and the server is realtime. An optimal switch
|
|
// to the server can be performed.
|
|
|
|
|
|
lbz r.9, PrThreadQuantum(r.7) // get process quantum value
|
|
b stt65
|
|
|
|
stt60:
|
|
|
|
|
|
// The client is realtime. In order for an optimal switch to occur, the
|
|
// server must also be realtime and run at a high or equal priority.
|
|
|
|
|
|
cmpw r.26, r.14 // check if server is lower priority
|
|
lbz r.9, PrThreadQuantum(r.7) // get process quantum value
|
|
blt LongWay // if lt, server is lower priority
|
|
|
|
stt65:
|
|
|
|
stb r.9, ThQuantum(NTH) // set server thread quantum
|
|
|
|
stt70:
|
|
|
|
|
|
// Set the next processor for the server thread.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
stb r.10, ThNextProcessor(NTH) // set server next processor number
|
|
|
|
#endif
|
|
|
|
|
|
// Set the address of the wait block list in the client thread, initialize
|
|
// the event wait block, and insert the wait block in client event wait list.
|
|
|
|
|
|
addi r.8, OTH, EVENT_WAIT_BLOCK_OFFSET // compute wait block address
|
|
stw r.8, ThWaitBlockList(OTH) // set address of wait block list
|
|
stw r.0, ThWaitStatus(OTH) // set initial wait status
|
|
stw r.6, WbObject(r.8) // set address of wait object
|
|
stw r.8, WbNextWaitBlock(r.8) // set next wait block address
|
|
lis r.10, WaitAny // get wait type and wait key
|
|
stw r.10, WbWaitKey(r.8) // set wait key and wait type
|
|
addi r.10, r.6, EvWaitListHead // compute wait object listhead address
|
|
lwz r.14, LsBlink(r.10) // get backward link of listhead
|
|
addi r.26, r.8, WbWaitListEntry // compute wait block list entry address
|
|
stw r.26, LsBlink(r.10) // set backward link of listhead
|
|
stw r.26, LsFlink(r.14) // set forward link in last entry
|
|
stw r.10, LsFlink(r.26) // set forward link in wait entry
|
|
stw r.14, LsBlink(r.26) // set backward link in wait entry
|
|
|
|
|
|
// Set the client thread wait parameters, set the thread state to Waiting,
|
|
// and insert the thread in the proper wait list.
|
|
|
|
|
|
stb r.0, ThAlertable(OTH) // set alertable FALSE.
|
|
stb r.4, ThWaitReason(OTH) // set wait reason
|
|
stb r.5, ThWaitMode(OTH) // set the wait mode
|
|
lbz r.6, ThEnableStackSwap(OTH) // get kernel stack swap enable
|
|
lwz r.10, [toc]KeTickCount(r.toc) // get &KeTickCount
|
|
lwz r.10, 0(r.10) // get low part of tick count
|
|
stw r.10, ThWaitTime(OTH) // set thread wait time
|
|
li r.8, Waiting // set thread state
|
|
stb r.8, ThState(OTH) //
|
|
lwz r.8,[toc]KiWaitInListHead(r.toc) // get address of wait in listhead
|
|
cmpwi r.5, 0 // is wait mode kernel?
|
|
beq stt75 // if eq, wait mode is kernel
|
|
cmpwi r.6, 0 // is kernel stack swap disabled?
|
|
beq stt75 // if eq, kernel stack swap disabled
|
|
cmpwi r.14, LOW_REALTIME_PRIORITY + 9 // check if priority in range
|
|
blt stt76 // if lt, thread priority in range
|
|
stt75:
|
|
lwz r.8,[toc]KiWaitOutListHead(r.toc) // get address of wait in listhead
|
|
stt76:
|
|
lwz r.14, LsBlink(r.8) // get backlink of wait listhead
|
|
addi r.26, OTH, ThWaitListEntry // compute wait list entry address
|
|
stw r.26, LsBlink(r.8) // set backward link of listhead
|
|
stw r.26, LsFlink(r.14) // set forward link in last entry
|
|
stw r.8, LsFlink(r.26) // set forward link in wait entry
|
|
stw r.14, LsBlink(r.26) // set backward link in wait entry
|
|
|
|
stt77:
|
|
|
|
|
|
// If the current thread is processing a queue entry, then attempt to
|
|
// activate another thread that is blocked on the queue object.
|
|
|
|
// N.B. The next thread address can change if the routine to activate
|
|
// a queue waiter is called.
|
|
|
|
|
|
lwz r.3, ThQueue(OTH) // get queue object address
|
|
cmpwi r.3, 0 // queue object attached?
|
|
beq stt78 // if eq, no queue object attached
|
|
stw NTH, PbNextThread(rPrcb) // set next thread address
|
|
bl ..KiActivateWaiterQueue // attempt to activate a blocked thread
|
|
lwz NTH, PbNextThread(rPrcb) // get next thread address
|
|
li r.0, 0
|
|
stw r.0, PbNextThread(rPrcb) // set next thread address to NULL
|
|
stt78:
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.27, [toc]KiContextSwapLock(r.2)// get &KiContextSwapLock
|
|
lwz r.28, [toc]KiDispatcherLock(r.2) // get &KiDispatcherLock
|
|
|
|
#endif
|
|
|
|
stw NTH, PbCurrentThread(rPrcb) // set address of current thread object
|
|
bl ..SwapContext // swap context
|
|
|
|
|
|
// Lower IRQL to its previous level.
|
|
|
|
// N.B. SwapContext releases the dispatcher database lock.
|
|
|
|
// N.B. Register NTH (r.31) contains the address of the new thread on return.
|
|
|
|
// In the following, we could lower IRQL, isync then check for pending
|
|
// interrupts. I believe it is faster to disable interrupts and get
|
|
// both loads going. We need to avoid the situation where a DPC or
|
|
// APC could be queued between the time we load PcSoftwareInterrupt
|
|
// and actually lowering IRQL. (plj)
|
|
|
|
// We load the thread's WaitStatus in this block for scheduling
|
|
// reasons in the hope that the normal case will be there is NOT
|
|
// a DPC or APC pending.
|
|
|
|
|
|
lbz r.27, ThWaitIrql(NTH) // get original IRQL
|
|
|
|
DISABLE_INTERRUPTS(r.5, r.6)
|
|
|
|
lhz r.4, KiPcr+PcSoftwareInterrupt(r.0)
|
|
lwz r.26, ThWaitStatus(NTH) // get wait completion status
|
|
stb r.27, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
|
|
ENABLE_INTERRUPTS(r.5)
|
|
|
|
cmpw r.4, r.27 // check if new IRQL allows
|
|
ble+ stt79 // APC/DPC and one is pending
|
|
|
|
bl ..KiDispatchSoftwareInterrupt // process software interrupt
|
|
|
|
stt79:
|
|
|
|
|
|
// If the wait was not interrupted to deliver a kernel APC, then return the
|
|
// completion status.
|
|
|
|
|
|
cmpwi r.26, STATUS_KERNEL_APC // check if awakened for kernel APC
|
|
ori r.3, r.26, 0 // set return status
|
|
bne stt90 // if ne, normal wait completion
|
|
|
|
|
|
// Disable interrupts and acquire the dispatcher database lock.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
DISABLE_INTERRUPTS(r.5, r.6)
|
|
|
|
|
|
// WARNING: The address of KiDispatcherLock was intentionally left in
|
|
// r.28 by SwapContext for use here.
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK(r.28, NTH, r.7, stt80, stt82)
|
|
|
|
#endif
|
|
|
|
|
|
// Raise IRQL to synchronization level and save wait IRQL.
|
|
|
|
|
|
li r.7, SYNCH_LEVEL
|
|
stb r.7, KiPcr+PcCurrentIrql(r.0)
|
|
|
|
#if !defined(NT_UP)
|
|
ENABLE_INTERRUPTS(r.5)
|
|
#endif
|
|
|
|
stb r.27, ThWaitIrql(NTH) // set client wait IRQL
|
|
|
|
b ContinueWait
|
|
|
|
#if !defined(NT_UP)
|
|
SPIN_ON_SPIN_LOCK_ENABLED(r.28, r.7, stt80, stt82, stt85, r.5, r.6)
|
|
#endif
|
|
|
|
LongWay:
|
|
|
|
|
|
// Ready the target thread for execution and wait on the specified wait
|
|
// object.
|
|
|
|
|
|
bl ..KiReadyThread // ready thread for execution
|
|
|
|
|
|
// Continue the wait and return the wait completion status.
|
|
|
|
// N.B. The wait continuation routine is called with the dispatcher
|
|
// database locked.
|
|
|
|
|
|
ContinueWait:
|
|
|
|
lwz r.3, sttObject(r.sp) // get wait object address
|
|
lwz r.4, sttReason(r.sp) // get wait reason
|
|
lwz r.5, sttMode(r.sp) // get wait mode
|
|
bl ..KiContinueClientWait // continue client wait
|
|
|
|
stt90:
|
|
lwz r.0, sttLR(r.sp) // restore return address
|
|
lwz r.26, swFrame + ExGpr26(r.sp) // restore gprs 26 thru 31
|
|
lwz r.27, swFrame + ExGpr27(r.sp) //
|
|
lwz r.28, swFrame + ExGpr28(r.sp) //
|
|
lwz r.29, swFrame + ExGpr29(r.sp) //
|
|
lwz r.30, swFrame + ExGpr30(r.sp) //
|
|
mtlr r.0 // set return address
|
|
lwz r.31, swFrame + ExGpr31(r.sp) //
|
|
lwz r.14, swFrame + ExGpr14(r.sp) // restore gpr 14
|
|
addi r.sp, r.sp, sttFrameLength // return stack frame
|
|
|
|
blr
|
|
|
|
DUMMY_EXIT(KiSwitchToThread)
|
|
|
|
SBTTL("Dispatch Software Interrupt")
|
|
|
|
|
|
// VOID
|
|
// KiDispatchSoftwareInterrupt (VOID)
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is called when the current irql drops below
|
|
// DISPATCH_LEVEL and a software interrupt may be pending. A
|
|
// software interrupt is either a pending DPC or a pending APC.
|
|
// If a DPC is pending, Irql is raised to DISPATCH_LEVEL and
|
|
// KiDispatchInterrupt is called. When KiDispatchInterrupt
|
|
// returns, the Irql (before we raised it) is compared to
|
|
// APC_LEVEL and if less and an APC interrupt is pending it
|
|
// is processed.
|
|
|
|
// Note: this routine manipulates PCR->CurrentIrql directly to
|
|
// avoid recursion by using Ke[Raise|Lower]Irql.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
.text
|
|
.align 5 // cache block align
|
|
|
|
LEAF_ENTRY_S(KiDispatchSoftwareInterrupt, _TEXT$00)
|
|
|
|
li r.3, 1 // Flag to dispatch routines
|
|
// to enable interrupts when
|
|
// returning to IRQL 0
|
|
DISABLE_INTERRUPTS(r.7, r.4)
|
|
|
|
ALTERNATE_ENTRY(KiDispatchSoftwareIntDisabled)
|
|
stw r.3, -8(r.sp) // Flag to dispatch routines
|
|
// indicating whether to enable
|
|
// or not to enable interrupts
|
|
// when returning to IRQL 0
|
|
|
|
lhz r.9, KiPcr+PcSoftwareInterrupt(r.0) // pending s/w interrupt?
|
|
lbz r.3, KiPcr+PcCurrentIrql(r.0) // get current irql
|
|
srwi. r.4, r.9, 8 // isolate DPC pending
|
|
cmpw cr.6, r.9, r.3 // see if APC int and APC level
|
|
cmpwi cr.7, r.3, APC_LEVEL // compare IRQL to APC LEVEL
|
|
|
|
|
|
// Possible values for SoftwareInterrupt (r.9) are
|
|
|
|
// 0x0101 DPC and APC interrupt pending
|
|
// 0x0100 DPC interrupt pending
|
|
// 0x0001 APC interrupt pending
|
|
// 0x0000 No software interrupt pending (unlikely but possible)
|
|
|
|
// Possible values for current IRQL are zero or one. By comparing
|
|
// SoftwareInterrupt against current IQRL (above) we can quickly see
|
|
// if any software interrupts are valid at this time.
|
|
|
|
// Calculate correct IRQL for the interrupt we are processing. If DPC
|
|
// then we need to be at DISPATCH_LEVEL which is one greater than APC_
|
|
// LEVEL. r.4 contains one if we are going to run a DPC, so we add
|
|
// APC_LEVEL to r.4 to get the desired IRQL.
|
|
|
|
|
|
addi r.4, r.4, APC_LEVEL // calculate new IRQL
|
|
|
|
ble cr.6,ExitEnabled // return if no valid interrupt
|
|
|
|
|
|
#if DBG
|
|
cmplwi cr.6, r.3, DISPATCH_LEVEL // sanity check, should only
|
|
blt+ cr.6, $+12 // below DISPATCH_LEVEL
|
|
twi 31, 0, 0x16 // BREAKPOINT
|
|
blr // return if wrong IRQL
|
|
#endif
|
|
|
|
|
|
// ..DispatchSoftwareInterrupt is an alternate entry used indirectly
|
|
// by KeReleaseSpinLock (via KxReleaseSpinLock). KeReleaseSpinLock has
|
|
// been carefully written to construct the same conditions as apply if
|
|
// execution came from above.
|
|
|
|
|
|
..DispatchSoftwareInterrupt:
|
|
|
|
stb r.4, KiPcr+PcCurrentIrql(r.0) // set IRQL
|
|
ENABLE_INTERRUPTS(r.7)
|
|
|
|
beq cr.0, ..KiDispatchApc // jif not DPC interrupt
|
|
beq cr.7, ..KiDispatchDpcOnly // jif DPC and old IRQL APC LEV.
|
|
b ..KiDispatchDpc // DPC int, old IRQL < APC LEV.
|
|
|
|
ExitEnabled:
|
|
ENABLE_INTERRUPTS(r.7)
|
|
blr
|
|
|
|
DUMMY_EXIT(KiDispatchSoftwareInterrupt)
|
|
|
|
SBTTL("Unlock Dispatcher Database")
|
|
|
|
|
|
// VOID
|
|
// KiUnlockDispatcherDatabase (
|
|
// IN KIRQL OldIrql
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered at synchronization level with the dispatcher
|
|
// database locked. Its function is to either unlock the dispatcher
|
|
// database and return or initiate a context switch if another thread
|
|
// has been selected for execution.
|
|
|
|
// N.B. This code merges with the following swap context code.
|
|
|
|
// N.B. A context switch CANNOT be initiated if the previous IRQL
|
|
// is greater than or equal to DISPATCH_LEVEL.
|
|
|
|
// N.B. This routine is carefully written to be a leaf function. If,
|
|
// however, a context swap should be performed, the routine is
|
|
// switched to a nested function.
|
|
|
|
// Arguments:
|
|
|
|
// OldIrql (r.3) - Supplies the IRQL when the dispatcher database
|
|
// lock was acquired.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
LEAF_ENTRY_S(KiUnlockDispatcherDatabase, _TEXT$00)
|
|
|
|
|
|
// Check if a thread has been scheduled to execute on the current processor.
|
|
|
|
|
|
cmpwi cr.1, r.3, APC_LEVEL // check if IRQL below dispatch level
|
|
lwz r.7, KiPcr+PcPrcb(r.0) // get address of PRCB
|
|
lhz r.9, KiPcr+PcSoftwareInterrupt(r.0) // pending s/w interrupt?
|
|
lwz r.8, PbNextThread(r.7) // get next thread address
|
|
cmpw cr.7, r.9, r.3 // compare pending against irql
|
|
cmpwi r.8, 0 // check if next thread selected
|
|
bne uddSwitch // jif new thread selected
|
|
|
|
|
|
// Not switching, release dispatcher database lock.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
lwz r.5, [toc]KiDispatcherLock(r.toc)
|
|
li r.6, 0
|
|
RELEASE_SPIN_LOCK(r.5, r.6)
|
|
#endif
|
|
|
|
|
|
// If already at dispatch level, we're done.
|
|
|
|
|
|
bgtlr cr.1 // return
|
|
|
|
|
|
// Dropping below DISPATCH_LEVEL, we may need to inspire a software
|
|
// interrupt if one is pending.
|
|
|
|
|
|
// Above we compared r.9 (SoftwareInterrupt) with r.3 (OldIrql) (result in
|
|
// cr.7). See KiDispatchSoftwareInterrupt (above) for possible values.
|
|
// If we did not take the above branch then OldIrql is either zero or one.
|
|
// If SoftwareInterrupt is greater than OldIrql then a pending software
|
|
// interrupt can be taken at this time.
|
|
|
|
|
|
bgt cr.7, uddIntPending // jif pending interrupt
|
|
|
|
stb r.3, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
blr // return
|
|
|
|
|
|
|
|
// A new thread has been selected to run on the current processor, but
|
|
// the new IRQL is not below dispatch level. Release the dispatcher lock.
|
|
// If the current processor is not executing a DPC, then request a dispatch
|
|
// interrupt on the current. IRQL is already at the right level.
|
|
|
|
|
|
uddCantSwitch:
|
|
|
|
#if !defined(NT_UP)
|
|
lwz r.5, [toc]KiDispatcherLock(r.toc)
|
|
li r.6, 0
|
|
RELEASE_SPIN_LOCK(r.5, r.6)
|
|
#endif
|
|
|
|
lwz r.6, PbDpcRoutineActive(r.7)
|
|
li r.9, 1
|
|
cmplwi r.6, 0
|
|
bnelr // return if DPC already active
|
|
|
|
lwz r.5, [toc]KiScheduleCount(r.toc)
|
|
stb r.9, KiPcr+PcDispatchInterrupt(r.0) // request dispatch interrupt
|
|
lwz r.6, 0(r.5)
|
|
addi r.6, r.6, 1 // bump schedule count
|
|
stw r.6, 0(r.5)
|
|
|
|
blr // return
|
|
|
|
|
|
// A software interrupt is pending with higher priority than OldIrql.
|
|
|
|
// cr.1 is the result of comparing OldIrql with APC_LEVEL.
|
|
|
|
|
|
uddIntPending:
|
|
|
|
|
|
// Set flag to enable interrupts after dispatching software interrupts.
|
|
// r.9 must be non-zero because PcSoftwareInt(r.9) > OldIrql. Only
|
|
// needs to be set once no matter which dispatch routine is called.
|
|
|
|
stw r.9, -8(r.sp)
|
|
|
|
beq cr.1, ..KiDispatchDpcOnly // new IRQL doesn't allow APCs
|
|
|
|
srwi. r.3, r.9, 8 // isolate DPC pending
|
|
addi r.3, r.3, APC_LEVEL // calculate correct IRQL
|
|
stb r.3, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
|
|
bne ..KiDispatchDpc // jif DPC at APC_LEVEL
|
|
|
|
|
|
// IRQL dropped from DISPATCH_LEVEL to APC_LEVEL, make sure no DPCs
|
|
// were queued while we were checking. We are now at APC level so
|
|
// any new DPCs will happen without our having to check again.
|
|
|
|
|
|
lbz r.4, KiPcr+PcDispatchInterrupt(r.0)
|
|
cmpwi r.4, 0 // new DPCs?
|
|
beq ..KiDispatchApc // jif not
|
|
|
|
li r.3, DISPATCH_LEVEL // re-raise to DISPATCH_LEVEL
|
|
stb r.3, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
|
|
b ..KiDispatchDpc
|
|
|
|
DUMMY_EXIT(KiUnlockDispatcherDatabase)
|
|
|
|
|
|
// A new thread has been selected to run on the current processor.
|
|
|
|
// If the new IRQL is less than dispatch level, then switch to the new
|
|
// thread.
|
|
|
|
|
|
uddSwitch:
|
|
bgt cr.1, uddCantSwitch // jif new IRQL > apc level
|
|
|
|
|
|
// N.B. This routine is carefully written as a nested function. Control
|
|
// drops into this function from above.
|
|
|
|
|
|
SPECIAL_ENTRY_S(KxUnlockDispatcherDatabase, _TEXT$00)
|
|
|
|
mflr r.0 // get return address
|
|
stwu r.sp, -kscFrameLength(r.sp) // buy stack frame
|
|
stw r.29, swFrame + ExGpr29(r.sp) // save gpr 29
|
|
stw r.30, swFrame + ExGpr30(r.sp) // save gpr 30
|
|
ori rPrcb, r.7, 0 // copy PRCB address
|
|
stw r.31, swFrame + ExGpr31(r.sp) // save gpr 31
|
|
lwz OTH, KiPcr+PcCurrentThread(r.0) // get current thread address
|
|
stw r.27, swFrame + ExGpr27(r.sp) // save gpr 27
|
|
ori NTH, r.8, 0 // thread to switch to
|
|
stw r.14, swFrame + ExGpr14(r.sp) // save gpr 14
|
|
li r.11,0
|
|
stw r.28, swFrame + ExGpr28(r.sp) // save gpr 28
|
|
stw r.26, swFrame + ExGpr26(r.sp) // save gpr 26
|
|
stw r.0, kscLR(r.sp) // save return address
|
|
|
|
PROLOGUE_END(KxUnlockDispatcherDatabase)
|
|
|
|
stw r.11, PbNextThread(rPrcb) // clear next thread address
|
|
stb r.3, ThWaitIrql(OTH) // save previous IRQL
|
|
|
|
|
|
// Reready current thread for execution and swap context to the selected thread.
|
|
|
|
|
|
ori r.3, OTH, 0 // set address of previous thread object
|
|
stw NTH, PbCurrentThread(rPrcb) // set address of current thread object
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.27,[toc]KiContextSwapLock(r.2)// get &KiContextSwapLock
|
|
lwz r.28,[toc]KiDispatcherLock(r.2) // get &KiDispatcherLock
|
|
|
|
#endif
|
|
|
|
bl ..KiReadyThread // reready thread for execution
|
|
b ksc130 // join common code
|
|
|
|
DUMMY_EXIT(KxUnlockDispatcherDatabase)
|
|
|
|
|
|
|
|
// VOID
|
|
// KxReleaseSpinLock(VOID)
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered when a call to KeReleaseSpinLock lowers
|
|
// IRQL to a level sufficiently low for a pending software interrupt
|
|
// to be deliverable.
|
|
|
|
// Although this routine has no arguments, the following entry conditions
|
|
// apply.
|
|
|
|
// Interrupts are disabled.
|
|
|
|
// r.7 MSR prior to disabling interrupts.
|
|
// r.4 IRQL to be raised to (PCR->CurrentIrql has been lowered
|
|
// even though interrupts are currently disabled).
|
|
|
|
// cr.0 ne if DPC pending
|
|
// cr.7 eq if ONLY DPCs can run (ie PCR->CurrentIrql == APC_LEVEL)
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
.struct 0
|
|
.space StackFrameHeaderLength
|
|
sp31: .space 4 // r.31 save
|
|
.align 3 // ensure 8 byte alignment
|
|
spLength:
|
|
|
|
SPECIAL_ENTRY_S(KxReleaseSpinLock, _TEXT$01)
|
|
|
|
stw r.31, sp31-spLength(r.sp) // save r.31
|
|
mflr r.31 // save return address (in r.31)
|
|
stwu r.sp, -spLength(r.sp) // buy stack frame
|
|
|
|
PROLOGUE_END(KxReleaseSpinLock)
|
|
|
|
li r.3, 1
|
|
stw r.3, -8(r.sp) // flag to dispatch routines
|
|
// to enable interrupts when
|
|
// returning to irql 0.
|
|
bl ..DispatchSoftwareInterrupt
|
|
|
|
mtlr r.31 // set return address
|
|
lwz r.31, sp31(r.sp) // restore r.31
|
|
addi r.sp, r.sp, spLength // release stack frame
|
|
|
|
SPECIAL_EXIT(KxReleaseSpinLock)
|
|
|
|
|
|
|
|
|
|
// VOID
|
|
// KiDispatchDpcOnly (VOID)
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered as a result of lowering IRQL to
|
|
// APC_LEVEL with a DPC interrupt pending. IRQL is currently
|
|
// at DISPATCH_LEVEL, the dispatcher database is unlocked.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
.set kdsiFrameLength, STK_MIN_FRAME+8
|
|
|
|
NESTED_ENTRY_S(KiDispatchDpcOnly, kdsiFrameLength, 0, 0, _TEXT$00)
|
|
PROLOGUE_END(KiDispatchDpcOnly)
|
|
|
|
kddo10: bl ..KiDispatchInterrupt
|
|
|
|
li r.3, APC_LEVEL // get new IRQL
|
|
|
|
DISABLE_INTERRUPTS(r.8, r.4)
|
|
|
|
lbz r.4, KiPcr+PcDispatchInterrupt(r.0) // more DPCs pending?
|
|
lwz r.5, KiPcr+PcPrcb(r.0) // get address of PRCB
|
|
cmpwi r.4, 0
|
|
lwz r.6, PbInterruptCount(r.5) // bump interrupt count
|
|
addi r.4, r.4, APC_LEVEL // calc new IRQL
|
|
addi r.6, r.6, 1
|
|
stw r.6, PbInterruptCount(r.5)
|
|
lwz r.6, STK_MIN_FRAME(r.sp) // parameter to enable
|
|
stb r.4, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
beq+ kddo20
|
|
|
|
ENABLE_INTERRUPTS(r.8)
|
|
|
|
b kddo10 // jif more DPCs to run
|
|
|
|
kddo20: cmpwi r.6, 0 // o.k to enable interrupts?
|
|
beq kddo25 // return if not
|
|
ENABLE_INTERRUPTS(r.8) // reenable interrupts and exit
|
|
|
|
kddo25:
|
|
NESTED_EXIT(KiDispatchDpcOnly, kdsiFrameLength, 0, 0)
|
|
|
|
|
|
|
|
// VOID
|
|
// KiDispatchDpc (VOID)
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered as a result of lowering IRQL below
|
|
// APC_LEVEL with a DPC interrupt pending. IRQL is currently
|
|
// at DISPATCH_LEVEL, the dispatcher database is unlocked.
|
|
|
|
// Once DPC processing is complete, APC processing may be required.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
NESTED_ENTRY_S(KiDispatchDpc, kdsiFrameLength, 0, 0, _TEXT$00)
|
|
PROLOGUE_END(KiDispatchDpc)
|
|
|
|
kdd10: bl ..KiDispatchInterrupt
|
|
|
|
DISABLE_INTERRUPTS(r.8, r.4)
|
|
|
|
lwz r.5, KiPcr+PcPrcb(r.0) // get address of PRCB
|
|
lhz r.4, KiPcr+PcSoftwareInterrupt(r.0) // more DPCs or APCs pending?
|
|
lwz r.6, PbInterruptCount(r.5) // bump interrupt count
|
|
cmpwi r.4, APC_LEVEL
|
|
addi r.6, r.6, 1
|
|
stw r.6, PbInterruptCount(r.5)
|
|
bgt- kdd20 // jif more DPCs
|
|
lwz r.6, STK_MIN_FRAME(r.sp) // parameter to enable
|
|
// interrupts
|
|
stb r.4, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
blt kdd30 // honor parameter to exit
|
|
// enabled or disabled when
|
|
// returning to IRQL 0
|
|
|
|
ENABLE_INTERRUPTS(r.8) // equal
|
|
|
|
b kda10 // jif APCs to run
|
|
|
|
kdd20: ENABLE_INTERRUPTS(r.8) // greater than
|
|
|
|
b kdd10 // jif more DPCs to run
|
|
|
|
kdd30: cmpwi r.6, 0 // o.k to enable interrupts?
|
|
beq- kdd35 // return if not
|
|
ENABLE_INTERRUPTS(r.8) // reenable interrupts and exit
|
|
|
|
kdd35:
|
|
NESTED_EXIT(KiDispatchDpc, kdsiFrameLength, 0, 0)
|
|
|
|
|
|
|
|
// VOID
|
|
// KiDispatchApc (VOID)
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered as a result of lowering IRQL below
|
|
// APC_LEVEL with an APC interrupt pending. IRQL is currently
|
|
// at APC_LEVEL.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
NESTED_ENTRY_S(KiDispatchApc, kdsiFrameLength, 0, 0, _TEXT$00)
|
|
PROLOGUE_END(KiDispatchApc)
|
|
|
|
kda10:
|
|
lwz r.6, KiPcr+PcPrcb(r.0) // get address of PRCB
|
|
li r.3, 0 // PreviousMode = Kernel
|
|
lwz r.7, PbApcBypassCount(r.6) // get APC bypass count
|
|
li r.4, 0 // TrapFrame = 0
|
|
li r.5, 0 // ExceptionFrame = 0
|
|
addi r.7, r.7, 1 // increment APC bypass count
|
|
stb r.3, KiPcr+PcApcInterrupt(r.0) // clear APC pending
|
|
stw r.7, PbApcBypassCount(r.6) // store new APC bypass count
|
|
bl ..KiDeliverApc
|
|
|
|
li r.3, 0 // get new IRQL
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
DISABLE_INTERRUPTS(r.8, r.4)
|
|
|
|
#else
|
|
|
|
stb r.3, KiPcr+PcCurrentIrql(r.0) // lower IRQL
|
|
sync
|
|
|
|
#endif
|
|
|
|
lwz r.5, KiPcr+PcPrcb(r.0) // get address of PRCB
|
|
lbz r.4, KiPcr+PcApcInterrupt(r.0) // more APCs pending?
|
|
lwz r.6, PbInterruptCount(r.5) // bump interrupt count
|
|
cmpwi r.4, 0
|
|
addi r.6, r.6, 1
|
|
stw r.6, PbInterruptCount(r.5)
|
|
stb r.4, KiPcr+PcCurrentIrql(r.0) // Raise IRQL if more APCs
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
ENABLE_INTERRUPTS(r.8)
|
|
|
|
#endif
|
|
|
|
bne- kda10 // jif more APCs to run
|
|
|
|
NESTED_EXIT(KiDispatchApc, kdsiFrameLength, 0, 0)
|
|
|
|
SBTTL("Swap Thread")
|
|
|
|
|
|
// VOID
|
|
// KiSwapThread (
|
|
// VOID
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is called to select the next thread to run on the
|
|
// current processor and to perform a context switch to the thread.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Outputs: ( for call to SwapContext )
|
|
|
|
// r.31 NTH pointer to new thread
|
|
// r.30 OTH pointer to old thread
|
|
// r.29 rPrcb pointer to processor control block
|
|
// r.28 pointer to dispatcher database lock
|
|
// r.27 pointer to context swap lock
|
|
|
|
// Return Value:
|
|
|
|
// Wait completion status (r.3).
|
|
|
|
|
|
|
|
.struct 0
|
|
.space swFrameLength
|
|
kscLR: .space 4
|
|
.align 3 // ensure 8 byte alignment
|
|
kscFrameLength:
|
|
|
|
.align 6 // cache line align
|
|
|
|
SPECIAL_ENTRY_S(KiSwapThread,_TEXT$00)
|
|
|
|
mflr r.0 // get return address
|
|
stwu r.sp, -kscFrameLength(r.sp) // buy stack frame
|
|
stw r.29, swFrame + ExGpr29(r.sp) // save gpr 29
|
|
stw r.30, swFrame + ExGpr30(r.sp) // save gpr 30
|
|
stw r.31, swFrame + ExGpr31(r.sp) // save gpr 31
|
|
lwz OTH, KiPcr+PcCurrentThread(r.0) // get current thread addr
|
|
lwz rPrcb, KiPcr+PcPrcb(r.0) // get Processor Control Block
|
|
stw r.27, swFrame + ExGpr27(r.sp) // save gpr 27
|
|
stw r.28, swFrame + ExGpr28(r.sp) // save gpr 28
|
|
stw r.14, swFrame + ExGpr14(r.sp) // save gpr 14
|
|
lwz r.27, [toc]KiReadySummary(r.toc) // get &KiReadySummary
|
|
lwz NTH, PbNextThread(rPrcb) // get address of next thread
|
|
stw r.26, swFrame + ExGpr26(r.sp) // save gpr 26
|
|
li r.28, 0 // load a 0
|
|
lwz r.26, 0(r.27) // get ready summary
|
|
cmpwi NTH, 0 // next thread selected?
|
|
stw r.0, kscLR(r.sp) // save return address
|
|
|
|
PROLOGUE_END(KiSwapThread)
|
|
|
|
stw r.28, PbNextThread(rPrcb) // zero address of next thread
|
|
bne ksc120 // if ne, next thread selected
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.5, [toc]KeTickCount(r.toc) // get &KeTickCount
|
|
lwz r.3, KiPcr+PcSetMember(r.0) // get processor affinity mask
|
|
lbz r.4, PbNumber(rPrcb) // get current processor number
|
|
lwz r.5, 0(r.5) // get low part of tick count
|
|
|
|
#endif
|
|
|
|
|
|
// Find the highest priority ready thread.
|
|
|
|
|
|
cntlzw r.6, r.26 // count zero bits from left
|
|
lwz r.8, [toc]KiDispatcherReadyListHead(r.toc) // get ready listhead base address
|
|
slw. r.7, r.26, r.6 // shift first set bit into sign bit
|
|
subfic r.6, r.6, 31 // convert shift count to priority
|
|
|
|
beq kscIdle // if mask is zero, no ready threads
|
|
|
|
kscReadyScan:
|
|
|
|
|
|
// If the thread can execute on the current processor, then remove it from
|
|
// the dispatcher ready queue.
|
|
|
|
|
|
slwi r.9, r.6, 3 // compute ready listhead offset
|
|
slwi r.7, r.7, 1 // position next ready summary bit
|
|
add r.9, r.9, r.8 // compute ready queue address
|
|
lwz r.10, LsFlink(r.9) // get address of first entry
|
|
subi NTH, r.10, ThWaitListEntry // compute address of thread object
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
kscAffinityScan:
|
|
|
|
lwz r.11, ThAffinity(NTH) // get thread affinity
|
|
lbz r.0, ThNextProcessor(NTH) // get last processor number
|
|
and. r.11, r.11, r.3 // check for compatible thread affinity
|
|
cmpw cr.6, r.0, r.4 // compare last processor with current
|
|
bne kscAffinityOk // if ne, thread affinity compatible
|
|
|
|
lwz r.10, LsFlink(r.10) // get address of next entry
|
|
cmpw r.10, r.9 // compare with queue address
|
|
subi NTH, r.10, ThWaitListEntry // compute address of thread object
|
|
bne kscAffinityScan // if ne, not end of list
|
|
ksc70:
|
|
cmpwi r.7, 0 // more ready queues to scan?
|
|
subi r.6, r.6, 1 // decrement ready queue priority
|
|
blt kscReadyScan // if lt, queue contains an entry
|
|
beq kscIdle // if eq, no ready threads
|
|
|
|
slwi r.7, r.7, 1 // position next ready summary bit
|
|
b ksc70 // check next bit
|
|
|
|
kscAffinityOk:
|
|
|
|
|
|
// If the thread last ran on the current processor, has been waiting for
|
|
// longer than a quantum, or its priority is greater than low realtime
|
|
// plus 9, then select the thread. Otherwise, an attempt is made to find
|
|
// a more appropriate candidate.
|
|
|
|
|
|
beq cr.6, kscReadyFound // if eq, processor number match
|
|
lbz r.0, ThIdealProcessor(NTH) // get ideal processor number
|
|
cmpwi r.6, LOW_REALTIME_PRIORITY + 9 // check if priority in range
|
|
cmpw cr.6, r.0, r.4 // compare ideal processor with current
|
|
beq cr.6, ksc100 // if eq, processor number match
|
|
bge ksc100 // if ge, priority high enough
|
|
lwz r.12, ThWaitTime(NTH) // get time of thread ready
|
|
sub r.12, r.5, r.12 // compute length of wait
|
|
cmpwi cr.7, r.12, READY_SKIP_QUANTUM + 1 // check if wait time exceeded
|
|
bge cr.7, ksc100 // if ge, waited long enough
|
|
|
|
|
|
// Search forward in the ready queue until the end of the list is reached
|
|
// or a more appropriate thread is found.
|
|
|
|
|
|
lwz r.14, LsFlink(r.10) // get address of next entry
|
|
ksc80:
|
|
cmpw r.14, r.9 // compare with queue address
|
|
subi r.28, r.14, ThWaitListEntry // compute address of thread object
|
|
beq ksc100 // if eq, end of list
|
|
|
|
lwz r.11, ThAffinity(r.28) // get thread affinity
|
|
lbz r.0, ThNextProcessor(r.28) // get last processor number
|
|
and. r.11, r.11, r.3 // check for compatible thread affinity
|
|
cmpw cr.6, r.0, r.4 // compare last processor with current
|
|
beq ksc85 // if eq, thread affinity not compatible
|
|
beq cr.6, ksc90 // if eq, processor number match
|
|
lbz r.0, ThIdealProcessor(NTH) // get ideal processor number
|
|
cmpw cr.6, r.0, r.4 // compare ideal processor with current
|
|
beq cr.6, ksc90 // if eq, processor number match
|
|
ksc85:
|
|
lwz r.12, ThWaitTime(r.28) // get time of thread ready
|
|
lwz r.14, LsFlink(r.14) // get address of next entry
|
|
sub r.12, r.5, r.12 // compute length of wait
|
|
cmpwi cr.7, r.12, READY_SKIP_QUANTUM + 1 // check if wait time exceeded
|
|
blt cr.7, ksc80 // if lt, wait time not exceeded
|
|
b ksc100 // wait time exceeded -- switch to
|
|
// first matching thread in ready queue
|
|
ksc90:
|
|
ori NTH, r.28, 0 // set thread address
|
|
ori r.10, r.14, 0 // set list entry address
|
|
ksc100:
|
|
stb r.4, ThNextProcessor(NTH) // set next processor number
|
|
|
|
kscReadyFound:
|
|
|
|
#endif
|
|
|
|
|
|
// Remove the selected thread from the ready queue.
|
|
|
|
|
|
lwz r.11, LsFlink(r.10) // get list entry forward link
|
|
lwz r.12, LsBlink(r.10) // get list entry backward link
|
|
li r.0, 1 // set bit for mask generation
|
|
slw r.0, r.0, r.6 // compute ready summary set member
|
|
cmpw r.11, r.12 // check for list empty
|
|
stw r.11, LsFlink(r.12) // set forward link in previous entry
|
|
stw r.12, LsBlink(r.11) // set backward link in next entry
|
|
bne ksc120 // if ne, list is not empty
|
|
xor r.26, r.26, r.0 // clear ready summary bit
|
|
stw r.26, 0(r.27) // update ready summary
|
|
ksc120:
|
|
|
|
|
|
// Swap context to the next thread.
|
|
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.27,[toc]KiContextSwapLock(r.2)// get &KiContextSwapLock
|
|
lwz r.28,[toc]KiDispatcherLock(r.2) // get &KiDispatcherLock
|
|
|
|
#endif
|
|
|
|
stw NTH, PbCurrentThread(rPrcb) // set new thread current
|
|
ksc130:
|
|
bl ..SwapContext // swap context
|
|
|
|
|
|
|
|
// Lower IRQL and return wait completion status.
|
|
|
|
// N.B. SwapContext releases the dispatcher database lock.
|
|
|
|
lbz r.4, ThWaitIrql(NTH) // get original wait IRQL
|
|
|
|
DISABLE_INTERRUPTS(r.6, r.7)
|
|
|
|
lhz r.5, KiPcr+PcSoftwareInterrupt(r.0) // check for pending s/w ints
|
|
lwz r.14, kscLR(r.sp) // get return address
|
|
lwz r.31, ThWaitStatus(NTH) // get wait completion status
|
|
lwz r.26, swFrame + ExGpr26(r.sp) // restore gprs 26 thru 30
|
|
lwz r.27, swFrame + ExGpr27(r.sp) //
|
|
lwz r.28, swFrame + ExGpr28(r.sp) //
|
|
lwz r.29, swFrame + ExGpr29(r.sp) //
|
|
lwz r.30, swFrame + ExGpr30(r.sp) //
|
|
stb r.4, KiPcr+PcCurrentIrql(r.0) // set new IRQL
|
|
|
|
ENABLE_INTERRUPTS(r.6)
|
|
|
|
cmpw r.5, r.4 // see if s/w int could now run
|
|
bgtl- ..KiDispatchSoftwareInterrupt // jif pending int can run
|
|
mtlr r.14 // set return address
|
|
ori r.3, r.31, 0 // set return status
|
|
lwz r.31, swFrame + ExGpr31(r.sp) // restore r.31
|
|
lwz r.14, swFrame + ExGpr14(r.sp) // restore r.14
|
|
addi r.sp, r.sp, kscFrameLength // return stack frame
|
|
|
|
SPECIAL_EXIT(KiSwapThread)
|
|
|
|
kscIdle:
|
|
|
|
|
|
// All ready queues were scanned without finding a runnable thread so
|
|
// default to the idle thread and set the appropriate bit in idle summary.
|
|
|
|
|
|
lwz r.5, [toc]KiIdleSummary(r.toc) // get &KiIdleSummary
|
|
|
|
#if defined(NT_UP)
|
|
|
|
li r.4, 1 // set current idle summary
|
|
#else
|
|
|
|
lwz r.4, 0(r.5) // get current idle summary
|
|
or r.4, r.4, r.3 // set member bit in idle summary
|
|
|
|
#endif
|
|
|
|
stw r.4, 0(r.5) // set new idle summary
|
|
|
|
lwz NTH,PbIdleThread(rPrcb) // set address of idle thread
|
|
b ksc120 // switch to idle thread
|
|
|
|
|
|
SBTTL("Swap Context to Next Thread")
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is called to swap context from one thread to the next.
|
|
|
|
// Arguments:
|
|
|
|
// r.sp Pointer to { StackFrameHeader, ExceptionFrame }
|
|
// r.31 NTH Address of next thread object
|
|
// r.30 OTH Address of previous thread object
|
|
// r.29 rPrcb Address of processor control block
|
|
// r.28 Address of KiDispatcherLock
|
|
// r.27 Address of KiContextSwapLock
|
|
|
|
// Return value:
|
|
|
|
// r.31 NTH Address of current thread object.
|
|
// r.29 rPrcb Address of processor control block
|
|
|
|
// Note that the TOC register is neither saved nor restored across a
|
|
// thread switch. This is because we are in NTOSKRNL (actually in the
|
|
// routine SwapContext) in both threads (ie the TOC does not change).
|
|
|
|
// GPR 13 is set to the address of the TEB where it will remain.
|
|
|
|
|
|
|
|
SPECIAL_ENTRY_S(SwapContext,_TEXT$00)
|
|
|
|
mfcr r.3 // get condition register
|
|
|
|
|
|
// Acquire the context swap lock so the address space of the old process
|
|
// cannot be deleted and then release the dispatcher database lock.
|
|
|
|
// N.B. This lock is used to protect the address space until the context
|
|
// switch has sufficiently progressed to the point where the address
|
|
// space is no longer needed. This lock is also acquired by the reaper
|
|
// thread before it finishes thread termination.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
b LkCtxSw // skip spin code
|
|
|
|
SPIN_ON_SPIN_LOCK(r.27,r.4,LkCtxSw,LkCtxSwSpin) // spin on context swap lock
|
|
|
|
ACQUIRE_SPIN_LOCK(r.27,r.31,r.4,LkCtxSw,LkCtxSwSpin) // acquire context swap lock
|
|
#endif
|
|
|
|
|
|
// Set the new thread's state to Running before releasing the dispatcher lock.
|
|
|
|
|
|
li r.8, Running // set state of new thread
|
|
stb r.8, ThState(NTH) // to running.
|
|
|
|
#if !defined(NT_UP)
|
|
RELEASE_SPIN_LOCK(r.28,r.4) // release dispatcher lock
|
|
#endif
|
|
|
|
|
|
// Save old thread non-volatile context.
|
|
|
|
|
|
mflr r.0 // get return address
|
|
|
|
stw r.15, swFrame + ExGpr15(r.sp) // save gprs 15 thru 25
|
|
stw r.16, swFrame + ExGpr16(r.sp) //
|
|
stw r.17, swFrame + ExGpr17(r.sp) //
|
|
stw r.18, swFrame + ExGpr18(r.sp) //
|
|
stw r.19, swFrame + ExGpr19(r.sp) //
|
|
stw r.20, swFrame + ExGpr20(r.sp) //
|
|
stw r.21, swFrame + ExGpr21(r.sp) //
|
|
stw r.22, swFrame + ExGpr22(r.sp) //
|
|
stw r.23, swFrame + ExGpr23(r.sp) //
|
|
stw r.24, swFrame + ExGpr24(r.sp) //
|
|
stw r.25, swFrame + ExGpr25(r.sp) //
|
|
|
|
stfd f.14, swFrame + ExFpr14(r.sp) // save non-volatile
|
|
stfd f.15, swFrame + ExFpr15(r.sp) // floating point regs
|
|
stfd f.16, swFrame + ExFpr16(r.sp) //
|
|
stfd f.17, swFrame + ExFpr17(r.sp) //
|
|
stfd f.18, swFrame + ExFpr18(r.sp) //
|
|
stfd f.19, swFrame + ExFpr19(r.sp) //
|
|
stfd f.20, swFrame + ExFpr20(r.sp) //
|
|
stfd f.21, swFrame + ExFpr21(r.sp) //
|
|
stfd f.22, swFrame + ExFpr22(r.sp) //
|
|
stfd f.23, swFrame + ExFpr23(r.sp) //
|
|
stfd f.24, swFrame + ExFpr24(r.sp) //
|
|
stfd f.25, swFrame + ExFpr25(r.sp) //
|
|
stfd f.26, swFrame + ExFpr26(r.sp) //
|
|
stfd f.27, swFrame + ExFpr27(r.sp) //
|
|
stfd f.28, swFrame + ExFpr28(r.sp) //
|
|
stfd f.29, swFrame + ExFpr29(r.sp) //
|
|
stfd f.30, swFrame + ExFpr30(r.sp) //
|
|
stfd f.31, swFrame + ExFpr31(r.sp) //
|
|
|
|
stw r.3, swFrame + SwConditionRegister(r.sp)// save CR
|
|
stw r.0, swFrame + SwSwapReturn(r.sp) // save return address
|
|
|
|
PROLOGUE_END(SwapContext)
|
|
|
|
|
|
// The following entry point is used to switch from the idle thread to
|
|
// another thread.
|
|
|
|
|
|
..SwapFromIdle:
|
|
|
|
#if DBG
|
|
cmpw NTH, OTH
|
|
bne th_ok
|
|
twi 31, 0, 0x16
|
|
th_ok:
|
|
#endif
|
|
|
|
stw NTH, KiPcr+PcCurrentThread(r.0) // set new thread current
|
|
|
|
|
|
// Get the old and new process object addresses.
|
|
|
|
|
|
#define NPROC r.3
|
|
#define OPROC r.4
|
|
|
|
lwz NPROC, ThApcState + AsProcess(NTH) // get new process object
|
|
lwz OPROC, ThApcState + AsProcess(OTH) // get old process object
|
|
|
|
DISABLE_INTERRUPTS(r.15, r.6)
|
|
|
|
lwz r.13, ThTeb(NTH) // get addr of user TEB
|
|
lwz r.24, ThStackLimit(NTH) // get stack limit
|
|
lwz r.23, ThInitialStack(NTH) // get initial kernel stk ptr
|
|
lwz r.22, ThKernelStack(NTH) // get new thread stk ptr
|
|
cmpw cr.0, NPROC, OPROC // same process ?
|
|
|
|
stw r.sp, ThKernelStack(OTH) // save current kernel stk ptr
|
|
stw r.13, KiPcr+PcTeb(r.0) // set addr of user TEB
|
|
|
|
// Although interrupts are disabled, someone may be attempting to
|
|
// single step thru the following. I can't see anyway to perform
|
|
// the two operations atomically so I am inserting a label that is
|
|
// known externally and can be checked against the exception address
|
|
// if we fail stack validation in common_exception_entry (in real0.s)
|
|
// in which case it's really ok. This has no performance impact.
|
|
|
|
// *** WARNING ****** WARNING ****** WARNING ****** WARNING ***
|
|
|
|
// (1) these two instructions MUST stay together,
|
|
// (2) the stack validation code in common_exception_entry
|
|
// KNOWS that the second instruction is a 'ori r.sp, r.22, 0'
|
|
// and will perform such an instruction in line to correct
|
|
// the problem. If you change this sequence you will need
|
|
// to make an equivalent change in real0.s and the correct-
|
|
// ability is dependent on the second instruction destroying
|
|
// the stack pointer.
|
|
// (plj).
|
|
|
|
|
|
stw r.24, KiPcr+PcStackLimit(r.0) // set stack limit
|
|
stw r.23, KiPcr+PcInitialStack(r.0) // set initial kernel stack ptr
|
|
.globl KepSwappingContext
|
|
KepSwappingContext:
|
|
ori r.sp, r.22, 0 // switch stacks
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
|
|
// Old process address space is no longer required. Ensure all
|
|
// stores are done prior to releasing the ContextSwap lock.
|
|
// N.B. SwapContextLock is still needed to ensure KiMasterPid
|
|
// integrity.
|
|
|
|
|
|
li r.16, 0
|
|
|
|
eieio
|
|
bne cr.0, ksc10
|
|
stw r.16, 0(r.27) // release Context Swap lock
|
|
b ksc20
|
|
|
|
ksc10:
|
|
|
|
#else
|
|
|
|
beq cr.0, ksc20
|
|
|
|
#endif
|
|
|
|
|
|
// If the process sequence number matches the system sequence number, then
|
|
// use the process PID. Otherwise, allocate a new process PID.
|
|
|
|
// N.B. The following code is duplicated from KiSwapProcess and will
|
|
// join KiSwapProcess at SwapProcessSlow if sequence numbers
|
|
// don't match. Register usage from here to the branch should
|
|
// match KiSwapProcess.
|
|
|
|
lwz r.10,[toc]KiMasterSequence(r.toc) // get &KiMasterSequence
|
|
lwz r.9,PrProcessSequence(NPROC) // get process sequence number
|
|
lwz r.11,0(r.10) // get master sequence number
|
|
lwz r.7,PrProcessPid(NPROC) // get process PID
|
|
cmpw r.11,r.9 // master sequence == process sequence?
|
|
bne ksc15 // jif not equal, go the slow path
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
stw r.16, 0(r.27) // release Context Swap lock
|
|
|
|
#endif
|
|
|
|
|
|
// Swap address space to the specified process.
|
|
|
|
|
|
lwz r.5,PrDirectoryTableBase(r.3) // get page dir page real addr
|
|
|
|
mtsr 0,r.7 // set sreg 0
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 1,r.7 // set sreg 1
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 2,r.7 // set sreg 2
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 3,r.7 // set sreg 3
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 4,r.7 // set sreg 4
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 5,r.7 // set sreg 5
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 6,r.7 // set sreg 6
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 7,r.7 // set sreg 7
|
|
addi r.7,r.7,12-7 // add 5 to VSID
|
|
mtsr 12,r.7 // set sreg 12
|
|
isync // context synchronize
|
|
stw r.5,KiPcr+PcPgDirRa(r.0) // store page dir page ra in PCR
|
|
|
|
#if COLLECT_PAGING_DATA
|
|
lwz r.10,[toc]KiFlushOnProcessSwap(r.toc)
|
|
lwz r.10,0(r.10)
|
|
cmpwi r.10,0
|
|
bnel ..KeFlushCurrentTb
|
|
#endif
|
|
|
|
b ksc20
|
|
|
|
ksc15:
|
|
bl SwapProcessSlow
|
|
|
|
ksc20:
|
|
lbz r.5, ThApcState + AsKernelApcPending(NTH)
|
|
lbz r.16, ThDebugActive(NTH) // get the active debug register
|
|
// mask
|
|
stb r.5, KiPcr+PcApcInterrupt(r.0) // set APC pending appropriately
|
|
stb r.16, KiPcr+PcDebugActive(r.0) // set the active debug register
|
|
// mask for the new thread
|
|
lwz r.5, PbContextSwitches(rPrcb) // get context switch count
|
|
lwz r.7, ThContextSwitches(NTH)
|
|
addi r.5, r.5, 1 // bump context switch count
|
|
stw r.5, PbContextSwitches(rPrcb) // for processor.
|
|
addi r.7, r.7, 1 // bump context switch count
|
|
stw r.7, ThContextSwitches(NTH) // for this thread
|
|
|
|
ENABLE_INTERRUPTS(r.15)
|
|
|
|
lwz r.0, swFrame + SwSwapReturn(r.sp) // get return address
|
|
lwz r.5, swFrame + SwConditionRegister(r.sp)// get CR
|
|
|
|
lwz r.15, swFrame + ExGpr15(r.sp) // restore gprs 15 thru 25
|
|
lwz r.16, swFrame + ExGpr16(r.sp) //
|
|
lwz r.17, swFrame + ExGpr17(r.sp) //
|
|
lwz r.18, swFrame + ExGpr18(r.sp) //
|
|
lwz r.19, swFrame + ExGpr19(r.sp) //
|
|
lwz r.20, swFrame + ExGpr20(r.sp) //
|
|
lwz r.21, swFrame + ExGpr21(r.sp) //
|
|
lwz r.22, swFrame + ExGpr22(r.sp) //
|
|
lwz r.23, swFrame + ExGpr23(r.sp) //
|
|
lwz r.24, swFrame + ExGpr24(r.sp) //
|
|
lwz r.25, swFrame + ExGpr25(r.sp) //
|
|
|
|
lfd f.14, swFrame + ExFpr14(r.sp) // restore non-volatile
|
|
lfd f.15, swFrame + ExFpr15(r.sp) // floating point regs
|
|
lfd f.16, swFrame + ExFpr16(r.sp) //
|
|
lfd f.17, swFrame + ExFpr17(r.sp) //
|
|
lfd f.18, swFrame + ExFpr18(r.sp) //
|
|
lfd f.19, swFrame + ExFpr19(r.sp) //
|
|
lfd f.20, swFrame + ExFpr20(r.sp) //
|
|
lfd f.21, swFrame + ExFpr21(r.sp) //
|
|
lfd f.22, swFrame + ExFpr22(r.sp) //
|
|
lfd f.23, swFrame + ExFpr23(r.sp) //
|
|
lfd f.24, swFrame + ExFpr24(r.sp) //
|
|
lfd f.25, swFrame + ExFpr25(r.sp) //
|
|
lfd f.26, swFrame + ExFpr26(r.sp) //
|
|
lfd f.27, swFrame + ExFpr27(r.sp) //
|
|
mtlr r.0 // set return address
|
|
mtcrf 0xff, r.5 // set condition register
|
|
lfd f.28, swFrame + ExFpr28(r.sp) //
|
|
lfd f.29, swFrame + ExFpr29(r.sp) //
|
|
lfd f.30, swFrame + ExFpr30(r.sp) //
|
|
lfd f.31, swFrame + ExFpr31(r.sp) //
|
|
|
|
SPECIAL_EXIT(SwapContext)
|
|
|
|
#undef NTH
|
|
#undef OTH
|
|
|
|
|
|
|
|
// VOID
|
|
// KiSwapProcess (
|
|
// IN PKPROCESS NewProcess,
|
|
// IN PKPROCESS OldProcess
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This function swaps the address space from one process to another by
|
|
// moving to the PCR the real address of the process page directory page
|
|
// and loading segment registers 0-7 and 12 with VSIDs derived therefrom.
|
|
|
|
// The fast path below is duplicated inline in SwapContext for speed.
|
|
// SwapContext joins this code at SwapProcessSlow if sequence numbers
|
|
// differ.
|
|
|
|
// Arguments:
|
|
|
|
// NewProcess (r3) - Supplies a pointer to a control object of type process
|
|
// which represents the new process that is switched to.
|
|
|
|
// OldProcess (r4) - Supplies a pointer to a control object of type process
|
|
// which represents the old process that is switched from.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
LEAF_ENTRY_S(KiSwapProcess,_TEXT$00)
|
|
|
|
|
|
// Get the Context Swap lock. This lock is used to protect a
|
|
// processes memory space, it serves double duty to protect access
|
|
// to KiMasterSequence.
|
|
|
|
// N.B. It is already held if entry is via SwapProcessSlow, the
|
|
// lock is ALWAYS released by this routine.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.6, [toc]KiContextSwapLock(r.2)
|
|
|
|
ACQUIRE_SPIN_LOCK(r.6,r.3,r.5,LkCtxSw2,LkCtxSw2Spin)
|
|
|
|
#endif
|
|
|
|
|
|
// If the process sequence number matches the system sequence number, then
|
|
// use the process PID. Otherwise, allocate a new process PID.
|
|
|
|
// WARNING: if you change register usage in the following be sure to make
|
|
// the same changes in SwapContext.
|
|
|
|
|
|
lwz r.10,[toc]KiMasterSequence(r.toc) // get &KiMasterSequence
|
|
lwz r.9,PrProcessSequence(r.3) // get process sequence number
|
|
lwz r.11,0(r.10) // get master sequence number
|
|
lwz r.7,PrProcessPid(r.3) // get process PID
|
|
cmpw r.11,r.9 // master sequence == process sequence?
|
|
bne SwapProcessSlow // jif not equal, out of line
|
|
|
|
|
|
|
|
// Swap address space to the specified process.
|
|
|
|
|
|
spup: lwz r.5,PrDirectoryTableBase(r.3) // get page dir page real addr
|
|
|
|
DISABLE_INTERRUPTS(r.8,r.0) // disable interrupts
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
sync
|
|
li r.10, 0
|
|
stw r.10, 0(r.6) // release KiContextSwapLock
|
|
|
|
#endif
|
|
|
|
stw r.5,KiPcr+PcPgDirRa(r.0) // store page dir page ra in PCR
|
|
mtsr 0,r.7 // set sreg 0
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 1,r.7 // set sreg 1
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 2,r.7 // set sreg 2
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 3,r.7 // set sreg 3
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 4,r.7 // set sreg 4
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 5,r.7 // set sreg 5
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 6,r.7 // set sreg 6
|
|
addi r.7,r.7,1 // add 1 to VSID
|
|
mtsr 7,r.7 // set sreg 7
|
|
addi r.7,r.7,12-7 // add 5 to VSID
|
|
mtsr 12,r.7 // set sreg 12
|
|
isync // context synchronize
|
|
|
|
ENABLE_INTERRUPTS(r.8) // enable interrupts
|
|
|
|
#if COLLECT_PAGING_DATA
|
|
lwz r.10,[toc]KiFlushOnProcessSwap(r.toc)
|
|
lwz r.10,0(r.10)
|
|
cmpwi r.10,0
|
|
bne ..KeFlushCurrentTb
|
|
#endif
|
|
|
|
ALTERNATE_EXIT(KiSwapProcess) // return
|
|
|
|
|
|
// We need a new PID, the dispatcher database lock is still held so
|
|
// we can update KiMasterPid without further protection.
|
|
|
|
|
|
SwapProcessSlow:
|
|
lwz r.8,[toc]KiMasterPid(r.toc) // get &KiMasterPid
|
|
lwz r.7,0(r.8) // get KiMasterPid
|
|
addi r.7,r.7,16 // bump master pid
|
|
rlwinm. r.7,r.7,0,0x007ffff0 // detect PID wrap
|
|
beq ..KxSwapProcess // jif PID wrap
|
|
|
|
stw r.11,PrProcessSequence(r.3) // save new process sequence
|
|
|
|
// control returns here from KxSwapProcess
|
|
|
|
|
|
spnp:
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.6, [toc]KiContextSwapLock(r.2)
|
|
|
|
#endif
|
|
|
|
stw r.7,0(r.8) // save new master PID
|
|
stw r.7,PrProcessPid(r.3) // save new process PID
|
|
b spup // continue with main line code
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
SPIN_ON_SPIN_LOCK(r.6,r.5,LkCtxSw2,LkCtxSw2Spin)
|
|
|
|
#endif
|
|
|
|
DUMMY_EXIT(KiSwapProcess)
|
|
|
|
|
|
|
|
// VOID
|
|
// KxSwapProcess (
|
|
// IN PKPROCESS NewProcess,
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This function is called (only) from KiSwapProcess when PID wrap has
|
|
// occured. KiSwapProcess is a LEAF function. The purpose of this
|
|
// function is to alloacte a stack frame and save data that needs to
|
|
// be restored for KiSwapProcess. This routine is called aproximately
|
|
// once every 16 million new processes. The emphasis in KiSwapProcess
|
|
// is to handle the other 16 million - 1 cases as fast as possible.
|
|
|
|
// Arguments:
|
|
|
|
// NewProcess (r3) - Supplies a pointer to a control object of
|
|
// type process which represents the new process being switched to.
|
|
// This must be saved and restored for KiSwapProcess.
|
|
|
|
// &KiMasterPid (r8) - Address of system global KiMasterPid
|
|
// This must be restored for KiSwapProcess.
|
|
|
|
// &KiMasterSequence (r10) - Address of system global KiMasterSequence.
|
|
|
|
// KiMasterSequence (r11) - Current Value of the above variable.
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
// Registers r3, r8 and the Link Register are restored. r7 contains
|
|
// the new PID which will be 16.
|
|
|
|
|
|
|
|
.struct 0
|
|
.space StackFrameHeaderLength
|
|
spLR: .space 4 // link register save
|
|
spR3: .space 4 // new process address save
|
|
.align 3 // ensure correct alignment
|
|
spFrameLength:
|
|
|
|
SPECIAL_ENTRY_S(KxSwapProcess,_TEXT$00)
|
|
|
|
mflr r.0 // get link register
|
|
stwu r.sp,-spFrameLength(r.sp) // buy stack frame
|
|
stw r.3,spR3(r.sp) // save new process address
|
|
stw r.0,spLR(r.sp) // save swap process' return address
|
|
|
|
PROLOGUE_END(KxSwapProcess)
|
|
|
|
|
|
// PID wrap has occured. On PowerPC we do not need to lock the process
|
|
// id wrap lock because tlb synchronization is handled by hardware.
|
|
|
|
|
|
addic. r.11,r.11,1 // bump master sequence number
|
|
bne+ spnsw // jif sequence number did not wrap
|
|
|
|
|
|
// The master sequence number has wrapped, this is 4 billion * 16 million
|
|
// processes,... not too shabby. We start the sequence again at 2 in case
|
|
// there are system processes that have been running since the system first
|
|
// started.
|
|
|
|
|
|
li r.11,2 // start again at 2
|
|
|
|
spnsw: stw r.11,0(r.10) // save new master sequence number
|
|
stw r.11,PrProcessSequence(r.3) // save new process sequence num
|
|
|
|
bl ..KeFlushCurrentTb // flush entire HPT (and all processor
|
|
// TLBs)
|
|
|
|
lwz r.0,spLR(r.sp) // get swap process' return address
|
|
lwz r.3,spR3(r.sp) // get new process address
|
|
lwz r.8,[toc]KiMasterPid(r.toc) // get &KiMasterPid
|
|
addi r.sp,r.sp,spFrameLength // return stack frame
|
|
li r.7,16 // set new PID
|
|
mtlr r.0
|
|
b spnp // continue in KiSwapProcess
|
|
|
|
DUMMY_EXIT(KxSwapProcess)
|
|
|
|
|
|
|
|
// VOID
|
|
// KiIdleLoop (
|
|
// VOID
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This is the idle loop for NT. This code runs in a thread for
|
|
// each processor in the system. The idle thread runs at IRQL
|
|
// DISPATCH_LEVEL and polls for work.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Return Value:
|
|
|
|
// None. (This routine never returns).
|
|
|
|
// Non-volatile register usage is as follows.
|
|
|
|
// r.14 --unused - available --
|
|
// r.15 Address of KdDebuggerEnabled
|
|
// r.16 Kernel TOC (backup)
|
|
// r.17 Idle loop MSR with Interrupts DISABLED
|
|
// r.18 Idle loop MSR with Interrupts ENABLED
|
|
// r.19 HalProcessorIdle entry point
|
|
// r.20 HAL's TOC
|
|
// r.21 Debugger poll count
|
|
// r.22 Address of KeTickCount
|
|
// r.23 Zero
|
|
// r.24 Address of dispatcher database lock (MP) (backup for r28)
|
|
// r.25 DpcListHead
|
|
// r.26 --unused - available --
|
|
// r.27 Address of Context Swap lock
|
|
// r.28 Address of dispatcher database lock (MP)
|
|
// r.29 Address of Processor Control Block
|
|
|
|
// When another thread is selected to run, SwapContext is called.
|
|
// Normally, callers of SwapContext are responsible for saving and
|
|
// restoring non-volatile regs r.14 and r.26 thru r.31. SwapContext
|
|
// saves/restores gprs r.15 thru r.25. The idle loop never returns so
|
|
// previous contents of r.14 and r.26 thru r.31 are not saved. The
|
|
// idle loop pre-initializes the storage area where SwapContext would
|
|
// normally save r.15 thru r.25 with values that the idle loop needs
|
|
// in those registers upon return from SwapContext and skips over the
|
|
// register save on the way into SwapContext (alternate entry point
|
|
// SwapFromIdle). All callers to SwapContext pass the following
|
|
// arguments-
|
|
|
|
// r.27 Address of Context Swap lock (&KiContextSwapLock)
|
|
// r.28 Address of dispatcher database lock (&KiDispatcherLock)
|
|
// r.29 Address of PRCB
|
|
// r.30 Address of OLD thread object
|
|
// r.31 Address of NEW thread object
|
|
|
|
// The idle loop does not have a fixed use for regs r.30 and r.31.
|
|
// r.29 contains the correct value for this processor. r.14 and
|
|
// r.26 contents are unknown and must be regenerated upon return
|
|
// from SwapContext. The assignment of function to these registers
|
|
// was chosen for easy regeneration of content.
|
|
|
|
// Note also that r.21 was assigned in the range of registers
|
|
// restored by SwapContext so that it is reset to its initial
|
|
// values whenever SwapContext is called.
|
|
|
|
|
|
|
|
#define rDbg r.15
|
|
#define rKTOC r.16
|
|
#define rIntOff r.17
|
|
#define rIntOn r.18
|
|
#define rHalIdle r.19
|
|
#define rHalToc r.20
|
|
#define rDbgCount r.21
|
|
#define rTickP r.22
|
|
#define rZero r.23
|
|
#define rDispLkSave r.24
|
|
#define rDPCHEAD r.25
|
|
|
|
#define rDispLk r.28
|
|
#define rPrcb r.29
|
|
|
|
|
|
SPECIAL_ENTRY_S(KiIdleLoop,_TEXT$00)
|
|
|
|
mflr r.0 // get return address
|
|
stwu r.sp, -kscFrameLength(r.sp) // buy stack frame
|
|
stw r.0, kscLR(r.sp) // save return address
|
|
|
|
PROLOGUE_END(KiIdleLoop)
|
|
|
|
|
|
|
|
// Setup initial global register values
|
|
|
|
|
|
ori rKTOC, r.toc, 0 // backup kernel's TOC
|
|
lwz rPrcb, KiPcr+PcPrcb(r.0) // Address of PCB to rPrcb
|
|
lwz rTickP, [toc]KeTickCount(r.toc) // Address of KeTickCount
|
|
lwz rDbg, [toc]KdDebuggerEnabled(r.toc)// Addr KdDebuggerEnabled
|
|
lwz rHalToc,[toc]__imp_HalProcessorIdle(r.toc)
|
|
lwz rHalToc,0(rHalToc)
|
|
lwz rHalIdle,0(rHalToc) // HalProcessorIdle entry
|
|
lwz rHalToc,4(rHalToc) // HAL's TOC
|
|
li rZero, 0 // Keep zero around, we use it
|
|
mfmsr rIntOff // get current machine state
|
|
rlwinm rIntOff, rIntOff, 0, 0xffff7fff // clear interrupt enable
|
|
ori rIntOn, rIntOff, 0x00008000 // set interrupt enable
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz rDispLk, [toc]KiDispatcherLock(r.toc)// get &KiDispatcherLock
|
|
lwz r.27, [toc]KiContextSwapLock(r.toc) // get &KiContextSwapLock
|
|
|
|
#endif
|
|
|
|
addi rDPCHEAD, rPrcb, PbDpcListHead // compute DPC listhead address
|
|
li rDbgCount, 0 // Clear breakin loop counter
|
|
|
|
#if !defined(NT_UP)
|
|
ori rDispLkSave, rDispLk, 0 // copy &KiDispatcherLock
|
|
#endif
|
|
|
|
|
|
// Registers 15 thru 25 are normally saved by SwapContext but the idle
|
|
// loop uses an alternate entry that skips the save by SwapContext.
|
|
// SwapContext will still restore them so we set up the stack so what
|
|
// we want is what gets restored. This is especially useful for things
|
|
// whose values need to be reset after SwapContext is called, eg rDbgCount.
|
|
|
|
|
|
lwz r.4, [toc]__imp_KeLowerIrql(r.toc) // &&fd(KeLowerIrql)
|
|
stw r.15, swFrame + ExGpr15(r.sp)
|
|
stw r.16, swFrame + ExGpr16(r.sp)
|
|
stw r.17, swFrame + ExGpr17(r.sp)
|
|
lwz r.4, 0(r.4) // &fd(KeLowerIrql)
|
|
stw r.18, swFrame + ExGpr18(r.sp)
|
|
stw r.19, swFrame + ExGpr19(r.sp)
|
|
stw r.20, swFrame + ExGpr20(r.sp)
|
|
lwz r.5, 0(r.4) // &KeLowerIrql
|
|
stw r.21, swFrame + ExGpr21(r.sp)
|
|
stw r.22, swFrame + ExGpr22(r.sp)
|
|
stw r.23, swFrame + ExGpr23(r.sp)
|
|
stw r.24, swFrame + ExGpr24(r.sp)
|
|
stw r.25, swFrame + ExGpr25(r.sp)
|
|
|
|
|
|
// Control reaches here with IRQL at HIGH_LEVEL. Lower IRQL to
|
|
// DISPATCH_LEVEL and set wait IRQL of idle thread.
|
|
|
|
|
|
mtctr r.5
|
|
lwz r.toc, 4(r.4) // HAL's TOC
|
|
lwz r.11, KiPcr+PcCurrentThread(r.0) // Lower thread and processor
|
|
li r.3, DISPATCH_LEVEL // IRQL to DISPATCH_LEVEL.
|
|
stb r.3, ThWaitIrql(r.11)
|
|
bctrl
|
|
ori r.toc, rKTOC, 0 // restore our TOC
|
|
|
|
|
|
// In a multiprocessor system the boot processor proceeds directly into
|
|
// the idle loop. As other processors start executing, however, they do
|
|
// not directly enter the idle loop and spin until all processors have
|
|
// been started and the boot master allows them to proceed.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.4, [toc]KiBarrierWait(r.toc)
|
|
|
|
BarrierWait:
|
|
lwz r.3, 0(r.4) // get barrier wait value
|
|
cmpwi r.3, 0 // if ne spin until allowed
|
|
bne BarrierWait // to proceed.
|
|
|
|
lbz r.3, PbNumber(rPrcb) // get processor number
|
|
cmpwi cr.4, r.3, 0 // save "processor == 0 ?"
|
|
|
|
#endif
|
|
|
|
|
|
// Set condition register and swap return values in the swap frame.
|
|
|
|
|
|
mfcr r.3 // save condition register
|
|
stw r.3, swFrame + SwConditionRegister(r.sp)
|
|
|
|
bl FindIdleReturn
|
|
FindIdleReturn:
|
|
mflr r.3
|
|
addi r.3, r.3, KiIdleReturn - FindIdleReturn
|
|
stw r.3, swFrame + SwSwapReturn(r.sp)// save return address
|
|
|
|
|
|
// The following loop is executed for the life of the system.
|
|
|
|
|
|
IdleLoop:
|
|
|
|
#if DBG
|
|
|
|
#if !defined(NT_UP)
|
|
bne cr.4, CheckDpcList // if ne, not processor zero
|
|
#endif
|
|
|
|
|
|
// Check if the debugger is enabled, and whether it is time to poll
|
|
// for a debugger breakin. (This check is only performed on cpu 0).
|
|
|
|
|
|
subic. rDbgCount, rDbgCount, 1 // decrement poll counter
|
|
bge+ CheckDpcList // jif not time yet.
|
|
lbz r.3, 0(rDbg) // check if debugger enabled
|
|
li rDbgCount, 20 * 1000 // set breakin loop counter
|
|
cmpwi r.3, 0
|
|
beq+ CheckDpcList // jif debugger not enabled
|
|
bl ..KdPollBreakIn // check if breakin requested
|
|
cmpwi r.3, 0
|
|
beq+ CheckDpcList // jif no breakin request
|
|
li r.3, DBG_STATUS_CONTROL_C // send status to debugger
|
|
bl ..DbgBreakPointWithStatus
|
|
|
|
#endif
|
|
|
|
|
|
// Disable interrupts and check if there is any work in the DPC list.
|
|
|
|
|
|
CheckDpcList:
|
|
|
|
mtmsr rIntOn // give interrupts a chance
|
|
isync // to interrupt spinning
|
|
mtmsr rIntOff // disable interrupts
|
|
cror 0,0,0 // N.B. 603e/ev Errata 15
|
|
|
|
|
|
|
|
// Process the deferred procedure call list for the current processor.
|
|
|
|
|
|
lwz r.3, LsFlink(rDPCHEAD) // get address of first entry
|
|
cmpw r.3, rDPCHEAD // is list empty?
|
|
beq CheckNextThread // if eq, DPC list is empty
|
|
|
|
ori r.31, rDPCHEAD, 0
|
|
bl ..KiProcessDpcList // process the DPC list
|
|
|
|
|
|
// Clear dispatch interrupt pending.
|
|
|
|
|
|
stb rZero, KiPcr+PcDispatchInterrupt(r.0) // clear pending DPC interrupt
|
|
|
|
#if DBG
|
|
li rDbgCount, 0 // clear breakin loop counter
|
|
#endif
|
|
|
|
|
|
// Check if a thread has been selected to run on this processor
|
|
|
|
|
|
CheckNextThread:
|
|
lwz r.31, PbNextThread(rPrcb) // get address of next thread
|
|
cmpwi r.31, 0
|
|
beq Idle // jif no thread to execute
|
|
|
|
|
|
// A thread has been selected for execution on this processor. Acquire
|
|
// dispatcher database lock, get the thread address again (it may have
|
|
// changed), clear the address of the next thread in the processor block,
|
|
// and call swap context to start execution of the selected thread.
|
|
|
|
// N.B. If the dispatcher database lock cannot be obtained immediately,
|
|
// then attempt to process another DPC rather than spinning on the
|
|
// dispatcher database lock.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
TRY_TO_ACQUIRE_SPIN_LOCK(rDispLk, rDispLk, r.11, LkDisp, CheckDpcList)
|
|
|
|
#endif
|
|
|
|
mtmsr rIntOn // enable interrupts
|
|
cror 0,0,0 // N.B. 603e/ev Errata 15
|
|
|
|
#if !defined(NT_UP)
|
|
lwz r.31, PbNextThread(rPrcb) // get next thread address
|
|
#endif
|
|
|
|
lwz r.30, PbCurrentThread(rPrcb) // get current thread address
|
|
stw rZero, PbNextThread(rPrcb) // clear address of next thread
|
|
stw r.31, PbCurrentThread(rPrcb) // set new thread current
|
|
|
|
|
|
// Acquire the context swap lock so the address space of the old process
|
|
// cannot be deleted and then release the dispatcher database lock. In
|
|
// this case the old process is the system process, but the context swap
|
|
// code releases the context swap lock so it must be acquired.
|
|
|
|
// N.B. This lock is used to protect the address space until the context
|
|
// switch has sufficiently progressed to the point where the address
|
|
// space is no longer needed. This lock is also acquired by the reaper
|
|
// thread before it finishes thread termination.
|
|
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
ACQUIRE_SPIN_LOCK(r.27,r.31,r.3,LkCtxSw1,LkCtxSw1Spin)
|
|
|
|
#endif
|
|
|
|
|
|
// Set the new thread's state to Running before releasing the dispatcher lock.
|
|
|
|
|
|
li r.3, Running // set state of new thread
|
|
stb r.3, ThState(r.31) // to running.
|
|
|
|
#if !defined(NT_UP)
|
|
RELEASE_SPIN_LOCK(r.28,rZero)
|
|
#endif
|
|
|
|
bl ..SwapFromIdle // swap context to new thread
|
|
|
|
KiIdleReturn:
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
ori rDispLk, rDispLkSave, 0 // restore &KiDispatcherLock
|
|
|
|
|
|
// rDbgCount (r.21) will have been reset to 0 by the register restore
|
|
// at the end of SwapContext.
|
|
|
|
// If processor 0, check for debugger breakin, otherwise just check for
|
|
// DPCs again.
|
|
|
|
|
|
#endif
|
|
|
|
b IdleLoop
|
|
|
|
|
|
// There are no entries in the DPC list and a thread has not been selected
|
|
// for execution on this processor. Call the HAL so power managment can be
|
|
// performed.
|
|
|
|
// N.B. The HAL is called with interrupts disabled. The HAL will return
|
|
// with interrupts enabled.
|
|
|
|
|
|
Idle:
|
|
mtctr rHalIdle // set entry point
|
|
ori r.toc, rHalToc, 0 // set HAL's TOC
|
|
bctrl // call HalProcessorIdle
|
|
isync // give HAL's interrupt enable
|
|
// a chance to take effect
|
|
ori r.toc, rKTOC, 0 // restore ntoskrnl's TOC
|
|
|
|
b IdleLoop
|
|
|
|
#if !defined(NT_UP)
|
|
SPIN_ON_SPIN_LOCK(r.27,r.3,LkCtxSw1,LkCtxSw1Spin)
|
|
#endif
|
|
|
|
DUMMY_EXIT(KiIdleLoop)
|
|
|
|
#undef rDPCHEAD
|
|
|
|
SBTTL("Process Deferred Procedure Call List")
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is called to process the given deferred procedure call
|
|
// list.
|
|
|
|
// If called from KiDispatchInterrupt, we will have been switched to
|
|
// the interrupt stack, the new stack pointer is in r.sp and entry is
|
|
// at ..KiProcessDpcList.alt.
|
|
|
|
// If called from the idle loop, we run on the idle loop thread's
|
|
// stack and no special action is needed. However, the idle loop
|
|
// does not pass r.9 as we expect it and only passes r.0 = 0 on MP
|
|
// systems. We take advantage of the separate entry points to set
|
|
// these registers appropriately.
|
|
|
|
// N.B. Interrupts must be disabled on entry to this routine. Control
|
|
// is returned to the caller with the same conditions true.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// On entry:
|
|
|
|
// r.0 - Zero
|
|
// r.9 - Machine State Register prior to disabling interrupts.
|
|
// rPrcb r.29 - address of processor control block.
|
|
// r.31 - address of DPC list head.
|
|
|
|
// On exit:
|
|
|
|
// r.0 - Zero
|
|
// r.9 - Machine State Register prior to disabling interrupts.
|
|
// rPrcb r.29 - address of processor control block.
|
|
// r.31 - address of DPC list head.
|
|
|
|
// Return value:
|
|
|
|
// None.
|
|
|
|
|
|
.struct 0
|
|
.space StackFrameHeaderLength
|
|
dp.lr: .space 4
|
|
dp.toc: .space 4
|
|
|
|
#if DBG
|
|
|
|
dp.func:.space 4
|
|
dp.strt:.space 4
|
|
dp.cnt: .space 4
|
|
dp.time:.space 4
|
|
|
|
#endif
|
|
|
|
.align 3 // ensure stack frame length is multile of 8
|
|
dp.framelen:
|
|
|
|
.text
|
|
|
|
#if DBG
|
|
|
|
# define rDpStart r.22
|
|
# define rDpCount r.23
|
|
# define rDpTime r.24
|
|
|
|
#endif
|
|
|
|
|
|
SPECIAL_ENTRY_S(KiProcessDpcList,_TEXT$00)
|
|
|
|
stwu r.sp, -dp.framelen(r.sp) // buy stack frame
|
|
|
|
// see routine description for why we do the following.
|
|
|
|
ori r.9, rIntOn, 0 // get MSR interrupts enabled
|
|
|
|
#if defined(NT_UP)
|
|
li r.0, 0
|
|
#endif
|
|
|
|
|
|
..KiProcessDpcList.alt:
|
|
|
|
mflr r.7 // save return address
|
|
|
|
// save regs we will use,... don't need to save 29 and 31 as they
|
|
// were saved by our caller and currently contain the values we want.
|
|
|
|
stw r.toc, dp.toc(r.sp)
|
|
|
|
#if DBG
|
|
|
|
stw rDpTime, dp.time(r.sp)
|
|
stw rDpCount, dp.cnt(r.sp)
|
|
stw rDpStart, dp.strt(r.sp)
|
|
|
|
#endif
|
|
|
|
stw r.7, dp.lr(r.sp) // save Link Register
|
|
|
|
PROLOGUE_END(KiProcessDpcList)
|
|
|
|
DpcCallRestart:
|
|
|
|
stw r.sp, PbDpcRoutineActive(rPrcb) // set DPC routine active
|
|
|
|
|
|
// Process the DPC list.
|
|
|
|
|
|
DpcCall:
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
addi r.7, rPrcb, PbDpcLock // compute DPC lock address
|
|
|
|
ACQUIRE_SPIN_LOCK(r.7, r.7, r.0, spinlk2, spinlk2spin)
|
|
|
|
#endif
|
|
|
|
lwz r.3, LsFlink(r.31) // get address of first entry
|
|
lwz r.12, LsFlink(r.3) // get addr of next entry
|
|
cmpw r.3, r.31 // is list empty?
|
|
subi r.3, r.3, DpDpcListEntry // subtract DpcListEntry offset
|
|
beq- UnlkDpc0 // if yes, release the lock.
|
|
|
|
|
|
// Get deferred routine address, this is done early as what
|
|
// we actually have is a function descriptor's address and we
|
|
// need to get the entry point address.
|
|
|
|
|
|
lwz r.11, DpDeferredRoutine(r.3)
|
|
lwz r.8, PbDpcQueueDepth(rPrcb) // get DPC queue depth
|
|
|
|
|
|
// remove entry from list
|
|
|
|
|
|
stw r.12, LsFlink(r.31) // set addr of next in header
|
|
stw r.31, LsBlink(r.12) // set addr of previous in next
|
|
|
|
lwz r.10, 0(r.11) // get DPC code address
|
|
|
|
|
|
// entry removed, set up arguments for DPC proc
|
|
|
|
// args are-
|
|
// dpc object address (r.3)
|
|
// deferred context (r.4)
|
|
// system argument 1 (r.5)
|
|
// system argument 2 (r.6)
|
|
|
|
// note, the arguments must be loaded from the DPC object BEFORE
|
|
// the inserted flag is cleared to prevent the object being
|
|
// overwritten before its time.
|
|
|
|
|
|
lwz r.4, DpDeferredContext(r.3)
|
|
lwz r.5, DpSystemArgument1(r.3)
|
|
lwz r.6, DpSystemArgument2(r.3)
|
|
|
|
subi r.8, r.8, 1 // decrement DPC queue depth
|
|
stw r.8, PbDpcQueueDepth(rPrcb) //
|
|
stw r.0, DpLock(r.3) // clear DPC inserted state
|
|
|
|
#if !defined(NT_UP)
|
|
RELEASE_SPIN_LOCK(r.7, r.0)
|
|
#endif
|
|
|
|
mtctr r.10 // ready address for branch
|
|
|
|
ENABLE_INTERRUPTS(r.9)
|
|
|
|
lwz r.toc, 4(r.11) // get DPC toc pointer
|
|
|
|
#if DBG
|
|
lwz rDpStart, KiPcr2 + Pc2TickCountLow(r.0) // get current time
|
|
lwz rDpCount, PbInterruptCount(rPrcb)// get current interrupt count
|
|
lwz rDpTime, PbInterruptTime(rPrcb) // get current interrupt time
|
|
stw r.10, dp.func(r.sp)
|
|
#endif
|
|
|
|
bctrl // call DPC routine
|
|
|
|
li r.0, 0 // reset zero constant
|
|
|
|
#if DBG
|
|
lbz r.10, KiPcr+PcCurrentIrql(r.0) // get current IRQL
|
|
cmplwi r.10, DISPATCH_LEVEL // check if < DISPATCH_LEVEL
|
|
blt DpcBadIrql // jif IRQL < DISPATCH_LEVEL
|
|
|
|
DpcIrqlOk:
|
|
|
|
lwz r.12, KiPcr2 + Pc2TickCountLow(r.0) // calculate time spent in
|
|
sub r.12, r.12, rDpStart // r.12 = time
|
|
cmpwi r.12, 100
|
|
bge DpcTookTooLong // jif >= 1 second
|
|
DpcTimeOk:
|
|
#endif
|
|
|
|
|
|
// Check to determine if any more DPCs are available to process.
|
|
|
|
|
|
DISABLE_INTERRUPTS(r.9, r.10)
|
|
|
|
lwz r.3, LsFlink(r.31) // get address of first entry
|
|
cmpw r.3, r.31 // is list empty?
|
|
bne- DpcCall // if no, process it
|
|
|
|
|
|
// Clear DpcRoutineActive, then check one last time that the DPC queue is
|
|
// empty. This is required to close a race condition with the DPC queueing
|
|
// code where it appears that a DPC routine is active (and thus an
|
|
// interrupt is not requested), but this code has decided that the queue
|
|
// is empty and is clearing DpcRoutineActive.
|
|
|
|
|
|
stw r.0, PbDpcRoutineActive(rPrcb)
|
|
stw r.0, PbDpcInterruptRequested(rPrcb) // clear DPC interrupt requested
|
|
eieio // force writes out
|
|
|
|
lwz r.3, LsFlink(r.31)
|
|
cmpw r.3, r.31
|
|
bne- DpcCallRestart
|
|
|
|
DpcDone:
|
|
|
|
|
|
// List is empty, restore non-volatile registers we have used.
|
|
|
|
|
|
lwz r.10, dp.lr(r.sp) // get link register
|
|
|
|
#if DBG
|
|
lwz rDpTime, dp.time(r.sp)
|
|
lwz rDpCount, dp.cnt(r.sp)
|
|
lwz rDpStart, dp.strt(r.sp)
|
|
#endif
|
|
|
|
|
|
|
|
// Return to caller.
|
|
|
|
|
|
lwz r.toc, dp.toc(r.sp) // restore kernel toc
|
|
mtlr r.10 // set return address
|
|
lwz r.sp, 0(r.sp) // release stack frame
|
|
|
|
blr // return
|
|
|
|
UnlkDpc0:
|
|
|
|
|
|
// The DPC list became empty while we were acquiring the DPC queue lock.
|
|
// Clear DPC routine active. The race condition mentioned above doesn't
|
|
// exist here because we hold the DPC queue lock.
|
|
|
|
|
|
stw r.0, PbDpcRoutineActive(rPrcb)
|
|
|
|
#if !defined(NT_UP)
|
|
RELEASE_SPIN_LOCK(r.7, r.0)
|
|
#endif
|
|
|
|
b DpcDone
|
|
|
|
|
|
// DpcTookTooLong, DpcBadIrql
|
|
|
|
// Come here is it took >= 1 second to execute a DPC routine. This is way
|
|
// too long, assume something is wrong and breakpoint.
|
|
|
|
// This code is out of line to avoid wasting cache space for something that
|
|
// (hopefully) never happens.
|
|
|
|
|
|
#if DBG
|
|
|
|
DpcTookTooLong:
|
|
lwz r.toc, dp.toc(r.sp) // restore kernel's TOC
|
|
lwz r.11, PbInterruptCount(rPrcb) // get current interrupt count
|
|
lwz r.10, PbInterruptTime(rPrcb) // get current interrupt time
|
|
lwz r.toc, dp.toc(r.sp) // restore our toc
|
|
sub r.11, r.11, rDpCount // compute number of interrupts
|
|
sub r.10, r.10, rDpTime // compute interrupt time
|
|
bl ..DbgBreakPoint // execute debug breakpoint
|
|
b DpcTimeOk // continue
|
|
|
|
DpcBadIrql:
|
|
lwz r.toc, dp.toc(r.sp) // restore kernel's TOC
|
|
bl ..DbgBreakPoint // breakpoint
|
|
b DpcIrqlOk // continue
|
|
|
|
#endif
|
|
|
|
#if !defined(NT_UP)
|
|
SPIN_ON_SPIN_LOCK(r.7, r.11, spinlk2, spinlk2spin)
|
|
#endif
|
|
|
|
DUMMY_EXIT(KiProcessDpcList)
|
|
|
|
SBTTL("Dispatch Interrupt")
|
|
|
|
#define rDPCHEAD r.31
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is entered as the result of a software interrupt generated
|
|
// at DISPATCH_LEVEL. Its function is to process the Deferred Procedure Call
|
|
// (DPC) list, and then perform a context switch if a new thread has been
|
|
// selected for execution on the processor.
|
|
|
|
// This routine is entered at IRQL DISPATCH_LEVEL with the dispatcher
|
|
// database unlocked. When a return to the caller finally occurs, the
|
|
// IRQL remains at DISPATCH_LEVEL, and the dispatcher database is still
|
|
// unlocked.
|
|
|
|
// Arguments:
|
|
|
|
// None.
|
|
|
|
// Outputs:
|
|
// ( for call to KiProcessDpcList )
|
|
// r.3 - address of first dpc in list
|
|
// r.0 - Zero
|
|
// r.9 - Machine State Register prior to disabling interrupts.
|
|
// rPrcb r.29 - address of processor control block.
|
|
// rDPCHEAD r.31 - address of DPC listhead.
|
|
// r.9 Machine State Register prior to disabling interrupts.
|
|
|
|
// ( for call to KiDispIntSwapContext )
|
|
|
|
// r.28 pointer to Dispatcher Database Lock
|
|
// r.29 rPrcb pointer to the processor control block
|
|
// r.31 NTH pointer to new thread
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
.struct 0
|
|
.space StackFrameHeaderLength
|
|
di.lr: .space 4
|
|
di.28: .space 4
|
|
di.29: .space 4
|
|
di.30: .space 4
|
|
di.31: .space 4
|
|
|
|
.align 3 // ensure stack frame length is multile of 8
|
|
di.framelen:
|
|
|
|
SPECIAL_ENTRY_S(KiDispatchInterrupt,_TEXT$00)
|
|
|
|
mflr r.0
|
|
stwu r.sp, -di.framelen(r.sp)
|
|
stw r.29, di.29(r.sp)
|
|
lwz rPrcb,KiPcr+PcPrcb(r.0)
|
|
stw r.30, di.30(r.sp)
|
|
stw r.31, di.31(r.sp)
|
|
stw r.0, di.lr(r.sp)
|
|
stw r.28, di.28(r.sp)
|
|
|
|
PROLOGUE_END(KiDispatchInterrupt)
|
|
|
|
|
|
// Setup commonly used constants
|
|
|
|
|
|
lwz r.3, PbDpcBypassCount(rPrcb) // get DPC bypass count
|
|
li r.0, 0 // zero
|
|
addi rDPCHEAD, rPrcb, PbDpcListHead // compute DPC listhead address
|
|
addi r.3, r.3, 1 // increment DPC bypass count
|
|
stw r.3, PbDpcBypassCount(rPrcb) // store new DPC bypass count
|
|
|
|
|
|
// Process the deferred procedure call list.
|
|
|
|
|
|
PollDpcList:
|
|
|
|
DISABLE_INTERRUPTS(r.9, r.8)
|
|
lwz r.3, LsFlink(rDPCHEAD) // get address of first entry
|
|
stb r.0, KiPcr+PcDispatchInterrupt(r.0)
|
|
cmpw r.3, rDPCHEAD // list has entries?
|
|
beq- di.empty // jif list is empty
|
|
|
|
|
|
// Switch to the interrupt stack
|
|
|
|
|
|
lwz r.6, KiPcr+PcInterruptStack(r.0)// get addr of interrupt stack
|
|
lwz r.28, KiPcr+PcInitialStack(r.0) // get current stack base
|
|
lwz r.30, KiPcr+PcStackLimit(r.0) // get current stack limit
|
|
subi r.4, r.6, KERNEL_STACK_SIZE // compute stack limit
|
|
stw r.sp,KiPcr+PcOnInterruptStack(r.0)// flag ON interrupt stack
|
|
stw r.sp, -dp.framelen(r.6) // save new back pointer
|
|
|
|
|
|
// N.B. Can't step thru the next two instructions.
|
|
|
|
|
|
stw r.4, KiPcr+PcStackLimit(r.0) // set stack limit
|
|
stw r.6, KiPcr+PcInitialStack(r.0) // set current base to int stk
|
|
subi r.sp, r.6, dp.framelen // calc new sp
|
|
|
|
bl ..KiProcessDpcList.alt // process all DPCs for this
|
|
// processor.
|
|
|
|
|
|
// N.B. KiProcessDpcList left r.0, r.9 intact.
|
|
|
|
// Return from KiProcessDpcList switched back to the proper stack,
|
|
// update PCR to reflect this.
|
|
|
|
|
|
stw r.30, KiPcr+PcStackLimit(r.0) // restore stack limit
|
|
stw r.28, KiPcr+PcInitialStack(r.0) // set old stack current
|
|
stw r.0, KiPcr+PcOnInterruptStack(r.0)// clear ON interrupt stack
|
|
|
|
di.empty:
|
|
|
|
ENABLE_INTERRUPTS(r.9)
|
|
|
|
|
|
// Check to determine if quantum end has occurred.
|
|
|
|
// N.B. If a new thread is selected as a result of processing a quantum
|
|
// end request, then the new thread is returned with the dispatcher
|
|
// database locked. Otherwise, NULL is returned with the dispatcher
|
|
// database unlocked.
|
|
|
|
lwz r.3, KiPcr+PcQuantumEnd(r.0) // get quantum end indicator
|
|
cmpwi r.3, 0 // if 0, no quantum end request
|
|
beq di.CheckForNewThread
|
|
stw r.0, KiPcr+PcQuantumEnd(r.0) // clear quantum end indicator
|
|
|
|
bl ..KiQuantumEnd // process quantum end
|
|
cmpwi r.3, 0 // new thread selected?
|
|
li r.0, 0 // reset r.0 to zero
|
|
|
|
// If KiQuantumEnd returned no new thread to run, the dispatcher
|
|
// database is unlocked, get out.
|
|
|
|
|
|
beq+ di.exit
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
|
|
// Even though the dispatcher database is already locked, we are expected
|
|
// to pass the address of the lock in r.28.
|
|
|
|
|
|
lwz r.28, [toc]KiDispatcherLock(r.toc)// get &KiDispatcherLock
|
|
|
|
#endif
|
|
|
|
b di.Switch // switch to new thread
|
|
|
|
|
|
// Check to determine if a new thread has been selected for execution on
|
|
// this processor.
|
|
|
|
|
|
di.CheckForNewThread:
|
|
lwz r.3, PbNextThread(rPrcb) // get address of next thread
|
|
cmpwi r.3, 0 // is there a new thread?
|
|
beq di.exit // no, branch.
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.28, [toc]KiDispatcherLock(r.toc)// get &KiDispatcherLock
|
|
|
|
|
|
|
|
// Lock dispatcher database and reread address of next thread object since it
|
|
// is possible for it to change in a multiprocessor system. (leave address
|
|
// of lock in r.28).
|
|
|
|
|
|
TRY_TO_ACQUIRE_SPIN_LOCK(r.28, r.28, r.11, di.spinlk, PollDpcList)
|
|
|
|
#endif
|
|
|
|
di.Switch:
|
|
lwz r.31, PbNextThread(rPrcb) // get thread address (again)
|
|
stw r.0, PbNextThread(rPrcb) // clear addr of next thread obj
|
|
|
|
|
|
// Reready current thread for execution and swap context to the
|
|
// selected thread. We do this indirectly thru KiDispIntSwapContext
|
|
// to avoid saving and restoring so many registers for the cases
|
|
// when KiDispatchInterrupt does not thread switch.
|
|
|
|
|
|
bl ..KiDispIntSwapContext // swap to new thread
|
|
|
|
di.exit:
|
|
|
|
lwz r.0, di.lr(r.sp)
|
|
lwz r.31, di.31(r.sp)
|
|
lwz r.30, di.30(r.sp)
|
|
mtlr r.0
|
|
lwz r.29, di.29(r.sp)
|
|
lwz r.28, di.28(r.sp)
|
|
addi r.sp, r.sp, di.framelen
|
|
|
|
SPECIAL_EXIT(KiDispatchInterrupt)
|
|
|
|
|
|
|
|
// VOID
|
|
// KiDispIntSwapContext (
|
|
// IN PKTHREAD Thread
|
|
// )
|
|
|
|
// Routine Description:
|
|
|
|
// This routine is called to perform a context switch to the specified
|
|
// thread. The current (new previous) thread is re-readied for execution.
|
|
|
|
// Since this routine is called as subroutine all volatile registers are
|
|
// considered free.
|
|
|
|
// Our caller has saved and will restore gprs 28 thru 31 and does not
|
|
// care if we trash them.
|
|
|
|
// This routine is entered at IRQL DISPATCH_LEVEL with the dispatcher
|
|
// database locked. When a return to the caller finally occurs, the
|
|
// dispatcher database is unlocked.
|
|
|
|
// Arguments:
|
|
|
|
// r.28 pointer to Dispatcher Database Lock
|
|
// r.29 rPrcb pointer to the processor control block
|
|
// r.31 NTH pointer to new thread
|
|
|
|
// Outputs: ( for call to SwapContext )
|
|
|
|
// r.27 pointer to KiContextSwapLock
|
|
// r.28 pointer to Dispatcher Database Lock
|
|
// r.29 rPrcb pointer to processor control block
|
|
// r.30 OTH pointer to old thread
|
|
// r.31 NTH pointer to new thread
|
|
|
|
// Return Value:
|
|
|
|
// Wait completion status (r.3).
|
|
|
|
|
|
|
|
.struct 0
|
|
.space swFrameLength
|
|
kdiscLR:.space 4
|
|
.align 3 // ensure 8 byte alignment
|
|
kdiscFrameLength:
|
|
|
|
.align 6 // cache line alignment
|
|
|
|
SPECIAL_ENTRY_S(KiDispIntSwapContext,_TEXT$00)
|
|
|
|
mflr r.0 // get return address
|
|
lwz r.30, KiPcr+PcCurrentThread(r.0) // get current (old) thread
|
|
stwu r.sp, -kdiscFrameLength(r.sp) // buy stack frame
|
|
stw r.14, swFrame + ExGpr14(r.sp) // save gpr 14
|
|
stw r.26, swFrame + ExGpr26(r.sp) // save gprs 26 and 27
|
|
stw r.27, swFrame + ExGpr27(r.sp) //
|
|
|
|
|
|
// Gprs 28, 29, 30 and 31 saved/restored by KiDispatchInterrupt
|
|
|
|
|
|
stw r.0, kdiscLR(r.sp) // save return address
|
|
|
|
PROLOGUE_END(KiDispIntSwapContext)
|
|
|
|
stw r.31, PbCurrentThread(rPrcb) // set new thread current
|
|
|
|
|
|
// Reready current thread and swap context to the selected thread.
|
|
|
|
|
|
ori r.3, r.30, 0
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
lwz r.27, [toc]KiContextSwapLock(r.2)
|
|
|
|
#endif
|
|
|
|
bl ..KiReadyThread // reready current thread
|
|
bl ..SwapContext // switch threads
|
|
|
|
|
|
// Restore registers and return.
|
|
|
|
|
|
lwz r.0, kdiscLR(r.sp) // restore return address
|
|
lwz r.26, swFrame + ExGpr26(r.sp) // restore gpr 26 and 27
|
|
mtlr r.0 // set return address
|
|
lwz r.27, swFrame + ExGpr27(r.sp) //
|
|
|
|
|
|
// Gprs 28, 29, 30 and 31 saved/restored by KiDispatchInterrupt
|
|
|
|
|
|
lwz r.14, swFrame + ExGpr14(r.sp) // restore gpr 14
|
|
addi r.sp, r.sp, kdiscFrameLength // return stack frame
|
|
|
|
SPECIAL_EXIT(KiDispIntSwapContext)
|
|
|
|
|
|
|
|
// VOID
|
|
// KiRequestSoftwareInterrupt (KIRQL RequestIrql)
|
|
|
|
// Routine Description:
|
|
|
|
// This function requests a software interrupt at the specified IRQL
|
|
// level.
|
|
|
|
// Arguments:
|
|
|
|
// RequestIrql (r.3) - Supplies the request IRQL value.
|
|
// Allowable values are APC_LEVEL (1)
|
|
// DPC_LEVEL (2)
|
|
|
|
// Return Value:
|
|
|
|
// None.
|
|
|
|
|
|
|
|
LEAF_ENTRY(KiRequestSoftwareInterrupt)
|
|
|
|
lbz r.6, KiPcr+PcCurrentIrql(r.0) // get current IRQL
|
|
rlwinm r.4, r.3, 31, 0x1 // transform 1 or 2 to 0 or 1
|
|
li r.5, 1 // non-zero value
|
|
cmpw r.6, r.3 // is current IRQL < requested IRQL?
|
|
stb r.5, KiPcr+PcSoftwareInterrupt(r.4) // set interrupt pending
|
|
blt ..KiDispatchSoftwareInterrupt // jump to dispatch interrupt if
|
|
// current IRQL low enough (note
|
|
// that this is a jump, not a call)
|
|
|
|
LEAF_EXIT(KiRequestSoftwareInterrupt)
|