reactos/ntoskrnl/io/iomgr/driver.c
Hermès Bélusca-Maïto 475098c8b1
[NTOS:IO] Don't uppercase the ServiceName in IopDisplayLoadingMessage().
Problematic behaviour was added in commit a97f262ed (r26067), and
commit c39812d1b (r46193) converted to RtlUpcaseUnicodeString() call.

This was modifying the caller's given string. This is not really
a good practice to do so just to make display fancier.

For example, IopInitializeBuiltinDriver(), that calls the display
function, also uses the passed ServiceName later after.

Because IopDisplayLoadingMessage() executes only in SOS mode,
uppercasing the ServiceName in one case but not the other would
implicitly modify the observable OS behaviour.

IopSuffixUnicodeString() is adapted to be similar to RtlPrefixUnicodeString().
2024-02-09 17:16:20 +01:00

2214 lines
69 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/io/iomgr/driver.c
* PURPOSE: Driver Object Management
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Filip Navara (navaraf@reactos.org)
* Hervé Poussineau (hpoussin@reactos.org)
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#include <mm/ARM3/miarm.h>
/* GLOBALS ********************************************************************/
ERESOURCE IopDriverLoadResource;
LIST_ENTRY DriverReinitListHead;
KSPIN_LOCK DriverReinitListLock;
PLIST_ENTRY DriverReinitTailEntry;
PLIST_ENTRY DriverBootReinitTailEntry;
LIST_ENTRY DriverBootReinitListHead;
KSPIN_LOCK DriverBootReinitListLock;
UNICODE_STRING IopHardwareDatabaseKey =
RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM");
static const WCHAR ServicesKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
POBJECT_TYPE IoDriverObjectType = NULL;
extern BOOLEAN PnpSystemInit;
extern BOOLEAN PnPBootDriversLoaded;
extern KEVENT PiEnumerationFinished;
USHORT IopGroupIndex;
PLIST_ENTRY IopGroupTable;
/* TYPES *********************************************************************/
// Parameters packet for Load/Unload work item's context
typedef struct _LOAD_UNLOAD_PARAMS
{
NTSTATUS Status;
PUNICODE_STRING RegistryPath;
WORK_QUEUE_ITEM WorkItem;
KEVENT Event;
PDRIVER_OBJECT DriverObject;
BOOLEAN SetEvent;
} LOAD_UNLOAD_PARAMS, *PLOAD_UNLOAD_PARAMS;
NTSTATUS
IopDoLoadUnloadDriver(
_In_opt_ PUNICODE_STRING RegistryPath,
_Inout_ PDRIVER_OBJECT *DriverObject);
/* PRIVATE FUNCTIONS **********************************************************/
NTSTATUS
NTAPI
IopInvalidDeviceRequest(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
VOID
NTAPI
IopDeleteDriver(IN PVOID ObjectBody)
{
PDRIVER_OBJECT DriverObject = ObjectBody;
PIO_CLIENT_EXTENSION DriverExtension, NextDriverExtension;
PAGED_CODE();
DPRINT1("Deleting driver object '%wZ'\n", &DriverObject->DriverName);
/* There must be no device objects remaining at this point */
ASSERT(!DriverObject->DeviceObject);
/* Get the extension and loop them */
DriverExtension = IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
while (DriverExtension)
{
/* Get the next one */
NextDriverExtension = DriverExtension->NextExtension;
ExFreePoolWithTag(DriverExtension, TAG_DRIVER_EXTENSION);
/* Move on */
DriverExtension = NextDriverExtension;
}
/* Check if the driver image is still loaded */
if (DriverObject->DriverSection)
{
/* Unload it */
MmUnloadSystemImage(DriverObject->DriverSection);
}
/* Check if it has a name */
if (DriverObject->DriverName.Buffer)
{
/* Free it */
ExFreePool(DriverObject->DriverName.Buffer);
}
/* Check if it has a service key name */
if (DriverObject->DriverExtension->ServiceKeyName.Buffer)
{
/* Free it */
ExFreePool(DriverObject->DriverExtension->ServiceKeyName.Buffer);
}
}
NTSTATUS
IopGetDriverNames(
_In_ HANDLE ServiceHandle,
_Out_ PUNICODE_STRING DriverName,
_Out_opt_ PUNICODE_STRING ServiceName)
{
UNICODE_STRING driverName = {.Buffer = NULL}, serviceName;
PKEY_VALUE_FULL_INFORMATION kvInfo;
NTSTATUS status;
PAGED_CODE();
/* 1. Check the "ObjectName" field in the driver's registry key (it has priority) */
status = IopGetRegistryValue(ServiceHandle, L"ObjectName", &kvInfo);
if (NT_SUCCESS(status))
{
/* We've got the ObjectName, use it as the driver name */
if (kvInfo->Type != REG_SZ || kvInfo->DataLength == 0)
{
ExFreePool(kvInfo);
return STATUS_ILL_FORMED_SERVICE_ENTRY;
}
driverName.Length = kvInfo->DataLength - sizeof(UNICODE_NULL);
driverName.MaximumLength = kvInfo->DataLength;
driverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, driverName.MaximumLength, TAG_IO);
if (!driverName.Buffer)
{
ExFreePool(kvInfo);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlMoveMemory(driverName.Buffer,
(PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
driverName.Length);
driverName.Buffer[driverName.Length / sizeof(WCHAR)] = UNICODE_NULL;
ExFreePool(kvInfo);
}
/* Check whether we need to get ServiceName as well, either to construct
* the driver name (because we could not use "ObjectName"), or because
* it is requested by the caller. */
PKEY_BASIC_INFORMATION basicInfo = NULL;
if (!NT_SUCCESS(status) || ServiceName != NULL)
{
/* Retrieve the necessary buffer size */
ULONG infoLength;
status = ZwQueryKey(ServiceHandle, KeyBasicInformation, NULL, 0, &infoLength);
if (status != STATUS_BUFFER_TOO_SMALL)
{
status = (NT_SUCCESS(status) ? STATUS_UNSUCCESSFUL : status);
goto Cleanup;
}
/* Allocate the buffer and retrieve the data */
basicInfo = ExAllocatePoolWithTag(PagedPool, infoLength, TAG_IO);
if (!basicInfo)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
status = ZwQueryKey(ServiceHandle, KeyBasicInformation, basicInfo, infoLength, &infoLength);
if (!NT_SUCCESS(status))
{
goto Cleanup;
}
serviceName.Length = basicInfo->NameLength;
serviceName.MaximumLength = basicInfo->NameLength;
serviceName.Buffer = basicInfo->Name;
}
/* 2. There is no "ObjectName" - construct it ourselves. Depending on the driver type,
* it will be either "\Driver\<ServiceName>" or "\FileSystem\<ServiceName>" */
if (driverName.Buffer == NULL)
{
ASSERT(basicInfo); // Container for serviceName
/* Retrieve the driver type */
ULONG driverType;
status = IopGetRegistryValue(ServiceHandle, L"Type", &kvInfo);
if (!NT_SUCCESS(status))
{
goto Cleanup;
}
if (kvInfo->Type != REG_DWORD || kvInfo->DataLength != sizeof(ULONG))
{
ExFreePool(kvInfo);
status = STATUS_ILL_FORMED_SERVICE_ENTRY;
goto Cleanup;
}
RtlMoveMemory(&driverType,
(PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
sizeof(ULONG));
ExFreePool(kvInfo);
/* Compute the necessary driver name string size */
if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
driverName.MaximumLength = sizeof(FILESYSTEM_ROOT_NAME);
else
driverName.MaximumLength = sizeof(DRIVER_ROOT_NAME);
driverName.MaximumLength += serviceName.Length;
driverName.Length = 0;
/* Allocate and build it */
driverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, driverName.MaximumLength, TAG_IO);
if (!driverName.Buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
RtlAppendUnicodeToString(&driverName, FILESYSTEM_ROOT_NAME);
else
RtlAppendUnicodeToString(&driverName, DRIVER_ROOT_NAME);
RtlAppendUnicodeStringToString(&driverName, &serviceName);
}
if (ServiceName != NULL)
{
ASSERT(basicInfo); // Container for serviceName
/* Allocate a copy for the caller */
PWCHAR buf = ExAllocatePoolWithTag(PagedPool, serviceName.Length, TAG_IO);
if (!buf)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlMoveMemory(buf, serviceName.Buffer, serviceName.Length);
ServiceName->MaximumLength = serviceName.Length;
ServiceName->Length = serviceName.Length;
ServiceName->Buffer = buf;
}
*DriverName = driverName;
status = STATUS_SUCCESS;
Cleanup:
if (basicInfo)
ExFreePoolWithTag(basicInfo, TAG_IO);
if (!NT_SUCCESS(status) && driverName.Buffer)
ExFreePoolWithTag(driverName.Buffer, TAG_IO);
return status;
}
/**
* @brief Determines whether String1 may be a suffix of String2.
* @return TRUE if String2 contains String1 as a suffix.
**/
static BOOLEAN
IopSuffixUnicodeString(
_In_ PCUNICODE_STRING String1,
_In_ PCUNICODE_STRING String2,
_In_ BOOLEAN CaseInSensitive)
{
PWCHAR pc1, pc2;
ULONG NumChars;
if (String2->Length < String1->Length)
return FALSE;
NumChars = String1->Length / sizeof(WCHAR);
pc1 = String1->Buffer;
pc2 = &String2->Buffer[String2->Length / sizeof(WCHAR) - NumChars];
if (pc1 && pc2)
{
if (CaseInSensitive)
{
while (NumChars--)
{
if (RtlUpcaseUnicodeChar(*pc1++) !=
RtlUpcaseUnicodeChar(*pc2++))
{
return FALSE;
}
}
}
else
{
while (NumChars--)
{
if (*pc1++ != *pc2++)
return FALSE;
}
}
return TRUE;
}
return FALSE;
}
/**
* @brief Displays a driver-loading message in SOS mode.
**/
static VOID
FASTCALL
IopDisplayLoadingMessage(
_In_ PCUNICODE_STRING ServiceName)
{
extern BOOLEAN SosEnabled; // See ex/init.c
static const UNICODE_STRING DotSys = RTL_CONSTANT_STRING(L".SYS");
CHAR TextBuffer[256];
if (!SosEnabled) return;
if (!KeLoaderBlock) return;
RtlStringCbPrintfA(TextBuffer, sizeof(TextBuffer),
"%s%sSystem32\\Drivers\\%wZ%s\r\n",
KeLoaderBlock->ArcBootDeviceName,
KeLoaderBlock->NtBootPathName,
ServiceName,
IopSuffixUnicodeString(&DotSys, ServiceName, TRUE)
? "" : ".SYS");
HalDisplayString(TextBuffer);
}
/*
* IopNormalizeImagePath
*
* Normalize an image path to contain complete path.
*
* Parameters
* ImagePath
* The input path and on exit the result path. ImagePath.Buffer
* must be allocated by ExAllocatePool on input. Caller is responsible
* for freeing the buffer when it's no longer needed.
*
* ServiceName
* Name of the service that ImagePath belongs to.
*
* Return Value
* Status
*
* Remarks
* The input image path isn't freed on error.
*/
NTSTATUS
FASTCALL
IopNormalizeImagePath(
_Inout_ _When_(return>=0, _At_(ImagePath->Buffer, _Post_notnull_ __drv_allocatesMem(Mem)))
PUNICODE_STRING ImagePath,
_In_ PUNICODE_STRING ServiceName)
{
UNICODE_STRING SystemRootString = RTL_CONSTANT_STRING(L"\\SystemRoot\\");
UNICODE_STRING DriversPathString = RTL_CONSTANT_STRING(L"\\SystemRoot\\System32\\drivers\\");
UNICODE_STRING DotSysString = RTL_CONSTANT_STRING(L".sys");
UNICODE_STRING InputImagePath;
DPRINT("Normalizing image path '%wZ' for service '%wZ'\n", ImagePath, ServiceName);
InputImagePath = *ImagePath;
if (InputImagePath.Length == 0)
{
ImagePath->Length = 0;
ImagePath->MaximumLength = DriversPathString.Length +
ServiceName->Length +
DotSysString.Length +
sizeof(UNICODE_NULL);
ImagePath->Buffer = ExAllocatePoolWithTag(NonPagedPool,
ImagePath->MaximumLength,
TAG_IO);
if (ImagePath->Buffer == NULL)
return STATUS_NO_MEMORY;
RtlCopyUnicodeString(ImagePath, &DriversPathString);
RtlAppendUnicodeStringToString(ImagePath, ServiceName);
RtlAppendUnicodeStringToString(ImagePath, &DotSysString);
}
else if (InputImagePath.Buffer[0] != L'\\')
{
ImagePath->Length = 0;
ImagePath->MaximumLength = SystemRootString.Length +
InputImagePath.Length +
sizeof(UNICODE_NULL);
ImagePath->Buffer = ExAllocatePoolWithTag(NonPagedPool,
ImagePath->MaximumLength,
TAG_IO);
if (ImagePath->Buffer == NULL)
return STATUS_NO_MEMORY;
RtlCopyUnicodeString(ImagePath, &SystemRootString);
RtlAppendUnicodeStringToString(ImagePath, &InputImagePath);
/* Free caller's string */
ExFreePoolWithTag(InputImagePath.Buffer, TAG_RTLREGISTRY);
}
DPRINT("Normalized image path is '%wZ' for service '%wZ'\n", ImagePath, ServiceName);
return STATUS_SUCCESS;
}
/**
* @brief Initialize a loaded driver
*
* @param[in] ModuleObject
* Module object representing the driver. It can be retrieved by IopLoadServiceModule.
* Freed on failure, so in a such case this should not be accessed anymore
*
* @param[in] ServiceHandle
* Handle to a driver's CCS/Services/<ServiceName> key
*
* @param[out] DriverObject
* This contains the driver object if it was created (even with unsuccessfull result)
*
* @param[out] DriverEntryStatus
* This contains the status value returned by the driver's DriverEntry routine
* (will not be valid of the return value is not STATUS_SUCCESS or STATUS_FAILED_DRIVER_ENTRY)
*
* @return Status of the operation
*/
NTSTATUS
IopInitializeDriverModule(
_In_ PLDR_DATA_TABLE_ENTRY ModuleObject,
_In_ HANDLE ServiceHandle,
_Out_ PDRIVER_OBJECT *OutDriverObject,
_Out_ NTSTATUS *DriverEntryStatus)
{
UNICODE_STRING DriverName, RegistryPath, ServiceName;
NTSTATUS Status;
PAGED_CODE();
Status = IopGetDriverNames(ServiceHandle, &DriverName, &ServiceName);
if (!NT_SUCCESS(Status))
{
MmUnloadSystemImage(ModuleObject);
return Status;
}
DPRINT("Driver name: '%wZ'\n", &DriverName);
/*
* Retrieve the driver's PE image NT header and perform some sanity checks.
* NOTE: We suppose that since the driver has been successfully loaded,
* its NT and optional headers are all valid and have expected sizes.
*/
PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(ModuleObject->DllBase);
ASSERT(NtHeaders);
// NOTE: ModuleObject->SizeOfImage is actually (number of PTEs)*PAGE_SIZE.
ASSERT(ModuleObject->SizeOfImage == ROUND_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage));
ASSERT(ModuleObject->EntryPoint == RVA(ModuleObject->DllBase, NtHeaders->OptionalHeader.AddressOfEntryPoint));
/* Obtain the registry path for the DriverInit routine */
PKEY_NAME_INFORMATION nameInfo;
ULONG infoLength;
Status = ZwQueryKey(ServiceHandle, KeyNameInformation, NULL, 0, &infoLength);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
nameInfo = ExAllocatePoolWithTag(NonPagedPool, infoLength, TAG_IO);
if (nameInfo)
{
Status = ZwQueryKey(ServiceHandle,
KeyNameInformation,
nameInfo,
infoLength,
&infoLength);
if (NT_SUCCESS(Status))
{
RegistryPath.Length = nameInfo->NameLength;
RegistryPath.MaximumLength = nameInfo->NameLength;
RegistryPath.Buffer = nameInfo->Name;
}
else
{
ExFreePoolWithTag(nameInfo, TAG_IO);
}
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
Status = NT_SUCCESS(Status) ? STATUS_UNSUCCESSFUL : Status;
}
if (!NT_SUCCESS(Status))
{
RtlFreeUnicodeString(&ServiceName);
RtlFreeUnicodeString(&DriverName);
MmUnloadSystemImage(ModuleObject);
return Status;
}
/* Create the driver object */
ULONG ObjectSize = sizeof(DRIVER_OBJECT) + sizeof(EXTENDED_DRIVER_EXTENSION);
OBJECT_ATTRIBUTES objAttrs;
PDRIVER_OBJECT driverObject;
InitializeObjectAttributes(&objAttrs,
&DriverName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ObCreateObject(KernelMode,
IoDriverObjectType,
&objAttrs,
KernelMode,
NULL,
ObjectSize,
0,
0,
(PVOID*)&driverObject);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
RtlFreeUnicodeString(&ServiceName);
RtlFreeUnicodeString(&DriverName);
MmUnloadSystemImage(ModuleObject);
DPRINT1("Error while creating driver object \"%wZ\" status %x\n", &DriverName, Status);
return Status;
}
DPRINT("Created driver object 0x%p for \"%wZ\"\n", driverObject, &DriverName);
RtlZeroMemory(driverObject, ObjectSize);
driverObject->Type = IO_TYPE_DRIVER;
driverObject->Size = sizeof(DRIVER_OBJECT);
/* Set the legacy flag if this is not a WDM driver */
if (!(NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER))
driverObject->Flags |= DRVO_LEGACY_DRIVER;
driverObject->DriverSection = ModuleObject;
driverObject->DriverStart = ModuleObject->DllBase;
driverObject->DriverSize = ModuleObject->SizeOfImage;
driverObject->DriverInit = ModuleObject->EntryPoint;
driverObject->HardwareDatabase = &IopHardwareDatabaseKey;
driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
driverObject->DriverExtension->DriverObject = driverObject;
/* Loop all Major Functions */
for (INT i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
/* Invalidate each function */
driverObject->MajorFunction[i] = IopInvalidDeviceRequest;
}
/* Add the Object and get its handle */
HANDLE hDriver;
Status = ObInsertObject(driverObject, NULL, FILE_READ_DATA, 0, NULL, &hDriver);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(nameInfo, TAG_IO);
RtlFreeUnicodeString(&ServiceName);
RtlFreeUnicodeString(&DriverName);
return Status;
}
/* Now reference it */
Status = ObReferenceObjectByHandle(hDriver,
0,
IoDriverObjectType,
KernelMode,
(PVOID*)&driverObject,
NULL);
/* Close the extra handle */
ZwClose(hDriver);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
RtlFreeUnicodeString(&ServiceName);
RtlFreeUnicodeString(&DriverName);
return Status;
}
/* Set up the service key name buffer */
UNICODE_STRING serviceKeyName;
serviceKeyName.Length = 0;
// NULL-terminate for Windows compatibility
serviceKeyName.MaximumLength = ServiceName.MaximumLength + sizeof(UNICODE_NULL);
serviceKeyName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
serviceKeyName.MaximumLength,
TAG_IO);
if (!serviceKeyName.Buffer)
{
ObMakeTemporaryObject(driverObject);
ObDereferenceObject(driverObject);
ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
RtlFreeUnicodeString(&ServiceName);
RtlFreeUnicodeString(&DriverName);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Copy the name and set it in the driver extension */
RtlCopyUnicodeString(&serviceKeyName, &ServiceName);
RtlFreeUnicodeString(&ServiceName);
driverObject->DriverExtension->ServiceKeyName = serviceKeyName;
/* Make a copy of the driver name to store in the driver object */
UNICODE_STRING driverNamePaged;
driverNamePaged.Length = 0;
// NULL-terminate for Windows compatibility
driverNamePaged.MaximumLength = DriverName.MaximumLength + sizeof(UNICODE_NULL);
driverNamePaged.Buffer = ExAllocatePoolWithTag(PagedPool,
driverNamePaged.MaximumLength,
TAG_IO);
if (!driverNamePaged.Buffer)
{
ObMakeTemporaryObject(driverObject);
ObDereferenceObject(driverObject);
ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
RtlFreeUnicodeString(&DriverName);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyUnicodeString(&driverNamePaged, &DriverName);
driverObject->DriverName = driverNamePaged;
/* Finally, call its init function */
Status = driverObject->DriverInit(driverObject, &RegistryPath);
*DriverEntryStatus = Status;
if (!NT_SUCCESS(Status))
{
DPRINT1("'%wZ' initialization failed, status (0x%08lx)\n", &DriverName, Status);
// return a special status value in case of failure
Status = STATUS_FAILED_DRIVER_ENTRY;
}
/* HACK: We're going to say if we don't have any DOs from DriverEntry, then we're not legacy.
* Other parts of the I/O manager depend on this behavior */
if (!driverObject->DeviceObject)
{
driverObject->Flags &= ~DRVO_LEGACY_DRIVER;
}
/* Windows does this fixup, keep it for compatibility */
for (INT i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
/*
* Make sure the driver didn't set any dispatch entry point to NULL!
* Doing so is illegal; drivers shouldn't touch entry points they
* do not implement.
*/
/* Check if it did so anyway */
if (!driverObject->MajorFunction[i])
{
/* Print a warning in the debug log */
DPRINT1("Driver <%wZ> set DriverObject->MajorFunction[%lu] to NULL!\n",
&driverObject->DriverName, i);
/* Fix it up */
driverObject->MajorFunction[i] = IopInvalidDeviceRequest;
}
}
// TODO: for legacy drivers, unload the driver if it didn't create any DO
ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
RtlFreeUnicodeString(&DriverName);
if (!NT_SUCCESS(Status))
{
// if the driver entry has been failed, clear the object
ObMakeTemporaryObject(driverObject);
ObDereferenceObject(driverObject);
return Status;
}
*OutDriverObject = driverObject;
MmFreeDriverInitialization((PLDR_DATA_TABLE_ENTRY)driverObject->DriverSection);
/* Set the driver as initialized */
IopReadyDeviceObjects(driverObject);
if (PnpSystemInit) IopReinitializeDrivers();
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MiResolveImageReferences(IN PVOID ImageBase,
IN PUNICODE_STRING ImageFileDirectory,
IN PUNICODE_STRING NamePrefix OPTIONAL,
OUT PCHAR *MissingApi,
OUT PWCHAR *MissingDriver,
OUT PLOAD_IMPORTS *LoadImports);
//
// Used for images already loaded (boot drivers)
//
CODE_SEG("INIT")
NTSTATUS
NTAPI
LdrProcessDriverModule(PLDR_DATA_TABLE_ENTRY LdrEntry,
PUNICODE_STRING FileName,
PLDR_DATA_TABLE_ENTRY *ModuleObject)
{
NTSTATUS Status;
UNICODE_STRING BaseName, BaseDirectory;
PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
PCHAR MissingApiName, Buffer;
PWCHAR MissingDriverName;
PVOID DriverBase = LdrEntry->DllBase;
/* Allocate a buffer we'll use for names */
Buffer = ExAllocatePoolWithTag(NonPagedPool,
MAXIMUM_FILENAME_LENGTH,
TAG_LDR_WSTR);
if (!Buffer)
{
/* Fail */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Check for a separator */
if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
{
PWCHAR p;
ULONG BaseLength;
/* Loop the path until we get to the base name */
p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
/* Get the length */
BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
BaseLength *= sizeof(WCHAR);
/* Setup the string */
BaseName.Length = (USHORT)BaseLength;
BaseName.Buffer = p;
}
else
{
/* Otherwise, we already have a base name */
BaseName.Length = FileName->Length;
BaseName.Buffer = FileName->Buffer;
}
/* Setup the maximum length */
BaseName.MaximumLength = BaseName.Length;
/* Now compute the base directory */
BaseDirectory = *FileName;
BaseDirectory.Length -= BaseName.Length;
BaseDirectory.MaximumLength = BaseDirectory.Length;
/* Resolve imports */
MissingApiName = Buffer;
Status = MiResolveImageReferences(DriverBase,
&BaseDirectory,
NULL,
&MissingApiName,
&MissingDriverName,
&LoadedImports);
/* Free the temporary buffer */
ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
/* Check the result of the imports resolution */
if (!NT_SUCCESS(Status)) return Status;
/* Return */
*ModuleObject = LdrEntry;
return STATUS_SUCCESS;
}
PDEVICE_OBJECT
IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
/*
* IopInitializeBuiltinDriver
*
* Initialize a driver that is already loaded in memory.
*/
CODE_SEG("INIT")
static
BOOLEAN
IopInitializeBuiltinDriver(IN PLDR_DATA_TABLE_ENTRY BootLdrEntry)
{
PDRIVER_OBJECT DriverObject;
NTSTATUS Status;
PWCHAR Buffer, FileNameWithoutPath;
PWSTR FileExtension;
PUNICODE_STRING ModuleName = &BootLdrEntry->BaseDllName;
PLDR_DATA_TABLE_ENTRY LdrEntry;
PLIST_ENTRY NextEntry;
UNICODE_STRING ServiceName;
BOOLEAN Success;
/*
* Display 'Loading XXX...' message
*/
IopDisplayLoadingMessage(ModuleName);
InbvIndicateProgress();
Buffer = ExAllocatePoolWithTag(PagedPool,
ModuleName->Length + sizeof(UNICODE_NULL),
TAG_IO);
if (Buffer == NULL)
{
return FALSE;
}
RtlCopyMemory(Buffer, ModuleName->Buffer, ModuleName->Length);
Buffer[ModuleName->Length / sizeof(WCHAR)] = UNICODE_NULL;
/*
* Generate filename without path (not needed by freeldr)
*/
FileNameWithoutPath = wcsrchr(Buffer, L'\\');
if (FileNameWithoutPath == NULL)
{
FileNameWithoutPath = Buffer;
}
else
{
FileNameWithoutPath++;
}
/*
* Strip the file extension from ServiceName
*/
Success = RtlCreateUnicodeString(&ServiceName, FileNameWithoutPath);
ExFreePoolWithTag(Buffer, TAG_IO);
if (!Success)
{
return FALSE;
}
FileExtension = wcsrchr(ServiceName.Buffer, L'.');
if (FileExtension != NULL)
{
ServiceName.Length -= (USHORT)wcslen(FileExtension) * sizeof(WCHAR);
FileExtension[0] = UNICODE_NULL;
}
UNICODE_STRING RegistryPath;
// Make the registry path for the driver
RegistryPath.Length = 0;
RegistryPath.MaximumLength = sizeof(ServicesKeyName) + ServiceName.Length;
RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, RegistryPath.MaximumLength, TAG_IO);
if (RegistryPath.Buffer == NULL)
{
return FALSE;
}
RtlAppendUnicodeToString(&RegistryPath, ServicesKeyName);
RtlAppendUnicodeStringToString(&RegistryPath, &ServiceName);
RtlFreeUnicodeString(&ServiceName);
HANDLE serviceHandle;
Status = IopOpenRegistryKeyEx(&serviceHandle, NULL, &RegistryPath, KEY_READ);
RtlFreeUnicodeString(&RegistryPath);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
/* Lookup the new Ldr entry in PsLoadedModuleList */
for (NextEntry = PsLoadedModuleList.Flink;
NextEntry != &PsLoadedModuleList;
NextEntry = NextEntry->Flink)
{
LdrEntry = CONTAINING_RECORD(NextEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
if (RtlEqualUnicodeString(ModuleName, &LdrEntry->BaseDllName, TRUE))
{
break;
}
}
ASSERT(NextEntry != &PsLoadedModuleList);
/*
* Initialize the driver
*/
NTSTATUS driverEntryStatus;
Status = IopInitializeDriverModule(LdrEntry,
serviceHandle,
&DriverObject,
&driverEntryStatus);
if (!NT_SUCCESS(Status))
{
DPRINT1("Driver '%wZ' load failed, status (%x)\n", ModuleName, Status);
return FALSE;
}
// The driver has been loaded, now check if where are any PDOs
// for that driver, and queue AddDevice call for them.
// The check is possible because HKLM/SYSTEM/CCS/Services/<ServiceName>/Enum directory
// is populated upon a new device arrival based on a (critical) device database
// Legacy drivers may add devices inside DriverEntry.
// We're lazy and always assume that they are doing so
BOOLEAN deviceAdded = !!(DriverObject->Flags & DRVO_LEGACY_DRIVER);
HANDLE enumServiceHandle;
UNICODE_STRING enumName = RTL_CONSTANT_STRING(L"Enum");
Status = IopOpenRegistryKeyEx(&enumServiceHandle, serviceHandle, &enumName, KEY_READ);
ZwClose(serviceHandle);
if (NT_SUCCESS(Status))
{
ULONG instanceCount = 0;
PKEY_VALUE_FULL_INFORMATION kvInfo;
Status = IopGetRegistryValue(enumServiceHandle, L"Count", &kvInfo);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if (kvInfo->Type != REG_DWORD || kvInfo->DataLength != sizeof(ULONG))
{
ExFreePool(kvInfo);
goto Cleanup;
}
RtlMoveMemory(&instanceCount,
(PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
sizeof(ULONG));
ExFreePool(kvInfo);
DPRINT("Processing %u instances for %wZ module\n", instanceCount, ModuleName);
for (ULONG i = 0; i < instanceCount; i++)
{
WCHAR num[11];
UNICODE_STRING instancePath;
RtlStringCbPrintfW(num, sizeof(num), L"%u", i);
Status = IopGetRegistryValue(enumServiceHandle, num, &kvInfo);
if (!NT_SUCCESS(Status))
{
continue;
}
if (kvInfo->Type != REG_SZ || kvInfo->DataLength == 0)
{
ExFreePool(kvInfo);
continue;
}
instancePath.Length = kvInfo->DataLength - sizeof(UNICODE_NULL);
instancePath.MaximumLength = kvInfo->DataLength;
instancePath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
instancePath.MaximumLength,
TAG_IO);
if (instancePath.Buffer)
{
RtlMoveMemory(instancePath.Buffer,
(PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
instancePath.Length);
instancePath.Buffer[instancePath.Length / sizeof(WCHAR)] = UNICODE_NULL;
PDEVICE_OBJECT pdo = IopGetDeviceObjectFromDeviceInstance(&instancePath);
if (pdo != NULL)
{
PiQueueDeviceAction(pdo, PiActionAddBootDevices, NULL, NULL);
ObDereferenceObject(pdo);
deviceAdded = TRUE;
}
else
{
DPRINT1("No device node found matching instance path '%wZ'\n", &instancePath);
}
}
ExFreePool(kvInfo);
}
ZwClose(enumServiceHandle);
}
Cleanup:
/* Remove extra reference from IopInitializeDriverModule */
ObDereferenceObject(DriverObject);
return deviceAdded;
}
/*
* IopInitializeBootDrivers
*
* Initialize boot drivers and free memory for boot files.
*
* Parameters
* None
*
* Return Value
* None
*/
CODE_SEG("INIT")
VOID
FASTCALL
IopInitializeBootDrivers(VOID)
{
PLIST_ENTRY ListHead, NextEntry, NextEntry2;
PLDR_DATA_TABLE_ENTRY LdrEntry;
NTSTATUS Status;
UNICODE_STRING DriverName;
ULONG i, Index;
PDRIVER_INFORMATION DriverInfo, DriverInfoTag;
HANDLE KeyHandle;
PBOOT_DRIVER_LIST_ENTRY BootEntry;
DPRINT("IopInitializeBootDrivers()\n");
/* Create the RAW FS built-in driver */
RtlInitUnicodeString(&DriverName, L"\\FileSystem\\RAW");
Status = IoCreateDriver(&DriverName, RawFsDriverEntry);
if (!NT_SUCCESS(Status))
{
/* Fail */
return;
}
/* Get highest group order index */
IopGroupIndex = PpInitGetGroupOrderIndex(NULL);
if (IopGroupIndex == 0xFFFF)
{
UNIMPLEMENTED_DBGBREAK();
}
/* Allocate the group table */
IopGroupTable = ExAllocatePoolWithTag(PagedPool,
IopGroupIndex * sizeof(LIST_ENTRY),
TAG_IO);
if (IopGroupTable == NULL)
{
UNIMPLEMENTED_DBGBREAK();
}
/* Initialize the group table lists */
for (i = 0; i < IopGroupIndex; i++) InitializeListHead(&IopGroupTable[i]);
/* Loop the boot modules */
ListHead = &KeLoaderBlock->LoadOrderListHead;
for (NextEntry = ListHead->Flink;
NextEntry != ListHead;
NextEntry = NextEntry->Flink)
{
/* Get the entry */
LdrEntry = CONTAINING_RECORD(NextEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
/* Check if the DLL needs to be initialized */
if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
{
/* Call its entrypoint */
MmCallDllInitialize(LdrEntry, NULL);
}
}
/* Loop the boot drivers */
ListHead = &KeLoaderBlock->BootDriverListHead;
for (NextEntry = ListHead->Flink;
NextEntry != ListHead;
NextEntry = NextEntry->Flink)
{
/* Get the entry */
BootEntry = CONTAINING_RECORD(NextEntry,
BOOT_DRIVER_LIST_ENTRY,
Link);
// FIXME: TODO: This LdrEntry is to be used in a special handling
// for SETUPLDR (a similar procedure is done on Windows), where
// the loader would, under certain conditions, be loaded in the
// SETUPLDR-specific code block below...
#if 0
/* Get the driver loader entry */
LdrEntry = BootEntry->LdrEntry;
#endif
/* Allocate our internal accounting structure */
DriverInfo = ExAllocatePoolWithTag(PagedPool,
sizeof(DRIVER_INFORMATION),
TAG_IO);
if (DriverInfo)
{
/* Zero it and initialize it */
RtlZeroMemory(DriverInfo, sizeof(DRIVER_INFORMATION));
InitializeListHead(&DriverInfo->Link);
DriverInfo->DataTableEntry = BootEntry;
/* Open the registry key */
Status = IopOpenRegistryKeyEx(&KeyHandle,
NULL,
&BootEntry->RegistryPath,
KEY_READ);
DPRINT("IopOpenRegistryKeyEx(%wZ) returned 0x%08lx\n", &BootEntry->RegistryPath, Status);
#if 0
if (NT_SUCCESS(Status))
#else // Hack still needed...
if ((NT_SUCCESS(Status)) || /* ReactOS HACK for SETUPLDR */
((KeLoaderBlock->SetupLdrBlock) && ((KeyHandle = (PVOID)1)))) // yes, it's an assignment!
#endif
{
/* Save the handle */
DriverInfo->ServiceHandle = KeyHandle;
/* Get the group oder index */
Index = PpInitGetGroupOrderIndex(KeyHandle);
/* Get the tag position */
DriverInfo->TagPosition = PipGetDriverTagPriority(KeyHandle);
/* Insert it into the list, at the right place */
ASSERT(Index < IopGroupIndex);
NextEntry2 = IopGroupTable[Index].Flink;
while (NextEntry2 != &IopGroupTable[Index])
{
/* Get the driver info */
DriverInfoTag = CONTAINING_RECORD(NextEntry2,
DRIVER_INFORMATION,
Link);
/* Check if we found the right tag position */
if (DriverInfoTag->TagPosition > DriverInfo->TagPosition)
{
/* We're done */
break;
}
/* Next entry */
NextEntry2 = NextEntry2->Flink;
}
/* Insert us right before the next entry */
NextEntry2 = NextEntry2->Blink;
InsertHeadList(NextEntry2, &DriverInfo->Link);
}
}
}
/* Loop each group index */
for (i = 0; i < IopGroupIndex; i++)
{
/* Loop each group table */
for (NextEntry = IopGroupTable[i].Flink;
NextEntry != &IopGroupTable[i];
NextEntry = NextEntry->Flink)
{
/* Get the entry */
DriverInfo = CONTAINING_RECORD(NextEntry,
DRIVER_INFORMATION,
Link);
/* Get the driver loader entry */
LdrEntry = DriverInfo->DataTableEntry->LdrEntry;
/* Initialize it */
if (IopInitializeBuiltinDriver(LdrEntry))
{
// it does not make sense to enumerate the tree if there are no new devices added
PiQueueDeviceAction(IopRootDeviceNode->PhysicalDeviceObject,
PiActionEnumRootDevices,
NULL,
NULL);
}
}
}
/* HAL Root Bus is being initialized before loading the boot drivers so this may cause issues
* when some devices are not being initialized with their drivers. This flag is used to delay
* all actions with devices (except PnP root device) until boot drivers are loaded.
* See PiQueueDeviceAction function
*/
PnPBootDriversLoaded = TRUE;
DbgPrint("BOOT DRIVERS LOADED\n");
PiQueueDeviceAction(IopRootDeviceNode->PhysicalDeviceObject,
PiActionEnumDeviceTree,
NULL,
NULL);
}
CODE_SEG("INIT")
VOID
FASTCALL
IopInitializeSystemDrivers(VOID)
{
PUNICODE_STRING *DriverList, *SavedList;
PiPerformSyncDeviceAction(IopRootDeviceNode->PhysicalDeviceObject, PiActionEnumDeviceTree);
/* HACK: No system drivers on the BootCD */
if (KeLoaderBlock->SetupLdrBlock) return;
/* Get the driver list */
SavedList = DriverList = CmGetSystemDriverList();
ASSERT(DriverList);
/* Loop it */
while (*DriverList)
{
/* Load the driver */
ZwLoadDriver(*DriverList);
/* Free the entry */
RtlFreeUnicodeString(*DriverList);
ExFreePool(*DriverList);
/* Next entry */
InbvIndicateProgress();
DriverList++;
}
/* Free the list */
ExFreePool(SavedList);
PiQueueDeviceAction(IopRootDeviceNode->PhysicalDeviceObject,
PiActionEnumDeviceTree,
NULL,
NULL);
}
/*
* IopUnloadDriver
*
* Unloads a device driver.
*
* Parameters
* DriverServiceName
* Name of the service to unload (registry key).
*
* UnloadPnpDrivers
* Whether to unload Plug & Plug or only legacy drivers. If this
* parameter is set to FALSE, the routine will unload only legacy
* drivers.
*
* Return Value
* Status
*
* To do
* Guard the whole function by SEH.
*/
NTSTATUS NTAPI
IopUnloadDriver(PUNICODE_STRING DriverServiceName, BOOLEAN UnloadPnpDrivers)
{
UNICODE_STRING Backslash = RTL_CONSTANT_STRING(L"\\");
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
UNICODE_STRING ImagePath;
UNICODE_STRING ServiceName;
UNICODE_STRING ObjectName;
PDRIVER_OBJECT DriverObject;
PDEVICE_OBJECT DeviceObject;
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
NTSTATUS Status;
USHORT LastBackslash;
BOOLEAN SafeToUnload = TRUE;
KPROCESSOR_MODE PreviousMode;
UNICODE_STRING CapturedServiceName;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
/* Need the appropriate priviliege */
if (!SeSinglePrivilegeCheck(SeLoadDriverPrivilege, PreviousMode))
{
DPRINT1("No unload privilege!\n");
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Capture the service name */
Status = ProbeAndCaptureUnicodeString(&CapturedServiceName,
PreviousMode,
DriverServiceName);
if (!NT_SUCCESS(Status))
{
return Status;
}
DPRINT("IopUnloadDriver('%wZ', %u)\n", &CapturedServiceName, UnloadPnpDrivers);
/* We need a service name */
if (CapturedServiceName.Length == 0 || CapturedServiceName.Buffer == NULL)
{
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return STATUS_INVALID_PARAMETER;
}
/*
* Get the service name from the registry key name
*/
Status = RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
&CapturedServiceName,
&Backslash,
&LastBackslash);
if (NT_SUCCESS(Status))
{
NT_ASSERT(CapturedServiceName.Length >= LastBackslash + sizeof(WCHAR));
ServiceName.Buffer = &CapturedServiceName.Buffer[LastBackslash / sizeof(WCHAR) + 1];
ServiceName.Length = CapturedServiceName.Length - LastBackslash - sizeof(WCHAR);
ServiceName.MaximumLength = CapturedServiceName.MaximumLength - LastBackslash - sizeof(WCHAR);
}
else
{
ServiceName = CapturedServiceName;
}
/*
* Construct the driver object name
*/
Status = RtlUShortAdd(sizeof(DRIVER_ROOT_NAME),
ServiceName.Length,
&ObjectName.MaximumLength);
if (!NT_SUCCESS(Status))
{
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return Status;
}
ObjectName.Length = 0;
ObjectName.Buffer = ExAllocatePoolWithTag(PagedPool,
ObjectName.MaximumLength,
TAG_IO);
if (!ObjectName.Buffer)
{
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return STATUS_INSUFFICIENT_RESOURCES;
}
NT_VERIFY(NT_SUCCESS(RtlAppendUnicodeToString(&ObjectName, DRIVER_ROOT_NAME)));
NT_VERIFY(NT_SUCCESS(RtlAppendUnicodeStringToString(&ObjectName, &ServiceName)));
/*
* Find the driver object
*/
Status = ObReferenceObjectByName(&ObjectName,
0,
0,
0,
IoDriverObjectType,
KernelMode,
0,
(PVOID*)&DriverObject);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't locate driver object for %wZ\n", &ObjectName);
ExFreePoolWithTag(ObjectName.Buffer, TAG_IO);
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return Status;
}
/* Free the buffer for driver object name */
ExFreePoolWithTag(ObjectName.Buffer, TAG_IO);
/* Check that driver is not already unloading */
if (DriverObject->Flags & DRVO_UNLOAD_INVOKED)
{
DPRINT1("Driver deletion pending\n");
ObDereferenceObject(DriverObject);
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return STATUS_DELETE_PENDING;
}
/*
* Get path of service...
*/
RtlZeroMemory(QueryTable, sizeof(QueryTable));
RtlInitUnicodeString(&ImagePath, NULL);
QueryTable[0].Name = L"ImagePath";
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
QueryTable[0].EntryContext = &ImagePath;
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
CapturedServiceName.Buffer,
QueryTable,
NULL,
NULL);
/* We no longer need service name */
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryRegistryValues() failed (Status %x)\n", Status);
ObDereferenceObject(DriverObject);
return Status;
}
/*
* Normalize the image path for all later processing.
*/
Status = IopNormalizeImagePath(&ImagePath, &ServiceName);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopNormalizeImagePath() failed (Status %x)\n", Status);
ObDereferenceObject(DriverObject);
return Status;
}
/* Free the service path */
ExFreePool(ImagePath.Buffer);
/*
* Unload the module and release the references to the device object
*/
/* Call the load/unload routine, depending on current process */
if (DriverObject->DriverUnload && DriverObject->DriverSection &&
(UnloadPnpDrivers || (DriverObject->Flags & DRVO_LEGACY_DRIVER)))
{
/* Loop through each device object of the driver
and set DOE_UNLOAD_PENDING flag */
DeviceObject = DriverObject->DeviceObject;
while (DeviceObject)
{
/* Set the unload pending flag for the device */
DeviceExtension = IoGetDevObjExtension(DeviceObject);
DeviceExtension->ExtensionFlags |= DOE_UNLOAD_PENDING;
/* Make sure there are no attached devices or no reference counts */
if ((DeviceObject->ReferenceCount) || (DeviceObject->AttachedDevice))
{
/* Not safe to unload */
DPRINT1("Drivers device object is referenced or has attached devices\n");
SafeToUnload = FALSE;
}
DeviceObject = DeviceObject->NextDevice;
}
/* If not safe to unload, then return success */
if (!SafeToUnload)
{
ObDereferenceObject(DriverObject);
return STATUS_SUCCESS;
}
DPRINT1("Unloading driver '%wZ' (manual)\n", &DriverObject->DriverName);
/* Set the unload invoked flag and call the unload routine */
DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
Status = IopDoLoadUnloadDriver(NULL, &DriverObject);
ASSERT(Status == STATUS_SUCCESS);
/* Mark the driver object temporary, so it could be deleted later */
ObMakeTemporaryObject(DriverObject);
/* Dereference it 2 times */
ObDereferenceObject(DriverObject);
ObDereferenceObject(DriverObject);
return Status;
}
else
{
DPRINT1("No DriverUnload function! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
/* Dereference one time (refd inside this function) */
ObDereferenceObject(DriverObject);
/* Return unloading failure */
return STATUS_INVALID_DEVICE_REQUEST;
}
}
VOID
NTAPI
IopReinitializeDrivers(VOID)
{
PDRIVER_REINIT_ITEM ReinitItem;
PLIST_ENTRY Entry;
/* Get the first entry and start looping */
Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
&DriverReinitListLock);
while (Entry)
{
/* Get the item */
ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
/* Increment reinitialization counter */
ReinitItem->DriverObject->DriverExtension->Count++;
/* Remove the device object flag */
ReinitItem->DriverObject->Flags &= ~DRVO_REINIT_REGISTERED;
/* Call the routine */
ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
ReinitItem->Context,
ReinitItem->DriverObject->
DriverExtension->Count);
/* Free the entry */
ExFreePool(Entry);
/* Move to the next one */
Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
&DriverReinitListLock);
}
}
VOID
NTAPI
IopReinitializeBootDrivers(VOID)
{
PDRIVER_REINIT_ITEM ReinitItem;
PLIST_ENTRY Entry;
/* Get the first entry and start looping */
Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
&DriverBootReinitListLock);
while (Entry)
{
/* Get the item */
ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
/* Increment reinitialization counter */
ReinitItem->DriverObject->DriverExtension->Count++;
/* Remove the device object flag */
ReinitItem->DriverObject->Flags &= ~DRVO_BOOTREINIT_REGISTERED;
/* Call the routine */
ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
ReinitItem->Context,
ReinitItem->DriverObject->
DriverExtension->Count);
/* Free the entry */
ExFreePool(Entry);
/* Move to the next one */
Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
&DriverBootReinitListLock);
}
/* Wait for all device actions being finished*/
KeWaitForSingleObject(&PiEnumerationFinished, Executive, KernelMode, FALSE, NULL);
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
IoCreateDriver(
_In_opt_ PUNICODE_STRING DriverName,
_In_ PDRIVER_INITIALIZE InitializationFunction)
{
WCHAR NameBuffer[100];
USHORT NameLength;
UNICODE_STRING LocalDriverName;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG ObjectSize;
PDRIVER_OBJECT DriverObject;
UNICODE_STRING ServiceKeyName;
HANDLE hDriver;
ULONG i, RetryCount = 0;
try_again:
/* First, create a unique name for the driver if we don't have one */
if (!DriverName)
{
/* Create a random name and set up the string */
NameLength = (USHORT)swprintf(NameBuffer,
DRIVER_ROOT_NAME L"%08u",
KeTickCount.LowPart);
LocalDriverName.Length = NameLength * sizeof(WCHAR);
LocalDriverName.MaximumLength = LocalDriverName.Length + sizeof(UNICODE_NULL);
LocalDriverName.Buffer = NameBuffer;
}
else
{
/* So we can avoid another code path, use a local var */
LocalDriverName = *DriverName;
}
/* Initialize the Attributes */
ObjectSize = sizeof(DRIVER_OBJECT) + sizeof(EXTENDED_DRIVER_EXTENSION);
InitializeObjectAttributes(&ObjectAttributes,
&LocalDriverName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
/* Create the Object */
Status = ObCreateObject(KernelMode,
IoDriverObjectType,
&ObjectAttributes,
KernelMode,
NULL,
ObjectSize,
0,
0,
(PVOID*)&DriverObject);
if (!NT_SUCCESS(Status)) return Status;
DPRINT("IopCreateDriver(): created DO %p\n", DriverObject);
/* Set up the Object */
RtlZeroMemory(DriverObject, ObjectSize);
DriverObject->Type = IO_TYPE_DRIVER;
DriverObject->Size = sizeof(DRIVER_OBJECT);
DriverObject->Flags = DRVO_BUILTIN_DRIVER;
DriverObject->DriverExtension = (PDRIVER_EXTENSION)(DriverObject + 1);
DriverObject->DriverExtension->DriverObject = DriverObject;
DriverObject->DriverInit = InitializationFunction;
/* Loop all Major Functions */
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
/* Invalidate each function */
DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
}
/* Set up the service key name buffer */
ServiceKeyName.MaximumLength = LocalDriverName.Length + sizeof(UNICODE_NULL);
ServiceKeyName.Buffer = ExAllocatePoolWithTag(PagedPool, LocalDriverName.MaximumLength, TAG_IO);
if (!ServiceKeyName.Buffer)
{
/* Fail */
ObMakeTemporaryObject(DriverObject);
ObDereferenceObject(DriverObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* For builtin drivers, the ServiceKeyName is equal to DriverName */
RtlCopyUnicodeString(&ServiceKeyName, &LocalDriverName);
ServiceKeyName.Buffer[ServiceKeyName.Length / sizeof(WCHAR)] = UNICODE_NULL;
DriverObject->DriverExtension->ServiceKeyName = ServiceKeyName;
/* Make a copy of the driver name to store in the driver object */
DriverObject->DriverName.MaximumLength = LocalDriverName.Length;
DriverObject->DriverName.Buffer = ExAllocatePoolWithTag(PagedPool,
DriverObject->DriverName.MaximumLength,
TAG_IO);
if (!DriverObject->DriverName.Buffer)
{
/* Fail */
ObMakeTemporaryObject(DriverObject);
ObDereferenceObject(DriverObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyUnicodeString(&DriverObject->DriverName, &LocalDriverName);
/* Add the Object and get its handle */
Status = ObInsertObject(DriverObject,
NULL,
FILE_READ_DATA,
0,
NULL,
&hDriver);
/* Eliminate small possibility when this function is called more than
once in a row, and KeTickCount doesn't get enough time to change */
if (!DriverName && (Status == STATUS_OBJECT_NAME_COLLISION) && (RetryCount < 100))
{
RetryCount++;
goto try_again;
}
if (!NT_SUCCESS(Status)) return Status;
/* Now reference it */
Status = ObReferenceObjectByHandle(hDriver,
0,
IoDriverObjectType,
KernelMode,
(PVOID*)&DriverObject,
NULL);
/* Close the extra handle */
ZwClose(hDriver);
if (!NT_SUCCESS(Status))
{
/* Fail */
ObMakeTemporaryObject(DriverObject);
ObDereferenceObject(DriverObject);
return Status;
}
/* Finally, call its init function */
DPRINT("Calling driver entrypoint at %p\n", InitializationFunction);
Status = InitializationFunction(DriverObject, NULL);
if (!NT_SUCCESS(Status))
{
/* If it didn't work, then kill the object */
DPRINT1("'%wZ' initialization failed, status (0x%08lx)\n", &LocalDriverName, Status);
ObMakeTemporaryObject(DriverObject);
ObDereferenceObject(DriverObject);
return Status;
}
/* Windows does this fixup, keep it for compatibility */
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
/*
* Make sure the driver didn't set any dispatch entry point to NULL!
* Doing so is illegal; drivers shouldn't touch entry points they
* do not implement.
*/
/* Check if it did so anyway */
if (!DriverObject->MajorFunction[i])
{
/* Print a warning in the debug log */
DPRINT1("Driver <%wZ> set DriverObject->MajorFunction[%lu] to NULL!\n",
&DriverObject->DriverName, i);
/* Fix it up */
DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
}
}
/* Return the Status */
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
IoDeleteDriver(
_In_ PDRIVER_OBJECT DriverObject)
{
/* Simply dereference the Object */
ObDereferenceObject(DriverObject);
}
/*
* @implemented
*/
VOID
NTAPI
IoRegisterBootDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_REINITIALIZE ReinitRoutine,
IN PVOID Context)
{
PDRIVER_REINIT_ITEM ReinitItem;
/* Allocate the entry */
ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
sizeof(DRIVER_REINIT_ITEM),
TAG_REINIT);
if (!ReinitItem) return;
/* Fill it out */
ReinitItem->DriverObject = DriverObject;
ReinitItem->ReinitRoutine = ReinitRoutine;
ReinitItem->Context = Context;
/* Set the Driver Object flag and insert the entry into the list */
DriverObject->Flags |= DRVO_BOOTREINIT_REGISTERED;
ExInterlockedInsertTailList(&DriverBootReinitListHead,
&ReinitItem->ItemEntry,
&DriverBootReinitListLock);
}
/*
* @implemented
*/
VOID
NTAPI
IoRegisterDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_REINITIALIZE ReinitRoutine,
IN PVOID Context)
{
PDRIVER_REINIT_ITEM ReinitItem;
/* Allocate the entry */
ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
sizeof(DRIVER_REINIT_ITEM),
TAG_REINIT);
if (!ReinitItem) return;
/* Fill it out */
ReinitItem->DriverObject = DriverObject;
ReinitItem->ReinitRoutine = ReinitRoutine;
ReinitItem->Context = Context;
/* Set the Driver Object flag and insert the entry into the list */
DriverObject->Flags |= DRVO_REINIT_REGISTERED;
ExInterlockedInsertTailList(&DriverReinitListHead,
&ReinitItem->ItemEntry,
&DriverReinitListLock);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoAllocateDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
IN PVOID ClientIdentificationAddress,
IN ULONG DriverObjectExtensionSize,
OUT PVOID *DriverObjectExtension)
{
KIRQL OldIrql;
PIO_CLIENT_EXTENSION DriverExtensions, NewDriverExtension;
BOOLEAN Inserted = FALSE;
/* Assume failure */
*DriverObjectExtension = NULL;
/* Allocate the extension */
NewDriverExtension = ExAllocatePoolWithTag(NonPagedPool,
sizeof(IO_CLIENT_EXTENSION) +
DriverObjectExtensionSize,
TAG_DRIVER_EXTENSION);
if (!NewDriverExtension) return STATUS_INSUFFICIENT_RESOURCES;
/* Clear the extension for teh caller */
RtlZeroMemory(NewDriverExtension,
sizeof(IO_CLIENT_EXTENSION) + DriverObjectExtensionSize);
/* Acqure lock */
OldIrql = KeRaiseIrqlToDpcLevel();
/* Fill out the extension */
NewDriverExtension->ClientIdentificationAddress = ClientIdentificationAddress;
/* Loop the current extensions */
DriverExtensions = IoGetDrvObjExtension(DriverObject)->
ClientDriverExtension;
while (DriverExtensions)
{
/* Check if the identifier matches */
if (DriverExtensions->ClientIdentificationAddress ==
ClientIdentificationAddress)
{
/* We have a collision, break out */
break;
}
/* Go to the next one */
DriverExtensions = DriverExtensions->NextExtension;
}
/* Check if we didn't collide */
if (!DriverExtensions)
{
/* Link this one in */
NewDriverExtension->NextExtension =
IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
IoGetDrvObjExtension(DriverObject)->ClientDriverExtension =
NewDriverExtension;
Inserted = TRUE;
}
/* Release the lock */
KeLowerIrql(OldIrql);
/* Check if insertion failed */
if (!Inserted)
{
/* Free the entry and fail */
ExFreePoolWithTag(NewDriverExtension, TAG_DRIVER_EXTENSION);
return STATUS_OBJECT_NAME_COLLISION;
}
/* Otherwise, return the pointer */
*DriverObjectExtension = NewDriverExtension + 1;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
PVOID
NTAPI
IoGetDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
IN PVOID ClientIdentificationAddress)
{
KIRQL OldIrql;
PIO_CLIENT_EXTENSION DriverExtensions;
/* Acquire lock */
OldIrql = KeRaiseIrqlToDpcLevel();
/* Loop the list until we find the right one */
DriverExtensions = IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
while (DriverExtensions)
{
/* Check for a match */
if (DriverExtensions->ClientIdentificationAddress ==
ClientIdentificationAddress)
{
/* Break out */
break;
}
/* Keep looping */
DriverExtensions = DriverExtensions->NextExtension;
}
/* Release lock */
KeLowerIrql(OldIrql);
/* Return nothing or the extension */
if (!DriverExtensions) return NULL;
return DriverExtensions + 1;
}
NTSTATUS
IopLoadDriver(
_In_ HANDLE ServiceHandle,
_Out_ PDRIVER_OBJECT *DriverObject)
{
UNICODE_STRING ImagePath;
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY ModuleObject;
PVOID BaseAddress;
PKEY_VALUE_FULL_INFORMATION kvInfo;
Status = IopGetRegistryValue(ServiceHandle, L"ImagePath", &kvInfo);
if (NT_SUCCESS(Status))
{
if ((kvInfo->Type != REG_EXPAND_SZ && kvInfo->Type != REG_SZ) || kvInfo->DataLength == 0)
{
ExFreePool(kvInfo);
return STATUS_ILL_FORMED_SERVICE_ENTRY;
}
ImagePath.Length = kvInfo->DataLength - sizeof(UNICODE_NULL);
ImagePath.MaximumLength = kvInfo->DataLength;
ImagePath.Buffer = ExAllocatePoolWithTag(PagedPool, ImagePath.MaximumLength, TAG_RTLREGISTRY);
if (!ImagePath.Buffer)
{
ExFreePool(kvInfo);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlMoveMemory(ImagePath.Buffer,
(PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
ImagePath.Length);
ImagePath.Buffer[ImagePath.Length / sizeof(WCHAR)] = UNICODE_NULL;
ExFreePool(kvInfo);
}
else
{
return Status;
}
/*
* Normalize the image path for all later processing.
*/
Status = IopNormalizeImagePath(&ImagePath, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
return Status;
}
DPRINT("FullImagePath: '%wZ'\n", &ImagePath);
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
/*
* Load the driver module
*/
DPRINT("Loading module from %wZ\n", &ImagePath);
Status = MmLoadSystemImage(&ImagePath, NULL, NULL, 0, (PVOID)&ModuleObject, &BaseAddress);
RtlFreeUnicodeString(&ImagePath);
if (!NT_SUCCESS(Status))
{
DPRINT("MmLoadSystemImage() failed (Status %lx)\n", Status);
ExReleaseResourceLite(&IopDriverLoadResource);
KeLeaveCriticalRegion();
return Status;
}
// Display the loading message
ULONG infoLength;
Status = ZwQueryKey(ServiceHandle, KeyBasicInformation, NULL, 0, &infoLength);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
PKEY_BASIC_INFORMATION servName = ExAllocatePoolWithTag(PagedPool, infoLength, TAG_IO);
if (servName)
{
Status = ZwQueryKey(ServiceHandle,
KeyBasicInformation,
servName,
infoLength,
&infoLength);
if (NT_SUCCESS(Status))
{
UNICODE_STRING serviceName = {
.Length = servName->NameLength,
.MaximumLength = servName->NameLength,
.Buffer = servName->Name
};
IopDisplayLoadingMessage(&serviceName);
}
ExFreePoolWithTag(servName, TAG_IO);
}
}
NTSTATUS driverEntryStatus;
Status = IopInitializeDriverModule(ModuleObject,
ServiceHandle,
DriverObject,
&driverEntryStatus);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopInitializeDriverModule() failed (Status %lx)\n", Status);
}
ExReleaseResourceLite(&IopDriverLoadResource);
KeLeaveCriticalRegion();
return Status;
}
static
VOID
NTAPI
IopLoadUnloadDriverWorker(
_Inout_ PVOID Parameter)
{
PLOAD_UNLOAD_PARAMS LoadParams = Parameter;
ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess);
if (LoadParams->DriverObject)
{
// unload request
LoadParams->DriverObject->DriverUnload(LoadParams->DriverObject);
LoadParams->Status = STATUS_SUCCESS;
}
else
{
// load request
HANDLE serviceHandle;
NTSTATUS status;
status = IopOpenRegistryKeyEx(&serviceHandle, NULL, LoadParams->RegistryPath, KEY_READ);
if (!NT_SUCCESS(status))
{
LoadParams->Status = status;
}
else
{
LoadParams->Status = IopLoadDriver(serviceHandle, &LoadParams->DriverObject);
ZwClose(serviceHandle);
}
}
if (LoadParams->SetEvent)
{
KeSetEvent(&LoadParams->Event, 0, FALSE);
}
}
/**
* @brief Process load and unload driver operations. This is mostly for NtLoadDriver
* and NtUnloadDriver, because their code should run inside PsInitialSystemProcess
*
* @param[in] RegistryPath The registry path
* @param DriverObject The driver object
*
* @return Status of the operation
*/
NTSTATUS
IopDoLoadUnloadDriver(
_In_opt_ PUNICODE_STRING RegistryPath,
_Inout_ PDRIVER_OBJECT *DriverObject)
{
LOAD_UNLOAD_PARAMS LoadParams;
/* Prepare parameters block */
LoadParams.RegistryPath = RegistryPath;
LoadParams.DriverObject = *DriverObject;
if (PsGetCurrentProcess() != PsInitialSystemProcess)
{
LoadParams.SetEvent = TRUE;
KeInitializeEvent(&LoadParams.Event, NotificationEvent, FALSE);
/* Initialize and queue a work item */
ExInitializeWorkItem(&LoadParams.WorkItem, IopLoadUnloadDriverWorker, &LoadParams);
ExQueueWorkItem(&LoadParams.WorkItem, DelayedWorkQueue);
/* And wait till it completes */
KeWaitForSingleObject(&LoadParams.Event, UserRequest, KernelMode, FALSE, NULL);
}
else
{
/* If we're already in a system process, call it right here */
LoadParams.SetEvent = FALSE;
IopLoadUnloadDriverWorker(&LoadParams);
}
return LoadParams.Status;
}
/*
* NtLoadDriver
*
* Loads a device driver.
*
* Parameters
* DriverServiceName
* Name of the service to load (registry key).
*
* Return Value
* Status
*
* Status
* implemented
*/
NTSTATUS NTAPI
NtLoadDriver(IN PUNICODE_STRING DriverServiceName)
{
UNICODE_STRING CapturedServiceName = { 0, 0, NULL };
KPROCESSOR_MODE PreviousMode;
PDRIVER_OBJECT DriverObject;
NTSTATUS Status;
PAGED_CODE();
PreviousMode = KeGetPreviousMode();
/* Need the appropriate priviliege */
if (!SeSinglePrivilegeCheck(SeLoadDriverPrivilege, PreviousMode))
{
DPRINT1("No load privilege!\n");
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Capture the service name */
Status = ProbeAndCaptureUnicodeString(&CapturedServiceName,
PreviousMode,
DriverServiceName);
if (!NT_SUCCESS(Status))
{
return Status;
}
DPRINT("NtLoadDriver('%wZ')\n", &CapturedServiceName);
/* We need a service name */
if (CapturedServiceName.Length == 0 || CapturedServiceName.Buffer == NULL)
{
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return STATUS_INVALID_PARAMETER;
}
/* Load driver and call its entry point */
DriverObject = NULL;
Status = IopDoLoadUnloadDriver(&CapturedServiceName, &DriverObject);
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
return Status;
}
/*
* NtUnloadDriver
*
* Unloads a legacy device driver.
*
* Parameters
* DriverServiceName
* Name of the service to unload (registry key).
*
* Return Value
* Status
*
* Status
* implemented
*/
NTSTATUS NTAPI
NtUnloadDriver(IN PUNICODE_STRING DriverServiceName)
{
return IopUnloadDriver(DriverServiceName, FALSE);
}
/* EOF */