windows-nt/Source/XPSP1/NT/base/ntos/ke/alpha/ctxsw.s
2020-09-26 16:20:57 +08:00

1249 lines
44 KiB
ArmAsm
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// TITLE("Context Swap")
//++
//
// Copyright (c) 1991 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// ctxsw.s
//
// Abstract:
//
// This module implements the ALPHA machine dependent code necessary to
// field the dispatch interrupt and to perform kernel initiated context
// switching.
//
// Author:
//
// David N. Cutler (davec) 1-Apr-1991
// Joe Notarangelo 05-Jun-1992
//
// Environment:
//
// Kernel mode only, IRQL DISPATCH_LEVEL.
//
// Revision History:
//
//--
#include "ksalpha.h"
// #define _COLLECT_SWITCH_DATA_ 1
SBTTL("Unlock Dispatcher Database")
//++
//
// VOID
// KiUnlockDispatcherDatabase (
// IN KIRQL OldIrql
// )
//
// Routine Description:
//
// This routine is entered at IRQL DISPATCH_LEVEL with the dispatcher
// database locked. Ifs 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 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 (a0) - Supplies the IRQL when the dispatcher database
// lock was acquired.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiUnlockDispatcherDatabase)
//
// Check if a thread has been scheduled to execute on the current processor
//
GET_PROCESSOR_CONTROL_BLOCK_BASE // get prcb address
cmpult a0, DISPATCH_LEVEL, t1 // check if IRQL below dispatch level
LDP t2, PbNextThread(v0) // get next thread address
bne t2, 30f // if ne, next thread selected
//
// Release dispatcher database lock, restore IRQL to its previous level
// and return
//
10: //
#if !defined(NT_UP)
bis a0, zero, a1 // set old IRQL value
ldil a0, LockQueueDispatcherLock // set lock queue number
br zero, KeReleaseQueuedSpinLock // release dispatcher lock
#else
SWAP_IRQL // lower IRQL
ret zero, (ra) // return
#endif
//
// A new thread has been selected to run on the current processor, but
// the new IRQL is not below dispatch level. If the current processor is
// not executing a DPC, then request a dispatch interrupt on the current
// processor before releasing the dispatcher lock and restoring IRQL.
//
20: ldl t2, PbDpcRoutineActive(v0) // check if DPC routine active
bne t2,10b // if ne, DPC routine active
#if !defined(NT_UP)
bis a0, zero, t0 // save old IRQL value
ldil a0, DISPATCH_LEVEL // set interrupt request level
REQUEST_SOFTWARE_INTERRUPT // request DPC interrupt
ldil a0, LockQueueDispatcherLock // set lock queue number
bis t0, zero, a1 // set old IRQL value
br zero, KeReleaseQueuedSpinLock // release dispatcher lock
#else
SWAP_IRQL // lower IRQL
ldil a0, DISPATCH_LEVEL // set interrupt request level
REQUEST_SOFTWARE_INTERRUPT // request DPC interrupt
ret zero, (ra) // return
#endif
//
// 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.
//
30: beq t1, 20b // if eq, not below dispatch level
.end KiUnlockDispatcherDatabase
//
// N.B. This routine is carefully written as a nested function.
// Control only reaches this routine from above.
//
// v0 contains the address of PRCB
// t2 contains the next thread
//
NESTED_ENTRY(KxUnlockDispatcherDatabase, ExceptionFrameLength, zero)
lda sp, -ExceptionFrameLength(sp) // allocate context frame
stq ra, ExIntRa(sp) // save return address
stq s0, ExIntS0(sp) // save integer registers
stq s1, ExIntS1(sp) //
stq s2, ExIntS2(sp) //
stq s3, ExIntS3(sp) //
stq s4, ExIntS4(sp) //
stq s5, ExIntS5(sp) //
stq fp, ExIntFp(sp) //
PROLOGUE_END
bis v0, zero, s0 // set address of PRCB
GET_CURRENT_THREAD // get current thread address
bis v0, zero, s1 // set current thread address
bis t2, zero, s2 // set next thread address
StoreByte(a0, ThWaitIrql(s1)) // save previous IRQL
STP zero, PbNextThread(s0) // clear next thread address
//
// Reready current thread for execution and swap context to the selected thread.
//
// N.B. The return from the call to swap context is directly to the swap
// thread exit.
//
bis s1, zero, a0 // set previous thread address
STP s2, PbCurrentThread(s0) // set address of current thread object
bsr ra, KiReadyThread // reready thread for execution
lda ra, KiSwapThreadExit // set return address
jmp SwapContext // swap context
.end KxUnlockDispatcherDatabase
SBTTL("Swap Thread")
//++
//
// INT_PTR
// 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.
//
// Return Value:
//
// Wait completion status (v0).
//
//--
NESTED_ENTRY(KiSwapThread, ExceptionFrameLength, zero)
lda sp, -ExceptionFrameLength(sp) // allocate context frame
stq ra, ExIntRa(sp) // save return address
stq s0, ExIntS0(sp) // save integer registers s0 - s5
stq s1, ExIntS1(sp) //
stq s2, ExIntS2(sp) //
stq s3, ExIntS3(sp) //
stq s4, ExIntS4(sp) //
stq s5, ExIntS5(sp) //
stq fp, ExIntFp(sp) // save fp
PROLOGUE_END
GET_PROCESSOR_CONTROL_REGION_BASE // get pcr address
bis v0, zero, s3 // save PCR address
LDP s0, PcPrcb(s3) // get address of PRCB
ldl s5, KiReadySummary // get ready summary
zapnot s5, 0x0f, t0 // clear high 32 bits
GET_CURRENT_THREAD // get current thread address
bis v0, zero, s1 // set current thread address
LDP s2, PbNextThread(s0) // get next thread address
#if !defined(NT_UP)
ldl fp, PcSetMember(s3) // get processor affinity mask
#endif
STP zero, PbNextThread(s0) // zero next thread address
bne s2, 120f // if ne, next thread selected
//
// Find the highest nibble in the ready summary that contains a set bit
// and left justify so the nibble is in bits <63:60>.
//
cmpbge zero, t0, s4 // generate mask of clear bytes
ldil t2, 7 // set initial bit number
srl s4, 1, t5 // check bits <15:8>
cmovlbc t5, 15, t2 // if bit clear, bit number = 15
srl s4, 2, t6 // check bits <23:16>
cmovlbc t6, 23, t2 // if bit clear, bit number = 23
srl s4, 3, t7 // check bits <31:24>
cmovlbc t7, 31, t2 // if bit clear, bit number = 31
bic t2, 7, t3 // get byte shift from priority
srl t0, t3, s4 // isolate highest nonzero byte
and s4, 0xf0, t4 // check if high nibble nonzero
subq t2, 4, t1 // compute bit number if high nibble zero
cmoveq t4, t1, t2 // if eq, high nibble zero
10: ornot zero, t2, t4 // compute left justify shift count
sll t0, t4, t0 // left justify ready summary to nibble
//
// If the next bit is set in the ready summary, then scan the corresponding
// dispatcher ready queue.
//
30: blt t0, 50f // if ltz, queue contains an entry
31: sll t0, 1, t0 // position next ready summary bit
subq t2, 1, t2 // decrement ready queue priority
bne t0, 30b // if ne, more queues to scan
//
// All ready queues were scanned without finding a runnable thread so
// default to the idle thread and set the appropirate bit in idle summary.
//
#if defined(_COLLECT_SWITCH_DATA_)
lda t0, KeThreadSwitchCounters // get switch counters address
ldl v0, TwSwitchToIdle(t0) // increment switch to idle count
addl v0, 1, v0 //
stl v0, TwSwitchToIdle(t0) //
#endif
#if defined(NT_UP)
ldil t0, 1 // get current idle summary
#else
ldl t0, KiIdleSummary // get current idle summary
bis t0, fp, t0 // set member bit in idle summary
#endif
stl t0, KiIdleSummary // set new idle summary
LDP s2, PbIdleThread(s0) // set address of idle thread
br zero, 120f // swap context
//
// Compute address of ready list head and scan reday queue for a runnable
// thread.
//
50: lda t1, KiDispatcherReadyListHead // get ready ready base address
#if defined(_AXP64_)
sll t2, 4, s4 // compute ready queue address
addq s4, t1, s4 //
#else
s8addl t2, t1, s4 // compute ready queue address
#endif
LDP t4, LsFlink(s4) // get address of next queue entry
55: SUBP t4, ThWaitListEntry, s2 // compute address of thread object
//
// If the thread can execute on the current processor, then remove it from
// the dispatcher ready queue.
//
#if !defined(NT_UP)
ldl t5, ThAffinity(s2) // get thread affinity
and t5, fp, t6 // the current processor
bne t6, 60f // if ne, thread affinity compatible
LDP t4, LsFlink(t4) // get address of next entry
cmpeq t4, s4, t1 // check for end of list
beq t1, 55b // if eq, not end of list
br zero, 31b //
//
// If the thread last ran on the current processor, the processor is the
// ideal processor for the thread, the thread has been waiting for longer
// than a quantum, ot its priority is greater than low realtime plus 9,
// then select the thread. Otherwise, an attempt is made to find a more
// appropriate candidate.
//
60: ldq_u t1, PcNumber(s3) // get current processor number
extbl t1, PcNumber % 8, t12 //
ldq_u t11, ThNextProcessor(s2) // get last processor number
extbl t11, ThNextProcessor % 8, t9 //
cmpeq t9, t12, t5 // check thread's last processor
bne t5, 110f // if eq, last processor match
ldq_u t6, ThIdealProcessor(s2) // get thread's ideal processor number
extbl t6, ThIdealProcessor % 8, a3 //
cmpeq a3, t12, t8 // check thread's ideal processor
bne t8, 100f // if eq, ideal processor match
ldl t6, KeTickCount // get low part of tick count
ldl t7, ThWaitTime(s2) // get time of thread ready
subq t6, t7, t8 // compute length of wait
cmpult t8, READY_SKIP_QUANTUM + 1, t1 // check if wait time exceeded
cmpult t2, LOW_REALTIME_PRIORITY + 9, t3 // check if priority in range
and t1, t3, v0 // check if priority and time match
beq v0, 100f // if eq, select this thread
//
// Search forward in the ready queue until the end of the list is reached
// or a more appropriate thread is found.
//
LDP t7, LsFlink(t4) // get address of next entry
80: cmpeq t7, s4, t1 // if eq, end of list
bne t1, 100f // select original thread
SUBP t7, ThWaitListEntry, a0 // compute address of thread object
ldl a2, ThAffinity(a0) // get thread affinity
and a2, fp, t1 // check for compatibile thread affinity
beq t1, 85f // if eq, thread affinity not compatible
ldq_u t5, ThNextProcessor(a0) // get last processor number
extbl t5, ThNextProcessor % 8, t9 //
cmpeq t9, t12, t10 // check if last processor number match
bne t10, 90f // if ne, last processor match
ldq_u a1, ThIdealProcessor(a0) // get ideal processor number
extbl a1, ThIdealProcessor % 8, a3 //
cmpeq a3, t12, t10 // check if ideal processor match
bne t10, 90f // if ne, ideal processor match
85: ldl t8, ThWaitTime(a0) // get time of thread ready
LDP t7, LsFlink(t7) // get address of next entry
subq t6, t8, t8 // compute length of wait
cmpult t8, READY_SKIP_QUANTUM + 1, t5 // check if wait time exceeded
bne t5, 80b // if ne, wait time not exceeded
br zero, 100f // select original thread
//
// Last processor or ideal processor match.
//
90: bis a0, zero, s2 // set thread address
bis t7, zero, t4 // set list entry address
bis t5, zero, t11 // copy last processor data
100: insbl t12, ThNextProcessor % 8, t8 // move next processor into position
mskbl t11, ThNextProcessor % 8, t5 // mask next processor position
bis t8, t5, t6 // merge
stq_u t6, ThNextProcessor(s2) // update next processor
110: //
#if defined(_COLLECT_SWITCH_DATA_)
ldq_u t5, ThNextProcessor(s2) // get last processor number
extbl t5, ThNextProcessor % 8, t9 //
ldq_u a1, ThIdealProcessor(s2) // get ideal processor number
extbl a1, ThIdealProcessor % 8, a3 //
lda t0, KeThreadSwitchCounters + TwFindAny // compute address of Any counter
ADDP t0, TwFindIdeal-TwFindAny, t1 // compute address of Ideal counter
cmpeq t9, t12, t7 // if eq, last processor match
ADDP t0, TwFindLast-TwFindAny, t6 // compute address of Last counter
cmpeq a3, t12, t5 // check if ideal processor match
cmovne t7, t6, t0 // if last match, use last counter
cmovne t5, t1, t0 // if ideal match, use ideal counter
ldl v0, 0(t0) // increment counter
addl v0, 1, v0 //
stl v0, 0(t0) //
#endif
#endif
LDP t5, LsFlink(t4) // get list entry forward link
LDP t6, LsBlink(t4) // get list entry backward link
STP t5, LsFlink(t6) // set forward link in previous entry
STP t6, LsBlink(t5) // set backward link in next entry
cmpeq t6, t5, t7 // if eq, list is empty
beq t7, 120f //
ldil t1, 1 // compute ready summary set member
sll t1, t2, t1 //
xor t1, s5, t1 // clear member bit in ready summary
stl t1, KiReadySummary //
//
// Swap context to the next thread
//
120: STP s2, PbCurrentThread(s0) // set address of current thread object
bsr ra, SwapContext // swap context
//
// Lower IRQL, deallocate context frame, and return wait completion status.
//
// N.B. SwapContext releases the dispatcher database lock.
//
// N.B. The register v0 contains the complement of the kernel APC pending state.
//
// N.B. The register s2 contains the address of the new thread.
//
ALTERNATE_ENTRY(KiSwapThreadExit)
LDP s1, ThWaitStatus(s2) // get wait completion status
ldq_u t1, ThWaitIrql(s2) // get original IRQL
extbl t1, ThWaitIrql % 8, a0 //
bis v0, a0, t3 // check if APC pending and IRQL is zero
bne t3, 10f // if ne, APC not pending or IRQL not zero
//
// Lower IRQL to APC level and dispatch APC interrupt.
//
ldil a0, APC_LEVEL // lower IRQL to APC level
SWAP_IRQL //
ldil a0, APC_LEVEL // clear software interrupt
DEASSERT_SOFTWARE_INTERRUPT //
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
ldl t1, PbApcBypassCount(v0) // increment the APC bypass count
addl t1, 1, t2 //
stl t2, PbApcBypassCount(v0) //
bis zero, zero, a0 // set previous mode to kernel
bis zero, zero, a1 // set exception frame address
bis zero, zero, a2 // set trap frame address
bsr ra, KiDeliverApc // deliver kernel mode APC
bis zero, zero, a0 // set original wait IRQL
//
// Lower IRQL to wait level, set return status, restore registers, and
// return.
//
10: SWAP_IRQL // lower IRQL to wait level
bis s1, zero, v0 // set return status value
ldq ra, ExIntRa(sp) // restore return address
ldq s0, ExIntS0(sp) // restore int regs S0-S5
ldq s1, ExIntS1(sp) //
ldq s2, ExIntS2(sp) //
ldq s3, ExIntS3(sp) //
ldq s4, ExIntS4(sp) //
ldq s5, ExIntS5(sp) //
ldq fp, ExIntFp(sp) // restore fp
lda sp, ExceptionFrameLength(sp) // deallocate context frame
ret zero, (ra) // return
.end KiSwapThread
SBTTL("Dispatch Interrupt")
//++
//
// 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 only the volatile integer registers have
// been saved. The volatile floating point registers have not been saved.
//
// Arguments:
//
// fp - Supplies a pointer to the base of a trap frame.
//
// Return Value:
//
// None.
//
//--
.struct 0
DpSp: .space 8 // saved stack pointer
DpBs: .space 8 // base of previous stack
DpLim: .space 8 // limit of previous stack
.space 8 // pad to octaword
DpcFrameLength: // DPC frame length
NESTED_ENTRY(KiDispatchInterrupt, ExceptionFrameLength, zero)
lda sp, -ExceptionFrameLength(sp) // allocate exception frame
stq ra, ExIntRa(sp) // save return address
//
// Save the saved registers in case we context switch to a new thread.
//
// N.B. - If we don't context switch then we need only restore those
// registers that we use in this routine, currently those registers
// are s0, s1
//
stq s0, ExIntS0(sp) // save integer registers s0-s6
stq s1, ExIntS1(sp) //
stq s2, ExIntS2(sp) //
stq s3, ExIntS3(sp) //
stq s4, ExIntS4(sp) //
stq s5, ExIntS5(sp) //
stq fp, ExIntFp(sp) //
PROLOGUE_END
//
// Increment the dispatch interrupt count
//
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
bis v0, zero, s0 // save current prcb address
ldl t2, PbDispatchInterruptCount(s0) // increment dispatch interrupt count
addl t2, 1, t3 //
stl t3, PbDispatchInterruptCount(s0) //
//
// Process the DPC List with interrupts off.
//
ldl t0, PbDpcQueueDepth(s0) // get current queue depth
beq t0, 20f // if eq, no DPCs pending
//
// Save current initial kernel stack address and set new initial kernel stack
// address.
//
PollDpcList: //
DISABLE_INTERRUPTS // disable interrupts
GET_PROCESSOR_CONTROL_REGION_BASE // get current PCR address
LDP a0, PcDpcStack(v0) // get address of DPC stack
lda t0, -DpcFrameLength(a0) // allocate DPC frame
LDP t4, PbCurrentThread(s0) // get current thread
LDP t5, ThStackLimit(t4) // get current stack limit
stq sp, DpSp(t0) // save old stack pointer
stq t5, DpLim(t0) // save old stack limit
SUBP a0, KERNEL_STACK_SIZE, t5 // compute new stack limit
STP t5, ThStackLimit(t4) // store new stack limit
bis t0, t0, sp // set new stack pointer
SET_INITIAL_KERNEL_STACK // set new initial kernel stack
stq v0, DpBs(sp) // save previous initial stack
bsr ra, KiRetireDpcList // process the DPC list
//
// Switch back to previous stack and restore the initial stack limit.
//
ldq a0, DpBs(sp) // get previous initial stack address
ldq t5, DpLim(sp) // get old stack limit
SET_INITIAL_KERNEL_STACK // reset current initial stack
ldq sp, DpSp(sp) // restore stack pointer
LDP t4, PbCurrentThread(s0) // get current thread
STP t5, ThStackLimit(t4) // restore stack limit
ENABLE_INTERRUPTS // enable interrupts
//
// Check to determine if quantum end has occured.
//
20: ldl t0, PbQuantumEnd(s0) // get quantum end indicator
beq t0, 25f // if eq, no quantum end request
stl zero, PbQuantumEnd(s0) // clear quantum end indicator
bsr ra, KiQuantumEnd // process quantum end request
beq v0, 50f // if eq, no next thread, return
bis v0, zero, s2 // set next thread
br zero, 40f // else restore interrupts and return
//
// Determine if a new thread has been selected for execution on
// this processor.
//
25: LDP v0, PbNextThread(s0) // get address of next thread object
beq v0, 50f // if eq, no new thread selected
//
// Lock dispatcher database and reread address of next thread object
// since it is possible for it to change in mp sysytem.
//
// N.B. This is a very special acquire of the dispatcher lock in that it
// will not be acquired unless it is free. Therefore, it is known
// that there cannot be any queued lock requests.
//
#if !defined(NT_UP)
lda s1, KiDispatcherLock // get dispatcher base lock address
ldil s2, LockQueueDispatcherLock * 2 // compute per processor
SPADDP s2, s0, s2 // lock queue entry address
lda s2, PbLockQueue(s2) //
#endif
30: ldl a0, KiSynchIrql // raise IRQL to synch level
SWAP_IRQL //
#if !defined(NT_UP)
LDP_L t0, 0(s1) // get current lock value
bis s2, zero, t1 // t1 = lock ownership value
bne t0, 45f // ne => spin lock owned
STP_C t1, 0(s1) // set lock to owned
beq t1, 45f // if eq, conditional store failed
mb // synchronize memory access
bis s1, LOCK_QUEUE_OWNER, t0 // set lock owner bit in lock entry
STP t0, LqLock(s2) //
#endif
//
// Reready current thread for execution and swap context to the selected thread.
//
LDP s2, PbNextThread(s0) // get addr of next thread
40: GET_CURRENT_THREAD // get current thread address
bis v0, zero, s1 // save current thread address
STP zero, PbNextThread(s0) // clear address of next thread
STP s2, PbCurrentThread(s0) // set address of current thread
bis s1, zero, a0 // set address of previous thread
bsr ra, KiReadyThread // reready thread for execution
bsr ra, KiSaveVolatileFloatState // save floating state
bsr ra, SwapContext // swap context
//
// Restore the saved integer registers that were changed for a context
// switch only.
//
// N.B. - The frame pointer must be restored before the volatile floating
// state because it is the pointer to the trap frame.
//
ldq s2, ExIntS2(sp) // restore s2 - s5
ldq s3, ExIntS3(sp) //
ldq s4, ExIntS4(sp) //
ldq s5, ExIntS5(sp) //
ldq fp, ExIntFp(sp) // restore the frame pointer
bsr ra, KiRestoreVolatileFloatState // restore floating state
//
// Restore the remaining saved integer registers and return.
//
50: ldq s0, ExIntS0(sp) // restore s0 - s1
ldq s1, ExIntS1(sp) //
ldq ra, ExIntRa(sp) // get return address
lda sp, ExceptionFrameLength(sp) // deallocate context frame
ret zero, (ra) // return
//
// Dispatcher lock is owned, spin on both the the dispatcher lock and
// the DPC queue going not empty.
//
#if !defined(NT_UP)
45: bis v0, zero, a0 // lower IRQL to wait for locks
SWAP_IRQL //
48: LDP t0, 0(s1) // read current dispatcher lock value
beq t0, 30b // lock available. retry spinlock
ldl t1, PbDpcQueueDepth(s0) // get current DPC queue depth
bne t1, PollDpcList // if ne, list not empty
br zero, 48b // loop in cache until lock available
#endif
.end KiDispatchInterrupt
SBTTL("Swap Context to Next Thread")
//++
//
// 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.
// sp - Pointer to a exception frame.
//
// Return value:
//
// v0 - complement of Kernel APC pending.
// s2 - Address of current thread object.
//
//--
NESTED_ENTRY(SwapContext, 0, zero)
stq ra, ExSwapReturn(sp) // save return address
PROLOGUE_END
//
// Set new thread's state to running. Note this must be done
// under the dispatcher lock so that KiSetPriorityThread sees
// the correct state.
//
ldil t0, Running // set state of new thread to running
StoreByte( t0, ThState(s2) ) //
//
// Acquire the context swap lock so the address space of the old thread
// 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)
ldil a0, LockQueueContextSwapLock * 2 // compute per processor
SPADDP a0, s0, a0 // lock queue entry address
lda a0, PbLockQueue(a0) //
bsr ra, KeAcquireQueuedSpinLockAtDpcLevel // acquire context swap lock
ldil a0, LockQueueDispatcherLock * 2 // compute per processor
SPADDP a0, s0, a0 // lock queue entry address
lda a0, PbLockQueue(a0) //
bsr ra, KeReleaseQueuedSpinLockFromDpcLevel // release dispatcher lock
#endif
//
// Accumulate the total time spent in a thread.
//
#if defined(PERF_DATA)
bis zero,zero,a0 // optional frequency not required
bsr ra, KeQueryPerformanceCounter // 64-bit cycle count in v0
ldq t0, PbStartCount(s0) // get starting cycle count
stq v0, PbStartCount(s0) // set starting cycle count
ldl t1, EtPerformanceCountHigh(s1) // get accumulated cycle count high
sll t1, 32, t2 //
ldl t3, EtPerformanceCountLow(s1) // get accumulated cycle count low
zap t3, 0xf0, t4 // zero out high dword sign extension
bis t2, t4, t3 //
subq v0, t0, t5 // compute elapsed cycle count
addq t5, t3, t4 // compute new cycle count
stl t4, EtPerformanceCountLow(s1) // set new cycle count in thread
srl t4, 32, t2 //
stl t2, EtPerformanceCountHigh(s1) //
#endif
bsr ra, KiSaveNonVolatileFloatState // save floating state
//
// The following entry point is used to switch from the idle thread to
// another thread.
//
ALTERNATE_ENTRY(SwapFromIdle)
//
// Check if an attempt is being made to swap context while executing a DPC.
//
ldl v0, PbDpcRoutineActive(s0) // get DPC routine active flag
beq v0, 10f //
ldil a0, ATTEMPTED_SWITCH_FROM_DPC // set bug check code
bsr ra, KeBugCheck // call bug check routine
//
// Get address of old and new process objects.
//
10: LDP s5, ThApcState + AsProcess(s1) // get address of old process
LDP s4, ThApcState + AsProcess(s2) // get address of new process
//
// Save the current PSR in the context frame, store the kernel stack pointer
// in the previous thread object, load the new kernel stack pointer from the
// new thread object, load the ptes for the new kernel stack in the DTB
// stack, select and new process id and swap to the new process, and restore
// the previous PSR from the context frame.
//
DISABLE_INTERRUPTS // disable interrupts
LDP a0, ThInitialStack(s2) // get initial kernel stack pointer
STP sp, ThKernelStack(s1) // save old kernel stack pointer
bis s2, zero, a1 // new thread address
LDP a2, ThTeb(s2) // get address of user TEB
//
// On uni-processor systems keep the global current thread address
// up to date.
//
#ifdef NT_UP
STP a1, KiCurrentThread // save new current thread
#endif //NT_UP
//
// If the old process is the same as the new process, then there is no need
// to change the address space. The a3 parameter indicates that the address
// space is not to be swapped if it is less than zero. Otherwise, a3 will
// contain the pfn of the PDR for the new address space.
//
ldil a3, -1 // assume no address space change
cmpeq s5, s4, t0 // old process = new process?
bne t0, 50f // if ne, no address space swap
//
// Update the processor set masks. Clear the processor set member number in
// the old process and set the processor member number in the new process.
//
#if !defined(NT_UP)
ldl t0, PbSetMember(s0) // get processor set member mask
ldq t1, PrActiveProcessors(s5) // get old processor sets
ldq t2, PrActiveProcessors(s4) // get new processor sets
bic t1, t0, t3 // clear member in old active set
bis t2, t0, t4 // set member in new active set
sll t0, 32, t5 // set member in new run on set
bis t4, t5, t4 //
stq t3, PrActiveProcessors(s5) // set old processor sets
stq t4, PrActiveProcessors(s4) // set new processor sets
#endif
LDP a3, PrDirectoryTableBase(s4) // get page directory PDE
srl a3, PTE_PFN, a3 // isolate page frame number
//
// If the maximum address space number is zero, then force a TB invalidate.
//
ldl a4, KiMaximumAsn // get maximum ASN number
bis zero, 1, a5 // set ASN wrap indicator
beq a4, 50f // if eq, only one ASN
bis a4, zero, t3 // save maximum ASN number
//
// Check if a pending TB invalidate is pending on the current processor.
//
#if !defined(NT_UP)
lda t8, KiTbiaFlushRequest // get TBIA flush request mask address
ldl t1, 0(t8) // get TBIA flush request mask
and t1, t0, t2 // check if current processor request
beq t2, 20f // if eq, no pending flush request
bic t1, t0, t1 // clear processor member in mask
stl t1, 0(t8) // set TBIA flush request mask
#endif
//
// If the process sequence number matches the master sequence number then
// use the process ASN. Otherwise, allocate a new ASN and check for wrap.
// If ASN wrap occurs, then also increment the master sequence number.
//
20: lda t9, KiMasterSequence // get master sequence number address
ldq t4, 0(t9) // get master sequence number
ldq t5, PrProcessSequence(s4) // get process sequence number
ldl a4, PrProcessAsn(s4) // get process ASN
xor t4, t5, a5 // check if sequence number matches
beq a5, 40f // if eq, sequence number match
lda t10, KiMasterAsn // get master ASN number address
ldl a4, 0(t10) // get master ASN number
addl a4, 1, a4 // increment master ASN number
cmpult t3, a4, a5 // check for ASN wrap
beq a5, 30f // if eq, ASN did not wrap
addq t4, 1, t4 // increment master sequence number
stq t4, 0(t9) // set master sequence number
#if !defined(NT_UP)
ldl t5, KeActiveProcessors // get active processor mask
bic t5, t0, t5 // clear current processor member
stl t5, 0(t8) // request flush on other processors
#endif
bis zero, zero, a4 // reset master ASN
30: stl a4, 0(t10) // set master ASN number
stl a4, PrProcessAsn(s4) // set process ASN number
stq t4, PrProcessSequence(s4) // set process sequence number
#if !defined(NT_UP)
ldq t5, PrActiveProcessors(s4) // get new processor sets
zapnot t5, 0xf, t5 // clear run on processor set
sll t5, 32, t3 // set run on set equal to active set
bis t5, t3, t5 //
stq t5, PrActiveProcessors(s4) // set new processor sets
#endif
//
// Merge TBIA flush request with ASN wrap indicator.
//
40: //
#if !defined(NT_UP)
bis t2, a5, a5 // merge TBIA indicators
#endif
//
// a0 = initial ksp of new thread
// a1 = new thread address
// a2 = new TEB
// a3 = PDR of new address space or -1
// a4 = new ASN
// a5 = ASN wrap indicator
//
50: SWAP_THREAD_CONTEXT // swap thread
LDP sp, ThKernelStack(s2) // get new kernel stack pointer
ENABLE_INTERRUPTS // enable interrupts
//
// Release the context swap lock.
//
#if !defined(NT_UP)
ldil a0, LockQueueContextSwapLock * 2 // compute per processor
SPADDP a0, s0, a0 // lock queue entry address
lda a0, PbLockQueue(a0) //
bsr ra, KeReleaseQueuedSpinLockFromDpcLevel // release context swap lock
#endif
//
// If the new thread has a kernel mode APC pending, then request an APC
// interrupt.
//
ldil v0, 1 // set no apc pending
LoadByte(t0, ThApcState + AsKernelApcPending(s2)) // get kernel APC pendng
ldl t2, ExPsr(sp) // get previous processor status
beq t0, 50f // if eq no apc pending
ldil a0, APC_INTERRUPT // set APC level value
REQUEST_SOFTWARE_INTERRUPT // request an apc interrupt
bis zero, zero, v0 // set APC pending
//
// Count number of context switches.
//
50: ldl t1, PbContextSwitches(s0) // increment number of switches
addl t1, 1, t1 //
stl t1, PbContextSwitches(s0) //
ldl t0, ThContextSwitches(s2) // increment thread switches
addl t0, 1, t0 //
stl t0, ThContextSwitches(s2) //
//
// Restore the nonvolatile floating state.
//
bsr ra, KiRestoreNonVolatileFloatState //
//
// load RA and return with address of current thread in s2
//
ldq ra, ExSwapReturn(sp) // get return address
ret zero, (ra) // return
.end SwapContext
SBTTL("Swap Process")
//++
//
// BOOLEAN
// KiSwapProcess (
// IN PKPROCESS NewProcess
// IN PKPROCESS OldProcess
// )
//
// Routine Description:
//
// This function swaps the address space from one process to another by
// assigning a new ASN if necessary and calling the palcode to swap
// the privileged portion of the process context (the page directory
// base pointer and the ASN). This function also maintains the processor
// set for both processes in the switch.
//
// Arguments:
//
// NewProcess (a0) - Supplies a pointer to a control object of type process
// which represents the new process to switch to.
//
// OldProcess (a1) - Supplies a pointer to a control object of type process
// which represents the old process to switch from..
//
// Return Value:
//
// None.
//
//--
.struct 0
SwA0: .space 8 // saved new process address
SwA1: .space 8 // saved old process address
SwRa: .space 8 // saved return address
.space 8 // unused
SwapFrameLength: // swap frame length
NESTED_ENTRY(KiSwapProcess, SwapFrameLength, zero)
lda sp, -SwapFrameLength(sp) // allocate stack frame
stq ra, SwRa(sp) // save return address
PROLOGUE_END
//
// Acquire the context swap lock, clear the processor set member in he old
// process, set the processor member in the new process, and release the
// context swap lock.
//
#if !defined(NT_UP)
stq a0, SwA0(sp) // save new process address
stq a1, SwA1(sp) // save old process address
ldil a0, LockQueueContextSwapLock // set lock queue number
bsr ra, KeAcquireQueuedSpinLock // acquire context swap lock
bis v0, zero, t6 // save old IRQL
GET_PROCESSOR_CONTROL_REGION_BASE // get PCR address
ldq a3, SwA0(sp) // restore new process address
ldq a1, SwA1(sp) // restore old process address
ldl t0, PcSetMember(v0) // get processor set member mask
ldq t1, PrActiveProcessors(a1) // get old processor sets
ldq t2, PrActiveProcessors(a3) // get new processor sets
bic t1, t0, t3 // clear member in old active set
bis t2, t0, t4 // set member in new active set
sll t0, 32, t5 // set member in new run on set
bis t4, t5, t4 //
stq t3, PrActiveProcessors(a1) // set old processor sets
stq t4, PrActiveProcessors(a3) // set new processor sets
#else
bis a0, zero, a3 // copy new process address
#endif
LDP a0, PrDirectoryTableBase(a3) // get page directory PDE
srl a0, PTE_PFN, a0 // isloate page frame number
//
// If the maximum address space number is zero, then assign ASN zero to
// the new process.
//
ldl a1, KiMaximumAsn // get maximum ASN number
ldil a2, TRUE // set ASN wrap indicator
beq a1, 40f // if eq, only one ASN
bis a1, zero, t3 // save maximum ASN number
//
// Check if a pending TB invalidate all is pending on the current processor.
//
#if !defined(NT_UP)
lda t8, KiTbiaFlushRequest // get TBIA flush request mask address
ldl t1, 0(t8) // get TBIA flush request mask
and t1, t0, t2 // check if current processor request
beq t2, 10f // if eq, no pending flush request
bic t1, t0, t1 // clear processor member in mask
stl t1, 0(t8) // set TBIA flush request mask
#endif
//
// If the process sequence number matches the master sequence number then
// use the process ASN. Otherwise, allocate a new ASN and check for wrap.
// If ASN wrap occurs, then also increment the master sequence number.
//
10: lda t9, KiMasterSequence // get master sequence number address
ldq t4, 0(t9) // get master sequence number
ldq t5, PrProcessSequence(a3) // get process sequence number
ldl a1, PrProcessAsn(a3) // get process ASN
xor t4, t5, a2 // check if sequence number matches
beq a2, 30f // if eq, sequence number match
lda t10, KiMasterAsn // get master ASN number address
ldl a1, 0(t10) // get master ASN number
addl a1, 1, a1 // increment master ASN number
cmpult t3, a1, a2 // check for ASN wrap
beq a2, 20f // if eq, ASN did not wrap
addq t4, 1, t4 // increment master sequence number
stq t4, 0(t9) // set master sequence number
#if !defined(NT_UP)
ldl t5, KeActiveProcessors // get active processor mask
bic t5, t0, t5 // clear current processor member
stl t5, 0(t8) // request flush on other processors
#endif
bis zero, zero, a1 // reset master ASN
20: stl a1, 0(t10) // set master ASN number
stl a1, PrProcessAsn(a3) // set process ASN number
stq t4, PrProcessSequence(a3) // set process sequence number
#if !defined(NT_UP)
ldq t5, PrActiveProcessors(a3) // get new processor sets
zapnot t5, 0xf, t5 // clear run on processor set
sll t5, 32, t3 // set run on set equal to active set
bis t5, t3, t5 //
stq t5, PrActiveProcessors(a3) // set new processor sets
#endif
//
// Merge TBIA flush request with ASN wrap indicator.
//
30: //
#if !defined(NT_UP)
bis t2, a2, a2 // merge TBIA indicators
#endif
//
// a0 = pfn of new page directory base
// a1 = new address space number
// a2 = tbiap indicator
//
40: SWAP_PROCESS_CONTEXT // swap address space
//
// Release context swap lock.
//
#if !defined(NT_UP)
ldil a0, LockQueueContextSwapLock // set lock queue number
bis t6, zero, a1 // set old IRQL value
bsr ra, KeReleaseQueuedSpinLock // release dispatcher lock
#endif
ldq ra, SwRa(sp) // restore return address
lda sp, SwapFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
.end KiSwapProcess