Files
nt-lite/code/kernel/ke_irp.c
T

131 lines
4.9 KiB
C

// 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 <kernel/ke_irp.h>
#include <kernel/ke_event.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_apc.h>
#include <kernel/ob_object.h>
#include <ntlite/ddi/io_driver.h>
// -------------------------------------------------------------------------
// 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;
}