// 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