Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2132 lines
61 KiB

//++
//
// Copyright (c) 1989-2000 Microsoft Corporation
//
// Component Name:
//
// NT / KE
//
// Module Name:
//
// ctxswap.s
//
// Abstract:
//
// This module implements the IA64 Process and Thread Context Swaps.
//
// Author:
//
// David N. Cutler (davec) 5-Mar-1989
//
// Environment:
//
// Kernel mode only
//
// Revision History:
//
// Bernard Lint Jul-12-1995
//
// Initial IA64 version
//
//--
#include "ksia64.h"
.file "ctxswap.s"
.text
//
// Globals imported:
//
.global KiReadySummary
.global KiIdleSummary
.global KiDispatcherReadyListHead
.global KeTickCount
.global KiMasterSequence
.global KiMasterRid
.global PPerfGlobalGroupMask
PublicFunction(KiDeliverApc)
PublicFunction(KiSaveExceptionFrame)
PublicFunction(KiRestoreExceptionFrame)
PublicFunction(KiActivateWaiterQueue)
PublicFunction(KiReadyThread)
PublicFunction(KeFlushEntireTb)
PublicFunction(KiQuantumEnd)
PublicFunction(KiSyncNewRegionId)
PublicFunction(KiCheckForSoftwareInterrupt)
PublicFunction(KiSaveHigherFPVolatileAtDispatchLevel)
PublicFunction(KeAcquireQueuedSpinLockAtDpcLevel)
PublicFunction(KeReleaseQueuedSpinLockFromDpcLevel)
PublicFunction(KeTryToAcquireQueuedSpinLockRaiseToSynch)
PublicFunction(WmiTraceContextSwap)
#if DBG
PublicFunction(KeBugCheckEx)
#endif // DBG
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. 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 fucntion.
//
// Arguments:
//
// OldIrql (a0) - Supplies the IRQL when the dispatcher database
// lock was acquired (in low order byte, not zero extended).
//
// Return Value:
//
// None.
//
//--------------------------------------------------------------------
//--
NESTED_ENTRY(KiUnlockDispatcherDatabase)
NESTED_SETUP(1,3,1,0)
//
// Register aliases
//
rDPC = loc2 // DPC active flag
rpT1 = t1 // temp pointer
rpT2 = t2 // temp pointer
rpT3 = t3 // temp pointer
rT1 = t5 // temp regs
rT2 = t6
rPrcb = t8 // PRCB pointer
pNotNl = pt2 // true if next thread not NULL
pIRQGE = pt3 // true if DISPATCH_LEVEL <= old irql
pIRQLT = pt4 // true if DISPATCH_LEVEL > old irql
pDPC = pt5 // true if DPC active
pNoAPC = pt2 // do not dispatch APC
pAPC = pt9
PROLOGUE_END
//
// Check if a thread has been scheduled to execute on the current processor
//
movl rPrcb = KiPcr + PcPrcb
;;
LDPTR (rPrcb, rPrcb) // rPrcb -> PRCB
;;
add rpT1 = PbNextThread, rPrcb // -> next thread
add rpT2 = PbDpcRoutineActive,rPrcb // -> DPC active flag
;;
LDPTR (v0, rpT1) // v0 = next thread
;;
cmp.ne pNotNl = zero, v0 // pNotNl = next thread is 0
zxt1 a0 = a0 // isolate old IRQL
;;
(pNotNl) cmp.leu.unc pIRQGE, pIRQLT = DISPATCH_LEVEL, a0
mov rDPC = 1 // speculate that DPC is active
(pIRQLT) br.spnt KxUnlockDispatcherDatabase
;;
//
// Case 1:
// Next thread is NULL:
// Release dispatcher database lock, restore IRQL to its previous level
// and return
//
//
// Case 2:
// 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 and restore IRQL. If the current processor is
// not executing a DPC, then request a dispatch interrupt on the current
// processor.
//
// At this point pNotNl = 1 if thread not NULL, 0 if NULL
//
(pIRQGE) ld4 rDPC = [rpT2] // rDPC.4 = DPC active flag
#if !defined(NT_UP)
add out0 = (LockQueueDispatcherLock * 16) + PbLockQueue, rPrcb
br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
#endif // !defined(NT_UP)
;;
LOWER_IRQL(a0)
cmp4.eq pDPC = rDPC, zero // pDPC = request DPC intr
REQUEST_DISPATCH_INT(pDPC) // request DPC interrupt
NESTED_RETURN
NESTED_EXIT(KiUnlockDispatcherDatabase)
//
// N.B. This routine is carefully written as a nested function.
// Control only reaches this routine from above.
//
// rPrcb contains the address of PRCB
// v0 contains the next thread
//
NESTED_ENTRY(KxUnlockDispatcherDatabase)
PROLOGUE_BEGIN
.regstk 1, 2, 1, 0
alloc t16 = ar.pfs, 1, 2, 1, 0
.save rp, loc0
mov loc0 = brp
.fframe SwitchFrameLength
add sp = -SwitchFrameLength, sp
;;
.save ar.unat, loc1
mov loc1 = ar.unat
add t0 = ExFltS19+SwExFrame+STACK_SCRATCH_AREA, sp
add t1 = ExFltS18+SwExFrame+STACK_SCRATCH_AREA, sp
;;
.save.gf 0x0, 0xC0000
stf.spill [t0] = fs19, ExFltS17-ExFltS19
stf.spill [t1] = fs18, ExFltS16-ExFltS18
;;
.save.gf 0x0, 0x30000
stf.spill [t0] = fs17, ExFltS15-ExFltS17
stf.spill [t1] = fs16, ExFltS14-ExFltS16
mov t10 = bs4
;;
.save.gf 0x0, 0xC000
stf.spill [t0] = fs15, ExFltS13-ExFltS15
stf.spill [t1] = fs14, ExFltS12-ExFltS14
mov t11 = bs3
;;
.save.gf 0x0, 0x3000
stf.spill [t0] = fs13, ExFltS11-ExFltS13
stf.spill [t1] = fs12, ExFltS10-ExFltS12
mov t12 = bs2
;;
.save.gf 0x0, 0xC00
stf.spill [t0] = fs11, ExFltS9-ExFltS11
stf.spill [t1] = fs10, ExFltS8-ExFltS10
mov t13 = bs1
;;
.save.gf 0x0, 0x300
stf.spill [t0] = fs9, ExFltS7-ExFltS9
stf.spill [t1] = fs8, ExFltS6-ExFltS8
mov t14 = bs0
;;
.save.gf 0x0, 0xC0
stf.spill [t0] = fs7, ExFltS5-ExFltS7
stf.spill [t1] = fs6, ExFltS4-ExFltS6
mov t15 = ar.lc
;;
.save.gf 0x0, 0x30
stf.spill [t0] = fs5, ExFltS3-ExFltS5
stf.spill [t1] = fs4, ExFltS2-ExFltS4
;;
.save.f 0xC
stf.spill [t0] = fs3, ExFltS1-ExFltS3 // save fs3
stf.spill [t1] = fs2, ExFltS0-ExFltS2 // save fs2
;;
.save.f 0x3
stf.spill [t0] = fs1, ExBrS4-ExFltS1 // save fs1
stf.spill [t1] = fs0, ExBrS3-ExFltS0 // save fs0
;;
.save.b 0x18
st8 [t0] = t10, ExBrS2-ExBrS4 // save bs4
st8 [t1] = t11, ExBrS1-ExBrS3 // save bs3
;;
.save.b 0x6
st8 [t0] = t12, ExBrS0-ExBrS2 // save bs2
st8 [t1] = t13, ExIntS2-ExBrS1 // save bs1
;;
.save.b 0x1
st8 [t0] = t14, ExIntS3-ExBrS0 // save bs0
movl t12 = KiPcr + PcCurrentThread
;;
.save.gf 0xC, 0x0
.mem.offset 0,0
st8.spill [t0] = s3, ExIntS1-ExIntS3 // save s3
.mem.offset 8,0
st8.spill [t1] = s2, ExIntS0-ExIntS2 // save s2
;;
.save.gf 0x3, 0x0
.mem.offset 0,0
st8.spill [t0] = s1, ExApLC-ExIntS1 // save s1
.mem.offset 8,0
st8.spill [t1] = s0, ExApEC-ExIntS0 // save s0
;;
.savepsp ar.pfs, ExceptionFrameLength-ExApEC-STACK_SCRATCH_AREA
st8 [t1] = t16, ExIntNats-ExApEC
mov t4 = ar.unat // captured Nats of s0-s3
mov s0 = rPrcb
LDPTR (s1, t12) // current thread
;;
.savepsp ar.lc, ExceptionFrameLength-ExApLC-STACK_SCRATCH_AREA
st8 [t0] = t15
.savepsp @priunat, ExceptionFrameLength-ExIntNats-STACK_SCRATCH_AREA
st8 [t1] = t4 // save Nats of s0-s3
mov s2 = v0
PROLOGUE_END
add rpT2 = PbNextThread, s0 // -> next thread
add out0 = ThWaitIrql, s1 // -> previous IRQL
;;
STPTRINC (rpT2, zero,PbCurrentThread-PbNextThread) // clear NextThread
st1 [out0] = a0, ThIdleSwapBlock-ThWaitIrql // save old IRQL
mov rpT3 = 1
;;
//
// Reready current thread for execution and swap context to the selected
// thread.
//
// Note: Set IdleSwapBlock in the current thread so no idle processor
// can switch to this processor before it is removed from the current
// processor.
STPTR (rpT2, s2) // set current thread object
st1 [out0] = rpT3, -ThIdleSwapBlock// out0 -> previous thread
br.call.sptk brp = KiReadyThread
;;
br.call.sptk brp = SwapContext
;;
//
// Lower IRQL, deallocate exception/switch frame.
//
// N.B. SwapContext releases the dispatcher database lock.
//
// N.B. v0 contains the kernel APC pending state on return.
//
// N.B. s2 contains the address of the new thread on return.
//
add rpT2 = ThWaitIrql, s2 // -> ThWaitIrql
cmp.ne pAPC, pNoAPC = zero, v0
;;
ld1 a0 = [rpT2] // a0 = original wait IRQL
;;
(pAPC) cmp.ne pNoAPC = zero, a0 // APC pending and IRQL == 0
(pNoAPC) br.spnt Kudd_Exit
;;
.regstk 1, 2, 3, 0
alloc t16 = ar.pfs, 1, 2, 3, 0
mov rT2 = APC_LEVEL
;;
SET_IRQL(rT2)
mov out0 = KernelMode
mov out1 = zero
mov out2 = zero
br.call.sptk brp = KiDeliverApc
;;
//
// Lower IRQL to wait level, set return status, restore registers, and return.
//
Kudd_Exit:
LOWER_IRQL(a0) // a0 = new irql
add out0 = STACK_SCRATCH_AREA+SwExFrame, sp
br.call.sptk brp = KiRestoreExceptionFrame
;;
add rpT1 = ExApEC+SwExFrame+STACK_SCRATCH_AREA, sp
;;
ld8 rT1 = [rpT1]
mov brp = loc0
;;
mov ar.unat = loc1
nop.f 0
mov ar.pfs = rT1
.restore
add sp = SwitchFrameLength, sp
nop.i 0
br.ret.sptk brp
;;
NESTED_EXIT(KxUnlockDispatcherDatabase)
SBTTL("Swap Thread")
//++
//--------------------------------------------------------------------
//
// BOOLEAN
// KiSwapContext (
// IN PKTHREAD Thread
// )
//
// Routine Description:
//
// This routine saves the non-volatile registers, marshals the
// arguments for SwapContext and calls SwapContext to perform
// the actual thread switch.
//
// Arguments:
//
// Thread - Supplies the address of the new thread.
//
// Return Value:
//
// If a kernel APC is pending, then a value of TRUE is returned.
// Otherwise, FALSE is returned.
//
// Notes:
//
// GP valid on entry -- GP is not switched, just use kernel GP
//--------------------------------------------------------------------
//--
NESTED_ENTRY(KiSwapContext)
//
// Register aliases
//
pNoAPC = pt2 // do not dispatch APC
rpT1 = t0 // temp pointer
rpT2 = t1 // temp pointer
rT1 = t10 // temp regs
PROLOGUE_BEGIN
.regstk 1, 2, 1, 0
alloc t16 = ar.pfs, 1, 2, 1, 0
.save rp, loc0
mov loc0 = brp
.fframe SwitchFrameLength
add sp = -SwitchFrameLength, sp
;;
.save ar.unat, loc1
mov loc1 = ar.unat
add t0 = ExFltS19+SwExFrame+STACK_SCRATCH_AREA, sp
add t1 = ExFltS18+SwExFrame+STACK_SCRATCH_AREA, sp
;;
.save.gf 0x0, 0xC0000
stf.spill [t0] = fs19, ExFltS17-ExFltS19
stf.spill [t1] = fs18, ExFltS16-ExFltS18
;;
.save.gf 0x0, 0x30000
stf.spill [t0] = fs17, ExFltS15-ExFltS17
stf.spill [t1] = fs16, ExFltS14-ExFltS16
mov t10 = bs4
;;
.save.gf 0x0, 0xC000
stf.spill [t0] = fs15, ExFltS13-ExFltS15
stf.spill [t1] = fs14, ExFltS12-ExFltS14
mov t11 = bs3
;;
.save.gf 0x0, 0x3000
stf.spill [t0] = fs13, ExFltS11-ExFltS13
stf.spill [t1] = fs12, ExFltS10-ExFltS12
mov t12 = bs2
;;
.save.gf 0x0, 0xC00
stf.spill [t0] = fs11, ExFltS9-ExFltS11
stf.spill [t1] = fs10, ExFltS8-ExFltS10
mov t13 = bs1
;;
.save.gf 0x0, 0x300
stf.spill [t0] = fs9, ExFltS7-ExFltS9
stf.spill [t1] = fs8, ExFltS6-ExFltS8
mov t14 = bs0
;;
.save.gf 0x0, 0xC0
stf.spill [t0] = fs7, ExFltS5-ExFltS7
stf.spill [t1] = fs6, ExFltS4-ExFltS6
mov t15 = ar.lc
;;
.save.gf 0x0, 0x30
stf.spill [t0] = fs5, ExFltS3-ExFltS5
stf.spill [t1] = fs4, ExFltS2-ExFltS4
;;
.save.f 0xC
stf.spill [t0] = fs3, ExFltS1-ExFltS3 // save fs3
stf.spill [t1] = fs2, ExFltS0-ExFltS2 // save fs2
;;
.save.f 0x3
stf.spill [t0] = fs1, ExBrS4-ExFltS1 // save fs1
stf.spill [t1] = fs0, ExBrS3-ExFltS0 // save fs0
;;
.save.b 0x18
st8 [t0] = t10, ExBrS2-ExBrS4 // save bs4
st8 [t1] = t11, ExBrS1-ExBrS3 // save bs3
;;
.save.b 0x6
st8 [t0] = t12, ExBrS0-ExBrS2 // save bs2
st8 [t1] = t13, ExIntS2-ExBrS1 // save bs1
;;
.save.b 0x1
st8 [t0] = t14, ExIntS3-ExBrS0 // save bs0
;;
.save.gf 0xC, 0x0
.mem.offset 0,0
st8.spill [t0] = s3, ExIntS1-ExIntS3 // save s3
.mem.offset 8,0
st8.spill [t1] = s2, ExIntS0-ExIntS2 // save s2
;;
.save.gf 0x3, 0x0
.mem.offset 0,0
st8.spill [t0] = s1, ExApLC-ExIntS1 // save s1
.mem.offset 8,0
st8.spill [t1] = s0, ExApEC-ExIntS0 // save s0
;;
.savepsp ar.pfs, ExceptionFrameLength-ExApEC-STACK_SCRATCH_AREA
st8 [t1] = t16, ExIntNats-ExApEC
mov t4 = ar.unat // captured Nats of s0-s3
;;
.savepsp ar.lc, ExceptionFrameLength-ExApLC-STACK_SCRATCH_AREA
st8 [t0] = t15
.savepsp @priunat, ExceptionFrameLength-ExIntNats-STACK_SCRATCH_AREA
st8 [t1] = t4 // save Nats of s0-s3
PROLOGUE_END
//
// For the call to SwapContext-
//
// s0 // Prcb address
// s1 // old thread address
// s2 // new thread address
// pt0 = 1
//
mov s2 = a0 // s2 <- New Thread
movl rpT1 = KiPcr + PcPrcb
;;
LDPTRINC (s0, rpT1, PcCurrentThread-PcPrcb)// s0 <- Prcb
;;
LDPTR (s1, rpT1) // s1 <- Old Thread
add rpT2 = PbCurrentThread, s0
;;
//
// Swap context to the next thread.
//
STPTR (rpT2, a0) // Set new thread current
cmp.eq pt0 = zero, zero // indicate lock context swap
br.call.sptk brp = SwapContext // call SwapContext(prcb, OldTh, NewTh)
;;
//
// Deallocate exception/switch frame.
//
// N.B. SwapContext releases the dispatcher database lock.
//
// N.B. v0 contains the kernel APC pending state on return, ie, 0 if
// no APC pending, 1 if APC pending. v0 will be forced to 0 if
// the new IRQL doesn't allow APCs.
//
// N.B. KiRestoreExceptionFrame doesn't touch v0, t21 or t22.
//
add rpT2 = ThWaitIrql, s2 // -> ThWaitIrql
add rpT1 = ExApEC+SwExFrame+STACK_SCRATCH_AREA, sp
add out0 = STACK_SCRATCH_AREA+SwExFrame, sp
;;
ld1 t21 = [rpT2] // t21 = original wait IRQL
ld8 t22 = [rpT1] // t22 = PFS
br.call.sptk brp = KiRestoreExceptionFrame
;;
mov brp = loc0
cmp.ne pNoAPC = zero, t21 // no APC if IRQL != 0
;;
mov ar.unat = loc1
nop.f 0
mov ar.pfs = t22
.restore
add sp = SwitchFrameLength, sp
(pNoAPC) mov v0 = zero
br.ret.sptk brp
;;
NESTED_EXIT(KiSwapContext)
SBTTL("Swap Context to Next Thread")
//++
//--------------------------------------------------------------------
// Routine:
//
// SwapContext
//
// Routine Description:
//
// This routine is called to swap context from one thread to the next.
//
// Arguments:
//
// s0 - Address of Processor Control Block (PRCB).
// s1 - Address of previous thread object.
// s2 - Address of next thread object.
//
// Return value:
//
// v0 - Kernel APC pending flag
// s0 - Address of Processor Control Block (PRCB).
// s1 - Address of previous thread object.
// s2 - Address of current thread object.
//
// Note:
// Kernel GP is not saved and restored across context switch
//
// !!WARNING!! - Thierry. 03/01/2000.
// Be aware that this implementation is a result of performance analysis.
// Please consider this when you are making changes...
//
//--------------------------------------------------------------------
//--
NESTED_ENTRY(SwapContext)
//
// Register aliases
//
rT1 = t1 // temp
rT2 = t2 // temp
rT3 = t3 // temp
rNewproc = t4 // next process object
rOldproc = t5 // previous process object
rpThBSL = t6 // pointer to new thread backing store limit
rpT1 = t7 // temp pointer
rpT2 = t8 // temp pointer
rpT3 = t9 // temp pointer
rAr1 = t10
rAr2 = t11
rAr3 = t12
rAr4 = t13
rNewIKS = t14 // new initial kernel stack
rNewKSL = t15 // new kernel stack limit
rNewBSP = t16 // new thread BSP/BSPSTORE
rOldBSP = t16 // old thread BSP
rOldRNAT = t17 // old thread RNAT
rNewRNAT = t17 // new thread RNAT
rOldSbase = t18 // old thread kstack base
pUsTh = pt4 // is user thread?
pKrTh = pt5 // is user thread?
pSave = pt7 // is high fp set dirty?
pDiff = ps4 // if new and old process different
pSame = ps5 // if new and old process same
//
// Set new thread's state to running. Note this must be done
// under the dispatcher lock so that KiSetPriorityThread sees
// the correct state.
//
PROLOGUE_BEGIN
#if !defined(NT_UP)
alloc rT2 = ar.pfs, 0, 0, 4, 0
mov rT1 = brp // move from brp takes 2 cycles
add rpT3 = ThState, s2
;;
lfetch.excl [rpT3]
mov rAr1 = Running
add rpT2 = SwPFS+STACK_SCRATCH_AREA, sp
;;
add out0 = (LockQueueContextSwapLock * 16) + PbLockQueue, s0
.savesp ar.pfs, SwPFS+STACK_SCRATCH_AREA
st8.nta [rpT2] = rT2, SwRp-SwPFS // save pfs
;;
.savesp brp, SwRp+STACK_SCRATCH_AREA
st8.nta [rpT2] = rT1 // save return link
st1.nta [rpT3] = rAr1 // set thread state to Running
br.call.sptk brp = KeAcquireQueuedSpinLockAtDpcLevel
;;
//
// Release DispatcherLock.
//
add out0 = (LockQueueDispatcherLock * 16) + PbLockQueue, s0
br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
;;
mov out0 = ar.fpsr // move from ar.fpsr takes 12 cycles
movl rpT1 = KiPcr+PcHighFpOwner // setup for prefetching
;;
{ .mmi
lfetch [rpT1]
cmp.ne pUsTh = zero, teb // test for ia32 save required
// must not have a nop.f for next 10 cycles--
// Using temporarely the explicit templating
// for the next cycles.
add out1 = ThStackBase, s1 // move early to start access for rOldSbase
{ .mmi
add rpT1 = SwFPSR+STACK_SCRATCH_AREA, sp
add rpT2 = SwPreds+STACK_SCRATCH_AREA, sp
nop.i 0x0
}
;;
{ .mmi
ld8.nta rOldSbase = [out1] // speculative start early for ia32 saves
lfetch.excl [rpT1]
add out2 = ThNumber, s2 // setup for prefetching
}
{ .mmi
mov.m ar.rsc = r0 // put RSE in lazy mode
mov rOldBSP = ar.bsp // move from ar.bsp takes 12 cycles
nop.i 0x0
}
;;
{ .mmi
lfetch [out2]
nop.m 0x0
mov rT1 = pr // move from pr takes 2 cycles
}
;;
{ .mmi
flushrs
mov rT3 = psr.um // move from psr.um takes 12 cycles
nop.i 0x0
}
;;
{ .mmi
lfetch.excl [rpT2]
mov.m rOldRNAT = ar.rnat // move from ar.rnat takes 5 cycles
add out2 = @gprel(PPerfGlobalGroupMask), gp
}
;;
{ .mli
lfetch [out2]
movl out3 = KiPcr + PcInterruptionCount // INTERRUPTION_LOGGING on or off, we are prefetching this line.
// If any real performance problem is detected, we will undef these lines.
}
;;
{ .mmi
lfetch [out3]
add rpT3 = SwRnat+STACK_SCRATCH_AREA, sp
}
;;
#else // NT_UP
alloc rT2 = ar.pfs, 0, 0, 4, 0
cmp.ne pUsTh = zero, teb // test for ia32 save required
;;
mov.m ar.rsc = r0 // put RSE in lazy mode
add out1 = ThStackBase, s1 // move early to start access for rOldSbase
mov out0 = ar.fpsr // move from ar.fpsr takes 12 cycles
// must not have a nop.f for next 10 cycles--
// Using temporarely the explicit templating
// for the next cycles.
;;
{ .mmi
ld8.nta rOldSbase = [out1] // speculative start early for ia32 saves
mov rOldBSP = ar.bsp // move from ar.bsp takes 12 cycles
add rpT1 = SwRp+STACK_SCRATCH_AREA, sp
}
;;
flushrs
mov rT3 = psr.um // move from psr.um takes 12 cycles
add rpT2 = SwPFS+STACK_SCRATCH_AREA, sp
;;
mov.m rOldRNAT = ar.rnat // move from ar.rnat takes 5 cycles
mov rT1 = brp // move from brp takes 2 cycles
add rpT3 = ThState, s2
;;
{ .mmi
mov rAr1 = Running
.savesp brp, SwRp+STACK_SCRATCH_AREA
st8.nta [rpT1] = rT1, SwFPSR-SwRp // save return link
nop.i 0x0
}
;;
{ .mii
st1.nta [rpT3] = rAr1 // set thread state to Running
mov rT1 = pr // move from pr takes 2 cycles
nop.i 0x0
}
;;
{ .mii
.savesp ar.pfs, SwPFS+STACK_SCRATCH_AREA
st8.nta [rpT2] = rT2, SwPreds-SwPFS // save pfs
add rpT3 = SwRnat+STACK_SCRATCH_AREA, sp
nop.i 0x0
}
;;
#endif // NT_UP
{ .mmi
st8.nta [rpT3] = rOldRNAT
nop.m 0x0
nop.i 0x0
}
st8 [rpT1] = out0, SwBsp-SwFPSR // save kernel FPSR
st8 [rpT2] = rT1 // save preserved predicates
;;
st8.nta [rpT1] = rOldBSP
add rpT3 = ThKernelBStore, s1
tbit.nz pSave = rT3, PSR_MFH // check mfh bit
(pUsTh) br.call.spnt brp = SwapContextIA32Save
;;
st8.nta [rpT3] = rOldBSP
(pSave) add out0 = -ThreadStateSaveAreaLength+TsHigherFPVolatile, rOldSbase
(pSave) br.call.spnt brp = KiSaveHigherFPVolatileAtDispatchLevel
;;
//
// 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.
//
PROLOGUE_END
//
// ***** TBD ****** Save performance counters? (user vs. kernel)
//
//
// Accumlate the total time spent in a thread.
//
#if defined(PERF_DATA)
**** TBD **** MIPS code
addu a0,sp,ExFltF20 // compute address of result
move a1,zero // set address of optional frequency
jal KeQueryPerformanceCounter // query performance counter
lw t0,ExFltF20(sp) // get current cycle count
lw t1,ExFltF20 + 4(sp) //
lw t2,PbStartCount(s0) // get starting cycle count
lw t3,PbStartCount + 4(s0) //
sw t0,PbStartCount(s0) // set starting cycle count
sw t1,PbStartCount + 4(s0) //
lw t4,EtPerformanceCountLow(s1) // get accumulated cycle count
lw t5,EtPerformanceCountHigh(s1) //
subu t6,t0,t2 // subtract low parts
subu t7,t1,t3 // subtract high parts
sltu v0,t0,t2 // generate borrow from high part
subu t7,t7,v0 // subtract borrow
addu t6,t6,t4 // add low parts
addu t7,t7,t5 // add high parts
sltu v0,t6,t4 // generate carry into high part
addu t7,t7,v0 // add carry
sw t6,EtPerformanceCountLow(s1) // set accumulated cycle count
sw t7,EtPerformanceCountHigh(s1) //
#endif // defined(PERF_DATA)
//
// The following entry point is used to switch from the idle thread to
// another thread.
//
;;
ALTERNATE_ENTRY(SwapFromIdle)
alloc rT1 = ar.pfs, 2, 0, 2, 0
//
// Check if we are tracing context swaps
//
mov out0 = s1 // assign out0 to old ethread pointer
add rpT3 = @gprel(PPerfGlobalGroupMask), gp
;;
ld8.nta rpT3 = [rpT3] // get value of PperfGlobalGroupMask
mov out1 = s2 // assign out1 to new ethread pointer
;;
add rpT2 = PERF_CONTEXTSWAP_OFFSET, rpT3
cmp.ne pt3 = zero, rpT3 // if it's non-zero, then trace on
;;
(pt3) ld4.nta rpT2 = [rpT2]
;;
(pt3) and rpT2 = PERF_CONTEXTSWAP_FLAG, rpT2
;;
(pt3) cmp.ne.unc pt4 = zero, rpT2
(pt4) br.call.spnt brp = WmiTraceContextSwap // optimize for no tracing case
;;
//
// Get address of old and new process objects.
//
add rpT2 = ThApcState+AsProcess,s2 // -> new thread AsProcess
add rpT1 = ThApcState+AsProcess,s1 // -> old thread AsProcess
;;
LDPTR (rOldproc, rpT1) // old process
LDPTR (rNewproc, rpT2) // new process
#if !defined(NT_UP)
//
// In MP system,
// should a thread address is recycled and the thread is migrated to a
// processor that holds the stale values in the high fp register set,
// set KiPcr->HighFpOwner to zero (i.e. when pt4 is set to TRUE)
//
add rpT1 = ThNumber, s2
movl rpT2 = KiPcr+PcHighFpOwner
;;
ld1 rT1 = [rpT1]
ld8 rT2 = [rpT2], PcNumber-PcHighFpOwner
add out0 = ThIdleSwapBlock, s1
;;
ld1 rT3 = [rpT2], PcHighFpOwner-PcNumber
st1 [out0] = zero // clear OldThread->IdleSwapBlock
cmp.eq pt3 = rT2, s2
;;
(pt3) cmp.ne.unc pt4 = rT1, rT3
;;
(pt4) st8 [rpT2] = zero
#endif // !defined(NT_UP)
;;
flushrs
FAST_DISABLE_INTERRUPTS
;;
//
// Thierry - 03/29/2000
// It should be noticed that the performance analysis for SwapContext
// was done with INTERRUPTION_LOGGING defined as 1.
//
#define INTERRUPTION_LOGGING 1
#if defined(INTERRUPTION_LOGGING)
// For Conditional Interrupt Logging
#define ContextSwitchBit 63
.global KiVectorLogMask
mov rT3 = gp
;;
movl gp = _gp
;;
add rpT1 = @gprel(KiVectorLogMask), gp
;;
ld8 rT1 = [rpT1]
mov gp = rT3
;;
tbit.z pt4 = rT1, ContextSwitchBit
(pt4) br.cond.sptk EndOfLogging0
movl rpT1 = KiPcr+PcInterruptionCount
mov rT3 = MAX_NUMBER_OF_IHISTORY_RECORDS - 1
cmp.ne pDiff,pSame=rOldproc,rNewproc
;;
(pDiff) mov rT1 = 0x91 // process switch
ld4.nt1 rT2 = [rpT1] // get current count
;;
(pSame) mov rT1 = 0x90 // thread switch
add rpT3 = 1, rT2 // incr count
and rT2 = rT3, rT2 // index of current entry
add rpT2 = 0x1000-PcInterruptionCount, rpT1 // base of history
;;
st4.nta [rpT1] = rpT3 // save count
shl rT2 = rT2, 5 // offset of current entry
;;
add rpT2 = rpT2, rT2 // address of current entry
;;
st8 [rpT2] = rT1, 8 // save switch type
;;
st8 [rpT2] = s2, 8 // save new thread pointer
;;
st8 [rpT2] = s1, 8 // save old thread
;;
st8 [rpT2] = sp // save old sp
;;
// For Conditional Interrupt Logging
EndOfLogging0:
#endif // INTERRUPTION_LOGGING
mov ar.rsc = r0 // put RSE in lazy mode
add rpT1 = ThInitialStack, s2
add rpT2 = ThKernelStack, s1
;;
//
// Store the kernel stack pointer in the previous thread object,
// load the new kernel stack pointer from the new thread object,
// switch backing store pointers, select new process id and swap
// to the new process.
//
ld8.nta rNewIKS = [rpT1], ThKernelStack-ThInitialStack
st8.nta [rpT2] = sp // save current sp
;;
ld8.nta sp = [rpT1], ThStackLimit-ThKernelStack
movl rpT2 = KiPcr + PcInitialStack
;;
alloc rT1 = 0,0,0,0 // make current frame 0 size
ld8.nta rNewKSL = [rpT1], ThInitialBStore-ThStackLimit
;;
loadrs // invalidate RSE and ALAT
ld8.nta rT1 = [rpT1], ThBStoreLimit-ThInitialBStore
;;
ld8.nta rT2 = [rpT1], ThDebugActive-ThBStoreLimit
st8 [rpT2] = rNewIKS, PcStackLimit-PcInitialStack
;;
// get debugger active state
ld1.nta rT3 = [rpT1], ThTeb-ThDebugActive
st8 [rpT2] = rNewKSL, PcInitialBStore-PcStackLimit
add rpT3 = SwBsp+STACK_SCRATCH_AREA, sp
;;
ld8 rNewBSP = [rpT3], SwRnat-SwBsp
st8 [rpT2] = rT1, PcBStoreLimit-PcInitialBStore
;;
ld8 rNewRNAT = [rpT3]
st8 [rpT2] = rT2, PcDebugActive-PcBStoreLimit
;;
// load new teb
ld8 teb = [rpT1], ThApcState+AsKernelApcPending-ThTeb
// set new debugger active state
st1 [rpT2] = rT3, PcCurrentThread-PcDebugActive
invala
//
// Setup PCR intial kernel BSP and BSTORE limit
//
mov ar.bspstore = rNewBSP // load new bspstore
cmp.ne pDiff,pSame=rOldproc,rNewproc // if ne, switch process
;;
mov ar.rnat = rNewRNAT // load new RNATs
;;
mov ar.rsc = RSC_KERNEL // enable RSE
;;
//
// If the new process is not the same as the old process, then swap the
// address space to the new process.
//
// N.B. The context swap lock cannot be dropped until all references to the
// old process address space are complete. This includes any possible
// TB Misses that could occur referencing the new address space while
// still executing in the old address space.
//
// N.B. The process address space swap is executed with interrupts disabled.
//
alloc rT1 = 0,4,2,0
STPTR (rpT2, s2)
;;
mov kteb = teb // update kernel TEB
FAST_ENABLE_INTERRUPTS
ld1 loc0 = [rpT1] // load the ApcPending flag
#if !defined(NT_UP)
//
// Release the context swap lock
// N.B. ContextSwapLock is always released in KxSwapProcess, if called
//
add out0 = (LockQueueContextSwapLock * 16) + PbLockQueue, s0
add loc1 = PcApcInterrupt-PcCurrentThread, rpT2
(pSame) br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
;;
#else // !defined(NT_UP)
add loc1 = PcApcInterrupt-PcCurrentThread, rpT2
;;
#endif // !defined(NT_UP)
mov out0 = rNewproc // set address of new process
mov out1 = rOldproc // set address of old process
(pDiff) br.call.sptk brp = KxSwapProcess // call swap address space(NewProc, OldProc)
;;
//
// In new address space, if changed.
//
st1 [loc1] = loc0 // request (or clear) APC pend.
add rpT1 = PbContextSwitches, s0
add rpT2 = ThContextSwitches, s2
;;
//
// If the new thread has a kernel mode APC pending, then request an APC
// interrupt.
//
ld4 loc1 = [rpT1]
ld4 loc2 = [rpT2]
;;
//
// Increment context switch counters
//
cmp.ne pUsTh, pKrTh = zero, teb
add loc1 = loc1, zero, 1
add loc2 = loc2, zero, 1
;;
st4 [rpT1] = loc1 // increment # of context switches
st4 [rpT2] = loc2 // increment # of context switches
add rpT1 = SwFPSR+STACK_SCRATCH_AREA, sp
add rpT2 = SwPFS+STACK_SCRATCH_AREA, sp
;;
ld8 loc1 = [rpT1], SwRp-SwFPSR // restore brp and pfs
ld8 loc2 = [rpT2], SwPreds-SwPFS
;;
ld8 rT3 = [rpT1]
ld8 rT2 = [rpT2]
mov v0 = loc0 // set v0 = apc pending
(pUsTh) br.call.spnt brp = SwapContextIA32Restore
;;
//
// Note: at this point s0 = Prcb, s1 = previous thread, s2 = current thread
//
mov ar.fpsr = loc1
mov ar.pfs = loc2
mov brp = rT3
mov pr = rT2 // Restore preserved preds
#if 0
//
// Thierry 03/22/2000:
//
// The following memory synchronization of the local processor
// I-cache and D-cache because of I-stream modifications is not
// required if the modifying code is written following the NT
// Core Team specifications:
// - [Allocate VA]
// - Modify the code
// - Call FlushIntructionCache()
// -> calls KiSweepIcache[Range]()
// - Execute the code.
//
// The removal of this instruction eliminates a "> 100 cycle" stall.
//
sync.i
#endif // 0
;;
srlz.i
br.ret.sptk brp
NESTED_EXIT(SwapContext)
//++
//--------------------------------------------------------------------
// Routine:
//
// SwapContextIA32Save
//
// Routine Description:
//
// This function saves the IA32 context on the kernel stack.
// Called from SwapContext.
//
// Arguments:
//
// rOldSbase : old thread kstack base.
//
// Return value:
//
// None.
//
// Note:
//
// SwapContext registers context.
//
//--------------------------------------------------------------------
//--
LEAF_ENTRY(SwapContextIA32Save)
mov rAr1 = ar21 // IA32 FP control register FCR
;;
mov rAr2 = ar24 // IA32 EFLAG register
;;
mov rAr3 = ar25
;;
mov rAr4 = ar26
;;
//
// we may skip saving ar27 because it cannot be modified by user code
//
mov rT1 = ar30
;;
mov rT2 = ar28
;;
mov rT3 = ar29
;;
// these are separated out due to cache miss potential
add rpT1 = -ThreadStateSaveAreaLength+TsAppRegisters+TsAr21, rOldSbase
add rpT2 = -ThreadStateSaveAreaLength+TsAppRegisters+TsAr24, rOldSbase
;;
st8 [rpT1] = rAr1, TsAr25-TsAr21
st8 [rpT2] = rAr2, TsAr26-TsAr24
;;
st8 [rpT1] = rAr3, TsAr29-TsAr25
st8 [rpT2] = rAr4, TsAr28-TsAr26
;;
st8 [rpT2] = rT2, TsAr30-TsAr28
;;
st8 [rpT2] = rT1
st8 [rpT1] = rT3
br.ret.sptk.few.clr brp
LEAF_EXIT(SwapContextIA32Save)
//++
//--------------------------------------------------------------------
// Routine:
//
// SwapContextIA32Restore
//
// Routine Description:
//
// This function restores the IA32 registers context.
// Called from SwapContext.
//
// Arguments:
//
// s2 - Address of next thread object.
//
// Return value:
//
// None.
//
// Note:
//
// SwapContext registers context.
//
//--------------------------------------------------------------------
//--
LEAF_ENTRY(SwapContextIA32Restore)
add rpT1 = ThStackBase, s2
;;
ld8.nta rpT1 = [rpT1]
;;
add rpT2 = -ThreadStateSaveAreaLength+TsAppRegisters+TsAr21, rpT1
add rpT3 = -ThreadStateSaveAreaLength+TsAppRegisters+TsAr24, rpT1
;;
ld8.nta rAr1 = [rpT2], TsAr25-TsAr21
ld8.nta rAr2 = [rpT3], TsAr26-TsAr24
;;
ld8.nta rAr3 = [rpT2], TsAr27-TsAr25
ld8.nta rAr4 = [rpT3], TsAr28-TsAr26
;;
mov ar21 = rAr1
mov ar24 = rAr2
mov ar25 = rAr3
mov ar26 = rAr4
ld8.nta rAr1 = [rpT2], TsAr29-TsAr27
ld8.nta rAr2 = [rpT3], TsAr30-TsAr28
;;
ld8.nta rAr3 = [rpT2]
ld8.nta rAr4 = [rpT3]
;;
mov ar27 = rAr1
mov ar28 = rAr2
mov ar29 = rAr3
mov ar30 = rAr4
br.ret.sptk.few.clr brp
LEAF_EXIT(SwapContextIA32Restore)
SBTTL("Swap Process")
//++
//--------------------------------------------------------------------
//
// VOID
// KiSwapProcess (
// IN PKPROCESS NewProcess,
// IN PKPROCESS OldProcess
// )
//
// Routine Description:
//
// This function swaps the address space from one process to another by
// assigning a new region id, if necessary, and loading the fixed entry
// in the TB that maps the process page directory page. This routine follows
// the PowerPC design for handling RID wrap.
//
// On entry/exit:
//
// Interrupt enabled.
//
// Arguments:
//
// NewProcess (a0) - Supplies a pointer to a control object of type process
// which represents the new process that is switched to (32-bit address).
//
// OldProcess (a1) - Supplies a pointer to a control object of type process
// which represents the old process that is switched from (32-bit address).
//
// Return Value:
//
// None.
//
//--------------------------------------------------------------------
//--
NESTED_ENTRY(KiSwapProcess)
NESTED_SETUP(2,3,3,0)
PROLOGUE_END
//
// Register aliases
//
rNewProc = a0
rOldProc = a1
rpCSLock = loc2
rpT1 = t0
rpT2 = t1
rProcSet = t2
rNewActive= t3
rOldActive= t4
rMasterSeq= t5
rNewSeq = t6
rOldPsrL = t7
rVa = t8
rPDE0 = t9 // PDE for page directory page 0
rVa2 = t10
rSessionBase = t11
rSessionInfo = t12
rT1 = t13
rT2 = t14
//
// KiSwapProcess must get the context swap lock
// KxSwapProcess is called from SwapContext with the lock held
//
#if !defined(NT_UP)
movl rpT1 = KiPcr+PcPrcb
;;
ld8 rpT1 = [rpT1]
;;
add out0 = (LockQueueContextSwapLock * 16) + PbLockQueue, rpT1
br.call.sptk brp = KeAcquireQueuedSpinLockAtDpcLevel
;;
br.sptk Ksp_Continue
#endif // !defined(NT_UP)
;;
ALTERNATE_ENTRY(KxSwapProcess)
NESTED_SETUP(2,3,3,0)
PROLOGUE_END
//
// Clear the processor set member number in the old process and set the
// processor member number in the new process.
//
Ksp_Continue:
#if !defined(NT_UP)
add rpT2 = PrActiveProcessors, rOldProc // -> old active processor set
movl rpT1 = KiPcr + PcSetMember // -> processor set member
;;
ld4 rProcSet= [rpT1] // rProcSet.4 = processor set member
add rpT1 = PrActiveProcessors, rNewProc // -> new active processor set
;;
ld4 rNewActive = [rpT1] // rNewActive.4 = new active processor set
ld4 rOldActive = [rpT2] // rOldActive.4 = old active processor set
;;
or rNewActive = rNewActive,rProcSet // set processor member in new set
xor rOldActive = rOldActive,rProcSet // clear processor member in old set
;;
st4 [rpT1] = rNewActive // set new active processor set
st4 [rpT2] = rOldActive // set old active processor set
#endif // !defined(NT_UP)
//
// If the process sequence number matches the system sequence number, then
// use the process RID. Otherwise, allocate a new process RID.
//
// N.B. KiMasterRid, KiMasterSequence are changed only when holding the
// KiContextSwapLock.
//
add rT2 = PrSessionMapInfo, rNewProc
add out0 = PrProcessRegion, rNewProc
;;
ld8 out1 = [rT2]
br.call.sptk brp = KiSyncNewRegionId
;;
//
// Switch address space to new process
// v0 = rRid = new process rid
//
fwb // hint to flush write buffers
FAST_DISABLE_INTERRUPTS
add rpT1 = PrDirectoryTableBase, rNewProc
movl rVa = KiPcr+PcPdeUtbase
add rpT2 = PrSessionParentBase, rNewProc
movl rVa2 = KiPcr+PcPdeStbase
;;
ld8.nta rPDE0 = [rpT1] // rPDE0 = Page directory page 0
ld8.nta rSessionBase = [rpT2]
ld8.nta rVa = [rVa]
ld8.nta rVa2 = [rVa2]
;;
//
// To access IFA, ITDR registers, PSR.ic bit must be 0. Otherwise,
// it causes an illegal operation fault. While PSR.ic=0, any
// interruption can not be afforded. Make sure there will be no
// TLB miss and no interrupt coming in during this period.
//
rsm 1 << PSR_IC // PSR.ic=0
;;
srlz.d // must serialize
mov rT1 = PAGE_SHIFT << IDTR_PS // load page size field for IDTR
;;
mov cr.itir = rT1 // set up IDTR for dirbase
ptr.d rVa, rT1 // remove DTR for user space
;;
mov cr.ifa = rVa // set up IFA for dirbase vaddr
mov rT2 = DTR_UTBASE_INDEX
;;
itr.d dtr[rT2] = rPDE0 // insert PDE0 to DTR
;;
ptr.d rVa2, rT1 // remove DTR for session
;; // to avoid a overlapping error
mov cr.ifa = rVa2
mov rT2 = DTR_STBASE_INDEX
;;
itr.d dtr[rT2] = rSessionBase // insert the root for session space
;;
ssm 1 << PSR_IC // PSR.ic=1
;;
srlz.i // must I serialize
#if DBG
mov t0 = PbProcessorState+KpsSpecialRegisters+KsTrD0+(8*DTR_UTBASE_INDEX)
movl t3 = KiPcr + PcPrcb
;;
ld8 t3 = [t3]
mov t1 = PbProcessorState+KpsSpecialRegisters+KsTrD0+(8*DTR_STBASE_INDEX)
;;
add t0 = t3, t0
add t1 = t3, t1
;;
st8 [t0] = rPDE0
st8 [t1] = rSessionBase
;;
#endif
FAST_ENABLE_INTERRUPTS
//
// Now make sure branch history is enabled for non wow processes
// and disabled for wow processes
//
add t1 = @gprel(KiVectorLogMask), gp
;;
ld8 t1 = [t1]
;;
cmp.eq pt0 = t1, r0
(pt0) br.cond.sptk SkipBranchHistory
mov t1 = 3
;;
mov t2 = cpuid[t1]
add t3 = PrWow64Process, rNewProc
;;
extr.u t2 = t2, 24, 8
ld4 t4 = [t3];
;;
cmp.ne pt1 = 7, t2
;;
mov t1 = 675
(pt1) br.dpnt SkipBranchHistory
;;
mov t2 = msr[t1]
cmp.eq pt1,pt2 = zero, t4 // Wow64 is non-zero
;;
(pt1) mov t3 = 2 // Enable the HB for ia64 procs
(pt2) mov t3 = 256 // Disable the HB for wow64 procs
;;
dep t2 = t3, t2, 0, 9 // Disable the HB for wow64 procs
;;
mov msr[t1] = t2;
;;
SkipBranchHistory:
#if !defined(NT_UP)
//
// Can now release the context swap lock
//
movl rpT1 = KiPcr+PcPrcb
;;
ld8 rpT1 = [rpT1]
;;
add out0 = (LockQueueContextSwapLock * 16) + PbLockQueue, rpT1
br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
;;
#endif // !defined(NT_UP)
NESTED_RETURN
NESTED_EXIT(KiSwapProcess)
SBTTL("Retire Deferred Procedure Call List")
//++
// Routine:
//
// VOID
// KiRetireDpcList (
// PKPRCB Prcb,
// )
//
// Routine Description:
//
// This routine is called to retire the specified deferred procedure
// call list. DPC routines are called using the idle thread (current)
// stack.
//
// N.B. Interrupts must be disabled on entry to this routine. Control is returned
// to the caller with the same conditions true.
//
// Arguments:
//
// a0 - Address of the current PRCB.
//
// Return value:
//
// None.
//
//--
NESTED_ENTRY(KiRetireDpcList)
NESTED_SETUP(1,2,4,0)
PROLOGUE_END
Krdl_Restart:
add t0 = PbDpcQueueDepth, a0
add t1 = PbDpcRoutineActive, a0
add t2 = PbDpcLock, a0
;;
ld4 t4 = [t0]
add t3 = PbDpcListHead+LsFlink, a0
;;
Krdl_Restart2:
cmp4.eq pt1 = zero, t4
st4 [t1] = t4
(pt1) br.spnt Krdl_Exit
;;
#if !defined(NT_UP)
ACQUIRE_SPINLOCK(t2, a0, Krdl_20)
#endif // !defined(NT_UP)
ld4 t4 = [t0]
LDPTR (t5, t3) // -> first DPC entry
;;
cmp4.eq pt1, pt2 = zero, t4
;;
(pt2) add t10 = LsFlink, t5
(pt2) add out0 = -DpDpcListEntry, t5
(pt1) br.spnt Krdl_Unlock
;;
LDPTR (t6, t10)
add t11 = DpDeferredRoutine, out0
add t12 = DpSystemArgument1, out0
;;
//
// Setup call to DPC routine
//
// arguments are:
// dpc object address (out0)
// deferred context (out1)
// system argument 1 (out2)
// system argument 2 (out3)
//
// N.B. the arguments must be loaded from the DPC object BEFORE
// the inserted flag is cleared to prevent the object being
// overwritten before its time.
//
ld8.nt1 t13 = [t11], DpDeferredContext-DpDeferredRoutine
ld8.nt1 out2 = [t12], DpSystemArgument2-DpSystemArgument1
;;
ld8.nt1 out1 = [t11], DpLock-DpDeferredContext
ld8.nt1 out3 = [t12]
add t4 = -1, t4
STPTRINC (t3, t6, -LsFlink)
ld8.nt1 t14 = [t13], 8
add t15 = LsBlink, t6
;;
ld8.nt1 gp = [t13]
STPTR (t15, t3)
STPTR (t11, zero)
st4 [t0] = t4
#if !defined(NT_UP)
RELEASE_SPINLOCK(t2) // set spin lock not owned
#endif //!defined(NT_UP)
FAST_ENABLE_INTERRUPTS
mov bt0 = t14
br.call.sptk.few.clr brp = bt0 // call DPC routine
;;
//
// Check to determine if any more DPCs are available to process.
//
FAST_DISABLE_INTERRUPTS
br Krdl_Restart
;;
//
// 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.
//
Krdl_Unlock:
#if !defined(NT_UP)
add t2 = PbDpcLock, a0
;;
RELEASE_SPINLOCK(t2)
#endif // !defined(NT_UP)
Krdl_Exit:
add t0 = PbDpcQueueDepth, a0
add t1 = PbDpcRoutineActive, a0
add out0 = PbDpcInterruptRequested, a0
;;
st4.nta [t1] = zero
st4.rel.nta [out0] = zero
add t2 = PbDpcLock, a0
ld4 t4 = [t0]
add t3 = PbDpcListHead+LsFlink, a0
;;
cmp4.eq pt1, pt2 = zero, t4
(pt2) br.spnt Krdl_Restart2
;;
NESTED_RETURN
NESTED_EXIT(KiRetireDpcList)
SBTTL("Dispatch Interrupt")
//++
//--------------------------------------------------------------------
// Routine:
//
// KiDispatchInterrupt
//
// 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.
//
// N.B. On entry to this routine the volatile states (excluding high
// floating point register set) have been saved.
//
// On entry:
//
// sp - points to stack scratch area.
//
// Arguments:
//
// None
//
// Return Value:
//
// None.
//--------------------------------------------------------------------
//--
NESTED_ENTRY(KiDispatchInterrupt)
PROLOGUE_BEGIN
.regstk 0, 4, 2, 0
alloc t16 = ar.pfs, 0, 4, 2, 0
.save rp, loc0
mov loc0 = brp
.fframe SwitchFrameLength
add sp = -SwitchFrameLength, sp
;;
.save ar.unat, loc1
mov loc1 = ar.unat
add t0 = ExFltS19+SwExFrame+STACK_SCRATCH_AREA, sp
add t1 = ExFltS18+SwExFrame+STACK_SCRATCH_AREA, sp
;;
.save.gf 0x0, 0xC0000
stf.spill [t0] = fs19, ExFltS17-ExFltS19
stf.spill [t1] = fs18, ExFltS16-ExFltS18
;;
.save.gf 0x0, 0x30000
stf.spill [t0] = fs17, ExFltS15-ExFltS17
stf.spill [t1] = fs16, ExFltS14-ExFltS16
mov t10 = bs4
;;
.save.gf 0x0, 0xC000
stf.spill [t0] = fs15, ExFltS13-ExFltS15
stf.spill [t1] = fs14, ExFltS12-ExFltS14
mov t11 = bs3
;;
.save.gf 0x0, 0x3000
stf.spill [t0] = fs13, ExFltS11-ExFltS13
stf.spill [t1] = fs12, ExFltS10-ExFltS12
mov t12 = bs2
;;
.save.gf 0x0, 0xC00
stf.spill [t0] = fs11, ExFltS9-ExFltS11
stf.spill [t1] = fs10, ExFltS8-ExFltS10
mov t13 = bs1
;;
.save.gf 0x0, 0x300
stf.spill [t0] = fs9, ExFltS7-ExFltS9
stf.spill [t1] = fs8, ExFltS6-ExFltS8
mov t14 = bs0
;;
.save.gf 0x0, 0xC0
stf.spill [t0] = fs7, ExFltS5-ExFltS7
stf.spill [t1] = fs6, ExFltS4-ExFltS6
mov t15 = ar.lc
;;
.save.gf 0x0, 0x30
stf.spill [t0] = fs5, ExFltS3-ExFltS5
stf.spill [t1] = fs4, ExFltS2-ExFltS4
;;
.save.f 0xC
stf.spill [t0] = fs3, ExFltS1-ExFltS3 // save fs3
stf.spill [t1] = fs2, ExFltS0-ExFltS2 // save fs2
;;
.save.f 0x3
stf.spill [t0] = fs1, ExBrS4-ExFltS1 // save fs1
stf.spill [t1] = fs0, ExBrS3-ExFltS0 // save fs0
;;
.save.b 0x18
st8 [t0] = t10, ExBrS2-ExBrS4 // save bs4
st8 [t1] = t11, ExBrS1-ExBrS3 // save bs3
;;
.save.b 0x6
st8 [t0] = t12, ExBrS0-ExBrS2 // save bs2
st8 [t1] = t13, ExIntS2-ExBrS1 // save bs1
;;
.save.b 0x1
st8 [t0] = t14, ExIntS3-ExBrS0 // save bs0
;;
.save.gf 0xC, 0x0
.mem.offset 0,0
st8.spill [t0] = s3, ExIntS1-ExIntS3 // save s3
.mem.offset 8,0
st8.spill [t1] = s2, ExIntS0-ExIntS2 // save s2
;;
.save.gf 0x3, 0x0
.mem.offset 0,0
st8.spill [t0] = s1, ExApLC-ExIntS1 // save s1
.mem.offset 8,0
st8.spill [t1] = s0, ExApEC-ExIntS0 // save s0
;;
.savepsp ar.pfs, ExceptionFrameLength-ExApEC-STACK_SCRATCH_AREA
st8 [t1] = t16, ExIntNats-ExApEC
mov t4 = ar.unat // captured Nats of s0-s3
;;
.savepsp ar.lc, ExceptionFrameLength-ExApLC-STACK_SCRATCH_AREA
st8 [t0] = t15
.savepsp @priunat, ExceptionFrameLength-ExIntNats-STACK_SCRATCH_AREA
st8 [t1] = t4 // save Nats of s0-s3
PROLOGUE_END
//
// Register aliases
//
rPrcb = loc2
rKerGP = loc3
rpT1 = t0
rpT2 = t1
rT1 = t2
rT2 = t3
rpDPLock = t4 // pointer to dispatcher lock
pNoTh = pt1 // No next thread to run
pNext = pt2 // next thread not null
pNull = pt3 // no thread available
pOwned = pt4 // dispatcher lock already owned
pNotOwned = pt5
pQEnd = pt6 // quantum end request pending
pNoQEnd = pt7 // no quantum end request pending
//
// Increment the dispatch interrupt count
//
mov rKerGP = gp // save gp
movl rPrcb = KiPcr + PcPrcb
;;
LDPTR (rPrcb, rPrcb) // rPrcb -> Prcb
;;
add rpT1 = PbDispatchInterruptCount, rPrcb
;;
ld4 rT1 = [rpT1]
;;
add rT1 = rT1, zero, 1
;;
st4 [rpT1] = rT1
// **** TBD **** use alpha optimization to first check Dpc Q depth
//
// Process the DPC list
//
Kdi_PollDpcList:
//
// Process the deferred procedure call list.
//
FAST_ENABLE_INTERRUPTS
;;
srlz.d
//
// **** TBD ***** No stack switch as in alpha, mips...
// Save current initial stack address and set new initial stack address.
//
FAST_DISABLE_INTERRUPTS
mov out0 = rPrcb
br.call.sptk brp = KiRetireDpcList
;;
//
// Check to determine if quantum end has occured.
//
// 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.
//
FAST_ENABLE_INTERRUPTS
add rpT1 = PbQuantumEnd, rPrcb
;;
ld4 rT1 = [rpT1] // get quantum end indicator
;;
cmp4.ne pQEnd, pNoQEnd = rT1, zero // if zero, no quantum end reqs
mov gp = rKerGP // restore gp
;;
(pQEnd) st4 [rpT1] = zero // clear quantum end indicator
(pNoQEnd) br.cond.sptk Kdi_NoQuantumEnd
(pQEnd) br.call.spnt brp = KiQuantumEnd // call KiQuantumEnd (C code)
;;
cmp4.eq pNoTh, pNext = v0, zero // pNoTh = no next thread
(pNoTh) br.dpnt Kdi_Exit // br to exit if no next thread
(pNext) br.dpnt Kdi_Swap // br to swap to next thread
//
// If no quantum end requests:
// Check to determine if a new thread has been selected for execution on
// this processor.
//
Kdi_NoQuantumEnd:
add rpT2 = PbNextThread, rPrcb
;;
LDPTR (rT1, rpT2) // rT1 = address of next thread object
;;
cmp.eq pNull = rT1, zero // pNull => no thread selected
(pNull) br.dpnt Kdi_Exit // exit if no thread selected
#if !defined(NT_UP)
//
// try to acquire the dispatcher database lock.
//
mov out0 = LockQueueDispatcherLock
movl out1 = KiPcr+PcSystemReserved+8
br.call.sptk brp = KeTryToAcquireQueuedSpinLockRaiseToSynch
;;
cmp.ne pOwned, pNotOwned = TRUE, v0 // pOwned = 1 if not free
(pOwned) br.dpnt Kdi_PollDpcList // br out if owned
;;
#else
mov rT1 = SYNCH_LEVEL
;;
SET_IRQL (rT1)
#endif // !defined(NT_UP)
//
// Reread address of next thread object since it is possible for it to
// change in a multiprocessor system.
//
Kdi_Swap:
add rpT2 = PbNextThread, rPrcb // -> next thread
movl rpT1 = KiPcr + PcCurrentThread
;;
LDPTR (s1, rpT1) // current thread object
LDPTR (s2, rpT2) // next thread object
add rpT1 = PbCurrentThread, rPrcb
;;
//
// Reready current thread for execution and swap context to the selected
// thread.
//
// Note: Set IdleSwapBlock in the current thread so no idle processor
// can switch to this processor before it is removed from the current
// processor.
//
STPTR (rpT2, zero) // clear addr of next thread
add out0 = ThIdleSwapBlock, s1 // block swap from idle
mov rT1 = 1
;;
STPTR (rpT1, s2) // set addr of current thread
st1 [out0] = rT1, -ThIdleSwapBlock// set addr of previous thread
br.call.sptk brp = KiReadyThread // call KiReadyThread(OldTh)
;;
mov s0 = rPrcb // setup call
cmp.ne pt0 = zero, zero // no need to lock context swap
br.call.sptk brp = SwapContext // call SwapContext(Prcb, OldTh, NewTh)
;;
//
// Restore saved registers, and return.
//
add out0 = STACK_SCRATCH_AREA+SwExFrame, sp
br.call.sptk brp = KiRestoreExceptionFrame
;;
Kdi_Exit:
add rpT1 = ExApEC+SwExFrame+STACK_SCRATCH_AREA, sp
;;
ld8 rT1 = [rpT1]
mov brp = loc0
;;
mov ar.unat = loc1
mov ar.pfs = rT1
.restore
add sp = SwitchFrameLength, sp
br.ret.sptk brp
NESTED_EXIT(KiDispatchInterrupt)