// ke_dpc.c - deferred procedure calls #include #include #include #include static LIST_ENTRY gDpcQueue; static SPINLOCK gDpcLock; static volatile long gDpcInitialized = 0; static volatile long gDpcActive = 0; void KeInitializeDpcSystem(void) { LIST_INIT(&gDpcQueue); gDpcLock = (SPINLOCK)SPINLOCK_INIT; gDpcActive = 0; gDpcInitialized = 1; } static void KiEnsureDpcInitialized(void) { if (gDpcInitialized) return; // Single-CPU bring-up path: this is only a safety net for early callers. KeInitializeDpcSystem(); } __declspec(dllexport) void KeInitializeDpc(KDPC* dpc, KDEFERRED_ROUTINE routine, void* context) { if (!dpc) return; LIST_INIT(&dpc->Link); dpc->Routine = routine; dpc->Context = context; dpc->SystemArgument1 = (void*)0; dpc->SystemArgument2 = (void*)0; dpc->Inserted = 0; } __declspec(dllexport) bool KeInsertQueueDpc(KDPC* dpc, void* systemArgument1, void* systemArgument2) { if (!dpc || !dpc->Routine) return false; KiEnsureDpcInitialized(); // Coalesce repeated inserts of the same KDPC. The first insert owns the // argument pair until the DPC is popped by the dispatcher. if (_InterlockedCompareExchange(&dpc->Inserted, 1, 0) != 0) return false; dpc->SystemArgument1 = systemArgument1; dpc->SystemArgument2 = systemArgument2; uintptr_t flags = KeAcquireSpinlock(&gDpcLock); ListInsertTail(&gDpcQueue, &dpc->Link); KeReleaseSpinlock(&gDpcLock, flags); return true; } void KiDrainDpcQueue(void) { if (!gDpcInitialized) return; // Never run DPC routines above DISPATCH_LEVEL. If a nested IRQ reaches here // at DIRQL, leave the work pending for the outermost exit. if (KeGetCurrentIrql() > DISPATCH_LEVEL) return; if (_InterlockedCompareExchange(&gDpcActive, 1, 0) != 0) return; KIRQL oldIrql = KeGetCurrentIrql(); if (oldIrql < DISPATCH_LEVEL) KeRaiseIrql(DISPATCH_LEVEL); for (;;) { uintptr_t flags = KeAcquireSpinlock(&gDpcLock); LIST_ENTRY* node = ListPopHead(&gDpcQueue); if (!node) { KeReleaseSpinlock(&gDpcLock, flags); break; } KDPC* dpc = CONTAINING_RECORD(node, KDPC, Link); void* arg1 = dpc->SystemArgument1; void* arg2 = dpc->SystemArgument2; dpc->SystemArgument1 = (void*)0; dpc->SystemArgument2 = (void*)0; dpc->Inserted = 0; KeReleaseSpinlock(&gDpcLock, flags); KeAssertIrql(DISPATCH_LEVEL); dpc->Routine(dpc, dpc->Context, arg1, arg2); KeAssertIrql(DISPATCH_LEVEL); } gDpcActive = 0; if (oldIrql < DISPATCH_LEVEL) KeLowerIrql(oldIrql); }