// 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 (DEVICE_OBJECT / IoCallDriver) #include #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); } // ------------------------------------------------------------------------- // IoCompleteRequest // ------------------------------------------------------------------------- static size_t IoIrpTransferBytes(const IRP *irp, KSTATUS status) { if (!irp || !KSUCCESS(status)) return 0; return (size_t)irp->SectorCount * 512u; } __declspec(dllexport) void IoCompleteRequest(IRP *irp, KSTATUS status, size_t information) { if (!irp) return; irp->Status = status; irp->Information = information; // Queue the APC before waking event waiters so a thread that waits on the // completion event will see and deliver the APC on its wait-return path. if (irp->CompletionApc) KeInsertQueueApc((KAPC *)irp->CompletionApc, irp, (void *)(uintptr_t)status); if (irp->CompletionEvent) KeSignalEvent((EVENT *)irp->CompletionEvent); if (irp->CompletionFn) irp->CompletionFn(irp, irp->CompletionCtx); } // ------------------------------------------------------------------------- // IoSubmitIrp // ------------------------------------------------------------------------- // Dispatches through the registered DEVICE_OBJECT. // Accepted IRPs are always completed by the target driver via IoCompleteRequest(). __declspec(dllexport) KSTATUS IoSubmitIrp(IRP *irp) { if (!irp) return STATUS_INVALID_PARAMETER; irp->Status = STATUS_FAILURE; irp->Information = 0; DEVICE_OBJECT *device = IoGetDiskDevice(irp->DriveIndex); if (!device) return STATUS_NOT_FOUND; KSTATUS st = IoCallDriver(device, irp); // Definitive driver contract: // STATUS_PENDING means the IRP was accepted and will be completed via // IoCompleteRequest(), possibly inline before IoCallDriver returns. // Any other status means the IRP was rejected and no completion occurs. return st; } // ------------------------------------------------------------------------- // 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 APC/callback the caller may have set. irp->CompletionEvent = ev; st = IoSubmitIrp(irp); if (st == STATUS_PENDING) { KeWaitForEvent(ev, 0); st = irp->Status; } // Release our reference to the temporary event object. ObDereferenceObject(&ev->Header); return st; }