277 lines
9.1 KiB
C
277 lines
9.1 KiB
C
// io_device.c - kernel driver/device dispatch and disk registry
|
|
#include <string.h>
|
|
#include <ntlite/ddi/io_driver.h>
|
|
#include <kernel/ke_irp.h>
|
|
#include <kernel/mm_virt.h>
|
|
#include <kernel/ke_debug.h>
|
|
|
|
|
|
#define MAX_PUBLISHED_DEVICES 64
|
|
#define MAX_DRIVER_MATCHERS 32
|
|
|
|
typedef struct IO_DRIVER_MATCHER_ENTRY {
|
|
bool Used;
|
|
const char* DriverName;
|
|
IO_BUS_TYPE BusType;
|
|
IO_DRIVER_MATCH_CALLBACK Match;
|
|
IO_DRIVER_BIND_CALLBACK Bind;
|
|
void* Context;
|
|
} IO_DRIVER_MATCHER_ENTRY;
|
|
|
|
static IO_PUBLISHED_DEVICE gPublishedDevices[MAX_PUBLISHED_DEVICES];
|
|
static uint32_t gPublishedDeviceCount;
|
|
static IO_DRIVER_MATCHER_ENTRY gDriverMatchers[MAX_DRIVER_MATCHERS];
|
|
static uint32_t gDriverMatcherCount;
|
|
|
|
static const char* IoBusTypeName(IO_BUS_TYPE busType) {
|
|
switch (busType) {
|
|
case IoBusPci: return "pci";
|
|
case IoBusUsb: return "usb";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
static bool IoTryBindDevice(IO_PUBLISHED_DEVICE* device,
|
|
IO_DRIVER_MATCHER_ENTRY* matcher) {
|
|
if (!device || !matcher || !matcher->Used || device->Bound)
|
|
return false;
|
|
if (matcher->BusType != IoBusUnknown && matcher->BusType != device->BusType)
|
|
return false;
|
|
if (!matcher->Match || !matcher->Bind)
|
|
return false;
|
|
if (!matcher->Match(device, matcher->Context))
|
|
return false;
|
|
|
|
KSTATUS st = matcher->Bind(device, matcher->Context);
|
|
if (KSUCCESS(st)) {
|
|
device->Bound = true;
|
|
device->BoundDriverName = matcher->DriverName;
|
|
DbgPrint("io: %s device %s bound by %s\n",
|
|
IoBusTypeName(device->BusType),
|
|
device->Name ? device->Name : "<unnamed>",
|
|
matcher->DriverName ? matcher->DriverName : "<unnamed>");
|
|
return true;
|
|
}
|
|
|
|
DbgPrint("io: %s matcher %s accepted %s but bind failed st=0x%x\n",
|
|
IoBusTypeName(device->BusType),
|
|
matcher->DriverName ? matcher->DriverName : "<unnamed>",
|
|
device->Name ? device->Name : "<unnamed>",
|
|
st);
|
|
return false;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoRegisterDriverMatcher(const char* driverName,
|
|
IO_BUS_TYPE busType,
|
|
IO_DRIVER_MATCH_CALLBACK match,
|
|
IO_DRIVER_BIND_CALLBACK bind,
|
|
void* context) {
|
|
if (!driverName || !match || !bind)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (gDriverMatcherCount >= MAX_DRIVER_MATCHERS)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
IO_DRIVER_MATCHER_ENTRY* entry = &gDriverMatchers[gDriverMatcherCount++];
|
|
memset(entry, 0, sizeof(*entry));
|
|
entry->Used = true;
|
|
entry->DriverName = driverName;
|
|
entry->BusType = busType;
|
|
entry->Match = match;
|
|
entry->Bind = bind;
|
|
entry->Context = context;
|
|
|
|
DbgPrint("io: registered %s matcher %s\n",
|
|
IoBusTypeName(busType), driverName);
|
|
|
|
for (uint32_t i = 0; i < gPublishedDeviceCount; i++) {
|
|
IoTryBindDevice(&gPublishedDevices[i], entry);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoPublishDevice(const IO_PUBLISHED_DEVICE* device) {
|
|
if (!device || device->BusType == IoBusUnknown)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (gPublishedDeviceCount >= MAX_PUBLISHED_DEVICES)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
IO_PUBLISHED_DEVICE* stored = &gPublishedDevices[gPublishedDeviceCount++];
|
|
memcpy(stored, device, sizeof(*stored));
|
|
stored->Bound = false;
|
|
stored->BoundDriverName = (const char*)0;
|
|
|
|
|
|
for (uint32_t i = 0; i < gDriverMatcherCount; i++) {
|
|
if (IoTryBindDevice(stored, &gDriverMatchers[i]))
|
|
break;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) uint32_t IoPublishedDeviceCount(void) {
|
|
return gPublishedDeviceCount;
|
|
}
|
|
|
|
#define MAX_DISK_DEVICES 8
|
|
#define MAX_DISK_ARRIVAL_CALLBACKS 16
|
|
|
|
typedef struct IO_DISK_CALLBACK_ENTRY {
|
|
bool Used;
|
|
IO_DISK_ARRIVAL_CALLBACK Callback;
|
|
void* Context;
|
|
} IO_DISK_CALLBACK_ENTRY;
|
|
|
|
static DEVICE_OBJECT* gDiskDevices[MAX_DISK_DEVICES];
|
|
static IO_DISK_CALLBACK_ENTRY gDiskArrivalCallbacks[MAX_DISK_ARRIVAL_CALLBACKS];
|
|
static uint32_t gDiskArrivalCallbackCount;
|
|
|
|
static void IoNotifyDiskArrived(uint8_t driveIndex, DEVICE_OBJECT* device) {
|
|
for (uint32_t i = 0; i < gDiskArrivalCallbackCount; i++) {
|
|
IO_DISK_CALLBACK_ENTRY* entry = &gDiskArrivalCallbacks[i];
|
|
if (entry->Used && entry->Callback)
|
|
entry->Callback(driveIndex, device, entry->Context);
|
|
}
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoCreateDevice(DRIVER_OBJECT* driver,
|
|
size_t extensionSize,
|
|
DEVICE_OBJECT** outDevice) {
|
|
if (!driver || !outDevice) return STATUS_INVALID_PARAMETER;
|
|
|
|
size_t total = sizeof(DEVICE_OBJECT) + extensionSize;
|
|
DEVICE_OBJECT* dev = (DEVICE_OBJECT*)MmAllocatePool(total);
|
|
if (!dev) return STATUS_NO_MEMORY;
|
|
|
|
memset(dev, 0, total);
|
|
dev->DriverObject = driver;
|
|
if (extensionSize)
|
|
dev->DeviceExtension = (void*)((uint8_t*)dev + sizeof(DEVICE_OBJECT));
|
|
dev->AlignmentRequirement = 1;
|
|
dev->DiskIndex = 0xFF;
|
|
|
|
*outDevice = dev;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) void IoDeleteDevice(DEVICE_OBJECT* device) {
|
|
if (!device) return;
|
|
MmFreePool(device);
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoRegisterDiskDevice(uint8_t driveIndex,
|
|
DEVICE_OBJECT* device) {
|
|
if (driveIndex >= MAX_DISK_DEVICES || !device || !device->DriverObject)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (gDiskDevices[driveIndex])
|
|
return STATUS_ALREADY_EXISTS;
|
|
|
|
device->Flags |= DEVICE_OBJECT_DISK;
|
|
device->DiskIndex = driveIndex;
|
|
gDiskDevices[driveIndex] = device;
|
|
IoNotifyDiskArrived(driveIndex, device);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoRegisterDiskArrivalCallback(IO_DISK_ARRIVAL_CALLBACK callback,
|
|
void* context) {
|
|
if (!callback)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if (gDiskArrivalCallbackCount >= MAX_DISK_ARRIVAL_CALLBACKS)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
IO_DISK_CALLBACK_ENTRY* entry = &gDiskArrivalCallbacks[gDiskArrivalCallbackCount++];
|
|
memset(entry, 0, sizeof(*entry));
|
|
entry->Used = true;
|
|
entry->Callback = callback;
|
|
entry->Context = context;
|
|
|
|
for (uint8_t i = 0; i < MAX_DISK_DEVICES; i++) {
|
|
if (gDiskDevices[i])
|
|
callback(i, gDiskDevices[i], context);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoEnumerateDiskDevices(IO_DISK_ARRIVAL_CALLBACK callback,
|
|
void* context) {
|
|
if (!callback)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
for (uint8_t i = 0; i < MAX_DISK_DEVICES; i++) {
|
|
if (gDiskDevices[i])
|
|
callback(i, gDiskDevices[i], context);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) DEVICE_OBJECT* IoGetDiskDevice(uint8_t driveIndex) {
|
|
if (driveIndex >= MAX_DISK_DEVICES) return (DEVICE_OBJECT*)0;
|
|
return gDiskDevices[driveIndex];
|
|
}
|
|
|
|
__declspec(dllexport) bool IoDiskDevicePresent(uint8_t driveIndex) {
|
|
return IoGetDiskDevice(driveIndex) != (DEVICE_OBJECT*)0;
|
|
}
|
|
|
|
__declspec(dllexport) uint64_t IoDiskSectorCount(uint8_t driveIndex) {
|
|
DEVICE_OBJECT* dev = IoGetDiskDevice(driveIndex);
|
|
return dev ? dev->SectorCount : 0;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoCallDriver(DEVICE_OBJECT* device, IRP* irp) {
|
|
if (!device || !device->DriverObject || !irp)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
IO_MAJOR_FUNCTION major = (irp->Flags & IRP_FLAG_WRITE) ? IRP_MJ_WRITE : IRP_MJ_READ;
|
|
if (major >= IRP_MJ_MAXIMUM_FUNCTION)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
PDRIVER_DISPATCH dispatch = device->DriverObject->MajorFunction[major];
|
|
if (!dispatch)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
return dispatch(device, irp);
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoReadDiskSectors(uint8_t driveIndex,
|
|
uint64_t lba,
|
|
uint32_t count,
|
|
void* buffer) {
|
|
if (!buffer || count == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
IRP irp;
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.DriveIndex = driveIndex;
|
|
irp.Flags = 0;
|
|
irp.Lba = lba;
|
|
irp.SectorCount = count;
|
|
irp.Buffer = buffer;
|
|
irp.Status = STATUS_FAILURE;
|
|
irp.Information = 0;
|
|
|
|
return IoSubmitIrpSync(&irp);
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS IoWriteDiskSectors(uint8_t driveIndex,
|
|
uint64_t lba,
|
|
uint32_t count,
|
|
const void* buffer) {
|
|
if (!buffer || count == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
IRP irp;
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.DriveIndex = driveIndex;
|
|
irp.Flags = IRP_FLAG_WRITE;
|
|
irp.Lba = lba;
|
|
irp.SectorCount = count;
|
|
irp.Buffer = (void*)buffer;
|
|
irp.Status = STATUS_FAILURE;
|
|
irp.Information = 0;
|
|
|
|
return IoSubmitIrpSync(&irp);
|
|
}
|