113 lines
4.4 KiB
C
113 lines
4.4 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 (HalDiskDispatch)
|
|
// hal_disk.c → io_driver.h only (no ke_event.h, no ke_semaphore.h)
|
|
|
|
#include <ke_irp.h>
|
|
#include <ke_event.h>
|
|
#include <ke_semaphore.h>
|
|
#include <ob_object.h>
|
|
#include <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);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// 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;
|
|
}
|