//++ // // Module Name: // // spinlock.s // // Abstract: // // This module implements the routines for acquiring and releasing // spin locks. // // Author: // // William K. Cheung (wcheung) 29-Sep-1995 // // Environment: // // Kernel mode only. // // Revision History: // // 31-Dec-1998 wc Updated to use xchg8 // // 07-Jul-1997 bl Updated to EAS2.3 // // 08-Feb-1996 Updated to EAS2.1 // //-- #include "ksia64.h" #include "icecap.h" .file "spinlock.s" // // Define LOG2(x) for those values whose bit numbers are needed in // order to test a single bit with the tbit instruction. // #define _LOG2_0x1 0 #define _LOG2_0x2 1 #define _LOG2_x(n) _LOG2_##n #define LOG2(n) _LOG2_x(n) // // Globals // PublicFunction(KiCheckForSoftwareInterrupt) //++ // // VOID // KiAcquireSpinLock ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function acquires a kernel spin lock. // // N.B. This function assumes that the current IRQL is set properly. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a kernel spin lock. // // Return Value: // // None. // //-- LEAF_ENTRY(KiAcquireSpinLock) ALTERNATE_ENTRY(KeAcquireSpinLockAtDpcLevel) #if !defined(NT_UP) #ifndef CAPKERN_SYNCH_POINTS ACQUIRE_SPINLOCK(a0,a0,Kiasl10) #else CAP_ACQUIRE_SPINLOCK(a0,a0,Kiasl10,t0,t1,t2,t3) #endif #endif // !defined(NT_UP) LEAF_RETURN LEAF_EXIT(KiAcquireSpinLock) //++ // // BOOLEAN // KeTryToAcquireSpinLockAtDpcLevel ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function attempts to acquires the specified kernel spinlock. If // the spinlock can be acquired, then TRUE is returned. Otherwise, FALSE // is returned. // // N.B. This function assumes that the current IRQL is set properly. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a kernel spin lock. // // Return Value: // // If the spin lock is acquired, then a value of TRUE is returned. // Otherwise, a value of FALSE is returned. // // N.B. The caller KeTryToAcquireSpinLock implicitly depends on the // contents of predicate registers pt1 & pt2. // //-- LEAF_ENTRY(KeTryToAcquireSpinLockAtDpcLevel) #if !defined(NT_UP) #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0, 8, t0, t1, t2, t3, pt0) #endif xchg8 t0 = [a0], a0 ;; cmp.ne pt0 = t0, zero // if ne, lock acq failed mov v0 = TRUE // acquire assumed succeed ;; (pt0) YIELD (pt0) mov v0 = FALSE // return FALSE #ifdef CAPKERN_SYNCH_POINTS (pt0) br.cond.spnt kttasladlSkipLog CAPSPINLOG1INT(a0, 1, t0, t1, t2, t3, pt0) kttasladlSkipLog: #endif #else mov v0 = TRUE #endif LEAF_RETURN LEAF_EXIT(KeTryToAcquireSpinLockAtDpcLevel) //++ // // VOID // KeAcquireSpinLock ( // IN PKSPIN_LOCK SpinLock // OUT PKIRQL OldIrql // ) // // Routine Description: // // This function raises the current IRQL to DISPATCH_LEVEL and acquires // the specified executive spinlock. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a executive spinlock. // // OldIrql (a1) - Supplies a pointer to a variable that receives the // the previous IRQL value. // // N.B. The Old IRQL MUST be stored after the lock is acquired. // // Return Value: // // None. // //-- LEAF_ENTRY(KeAcquireSpinLock) // // Get original IRQL, raise IRQL to DISPATCH_LEVEL // and then acquire the specified spinlock. // mov t0 = DISPATCH_LEVEL SWAP_IRQL(t0) #if !defined(NT_UP) #ifndef CAPKERN_SYNCH_POINTS ACQUIRE_SPINLOCK(a0,a0,Kasl10) #else CAP_ACQUIRE_SPINLOCK(a0,a0,Kasl10,t1,t2,t3,t4) #endif #endif // !defined(NT_UP) st1 [a1] = v0 // save old IRQL LEAF_RETURN LEAF_EXIT(KeAcquireSpinLock) //++ // // KIRQL // KeAcquireSpinLockRaiseToSynch ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function raises the current IRQL to synchronization level and // acquires the specified spinlock. // // Arguments: // // SpinLock (a0) - Supplies a pointer to the spinlock that is to be // acquired. // // Return Value: // // The previous IRQL is returned as the function value. // //-- LEAF_ENTRY(KeAcquireSpinLockRaiseToSynch) // // Register aliases // pHeld = pt0 pFree = pt1 mov t1 = SYNCH_LEVEL #if !defined(NT_UP) #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0,1,t3,t4,t5,t6,pt2) mov t2 = zero #endif GET_IRQL (v0) KaslrtsRetry: SET_IRQL (t1) xchg8 t0 = [a0], a0 ;; cmp.eq pFree, pHeld = 0, t0 ;; PSET_IRQL (pHeld, v0) #ifdef CAPKERN_SYNCH_POINTS (pHeld) br.cond.dpnt KaslrtsSkipCollLog cmp.eq pt2 = t2, zero (pt2) br.cond.sptk KaslrtsSkipCollLog CAPSPINLOG2INT(t2,a0,2,t3,t4,t5,t6,pt2) KaslrtsSkipCollLog: #endif //CAPKERN_SYNCH_POINTS (pFree) LEAF_RETURN ;; KaslrtsLoop: YIELD #ifdef CAPKERN_SYNCH_POINTS add t2 = 1, t2 #endif cmp.eq pFree, pHeld = 0, t0 ld8.nt1 t0 = [a0] (pFree) br.cond.dpnt KaslrtsRetry (pHeld) br.cond.dptk KaslrtsLoop #else SWAP_IRQL (t1) // Raise IRQL LEAF_RETURN #endif // !defined(NT_UP) LEAF_EXIT(KeAcquireSpinLockRaiseToSynch) //++ // // KIRQL // KeAcquireSpinLockRaiseToDpc ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function raises the current IRQL to dispatcher level and acquires // the specified spinlock. // // Arguments: // // SpinLock (a0) - Supplies a pointer to the spinlock that is to be // acquired. // // Return Value: // // The previous IRQL is returned as the function value. // //-- LEAF_ENTRY(KeAcquireSpinLockRaiseToDpc) mov t2 = DISPATCH_LEVEL ;; SWAP_IRQL (t2) #if !defined(NT_UP) cmp.eq pt0, pt1 = zero, zero cmp.eq pt2, pt3 = zero, zero #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0, 1, t3, t4, t5, t6, pt4) mov t1 = zero #endif ;; Kaslrtp10: .pred.rel "mutex",pt0,pt1 (pt0) xchg8 t0 = [a0], a0 (pt1) ld8.nt1 t0 = [a0] ;; (pt0) cmp.ne pt2, pt3 = zero, t0 cmp.eq pt0, pt1 = zero, t0 ;; (pt1) YIELD #ifdef CAPKERN_SYNCH_POINTS (pt2) add t1 = 1, t1 #endif (pt2) br.dpnt Kaslrtp10 #ifdef CAPKERN_SYNCH_POINTS cmp.eq pt1 = t1, zero (pt1) br.sptk KaslrtpSkipCollLog CAPSPINLOG2INT(t1,a0,2,t3,t4,t5,t6,pt1) KaslrtpSkipCollLog: #endif (pt3) br.ret.dptk brp #else LEAF_RETURN #endif // !defined(NT_UP) LEAF_EXIT(KeAcquireSpinLockRaiseToDpc) //++ // // VOID // KiReleaseSpinLock ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function releases a kernel spin lock. // // N.B. This function assumes that the current IRQL is set properly. // // Arguments: // // SpinLock (a0) - Supplies a pointer to an executive spin lock. // // Return Value: // // None. // //-- LEAF_ENTRY(KiReleaseSpinLock) ALTERNATE_ENTRY(KeReleaseSpinLockFromDpcLevel) #if !defined(NT_UP) st8.rel [a0] = zero // set spin lock not owned #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0, 7, t0, t1, t2, t3, pt0) #endif #endif LEAF_RETURN LEAF_EXIT(KiReleaseSpinLock) //++ // // VOID // KeReleaseSpinLock ( // IN PKSPIN_LOCK SpinLock // IN KIRQL OldIrql // ) // // Routine Description: // // This function releases an executive spin lock and lowers the IRQL // to its previous value. Called at DPC_LEVEL. // // Arguments: // // SpinLock (a0) - Supplies a pointer to an executive spin lock. // // OldIrql (a1) - Supplies the previous IRQL value. // // Return Value: // // None. // //-- LEAF_ENTRY(KeReleaseSpinLock) zxt1 a1 = a1 #if !defined(NT_UP) st8.rel [a0] = zero // set spinlock not owned #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0, 7, t0, t1, t2, t3, pt0) #endif #endif ;; LEAF_LOWER_IRQL_AND_RETURN(a1) // Lower IRQL and return LEAF_EXIT(KeReleaseSpinLock) //++ // // BOOLEAN // KeTryToAcquireSpinLock ( // IN PKSPIN_LOCK SpinLock // OUT PKIRQL OldIrql // ) // // Routine Description: // // This function raises the current IRQL to DISPATCH_LEVEL and attempts // to acquires the specified executive spinlock. If the spinlock can be // acquired, then TRUE is returned. Otherwise, the IRQL is restored to // its previous value and FALSE is returned. Called at IRQL <= DISPATCH_LEVEL. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a executive spinlock. // // OldIrql (a1) - Supplies a pointer to a variable that receives the // the previous IRQL value. // // Return Value: // // If the spin lock is acquired, then a value of TRUE is returned. // Otherwise, a value of FALSE is returned. // // N.B. This routine assumes KeTryToAcquireSpinLockAtDpcLevel pt1 & pt2 will // be set to reflect the result of the attempt to acquire the spinlock. // //-- LEAF_ENTRY(KeTryToAcquireSpinLock) #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0, 8, t0, t1, t2, t3, pt2) #endif rOldIrql = t2 // // Raise IRQL to DISPATCH_LEVEL and try to acquire the specified spinlock. // Return FALSE if failed; otherwise, return TRUE. // GET_IRQL (rOldIrql) // get original IRQL mov t0 = DISPATCH_LEVEL;; SET_IRQL (t0) // raise to dispatch level #if !defined(NT_UP) xchg8 t0 = [a0], a0 ;; cmp.ne pt2 = t0, zero // if ne, lock acq failed ;; // // If successfully acquired, pt1 is set to TRUE while pt2 is set to FALSE. // Otherwise, pt2 is set to TRUE while pt1 is set to FALSE. // (pt2) YIELD (pt2) mov v0 = FALSE // return FALSE PSET_IRQL (pt2, rOldIrql) // restore old IRQL #ifdef CAPKERN_SYNCH_POINTS (pt2) br.cond.dpnt KttasSkipLog CAPSPINLOG1INT(a0, 1, t0, t1, t2, t3, pt0) KttasSkipLog: #endif (pt2) LEAF_RETURN ;; #endif // !defined(NT_UP) st1 [a1] = rOldIrql // save old IRQL mov v0 = TRUE // successfully acquired LEAF_RETURN LEAF_EXIT(KeTryToAcquireSpinLock) //++ // // BOOLEAN // KeTestSpinLock ( // IN PKSPIN_LOCK SpinLock // ) // // Routine Description: // // This function tests a kernel spin lock. If the spinlock is // busy, FALSE is returned. If not, TRUE is returned. The spinlock // is never acquired. This is provided to allow code to spin at low // IRQL, only raising the IRQL when there is a reasonable hope of // acquiring the lock. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a kernel spin lock. // // Return Value: // // TRUE - Spinlock appears available // FALSE - SpinLock is busy //-- LEAF_ENTRY(KeTestSpinLock) ld8.nt1 t0 = [a0] ;; cmp.ne pt0 = 0, t0 mov v0 = TRUE // default TRUE ;; (pt0) YIELD (pt0) mov v0 = FALSE // if t0 != 0 return FALSE LEAF_RETURN LEAF_EXIT(KeTestSpinLock) SBTTL("Acquire Queued SpinLock and Raise IRQL") //++ // // VOID // KeAcquireInStackQueuedSpinLock ( // IN PKSPIN_LOCK SpinLock, // IN PKLOCK_QUEUE_HANDLE LockHandle // ) // // Routine Description: // // This function raises the current IRQL to dispatch level and // acquires the specified queued spinlock. // // Arguments: // // SpinLock (a0) - Supplies a pointer to a spin lock. // // LockHandle (a1) - Supplies a pointer to a lock handle. // // Return Value: // // None. // //-- LEAF_ENTRY(KeAcquireInStackQueuedSpinLockRaiseToSynch) add t5 = LqhNext, a1 mov t1 = SYNCH_LEVEL br.sptk Kaisqsl10 ;; ALTERNATE_ENTRY(KeAcquireInStackQueuedSpinLock) add t5 = LqhNext, a1 mov t1 = DISPATCH_LEVEL ;; Kaisqsl10: #if !defined(NT_UP) st8 [t5] = zero // set next link to NULL add t4 = LqhLock, a1 ;; st8.rel [t4] = a0 // set spin lock address #endif // !defined(NT_UP) SWAP_IRQL (t1) // old IRQL in register v0 add t0 = LqhOldIrql, a1 ;; st1 [t0] = v0 // save old IRQL #if !defined(NT_UP) // // Finish in common code. The following register values // are assumed in that code. // // t4 = &LockEntry->ActualLock // t5 = LockEntry // a0 = *(&LockEntry->ActualLock) // // Note: LqhNext == 0, otherwise we would have to trim t5 here. // br KxqAcquireQueuedSpinLock // finish in common code #else br.ret.sptk brp #endif !defined(NT_UP) LEAF_EXIT(KeAcquireInStackQueuedSpinLock) SBTTL("Acquire Queued SpinLock and Raise IRQL") //++ // // KIRQL // KeAcquireQueuedSpinLock ( // IN KSPIN_LOCK_QUEUE_NUMBER Number // ) // // KIRQL // KeAcquireQueuedSpinLockRaiseToSynch ( // IN KSPIN_LOCK_QUEUE_NUMBER Number // ) // // Routine Description: // // This function raises the current IRQL to synchronization level and // acquires the specified queued spinlock. // // Arguments: // // Number (a0) - Supplies the queued spinlock number. // // Return Value: // // The previous IRQL is returned as the function value. // //-- LEAF_ENTRY(KeAcquireQueuedSpinLock) add t0 = a0, a0 mov t1 = DISPATCH_LEVEL br Kaqsl10 ;; ALTERNATE_ENTRY(KeAcquireQueuedSpinLockRaiseToSynch) add t0 = a0, a0 mov t1 = SYNCH_LEVEL ;; Kaqsl10: SWAP_IRQL (t1) // old IRQL in register v0 #if !defined(NT_UP) movl t2 = KiPcr+PcPrcb ;; ld8 t2 = [t2] ;; shladd t3 = t0, 3, t2 // get associated spinlock addr ;; add t5 = PbLockQueue, t3 ;; add t4 = LqLock, t5 ;; ld8 t6 = [t4] mov t11 = 0x7 ;; andcm a0 = t6, t11 // mask the lower 3 bits ;; ALTERNATE_ENTRY(KxqAcquireQueuedSpinLock) #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(a0,1,t6,t7,t8,t9,pt1) mov t12 = zero #endif mf // Do a memory fence to ensure the write of LqhNext // occurs before the xchg8 on lock address. // // t4 = &LockEntry->ActualLock // t5 = LockEntry // a0 = *(&LockEntry->ActualLock) // xchg8 t7 = [a0], t5 ;; cmp.ne pt0, pt1 = r0, t7 // if ne, lock already owned ;; (pt1) or t8 = LOCK_QUEUE_OWNER, a0 (pt0) or t8 = LOCK_QUEUE_WAIT, a0 ;; st8.rel [t4] = t8 add t9 = LqNext, t7 (pt1) br.ret.sptk brp ;; // // The lock is already held by another processor. Set the wait // bit in this processor's Lock Queue entry, then set the next // field in the Lock Queue entry of the last processor to attempt // to acquire the lock (this is the address returned by the xchg // above) to point to THIS processor's lock queue entry. // st8.rel [t9] = t5 Kaqsl20: #ifdef CAPKERN_SYNCH_POINTS add t12 = 1, t12 #endif YIELD ld8 t10 = [t4] ;; tbit.z pt1, pt0 = t10, LOG2(LOCK_QUEUE_WAIT) (pt0) br.dptk.few Kaqsl20 #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG2INT(t12,a0,4,t6,t7,t8,t9,pt2) #endif (pt1) br.ret.dpnt brp // if zero, lock acquired #else br.ret.sptk brp #endif // !defined(NT_UP) ;; LEAF_EXIT(KeAcquireQueuedSpinLock) SBTTL("Release Queued SpinLock and Lower IRQL") //++ // // VOID // KeReleaseInStackQueuedSpinLock ( // IN PKLOCK_QUEUE_HANDLE LockHandle // ) // // Routine Description: // // This function releases a queued spinlock and lowers the IRQL to its // previous value. // // Arguments: // // LockHandle (a0) - Supplies a pointer to a lock handle. // // Return Value: // // None. // //-- LEAF_ENTRY(KeReleaseInStackQueuedSpinLock) alloc t22 = ar.pfs, 2, 2, 2, 0 add t9 = LqhOldIrql, a0 add t5 = LqhNext, a0 // set address of lock queue ;; ld1.nt1 a1 = [t9] // get old IRQL br KxqReleaseQueuedSpinLock // finish in common code LEAF_EXIT(KeReleaseInStackQueuedSpinLock) SBTTL("Release Queued SpinLock and Lower IRQL") //++ // // VOID // KeReleaseQueuedSpinLock ( // IN KSPIN_LOCK_QUEUE_NUMBER Number, // IN KIRQL OldIrql // ) // // Routine Description: // // This function releases a queued spinlock and lowers the IRQL to its // previous value. // // Arguments: // // Number (a0) - Supplies the queued spinlock number. // // OldIrql (a1) - Supplies the previous IRQL value. // // Return Value: // // None. // //-- LEAF_ENTRY(KeReleaseQueuedSpinLock) PROLOGUE_BEGIN #if !defined(NT_UP) movl v0 = KiPcr+PcPrcb ;; ld8 v0 = [v0] add t0 = a0, a0 ;; shladd t1 = t0, 3, v0 ;; add t5 = PbLockQueue, t1 ;; #endif // !defined(NT_UP) ALTERNATE_ENTRY(KxqReleaseQueuedSpinLock) #if !defined(NT_UP) add v0 = LqNext, t5 add t2 = LqLock, t5 ;; ld8.acq t4 = [t2] mov ar.ccv = t5 ld8 t3 = [v0] ;; #ifdef CAPKERN_SYNCH_POINTS and t6 = ~7, t4 CAPSPINLOG1INT(t6,7,t7,t8,t9,t10,pt0) mov ar.ccv = t5 /* CAPSPINLOG1INT mutates ar.ccv */ mov t9 = zero #endif and t4 = ~LOCK_QUEUE_OWNER, t4 // clear lock owner bit ;; add t6 = LqLock, t3 st8.rel [t2] = t4 cmp.ne pt0, pt1 = r0, t3 // if ne, another processor waiting (pt0) br.sptk.few Krqsl30 ld8 t7 = [t4] ;; cmp.ne pt2 = t5, t7 (pt2) br.spnt.few Krqsl20 cmpxchg8.rel t8 = [t4], r0, ar.ccv ;; cmp.ne pt0, pt1 = t8, t5 // if ne, another processor waiting (pt0) br.spnt.few Krqsl20 ;; Krqsl10: #ifdef CAPKERN_SYNCH_POINTS cmp.eq pt2 = t9, zero (pt2) br.sptk KrqslSkipCollLog and t6 = ~7, t4 CAPSPINLOG2INT(t9,t6,9,t7,t8,t10,t11,pt2) KrqslSkipCollLog: #endif #endif // !defined(NT_UP) LEAF_LOWER_IRQL_AND_RETURN(a1) // lower IRQL to previous level ;; #if !defined(NT_UP) // // Another processor has inserted its lock queue entry in the lock queue, // but has not yet written its lock queue entry address in the current // processor's next link. Spin until the lock queue address is written. // Krqsl20: YIELD #ifdef CAPKERN_SYNCH_POINTS and t9 = 1, t9 #endif ld8 t3 = [v0] // get next lock queue entry addr ;; cmp.eq pt0 = r0, t3 // if eq, addr not written yet add t6 = LqLock, t3 (pt0) br.sptk Krqsl20 // try again ;; // // Grant the next process in the lock queue ownership of the spinlock. // (Turn off the WAIT bit and on the OWNER bit in the next entries lock // field). // Krqsl30: ld8.nt1 t2 = [t6] // get spinlock addr and lock bit ;; st8 [v0] = r0 // clear next lock queue entry addr ;; xor t2 = (LOCK_QUEUE_OWNER|LOCK_QUEUE_WAIT), t2 ;; st8.rel [t6] = t2 br Krqsl10 ;; #endif // !defined(NT_UP) LEAF_EXIT(KeReleaseQueuedSpinLock) SBTTL("Try to Acquire Queued SpinLock and Raise IRQL") //++ // // LOGICAL // KeTryToAcquireQueuedSpinLock ( // IN KSPIN_LOCK_QUEUE_NUMBER Number // OUT PKIRQL OldIrql // ) // // LOGICAL // KeTryToAcquireQueuedSpinLockRaiseToSynch ( // IN KSPIN_LOCK_QUEUE_NUMBER Number // OUT PKIRQL OldIrql // ) // // LOGICAL // KeTryToAcquireQueuedSpinLockAtRaisedIrql ( // IN PKSPIN_LOCK_QUEUE LockQueue // ) // // Routine Description: // // This function raises the current IRQL to synchronization level and // attempts to acquire the specified queued spinlock. If the spinlock // cannot be acquired, then IRQL is restored and FALSE is returned as // the function value. Otherwise, TRUE is returned as the function // value. // // Arguments: // // Number (a0) - Supplies the queued spinlock number. // // OldIrql (a1) - Supplies a pointer to a variable that receives the // the previous IRQL value. // // Return Value: // // If the spin lock is acquired, then a value of TRUE is returned. // Otherwise, a value of FALSE is returned. // //-- LEAF_ENTRY(KeTryToAcquireQueuedSpinLock) movl t2 = KiPcr+PcPrcb mov t1 = DISPATCH_LEVEL br Kttaqsl10 ;; ALTERNATE_ENTRY(KeTryToAcquireQueuedSpinLockRaiseToSynch) mov t1 = SYNCH_LEVEL movl t2 = KiPcr+PcPrcb ;; Kttaqsl10: #if !defined(NT_UP) rsm 1 << PSR_I // disable interrupts ld8 t2 = [t2] // get PRCB address ;; shladd t3 = a0, 4, t2 // get address of lock queue entry ;; add t5 = PbLockQueue, t3 add t6 = LqLock+PbLockQueue, t3 ;; ld8 t4 = [t6] // get associated spinlock addr mov ar.ccv = r0 // cmpxchg oldvalue must be 0 mov t11 = 0x7 ;; andcm t12 = t4, t11 ;; #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(t12,8,t7,t8,t9,t10,pt0) mov ar.ccv = zero /* CAPSPINLOG1INT mutates ar.ccv */ #endif // // Try to acquire the specified spinlock. // // N.B. A noninterlocked test is done before the interlocked attempt. This // allows spinning without interlocked cycles. // ld8 t8 = [t12] // get current lock value ;; cmp.ne pt0, pt1 = r0, t8 // if ne, lock owned (pt0) br.spnt.few Kttaqs20 ;; cmpxchg8.acq t8 = [t12], t5, ar.ccv // try to acquire the lock ;; cmp.ne pt0, pt1 = r0, t8 // if ne, lock owned or t4 = LOCK_QUEUE_OWNER, t4// set lock owner bit (pt0) br.spnt.few Kttaqs20 ;; st8 [t6] = t4 #endif SWAP_IRQL(t1) #if !defined(NT_UP) ssm 1 << PSR_I // enable interrupts #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(t12,1,t7,t8,t9,t10,pt0) #endif #endif st1 [a1] = v0 // save old IRQL value mov v0 = TRUE // set return value to TRUE LEAF_RETURN ;; #if !defined(NT_UP) // // The attempt to acquire the specified spin lock failed. Lower IRQL to its // previous value and return FALSE. // Kttaqs20: ssm 1 << PSR_I // enable interrupts mov v0 = FALSE // set return value to FALSE YIELD LEAF_RETURN #endif LEAF_EXIT(KeTryToAcquireQueuedSpinLock) SBTTL("Try to Acquire Queued SpinLock without raising IRQL") //++ // // LOGICAL // KeTryToAcquireQueuedSpinLockAtRaisedIrql ( // IN PKSPIN_LOCK_QUEUE LockQueue // ) // // Routine Description: // // This function attempts to acquire the specified queued spinlock. // If the spinlock cannot be acquired, then FALSE is returned as // the function value. Otherwise, TRUE is returned as the function // value. // // Arguments: // // LockQueue (a0) - Supplies the address of the queued spinlock. // // Return Value: // // If the spin lock is acquired, then a value of TRUE is returned. // Otherwise, a value of FALSE is returned. // //-- LEAF_ENTRY(KeTryToAcquireQueuedSpinLockAtRaisedIrql) #if !defined(NT_UP) add t6 = LqLock, a0 ;; ld8 t4 = [t6] // get associated spinlock addr mov ar.ccv = r0 // cmpxchg oldvalue must be 0 mov t11 = 0x7 ;; andcm t12 = t4, t11 ;; #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(t12,8,t7,t8,t9,t10,pt0) mov ar.ccv = zero /*CAPSPINLOG1INT mutates ar.ccv*/ #endif // // Try to acquire the specified spinlock. // // N.B. A noninterlocked test is done before the interlocked attempt. This // allows spinning without interlocked cycles. // ld8 t8 = [t12] // get current lock value mov v0 = FALSE // assume failure ;; cmp.ne pt0, pt1 = r0, t8 // if ne, lock owned (pt0) br.ret.spnt.few.clr brp // if owned, return failure ;; cmpxchg8.acq t8 = [t12], a0, ar.ccv // try to acquire the lock ;; cmp.ne pt0, pt1 = r0, t8 // if ne, lock owned or t4 = LOCK_QUEUE_OWNER, t4// set lock owner bit ;; (pt0) YIELD (pt0) br.ret.spnt.few.clr brp // if owned, return failure ;; st8 [t6] = t4 #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(t12,1,t7,t8,t9,t10,pt0) #endif #endif mov v0 = TRUE // set return value to TRUE LEAF_RETURN ;; LEAF_EXIT(KeTryToAcquireQueuedSpinLockAtRaisedIrql) SBTTL("Acquire Queued SpinLock at Current IRQL") //++ // VOID // KeAcquireInStackQueuedSpinLockAtDpcLevel ( // IN PKSPIN_LOCK SpinLock, // IN PKLOCK_QUEUE_HANDLE LockHandle // ) // // Routine Description: // // This function acquires the specified queued spinlock at the current // IRQL. // // Arguments: // // SpinLock (a0) - Supplies the address of a spin lock. // // LockHandle (a1) - Supplies the address of an in stack lock handle. // // Return Value: // // None. // //-- LEAF_ENTRY(KeAcquireInStackQueuedSpinLockAtDpcLevel) #if !defined(NT_UP) add t0 = LqhNext, a1 add t1 = LqhLock, a1 ;; st8 [t0] = r0 st8 [t1] = a0 add a0 = LqhNext, a1 ;; #endif // !defined(NT_UP) //++ // // VOID // KeAcquireQueuedSpinLockAtDpcLevel ( // IN PKSPIN_LOCK_QUEUE LockQueue // ) // // Routine Description: // // This function acquires the specified queued spinlock at the current // IRQL. // // Arguments: // // LockQueue (a0) - Supplies the address of the lock queue entry. // // Return Value: // // None. // //-- ALTERNATE_ENTRY(KeAcquireQueuedSpinLockAtDpcLevel) #if !defined(NT_UP) add t0 = LqLock, a0 add t1 = LqNext, a0 mov t11 = 0x7 ;; ld8 t4 = [t0] ;; mf andcm t12 = t4, t11 // mask the lower 3 bits ;; #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG1INT(t12,1,t5,t6,t7,t8,pt0) mov t6 = zero #endif xchg8 t3 = [t12], a0 or t5 = LOCK_QUEUE_OWNER, t4 // set lock owner bit ;; cmp.ne pt0, pt1 = r0, t3 // if ne, lock already owned add t2 = LqNext, t3 ;; (pt0) or t5 = LOCK_QUEUE_WAIT, t4 ;; st8.rel [t0] = t5 (pt0) st8.rel [t2] = a0 // set addr of lock queue entry (pt1) br.ret.sptk brp ;; // // The lock is owned by another processor. Set the lock bit in the current // processor lock queue entry, set the next link in the previous lock queue // entry, and spin on the current processor's lock bit. // Kiaqsl10: #ifdef CAPKERN_SYNCH_POINTS add t6 = 1, t6 #endif YIELD ld8 t4 = [t0] // get lock addr and lock wait bit ;; tbit.z pt1, pt0 = t4, LOG2(LOCK_QUEUE_WAIT) (pt0) br.dptk.few Kiaqsl10 #ifdef CAPKERN_SYNCH_POINTS CAPSPINLOG2INT(t6,t12,4,t7,t8,t9,t10,pt2) #endif (pt1) br.ret.dpnt brp // if zero, lock acquired #else br.ret.sptk brp #endif // !defined(NT_UP) ;; LEAF_EXIT(KeAcquireInStackQueuedSpinLockAtDpcLevel) SBTTL("Release Queued SpinLock at Current IRQL") //++ // // VOID // KeReleaseInStackQueuedSpinLockFromDpcLevel ( // IN PKLOCK_QUEUE_HANDLE LockHandle // ) // // Routine Description: // // This function releases a queued spinlock and preserves the current // IRQL. // // Arguments: // // LockHandle (a0) - Supplies the address of a lock handle. // // Return Value: // // None. // //-- LEAF_ENTRY(KeReleaseInStackQueuedSpinLockFromDpcLevel) #if !defined(NT_UP) add a0 = LqhNext, a0 ;; #endif // !defined(NT_UP) //++ // // VOID // KiReleaseQueuedSpinLock ( // IN PKSPIN_LOCK_QUEUE LockQueue // ) // // Routine Description: // // This function releases a queued spinlock and preserves the current // IRQL. // // Arguments: // // LockQueue (a0) - Supplies the address of the lock queue entry. // // Return Value: // // None. // //-- ALTERNATE_ENTRY(KeReleaseQueuedSpinLockFromDpcLevel) #if !defined(NT_UP) mov ar.ccv = a0 add t0 = LqNext, a0 add t1 = LqLock, a0 ;; ld8 t3 = [t0] // get next lock queue entry addr ld8 t4 = [t1] // get associate spin lock addr ;; #ifdef CAPKERN_SYNCH_POINTS and t6 = ~7, t4 CAPSPINLOG1INT(t6,7,t7,t8,t9,t10,pt0) mov ar.ccv = a0 /* CAPSPINLOG1INT mutates ar.ccv */ mov t9 = zero #endif and t4 = ~LOCK_QUEUE_OWNER, t4 // clear lock owner bit ;; st8.rel [t1] = t4 cmp.ne pt0, pt1 = r0, t3 // if ne, another processor waiting (pt0) br.spnt.few Kirqsl30 KiIrqsl10: ld8.nt1 t3 = [t4] // get current lock ownership ;; cmp.ne pt0, pt1 = a0, t3 // if ne, another processor waiting (pt0) br.spnt.few Kirqsl20 ;; cmpxchg8.rel t3 = [t4], r0, ar.ccv ;; cmp.ne pt0, pt1 = a0, t3 // if ne, try again (pt1) br.ret.sptk brp ;; // // Another processor has inserted its lock queue entry in the lock queue, // but has not yet written its lock queue entry address in the current // processor's next link. Spin until the lock queue address is written. // Kirqsl20: YIELD #ifdef CAPKERN_SYNCH_POINTS add t9 = 1, t9 #endif ld8 t3 = [t0] // get next lock queue entry addr ;; cmp.eq pt0 = r0, t3 // if eq, addr not written yet (pt0) br.sptk Kirqsl20 // try again Kirqsl30: add t6 = LqLock, t3 ;; ld8.nt1 t2 = [t6] // get spinlock addr and lock bit st8 [t0] = r0 // clear next lock queue entry addr ;; // Set owner bit, clear wait bit. xor t2 = (LOCK_QUEUE_OWNER|LOCK_QUEUE_WAIT), t2 ;; st8.rel [t6] = t2 #ifdef CAPKERN_SYNCH_POINTS cmp.eq pt2 = t9, zero (pt2) br.sptk KirqslSkipCollLog and t6 = ~7, t4 CAPSPINLOG2INT(t9,t6,9,t7,t8,t10,t11,pt2) KirqslSkipCollLog: #endif #endif // !defined(NT_UP) br.ret.sptk brp ;; LEAF_EXIT(KeReleaseInStackQueuedSpinLockFromDpcLevel) //++ // // VOID // KiLowerIrqlSoftwareInterruptPending( // IN TEMP_REG NewIrql // ) // // Routine Description: // // This function is entered directly from a LEAF function that is // lowering IRQL before it exits when there is a software interrupt // pending that will fire as a result of lowering IRQL. // // In this special case, we need to promote to a nested entry in // order to process the simulated interrupt. // // Return is directly to the caller of the leaf function. // // This routine is entered with interrupts disabled, this is a // side effect of the code that branched here needing interrupts // disabled while checking and lowering. // // Arguments: // // NewIrql - Because we are branched to from a leaf routine, // the argument must be passed in non-windowed // register t22 (r31). // // Return Value: // // None. // //-- NESTED_ENTRY(KiLowerIrqlSoftwareInterruptPending) NESTED_SETUP(0,3,1,0) PROLOGUE_END mov out0 = t22 ssm 1 << PSR_I ;; br.call.sptk brp = KiCheckForSoftwareInterrupt;; NESTED_RETURN NESTED_EXIT(KiLowerIrqlSoftwareInterruptPending)