// ke_irp.c - I/O Request Packet dispatch and queue helpers // // This file lives in the kernel layer and is the ONLY place that bridges the // IRP abstraction to kernel sync objects. All calls to KeEventCreate, // KeSignalEvent, KeWaitForEvent, KeSignalSemaphore, and ObDereferenceObject // are confined here; hal_disk.c and io_driver.h remain free of those symbols. // // Dependency graph (→ = includes / calls): // ke_irp.c → ke_irp.h (IRP type, IRP_QUEUE, forward decls) // → ke_event.h (KeEventCreate, KeSignalEvent, KeWaitForEvent) // → ke_semaphore.h (KeSignalSemaphore) // → ob_object.h (ObDereferenceObject) // → io_driver.h (HalDiskDispatch) // hal_disk.c → io_driver.h only (no ke_event.h, no ke_semaphore.h) #include #include #include #include #include // ------------------------------------------------------------------------- // IRP_QUEUE operations // ------------------------------------------------------------------------- // These are defined here (not as static inlines in ke_irp.h) so that // ke_irp.h can avoid including ke_semaphore.h, keeping the header safe // for inclusion from the HAL layer. __declspec(dllexport) void IrpQueueInit(IRP_QUEUE *q, struct SEMAPHORE *sem) { q->Lock = (SPINLOCK)SPINLOCK_INIT; q->Semaphore = sem; LIST_INIT(&q->Head); } __declspec(dllexport) void IrpQueueEnqueue(IRP_QUEUE *q, IRP *irp) { uintptr_t flags = KeAcquireSpinlock(&q->Lock); if (irp->Flags & IRP_FLAG_PRIORITY) ListInsertHead(&q->Head, &irp->Link); else ListInsertTail(&q->Head, &irp->Link); KeReleaseSpinlock(&q->Lock, flags); // Signal outside the spinlock — safe, and avoids priority-inversion risk. KeSignalSemaphore((SEMAPHORE *)q->Semaphore); } __declspec(dllexport) IRP *IrpQueueDequeue(IRP_QUEUE *q) { uintptr_t flags = KeAcquireSpinlock(&q->Lock); LIST_ENTRY *node = ListPopHead(&q->Head); KeReleaseSpinlock(&q->Lock, flags); if (!node) return (IRP *)0; return CONTAINING_RECORD(node, IRP, Link); } // ------------------------------------------------------------------------- // IoSubmitIrp // ------------------------------------------------------------------------- // Calls HalDiskDispatch() which is HAL-layer safe (no event references). // If the dispatch ran inline (sync fallback), we fire the completion hooks // here in the kernel layer where we can legally call KeSignalEvent. __declspec(dllexport) KSTATUS IoSubmitIrp(IRP *irp) { if (!irp) return STATUS_INVALID_PARAMETER; KSTATUS st = HalDiskDispatch(irp->DriveIndex, irp); if (st == STATUS_NOT_FOUND) return STATUS_NOT_FOUND; if (st == STATUS_SUCCESS) { // Sync fallback ran; irp->Status is already written. // Fire completion hooks now — we are in kernel context so this is safe. if (irp->CompletionEvent) KeSignalEvent((EVENT *)irp->CompletionEvent); if (irp->CompletionFn) irp->CompletionFn(irp, irp->CompletionCtx); } // Return STATUS_PENDING in both cases so callers have a uniform contract: // "completion is signalled via the event/callback, not this return value." return STATUS_PENDING; } // ------------------------------------------------------------------------- // IoSubmitIrpSync // ------------------------------------------------------------------------- // Creates a stack-allocated auto-reset event, attaches it to the IRP, // submits, and blocks. Callers that already provided a CompletionEvent // should use IoSubmitIrp() directly and wait on their own event instead. __declspec(dllexport) KSTATUS IoSubmitIrpSync(IRP *irp) { if (!irp) return STATUS_INVALID_PARAMETER; EVENT *ev = (EVENT *)0; KSTATUS st = KeEventCreate(EventAutoReset, 0, &ev); if (!KSUCCESS(st)) return st; // Attach our event; preserve any callback the caller may have set. irp->CompletionEvent = ev; st = IoSubmitIrp(irp); // IoSubmitIrp always returns STATUS_PENDING on the non-error paths. // The event is signalled either by the driver worker (async) or by // IoSubmitIrp itself (sync fallback), so KeWaitForEvent will always // return promptly in the sync-fallback case. if (st == STATUS_PENDING) KeWaitForEvent(ev); // Release our reference to the temporary event object. ObDereferenceObject(&ev->Header); return irp->Status; }