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

227 lines
9.5 KiB
C

// Driver module identity and versioned declarative module exports.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddk/module.h>
#include <kernel/driver_module.h>
#include <kernel/ldr_image.h>
#include <kernel/mm_virt.h>
#include <kernel/io_pnp.h>
#include <kernel/ke_debug.h>
#define DM_NAME_MAX 64u
typedef enum DM_MODULE_STATE {
DmModuleStarting,
DmModuleStarted,
DmModuleFailed,
DmModuleStopping,
DmModuleStopped,
} DM_MODULE_STATE;
struct NTL_MODULE {
struct NTL_MODULE* Next;
char Name[DM_NAME_MAX];
uintptr_t ImageBase;
DM_MODULE_STATE State;
NTL_MODULE_PREPARE_SHUTDOWN PrepareShutdown;
NTL_MODULE_COMMIT_SHUTDOWN CommitShutdown;
};
static NTL_MODULE* gDriverModules;
static bool DmImageContains(uintptr_t base, size_t imageSize, uintptr_t ptr, size_t bytes) {
return ptr >= base && ptr - base <= imageSize && bytes <= imageSize - (ptr - base);
}
__declspec(dllexport) const char* NtlModuleGetName(const NTL_MODULE* module) {
return module ? module->Name : (const char*)0;
}
static bool DmValidImageString(uintptr_t base, size_t size, const char* text,
size_t maxLength) {
if (!DmImageContains(base, size, (uintptr_t)text, 1u)) return false;
for (size_t i = 0; i < maxLength; ++i) {
if (!DmImageContains(base, size, (uintptr_t)text + i, 1u)) return false;
if (!text[i])
return i && SysSvIsUtf8Text(SysSvFromParts(text, i));
}
return false;
}
static bool DmValidIoDriver(uintptr_t imageBase, size_t imageSize, const DRIVER_OBJECT* driver) {
if (!DmImageContains(imageBase, imageSize, (uintptr_t)driver, sizeof(*driver)) ||
driver->Header.Type != ObjectTypeDriver ||
driver->Header.LifetimeState != ObjectAlive ||
!driver->Name.Length ||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->Name.Data,
driver->Name.Length) ||
!SysSvIsUtf8Text(driver->Name) ||
(driver->DriverAddDevice &&
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->DriverAddDevice, 1u)) ||
(driver->DriverUnload &&
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->DriverUnload, 1u)))
return false;
for (uint32_t i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) {
if (driver->MajorFunction[i] &&
!DmImageContains(imageBase, imageSize,
(uintptr_t)driver->MajorFunction[i], 1u))
return false;
}
if (!driver->IdTable) return true;
if (!driver->DriverAddDevice ||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->IdTable,
sizeof(*driver->IdTable)) ||
!driver->IdTable->Count || driver->IdTable->Count > 32u ||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->IdTable->Ids,
driver->IdTable->Count * sizeof(IO_DEVICE_ID)))
return false;
for (size_t i = 0; i < driver->IdTable->Count; ++i) {
const IO_DEVICE_ID* id = &driver->IdTable->Ids[i];
if ((id->BusType != IoBusPci && id->BusType != IoBusUsb &&
id->BusType != IoBusUnknown) ||
!id->Id.Length ||
!DmImageContains(imageBase, imageSize, (uintptr_t)id->Id.Data,
id->Id.Length) ||
!SysSvIsUtf8Text(id->Id))
return false;
}
return true;
}
KSTATUS DmStartLoadedDriver(const char* name, uintptr_t imageBase) {
if (!name || !*name || !imageBase)
return NTLSTATUS_INVALID_PARAMETER;
size_t length = 0;
while (length < DM_NAME_MAX && name[length])
length++;
if (!length || length == DM_NAME_MAX)
return NTLSTATUS_INVALID_PARAMETER;
for (NTL_MODULE* it = gDriverModules; it; it = it->Next) {
if (strcmp(it->Name, name) == 0 || it->ImageBase == imageBase)
return NTLSTATUS_ALREADY_EXISTS;
}
size_t imageSize = LdrGetImageSize(imageBase);
if (!imageSize)
return NTLSTATUS_INVALID_IMAGE_FORMAT;
const NTL_MODULE_EXPORT* manifest =
(const NTL_MODULE_EXPORT*)LdrGetImageExport(imageBase, NTL_DDK_MODULE_EXPORT_NAME);
NTL_MODULE_INITIALIZE initialize = (NTL_MODULE_INITIALIZE)0;
if (!manifest) return NTLSTATUS_INVALID_IMAGE_FORMAT;
{
if (!DmImageContains(imageBase, imageSize, (uintptr_t)manifest, sizeof(*manifest)) ||
manifest->Signature != NTL_DDK_MODULE_SIGNATURE ||
manifest->Version != NTL_DDK_MODULE_ABI_VERSION ||
manifest->Size != sizeof(*manifest) || manifest->Flags != 0u ||
manifest->Reserved != 0u || manifest->Reserved2 != 0u ||
(manifest->Initialize && !DmImageContains(imageBase, imageSize,
(uintptr_t)manifest->Initialize, 1u)) ||
(manifest->PrepareShutdown && !DmImageContains(imageBase, imageSize,
(uintptr_t)manifest->PrepareShutdown, 1u)) ||
(manifest->CommitShutdown && !DmImageContains(imageBase, imageSize,
(uintptr_t)manifest->CommitShutdown, 1u)))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
if ((manifest->DeviceCount && !manifest->Devices) ||
manifest->DeviceCount > 64u ||
(manifest->DeviceCount &&
!DmImageContains(imageBase, imageSize, (uintptr_t)manifest->Devices,
(size_t)manifest->DeviceCount * sizeof(NTL_DEVICE_DESC))))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
for (uint32_t i = 0; i < manifest->DeviceCount; ++i) {
const NTL_DEVICE_DESC* d = &manifest->Devices[i];
if (d->Size != sizeof(*d) || d->Flags || !d->Name || !d->Start ||
!DmValidImageString(imageBase, imageSize, d->Name, 64u) ||
!DmImageContains(imageBase, imageSize, (uintptr_t)d->Start, 1u) ||
(d->Stop && !DmImageContains(imageBase, imageSize, (uintptr_t)d->Stop, 1u)) ||
(d->CancelStart && !DmImageContains(imageBase, imageSize,
(uintptr_t)d->CancelStart, 1u)) ||
!d->MatchCount || d->MatchCount > 32u ||
!DmImageContains(imageBase, imageSize, (uintptr_t)d->Matches,
(size_t)d->MatchCount * sizeof(NTL_DEVICE_MATCH)))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
for (uint32_t j = 0; j < d->MatchCount; ++j) {
const NTL_DEVICE_MATCH* m = &d->Matches[j];
if ((m->Bus != NtlBusPci && m->Bus != NtlBusUsb) ||
!DmValidImageString(imageBase, imageSize, m->Id, 96u))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
}
}
if ((manifest->IoDriverCount && !manifest->IoDrivers) ||
manifest->IoDriverCount > 64u ||
(manifest->IoDriverCount &&
!DmImageContains(imageBase, imageSize, (uintptr_t)manifest->IoDrivers,
(size_t)manifest->IoDriverCount * sizeof(*manifest->IoDrivers))))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
for (uint32_t i = 0; i < manifest->IoDriverCount; ++i) {
if (!DmImageContains(imageBase, imageSize,
(uintptr_t)manifest->IoDrivers[i], sizeof(DRIVER_OBJECT)))
return NTLSTATUS_INVALID_IMAGE_FORMAT;
}
initialize = manifest->Initialize;
}
NTL_MODULE* module = (NTL_MODULE*)MmAllocatePool(sizeof(*module));
if (!module)
return NTLSTATUS_NO_MEMORY;
memset(module, 0, sizeof(*module));
memcpy(module->Name, name, length);
module->ImageBase = imageBase;
module->State = DmModuleStarting;
module->PrepareShutdown = manifest->PrepareShutdown;
module->CommitShutdown = manifest->CommitShutdown;
module->Next = gDriverModules;
gDriverModules = module;
KSTATUS status = initialize ? initialize(module) : NTLSTATUS_SUCCESS;
if (!KSUCCESS(status) && !KFAILED(status))
status = NTLSTATUS_INVALID_PARAMETER;
if (KSUCCESS(status) && manifest) {
for (uint32_t i = 0; i < manifest->IoDriverCount; ++i) {
if (!DmValidIoDriver(imageBase, imageSize, manifest->IoDrivers[i])) {
status = NTLSTATUS_INVALID_IMAGE_FORMAT;
break;
}
}
}
if (KSUCCESS(status))
status = IoPnpPublishModule(module, manifest->Devices, manifest->DeviceCount,
manifest->IoDrivers, manifest->IoDriverCount);
module->State = KSUCCESS(status) ? DmModuleStarted : DmModuleFailed;
return status;
}
KSTATUS DmPrepareShutdown(void) {
KSTATUS firstError = NTLSTATUS_SUCCESS;
for (NTL_MODULE* module = gDriverModules; module; module = module->Next) {
if (module->State != DmModuleStarted) continue;
module->State = DmModuleStopping;
if (!module->PrepareShutdown) continue;
KSTATUS st = module->PrepareShutdown(module);
if (!KSUCCESS(st)) {
DbgPrint("shutdown: prepare %s failed 0x%x\n", module->Name, st);
if (KSUCCESS(firstError)) firstError = st;
}
}
return firstError;
}
void DmCommitShutdown(void) {
for (NTL_MODULE* module = gDriverModules; module; module = module->Next) {
if (module->State != DmModuleStopping) continue;
if (module->CommitShutdown) module->CommitShutdown(module);
module->State = DmModuleStopped;
}
}