Windows2000/private/ntos/io/ioinit.c
2020-09-30 17:12:32 +02:00

3201 lines
126 KiB
C

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
ioinit.c
Abstract:
This module contains the code to initialize the I/O system.
Author:
Darryl E. Havens (darrylh) April 27, 1989
Environment:
Kernel mode, system initialization code
--*/
#include "iop.h"
#include <setupblk.h>
#include <inbv.h>
#include <ntddstor.h>
// Define the default number of I/O stack locations a large IRP should
// have if not specified by the registry.
#define DEFAULT_LARGE_IRP_LOCATIONS 8
// Define the default number of IRP that can be in progress and allocated
// from a lookaside list.
#define DEFAULT_LOOKASIDE_IRP_LIMIT 512
// Define the type for driver group name entries in the group list so that
// load order dependencies can be tracked.
typedef struct _TREE_ENTRY{
struct _TREE_ENTRY *Left;
struct _TREE_ENTRY *Right;
struct _TREE_ENTRY *Sibling;
ULONG DriversThisType;
ULONG DriversLoaded;
UNICODE_STRING GroupName;
} TREE_ENTRY, *PTREE_ENTRY;
typedef struct _DRIVER_INFORMATION{
LIST_ENTRY Link;
PDRIVER_OBJECT DriverObject;
PBOOT_DRIVER_LIST_ENTRY DataTableEntry;
HANDLE ServiceHandle;
USHORT TagPosition;
BOOLEAN Failed;
BOOLEAN Processed;
} DRIVER_INFORMATION, *PDRIVER_INFORMATION;
PTREE_ENTRY IopGroupListHead;
// I/O Error logging support
PVOID IopErrorLogObject = NULL;
// Group order table
ULONG IopGroupIndex;
PLIST_ENTRY IopGroupTable;
// Define a macro for initializing drivers.
#define InitializeDriverObject( Object ) { \
ULONG i; \
RtlZeroMemory( Object, \
sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )); \
Object->DriverExtension = (PDRIVER_EXTENSION) (Object + 1); \
Object->DriverExtension->DriverObject = Object; \
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) \
Object->MajorFunction[i] = IopInvalidDeviceRequest; \
Object->Type = IO_TYPE_DRIVER; \
Object->Size = sizeof( DRIVER_OBJECT ); \
}
// Define external procedures not in common header files
VOID IopInitializeData(VOID);
NTSTATUS RawInitialize(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
// Define the local procedures
BOOLEAN IopCheckDependencies(IN HANDLE KeyHandle);
VOID IopCreateArcNames(IN PLOADER_PARAMETER_BLOCK LoaderBlock);
BOOLEAN IopCreateObjectTypes(VOID);
PTREE_ENTRY IopCreateEntry(IN PUNICODE_STRING GroupName);
BOOLEAN IopCreateRootDirectories(VOID);
VOID IopFreeGroupTree(IN PTREE_ENTRY TreeEntry);
NTSTATUS IopInitializeAttributesAndCreateObject(IN PUNICODE_STRING ObjectName, IN OUT POBJECT_ATTRIBUTES ObjectAttributes, OUT PDRIVER_OBJECT *DriverObject);
BOOLEAN IopInitializeBootDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock, OUT PDRIVER_OBJECT *PreviousDriver);
PDRIVER_OBJECT IopInitializeBuiltinDriver(
IN PUNICODE_STRING DriverName,
IN PUNICODE_STRING RegistryPath,
IN PDRIVER_INITIALIZE DriverInitializeRoutine,
IN PLDR_DATA_TABLE_ENTRY TableEntry,
IN BOOLEAN TextModeSetup);
BOOLEAN IopInitializeSingleBootDriver(
IN HANDLE KeyHandle,
IN PBOOT_DRIVER_LIST_ENTRY BootDriver,
OUT PUNICODE_STRING DriverName OPTIONAL);
BOOLEAN IopInitializeSystemDrivers(VOID);
PTREE_ENTRY IopLookupGroupName(IN PUNICODE_STRING GroupName, IN BOOLEAN Insert);
BOOLEAN IopMarkBootPartition(IN PLOADER_PARAMETER_BLOCK LoaderBlock);
BOOLEAN IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock, OUT PSTRING NtDeviceName);
VOID IopStoreSystemPartitionInformation(IN PUNICODE_STRING NtSystemPartitionDeviceName, IN OUT PUNICODE_STRING OsLoaderPathName);
USHORT IopGetDriverTagPriority(IN HANDLE Servicehandle);
VOID IopInsertDriverList(IN PLIST_ENTRY ListHead, IN PDRIVER_INFORMATION DriverInfo);
VOID IopNotifySetupDevices(PDEVICE_NODE DeviceNode);
BOOLEAN IopWaitForBootDevicesStarted(IN VOID);
BOOLEAN IopWaitForBootDevicesDeleted(IN VOID);
VOID IopSetIoRoutines(IN VOID);
// The following allows the I/O system's initialization routines to be paged out of memory.
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,IoInitSystem)
#pragma alloc_text(INIT,IopCheckDependencies)
#pragma alloc_text(INIT,IopCreateArcNames)
#pragma alloc_text(INIT,IopCreateEntry)
#pragma alloc_text(INIT,IopCreateObjectTypes)
#pragma alloc_text(INIT,IopCreateRootDirectories)
#pragma alloc_text(INIT,IopFreeGroupTree)
#pragma alloc_text(INIT,IopInitializeAttributesAndCreateObject)
#pragma alloc_text(INIT,IopInitializeBootDrivers)
#pragma alloc_text(INIT,IopInitializeBuiltinDriver)
#pragma alloc_text(INIT,IopInitializeSingleBootDriver)
#pragma alloc_text(INIT,IopInitializeSystemDrivers)
#pragma alloc_text(INIT,IopLookupGroupName)
#pragma alloc_text(INIT,IopMarkBootPartition)
#pragma alloc_text(INIT,IopReassignSystemRoot)
#pragma alloc_text(INIT,IopStoreSystemPartitionInformation)
#pragma alloc_text(INIT,IopInsertDriverList)
#pragma alloc_text(INIT,IopGetDriverTagPriority)
#pragma alloc_text(INIT,IopNotifySetupDevices)
#pragma alloc_text(INIT,IopWaitForBootDevicesStarted)
#pragma alloc_text(INIT,IopWaitForBootDevicesDeleted)
#pragma alloc_text(INIT,IopLoadBootFilterDriver)
#pragma alloc_text(INIT,IopSetIoRoutines)
#endif
BOOLEAN IoInitSystem(PLOADER_PARAMETER_BLOCK LoaderBlock)
/*++
Routine Description:
This routine initializes the I/O system.
Arguments:
LoaderBlock - Supplies a pointer to the loader parameter block that was created by the OS Loader.
Return Value:
The function value is a BOOLEAN indicating whether or not the I/O system was successfully initialized.
--*/
{
PDRIVER_OBJECT driverObject;
PDRIVER_OBJECT *nextDriverObject;
STRING ntDeviceName;
UCHAR deviceNameBuffer[256];
ULONG largePacketSize;
ULONG smallPacketSize;
ULONG mdlPacketSize;
ULONG numberOfPackets;
ULONG poolSize;
PLIST_ENTRY entry;
PREINIT_PACKET reinitEntry;
LARGE_INTEGER deltaTime;
MM_SYSTEMSIZE systemSize;
USHORT completionZoneSize;
USHORT largeIrpZoneSize;
USHORT smallIrpZoneSize;
USHORT mdlZoneSize;
ULONG oldNtGlobalFlag;
NTSTATUS status;
ANSI_STRING ansiString;
UNICODE_STRING eventName;
UNICODE_STRING startTypeName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
PDEVICE_NODE deviceNode;
PNPAGED_LOOKASIDE_LIST lookaside;
ULONG Index;
PKPRCB prcb;
ULONG len;
PKEY_VALUE_PARTIAL_INFORMATION value;
UCHAR valueBuffer[32];
ASSERT(IopQueryOperationLength[FileMaximumInformation] == 0xff);
ASSERT(IopSetOperationLength[FileMaximumInformation] == 0xff);
ASSERT(IopQueryOperationAccess[FileMaximumInformation] == 0xffffffff);
ASSERT(IopSetOperationAccess[FileMaximumInformation] == 0xffffffff);
ASSERT(IopQueryFsOperationLength[FileFsMaximumInformation] == 0xff);
ASSERT(IopSetFsOperationLength[FileFsMaximumInformation] == 0xff);
ASSERT(IopQueryFsOperationAccess[FileFsMaximumInformation] == 0xffffffff);
ASSERT(IopSetFsOperationAccess[FileFsMaximumInformation] == 0xffffffff);
// Initialize the I/O database resource, lock, and the file system and
// network file system queue headers. Also allocate the cancel spin lock.
ntDeviceName.Buffer = deviceNameBuffer;
ntDeviceName.MaximumLength = sizeof(deviceNameBuffer);
ntDeviceName.Length = 0;
ExInitializeResource(&IopDatabaseResource);
ExInitializeResource(&IopSecurityResource);
KeInitializeSpinLock(&IopDatabaseLock);
InitializeListHead(&IopDiskFileSystemQueueHead);
InitializeListHead(&IopCdRomFileSystemQueueHead);
InitializeListHead(&IopTapeFileSystemQueueHead);
InitializeListHead(&IopNetworkFileSystemQueueHead);
InitializeListHead(&IopBootDriverReinitializeQueueHead);
InitializeListHead(&IopDriverReinitializeQueueHead);
InitializeListHead(&IopNotifyShutdownQueueHead);
InitializeListHead(&IopNotifyLastChanceShutdownQueueHead);
InitializeListHead(&IopFsNotifyChangeQueueHead);
KeInitializeSpinLock(&IopCancelSpinLock);
KeInitializeSpinLock(&IopVpbSpinLock);
KeInitializeSpinLock(&IoStatisticsLock);
IopSetIoRoutines();
// Initialize the unique device object number counter used by IoCreateDevice
// when automatically generating a device object name.
IopUniqueDeviceObjectNumber = 0;
// Initialize the large I/O Request Packet (IRP) lookaside list head and the
// mutex which guards the list.
if (!IopLargeIrpStackLocations) {
IopLargeIrpStackLocations = DEFAULT_LARGE_IRP_LOCATIONS;
}
systemSize = MmQuerySystemSize();
switch (systemSize) {
case MmSmallSystem:
completionZoneSize = 6;
smallIrpZoneSize = 6;
largeIrpZoneSize = 8;
mdlZoneSize = 16;
IopLookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT;
break;
case MmMediumSystem:
completionZoneSize = 24;
smallIrpZoneSize = 24;
largeIrpZoneSize = 32;
mdlZoneSize = 90;
IopLookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 2;
break;
case MmLargeSystem:
if (MmIsThisAnNtAsSystem()) {
completionZoneSize = 96;
smallIrpZoneSize = 96;
largeIrpZoneSize = 128;
mdlZoneSize = 256;
IopLookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 4;
} else {
completionZoneSize = 32;
smallIrpZoneSize = 32;
largeIrpZoneSize = 64;
mdlZoneSize = 128;
IopLookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 3;
}
break;
}
// Initialize the system I/O completion lookaside list.
ExInitializeNPagedLookasideList(&IopCompletionLookasideList, NULL, NULL, 0, sizeof(IOP_MINI_COMPLETION_PACKET), ' pcI', completionZoneSize);
// Initialize the system large IRP lookaside list.
largePacketSize = (ULONG)(sizeof(IRP) + (IopLargeIrpStackLocations * sizeof(IO_STACK_LOCATION)));
ExInitializeNPagedLookasideList(&IopLargeIrpLookasideList, NULL, NULL, 0, largePacketSize, 'lprI', largeIrpZoneSize);
// Initialize the system small IRP lookaside list.
smallPacketSize = (ULONG)(sizeof(IRP) + sizeof(IO_STACK_LOCATION));
ExInitializeNPagedLookasideList(&IopSmallIrpLookasideList, NULL, NULL, 0, smallPacketSize, 'sprI', smallIrpZoneSize);
// Initialize the system MDL lookaside list.
mdlPacketSize = (ULONG)(sizeof(MDL) + (IOP_FIXED_SIZE_MDL_PFNS * sizeof(PFN_NUMBER)));
ExInitializeNPagedLookasideList(&IopMdlLookasideList, NULL, NULL, 0, mdlPacketSize, ' ldM', mdlZoneSize);
// Initialize the per processor nonpaged lookaside lists and descriptors.
for (Index = 0; Index < (ULONG)KeNumberProcessors; Index += 1) {
prcb = KiProcessorBlock[Index];
// Initialize the I/O completion per processor lookaside pointers
prcb->PPLookasideList[LookasideCompletionList].L = &IopCompletionLookasideList;
lookaside = (PNPAGED_LOOKASIDE_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'PpcI');
if (lookaside != NULL) {
ExInitializeNPagedLookasideList(lookaside, NULL, NULL, 0, sizeof(IOP_MINI_COMPLETION_PACKET), 'PpcI', completionZoneSize);
} else {
lookaside = &IopCompletionLookasideList;
}
prcb->PPLookasideList[LookasideCompletionList].P = lookaside;
// Initialize the large IRP per processor lookaside pointers.
prcb->PPLookasideList[LookasideLargeIrpList].L = &IopLargeIrpLookasideList;
lookaside = (PNPAGED_LOOKASIDE_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'LprI');
if (lookaside != NULL) {
ExInitializeNPagedLookasideList(lookaside, NULL, NULL, 0, largePacketSize, 'LprI', largeIrpZoneSize);
} else {
lookaside = &IopLargeIrpLookasideList;
}
prcb->PPLookasideList[LookasideLargeIrpList].P = lookaside;
// Initialize the small IRP per processor lookaside pointers.
prcb->PPLookasideList[LookasideSmallIrpList].L = &IopSmallIrpLookasideList;
lookaside = (PNPAGED_LOOKASIDE_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'SprI');
if (lookaside != NULL) {
ExInitializeNPagedLookasideList(lookaside, NULL, NULL, 0, smallPacketSize, 'SprI', smallIrpZoneSize);
} else {
lookaside = &IopSmallIrpLookasideList;
}
prcb->PPLookasideList[LookasideSmallIrpList].P = lookaside;
// Initialize the MDL per processor lookaside list pointers.
prcb->PPLookasideList[LookasideMdlList].L = &IopMdlLookasideList;
lookaside = (PNPAGED_LOOKASIDE_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'PldM');
if (lookaside != NULL) {
ExInitializeNPagedLookasideList(lookaside, NULL, NULL, 0, mdlPacketSize, 'PldM', mdlZoneSize);
} else {
lookaside = &IopMdlLookasideList;
}
prcb->PPLookasideList[LookasideMdlList].P = lookaside;
}
KeInitializeSpinLock(&IopCompletionLock);// Initialize the I/O completion spin lock.
// Initalize the error log spin locks and log list.
KeInitializeSpinLock(&IopErrorLogLock);
KeInitializeSpinLock(&IopErrorLogAllocationLock);
InitializeListHead(&IopErrorLogListHead);
// Determine if the Error Log service will ever run this boot.
InitializeObjectAttributes(&objectAttributes,
&CmRegistryMachineSystemCurrentControlSetServicesEventLog,
OBJ_CASE_INSENSITIVE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL);
status = ZwOpenKey(&handle, KEY_READ, &objectAttributes);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&startTypeName, L"Start");
value = (PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer;
status = NtQueryValueKey(handle, &startTypeName, KeyValuePartialInformation, valueBuffer, sizeof(valueBuffer), &len);
if (NT_SUCCESS(status) && (value->Type == REG_DWORD)) {
if (SERVICE_DISABLED == (*(PULONG)(value->Data))) {// We are disabled for this boot.
IopErrorLogDisabledThisBoot = TRUE;
} else {
IopErrorLogDisabledThisBoot = FALSE;
}
} else {// Didn't find the value so we are not enabled.
IopErrorLogDisabledThisBoot = TRUE;
}
} else {// Didn't find the key so we are not enabled
IopErrorLogDisabledThisBoot = TRUE;
}
KeInitializeSemaphore(&IopRegistrySemaphore, 1, 1);// Initialize the registry access semaphore.
// Initialize the timer database and start the timer DPC routine firing
// so that drivers can use it during initialization.
deltaTime.QuadPart = -10 * 1000 * 1000;
KeInitializeSpinLock(&IopTimerLock);
InitializeListHead(&IopTimerQueueHead);
KeInitializeDpc(&IopTimerDpc, IopTimerDispatch, NULL);
KeInitializeTimerEx(&IopTimer, SynchronizationTimer);
(VOID)KeSetTimerEx(&IopTimer, deltaTime, 1000, &IopTimerDpc);
// Initialize the IopHardError structure used for informational pop-ups.
ExInitializeWorkItem(&IopHardError.ExWorkItem, IopHardErrorThread, NULL);
InitializeListHead(&IopHardError.WorkQueue);
KeInitializeSpinLock(&IopHardError.WorkQueueSpinLock);
KeInitializeSemaphore(&IopHardError.WorkQueueSemaphore, 0, MAXLONG);
IopHardError.ThreadStarted = FALSE;
IopCurrentHardError = NULL;
// Create the link tracking named event.
RtlInitUnicodeString(&eventName, L"\\Security\\TRKWKS_EVENT");
InitializeObjectAttributes(&objectAttributes, &eventName, OBJ_PERMANENT, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL);
status = NtCreateEvent(&handle, EVENT_ALL_ACCESS, &objectAttributes, NotificationEvent, FALSE);
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IOINIT: NtCreateEvent failed\n");
#endif
return FALSE;
}
(VOID)ObReferenceObjectByHandle(handle, 0, ExEventObjectType, KernelMode, (PVOID *)&IopLinkTrackingServiceEvent, NULL);
KeInitializeEvent(&IopLinkTrackingPacket.Event, NotificationEvent, FALSE);
KeInitializeEvent(&IopLinkTrackingPortObject, SynchronizationEvent, TRUE);
KeInitializeSemaphore(&IopProfileChangeSemaphore, 1, 1);
// Create all of the objects for the I/O system.
if (!IopCreateObjectTypes()) {
#if DBG
DbgPrint("IOINIT: IopCreateObjectTypes failed\n");
#endif
return FALSE;
}
// Create the root directories for the I/O system.
if (!IopCreateRootDirectories()) {
#if DBG
DbgPrint("IOINIT: IopCreateRootDirectories failed\n");
#endif
return FALSE;
}
// Initialize the resource map
IopInitializeResourceMap(LoaderBlock);
// Initialize PlugPlay services phase 0
status = IopInitializePlugPlayServices(LoaderBlock, 0);
if (!NT_SUCCESS(status)) {
return FALSE;
}
// Call Power manager to initialize for drivers
PoInitDriverServices(0);
// Call HAL to initialize PnP bus driver
HalInitPnpDriver();
deviceNode = IopRootDeviceNode->Child;
while (deviceNode) {
if ((deviceNode->Flags & DNF_STARTED) && !(deviceNode->Flags & DNF_LEGACY_DRIVER)) {
IopInitHalDeviceNode = deviceNode;
deviceNode->Flags |= DNF_HAL_NODE;
break;
}
deviceNode = deviceNode->Sibling;
}
// Call WMI to initialize it and allow it to create its driver object
// Note that no calls to WMI can occur until it is initialized here.
WMIInitialize();
// Save this for use during PnP enumeration -- we NULL it out later
// before LoaderBlock is reused.
IopLoaderBlock = (PVOID)LoaderBlock;
// If this is a remote boot, we need to add a few values to the registry.
if (IoRemoteBootClient) {
status = IopAddRemoteBootValuesToRegistry(LoaderBlock);
if (!NT_SUCCESS(status)) {
KeBugCheckEx(NETWORK_BOOT_INITIALIZATION_FAILED, 1, status, 0, 0);
}
}
// Initialize PlugPlay services phase 1 to execute firmware mapper
status = IopInitializePlugPlayServices(LoaderBlock, 1);
if (!NT_SUCCESS(status)) {
return FALSE;
}
// Initialize the drivers loaded by the boot loader (OSLOADER)
nextDriverObject = &driverObject;
if (!IopInitializeBootDrivers(LoaderBlock, nextDriverObject)) {
#if DBG
DbgPrint("IOINIT: Initializing boot drivers failed\n");
#endif // DBG
return FALSE;
}
// Once we have initialized the boot drivers, we don't need the
// copy of the pointer to the loader block any more.
IopLoaderBlock = NULL;
// If this is a remote boot, start the network and assign
// C: to \Device\LanmanRedirector.
if (IoRemoteBootClient) {
status = IopStartNetworkForRemoteBoot(LoaderBlock);
if (!NT_SUCCESS(status)) {
KeBugCheckEx(NETWORK_BOOT_INITIALIZATION_FAILED, 2, status, 0, 0);
}
}
// Save the current value of the NT Global Flags and enable kernel debugger
// symbol loading while drivers are being loaded so that systems can be
// debugged regardless of whether they are free or checked builds.
oldNtGlobalFlag = NtGlobalFlag;
if (!(NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)) {
NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
}
status = PsLocateSystemDll();
if (!NT_SUCCESS(status)) {
return FALSE;
}
// Initialize the device drivers for the system.
if (!IopInitializeSystemDrivers()) {
#if DBG
DbgPrint("IOINIT: Initializing system drivers failed\n");
#endif // DBG
return FALSE;
}
// Free the memory allocated to contain the group dependency list.
if (IopGroupListHead) {
IopFreeGroupTree(IopGroupListHead);
}
// Walk the list of drivers that have requested that they be called again
// for reinitialization purposes.
while (entry = ExInterlockedRemoveHeadList(&IopDriverReinitializeQueueHead, &IopDatabaseLock)) {
reinitEntry = CONTAINING_RECORD(entry, REINIT_PACKET, ListEntry);
reinitEntry->DriverObject->DriverExtension->Count++;
reinitEntry->DriverObject->Flags &= ~DRVO_REINIT_REGISTERED;
reinitEntry->DriverReinitializationRoutine(reinitEntry->DriverObject, reinitEntry->Context, reinitEntry->DriverObject->DriverExtension->Count);
ExFreePool(reinitEntry);
}
// Reassign \SystemRoot to NT device name path.
if (!IopReassignSystemRoot(LoaderBlock, &ntDeviceName)) {
return FALSE;
}
// Protect the system partition of an ARC system if necessary
if (!IopProtectSystemPartition(LoaderBlock)) {
return(FALSE);
}
// Assign DOS drive letters to disks and cdroms and define \SystemRoot.
ansiString.MaximumLength = NtSystemRoot.MaximumLength / sizeof(WCHAR);
ansiString.Length = 0;
ansiString.Buffer = (RtlAllocateStringRoutine)(ansiString.MaximumLength);
status = RtlUnicodeStringToAnsiString(&ansiString, &NtSystemRoot, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrint("IOINIT: UnicodeToAnsi( %wZ ) failed - %x\n", &NtSystemRoot, status);
return(FALSE);
}
IoAssignDriveLetters(LoaderBlock, &ntDeviceName, ansiString.Buffer, &ansiString);
status = RtlAnsiStringToUnicodeString(&NtSystemRoot, &ansiString, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrint("IOINIT: AnsiToUnicode( %Z ) failed - %x\n", &ansiString, status);
return(FALSE);
}
// Also restore the NT Global Flags to their original state.
NtGlobalFlag = oldNtGlobalFlag;
// Call Power manager to initialize for post-boot drivers
PoInitDriverServices(1);
// Indicate that the I/O system successfully initialized itself.
return TRUE;
}
VOID IopSetIoRoutines()
{
if (pIofCallDriver == NULL) {
pIofCallDriver = IopfCallDriver;
}
if (pIofCompleteRequest == NULL) {
pIofCompleteRequest = IopfCompleteRequest;
}
if (pIoAllocateIrp == NULL) {
pIoAllocateIrp = IopAllocateIrpPrivate;
}
if (pIoFreeIrp == NULL) {
pIoFreeIrp = IopFreeIrp;
}
}
BOOLEAN IopCheckDependencies(IN HANDLE KeyHandle)
/*++
Routine Description:
This routine gets the "DependOnGroup" field for the specified key node
and determines whether any driver in the group(s) that this entry is
dependent on has successfully loaded.
Arguments:
KeyHandle - Supplies a handle to the key representing the driver in question.
Return Value:
The function value is TRUE if the driver should be loaded, otherwise FALSE
--*/
{
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
UNICODE_STRING groupName;
BOOLEAN load;
ULONG length;
PWSTR source;
PTREE_ENTRY treeEntry;
// Attempt to obtain the "DependOnGroup" key for the specified driver
// entry. If one does not exist, then simply mark this driver as being
// one to attempt to load. If it does exist, then check to see whether
// or not any driver in the groups that it is dependent on has loaded and allow it to load.
if (!NT_SUCCESS(IopGetRegistryValue(KeyHandle, L"DependOnGroup", &keyValueInformation))) {
return TRUE;
}
length = keyValueInformation->DataLength;
source = (PWSTR)((PUCHAR)keyValueInformation + keyValueInformation->DataOffset);
load = TRUE;
while (length) {
RtlInitUnicodeString(&groupName, source);
groupName.Length = groupName.MaximumLength;
treeEntry = IopLookupGroupName(&groupName, FALSE);
if (treeEntry) {
if (!treeEntry->DriversLoaded) {
load = FALSE;
break;
}
}
length -= groupName.MaximumLength;
source = (PWSTR)((PUCHAR)source + groupName.MaximumLength);
}
ExFreePool(keyValueInformation);
return load;
}
VOID IopCreateArcNames(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
/*++
Routine Description:
The loader block contains a table of disk signatures and corresponding
ARC names. Each device that the loader can access will appear in the
table. This routine opens each disk device in the system, reads the
signature and compares it to the table. For each match, it creates a
symbolic link between the nt device name and the ARC name.
The checksum value provided by the loader is the ULONG sum of all
elements in the checksum, inverted, plus 1:
checksum = ~sum + 1;
This way the sum of all of the elements can be calculated here and
added to the checksum in the loader block.
If the result is zero, then there is a match.
Arguments:
LoaderBlock - Supplies a pointer to the loader parameter block that was created by the OS Loader.
Return Value:
None.
--*/
{
STRING arcBootDeviceString;
UCHAR deviceNameBuffer[128];
STRING deviceNameString;
UNICODE_STRING deviceNameUnicodeString;
PDEVICE_OBJECT deviceObject;
UCHAR arcNameBuffer[128];
STRING arcNameString;
UNICODE_STRING arcNameUnicodeString;
PFILE_OBJECT fileObject;
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
DISK_GEOMETRY diskGeometry;
PDRIVE_LAYOUT_INFORMATION driveLayout;
PLIST_ENTRY listEntry;
PARC_DISK_SIGNATURE diskBlock;
ULONG diskNumber;
ULONG partitionNumber;
PCHAR arcName;
PULONG buffer;
PIRP irp;
KEVENT event;
LARGE_INTEGER offset;
ULONG checkSum;
ULONG i;
PVOID tmpPtr;
BOOLEAN useLegacyEnumeration = FALSE;
BOOLEAN singleBiosDiskFound;
BOOLEAN bootDiskFound = FALSE;
PARC_DISK_INFORMATION arcInformation = LoaderBlock->ArcDiskInformation;
ULONG totalDriverDisksFound = IoGetConfigurationInformation()->DiskCount;
ULONG totalPnpDisksFound = 0;
STRING arcSystemDeviceString;
STRING osLoaderPathString;
UNICODE_STRING osLoaderPathUnicodeString;
PWSTR diskList = NULL;
wchar_t *pDiskNameList;
STORAGE_DEVICE_NUMBER pnpDiskDeviceNumber;
// ask PNP to give us a list with all the currently active disks
pDiskNameList = diskList;
pnpDiskDeviceNumber.DeviceNumber = 0xFFFFFFFF;
status = IoGetDeviceInterfaces(&DiskClassGuid, NULL, 0, &diskList);
if (!NT_SUCCESS(status)) {
useLegacyEnumeration = TRUE;
if (pDiskNameList) {
*pDiskNameList = L'\0';
}
} else {
// count the number of disks returned
pDiskNameList = diskList;
while (*pDiskNameList != L'\0') {
totalPnpDisksFound++;
pDiskNameList = pDiskNameList + (wcslen(pDiskNameList) + 1);
}
pDiskNameList = diskList;
// if the disk returned by PNP are not all the disks in the system
// it means that some legacy driver has generated a disk device object/link.
// In that case we need to enumerate all pnp disks and then using the legacy
// for-loop also enumerate the non-pnp disks
if (totalPnpDisksFound < totalDriverDisksFound) {
useLegacyEnumeration = TRUE;
}
}
// If a single bios disk was found if there is only a
// single entry on the disk signature list.
singleBiosDiskFound = (arcInformation->DiskSignatures.Flink->Flink == &arcInformation->DiskSignatures) ? (TRUE) : (FALSE);
// Create hal/loader partition name
sprintf(arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
RtlAnsiStringToUnicodeString(&IoArcHalDeviceName, &arcNameString, TRUE);
// Create boot partition name
sprintf(arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
RtlAnsiStringToUnicodeString(&IoArcBootDeviceName, &arcNameString, TRUE);
i = strlen(LoaderBlock->ArcBootDeviceName) + 1;
IoLoaderArcBootDeviceName = ExAllocatePool(PagedPool, i);
if (IoLoaderArcBootDeviceName) {
memcpy(IoLoaderArcBootDeviceName, LoaderBlock->ArcBootDeviceName, i);
}
if (singleBiosDiskFound && strstr(LoaderBlock->ArcBootDeviceName, "cdrom")) {
singleBiosDiskFound = FALSE;
}
// Get ARC boot device name from loader block.
RtlInitAnsiString(&arcBootDeviceString, LoaderBlock->ArcBootDeviceName);
// Get ARC system device name from loader block.
RtlInitAnsiString(&arcSystemDeviceString, LoaderBlock->ArcHalDeviceName);
// If this is a remote boot, create an ArcName for the redirector path.
if (IoRemoteBootClient) {
bootDiskFound = TRUE;
RtlInitAnsiString(&deviceNameString, "\\Device\\LanmanRedirector");
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (NT_SUCCESS(status)) {
sprintf(arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
status = RtlAnsiStringToUnicodeString(&arcNameUnicodeString, &arcNameString, TRUE);
if (NT_SUCCESS(status)) {
// Create symbolic link between NT device name and ARC name.
IoCreateSymbolicLink(&arcNameUnicodeString, &deviceNameUnicodeString);
RtlFreeUnicodeString(&arcNameUnicodeString);
// We've found the system partition--store it away in the registry
// to later be transferred to a application-friendly location.
RtlInitAnsiString(&osLoaderPathString, LoaderBlock->NtHalPathName);
status = RtlAnsiStringToUnicodeString(&osLoaderPathUnicodeString, &osLoaderPathString, TRUE);
#if DBG
if (!NT_SUCCESS(status)) {
DbgPrint("IopCreateArcNames: couldn't allocate unicode string for OsLoader path - %x\n", status);
}
#endif // DBG
if (NT_SUCCESS(status)) {
IopStoreSystemPartitionInformation(&deviceNameUnicodeString, &osLoaderPathUnicodeString);
RtlFreeUnicodeString(&osLoaderPathUnicodeString);
}
}
RtlFreeUnicodeString(&deviceNameUnicodeString);
}
}
// For each disk in the system do the following:
// 1. open the device
// 2. get its geometry
// 3. read the MBR
// 4. determine ARC name via disk signature and checksum
// 5. construct ARC name.
// In order to deal with the case of disk dissappearing before we get to this point
// (due to a failed start on one of many disks present in the system) we ask PNP for a list
// of all the currenttly active disks in the system. If the number of disks returned is
// less than the IoGetConfigurationInformation()->DiskCount, then we have legacy disks
// that we need to enumerate in the for loop.
// In the legacy case, the ending condition for the loop is NOT the total disk on the
// system but an arbitrary number of the max total legacy disks expected in the system..
// Additional note: Legacy disks get assigned symbolic links AFTER all pnp enumeration is complete
totalDriverDisksFound = max(totalPnpDisksFound, totalDriverDisksFound);
if (useLegacyEnumeration && (totalPnpDisksFound == 0)) {
// search up to a maximum arbitrary number of legacy disks
totalDriverDisksFound += 20;
}
for (diskNumber = 0; diskNumber < totalDriverDisksFound; diskNumber++) {
// Construct the NT name for a disk and obtain a reference.
if (pDiskNameList && (*pDiskNameList != L'\0')) {
// retrieve the first symbolic linkname from the PNP disk list
RtlInitUnicodeString(&deviceNameUnicodeString, pDiskNameList);
pDiskNameList = pDiskNameList + (wcslen(pDiskNameList) + 1);
status = IoGetDeviceObjectPointer(&deviceNameUnicodeString, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
if (NT_SUCCESS(status)) {
// since PNP gave s just asym link we have to retrieve the actual
// disk number through an IOCTL call to the disk stack.
// Create IRP for get device number device control.
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
deviceObject,
NULL,
0,
&pnpDiskDeviceNumber,
sizeof(STORAGE_DEVICE_NUMBER),
FALSE,
&event,
&ioStatusBlock);
if (!irp) {
ObDereferenceObject(fileObject);
continue;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatusBlock.Status;
}
if (!NT_SUCCESS(status)) {
ObDereferenceObject(fileObject);
continue;
}
}
if (useLegacyEnumeration && (*pDiskNameList == L'\0')) {
// end of pnp disks
// if there are any legacy disks following we need to update
// the total disk found number to cover the maximum disk number
// a legacy disk could be at. (in a sparse name space)
if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {
pnpDiskDeviceNumber.DeviceNumber = 0;
}
diskNumber = max(diskNumber, pnpDiskDeviceNumber.DeviceNumber);
totalDriverDisksFound = diskNumber + 20;
}
} else {
sprintf(deviceNameBuffer, "\\Device\\Harddisk%d\\Partition0", diskNumber);
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
status = IoGetDeviceObjectPointer(&deviceNameUnicodeString, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
RtlFreeUnicodeString(&deviceNameUnicodeString);
// set the pnpDiskNumber value so its not used.
pnpDiskDeviceNumber.DeviceNumber = 0xFFFFFFFF;
}
if (!NT_SUCCESS(status)) {
continue;
}
// Create IRP for get drive geometry device control.
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
deviceObject,
NULL,
0,
&diskGeometry,
sizeof(DISK_GEOMETRY),
FALSE,
&event,
&ioStatusBlock);
if (!irp) {
ObDereferenceObject(fileObject);
continue;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatusBlock.Status;
}
if (!NT_SUCCESS(status)) {
ObDereferenceObject(fileObject);
continue;
}
// Get partition information for this disk.
status = IoReadPartitionTable(deviceObject, diskGeometry.BytesPerSector, TRUE, &driveLayout);
ObDereferenceObject(fileObject);
if (!NT_SUCCESS(status)) {
continue;
}
// Make sure sector size is at least 512 bytes.
if (diskGeometry.BytesPerSector < 512) {
diskGeometry.BytesPerSector = 512;
}
// Check to see if EZ Drive is out there on this disk. If
// it is then zero out the signature in the drive layout since
// this will never be written by anyone AND change to offset to
// actually read sector 1 rather than 0 cause that's what the
// loader actually did.
offset.QuadPart = 0;
HalExamineMBR(deviceObject, diskGeometry.BytesPerSector, (ULONG)0x55, &tmpPtr);
if (tmpPtr) {
offset.QuadPart = diskGeometry.BytesPerSector;
ExFreePool(tmpPtr);
#ifdef _X86_
} else if (KeI386MachineType & MACHINE_TYPE_PC_9800_COMPATIBLE) {
// PC 9800 compatible machines do not have a standard
// MBR format and use a different sector for checksuming.
offset.QuadPart = 512;
#endif //_X86_
}
// Allocate buffer for sector read and construct the read request.
buffer = ExAllocatePool(NonPagedPoolCacheAlignedMustS, diskGeometry.BytesPerSector);
if (buffer) {
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, deviceObject, buffer, diskGeometry.BytesPerSector, &offset, &event, &ioStatusBlock);
if (!irp) {
ExFreePool(driveLayout);
ExFreePool(buffer);
continue;
}
} else {
ExFreePool(driveLayout);
continue;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatusBlock.Status;
}
if (!NT_SUCCESS(status)) {
ExFreePool(driveLayout);
ExFreePool(buffer);
continue;
}
// Calculate MBR sector checksum. Only 512 bytes are used.
checkSum = 0;
for (i = 0; i < 128; i++) {
checkSum += buffer[i];
}
// For each ARC disk information record in the loader block
// match the disk signature and checksum to determine its ARC
// name and construct the NT ARC names symbolic links.
for (listEntry = arcInformation->DiskSignatures.Flink; listEntry != &arcInformation->DiskSignatures; listEntry = listEntry->Flink) {
// Get next record and compare disk signatures.
diskBlock = CONTAINING_RECORD(listEntry, ARC_DISK_SIGNATURE, ListEntry);
// Compare disk signatures.
// Or if there is only a single disk drive from
// both the bios and driver viewpoints then assign an arc name to that drive.
if ((singleBiosDiskFound && (totalDriverDisksFound == 1)) ||
(diskBlock->Signature == driveLayout->Signature &&
!(diskBlock->CheckSum + checkSum) &&
diskBlock->ValidPartitionTable)) {
// Create unicode device name for physical disk.
if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {
sprintf(deviceNameBuffer, "\\Device\\Harddisk%d\\Partition0", diskNumber);
} else {
sprintf(deviceNameBuffer, "\\Device\\Harddisk%d\\Partition0", pnpDiskDeviceNumber.DeviceNumber);
}
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
// Create unicode ARC name for this partition.
arcName = diskBlock->ArcName;
sprintf(arcNameBuffer, "\\ArcName\\%s", arcName);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
status = RtlAnsiStringToUnicodeString(&arcNameUnicodeString, &arcNameString, TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
// Create symbolic link between NT device name and ARC name.
IoCreateSymbolicLink(&arcNameUnicodeString, &deviceNameUnicodeString);
RtlFreeUnicodeString(&arcNameUnicodeString);
RtlFreeUnicodeString(&deviceNameUnicodeString);
// Create an ARC name for every partition on this disk.
for (partitionNumber = 0; partitionNumber < driveLayout->PartitionCount; partitionNumber++) {
// Create unicode NT device name.
if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {
sprintf(deviceNameBuffer, "\\Device\\Harddisk%d\\Partition%d", diskNumber, partitionNumber + 1);
} else {
sprintf(deviceNameBuffer, "\\Device\\Harddisk%d\\Partition%d", pnpDiskDeviceNumber.DeviceNumber, partitionNumber + 1);
}
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
// Create unicode ARC name for this partition and
// check to see if this is the boot disk.
sprintf(arcNameBuffer, "%spartition(%d)", arcName, partitionNumber + 1);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
if (RtlEqualString(&arcNameString, &arcBootDeviceString, TRUE)) {
bootDiskFound = TRUE;
}
// See if this is the system partition.
if (RtlEqualString(&arcNameString, &arcSystemDeviceString, TRUE)) {
// We've found the system partition--store it away in the registry
// to later be transferred to a application-friendly location.
RtlInitAnsiString(&osLoaderPathString, LoaderBlock->NtHalPathName);
status = RtlAnsiStringToUnicodeString(&osLoaderPathUnicodeString, &osLoaderPathString, TRUE);
#if DBG
if (!NT_SUCCESS(status)) {
DbgPrint("IopCreateArcNames: couldn't allocate unicode string for OsLoader path - %x\n", status);
}
#endif // DBG
if (NT_SUCCESS(status)) {
IopStoreSystemPartitionInformation(&deviceNameUnicodeString, &osLoaderPathUnicodeString);
RtlFreeUnicodeString(&osLoaderPathUnicodeString);
}
}
// Add the NT ARC namespace prefix to the ARC name constructed.
sprintf(arcNameBuffer, "\\ArcName\\%spartition(%d)", arcName, partitionNumber + 1);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
status = RtlAnsiStringToUnicodeString(&arcNameUnicodeString, &arcNameString, TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
// Create symbolic link between NT device name and ARC name.
IoCreateSymbolicLink(&arcNameUnicodeString, &deviceNameUnicodeString);
RtlFreeUnicodeString(&arcNameUnicodeString);
RtlFreeUnicodeString(&deviceNameUnicodeString);
}
} else {
#if DBG
// Check key indicators to see if this condition may be caused by a viral infection.
if (diskBlock->Signature == driveLayout->Signature &&
(diskBlock->CheckSum + checkSum) != 0 &&
diskBlock->ValidPartitionTable) {
DbgPrint("IopCreateArcNames: Virus or duplicate disk signatures\n");
}
#endif
}
}
ExFreePool(driveLayout);
ExFreePool(buffer);
}
if (!bootDiskFound) {
// Locate the disk block that represents the boot device.
diskBlock = NULL;
for (listEntry = arcInformation->DiskSignatures.Flink; listEntry != &arcInformation->DiskSignatures; listEntry = listEntry->Flink) {
diskBlock = CONTAINING_RECORD(listEntry, ARC_DISK_SIGNATURE, ListEntry);
if (strcmp(diskBlock->ArcName, LoaderBlock->ArcBootDeviceName) == 0) {
break;
}
diskBlock = NULL;
}
if (diskBlock) {
// This could be a CdRom boot. Search all of the NT CdRoms
// to locate a checksum match on the diskBlock found. If
// there is a match, assign the ARC name to the CdRom.
irp = NULL;
buffer = ExAllocatePool(NonPagedPoolCacheAlignedMustS, 2048);
if (buffer) {
// Construct the NT names for CdRoms and search each one
// for a checksum match. If found, create the ARC Name
// symbolic link.
for (diskNumber = 0; TRUE; diskNumber++) {
sprintf(deviceNameBuffer, "\\Device\\CdRom%d", diskNumber);
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (NT_SUCCESS(status)) {
status = IoGetDeviceObjectPointer(&deviceNameUnicodeString, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
// All CdRoms have been processed.
RtlFreeUnicodeString(&deviceNameUnicodeString);
break;
}
// Read the block for the checksum calculation.
offset.QuadPart = 0x8000;
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, deviceObject, buffer, 2048, &offset, &event, &ioStatusBlock);
checkSum = 0;
if (irp) {
KeInitializeEvent(&event, NotificationEvent, FALSE);
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatusBlock.Status;
}
if (NT_SUCCESS(status)) {
// Calculate MBR sector checksum.
// 2048 bytes are used.
for (i = 0; i < 2048 / sizeof(ULONG); i++) {
checkSum += buffer[i];
}
}
}
ObDereferenceObject(fileObject);
if (!(diskBlock->CheckSum + checkSum)) {
// This is the boot CdRom. Create the symlink for
// the ARC name from the loader block.
sprintf(arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&arcNameString, arcNameBuffer);
status = RtlAnsiStringToUnicodeString(&arcNameUnicodeString, &arcNameString, TRUE);
if (NT_SUCCESS(status)) {
IoCreateSymbolicLink(&arcNameUnicodeString, &deviceNameUnicodeString);
RtlFreeUnicodeString(&arcNameUnicodeString);
}
RtlFreeUnicodeString(&deviceNameUnicodeString);
break;
}
RtlFreeUnicodeString(&deviceNameUnicodeString);
}
}
ExFreePool(buffer);
}
}
}
if (diskList) {
ExFreePool(diskList);
}
}
GENERIC_MAPPING IopFileMapping = {
STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_EXECUTE,
FILE_ALL_ACCESS
};
GENERIC_MAPPING IopCompletionMapping = {
STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE,
STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
IO_COMPLETION_ALL_ACCESS
};
BOOLEAN IopCreateObjectTypes(VOID)
/*++
Routine Description:
This routine creates the object types used by the I/O system and its components.
The object types created are:
Adapter
Controller
Device
Driver
File
I/O Completion
Arguments:
None.
Return Value:
The function value is a BOOLEAN indicating whether or not the object types were successfully created.
--*/
{
OBJECT_TYPE_INITIALIZER objectTypeInitializer;
UNICODE_STRING nameString;
// Initialize the common fields of the Object Type Initializer record
RtlZeroMemory(&objectTypeInitializer, sizeof(objectTypeInitializer));
objectTypeInitializer.Length = sizeof(objectTypeInitializer);
objectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
objectTypeInitializer.GenericMapping = IopFileMapping;
objectTypeInitializer.PoolType = NonPagedPool;
objectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;
objectTypeInitializer.UseDefaultObject = TRUE;
// Create the object type for adapter objects.
RtlInitUnicodeString(&nameString, L"Adapter");
// objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( struct _ADAPTER_OBJECT );
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoAdapterObjectType))) {
return FALSE;
}
#ifdef _PNP_POWER_
// Create the object type for device helper objects.
RtlInitUnicodeString(&nameString, L"DeviceHandler");
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoDeviceHandlerObjectType))) {
return FALSE;
}
IoDeviceHandlerObjectSize = sizeof(DEVICE_HANDLER_OBJECT);
#endif
// Create the object type for controller objects.
RtlInitUnicodeString(&nameString, L"Controller");
objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(CONTROLLER_OBJECT);
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoControllerObjectType))) {
return FALSE;
}
// Create the object type for device objects.
RtlInitUnicodeString(&nameString, L"Device");
objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DEVICE_OBJECT);
objectTypeInitializer.ParseProcedure = IopParseDevice;
objectTypeInitializer.DeleteProcedure = IopDeleteDevice;
objectTypeInitializer.SecurityProcedure = IopGetSetSecurityObject;
objectTypeInitializer.QueryNameProcedure = (OB_QUERYNAME_METHOD)NULL;
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoDeviceObjectType))) {
return FALSE;
}
// Create the object type for driver objects.
RtlInitUnicodeString(&nameString, L"Driver");
objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DRIVER_OBJECT);
objectTypeInitializer.ParseProcedure = (OB_PARSE_METHOD)NULL;
objectTypeInitializer.DeleteProcedure = IopDeleteDriver;
objectTypeInitializer.SecurityProcedure = (OB_SECURITY_METHOD)NULL;
objectTypeInitializer.QueryNameProcedure = (OB_QUERYNAME_METHOD)NULL;
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoDriverObjectType))) {
return FALSE;
}
// Create the object type for I/O completion objects.
RtlInitUnicodeString(&nameString, L"IoCompletion");
objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KQUEUE);
objectTypeInitializer.InvalidAttributes = OBJ_PERMANENT | OBJ_OPENLINK;
objectTypeInitializer.GenericMapping = IopCompletionMapping;
objectTypeInitializer.ValidAccessMask = IO_COMPLETION_ALL_ACCESS;
objectTypeInitializer.DeleteProcedure = IopDeleteIoCompletion;
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoCompletionObjectType))) {
return FALSE;
}
// Create the object type for file objects.
RtlInitUnicodeString(&nameString, L"File");
objectTypeInitializer.DefaultPagedPoolCharge = IO_FILE_OBJECT_PAGED_POOL_CHARGE;
objectTypeInitializer.DefaultNonPagedPoolCharge = IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE + sizeof(FILE_OBJECT);
objectTypeInitializer.InvalidAttributes = OBJ_PERMANENT | OBJ_EXCLUSIVE | OBJ_OPENLINK;
objectTypeInitializer.GenericMapping = IopFileMapping;
objectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;
objectTypeInitializer.MaintainHandleCount = TRUE;
objectTypeInitializer.CloseProcedure = IopCloseFile;
objectTypeInitializer.DeleteProcedure = IopDeleteFile;
objectTypeInitializer.ParseProcedure = IopParseFile;
objectTypeInitializer.SecurityProcedure = IopGetSetSecurityObject;
objectTypeInitializer.QueryNameProcedure = IopQueryName;
objectTypeInitializer.UseDefaultObject = FALSE;
if (!NT_SUCCESS(ObCreateObjectType(&nameString, &objectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &IoFileObjectType))) {
return FALSE;
}
return TRUE;
}
PTREE_ENTRY IopCreateEntry(IN PUNICODE_STRING GroupName)
/*++
Routine Description:
This routine creates an entry for the specified group name suitable for
being inserted into the group name tree.
Arguments:
GroupName - Specifies the name of the group for the entry.
Return Value:
The function value is a pointer to the created entry.
--*/
{
PTREE_ENTRY treeEntry;
// Allocate and initialize an entry suitable for placing into the group name tree.
treeEntry = ExAllocatePool(PagedPool, sizeof(TREE_ENTRY) + GroupName->Length);
if (!treeEntry) {
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(treeEntry, sizeof(TREE_ENTRY));
treeEntry->GroupName.Length = GroupName->Length;
treeEntry->GroupName.MaximumLength = GroupName->Length;
treeEntry->GroupName.Buffer = (PWCHAR)(treeEntry + 1);
RtlCopyMemory(treeEntry->GroupName.Buffer, GroupName->Buffer, GroupName->Length);
return treeEntry;
}
BOOLEAN IopCreateRootDirectories(VOID)
/*++
Routine Description:
This routine is invoked to create the object manager directory objects
to contain the various device and file system driver objects.
Arguments:
None.
Return Value:
The function value is a BOOLEAN indicating whether or not the directory
objects were successfully created.
--*/
{
HANDLE handle;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING nameString;
NTSTATUS status;
// Create the root directory object for the \Driver directory.
RtlInitUnicodeString(&nameString, L"\\Driver");
InitializeObjectAttributes(&objectAttributes, &nameString, OBJ_PERMANENT, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL);
status = NtCreateDirectoryObject(&handle, DIRECTORY_ALL_ACCESS, &objectAttributes);
if (!NT_SUCCESS(status)) {
return FALSE;
} else {
(VOID)NtClose(handle);
}
// Create the root directory object for the \FileSystem directory.
RtlInitUnicodeString(&nameString, L"\\FileSystem");
status = NtCreateDirectoryObject(&handle, DIRECTORY_ALL_ACCESS, &objectAttributes);
if (!NT_SUCCESS(status)) {
return FALSE;
} else {
(VOID)NtClose(handle);
}
return TRUE;
}
VOID IopFreeGroupTree(PTREE_ENTRY TreeEntry)
/*++
Routine Description:
This routine is invoked to free a node from the group dependency tree.
It is invoked the first time with the root of the tree, and thereafter
recursively to walk the tree and remove the nodes.
Arguments:
TreeEntry - Supplies a pointer to the node to be freed.
Return Value:
None.
--*/
{
// Simply walk the tree in ascending order from the bottom up and free
// each node along the way.
if (TreeEntry->Left) {
IopFreeGroupTree(TreeEntry->Left);
}
if (TreeEntry->Sibling) {
IopFreeGroupTree(TreeEntry->Sibling);
}
if (TreeEntry->Right) {
IopFreeGroupTree(TreeEntry->Right);
}
// All of the children and siblings for this node have been freed, so
// now free this node as well.
ExFreePool(TreeEntry);
}
NTSTATUS IopInitializeAttributesAndCreateObject(IN PUNICODE_STRING ObjectName, IN OUT POBJECT_ATTRIBUTES ObjectAttributes, OUT PDRIVER_OBJECT *DriverObject)
/*++
Routine Description:
This routine is invoked to initialize a set of object attributes and to create a driver object.
Arguments:
ObjectName - Supplies the name of the driver object.
ObjectAttributes - Supplies a pointer to the object attributes structure to be initialized.
DriverObject - Supplies a variable to receive a pointer to the resultant created driver object.
Return Value:
The function value is the final status of the operation.
--*/
{
NTSTATUS status;
// Simply initialize the object attributes and create the driver object.
InitializeObjectAttributes(ObjectAttributes,
ObjectName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL);
status = ObCreateObject(KeGetPreviousMode(),
IoDriverObjectType,
ObjectAttributes,
KernelMode,
(PVOID)NULL,
(ULONG)(sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION)),
0,
0,
(PVOID *)DriverObject);
return status;
}
BOOLEAN IopInitializeBootDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock, OUT PDRIVER_OBJECT *PreviousDriver)
/*++
Routine Description:
This routine is invoked to initialize the boot drivers that were loaded
by the OS Loader. The list of drivers is provided as part of the loader parameter block.
Arguments:
LoaderBlock - Supplies a pointer to the loader parameter block, created by the OS Loader.
Previous Driver - Supplies a variable to receive the address of the driver object chain created by initializing the drivers.
Return Value:
The function value is a BOOLEAN indicating whether or not the boot drivers were successfully initialized.
--*/
{
UNICODE_STRING completeName;
UNICODE_STRING rawFsName;
NTSTATUS status;
PLIST_ENTRY nextEntry;
PLIST_ENTRY entry;
PREINIT_PACKET reinitEntry;
PBOOT_DRIVER_LIST_ENTRY bootDriver;
HANDLE keyHandle;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PDRIVER_OBJECT driverObject;
USHORT i, j;
PLDR_DATA_TABLE_ENTRY driverEntry;
PLDR_DATA_TABLE_ENTRY dllEntry;
UNICODE_STRING groupName;
PTREE_ENTRY treeEntry;
PDRIVER_INFORMATION driverInfo;
START_CONTEXT startContext;
BOOLEAN moreProcessing;
BOOLEAN newDevice;
BOOLEAN textModeSetup = FALSE;
BOOLEAN bootReinitDriversFound;
BOOLEAN bootConfigsOK;
UNREFERENCED_PARAMETER(PreviousDriver);
// Initialize the built-in RAW file system driver.
RtlInitUnicodeString(&rawFsName, L"\\FileSystem\\RAW");
RtlInitUnicodeString(&completeName, L"");
if (!IopInitializeBuiltinDriver(&rawFsName, &completeName, RawInitialize, NULL, textModeSetup)) {
#if DBG
DbgPrint("IOINIT: Failed to initialize RAW filsystem \n");
#endif
return FALSE;
}
// Determine number of group orders and build a list_entry array to link all the drivers
// together based on their groups.
IopGroupIndex = IopGetGroupOrderIndex(NULL);
if (IopGroupIndex == NO_MORE_GROUP) {
return FALSE;
}
IopGroupTable = (PLIST_ENTRY)ExAllocatePool(PagedPool, IopGroupIndex * sizeof(LIST_ENTRY));
if (IopGroupTable == NULL) {
return FALSE;
}
for (i = 0; i < IopGroupIndex; i++) {
InitializeListHead(&IopGroupTable[i]);
}
PnpAsyncOk = FALSE;
// Call DllInitialize for driver dependent DLLs.
nextEntry = LoaderBlock->LoadOrderListHead.Flink;
while (nextEntry != &LoaderBlock->LoadOrderListHead) {
dllEntry = CONTAINING_RECORD(nextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (dllEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL) {
(VOID)MmCallDllInitialize(dllEntry);
}
nextEntry = nextEntry->Flink;
}
// Allocate pool to store driver's start information.
// All the driver info records with the same group value will be linked into a list.
nextEntry = LoaderBlock->BootDriverListHead.Flink;
while (nextEntry != &LoaderBlock->BootDriverListHead) {
bootDriver = CONTAINING_RECORD(nextEntry, BOOT_DRIVER_LIST_ENTRY, Link);
driverEntry = bootDriver->LdrEntry;
driverInfo = (PDRIVER_INFORMATION)ExAllocatePool(PagedPool, sizeof(DRIVER_INFORMATION));
if (driverInfo) {
RtlZeroMemory(driverInfo, sizeof(DRIVER_INFORMATION));
InitializeListHead(&driverInfo->Link);
driverInfo->DataTableEntry = bootDriver;
// Open the driver's registry key to find out if this is a
// filesystem or a driver.
status = IopOpenRegistryKeyEx(&keyHandle, (HANDLE)NULL, &bootDriver->RegistryPath, KEY_READ);
if (!NT_SUCCESS(status)) {
ExFreePool(driverInfo);
} else {
driverInfo->ServiceHandle = keyHandle;
j = IopGetGroupOrderIndex(keyHandle);
if (j == SETUP_RESERVED_GROUP) {
textModeSetup = TRUE;
// Special handling for setupdd.sys
status = IopGetDriverNameFromKeyNode(keyHandle, &completeName);
if (NT_SUCCESS(status)) {
driverObject = IopInitializeBuiltinDriver(
&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE)driverEntry->EntryPoint,
driverEntry,
textModeSetup);
ExFreePool(completeName.Buffer);
NtClose(keyHandle);
ExFreePool(driverInfo);
if (driverObject) {
// Once we successfully initialized the setupdd.sys, we are ready
// to notify it all the root enumerated devices.
IopNotifySetupDevices(IopRootDeviceNode);
} else {
ExFreePool(IopGroupTable);
return FALSE;
}
}
} else {
driverInfo->TagPosition = IopGetDriverTagPriority(keyHandle);
IopInsertDriverList(&IopGroupTable[j], driverInfo);
}
}
}
nextEntry = nextEntry->Flink;
}
// Process each driver base on its group. The group with lower index number (higher
// priority) is processed first.
for (i = 0; i < IopGroupIndex; i++) {
nextEntry = IopGroupTable[i].Flink;
while (nextEntry != &IopGroupTable[i]) {
driverInfo = CONTAINING_RECORD(nextEntry, DRIVER_INFORMATION, Link);
keyHandle = driverInfo->ServiceHandle;
bootDriver = driverInfo->DataTableEntry;
driverEntry = bootDriver->LdrEntry;
driverInfo->Processed = TRUE;
// call the driver's driver entry
// See if this driver has an ObjectName value. If so, this value
// overrides the default ("\Driver" or "\FileSystem").
status = IopGetDriverNameFromKeyNode(keyHandle, &completeName);
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IOINIT: Could not get driver name for %wZ\n", &bootDriver->RegistryPath);
#endif // DBG
driverInfo->Failed = TRUE;
} else {
status = IopGetRegistryValue(keyHandle, L"Group", &keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->DataLength) {
groupName.Length = (USHORT)keyValueInformation->DataLength;
groupName.MaximumLength = groupName.Length;
groupName.Buffer = (PWSTR)((PUCHAR)keyValueInformation + keyValueInformation->DataOffset);
treeEntry = IopLookupGroupName(&groupName, TRUE);
} else {
treeEntry = (PTREE_ENTRY)NULL;
}
ExFreePool(keyValueInformation);
} else {
treeEntry = (PTREE_ENTRY)NULL;
}
// Check if this bus has a BOOT config.
// Buses with BOOT configs allow assignment of BOOT configs on their level.
bootConfigsOK = TRUE;
status = IopGetRegistryValue(keyHandle, L"HasBootConfig", &keyValueInformation);
if (NT_SUCCESS(status)) {
if (*(PULONG)((PUCHAR)keyValueInformation + keyValueInformation->DataOffset) == 0) {
bootConfigsOK = FALSE;
}
ExFreePool(keyValueInformation);
}
driverObject = NULL;
if (IopCheckDependencies(keyHandle)) {
// The driver may already be initialized by IopInitializeBootFilterDriver
// if it is boot filter driver.
// If not, initialize it.
driverObject = driverInfo->DriverObject;
if (driverObject == NULL) {
driverObject = IopInitializeBuiltinDriver(
&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE)driverEntry->EntryPoint,
driverEntry,
textModeSetup);
// Pnp might unload the driver before we get a chance to look at this. So take an extra
// reference.
if (driverObject) {
ObReferenceObject(driverObject);
}
}
}
if (driverObject) {
if (treeEntry) {
treeEntry->DriversLoaded++;
}
driverInfo->DriverObject = driverObject;
} else {
driverInfo->Failed = TRUE;
}
ExFreePool(completeName.Buffer);
}
if (!driverInfo->Failed) {
USHORT group;
IopAddDevicesToBootDriver(driverObject);
// Scan the hardware tree looking for devices which need
// resources or starting.
IopRequestDeviceAction(NULL, ReenumerateBootDevices, NULL, (PNTSTATUS)&bootConfigsOK);
}
// Before processing next boot driver, wait for IoRequestDeviceRemoval complete.
// The driver to be processed may need the resources being released by
// IoRequestDeviceRemoval. (For drivers report detected BOOT device if they fail to
// get the resources in their DriverEntry. They will fail and we will bugcheck with
// inaccessible boot device.)
if (!IopWaitForBootDevicesDeleted()) {
return FALSE;
}
nextEntry = nextEntry->Flink;
}
// If we are done with Bus driver group, then it's time to reserved the Hal resources
// and reserve boot resources
if (i == BUS_DRIVER_GROUP) {
if (textModeSetup == FALSE) {
// BUGBUG - There problems with Async ops, disable for now.
// PnpAsyncOk = TRUE;
}
// Reserve BOOT configs on Internal bus 0.
IopReserveLegacyBootResources(Internal, 0);
IopReserveResourcesRoutine = IopAllocateBootResources;
ASSERT(IopInitHalResources == NULL);
ASSERT(IopInitReservedResourceList == NULL);
IopBootConfigsReserved = TRUE;
}
}
// If we started a network boot driver, then imitate what DHCP does
// in sending IOCTLs.
if (IoRemoteBootClient) {
status = IopStartTcpIpForRemoteBoot(LoaderBlock);
if (!NT_SUCCESS(status)) {
KeBugCheckEx(NETWORK_BOOT_INITIALIZATION_FAILED, 3, status, 0, 0);
}
}
// Scan the hardware tree looking for devices which need
// resources or starting.
PnPBootDriversLoaded = TRUE;
IopResourcesReleased = TRUE;
IopRequestDeviceAction(NULL, RestartEnumeration, NULL, NULL);
// If start irps are handled asynchronously, we need to make sure all the boot devices
// started before continue.
if (!IopWaitForBootDevicesStarted()) {
return FALSE;
}
bootReinitDriversFound = FALSE;
while (entry = ExInterlockedRemoveHeadList(&IopBootDriverReinitializeQueueHead, &IopDatabaseLock)) {
bootReinitDriversFound = TRUE;
reinitEntry = CONTAINING_RECORD(entry, REINIT_PACKET, ListEntry);
reinitEntry->DriverObject->DriverExtension->Count++;
reinitEntry->DriverObject->Flags &= ~DRVO_BOOTREINIT_REGISTERED;
reinitEntry->DriverReinitializationRoutine(reinitEntry->DriverObject,
reinitEntry->Context,
reinitEntry->DriverObject->DriverExtension->Count);
ExFreePool(reinitEntry);
}
// If there were any drivers that registered for boot reinitialization, then
// we need to wait one more time to make sure we catch any additional
// devices that were created in response to the reinitialization callback.
if (bootReinitDriversFound && !IopWaitForBootDevicesStarted()) {
return FALSE;
}
// Link NT device names to ARC names now that all of the boot drivers
// have intialized.
IopCreateArcNames(LoaderBlock);
// Find and mark the boot partition device object so that if a subsequent
// access or mount of the device during initialization occurs, an more
// bugcheck can be produced that helps the user understand why the system
// is failing to boot and run properly. This occurs when either one of the
// device drivers or the file system fails to load, or when the file system
// cannot mount the device for some other reason.
if (!IopMarkBootPartition(LoaderBlock)) {
return FALSE;
}
// BUGBUG - There problems with Async ops, disable for now.
// PnpAsyncOk = TRUE;
PnPBootDriversInitialized = TRUE;
// Go thru every driver that we initialized. If it supports AddDevice entry and
// did not create any device object after we start it. We mark it as failure so
// text mode setup knows this driver is not needed.
for (i = 0; i < IopGroupIndex; i++) {
while (IsListEmpty(&IopGroupTable[i]) == FALSE) {
BOOLEAN failed;
nextEntry = RemoveHeadList(&IopGroupTable[i]);
driverInfo = CONTAINING_RECORD(nextEntry, DRIVER_INFORMATION, Link);
driverObject = driverInfo->DriverObject;
if (textModeSetup &&
(driverInfo->Failed == FALSE) &&
!IopIsLegacyDriver(driverObject) &&
(driverObject->DeviceObject == NULL) &&
!(driverObject->Flags & DRVO_REINIT_REGISTERED)) {
// If failed is not set and it's not a legacy driver and it has no device object
// tread it as failure case.
driverInfo->Failed = TRUE;
if (!(driverObject->Flags & DRVO_UNLOAD_INVOKED)) {
driverObject->Flags |= DRVO_UNLOAD_INVOKED;
if (driverObject->DriverUnload) {
driverObject->DriverUnload(driverObject);
}
ObMakeTemporaryObject(driverObject); // Reference taken while inserting into the object table.
ObDereferenceObject(driverObject); // Reference taken when getting driver object pointer.
}
}
if (driverObject) {
ObDereferenceObject(driverObject); // Reference taken specifically for text mode setup.
}
if (driverInfo->Failed) {
driverInfo->DataTableEntry->LdrEntry->Flags |= LDRP_FAILED_BUILTIN_LOAD;
}
NtClose(driverInfo->ServiceHandle);
ExFreePool(driverInfo);
}
}
ExFreePool(IopGroupTable);
// Initialize the drivers necessary to dump all of physical memory to the
// disk if the system is configured to do so.
return TRUE;
}
PDRIVER_OBJECT
IopInitializeBuiltinDriver(
IN PUNICODE_STRING DriverName,
IN PUNICODE_STRING RegistryPath,
IN PDRIVER_INITIALIZE DriverInitializeRoutine,
IN PLDR_DATA_TABLE_ENTRY DriverEntry,
IN BOOLEAN TextModeSetup)
/*++
Routine Description:
This routine is invoked to initialize a built-in driver.
Arguments:
DriverName - Specifies the name to be used in creating the driver object.
RegistryPath - Specifies the path to be used by the driver to get to the registry.
DriverInitializeRoutine - Specifies the initialization entry point of the built-in driver.
DriverEntry - Specifies the driver data table entry to determine if the driver is a wdm driver.
TextModeSetup - Specifies if this is TextMode setup boot.
Return Value:
The function returns a pointer to a DRIVER_OBJECT if the built-in
driver successfully initialized. Otherwise, a value of NULL is returned.
--*/
{
HANDLE handle;
PDRIVER_OBJECT driverObject;
PDRIVER_OBJECT tmpDriverObject;
OBJECT_ATTRIBUTES objectAttributes;
PWSTR buffer;
NTSTATUS status;
HANDLE serviceHandle;
PWSTR pserviceName;
USHORT serviceNameLength;
PDRIVER_EXTENSION driverExtension;
PIMAGE_NT_HEADERS ntHeaders;
PVOID imageBase;
#if DBG
LARGE_INTEGER stime, etime;
ULONG dtime;
#endif
PLIST_ENTRY entry;
PLDR_DATA_TABLE_ENTRY DataTableEntry;
// Begin by creating the driver object.
status = IopInitializeAttributesAndCreateObject(DriverName, &objectAttributes, &driverObject);
if (!NT_SUCCESS(status)) {
return NULL;
}
// Initialize the driver object.
InitializeDriverObject(driverObject);
driverObject->DriverInit = DriverInitializeRoutine;
// Insert the driver object into the object table.
status = ObInsertObject(driverObject, NULL, FILE_READ_DATA, 0, (PVOID *)NULL, &handle);
if (!NT_SUCCESS(status)) {
return NULL;
}
// Reference the handle and obtain a pointer to the driver object so that
// the handle can be deleted without the object going away.
status = ObReferenceObjectByHandle(handle,
0,
IoDriverObjectType,
KernelMode,
(PVOID *)&tmpDriverObject,
(POBJECT_HANDLE_INFORMATION)NULL);
ASSERT(status == STATUS_SUCCESS);
// Fill in the DriverSection so the image will be automatically unloaded on
// failures. We should use the entry from the PsModuleList.
entry = PsLoadedModuleList.Flink;
while (entry != &PsLoadedModuleList && DriverEntry) {
DataTableEntry = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (RtlEqualString((PSTRING)&DriverEntry->BaseDllName, (PSTRING)&DataTableEntry->BaseDllName, TRUE)) {
driverObject->DriverSection = DataTableEntry;
break;
}
entry = entry->Flink;
}
// The boot process takes a while loading drivers. Indicate that
// progress is being made.
InbvIndicateProgress();
// Get start and sice for the DriverObject.
if (DriverEntry) {
imageBase = DriverEntry->DllBase;
ntHeaders = RtlImageNtHeader(imageBase);
driverObject->DriverStart = imageBase;
driverObject->DriverSize = ntHeaders->OptionalHeader.SizeOfImage;
if (!(ntHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER)) {
driverObject->Flags |= DRVO_LEGACY_DRIVER;
}
} else {
ntHeaders = NULL;
driverObject->Flags |= DRVO_LEGACY_DRIVER;
}
// Save the name of the driver so that it can be easily located by functions
// such as error logging.
buffer = ExAllocatePool(PagedPool, DriverName->MaximumLength + 2);
if (buffer) {
driverObject->DriverName.Buffer = buffer;
driverObject->DriverName.MaximumLength = DriverName->MaximumLength;
driverObject->DriverName.Length = DriverName->Length;
RtlCopyMemory(driverObject->DriverName.Buffer, DriverName->Buffer, DriverName->MaximumLength);
buffer[DriverName->Length >> 1] = (WCHAR) '\0';
}
// Save the name of the service key so that it can be easily located by PnP
// mamager.
driverExtension = driverObject->DriverExtension;
if (RegistryPath && RegistryPath->Length != 0) {
pserviceName = RegistryPath->Buffer + RegistryPath->Length / sizeof(WCHAR) - 1;
if (*pserviceName == OBJ_NAME_PATH_SEPARATOR) {
pserviceName--;
}
serviceNameLength = 0;
while (pserviceName != RegistryPath->Buffer) {
if (*pserviceName == OBJ_NAME_PATH_SEPARATOR) {
pserviceName++;
break;
} else {
serviceNameLength += sizeof(WCHAR);
pserviceName--;
}
}
if (pserviceName == RegistryPath->Buffer) {
serviceNameLength += sizeof(WCHAR);
}
buffer = ExAllocatePool(NonPagedPool, serviceNameLength + sizeof(UNICODE_NULL));
if (buffer) {
driverExtension->ServiceKeyName.Buffer = buffer;
driverExtension->ServiceKeyName.MaximumLength = serviceNameLength + sizeof(UNICODE_NULL);
driverExtension->ServiceKeyName.Length = serviceNameLength;
RtlCopyMemory(driverExtension->ServiceKeyName.Buffer, pserviceName, serviceNameLength);
buffer[driverExtension->ServiceKeyName.Length >> 1] = UNICODE_NULL;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
driverExtension->ServiceKeyName.Buffer = NULL;
driverExtension->ServiceKeyName.Length = 0;
goto exit;
}
// Prepare driver initialization
status = IopOpenRegistryKeyEx(&serviceHandle, NULL, RegistryPath, KEY_ALL_ACCESS);
if (NT_SUCCESS(status)) {
status = IopPrepareDriverLoading(&driverExtension->ServiceKeyName, serviceHandle, ntHeaders);
NtClose(serviceHandle);
if (!NT_SUCCESS(status)) {
goto exit;
}
} else {
goto exit;
}
} else {
driverExtension->ServiceKeyName.Buffer = NULL;
driverExtension->ServiceKeyName.MaximumLength = 0;
driverExtension->ServiceKeyName.Length = 0;
}
// Load the Registry information in the appropriate fields of the device
// object.
driverObject->HardwareDatabase = &CmRegistryMachineHardwareDescriptionSystemName;
#if DBG
KeQuerySystemTime(&stime);
#endif
// Now invoke the driver's initialization routine to initialize itself.
PERFINFO_DRIVER_INIT(driverObject);
status = driverObject->DriverInit(driverObject, RegistryPath);
PERFINFO_DRIVER_INIT_COMPLETE(driverObject);
#if DBG
// If DriverInit took longer than 5 seconds or the driver did not load,
// print a message.
KeQuerySystemTime(&etime);
dtime = (ULONG)((etime.QuadPart - stime.QuadPart) / 1000000);
if (dtime > 50 || !NT_SUCCESS(status)) {
if (dtime < 10) {
DbgPrint("IOINIT: Built-in driver %wZ failed to initialize - %lX\n", DriverName, status);
} else {
DbgPrint("IOINIT: Built-in driver %wZ took %d.%ds to ", DriverName, dtime / 10, dtime % 10);
if (NT_SUCCESS(status)) {
DbgPrint("initialize\n");
} else {
DbgPrint("fail initialization - %lX\n", status);
}
}
}
#endif
exit:
NtClose(handle);
// If we load the driver because we think it is a legacy driver and
// it does not create any device object in its DriverEntry. We will
// unload this driver.
if (NT_SUCCESS(status) && !IopIsLegacyDriver(driverObject)) {
if (driverObject->DeviceObject == NULL &&
driverExtension->ServiceKeyName.Buffer &&
!IopIsAnyDeviceInstanceEnabled(&driverExtension->ServiceKeyName, NULL, FALSE)) {
if (TextModeSetup && !(driverObject->Flags & DRVO_REINIT_REGISTERED)) {
// Clean up but leave driver object. Because it may be needed later.
// After boot driver phase completes, we will process all the driver objects
// which still have no device to control.
IopDriverLoadingFailed(NULL, &driverObject->DriverExtension->ServiceKeyName);
}
} else {
// Start the devices controlled by the driver and enumerate them
// At this point, we know there is at least one device controlled by the driver.
IopDeleteLegacyKey(driverObject);
}
}
if (NT_SUCCESS(status)) {
IopReadyDeviceObjects(driverObject);
return driverObject;
} else {
if (status != STATUS_PLUGPLAY_NO_DEVICE) {
// if STATUS_PLUGPLAY_NO_DEVICE, the driver was disable by hardware profile.
IopDriverLoadingFailed(NULL, &driverObject->DriverExtension->ServiceKeyName);
ObMakeTemporaryObject(driverObject);
ObDereferenceObject(driverObject);
}
return NULL;
}
}
BOOLEAN IopInitializeSystemDrivers(VOID)
/*++
Routine Description:
This routine is invoked to load and initialize all of the drivers that
are supposed to be loaded during Phase 1 initialization of the I/O
system. This is accomplished by calling the Configuration Manager to
get a NULL-terminated array of handles to the open keys for each driver
that is to be loaded, and then loading and initializing the driver.
Arguments:
None.
Return Value:
The function value is a BOOLEAN indicating whether or not the drivers
were successfully loaded and initialized.
--*/
{
BOOLEAN newDevice, moreProcessing;
NTSTATUS status;
PHANDLE driverList;
PHANDLE savedList;
HANDLE enumHandle;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
UNICODE_STRING groupName, enumName;
PTREE_ENTRY treeEntry;
UNICODE_STRING driverName;
PDRIVER_OBJECT driverObject;
START_CONTEXT startContext;
// Scan the device node tree to process any device which have been enumerated
// but not been added/started.
startContext.LoadDriver = TRUE;
startContext.AddContext.DriverStartType = SERVICE_DEMAND_START;
IopProcessAddDevices(IopRootDeviceNode, NO_MORE_GROUP, SERVICE_DEMAND_START);
// Process the whole device tree to assign resources to those devices who
// have been successfully added to their drivers.
newDevice = TRUE;
startContext.AddContext.GroupsToStart = NO_MORE_GROUP;
startContext.AddContext.GroupToStartNext = NO_MORE_GROUP;
while (newDevice || moreProcessing) {
startContext.NewDevice = FALSE;
moreProcessing = IopProcessAssignResources(IopRootDeviceNode, FALSE, TRUE);
// Process the whole device tree to start those devices who have been allocated
// resources and waiting to be started.
// Note, the IopProcessStartDevices routine may enumerate new devices.
IopProcessStartDevices(IopRootDeviceNode, &startContext);
newDevice = startContext.NewDevice;
}
// Walk thru the service list to load the remaining system start drivers.
// (Most likely these drivers are software drivers.)
// Get the list of drivers that are to be loaded during this phase of
// system initialization, and invoke each driver in turn. Ensure that
// the list really exists, otherwise get out now.
if (!(driverList = CmGetSystemDriverList())) {
return TRUE;
}
// Walk the entire list, loading each of the drivers if not already loaded,
// until there are no more drivers in the list.
for (savedList = driverList; *driverList; driverList++) {
// Now check if the driver has been loaded already.
// get the name of the driver object first ...
status = IopGetDriverNameFromKeyNode(*driverList, &driverName);
if (NT_SUCCESS(status)) {
driverObject = IopReferenceDriverObjectByName(&driverName);
RtlFreeUnicodeString(&driverName);
if (driverObject) {
// Driver was loaded already. Dereference the driver object
// and skip it.
ObDereferenceObject(driverObject);
continue;
}
}
// Open registry ServiceKeyName\Enum branch to check if the driver was
// loaded before but failed.
PiWstrToUnicodeString(&enumName, REGSTR_KEY_ENUM);
status = IopOpenRegistryKeyEx(&enumHandle, *driverList, &enumName, KEY_READ);
if (NT_SUCCESS(status)) {
ULONG startFailed = 0;
status = IopGetRegistryValue(enumHandle, L"INITSTARTFAILED", &keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->DataLength) {
startFailed = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
ExFreePool(keyValueInformation);
}
ZwClose(enumHandle);
if (startFailed != 0) {
continue;
}
}
// The driver is not loaded yet. Load it ...
status = IopGetRegistryValue(*driverList, L"Group", &keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->DataLength) {
groupName.Length = (USHORT)keyValueInformation->DataLength;
groupName.MaximumLength = groupName.Length;
groupName.Buffer = (PWSTR)((PUCHAR)keyValueInformation + keyValueInformation->DataOffset);
treeEntry = IopLookupGroupName(&groupName, TRUE);
} else {
treeEntry = (PTREE_ENTRY)NULL;
}
ExFreePool(keyValueInformation);
} else {
treeEntry = (PTREE_ENTRY)NULL;
}
if (IopCheckDependencies(*driverList)) {
if (NT_SUCCESS(IopLoadDriver(*driverList, TRUE))) {
if (treeEntry) {
treeEntry->DriversLoaded++;
}
}
}
// The boot process takes a while loading drivers. Indicate that
// progress is being made.
InbvIndicateProgress();
}
// Finally, free the pool that was allocated for the list and return
// an indicator the load operation worked.
ExFreePool((PVOID)savedList);
// Walk the device tree again to enumerate any devices reported after the system drivers
// started.
startContext.LoadDriver = TRUE;
startContext.AddContext.DriverStartType = SERVICE_DEMAND_START;
IopProcessAddDevices(IopRootDeviceNode, NO_MORE_GROUP, SERVICE_DEMAND_START);
// Process the whole device tree to assign resources to those devices who
// have been successfully added to their drivers.
newDevice = TRUE;
startContext.AddContext.GroupsToStart = NO_MORE_GROUP;
startContext.AddContext.GroupToStartNext = NO_MORE_GROUP;
do {
startContext.NewDevice = FALSE;
moreProcessing = IopProcessAssignResources(IopRootDeviceNode, newDevice, TRUE);
// Process the whole device tree to start those devices who have been allocated
// resources and waiting to be started.
// Note, the IopProcessStartDevices routine may enumerate new devices.
IopProcessStartDevices(IopRootDeviceNode, &startContext);
newDevice = startContext.NewDevice;
} while (newDevice || moreProcessing);
// Mark pnp has completed the driver loading for both system and
// autoload drivers.
PnPInitialized = TRUE;
return TRUE;
}
PTREE_ENTRY IopLookupGroupName(IN PUNICODE_STRING GroupName, IN BOOLEAN Insert)
/*++
Routine Description:
This routine looks up a group entry in the group load tree and either
returns a pointer to it, or optionally creates the entry and inserts
it into the tree.
Arguments:
GroupName - The name of the group to look up, or insert.
Insert - Indicates whether or not an entry is to be created and inserted
into the tree if the name does not already exist.
Return Value:
The function value is a pointer to the entry for the specified group name, or NULL.
--*/
{
PTREE_ENTRY treeEntry;
PTREE_ENTRY previousEntry;
// Begin by determining whether or not there are any entries in the tree
// whatsoever. If not, and it is OK to insert, then insert this entry
// into the tree.
if (!IopGroupListHead) {
if (!Insert) {
return (PTREE_ENTRY)NULL;
} else {
IopGroupListHead = IopCreateEntry(GroupName);
return IopGroupListHead;
}
}
// The tree is not empty, so actually attempt to do a lookup.
treeEntry = IopGroupListHead;
for (;;) {
if (GroupName->Length < treeEntry->GroupName.Length) {
if (treeEntry->Left) {
treeEntry = treeEntry->Left;
} else {
if (!Insert) {
return (PTREE_ENTRY)NULL;
} else {
treeEntry->Left = IopCreateEntry(GroupName);
return treeEntry->Left;
}
}
} else if (GroupName->Length > treeEntry->GroupName.Length) {
if (treeEntry->Right) {
treeEntry = treeEntry->Right;
} else {
if (!Insert) {
return (PTREE_ENTRY)NULL;
} else {
treeEntry->Right = IopCreateEntry(GroupName);
return treeEntry->Right;
}
}
} else {
if (!RtlEqualUnicodeString(GroupName, &treeEntry->GroupName, TRUE)) {
previousEntry = treeEntry;
while (treeEntry->Sibling) {
treeEntry = treeEntry->Sibling;
if (RtlEqualUnicodeString(GroupName, &treeEntry->GroupName, TRUE)) {
return treeEntry;
}
previousEntry = previousEntry->Sibling;
}
if (!Insert) {
return (PTREE_ENTRY)NULL;
} else {
previousEntry->Sibling = IopCreateEntry(GroupName);
return previousEntry->Sibling;
}
} else {
return treeEntry;
}
}
}
}
BOOLEAN IopMarkBootPartition(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
/*++
Routine Description:
This routine is invoked to locate and mark the boot partition device object
as a boot device so that subsequent operations can fail more cleanly and
with a better explanation of why the system failed to boot and run properly.
Arguments:
LoaderBlock - Supplies a pointer to the loader parameter block created
by the OS Loader during the boot process. This structure contains
the various system partition and boot device names and paths.
Return Value:
The function value is TRUE if everything worked, otherwise FALSE.
Notes:
If the boot partition device object cannot be found, then the system will bugcheck.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
STRING deviceNameString;
UCHAR deviceNameBuffer[256];
UNICODE_STRING deviceNameUnicodeString;
NTSTATUS status;
HANDLE fileHandle;
IO_STATUS_BLOCK ioStatus;
PFILE_OBJECT fileObject;
CHAR ArcNameFmt[12];
ArcNameFmt[0] = '\\';
ArcNameFmt[1] = 'A';
ArcNameFmt[2] = 'r';
ArcNameFmt[3] = 'c';
ArcNameFmt[4] = 'N';
ArcNameFmt[5] = 'a';
ArcNameFmt[6] = 'm';
ArcNameFmt[7] = 'e';
ArcNameFmt[8] = '\\';
ArcNameFmt[9] = '%';
ArcNameFmt[10] = 's';
ArcNameFmt[11] = '\0';
// Open the ARC boot device object. The boot device driver should have
// created the object.
sprintf(deviceNameBuffer, ArcNameFmt, LoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
return FALSE;
}
InitializeObjectAttributes(&objectAttributes, &deviceNameUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ZwOpenFile(&fileHandle, FILE_READ_ATTRIBUTES, &objectAttributes, &ioStatus, 0, FILE_NON_DIRECTORY_FILE);
if (!NT_SUCCESS(status)) {
KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE, (ULONG_PTR)&deviceNameUnicodeString, status, 0, 0);
}
// Convert the file handle into a pointer to the device object itself.
status = ObReferenceObjectByHandle(fileHandle, 0, IoFileObjectType, KernelMode, (PVOID *)&fileObject, NULL);
if (!NT_SUCCESS(status)) {
RtlFreeUnicodeString(&deviceNameUnicodeString);
return FALSE;
}
// Mark the device object represented by the file object.
fileObject->DeviceObject->Flags |= DO_SYSTEM_BOOT_PARTITION;
// Reference the device object and store the reference.
ObReferenceObject(fileObject->DeviceObject);
IopErrorLogObject = fileObject->DeviceObject;
RtlFreeUnicodeString(&deviceNameUnicodeString);
// Finally, close the handle and dereference the file object.
NtClose(fileHandle);
ObDereferenceObject(fileObject);
return TRUE;
}
BOOLEAN IopReassignSystemRoot(IN PLOADER_PARAMETER_BLOCK LoaderBlock, OUT PSTRING NtDeviceName)
/*++
Routine Description:
This routine is invoked to reassign \SystemRoot from being an ARC path
name to its NT path name equivalent. This is done by looking up the
ARC device name as a symbolic link and determining which NT device object
is referred to by it. The link is then replaced with the new name.
Arguments:
LoaderBlock - Supplies a pointer to the loader parameter block created
by the OS Loader during the boot process. This structure contains
the various system partition and boot device names and paths.
NtDeviceName - Specifies a pointer to a STRING to receive the NT name of the device from which the system was booted.
Return Value:
The function value is a BOOLEAN indicating whether or not the ARC name was resolved to an NT name.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
UCHAR deviceNameBuffer[256];
WCHAR arcNameUnicodeBuffer[64];
UCHAR arcNameStringBuffer[256];
STRING deviceNameString;
STRING arcNameString;
STRING linkString;
UNICODE_STRING linkUnicodeString;
UNICODE_STRING deviceNameUnicodeString;
UNICODE_STRING arcNameUnicodeString;
HANDLE linkHandle;
#if DBG
UCHAR debugBuffer[256];
STRING debugString;
UNICODE_STRING debugUnicodeString;
#endif
CHAR ArcNameFmt[12];
ArcNameFmt[0] = '\\';
ArcNameFmt[1] = 'A';
ArcNameFmt[2] = 'r';
ArcNameFmt[3] = 'c';
ArcNameFmt[4] = 'N';
ArcNameFmt[5] = 'a';
ArcNameFmt[6] = 'm';
ArcNameFmt[7] = 'e';
ArcNameFmt[8] = '\\';
ArcNameFmt[9] = '%';
ArcNameFmt[10] = 's';
ArcNameFmt[11] = '\0';
// Open the ARC boot device symbolic link object. The boot device
// driver should have created the object.
sprintf(deviceNameBuffer, ArcNameFmt, LoaderBlock->ArcBootDeviceName);
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&deviceNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
return FALSE;
}
InitializeObjectAttributes(&objectAttributes, &deviceNameUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject(&linkHandle, SYMBOLIC_LINK_ALL_ACCESS, &objectAttributes);
if (!NT_SUCCESS(status)) {
#if DBG
sprintf(debugBuffer, "IOINIT: unable to resolve %s, Status == %X\n", deviceNameBuffer, status);
RtlInitAnsiString(&debugString, debugBuffer);
status = RtlAnsiStringToUnicodeString(&debugUnicodeString, &debugString, TRUE);
if (NT_SUCCESS(status)) {
ZwDisplayString(&debugUnicodeString);
RtlFreeUnicodeString(&debugUnicodeString);
}
#endif // DBG
RtlFreeUnicodeString(&deviceNameUnicodeString);
return FALSE;
}
// Get handle to \SystemRoot symbolic link.
arcNameUnicodeString.Buffer = arcNameUnicodeBuffer;
arcNameUnicodeString.Length = 0;
arcNameUnicodeString.MaximumLength = sizeof(arcNameUnicodeBuffer);
status = NtQuerySymbolicLinkObject(linkHandle, &arcNameUnicodeString, NULL);
if (!NT_SUCCESS(status)) {
return FALSE;
}
arcNameString.Buffer = arcNameStringBuffer;
arcNameString.Length = 0;
arcNameString.MaximumLength = sizeof(arcNameStringBuffer);
status = RtlUnicodeStringToAnsiString(&arcNameString, &arcNameUnicodeString, FALSE);
arcNameStringBuffer[arcNameString.Length] = '\0';
NtClose(linkHandle);
RtlFreeUnicodeString(&deviceNameUnicodeString);
RtlInitAnsiString(&linkString, INIT_SYSTEMROOT_LINKNAME);
status = RtlAnsiStringToUnicodeString(&linkUnicodeString, &linkString, TRUE);
if (!NT_SUCCESS(status)) {
return FALSE;
}
InitializeObjectAttributes(&objectAttributes, &linkUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject(&linkHandle, SYMBOLIC_LINK_ALL_ACCESS, &objectAttributes);
if (!NT_SUCCESS(status)) {
return FALSE;
}
NtMakeTemporaryObject(linkHandle);
NtClose(linkHandle);
sprintf(deviceNameBuffer, "%Z%s", &arcNameString, LoaderBlock->NtBootPathName);
// Get NT device name for \SystemRoot assignment.
RtlCopyString(NtDeviceName, &arcNameString);
deviceNameBuffer[strlen(deviceNameBuffer) - 1] = '\0';
RtlInitAnsiString(&deviceNameString, deviceNameBuffer);
InitializeObjectAttributes(&objectAttributes, &linkUnicodeString, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
status = RtlAnsiStringToUnicodeString(&arcNameUnicodeString, &deviceNameString, TRUE);
if (!NT_SUCCESS(status)) {
return FALSE;
}
status = NtCreateSymbolicLinkObject(&linkHandle, SYMBOLIC_LINK_ALL_ACCESS, &objectAttributes, &arcNameUnicodeString);
RtlFreeUnicodeString(&arcNameUnicodeString);
RtlFreeUnicodeString(&linkUnicodeString);
NtClose(linkHandle);
#if DBG
if (NT_SUCCESS(status)) {
sprintf(debugBuffer, "INIT: Reassigned %s => %s\n", INIT_SYSTEMROOT_LINKNAME, deviceNameBuffer);
} else {
sprintf(debugBuffer, "INIT: unable to create %s => %s, Status == %X\n", INIT_SYSTEMROOT_LINKNAME, deviceNameBuffer, status);
}
RtlInitAnsiString(&debugString, debugBuffer);
status = RtlAnsiStringToUnicodeString(&debugUnicodeString, &debugString, TRUE);
if (NT_SUCCESS(status)) {
ZwDisplayString(&debugUnicodeString);
RtlFreeUnicodeString(&debugUnicodeString);
}
#endif // DBG
return TRUE;
}
VOID IopStoreSystemPartitionInformation(IN PUNICODE_STRING NtSystemPartitionDeviceName, IN OUT PUNICODE_STRING OsLoaderPathName)
/*++
Routine Description:
This routine writes two values to the registry (under HKLM\SYSTEM\Setup)--one containing the NT device name of the system partition and the other containing the path to the OS loader.
These values will later be migrated into a Win95-compatible registry location (NT path converted to DOS path),
so that installation programs (including our own setup) have a rock-solid way of knowing what the system partition is, on both ARC and x86.
ERRORS ENCOUNTERED IN THIS ROUTINE ARE NOT CONSIDERED FATAL.
Arguments:
NtSystemPartitionDeviceName - supplies the NT device name of the system partition.
This is the \Device\Harddisk<n>\Partition<m> name, which used to be the actual device name,
but now is a symbolic link to a name of the form \Device\Volume<x>.
We open up this symbolic link, and retrieve the name that it points to.
The target name is the one we store away in the registry.
OsLoaderPathName - supplies the path (on the partition specified in the 1st parameter)
where the OS loader is located. Upon return, this path will have had its trailing
backslash removed (if present, and path isn't root).
--*/
{
NTSTATUS status;
HANDLE linkHandle;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE systemHandle, setupHandle;
UNICODE_STRING nameString, volumeNameString;
WCHAR voumeNameStringBuffer[256];
// Declare a unicode buffer big enough to contain the longest string we'll be using.
// (ANSI string in 'sizeof()' below on purpose--we want the number of chars here.)
WCHAR nameBuffer[sizeof("SystemPartition")];
// Both UNICODE_STRING buffers should be NULL-terminated.
ASSERT(NtSystemPartitionDeviceName->MaximumLength >= NtSystemPartitionDeviceName->Length + sizeof(WCHAR));
ASSERT(NtSystemPartitionDeviceName->Buffer[NtSystemPartitionDeviceName->Length / sizeof(WCHAR)] == L'\0');
ASSERT(OsLoaderPathName->MaximumLength >= OsLoaderPathName->Length + sizeof(WCHAR));
ASSERT(OsLoaderPathName->Buffer[OsLoaderPathName->Length / sizeof(WCHAR)] == L'\0');
// Open the NtSystemPartitionDeviceName symbolic link, and find out the volume device it points to.
InitializeObjectAttributes(&objectAttributes, NtSystemPartitionDeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject(&linkHandle, SYMBOLIC_LINK_QUERY, &objectAttributes);
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IopStoreSystemPartitionInformation: couldn't open symbolic link %wZ - %x\n", NtSystemPartitionDeviceName, status);
#endif // DBG
return;
}
volumeNameString.Buffer = voumeNameStringBuffer;
volumeNameString.Length = 0;
// Leave room at the end of the buffer for a terminating null, in case we need to add one.
volumeNameString.MaximumLength = sizeof(voumeNameStringBuffer) - sizeof(WCHAR);
status = NtQuerySymbolicLinkObject(linkHandle, &volumeNameString, NULL);
// We don't need the handle to the symbolic link any longer.
NtClose(linkHandle);
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IopStoreSystemPartitionInformation: couldn't query symbolic link %wZ - %x\n", NtSystemPartitionDeviceName, status);
#endif // DBG
return;
}
// Make sure the volume name string is null-terminated.
volumeNameString.Buffer[volumeNameString.Length / sizeof(WCHAR)] = L'\0';
// Open HKLM\SYSTEM key.
status = IopOpenRegistryKeyEx(&systemHandle, NULL, &CmRegistryMachineSystemName, KEY_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IopStoreSystemPartitionInformation: couldn't open \\REGISTRY\\MACHINE\\SYSTEM - %x\n", status);
#endif // DBG
return;
}
// Now open/create the setup subkey.
ASSERT(sizeof(L"Setup") <= sizeof(nameBuffer));
nameBuffer[0] = L'S';
nameBuffer[1] = L'e';
nameBuffer[2] = L't';
nameBuffer[3] = L'u';
nameBuffer[4] = L'p';
nameBuffer[5] = L'\0';
nameString.MaximumLength = sizeof(L"Setup");
nameString.Length = sizeof(L"Setup") - sizeof(WCHAR);
nameString.Buffer = nameBuffer;
status = IopCreateRegistryKeyEx(&setupHandle, systemHandle, &nameString, KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, NULL);
NtClose(systemHandle); // Don't need the handle to the HKLM\System key anymore.
if (!NT_SUCCESS(status)) {
#if DBG
DbgPrint("IopStoreSystemPartitionInformation: couldn't open Setup subkey - %x\n", status);
#endif // DBG
return;
}
ASSERT(sizeof(L"SystemPartition") <= sizeof(nameBuffer));
nameBuffer[0] = L'S';
nameBuffer[1] = L'y';
nameBuffer[2] = L's';
nameBuffer[3] = L't';
nameBuffer[4] = L'e';
nameBuffer[5] = L'm';
nameBuffer[6] = L'P';
nameBuffer[7] = L'a';
nameBuffer[8] = L'r';
nameBuffer[9] = L't';
nameBuffer[10] = L'i';
nameBuffer[11] = L't';
nameBuffer[12] = L'i';
nameBuffer[13] = L'o';
nameBuffer[14] = L'n';
nameBuffer[15] = L'\0';
nameString.MaximumLength = sizeof(L"SystemPartition");
nameString.Length = sizeof(L"SystemPartition") - sizeof(WCHAR);
status = NtSetValueKey(setupHandle, &nameString, TITLE_INDEX_VALUE, REG_SZ, volumeNameString.Buffer, volumeNameString.Length + sizeof(WCHAR));
#if DBG
if (!NT_SUCCESS(status)) {
DbgPrint("IopStoreSystemPartitionInformation: couldn't write SystemPartition value - %x\n", status);
}
#endif // DBG
ASSERT(sizeof(L"OsLoaderPath") <= sizeof(nameBuffer));
nameBuffer[0] = L'O';
nameBuffer[1] = L's';
nameBuffer[2] = L'L';
nameBuffer[3] = L'o';
nameBuffer[4] = L'a';
nameBuffer[5] = L'd';
nameBuffer[6] = L'e';
nameBuffer[7] = L'r';
nameBuffer[8] = L'P';
nameBuffer[9] = L'a';
nameBuffer[10] = L't';
nameBuffer[11] = L'h';
nameBuffer[12] = L'\0';
nameString.MaximumLength = sizeof(L"OsLoaderPath");
nameString.Length = sizeof(L"OsLoaderPath") - sizeof(WCHAR);
// Strip off the trailing backslash from the path (unless, of course, the path is a single backslash).
if ((OsLoaderPathName->Length > sizeof(WCHAR)) && (*(PWCHAR)((PCHAR)OsLoaderPathName->Buffer + OsLoaderPathName->Length - sizeof(WCHAR)) == L'\\')) {
OsLoaderPathName->Length -= sizeof(WCHAR);
*(PWCHAR)((PCHAR)OsLoaderPathName->Buffer + OsLoaderPathName->Length) = L'\0';
}
status = NtSetValueKey(setupHandle, &nameString, TITLE_INDEX_VALUE, REG_SZ, OsLoaderPathName->Buffer, OsLoaderPathName->Length + sizeof(WCHAR));
#if DBG
if (!NT_SUCCESS(status)) {
DbgPrint("IopStoreSystemPartitionInformation: couldn't write OsLoaderPath value - %x\n", status);
}
#endif // DBG
NtClose(setupHandle);
}
USHORT IopGetDriverTagPriority(IN HANDLE ServiceHandle)
/*++
Routine Description:
This routine reads the Tag value of a driver and determine the tag's priority among its driver group.
Arguments:
ServiceHandle - specifies the handle of the driver's service key.
Return Value:
USHORT for priority.
--*/
{
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PKEY_VALUE_FULL_INFORMATION keyValueInformation1;
UNICODE_STRING groupName;
HANDLE handle;
USHORT index = (USHORT)-1;
PULONG groupOrder;
ULONG count, tag;
// Open System\CurrentControlSet\Control\GroupOrderList
PiWstrToUnicodeString(&groupName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GroupOrderList");
status = IopOpenRegistryKeyEx(&handle, NULL, &groupName, KEY_READ);
if (!NT_SUCCESS(status)) {
return index;
}
// Read service key's Group value
status = IopGetRegistryValue(ServiceHandle, L"Group", &keyValueInformation);
if (NT_SUCCESS(status)) {
// Try to read what caller wants.
if ((keyValueInformation->Type == REG_SZ) && (keyValueInformation->DataLength != 0)) {
IopRegistryDataToUnicodeString(&groupName, (PWSTR)KEY_VALUE_DATA(keyValueInformation), keyValueInformation->DataLength);
}
} else {
// If we failed to read the Group value, or no Group value...
NtClose(handle);
return index;
}
// Read service key's Tag value
status = IopGetRegistryValue(ServiceHandle, L"Tag", &keyValueInformation1);
if (NT_SUCCESS(status)) {
// Try to read what caller wants.
if ((keyValueInformation1->Type == REG_DWORD) && (keyValueInformation1->DataLength != 0)) {
tag = *(PULONG)KEY_VALUE_DATA(keyValueInformation1);
}
ExFreePool(keyValueInformation1);
} else {
// If we failed to read the Group value, or no Group value...
ExFreePool(keyValueInformation);
NtClose(handle);
return index;
}
// Read group order list value for the driver's Group
status = IopGetRegistryValue(handle, groupName.Buffer, &keyValueInformation1);
ExFreePool(keyValueInformation);
NtClose(handle);
if (NT_SUCCESS(status)) {
// Try to read what caller wants.
if ((keyValueInformation1->Type == REG_BINARY) && (keyValueInformation1->DataLength != 0)) {
groupOrder = (PULONG)KEY_VALUE_DATA(keyValueInformation1);
count = *groupOrder;
ASSERT((count + 1) * sizeof(ULONG) <= keyValueInformation1->DataLength);
groupOrder++;
for (index = 1; index <= count; index++) {
if (tag == *groupOrder) {
break;
} else {
groupOrder++;
}
}
}
ExFreePool(keyValueInformation1);
} else {
// If we failed to read the Group value, or no Group value...
return index;
}
return index;
}
VOID IopInsertDriverList(IN PLIST_ENTRY ListHead, IN PDRIVER_INFORMATION DriverInfo)
/*++
Routine Description:
This routine reads the Tag value of a driver and determine the tag's priority among its driver group.
Arguments:
ServiceHandle - specifies the handle of the driver's service key.
Return Value:
USHORT for priority.
--*/
{
PLIST_ENTRY nextEntry;
PDRIVER_INFORMATION info;
nextEntry = ListHead->Flink;
while (nextEntry != ListHead) {
info = CONTAINING_RECORD(nextEntry, DRIVER_INFORMATION, Link);
// Scan the driver info list to find the driver whose priority is
// lower than current driver's.
// (Lower TagPosition value means higher Priority)
if (info->TagPosition > DriverInfo->TagPosition) {
break;
}
nextEntry = nextEntry->Flink;
}
// Insert the Driver info to the front of the nextEntry
nextEntry = nextEntry->Blink;
InsertHeadList(nextEntry, &DriverInfo->Link);
}
VOID IopNotifySetupDevices(PDEVICE_NODE DeviceNode)
/*++
Routine Description:
This routine notifies setupdd.sys for all the enumerated devices whose service have not been setup.
This routine only gets executed on textmode setup phase.
Parameters:
DeviceNode - specifies the root of the subtree to be processed.
Return Value:
None.
--*/
{
PDEVICE_NODE deviceNode = DeviceNode->Child;
PDEVICE_OBJECT deviceObject;
HANDLE handle;
UNICODE_STRING unicodeString;
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
while (deviceNode) {
IopNotifySetupDevices(deviceNode);
if (deviceNode->ServiceName.Length == 0) {
// We only notify setupdd the device nodes which do not have service setup yet.
// It is impossible that at this point, a device has a service setup and
// setupdd has to change it.
deviceObject = deviceNode->PhysicalDeviceObject;
status = IopDeviceObjectToDeviceInstance(deviceObject, &handle, KEY_ALL_ACCESS);
if (NT_SUCCESS(status)) {
// Notify setup about the device.
IopNotifySetupDeviceArrival(deviceObject, handle, TRUE);
// Finally register the device
status = PpDeviceRegistration(
&deviceNode->InstancePath,
TRUE,
&unicodeString // registered ServiceName
);
if (NT_SUCCESS(status)) {
deviceNode->ServiceName = unicodeString;
if (IopIsDevNodeProblem(deviceNode, CM_PROB_NOT_CONFIGURED)) {
IopClearDevNodeProblem(deviceNode);
}
}
ZwClose(handle);
}
}
deviceNode = deviceNode->Sibling;
}
}
NTSTATUS IopLogErrorEvent(IN ULONG SequenceNumber,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIOStatus,
IN ULONG LengthOfInsert1,
IN PWCHAR Insert1,
IN ULONG LengthOfInsert2,
IN PWCHAR Insert2)
/*++
Routine Description:
This routine allocates an error log entry, copies the supplied data
to it, and requests that it be written to the error log file.
Arguments:
SequenceNumber - A value that is unique to an IRP over the life of the irp in
this driver. - 0 generally means an error not associated with an IRP
UniqueErrorValue - A unique long word that identifies the particular
call to this function.
FinalStatus - The final status given to the irp that was associated
with this error. If this log entry is being made during one of the retries this value will be STATUS_SUCCESS.
SpecificIOStatus - The IO status for a particular error.
LengthOfInsert1 - The length in bytes (including the terminating NULL)
of the first insertion string.
Insert1 - The first insertion string.
LengthOfInsert2 - The length in bytes (including the terminating NULL)
of the second insertion string. NOTE, there must
be a first insertion string for their to be a second insertion string.
Insert2 - The second insertion string.
Return Value:
STATUS_SUCCESS - Success
STATUS_INVALID_HANDLE - Uninitialized error log device object
STATUS_NO_DATA_DETECTED - NULL Error log entry
--*/
{
PIO_ERROR_LOG_PACKET errorLogEntry;
PUCHAR ptrToFirstInsert;
PUCHAR ptrToSecondInsert;
if (!IopErrorLogObject) {
return(STATUS_INVALID_HANDLE);
}
errorLogEntry = IoAllocateErrorLogEntry(IopErrorLogObject, (UCHAR)(sizeof(IO_ERROR_LOG_PACKET) + LengthOfInsert1 + LengthOfInsert2));
if (errorLogEntry != NULL) {
errorLogEntry->ErrorCode = SpecificIOStatus;
errorLogEntry->SequenceNumber = SequenceNumber;
errorLogEntry->MajorFunctionCode = 0;
errorLogEntry->RetryCount = 0;
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
errorLogEntry->FinalStatus = FinalStatus;
errorLogEntry->DumpDataSize = 0;
ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];
ptrToSecondInsert = ptrToFirstInsert + LengthOfInsert1;
if (LengthOfInsert1) {
errorLogEntry->NumberOfStrings = 1;
errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert - (PUCHAR)errorLogEntry);
RtlCopyMemory(ptrToFirstInsert, Insert1, LengthOfInsert1);
if (LengthOfInsert2) {
errorLogEntry->NumberOfStrings = 2;
RtlCopyMemory(ptrToSecondInsert, Insert2, LengthOfInsert2);
} //LenghtOfInsert2
} // LenghtOfInsert1
IoWriteErrorLogEntry(errorLogEntry);
return(STATUS_SUCCESS);
} // errorLogEntry != NULL
return(STATUS_NO_DATA_DETECTED);
} //IopLogErrorEvent
BOOLEAN IopWaitForBootDevicesStarted(IN VOID)
/*++
Routine Description:
This routine waits for enumeration lock to be released for ALL devices.
Arguments:
None.
Return Value:
BOOLEAN.
--*/
{
PDEVICE_NODE deviceNode;
NTSTATUS status;
KIRQL oldIrql;
// Wait on IoInvalidateDeviceRelations event to make sure all the devcies are enumerated
// before progressing to mark boot partitions.
status = KeWaitForSingleObject(&PiEnumerationLock, Executive, KernelMode, FALSE, NULL);
if (!NT_SUCCESS(status)) {
return FALSE;
}
if (PnpAsyncOk) {
// Perform top-down check to make sure all the devices with Async start and Async Query
// Device Relations are done.
deviceNode = IopRootDeviceNode;
for (; ;) {
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
if (deviceNode->Flags & DNF_ASYNC_REQUEST_PENDING) {
KeClearEvent(&PiEnumerationLock);
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
// Wait on PiEnumerationLock to be signaled before proceeding.
// At this point if a device node is marked ASYNC request pending, this
// must be an ASYNC start or enumeration which will queue an enumeration
// request and once the enumeration completes, the PiEnumerationLock will be signaled.
status = KeWaitForSingleObject(&PiEnumerationLock, Executive, KernelMode, FALSE, NULL);
if (!NT_SUCCESS(status)) {
return FALSE;
}
continue; // Make sure this device is done.
} else {
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
}
if (deviceNode->Child) {
deviceNode = deviceNode->Child;
continue;
}
if (deviceNode->Sibling) {
deviceNode = deviceNode->Sibling;
continue;
}
for (; ;) {
deviceNode = deviceNode->Parent;
// If that was the last node to check, then exit loop
if (deviceNode == IopRootDeviceNode) {
goto exit;
}
if (deviceNode->Sibling) {
deviceNode = deviceNode->Sibling;
break;
}
}
}
exit:
;
}
return TRUE;
}
BOOLEAN IopWaitForBootDevicesDeleted(IN VOID)
/*++
Routine Description:
This routine waits for IoRequestDeviceRemoval to be completed.
Arguments:
None.
Return Value:
BOOLEAN.
--*/
{
NTSTATUS status;
// Wait on device removal event to make sure all the deleted devcies are processed before moving on to process next boot driver.
status = KeWaitForSingleObject(&PiEventQueueEmpty, Executive, KernelMode, FALSE, NULL);
return NT_SUCCESS(status);
}
PDRIVER_OBJECT IopLoadBootFilterDriver(IN PUNICODE_STRING DriverName, IN ULONG GroupIndex)
/*++
Routine Description:
This initializes boot filter drivers.
Arguments:
DriverName - specifies the name of the driver to be initialized.
GroupIndex - specifies the Driver's group index (could be anything)
Return Value:
PDRIVER_OBJECT
--*/
{
PDRIVER_OBJECT driverObject = NULL;
PLIST_ENTRY nextEntry;
PDRIVER_INFORMATION driverInfo;
UNICODE_STRING completeName;
PBOOT_DRIVER_LIST_ENTRY bootDriver;
PLDR_DATA_TABLE_ENTRY driverEntry;
HANDLE keyHandle;
NTSTATUS status;
if (IopGroupTable == NULL || GroupIndex >= IopGroupIndex) {
// If we have not reached the boot driver initialization phase or the filter driver is not a boot driver.
return driverObject;
}
// Go thru every driver that we initialized.
// If it supports AddDevice entry and did not create any device object after we start it.
// We mark it as failure so text mode setup knows this driver is not needed.
nextEntry = IopGroupTable[GroupIndex].Flink;
while (nextEntry != &IopGroupTable[GroupIndex]) {
driverInfo = CONTAINING_RECORD(nextEntry, DRIVER_INFORMATION, Link);
if (driverInfo->Processed == FALSE) {
keyHandle = driverInfo->ServiceHandle;
status = IopGetDriverNameFromKeyNode(keyHandle, &completeName);
if (NT_SUCCESS(status)) {
if (RtlEqualUnicodeString(DriverName, &completeName, TRUE)) { // case-insensitive
bootDriver = driverInfo->DataTableEntry;
driverEntry = bootDriver->LdrEntry;
driverObject = IopInitializeBuiltinDriver(&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE)driverEntry->EntryPoint,
driverEntry,
FALSE);
driverInfo->DriverObject = driverObject;
driverInfo->Processed = TRUE;
// Pnp might unload the driver before we get a chance to look at this. So take an extra reference.
if (driverObject) {
ObReferenceObject(driverObject);
}
ExFreePool(completeName.Buffer);
break;
}
ExFreePool(completeName.Buffer);
}
}
nextEntry = nextEntry->Flink;
}
return driverObject;
}
#if 0
VOID IopCheckClassFiltersForBootDevice(PDEVICE_NODE DeviceNode)
/*++
Routine Description:
This routine notifies setupdd.sys for all the enumerated devices whose service have not been setup.
This routine only gets executed on textmode setup phase.
Parameters:
DeviceNode - specifies the root of the subtree to be processed.
--*/
{
PDEVICE_OBJECT deviceObject;
HANDLE handle;
UNICODE_STRING unicodeString, unicodeName;
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
if (DeviceNode->ServiceName.Length != 0) {
deviceObject = DeviceNode->PhysicalDeviceObject;
status = IopDeviceObjectToDeviceInstance(deviceObject, &handle, KEY_ALL_ACCESS);
if (NT_SUCCESS(status)) {
// Check if Class GUID is present.
// If yes, add the device instance to its Class Filters' service keys such that the class filter drivers can stay around.
// Get the class value to locate the class key for this devnode
status = IopGetRegistryValue(handle, REGSTR_VALUE_CLASSGUID, &keyValueInformation);
if (NT_SUCCESS(status) && ((keyValueInformation->Type == REG_SZ) && (keyValueInformation->DataLength != 0))) {
HANDLE controlKey, classKey = NULL;
UNICODE_STRING unicodeClassGuid;
RTL_QUERY_REGISTRY_TABLE queryTable[2];
IopRegistryDataToUnicodeString(&unicodeClassGuid, (PWSTR)KEY_VALUE_DATA(keyValueInformation), keyValueInformation->DataLength);
// Open the class key
status = IopOpenRegistryKeyEx(&controlKey, NULL, &CmRegistryMachineSystemCurrentControlSetControlClass, KEY_READ);
if (!NT_SUCCESS(status)) {
classKey = NULL;
} else {
status = IopOpenRegistryKeyEx(&classKey, controlKey, &unicodeClassGuid, KEY_READ);
NtClose(controlKey);
if (!NT_SUCCESS(status)) {
classKey = NULL;
}
}
ExFreePool(keyValueInformation);
if (classKey) {
RTL_QUERY_REGISTRY_TABLE queryTable;
RtlZeroMemory(&queryTable, sizeof(queryTable));
queryTable.QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE)IopAddDeviceInstanceForClassFilters;
queryTable.Name = REGSTR_VAL_LOWERFILTERS;
RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)classKey, &queryTable, &deviceNode->InstancePath, NULL);
queryTable.QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE)IopAddDeviceInstanceForClassFilters;
queryTable.Name = REGSTR_VAL_UPPERFILTERS;
RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)classKey, &queryTable, &deviceNode->InstancePath, NULL);
NtClose(classKey);
}
}
ZwClose(handle);
}
}
}
NTSTATUS IopAddDeviceInstanceForClassFilters(IN PWSTR ValueName,
IN ULONG ValueType,
IN PWCHAR ValueData,
IN ULONG ValueLength,
IN PUNICODE_STRING DeviceInstancePath,
IN ULONG notUse)
/*++
Routine Description:
This routine add a device instance path to the service's enum subkey.
Arguments:
ValueName - the name of the value
ValueType - the type of the value
ValueData - the data in the value (unicode string data)
ValueLength - the number of bytes in the value data
Context - a structure which contains the device node, the context passed to IopCallDriverAddDevice and the driver lists for the device node.
EntryContext - the index of the driver list the routine should append nodes to.
Return Value:
STATUS_SUCCESS if the driver was located and added to the list successfully or if there was a non-fatal error while handling the driver.
an error value indicating why the driver could not be added to the list.
--*/
{
UNICODE_STRING serviceName;
UNICODE_STRING matchingDeviceInstance;
UNICODE_STRING unicodeString;
CHAR unicodeBuffer[20];
HANDLE serviceEnumHandle;
ULONG count;
NTSTATUS status;
RtlInitUnicodeString(&serviceName, ValueData);
status = IopOpenServiceEnumKeys(&serviceName, KEY_ALL_ACCESS, NULL, &serviceEnumHandle, TRUE);
if (!NT_SUCCESS(status)) {
return STATUS_SUCCESS;
}
// Now, search through the service's existing list of device instances, to see if this instance has previously been registered.
status = PiFindDevInstMatch(serviceEnumHandle, DeviceInstancePath, &count, &matchingDeviceInstance);
if (!NT_SUCCESS(status)) {
return STATUS_SUCCESS;
}
if (!matchingDeviceInstance.Buffer) {
// Create the value entry and update NextInstance= for the madeup key
PiUlongToUnicodeString(&unicodeString, unicodeBuffer, 20, count);
status = ZwSetValueKey(serviceEnumHandle, &unicodeString, TITLE_INDEX_VALUE, REG_SZ, DeviceInstancePath->Buffer, DeviceInstancePath->Length);
count++;
PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_COUNT);
ZwSetValueKey(serviceEnumHandle, &unicodeString, TITLE_INDEX_VALUE, REG_DWORD, &count, sizeof(count));
PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_NEXT_INSTANCE);
ZwSetValueKey(serviceEnumHandle, &unicodeString, TITLE_INDEX_VALUE, REG_DWORD, &count, sizeof(count));
} else {
RtlFreeUnicodeString(&matchingDeviceInstance);
}
NtClose(serviceEnumHandle);
return STATUS_SUCCESS;
}
#endif