summaryrefslogtreecommitdiffstats
path: root/private/ntos/ke/ppc/ctxswap.s
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ke/ppc/ctxswap.s')
-rw-r--r--private/ntos/ke/ppc/ctxswap.s2942
1 files changed, 2942 insertions, 0 deletions
diff --git a/private/ntos/ke/ppc/ctxswap.s b/private/ntos/ke/ppc/ctxswap.s
new file mode 100644
index 000000000..2d4c2c41b
--- /dev/null
+++ b/private/ntos/ke/ppc/ctxswap.s
@@ -0,0 +1,2942 @@
+// 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)