97 lines
3.1 KiB
C
97 lines
3.1 KiB
C
// Public nt-lite DDK synchronization declarations.
|
|
|
|
#pragma once
|
|
|
|
#include <ntlite/atomic.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <intrin.h>
|
|
#include <ntlite/ddk/irql.h>
|
|
|
|
// Legacy spinlock storage used by current driver-visible objects.
|
|
typedef struct SPINLOCK {
|
|
volatile int32_t Locked;
|
|
} SPINLOCK;
|
|
|
|
#define SPINLOCK_INIT { 0 }
|
|
|
|
// Requests a synchronization wait without a timeout.
|
|
#define KE_WAIT_INFINITE UINT64_MAX
|
|
|
|
#define KI_SPINLOCK_IRQL_SHIFT ((unsigned)(sizeof(uintptr_t) * 8u - 8u))
|
|
#define KI_SPINLOCK_FLAGS_MASK (~(((uintptr_t)0xFFu) << KI_SPINLOCK_IRQL_SHIFT))
|
|
|
|
// Packs the interrupt flags and prior IRQL into a spinlock cookie.
|
|
static __forceinline uintptr_t KiPackSpinlockCookie(uintptr_t flags, KIRQL oldIrql) {
|
|
return (flags & KI_SPINLOCK_FLAGS_MASK) |
|
|
(((uintptr_t)oldIrql) << KI_SPINLOCK_IRQL_SHIFT);
|
|
}
|
|
|
|
// Extracts the saved interrupt flags from a spinlock cookie.
|
|
static __forceinline uintptr_t KiSpinlockCookieFlags(uintptr_t cookie) {
|
|
return cookie & KI_SPINLOCK_FLAGS_MASK;
|
|
}
|
|
|
|
// Extracts the saved IRQL from a spinlock cookie.
|
|
static __forceinline KIRQL KiSpinlockCookieIrql(uintptr_t cookie) {
|
|
return (KIRQL)(cookie >> KI_SPINLOCK_IRQL_SHIFT);
|
|
}
|
|
|
|
// Acquires a spinlock and raises to at least DISPATCH_LEVEL.
|
|
static __forceinline uintptr_t KeAcquireSpinlock(SPINLOCK* sl) {
|
|
uintptr_t flags = KeDisableInterrupts();
|
|
|
|
KIRQL oldIrql = KeGetCurrentIrql();
|
|
if (oldIrql < DISPATCH_LEVEL)
|
|
KeRaiseIrql(DISPATCH_LEVEL);
|
|
|
|
while (NtlAtomicCompareExchange32(&sl->Locked, 1, 0) != 0)
|
|
_mm_pause();
|
|
|
|
return KiPackSpinlockCookie(flags, oldIrql);
|
|
}
|
|
|
|
// Releases a spinlock and restores the caller's saved IRQL and interrupt state.
|
|
static __forceinline void KeReleaseSpinlock(SPINLOCK* sl, uintptr_t cookie) {
|
|
uintptr_t flags = KiSpinlockCookieFlags(cookie);
|
|
KIRQL oldIrql = KiSpinlockCookieIrql(cookie);
|
|
|
|
NtlAtomicExchange32(&sl->Locked, 0);
|
|
|
|
// Restore the caller's hardware interrupt state before dropping to an
|
|
// IRQL that can dispatch APCs. PASSIVE_LEVEL with IF still clear is not a
|
|
// valid APC execution context.
|
|
KeRestoreInterrupts(flags);
|
|
KeLowerIrql(oldIrql);
|
|
}
|
|
|
|
// Attempts to acquire a spinlock without blocking.
|
|
static __forceinline bool KeTryAcquireSpinlock(SPINLOCK* sl, uintptr_t* outCookie) {
|
|
uintptr_t flags = KeDisableInterrupts();
|
|
|
|
KIRQL oldIrql = KeGetCurrentIrql();
|
|
if (oldIrql < DISPATCH_LEVEL)
|
|
KeRaiseIrql(DISPATCH_LEVEL);
|
|
|
|
if (NtlAtomicCompareExchange32(&sl->Locked, 1, 0) == 0) {
|
|
*outCookie = KiPackSpinlockCookie(flags, oldIrql);
|
|
return true;
|
|
}
|
|
|
|
// Keep the failed-acquire unwind consistent with normal release: restore
|
|
// IF before a PASSIVE transition can dispatch pending APCs.
|
|
KeRestoreInterrupts(flags);
|
|
KeLowerIrql(oldIrql);
|
|
return false;
|
|
}
|
|
|
|
// Acquires a spinlock for DPC-level code.
|
|
static __forceinline uintptr_t KeAcquireSpinlockRaiseToDpc(SPINLOCK* sl) {
|
|
return KeAcquireSpinlock(sl);
|
|
}
|
|
|
|
// Releases a spinlock acquired for DPC-level code.
|
|
static __forceinline void KeReleaseSpinlockFromDpc(SPINLOCK* sl, uintptr_t cookie) {
|
|
KeReleaseSpinlock(sl, cookie);
|
|
}
|