windows-nt/Source/XPSP1/NT/base/ntos/io/pnpmgr/pnpinit.c

3145 lines
87 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
pnpsubs.c
Abstract:
This module contains the plug-and-play initialization
subroutines for the I/O system.
Author:
Shie-Lin Tzong (shielint) 30-Jan-1995
Environment:
Kernel mode
Revision History:
--*/
#include "pnpmgrp.h"
#pragma hdrstop
#define SYSTEM_HIVE_LOW 80
#define SYSTEM_HIVE_HIGH 90
#include <inbv.h>
#include <hdlsblk.h>
#include <hdlsterm.h>
#include <initguid.h>
#include <ntddramd.h>
#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'nipP')
#endif
//
// 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;
NTSTATUS Status;
} DRIVER_INFORMATION, *PDRIVER_INFORMATION;
PTREE_ENTRY IopGroupListHead;
#define ALLOW_WORLD_READ_OF_ENUM 1
PTREE_ENTRY
PipCreateEntry(
IN PUNICODE_STRING GroupName
);
VOID
PipFreeGroupTree(
IN PTREE_ENTRY TreeEntry
);
USHORT
PipGetDriverTagPriority(
IN HANDLE Servicehandle
);
NTSTATUS
PipPnPDriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
PipAddDevicesToBootDriver(
IN PDRIVER_OBJECT DriverObject
);
BOOLEAN
PipAddDevicesToBootDriverWorker(
IN HANDLE DeviceInstanceHandle,
IN PUNICODE_STRING DeviceInstancePath,
IN OUT PVOID Context
);
BOOLEAN
PipCheckDependencies(
IN HANDLE KeyHandle
);
INTERFACE_TYPE
PipDetermineDefaultInterfaceType(
VOID
);
VOID
PipInsertDriverList(
IN PLIST_ENTRY ListHead,
IN PDRIVER_INFORMATION DriverInfo
);
PTREE_ENTRY
PipLookupGroupName(
IN PUNICODE_STRING GroupName,
IN BOOLEAN Insert
);
VOID
PipNotifySetupDevices(
PDEVICE_NODE DeviceNode
);
BOOLEAN
PipWaitForBootDevicesDeleted(
IN VOID
);
BOOLEAN
PipWaitForBootDevicesStarted(
IN VOID
);
BOOLEAN
PiInitPhase0(
VOID
);
NTSTATUS
RawInitialize(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
PiInitCacheGroupInformation(
VOID
);
VOID
PiInitReleaseCachedGroupInformation(
VOID
);
NTSTATUS
IopStartRamdisk(
PLOADER_PARAMETER_BLOCK LoaderBlock
);
//
// Group order table
//
ULONG IopGroupIndex;
PLIST_ENTRY IopGroupTable;
//
// Group order cache list.
//
UNICODE_STRING *PiInitGroupOrderTable = NULL;
USHORT PiInitGroupOrderTableCount = 0;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, IopInitializeBootDrivers)
#pragma alloc_text(INIT, IopInitializePlugPlayServices)
#pragma alloc_text(INIT, IopInitializeSystemDrivers)
#pragma alloc_text(INIT, PipAddDevicesToBootDriver)
#pragma alloc_text(INIT, PipAddDevicesToBootDriverWorker)
#pragma alloc_text(INIT, PipCheckDependencies)
#pragma alloc_text(INIT, PipCreateEntry)
#pragma alloc_text(INIT, PipDetermineDefaultInterfaceType)
#pragma alloc_text(INIT, PipFreeGroupTree)
#pragma alloc_text(INIT, PipGetDriverTagPriority)
#pragma alloc_text(INIT, PipInsertDriverList)
#pragma alloc_text(INIT, PipLoadBootFilterDriver)
#pragma alloc_text(INIT, PipLookupGroupName)
#pragma alloc_text(INIT, PipNotifySetupDevices)
#pragma alloc_text(INIT, PipPnPDriverEntry)
#pragma alloc_text(INIT, PipWaitForBootDevicesDeleted)
#pragma alloc_text(INIT, PipWaitForBootDevicesStarted)
#pragma alloc_text(INIT, PiInitPhase0)
#pragma alloc_text(INIT, PpInitSystem)
#pragma alloc_text(INIT, PiInitCacheGroupInformation)
#pragma alloc_text(INIT, PiInitReleaseCachedGroupInformation)
#pragma alloc_text(INIT, PpInitGetGroupOrderIndex)
#pragma alloc_text(INIT, IopStartRamdisk)
#endif
NTSTATUS
IopInitializePlugPlayServices(
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN ULONG Phase
)
/*++
Routine Description:
This routine initializes kernel mode Plug and Play services.
Arguments:
LoaderBlock - supplies a pointer to the LoaderBlock passed in from the
OS Loader.
Returns:
NTSTATUS code for sucess or reason of failure.
--*/
{
NTSTATUS status;
HANDLE hTreeHandle, parentHandle, handle, hCurrentControlSet = NULL;
UNICODE_STRING unicodeName;
PKEY_VALUE_FULL_INFORMATION detectionInfo;
PDEVICE_OBJECT deviceObject;
ULONG disposition;
INTERFACE_TYPE interface;
if (Phase == 0) {
PnPInitialized = FALSE;
//
// Register with CM so we get called when the system hive becomes too
// large.
//
PpSystemHiveLimits.Low = SYSTEM_HIVE_LOW;
PpSystemHiveLimits.High = SYSTEM_HIVE_HIGH;
CmRegisterSystemHiveLimitCallback(
SYSTEM_HIVE_LOW,
SYSTEM_HIVE_HIGH,
(PVOID)&PpSystemHiveLimits,
(PCM_HYSTERESIS_CALLBACK)PpSystemHiveLimitCallback
);
PpSystemHiveTooLarge = FALSE;
//
// Initialize the blocked driver database.
//
PpInitializeBootDDB(LoaderBlock);
//
// Build up the group order cache list. This is the MultiSz string that
// tells us what order to start legacy drivers in. Drivers belonging to
// an earlier group get started first (within the group Tag ordering is
// used)
//
status = PiInitCacheGroupInformation();
if (!NT_SUCCESS(status)) {
return FALSE;
}
//
// Initialize the registry access semaphore.
//
KeInitializeSemaphore( &PpRegistrySemaphore, 1, 1 );
//
// Initialize the Legacy Bus information table.
//
for (interface = Internal; interface < MaximumInterfaceType; interface++) {
InitializeListHead(&IopLegacyBusInformationTable[interface]);
}
//
// Initialize the resource map
//
IopInitializeResourceMap (LoaderBlock);
//
// Allocate two one-page scratch buffers to be used by our
// initialization code. This avoids constant pool allocations.
//
IopPnpScratchBuffer1 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE);
if (!IopPnpScratchBuffer1) {
return STATUS_INSUFFICIENT_RESOURCES;
}
IopPnpScratchBuffer2 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE);
if (!IopPnpScratchBuffer2) {
ExFreePool(IopPnpScratchBuffer1);
return STATUS_INSUFFICIENT_RESOURCES;
}
IopInitReservedResourceList = NULL;
IopAllocateBootResourcesRoutine = IopReportBootResources;
//
// Determine the PnpDefaultInterfaceType. For root enumerated devices if the Interface
// type of their resource list or resource requirements list are undefined. We will use
// the default type instead.
//
PnpDefaultInterfaceType = PipDetermineDefaultInterfaceType();
//
// Initialize root arbiters
//
status = IopPortInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopMemInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopDmaInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopIrqInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopBusNumberInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopOpenRegistryKeyEx( &hCurrentControlSet,
NULL,
&CmRegistryMachineSystemCurrentControlSet,
KEY_ALL_ACCESS
);
if (!NT_SUCCESS(status)) {
hCurrentControlSet = NULL;
goto init_Exit0;
}
//
// Open HKLM\System\CurrentControlSet\Control\Pnp
//
PiWstrToUnicodeString(&unicodeName, REGSTR_PATH_CONTROL_PNP);
status = IopCreateRegistryKeyEx( &handle,
hCurrentControlSet,
&unicodeName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL
);
if (NT_SUCCESS(status)) {
//
// HACK: Since it was too late to make the change in XP, we target
// this behaviour at machines with MATROX G100. The inf sets this
// flag in the registry.
//
status = IopGetRegistryValue(handle,
REGSTR_VAL_WIN2000STARTORDER,
&detectionInfo
);
if (NT_SUCCESS(status)) {
if (detectionInfo->Type == REG_DWORD && detectionInfo->DataLength == sizeof(ULONG)) {
PpCallerInitializesRequestTable = (BOOLEAN) *(KEY_VALUE_DATA(detectionInfo));
}
ExFreePool(detectionInfo);
}
NtClose(handle);
}
//
// Next open/create System\CurrentControlSet\Enum\Root key.
//
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ENUM);
status = IopCreateRegistryKeyEx( &handle,
hCurrentControlSet,
&unicodeName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
&disposition
);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
if (disposition == REG_CREATED_NEW_KEY) {
SECURITY_DESCRIPTOR newSD;
PACL newDacl;
ULONG sizeDacl;
status = RtlCreateSecurityDescriptor( &newSD,
SECURITY_DESCRIPTOR_REVISION );
ASSERT( NT_SUCCESS( status ) );
//
// calculate the size of the new DACL
//
sizeDacl = sizeof(ACL);
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeLocalSystemSid) - sizeof(ULONG);
#if ALLOW_WORLD_READ_OF_ENUM
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeWorldSid) - sizeof(ULONG);
#endif
//
// create and initialize the new DACL
//
newDacl = ExAllocatePool(PagedPool, sizeDacl);
if (newDacl != NULL) {
status = RtlCreateAcl(newDacl, sizeDacl, ACL_REVISION);
ASSERT( NT_SUCCESS( status ) );
//
// Add just the local system full control ace to this new DACL
//
status = RtlAddAccessAllowedAceEx( newDacl,
ACL_REVISION,
CONTAINER_INHERIT_ACE,
KEY_ALL_ACCESS,
SeLocalSystemSid
);
ASSERT( NT_SUCCESS( status ) );
#if ALLOW_WORLD_READ_OF_ENUM
//
// Add just the local system full control ace to this new DACL
//
status = RtlAddAccessAllowedAceEx( newDacl,
ACL_REVISION,
CONTAINER_INHERIT_ACE,
KEY_READ,
SeWorldSid
);
ASSERT( NT_SUCCESS( status ) );
#endif
//
// Set the new DACL in the absolute security descriptor
//
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,
TRUE,
newDacl,
FALSE
);
ASSERT( NT_SUCCESS( status ) );
//
// validate the new security descriptor
//
status = RtlValidSecurityDescriptor(&newSD);
ASSERT( NT_SUCCESS( status ) );
status = ZwSetSecurityObject( handle,
DACL_SECURITY_INFORMATION,
&newSD
);
if (!NT_SUCCESS(status)) {
IopDbgPrint(( IOP_ERROR_LEVEL,
"IopInitializePlugPlayServices: ZwSetSecurityObject on Enum key failed, status = %8.8X\n", status));
}
ExFreePool(newDacl);
} else {
IopDbgPrint(( IOP_ERROR_LEVEL,
"IopInitializePlugPlayServices: ExAllocatePool failed allocating DACL for Enum key\n"));
}
}
parentHandle = handle;
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ROOTENUM);
status = IopCreateRegistryKeyEx( &handle,
parentHandle,
&unicodeName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL
);
NtClose(parentHandle);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
NtClose(handle);
//
// Create the registry entry for the root of the hardware tree (HTREE\ROOT\0).
//
status = IopOpenRegistryKeyEx( &handle,
NULL,
&CmRegistryMachineSystemCurrentControlSetEnumName,
KEY_ALL_ACCESS
);
if (NT_SUCCESS(status)) {
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_ROOT_DEVNODE);
status = IopCreateRegistryKeyEx( &hTreeHandle,
handle,
&unicodeName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL
);
NtClose(handle);
if (NT_SUCCESS(status)) {
NtClose(hTreeHandle);
}
}
//
// Before creating device node tree, we need to initialize the device
// tree lock.
//
InitializeListHead(&IopPendingEjects);
InitializeListHead(&IopPendingSurpriseRemovals);
InitializeListHead(&IopPnpEnumerationRequestList);
ExInitializeResourceLite(&IopDeviceTreeLock);
ExInitializeResourceLite(&IopSurpriseRemoveListLock);
PiInitializeEngineLock();
KeInitializeEvent(&PiEventQueueEmpty, NotificationEvent, TRUE );
KeInitializeEvent(&PiEnumerationLock, NotificationEvent, TRUE );
KeInitializeSpinLock(&IopPnPSpinLock);
//
// Initialize the hardware profile/docking support.
//
PpProfileInit();
//
// Initialize warm docking variables.
//
IopWarmEjectPdo = NULL;
KeInitializeEvent(&IopWarmEjectLock, SynchronizationEvent, TRUE );
//
// Create a PnP manager's driver object to own all the detected PDOs.
//
PiWstrToUnicodeString(&unicodeName, PNPMGR_STR_PNP_DRIVER);
status = IoCreateDriver (&unicodeName, PipPnPDriverEntry);
if (NT_SUCCESS(status)) {
//
// Create empty device node tree, i.e., only contains only root device node
// (No need to initialize Parent, Child and Sibling links.)
status = IoCreateDevice( IoPnpDriverObject,
sizeof(IOPNP_DEVICE_EXTENSION),
NULL,
FILE_DEVICE_CONTROLLER,
0,
FALSE,
&deviceObject );
if (NT_SUCCESS(status)) {
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
status = PipAllocateDeviceNode(
deviceObject,
&IopRootDeviceNode);
if (!IopRootDeviceNode) {
IoDeleteDevice(deviceObject);
IoDeleteDriver(IoPnpDriverObject);
} else {
IopRootDeviceNode->Flags |= DNF_MADEUP |
DNF_ENUMERATED |
DNF_IDS_QUERIED |
DNF_NO_RESOURCE_REQUIRED;
IopRootDeviceNode->InstancePath.Buffer = ExAllocatePool( PagedPool,
sizeof(REGSTR_VAL_ROOT_DEVNODE));
if (IopRootDeviceNode->InstancePath.Buffer != NULL) {
IopRootDeviceNode->InstancePath.MaximumLength = sizeof(REGSTR_VAL_ROOT_DEVNODE);
IopRootDeviceNode->InstancePath.Length = sizeof(REGSTR_VAL_ROOT_DEVNODE) - sizeof(WCHAR);
RtlCopyMemory( IopRootDeviceNode->InstancePath.Buffer,
REGSTR_VAL_ROOT_DEVNODE,
sizeof(REGSTR_VAL_ROOT_DEVNODE));
} else {
ASSERT(IopRootDeviceNode->InstancePath.Buffer);
status = STATUS_INSUFFICIENT_RESOURCES;
goto init_Exit0;
}
status = IopMapDeviceObjectToDeviceInstance(
IopRootDeviceNode->PhysicalDeviceObject,
&IopRootDeviceNode->InstancePath);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
PipSetDevNodeState(IopRootDeviceNode, DeviceNodeStarted, NULL);
}
}
}
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
//
// Initialize the kernel mode pnp notification system
//
status = PpInitializeNotification();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
IopInitializePlugPlayNotification();
//
// Initialize table for holding bus type guid list.
//
status = PpBusTypeGuidInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
//
// Enumerate the ROOT bus synchronously.
//
PipRequestDeviceAction( IopRootDeviceNode->PhysicalDeviceObject,
ReenumerateRootDevices,
FALSE,
0,
NULL,
NULL);
init_Exit0:
//
// If we managed to open the Current Control Set close it
//
if (hCurrentControlSet) {
NtClose(hCurrentControlSet);
}
if (!NT_SUCCESS(status)) {
ExFreePool(IopPnpScratchBuffer1);
ExFreePool(IopPnpScratchBuffer2);
}
} else if (Phase == 1) {
BOOLEAN legacySerialPortMappingOnly = FALSE;
//
// Next open/create System\CurrentControlSet\Enum\Root key.
//
status = IopOpenRegistryKeyEx( &hCurrentControlSet,
NULL,
&CmRegistryMachineSystemCurrentControlSet,
KEY_ALL_ACCESS
);
if (!NT_SUCCESS(status)) {
hCurrentControlSet = NULL;
goto init_Exit1;
}
//
// Open HKLM\System\CurrentControlSet\Control\Pnp
//
PiWstrToUnicodeString(&unicodeName, REGSTR_PATH_CONTROL_PNP);
status = IopCreateRegistryKeyEx( &handle,
hCurrentControlSet,
&unicodeName,
KEY_ALL_ACCESS,
REG_OPTION_NON_VOLATILE,
NULL
);
if (!NT_SUCCESS(status)) {
goto init_Exit1;
}
//
// Check the "DisableFirmwareMapper" value entry to see whether we
// should skip mapping ntdetect/firmware reported devices (except for
// COM ports, which we always map).
//
status = IopGetRegistryValue(handle,
REGSTR_VALUE_DISABLE_FIRMWARE_MAPPER,
&detectionInfo
);
if (NT_SUCCESS(status)) {
if (detectionInfo->Type == REG_DWORD && detectionInfo->DataLength == sizeof(ULONG)) {
legacySerialPortMappingOnly = (BOOLEAN) *(KEY_VALUE_DATA(detectionInfo));
}
ExFreePool(detectionInfo);
}
NtClose(handle);
//
// Collect the necessary firmware tree information.
//
MapperProcessFirmwareTree(legacySerialPortMappingOnly);
//
// Map this into the root enumerator tree
//
MapperConstructRootEnumTree(legacySerialPortMappingOnly);
#if i386
if (!legacySerialPortMappingOnly) {
//
// Now do the PnP BIOS enumerated devnodes.
//
extern NTSTATUS PnPBiosMapper(VOID);
status = PnPBiosMapper();
//
// If the previous call succeeds, we have a PNPBios, turn any newly
// created ntdetect COM ports into phantoms
//
if (NT_SUCCESS(status)) {
MapperPhantomizeDetectedComPorts();
}
}
EisaBuildEisaDeviceNode();
#endif
//
// We're done with the firmware mapper device list.
//
MapperFreeList();
//
// Enumerate the ROOT bus synchronously.
//
PipRequestDeviceAction( IopRootDeviceNode->PhysicalDeviceObject,
ReenumerateRootDevices,
FALSE,
0,
NULL,
NULL);
init_Exit1:
//
// If we managed to open the Current Control Set close it
//
if(hCurrentControlSet) {
NtClose(hCurrentControlSet);
}
//
// Free our scratch buffers and exit.
//
ExFreePool(IopPnpScratchBuffer1);
ExFreePool(IopPnpScratchBuffer2);
status = STATUS_SUCCESS;
} else {
status = STATUS_INVALID_PARAMETER_1;
}
return status;
}
NTSTATUS
PipPnPDriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the callback function when we call IoCreateDriver to create a
PnP Driver Object. In this function, we need to remember the DriverObject.
Arguments:
DriverObject - Pointer to the driver object created by the system.
RegistryPath - is NULL.
Return Value:
STATUS_SUCCESS
--*/
{
UNREFERENCED_PARAMETER( RegistryPath );
//
// File the pointer to our driver object away
//
IoPnpDriverObject = DriverObject;
//
// Fill in the driver object
//
DriverObject->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)IopPnPAddDevice;
DriverObject->MajorFunction[ IRP_MJ_PNP ] = IopPnPDispatch;
DriverObject->MajorFunction[ IRP_MJ_POWER ] = IopPowerDispatch;
DriverObject->MajorFunction[ IRP_MJ_SYSTEM_CONTROL ] = IopSystemControlDispatch;
return STATUS_SUCCESS;
}
INTERFACE_TYPE
PipDetermineDefaultInterfaceType (
VOID
)
/*++
Routine Description:
This routine checks if detection flag is set to enable driver detection.
The detection will be enabled if there is no PCI bus in the machine and only
on ALPHA machine.
Parameters:
None.
Return Value:
BOOLEAN value to indicate if detection is enabled.
--*/
{
NTSTATUS status;
PVOID p;
PHAL_BUS_INFORMATION pBusInfo;
ULONG length, i;
INTERFACE_TYPE interfaceType = Isa;
pBusInfo = IopPnpScratchBuffer1;
length = PNP_LARGE_SCRATCH_BUFFER_SIZE;
status = HalQuerySystemInformation (
HalInstalledBusInformation,
length,
pBusInfo,
&length
);
if (!NT_SUCCESS(status)) {
return interfaceType;
}
//
// Check installed bus information to make sure there is no existing Pnp Isa
// bus extender.
//
p = pBusInfo;
for (i = 0; i < length / sizeof(HAL_BUS_INFORMATION); i++, pBusInfo++) {
if (pBusInfo->BusType == Isa || pBusInfo->BusType == Eisa) {
interfaceType = Isa;
break;
} else if (pBusInfo->BusType == MicroChannel) {
interfaceType = MicroChannel;
}
}
return interfaceType;
}
BOOLEAN
PipCheckDependencies(
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 = PipLookupGroupName( &groupName, FALSE );
if (treeEntry) {
if (!treeEntry->DriversLoaded) {
load = FALSE;
break;
}
}
length -= groupName.MaximumLength;
source = (PWSTR) ((PUCHAR) source + groupName.MaximumLength);
}
ExFreePool( keyValueInformation );
return load;
}
PTREE_ENTRY
PipCreateEntry(
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 );
//
// We return NULL here and what this really implies that
// we won't be able to determine if drivers for this group
// was loaded.
//
if (!treeEntry) {
return NULL;
}
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;
}
VOID
PipFreeGroupTree(
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) {
PipFreeGroupTree( TreeEntry->Left );
}
if (TreeEntry->Sibling) {
PipFreeGroupTree( TreeEntry->Sibling );
}
if (TreeEntry->Right) {
PipFreeGroupTree( TreeEntry->Right );
}
//
// All of the children and siblings for this node have been freed, so
// now free this node as well.
//
ExFreePool( TreeEntry );
}
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;
PBOOT_DRIVER_LIST_ENTRY bootDriver;
HANDLE keyHandle;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PDRIVER_OBJECT driverObject;
USHORT i, j;
PKLDR_DATA_TABLE_ENTRY driverEntry;
PKLDR_DATA_TABLE_ENTRY dllEntry;
UNICODE_STRING groupName;
PTREE_ENTRY treeEntry;
PDRIVER_INFORMATION driverInfo;
BOOLEAN textModeSetup = FALSE;
BOOLEAN bootReinitDriversFound;
ULONG remotebootcount = 0;
UNREFERENCED_PARAMETER( PreviousDriver );
//
// Initialize the built-in RAW file system driver.
//
PiWstrToUnicodeString( &rawFsName, L"\\FileSystem\\RAW" );
PiWstrToUnicodeString( &completeName, L"" );
IopInitializeBuiltinDriver(&rawFsName,
&completeName,
RawInitialize,
NULL,
FALSE,
&driverObject);
if (!driverObject) {
#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 = PpInitGetGroupOrderIndex(NULL);
if (IopGroupIndex == NO_MORE_GROUP) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_FIND_GROUPS_FAILED, NULL);
return FALSE;
}
IopGroupTable = (PLIST_ENTRY) ExAllocatePool(PagedPool, IopGroupIndex * sizeof (LIST_ENTRY));
if (IopGroupTable == NULL) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_OUT_OF_MEMORY, 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, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (dllEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL) {
(VOID)MmCallDllInitialize(dllEntry, &LoaderBlock->LoadOrderListHead);
}
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 = PpInitGetGroupOrderIndex(keyHandle);
if (j == SETUP_RESERVED_GROUP) {
textModeSetup = TRUE;
//
// Special handling for setupdd.sys
//
status = IopGetDriverNameFromKeyNode( keyHandle,
&completeName );
if (NT_SUCCESS(status)) {
driverInfo->Status = IopInitializeBuiltinDriver(
&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE) driverEntry->EntryPoint,
driverEntry,
FALSE,
&driverObject);
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.
//
PipNotifySetupDevices(IopRootDeviceNode);
} else {
ExFreePool(IopGroupTable);
return FALSE;
}
}
} else {
driverInfo->TagPosition = PipGetDriverTagPriority(keyHandle);
PipInsertDriverList(&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,
REGSTR_VALUE_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 = PipLookupGroupName( &groupName, TRUE );
} else {
treeEntry = (PTREE_ENTRY) NULL;
}
ExFreePool( keyValueInformation );
} else {
treeEntry = (PTREE_ENTRY) NULL;
}
driverObject = NULL;
if (PipCheckDependencies( 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 && !driverInfo->Failed) {
driverInfo->Status = IopInitializeBuiltinDriver(
&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE) driverEntry->EntryPoint,
driverEntry,
FALSE,
&driverObject);
//
// Pnp might unload the driver before we get a chance to look at this. So take an extra
// reference.
//
if (driverObject) {
ObReferenceObject(driverObject);
//
// 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 (!IopIsLegacyDriver(driverObject)) {
if (driverObject->DeviceObject == NULL &&
driverObject->DriverExtension->ServiceKeyName.Buffer &&
!IopIsAnyDeviceInstanceEnabled(&driverObject->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 (driverObject) {
if (treeEntry) {
treeEntry->DriversLoaded++;
}
driverInfo->DriverObject = driverObject;
} else {
driverInfo->Failed = TRUE;
}
ExFreePool( completeName.Buffer );
}
if (!driverInfo->Failed) {
PipAddDevicesToBootDriver(driverObject);
//
// Scan the hardware tree looking for devices which need
// resources or starting.
//
PipRequestDeviceAction( NULL,
ReenumerateBootDevices,
FALSE,
0,
NULL,
NULL);
}
//
// 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 (!PipWaitForBootDevicesDeleted()) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_WAIT_BOOT_DEVICES_DELETE_FAILED, NULL);
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) {
//
// ISSUE - 2000/08/23 - SantoshJ - There are problems with Async ops, disable for now.
//
// PnpAsyncOk = TRUE;
}
//
// Reserve BOOT configs on Internal bus 0.
//
IopAllocateLegacyBootResources(Internal, 0);
IopAllocateBootResourcesRoutine = 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) {
//
// try a hack since TCPIP may not be initialized. (There is no
// guarantee that if a device is initialized that the protocols are
// finished binding.) So if the call fails, we just sleep for a bit
// and try again until it works or we fall out of this loop.
//
remotebootcount = 0;
status = IopStartTcpIpForRemoteBoot(LoaderBlock);
while ( status == STATUS_DEVICE_DOES_NOT_EXIST && remotebootcount < 20) {
LARGE_INTEGER Delay;
//
// sleep for a second and try again.
//
Delay.LowPart = 0xff676980 ;
Delay.HighPart = 0xffffffff ;
NtDelayExecution( FALSE, &Delay );
remotebootcount += 1;
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;
PipRequestDeviceAction(NULL, AssignResources, FALSE, 0, NULL, NULL);
//
// If start irps are handled asynchronously, we need to make sure all the boot devices
// started before continue.
//
if (!PipWaitForBootDevicesStarted()) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_WAIT_BOOT_DEVICES_START_FAILED, NULL);
return FALSE;
}
bootReinitDriversFound = IopCallBootDriverReinitializationRoutines();
//
// 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 && !PipWaitForBootDevicesStarted()) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_WAIT_BOOT_DEVICES_REINIT_FAILED, NULL);
return FALSE;
}
//
// Link NT device names to ARC names now that all of the boot drivers
// have intialized.
//
IopCreateArcNames( LoaderBlock );
//
// If we're booting from a RAM disk, initialize it now.
//
if ( _memicmp( LoaderBlock->ArcBootDeviceName, "ramdisk(0)", 10 ) == 0 ) {
status = IopStartRamdisk(LoaderBlock);
// IopStartRamdisk will bugcheck on any failure.
ASSERT( NT_SUCCESS(status) );
if (!PipWaitForBootDevicesStarted()) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_WAIT_BOOT_DEVICES_START_FAILED, NULL);
return FALSE;
}
}
//
// 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 )) {
HeadlessKernelAddLogEntry(HEADLESS_LOG_MARK_BOOT_PARTITION_FAILED, NULL);
return FALSE;
}
PnPBootDriversInitialized = TRUE;
//
// Go thru every driver that we initialized. If it supports AddDevice yet
// did not create any device objects after we started it, we should unload
// it (this is the counterpart to the code in pnpenum that unloads
// unneccessary filters *after* the paging stack is online).
//
// We also mark it as failure so text mode setup knows this driver is not
// actually needed.
//
for (i = 0; i < IopGroupIndex; i++) {
while (IsListEmpty(&IopGroupTable[i]) == FALSE) {
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;
}
NTSTATUS
PipAddDevicesToBootDriver(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This functions is used by Pnp manager to inform a boot device driver of
all the devices it can possibly control. This routine is for boot
drivers only.
Parameters:
DriverObject - Supplies a driver object to receive its boot devices.
Return Value:
NTSTATUS code.
--*/
{
NTSTATUS status;
//
// For each device instance in the driver's service/enum key, we will
// invoke the driver's AddDevice routine and perform enumeration on
// the device.
// Note, we don't acquire registry lock before calling IopApplyFunction
// routine. We know this code is for boot driver initialization. No
// one else would access the registry Enum key at this time and most
// important we need the registry lock in other down level routines.
//
status = PipApplyFunctionToServiceInstances(
NULL,
&DriverObject->DriverExtension->ServiceKeyName,
KEY_ALL_ACCESS,
TRUE,
PipAddDevicesToBootDriverWorker,
DriverObject,
NULL
);
return status;
}
BOOLEAN
PipAddDevicesToBootDriverWorker(
IN HANDLE DeviceInstanceHandle,
IN PUNICODE_STRING DeviceInstancePath,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is a callback function for PipApplyFunctionToServiceInstances.
It is called for each device instance key referenced by a service instance
value under the specified service's volatile Enum subkey. The purpose of this
routine is to invoke the AddDevice() entry of a boot driver with the device
object.
Note this routine is also used for the devices controlled by a legacy driver.
If the specified device instance is controlled by a legacy driver this routine
sets the device node flags.
Arguments:
DeviceInstanceHandle - Supplies a handle to the registry path (relative to
HKLM\CCS\System\Enum) to this device instance.
DeviceInstancePath - Supplies the registry path (relative to HKLM\CCS\System\Enum)
to this device instance.
Context - Supplies a pointer to a DRIVER_OBJECT structure.
Return Value:
TRUE to continue the enumeration.
FALSE to abort it.
--*/
{
// PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT)Context;
PDEVICE_OBJECT physicalDevice;
PDEVICE_NODE deviceNode;
ADD_CONTEXT addContext;
UNREFERENCED_PARAMETER( Context );
//
// Reference the physical device object associated with the device instance.
//
physicalDevice = IopDeviceObjectFromDeviceInstance(DeviceInstancePath);
if (!physicalDevice) {
return TRUE;
}
PipRequestDeviceAction( physicalDevice, AddBootDevices, FALSE, 0, NULL, NULL );
ObDereferenceObject(physicalDevice);
return TRUE;
}
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, driverEntryStatus;
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;
KEVENT completionEvent;
// PpReleaseBootDDB();
KeInitializeEvent( &completionEvent, NotificationEvent, FALSE );
status = PipRequestDeviceAction( IopRootDeviceNode->PhysicalDeviceObject,
StartSystemDevices,
FALSE,
0,
&completionEvent,
NULL);
if (NT_SUCCESS(status)) {
status = KeWaitForSingleObject( &completionEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
//
// 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.
//
driverList = CmGetSystemDriverList();
if (driverList != NULL) {
//
// 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);
ZwClose(*driverList);
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) {
ZwClose(*driverList);
continue;
}
}
//
// The driver is not loaded yet. Load it ...
//
status = IopGetRegistryValue( *driverList,
REGSTR_VALUE_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 = PipLookupGroupName( &groupName, TRUE );
} else {
treeEntry = (PTREE_ENTRY) NULL;
}
ExFreePool( keyValueInformation );
} else {
treeEntry = (PTREE_ENTRY) NULL;
}
if (PipCheckDependencies( *driverList )) {
if (NT_SUCCESS( IopLoadDriver( *driverList, TRUE, FALSE, &driverEntryStatus ) )) {
if (treeEntry) {
treeEntry->DriversLoaded++;
}
}
} else {
ZwClose(*driverList);
}
//
// 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 );
}
PipRequestDeviceAction( IopRootDeviceNode->PhysicalDeviceObject,
StartSystemDevices,
FALSE,
0,
NULL,
NULL);
//
// Mark pnp has completed the driver loading for both system and
// autoload drivers.
//
PnPInitialized = TRUE;
//
// We don't need the group order list anymore. Release the cached data
// associated with it.
//
PiInitReleaseCachedGroupInformation();
//
// Release the Boot Driver Database information.
//
PpReleaseBootDDB();
//
// Free the memory allocated to contain the group dependency list.
//
if (IopGroupListHead) {
PipFreeGroupTree( IopGroupListHead );
}
return TRUE;
}
PTREE_ENTRY
PipLookupGroupName(
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 = PipCreateEntry( 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 = PipCreateEntry( 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 = PipCreateEntry( 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 = PipCreateEntry( GroupName );
return previousEntry->Sibling;
}
} else {
return treeEntry;
}
}
}
}
USHORT
PipGetDriverTagPriority (
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,
REGSTR_VALUE_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);
} else {
status = STATUS_UNSUCCESSFUL;
}
ExFreePool(keyValueInformation1);
}
if (!NT_SUCCESS(status)) {
//
// 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
PipInsertDriverList (
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
PipNotifySetupDevices (
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;
while (deviceNode) {
PipNotifySetupDevices(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 (PipIsDevNodeProblem(deviceNode, CM_PROB_NOT_CONFIGURED)) {
PipClearDevNodeProblem(deviceNode);
}
}
ZwClose(handle);
}
}
deviceNode = deviceNode->Sibling;
}
}
BOOLEAN
PipWaitForBootDevicesStarted (
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) {
#if 0 // BUGBUG
//
// 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 (PipIsRequestPending(deviceNode)) {
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:
;
#endif
}
return TRUE;
}
BOOLEAN
PipWaitForBootDevicesDeleted (
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 (BOOLEAN)NT_SUCCESS(status);
}
NTSTATUS
PipLoadBootFilterDriver (
IN PUNICODE_STRING DriverName,
IN ULONG GroupIndex,
OUT PDRIVER_OBJECT *LoadedFilter
)
/*++
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;
PLIST_ENTRY nextEntry;
PDRIVER_INFORMATION driverInfo;
UNICODE_STRING completeName;
PBOOT_DRIVER_LIST_ENTRY bootDriver;
PKLDR_DATA_TABLE_ENTRY driverEntry;
HANDLE keyHandle;
NTSTATUS status, retStatus;
retStatus = STATUS_UNSUCCESSFUL;
*LoadedFilter = NULL;
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 retStatus;
}
//
// 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);
keyHandle = driverInfo->ServiceHandle;
status = IopGetDriverNameFromKeyNode(
keyHandle,
&completeName);
if (NT_SUCCESS(status)) {
if (RtlEqualUnicodeString(DriverName,
&completeName,
TRUE)) { // case-insensitive
if (driverInfo->Processed == FALSE) {
bootDriver = driverInfo->DataTableEntry;
driverEntry = bootDriver->LdrEntry;
driverInfo->Status = IopInitializeBuiltinDriver(
&completeName,
&bootDriver->RegistryPath,
(PDRIVER_INITIALIZE) driverEntry->EntryPoint,
driverEntry,
TRUE,
&driverObject);
retStatus = driverInfo->Status;
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);
*LoadedFilter = driverObject;
} else {
driverInfo->Failed = TRUE;
}
} else {
retStatus = driverInfo->Status;
}
ExFreePool(completeName.Buffer);
break;
}
ExFreePool(completeName.Buffer);
}
nextEntry = nextEntry->Flink;
}
return retStatus;
}
VOID
IopMarkHalDeviceNode(
)
{
PDEVICE_NODE deviceNode;
deviceNode = IopRootDeviceNode->Child;
while (deviceNode) {
if ((deviceNode->State == DeviceNodeStarted ||
deviceNode->State == DeviceNodeStartPostWork ) &&
!(deviceNode->Flags & DNF_LEGACY_DRIVER)) {
IopInitHalDeviceNode = deviceNode;
deviceNode->Flags |= DNF_HAL_NODE;
break;
}
deviceNode = deviceNode->Sibling;
}
}
NTSTATUS
IopPnpDriverStarted(
IN PDRIVER_OBJECT DriverObject,
IN HANDLE KeyHandle,
IN PUNICODE_STRING ServiceName
)
{
NTSTATUS status = STATUS_SUCCESS;
if (DriverObject->DeviceObject == NULL && ServiceName->Buffer &&
!IopIsAnyDeviceInstanceEnabled(ServiceName, NULL, FALSE) &&
!(DriverObject->Flags & DRVO_REINIT_REGISTERED)) {
IopDriverLoadingFailed(KeyHandle, NULL);
status = STATUS_PLUGPLAY_NO_DEVICE;
} 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 0
if (PnPInitialized) {
status = PipStartDriverDevices(DriverObject);
}
#endif
}
return status;
}
NTSTATUS
PiInitCacheGroupInformation(
VOID
)
/*++
Routine Description:
This routine caches the service group order list. We only need this list
while we are processing boot start and system start legacy drivers.
Parameters:
None.
Return Value:
NTSTATUS.
--*/
{
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
UNICODE_STRING *groupTable, group;
NTSTATUS status;
HANDLE handle;
ULONG count;
//
// Open System\CurrentControlSet\Control\ServiceOrderList
//
PiWstrToUnicodeString(
&group,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ServiceGroupOrder"
);
status = IopOpenRegistryKeyEx(
&handle,
NULL,
&group,
KEY_READ
);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Read and build a unicode string array containing all the group names.
//
status = IopGetRegistryValue(
handle,
L"List",
&keyValueInformation
);
ZwClose(handle);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_MULTI_SZ) &&
(keyValueInformation->DataLength != 0)) {
status = PipRegMultiSzToUnicodeStrings(keyValueInformation, &groupTable, &count);
} else {
status = STATUS_UNSUCCESSFUL;
}
ExFreePool(keyValueInformation);
}
if (!NT_SUCCESS(status)) {
return status;
}
PiInitGroupOrderTable = groupTable;
PiInitGroupOrderTableCount = (USHORT) count;
return STATUS_SUCCESS;
}
VOID
PiInitReleaseCachedGroupInformation(
VOID
)
/*++
Routine Description:
This routine releases the service group order list cache. It should be
called just after the system start legacy drivers have been loaded.
Parameters:
None.
Return Value:
None.
--*/
{
ASSERT(PnPInitialized);
if (PiInitGroupOrderTable) {
PipFreeUnicodeStringList(
PiInitGroupOrderTable,
PiInitGroupOrderTableCount
);
PiInitGroupOrderTable = NULL;
PiInitGroupOrderTableCount = 0;
}
}
USHORT
PpInitGetGroupOrderIndex(
IN HANDLE ServiceHandle
)
/*++
Routine Description:
This routine reads the Group value of the service key, finds its position
in the ServiceOrderList. If ServiceHandle is NULL or unrecognized group
value, it returns a value with max group order + 1.
Parameters:
ServiceHandle - supplies a handle to the service key.
Return Value:
group order index.
--*/
{
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
UNICODE_STRING group;
USHORT index;
PAGED_CODE();
ASSERT(!PnPInitialized);
if (PiInitGroupOrderTable == NULL) {
return NO_MORE_GROUP;
}
if (ServiceHandle == NULL) {
return PiInitGroupOrderTableCount + 1;
}
//
// Read service key's Group value
//
status = IopGetRegistryValue(
ServiceHandle,
REGSTR_VALUE_GROUP,
&keyValueInformation
);
if (!NT_SUCCESS(status)) {
//
// If we failed to read the Group value, or no Group value...
//
return PiInitGroupOrderTableCount;
}
//
// Verify type information
//
if ((keyValueInformation->Type != REG_SZ) ||
(keyValueInformation->DataLength == 0)) {
ASSERT(0);
ExFreePool(keyValueInformation);
return PiInitGroupOrderTableCount;
}
IopRegistryDataToUnicodeString(
&group,
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
keyValueInformation->DataLength
);
for (index = 0; index < PiInitGroupOrderTableCount; index++) {
if (RtlEqualUnicodeString(&group, &PiInitGroupOrderTable[index], TRUE)) {
break;
}
}
ExFreePool(keyValueInformation);
return index;
}
BOOLEAN
PpInitSystem (
VOID
)
/*++
Routine Description:
This function performs initialization of the kernel-mode Plug and Play
Manager. It is called during phase 0 and phase 1 initialization. Its
function is to dispatch to the appropriate phase initialization routine.
Arguments:
None.
Return Value:
TRUE - Initialization succeeded.
FALSE - Initialization failed.
--*/
{
switch ( InitializationPhase ) {
case 0 :
return PiInitPhase0();
case 1 :
#if defined(_X86_)
PnPBiosInitializePnPBios() ;
#endif
return TRUE;
default:
KeBugCheckEx(UNEXPECTED_INITIALIZATION_CALL, 2, InitializationPhase, 0, 0);
}
}
BOOLEAN
PiInitPhase0(
VOID
)
/*++
Routine Description:
This function performs Phase 0 initializaion of the Plug and Play Manager
component of the NT system. It initializes the PnP registry and bus list
resources, and initializes the bus list head to empty.
Arguments:
None.
Return Value:
TRUE - Initialization succeeded.
FALSE - Initialization failed.
--*/
{
//
// Initialize the device-specific, Plug and Play registry resource.
//
ExInitializeResourceLite( &PpRegistryDeviceResource );
PpInitializeDeviceReferenceTable();
return TRUE;
}
NTSTATUS
IopStartRamdisk(
PLOADER_PARAMETER_BLOCK LoaderBlock
)
{
ULONG code;
NTSTATUS status;
WCHAR buffer[ RAMDISK_MAX_DEVICE_NAME ];
UNICODE_STRING guidString;
PLIST_ENTRY listEntry;
PMEMORY_ALLOCATION_DESCRIPTOR memoryDescriptor = NULL;
UNICODE_STRING ustring;
UNICODE_STRING ustring2;
UNICODE_STRING string;
RAMDISK_CREATE_INPUT create;
OBJECT_ATTRIBUTES obja;
IO_STATUS_BLOCK iosb;
HANDLE handle = NULL;
PCHAR options;
RtlInitUnicodeString( &string, RAMDISK_DEVICENAME );
//
// Find the descriptor for the memory block into which the loader read the
// disk image.
//
for ( listEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
listEntry != &LoaderBlock->MemoryDescriptorListHead;
listEntry = listEntry->Flink ) {
memoryDescriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (memoryDescriptor->MemoryType == LoaderXIPRom) {
break;
}
}
if ( listEntry == &LoaderBlock->MemoryDescriptorListHead ) {
KdPrint(( "IopStartRamdisk: Couldn't find LoaderXIPRom descriptor\n" ));
code = 1;
status = STATUS_INVALID_PARAMETER;
goto failed;
}
//
// Build the IOCTL parameter block.
//
RtlZeroMemory( &create, sizeof(create) );
create.Version = sizeof(RAMDISK_CREATE_INPUT);
create.DiskType = RAMDISK_TYPE_BOOT_DISK;
create.BasePage = memoryDescriptor->BasePage;
create.DriveLetter = L'C'; // ISSUE: Does this need to be configurable?
create.Options.Fixed = (BOOLEAN)TRUE;
create.Options.Readonly = (BOOLEAN)FALSE;
create.Options.NoDriveLetter = (BOOLEAN)FALSE;
create.Options.Hidden = (BOOLEAN)FALSE;
create.Options.NoDosDevice = (BOOLEAN)FALSE;
//
// Use the well-known boot disk GUID.
//
create.DiskGuid = RamdiskBootDiskGuid;
//
// Look for RDIMAGEOFFSET and RDIMAGELENGTH load options.
//
create.DiskOffset = 0;
create.DiskLength = memoryDescriptor->PageCount * PAGE_SIZE;
options = LoaderBlock->LoadOptions;
if ( options != NULL ) {
PCHAR option;
_strupr( options );
option = strstr( options, "RDIMAGEOFFSET" );
if ( option != NULL ) {
option = strstr( option, "=" );
if ( option != NULL ) {
create.DiskOffset = atol( option + 1 );
}
}
create.DiskLength -= create.DiskOffset;
option = strstr( options, "RDIMAGELENGTH" );
if ( option != NULL ) {
option = strstr( option, "=" );
if ( option != NULL ) {
ULONGLONG length = _atoi64( option + 1 );
ASSERT( length <= create.DiskLength );
create.DiskLength = length;
}
}
}
//
// Send an IOCTL to ramdisk.sys telling it to create the RAM disk.
//
InitializeObjectAttributes( &obja, &string, OBJ_CASE_INSENSITIVE, NULL, NULL );
status = NtOpenFile(
&handle,
GENERIC_READ | GENERIC_WRITE,
&obja,
&iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT
);
if ( NT_SUCCESS(status) ) {
status = iosb.Status;
}
if ( !NT_SUCCESS(status) ) {
KdPrint(( "IopStartRamdisk: Error opening %wZ. Error: %x\n", &string, status ));
code = 2;
goto failed;
}
status = NtDeviceIoControlFile(
handle,
NULL,
NULL,
NULL,
&iosb,
FSCTL_CREATE_RAM_DISK,
&create,
sizeof(create),
NULL,
0
);
NtClose( handle );
if ( NT_SUCCESS(status) ) {
status = iosb.Status;
}
if ( !NT_SUCCESS(status) ) {
KdPrint(( "IopStartRamdisk: Error creating RAM disk: %x\n", status ));
code = 3;
goto failed;
}
//
// Create an ARC name pointing ramdisk(0) to the RAM disk.
//
status = RtlStringFromGUID( &create.DiskGuid, &guidString);
if ( !NT_SUCCESS(status) ) {
KdPrint(( "IopStartRamdisk: Error creating disk GUID string: %x\n", status ));
code = 4;
goto failed;
}
_snwprintf( buffer, RAMDISK_MAX_DEVICE_NAME, L"\\Device\\Ramdisk%wZ", &guidString);
RtlInitUnicodeString( &ustring, L"\\ArcName\\ramdisk(0)" );
RtlInitUnicodeString( &ustring2, buffer );
status = IoCreateSymbolicLink( &ustring, &ustring2 );
RtlFreeUnicodeString( &guidString );
if (!NT_SUCCESS(status)) {
KdPrint(( "IopStartRamdisk: Failed to create arcname symbolic link (%wZ --> %wZ). %x\n",
&ustring, &ustring2, status ));
code = 5;
goto failed;
}
return STATUS_SUCCESS;
failed:
KeBugCheckEx( INACCESSIBLE_BOOT_DEVICE,
(ULONG_PTR)&string,
status,
code,
0
);
} // IopStartRamdisk