/*++ 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 Revision History: --*/ #include "iop.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 4; // // 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; PTREE_ENTRY IopGroupListHead; // // Define a macro for initializing drivers. // //#if _PNP_POWER_ #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 ); \ } //#else #if 0 #define InitializeDriverObject( DriverObject ) { \ ULONG i; \ RtlZeroMemory( DriverObject, sizeof( DRIVER_OBJECT ) ); \ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) \ DriverObject->MajorFunction[i] = IopInvalidDeviceRequest; \ DriverObject->Type = IO_TYPE_DRIVER; \ DriverObject->Size = sizeof( DRIVER_OBJECT ); \ } #endif // // 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 ); BOOLEAN IopInitializeBuiltinDriver( IN PUNICODE_STRING DriverName, IN PUNICODE_STRING RegistryPath, IN PDRIVER_INITIALIZE DriverInitializeRoutine ); BOOLEAN IopInitializeDumpDrivers( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); 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 IopRevertModuleList( IN ULONG ListCount ); VOID IopStoreSystemPartitionInformation( IN PUNICODE_STRING NtSystemPartitionDeviceName, IN OUT PUNICODE_STRING OsLoaderPathName ); #ifdef _PNP_POWER_ VOID IopVerifyBuiltInBuses( VOID ); NTSTATUS IopRegisterNewBusInstance ( PBUS_HANDLER BusHandler ); VOID PiGetBusDescription( INTERFACE_TYPE InterfaceType, WCHAR BusName[MAX_BUS_NAME] ); #endif // _PNP_POWER_ // // 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,IopInitializeDumpDrivers) #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,IopRevertModuleList) #pragma alloc_text(INIT,IopStoreSystemPartitionInformation) #ifdef _PNP_POWER_ #pragma alloc_text(INIT,IopVerifyBuiltInBuses) #pragma alloc_text(INIT,IopRegisterNewBusInstance) #endif // _PNP_POWER_ #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 packetSize; ULONG numberOfPackets; ULONG poolSize; PLIST_ENTRY entry; PREINIT_PACKET reinitEntry; LARGE_INTEGER deltaTime; MM_SYSTEMSIZE systemSize; USHORT largeIrpZoneSize; USHORT smallIrpZoneSize; USHORT mdlZoneSize; ULONG oldNtGlobalFlag; NTSTATUS status; ANSI_STRING AnsiString; 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 ); //#if _PNP_POWER_ status = IopInitializePlugPlayServices(LoaderBlock); if (!NT_SUCCESS(status)) { return FALSE; } //#endif // // 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 ); KeInitializeSpinLock( &IopDatabaseLock ); InitializeListHead( &IopDiskFileSystemQueueHead ); InitializeListHead( &IopCdRomFileSystemQueueHead ); InitializeListHead( &IopTapeFileSystemQueueHead ); InitializeListHead( &IopNetworkFileSystemQueueHead ); InitializeListHead( &IopDriverReinitializeQueueHead ); InitializeListHead( &IopNotifyShutdownQueueHead ); InitializeListHead( &IopFsNotifyChangeQueueHead ); KeInitializeSpinLock( &IopCancelSpinLock ); KeInitializeSpinLock( &IopVpbSpinLock ); KeInitializeSpinLock( &IoStatisticsLock ); // // 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 : smallIrpZoneSize = 6; largeIrpZoneSize = 8; mdlZoneSize = 16; break; case MmMediumSystem : smallIrpZoneSize = 24; largeIrpZoneSize = 32; mdlZoneSize = 90; break; case MmLargeSystem : if (MmIsThisAnNtAsSystem()) { smallIrpZoneSize = 96; largeIrpZoneSize = 128; mdlZoneSize = 256; } else { smallIrpZoneSize = 32; largeIrpZoneSize = 64; mdlZoneSize = 128; } break; } // // Initialize the large IRP lookaside list. // packetSize = (ULONG) (sizeof( IRP ) + (IopLargeIrpStackLocations * sizeof( IO_STACK_LOCATION ))); ExInitializeNPagedLookasideList( &IopLargeIrpLookasideList, NULL, NULL, 0, packetSize, 'lprI', largeIrpZoneSize ); // // Initialize the small IRP lookaside list. // packetSize = (ULONG) (sizeof( IRP ) + sizeof( IO_STACK_LOCATION )); ExInitializeNPagedLookasideList( &IopSmallIrpLookasideList, NULL, NULL, 0, packetSize, 'sprI', smallIrpZoneSize ); // // Initialize the MDL lookaside list. // packetSize = (ULONG) (sizeof( MDL ) + (IOP_FIXED_SIZE_MDL_PFNS * sizeof( ULONG ))); ExInitializeNPagedLookasideList( &IopMdlLookasideList, NULL, NULL, 0, packetSize, ' ldM', mdlZoneSize ); // // Initialize the I/O completion spin lock. // KeInitializeSpinLock( &IopCompletionLock ); // // Initalize the error log spin locks and log list. // KeInitializeSpinLock( &IopErrorLogLock ); KeInitializeSpinLock( &IopErrorLogAllocationLock ); InitializeListHead( &IopErrorLogListHead ); // // Initialize the registry access semaphore. // KeInitializeSemaphore( &IopRegistrySemaphore, 1, 1 ); // // 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 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 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; } // // 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 ); //#if _PNP_POWER_ reinitEntry->DriverObject->DriverExtension->Count++; reinitEntry->DriverReinitializationRoutine( reinitEntry->DriverObject, reinitEntry->Context, reinitEntry->DriverObject->DriverExtension->Count ); //#else #if 0 reinitEntry->DriverObject->Count++; reinitEntry->DriverReinitializationRoutine( reinitEntry->DriverObject, reinitEntry->Context, reinitEntry->DriverObject->Count ); #endif // _PNP_POWER_ 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; // // Indicate that the I/O system successfully initialized itself. // return TRUE; } 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[64]; STRING deviceNameString; UNICODE_STRING deviceNameUnicodeString; PDEVICE_OBJECT deviceObject; UCHAR arcNameBuffer[64]; 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 singleBiosDiskFound; BOOLEAN bootDiskFound = FALSE; PARC_DISK_INFORMATION arcInformation = LoaderBlock->ArcDiskInformation; ULONG totalDriverDisksFound = IoGetConfigurationInformation()->DiskCount; STRING arcSystemDeviceString; STRING osLoaderPathString; UNICODE_STRING osLoaderPathUnicodeString; // // 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); // // Get ARC boot device name from loader block. // RtlInitAnsiString( &arcBootDeviceString, LoaderBlock->ArcBootDeviceName ); // // Get ARC system device name from loader block. // RtlInitAnsiString( &arcSystemDeviceString, LoaderBlock->ArcHalDeviceName ); // // 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. // for (diskNumber = 0; diskNumber < totalDriverDisksFound; diskNumber++) { // // Construct the NT name for a disk and obtain a reference. // 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 ); 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. // sprintf( deviceNameBuffer, "\\Device\\Harddisk%d\\Partition0", diskNumber ); 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. // sprintf( deviceNameBuffer, "\\Device\\Harddisk%d\\Partition%d", diskNumber, 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); } } } } 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; #if DBG IopCreateFileEventId = RtlCreateEventId( NULL, 0, "CreateFile", 9, RTL_EVENT_PUNICODE_STRING_PARAM, "FileName", 0, RTL_EVENT_FLAGS_PARAM, "", 13, GENERIC_READ, "GenericRead", GENERIC_WRITE, "GenericWrite", GENERIC_EXECUTE, "GenericExecute", GENERIC_ALL, "GenericAll", FILE_READ_DATA, "Read", FILE_WRITE_DATA, "Write", FILE_APPEND_DATA, "Append", FILE_EXECUTE, "Execute", FILE_READ_EA, "ReadEa", FILE_WRITE_EA, "WriteEa", FILE_DELETE_CHILD, "DeleteChild", FILE_READ_ATTRIBUTES, "ReadAttributes", FILE_WRITE_ATTRIBUTES, "WriteAttributes", RTL_EVENT_FLAGS_PARAM, "", 3, FILE_SHARE_READ, "ShareRead", FILE_SHARE_WRITE, "ShareWrite", FILE_SHARE_DELETE, "ShareDelete", RTL_EVENT_ENUM_PARAM, "", 5, FILE_SUPERSEDE, "Supersede", FILE_OPEN, "Open", FILE_CREATE, "Create", FILE_OPEN_IF, "OpenIf", FILE_OVERWRITE, "Overwrite", RTL_EVENT_FLAGS_PARAM, "", 14, FILE_DIRECTORY_FILE, "OpenDirectory", FILE_WRITE_THROUGH, "WriteThrough", FILE_SEQUENTIAL_ONLY, "Sequential", FILE_NO_INTERMEDIATE_BUFFERING, "NoBuffering", FILE_SYNCHRONOUS_IO_ALERT, "Synchronous", FILE_SYNCHRONOUS_IO_NONALERT, "SynchronousNoAlert", FILE_NON_DIRECTORY_FILE, "OpenNonDirectory", FILE_CREATE_TREE_CONNECTION, "CreateTreeConnect", FILE_COMPLETE_IF_OPLOCKED, "CompleteIfOpLocked", FILE_NO_EA_KNOWLEDGE, "NoEas", FILE_RANDOM_ACCESS, "Random", FILE_DELETE_ON_CLOSE, "DeleteOnClose", FILE_OPEN_BY_FILE_ID, "OpenById", FILE_OPEN_FOR_BACKUP_INTENT, "BackupIntent", RTL_EVENT_ENUM_PARAM, "", 2, CreateFileTypeNamedPipe, "NamedPiped", CreateFileTypeMailslot, "MailSlot", RTL_EVENT_ULONG_PARAM, "Handle", 0, RTL_EVENT_STATUS_PARAM, "", 0, RTL_EVENT_ENUM_PARAM, "", 6, FILE_SUPERSEDED, "Superseded", FILE_OPENED, "Opened", FILE_CREATED, "Created", FILE_OVERWRITTEN, "Truncated", FILE_EXISTS, "Exists", FILE_DOES_NOT_EXIST, "DoesNotExist" ); IopReadFileEventId = RtlCreateEventId( NULL, 0, "ReadFile", 6, RTL_EVENT_ULONG_PARAM, "Handle", 0, RTL_EVENT_ULONG_PARAM, "Buffer", 0, RTL_EVENT_ULONG_PARAM, "Length", 0, RTL_EVENT_STATUS_PARAM, "Io", 0, RTL_EVENT_ULONG_PARAM, "IoLength", 0, RTL_EVENT_STATUS_PARAM, "", 0 ); IopWriteFileEventId = RtlCreateEventId( NULL, 0, "WriteFile", 6, RTL_EVENT_ULONG_PARAM, "Handle", 0, RTL_EVENT_ULONG_PARAM, "Buffer", 0, RTL_EVENT_ULONG_PARAM, "Length", 0, RTL_EVENT_STATUS_PARAM, "Io", 0, RTL_EVENT_ULONG_PARAM, "IoLength", 0, RTL_EVENT_STATUS_PARAM, "", 0 ); IopCloseFileEventId = RtlCreateEventId( NULL, 0, "CloseFile", 2, RTL_EVENT_ULONG_PARAM, "Handle", 0, RTL_EVENT_STATUS_PARAM, "", 0 ); #endif // DBG // // 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; #ifdef _PNP_POWER_ objectTypeInitializer.DeleteProcedure = IopDeleteDevice; #endif // _PNP_POWER_ 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, //#if _PNP_POWER_ (ULONG) (sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )), //#else #if 0 (ULONG) sizeof( DRIVER_OBJECT ), #endif 0, 0, (PVOID *)DriverObject ); return status; } #ifdef _PNP_POWER_ BOOLEAN IopInitializeSingleBootDriver( IN HANDLE KeyHandle, IN PBOOT_DRIVER_LIST_ENTRY BootDriver, OUT PUNICODE_STRING DriverName OPTIONAL ) /*++ Routine Description: This routine is invoked to initialize a single boot driver. Arguments: KeyHandle - Supplies a handle to the driver's key in the Services list. BootDriver - Supplies a pointer to the driver's node in the loader's boot driver list. DriverName - Optionally receives the driver name used for this driver object. (NOTE: If this argument is specified, then it is the caller's responsibility to free the memory allocated for the string buffer.) Return Value: The function value is a BOOLEAN indicating whether or not the boot driver was successfully initialized. --*/ { NTSTATUS status; UNICODE_STRING completeName; PKEY_VALUE_FULL_INFORMATION keyValueInformation; UNICODE_STRING groupName; PTREE_ENTRY treeEntry; PLDR_DATA_TABLE_ENTRY driverEntry; BOOLEAN success = FALSE; // // 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 } else { status = IopGetRegistryValue(KeyHandle, REGSTR_VALUE_GROUP, &keyValueInformation ); if(NT_SUCCESS(status)) { if((keyValueInformation->Type == REG_SZ) && keyValueInformation->DataLength) { groupName.MaximumLength = groupName.Length = (USHORT)keyValueInformation->DataLength; groupName.Buffer = (PWCHAR)KEY_VALUE_DATA(keyValueInformation); treeEntry = IopLookupGroupName(&groupName, TRUE); } else { treeEntry = (PTREE_ENTRY)NULL; } ExFreePool(keyValueInformation); } else { treeEntry = (PTREE_ENTRY)NULL; } driverEntry = BootDriver->LdrEntry; if(IopCheckDependencies(KeyHandle) && IopInitializeBuiltinDriver(&completeName, &BootDriver->RegistryPath, (PDRIVER_INITIALIZE)driverEntry->EntryPoint )) { success = TRUE; if(treeEntry) { treeEntry->DriversLoaded++; } } else { PLIST_ENTRY NextEntry; PLDR_DATA_TABLE_ENTRY DataTableEntry; NextEntry = PsLoadedModuleList.Flink; while(NextEntry != &PsLoadedModuleList) { DataTableEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if(RtlEqualString((PSTRING)&driverEntry->BaseDllName, (PSTRING)&DataTableEntry->BaseDllName, TRUE)) { driverEntry->Flags |= LDRP_FAILED_BUILTIN_LOAD; DataTableEntry->Flags |= LDRP_FAILED_BUILTIN_LOAD; break; } NextEntry = NextEntry->Flink; } } if(success && ARGUMENT_PRESENT(DriverName)) { *DriverName = completeName; } else { ExFreePool(completeName.Buffer); } } return success; } #endif // _PNP_POWER_ 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. ** _PNP_POWER_ Only ** Two passes are made over the loader parameter block. In the first pass, all HAL bus extenders are loaded. This is an iterative process, where the initialization of one bus extender may materialize other buses--perhaps for extenders that have already initialized. Also during this pass, all enumerable devices are found and registered in HKLM\System\Enum. In the second pass, all other boot drivers are loaded. Since Plug and Play device enumeration was done as part of the first pass, all PnP information is available to drivers initialized in the second pass. 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; #if 0 OBJECT_ATTRIBUTES objectAttributes; #endif UNICODE_STRING rawFsName; NTSTATUS status; PLIST_ENTRY nextEntry; PBOOT_DRIVER_LIST_ENTRY bootDriver; HANDLE keyHandle; PKEY_VALUE_FULL_INFORMATION keyValueInformation; #ifdef _PNP_POWER_ ULONG bootInitPass, serviceInstanceCount, i; BOOLEAN isBusExtender, success; PPLUGPLAY_BUS_ENUMERATOR busEnumerator; HANDLE serviceEnumHandle; UNICODE_STRING serviceEnumName; PHAL_BUS_INFORMATION oldBusInformation; ULONG oldBusCount; PLIST_ENTRY nextBusInstance; HANDLE driverHandle; PDRIVER_OBJECT driverObject; #else PLDR_DATA_TABLE_ENTRY driverEntry; UNICODE_STRING groupName; PTREE_ENTRY treeEntry; #endif // _PNP_POWER_ UNREFERENCED_PARAMETER( PreviousDriver ); // // Initialize the built-in RAW file system driver. // RtlInitUnicodeString( &rawFsName, L"\\FileSystem\\RAW" ); RtlInitUnicodeString( &completeName, L"" ); if (!IopInitializeBuiltinDriver( &rawFsName, &completeName, RawInitialize )) { #if DBG DbgPrint( "IOINIT: Failed to initialize RAW filsystem \n" ); #endif return FALSE; } #ifdef _PNP_POWER_ // // For the bus/device enumeration, we must acquire exclusive (write) access to // resources for the PnP device portions of the registry, as well as to // the PnP bus list. // KeEnterCriticalRegion(); ExAcquireResourceExclusive(&PpBusResource, TRUE); ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE); // // Make two passes through the list of boot drivers--first enumerating/loading // all bus extenders, and then loading all other boot drivers. // success = TRUE; for(bootInitPass = 0; bootInitPass < 2; bootInitPass++) { // // Traverse the list of boot drivers, looking for appropriate drivers // for this initialization pass. // for(nextEntry = LoaderBlock->BootDriverListHead.Flink; nextEntry != &LoaderBlock->BootDriverListHead; nextEntry = nextEntry->Flink) { bootDriver = CONTAINING_RECORD(nextEntry, BOOT_DRIVER_LIST_ENTRY, Link ); // // Open the driver's registry key. // status = IopOpenRegistryKey(&keyHandle, NULL, &bootDriver->RegistryPath, KEY_READ, FALSE ); if(!NT_SUCCESS(status)) { // // Something is quite wrong. The driver must have a key in the // registry, or the OS Loader wouldn't have loaded it in the first // first place. Skip this driver and keep going. // #if DBG DbgPrint("IOINIT: IopInitializeBootDrivers couldn't open registry\n"); DbgPrint(" key for %wZ. Cannot initialize driver.\n", &bootDriver->RegistryPath ); #endif // DBG continue; } // // Find out whether this driver is a bus extender. // isBusExtender = FALSE; status = IopGetRegistryValue(keyHandle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation ); if(NT_SUCCESS(status)) { if((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength >= sizeof(ULONG))) { isBusExtender = (PlugPlayServiceBusExtender == (PLUGPLAY_SERVICE_TYPE)(*(PULONG)KEY_VALUE_DATA(keyValueInformation))); } ExFreePool(keyValueInformation); } if(isBusExtender) { if(bootInitPass != 0) { // // All bus extenders are initialized in the first pass. // goto BootDriverInitCleanup; } // // Find this bus extender in our bus enumerator list. Determine // whether we need to initialize this bus extender now. // if(!(busEnumerator = PiBusEnumeratorFromRegistryPath(&bootDriver->RegistryPath))) { #if DBG DbgPrint("IOINIT: IopInitializeBootDrivers couldn't find the bus\n"); DbgPrint(" enumerator %wZ. Cannot initialize driver.\n", &bootDriver->RegistryPath ); #endif // DBG success = FALSE; goto BootDriverInitCleanup; } // // Initialize the bus extender. // if(!IopInitializeSingleBootDriver(keyHandle, bootDriver, &completeName)) { #if DBG DbgPrint("IOINIT: IopInitializeBootDrivers couldn't initialize bus\n"); DbgPrint(" extender %wZ.\n", &bootDriver->FilePath); #endif // DBG // // This bus extender failed to initialize. This will be reflected by // the absence of a driver object pointer in its DriverObject field. // If the bus enumerator specified any Plug&Play IDs for buses it // controlled, then we will leave it in our bus list, so that if // enumeration turns up a bus of this type, we will be able to match it // up with its corresponding extender, and then inform the user that // there is a problem with the bus driver for the newly found bus. // if(!busEnumerator->PlugPlayIDCount) { // // There's no need to keep this around, so remove from the bus // extender list, and free its memory. // RemoveEntryList(&(busEnumerator->BusEnumeratorListEntry)); ExFreePool(busEnumerator->ServiceName.Buffer); ExFreePool(busEnumerator); } goto BootDriverInitCleanup; } #if 1 busEnumerator->DriverName = completeName; #else // // The bus extender initialized successfully. Store away a pointer to its // driver object, so that we can call the driver's 'add device instance' // routine when we enumerate buses that this driver should control. We also // need this pointer, so that we can call the driver's bus detection routine // (if supplied) after we've found all enumerable buses. // // Note that it is OK for us to be storing away a pointer to the device object // (even though in general this is a big no-no, since the driver unload is // allowed/disallowed based on opened device objects, regardless of the driver // object's refcount). In this case, though, these are boot devices and can't // be unloaded anyway. Even if they weren't, it is acceptable to store away // a driver object pointer to a bus extender, because these drivers should never // supply an unload entry point. // // First, open the driver object. // InitializeObjectAttributes(&objectAttributes, &completeName, 0, NULL, NULL ); status = ObOpenObjectByName(&objectAttributes, IoDriverObjectType, KernelMode, NULL, FILE_READ_ATTRIBUTES, NULL, &driverHandle ); ExFreePool(completeName.Buffer); if(NT_SUCCESS(status)) { // // Now reference the driver object. // status = ObReferenceObjectByHandle(driverHandle, 0, IoDriverObjectType, KernelMode, &driverObject, NULL ); NtClose(driverHandle); } if(!NT_SUCCESS(status)) { // // Treat this case just as if the driver had failed to initialize. Again, // we will keep the bus extender node in our list only if it specifies // Plug & Play IDs which we may need for later reference. // #if DBG DbgPrint("IOINIT: IopInitializeBootDrivers couldn't retrieve driver object\n"); DbgPrint(" for %wZ (status %x).\n", &bootDriver->RegistryPath, status); #endif // DBG if(!busEnumerator->PlugPlayIDCount) { // // There's no need to keep this around, so remove from the bus // extender list, and free its memory. // RemoveEntryList(&(busEnumerator->BusEnumeratorListEntry)); ExFreePool(busEnumerator->ServiceName.Buffer); ExFreePool(busEnumerator); } goto BootDriverInitCleanup; } busEnumerator->DriverObject = driverObject; #endif // 1 } else { // this driver is not a bus extender if(bootInitPass != 1) { // // All non-bus extender drivers are initialized in the second pass. // goto BootDriverInitCleanup; } // // Initialize the driver (ignore failures). // IopInitializeSingleBootDriver(keyHandle, bootDriver, NULL); } BootDriverInitCleanup: NtClose(keyHandle); if(!success) { break; } } if(bootInitPass == 0) { // // We have now initialized all bus extender drivers. We must now check // each built-in bus instance that we previously registered, to see // whether that bus instance is still being controlled by the built-in // bus extender. It is likely that one or more of these BIB's have been // 'taken over' by an installable bus extender during its initialization. // This capability allows the continued use of old, non-PnP HALs, where // PnP functionality is gained through the addition of one or more bus // extenders. // IopVerifyBuiltInBuses(); // // Release the PnP resources we previously acquired. // ExReleaseResource(&PpRegistryDeviceResource); ExReleaseResource(&PpBusResource); KeLeaveCriticalRegion(); // // Now that we've initialized all bus extenders, we can perform a full // enumeration of the system, so that all enumerable devices will be // available for the rest of the boot drivers during the next pass. // status = PpPerformFullEnumeration(TRUE); #if DBG if(!NT_SUCCESS(status)) { DbgPrint("IOINIT: PpPerformFullEnumeration failed (status %x).\n", status); } #endif // DBG } if(!success) { return FALSE; } } #else // // BUGBUG (lonnym): for _PNP_POWER_, much of the code inside the following loop // has been moved to the IopInitializeSingleBootDriver routine. Great care must // be taken to ensure that changes made here are also made there (and vice versa). // // // Walk the list of boot drivers and initialize each. // nextEntry = LoaderBlock->BootDriverListHead.Flink; while (nextEntry != &LoaderBlock->BootDriverListHead) { // // Initialize the next boot driver in the list. // bootDriver = CONTAINING_RECORD( nextEntry, BOOT_DRIVER_LIST_ENTRY, Link ); driverEntry = bootDriver->LdrEntry; // // Open the driver's registry key to find out if this is a // filesystem or a driver. // status = IopOpenRegistryKey( &keyHandle, (HANDLE) NULL, &bootDriver->RegistryPath, KEY_READ, FALSE ); if (!NT_SUCCESS( status )) { // // Something is quite wrong. The driver must have a key in the // registry, or the OS Loader wouldn't have loaded it in the first // first place. Skip this driver and keep going. // #if DBG DbgPrint( "IOINIT: IopInitializeBootDrivers couldn't open registry\n" ); DbgPrint( " key for %wZ. Cannot initialize driver.\n", &bootDriver->RegistryPath ); #endif // DBG nextEntry = nextEntry->Flink; continue; } // // 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 } 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; } if (IopCheckDependencies( keyHandle ) && IopInitializeBuiltinDriver( &completeName, &bootDriver->RegistryPath, (PDRIVER_INITIALIZE) driverEntry->EntryPoint )) { if (treeEntry) { treeEntry->DriversLoaded++; } } else { PLIST_ENTRY NextEntry; PLDR_DATA_TABLE_ENTRY DataTableEntry; NextEntry = PsLoadedModuleList.Flink; while (NextEntry != &PsLoadedModuleList) { DataTableEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (RtlEqualString((PSTRING)&driverEntry->BaseDllName, (PSTRING)&DataTableEntry->BaseDllName, TRUE )) { driverEntry->Flags |= LDRP_FAILED_BUILTIN_LOAD; DataTableEntry->Flags |= LDRP_FAILED_BUILTIN_LOAD; break; } NextEntry = NextEntry->Flink; } } ExFreePool( completeName.Buffer ); } NtClose( keyHandle ); nextEntry = nextEntry->Flink; } #endif // _PNP_POWER_ // // 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; } // // Initialize the drivers necessary to dump all of physical memory to the // disk if the system is configured to do so. // return IopInitializeDumpDrivers( LoaderBlock ); } BOOLEAN IopInitializeBuiltinDriver( IN PUNICODE_STRING DriverName, IN PUNICODE_STRING RegistryPath, IN PDRIVER_INITIALIZE DriverInitializeRoutine ) /*++ 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. Return Value: The function value is a BOOLEAN indicating whether or not the built-in driver successfully initialized. --*/ { HANDLE handle; PDRIVER_OBJECT driverObject; OBJECT_ATTRIBUTES objectAttributes; PWSTR buffer; NTSTATUS status; //#if _PNP_POWER_ HANDLE serviceHandle; PWSTR pserviceName; USHORT serviceNameLength; PDRIVER_EXTENSION driverExtension; //#endif #if DBG LARGE_INTEGER stime, etime; ULONG dtime; #endif // // Begin by creating the driver object. // status = IopInitializeAttributesAndCreateObject( DriverName, &objectAttributes, &driverObject ); if (!NT_SUCCESS( status )) { return FALSE; } // // 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 FALSE; } // // 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'; } //#if _PNP_POWER_ // // 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 = IopOpenRegistryKey(&serviceHandle, NULL, RegistryPath, KEY_ALL_ACCESS, FALSE ); if (NT_SUCCESS(status)) { status = IopPrepareDriverLoading(&driverExtension->ServiceKeyName, serviceHandle); NtClose(serviceHandle); if (!NT_SUCCESS(status)) { goto exit; } } else { goto exit; } } else { driverExtension->ServiceKeyName.Buffer = NULL; driverExtension->ServiceKeyName.MaximumLength = 0; driverExtension->ServiceKeyName.Length = 0; } //#endif // // 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. // status = driverObject->DriverInit( driverObject, RegistryPath ); #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 (NT_SUCCESS( status )) { IopReadyDeviceObjects( driverObject ); return TRUE; } 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 ); return FALSE; } } BOOLEAN IopInitializeDumpDrivers( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This routine is invoked to load and initialize any drivers that are required to dump memory to the boot device's paging file, if the system is configured to do so. Arguments: LoaderBlock - System boot loader parameter block. Return Value: The final function value is TRUE if everything worked, else FALSE. --*/ { HANDLE keyHandle; HANDLE crashHandle; HANDLE deviceHandle; UNICODE_STRING keyName; NTSTATUS status; PKEY_VALUE_FULL_INFORMATION keyValueInformation; OBJECT_ATTRIBUTES objectAttributes; ULONG dumpControl; ULONG handleValue; BOOLEAN scsiDump; ULONG autoReboot; PVOID scratch; PCHAR partitionName; IO_STATUS_BLOCK ioStatus; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PIRP irp; KEVENT event; DUMP_POINTERS dumpPointers; PDUMP_CONTROL_BLOCK dcb; ULONG savedModuleCount; ULONG dcbSize; PLIST_ENTRY nextEntry; UNICODE_STRING driverName; PLDR_DATA_TABLE_ENTRY loaderEntry; PLDR_DATA_TABLE_ENTRY imageHandle; PUCHAR imageBaseAddress; PWCHAR buffer; ANSI_STRING ansiString; PMINIPORT_NODE mpNode; UNICODE_STRING tmpName; LARGE_INTEGER page; PARTITION_INFORMATION partitionInfo; SCSI_ADDRESS scsiAddress; PMAPPED_ADDRESS addresses; PHYSICAL_ADDRESS paTemp; // // Begin by opening the path to the control for dumping memory. Note // that if it does not exist, then no dumps will occur. // RtlInitUnicodeString( &keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control" ); status = IopOpenRegistryKey( &keyHandle, (HANDLE) NULL, &keyName, KEY_READ, FALSE ); if (!NT_SUCCESS( status )) { return TRUE; } RtlInitUnicodeString( &keyName, L"CrashControl" ); status = IopOpenRegistryKey( &crashHandle, keyHandle, &keyName, KEY_READ, FALSE ); NtClose( keyHandle ); if (!NT_SUCCESS( status )) { return TRUE; } // // Now get the value of the crash control to determine whether or not // dumping is enabled. // dumpControl = 0; status = IopGetRegistryValue( crashHandle, L"CrashDumpEnabled", &keyValueInformation ); if (NT_SUCCESS( status )) { if (keyValueInformation->DataLength) { handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset)); ExFreePool( keyValueInformation); if (handleValue) { dumpControl |= DCB_DUMP_ENABLED; } } } status = IopGetRegistryValue( crashHandle, L"LogEvent", &keyValueInformation ); if (NT_SUCCESS( status )) { if (keyValueInformation->DataLength) { handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset)); ExFreePool( keyValueInformation); if (handleValue) { dumpControl |= DCB_SUMMARY_ENABLED; } } } status = IopGetRegistryValue( crashHandle, L"SendAlert", &keyValueInformation ); if (NT_SUCCESS( status )) { if (keyValueInformation->DataLength) { handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset)); ExFreePool( keyValueInformation); if (handleValue) { dumpControl |= DCB_SUMMARY_ENABLED; } } } // // Now determine whether or not automatic reboot is enabled. // autoReboot = 0; status = IopGetRegistryValue( crashHandle, L"AutoReboot", &keyValueInformation ); NtClose( crashHandle ); if (NT_SUCCESS( status )) { if (keyValueInformation->DataLength) { autoReboot = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset)); } ExFreePool( keyValueInformation ); } // // If we aren't auto rebooting or crashing then return now. // if (!dumpControl && !autoReboot) { return TRUE; } // // Memory crash dumps are enabled. Begin by determining whether or not // the boot device is an internal "AT" type disk drive or a SCSI disk // drive. // // // It actually turns out that simply looking for the string 'scsi' in the // name is not good enough, since it is possible that the device being // booted from is on a SCSI adapter that was gotten to via the BIOS. This // only happens if the system was initially installed by triple-booting, // but since it is so popular, it is handled here. // // The device itself is opened so that IOCTLs can be given to it. This // allows the SCSI get address IOCTL to be issued to determine whether or // not this really is a SCSI device. // // // We only need to go through this work if we are going to take the dump. // if (dumpControl) { // // Note that this allocation MUST MUST MUST be kept in sync with the // allocation in diskdump. No point in creating a defined symbol // since there aren't any shared files. // scratch = ExAllocatePoolWithTag( NonPagedPool, 8*PAGE_SIZE, 'pmuD' ); strcpy( (PCHAR) scratch, "\\ArcName\\" ); partitionName = (PCHAR) scratch + strlen( (PCHAR) scratch ); strcpy( partitionName, LoaderBlock->ArcBootDeviceName ); RtlInitAnsiString( &ansiString, (PCHAR) scratch ); RtlAnsiStringToUnicodeString( &tmpName, &ansiString, TRUE ); InitializeObjectAttributes( &objectAttributes, &tmpName, 0, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtOpenFile( &deviceHandle, FILE_READ_DATA | SYNCHRONIZE, &objectAttributes, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); RtlFreeUnicodeString( &tmpName ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not open boot device partition, %s\n", (PCHAR) scratch ); #endif // DBG ExFreePool( scratch ); return TRUE; } // // Check to see whether or not the system was booted from a SCSI device. // status = NtDeviceIoControlFile( deviceHandle, (HANDLE) NULL, (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &ioStatus, IOCTL_SCSI_GET_ADDRESS, (PVOID) NULL, 0, &scsiAddress, sizeof( SCSI_ADDRESS ) ); if (status == STATUS_PENDING) { (VOID) NtWaitForSingleObject( deviceHandle, FALSE, (PLARGE_INTEGER) NULL ); status = ioStatus.Status; } scsiDump = (BOOLEAN) (NT_SUCCESS( status )); } // // Allocate and initialize the structures necessary to describe and control // the post-bugcheck code. // dcbSize = sizeof( DUMP_CONTROL_BLOCK ) + sizeof( MINIPORT_NODE ); dcb = ExAllocatePoolWithTag( NonPagedPool, dcbSize, 'pmuD' ); if (!dcb) { #if DBG DbgPrint( "IOINIT: Not enough pool to allocate DCB\n" ); #endif // DBG NtClose( deviceHandle ); ExFreePool( scratch ); return FALSE; } RtlZeroMemory( dcb, dcbSize ); dcb->Type = IO_TYPE_DCB; dcb->Size = (USHORT) dcbSize; dcb->Flags = autoReboot ? DCB_AUTO_REBOOT : 0; dcb->NumberProcessors = KeNumberProcessors; dcb->ProcessorArchitecture = KeProcessorArchitecture; dcb->MemoryDescriptor = MmPhysicalMemoryBlock; dcb->MemoryDescriptorChecksum = IopChecksum( MmPhysicalMemoryBlock, sizeof( PHYSICAL_MEMORY_DESCRIPTOR ) - sizeof( PHYSICAL_MEMORY_RUN ) + (MmPhysicalMemoryBlock->NumberOfRuns * sizeof( PHYSICAL_MEMORY_RUN )) ); dcb->LoadedModuleList = &PsLoadedModuleList; InitializeListHead( &dcb->MiniportQueue ); dcb->MinorVersion = (USHORT) NtBuildNumber; dcb->MajorVersion = (USHORT) ((NtBuildNumber >> 28) & 0xfffffff); // // Get pointers to localized text we will need during dump. // if (!KeGetBugMessageText( BUGCODE_PSS_CRASH_INIT, &dcb->PssInitMsg ) || !KeGetBugMessageText( BUGCODE_PSS_CRASH_PROGRESS, &dcb->PssProgressMsg ) || !KeGetBugMessageText( BUGCODE_PSS_CRASH_DONE, &dcb->PssDoneMsg ) ) { #if DBG DbgPrint( "IOINIT: Could not find PSS messages for crash dump.\n", (PCHAR) scratch ); #endif // DBG ExFreePool( dcb ); ExFreePool( scratch ); return FALSE; } if (dumpControl) { // // Determine the disk signature for the device from which the system was // booted and get the partition offset. // status = NtDeviceIoControlFile( deviceHandle, (HANDLE) NULL, (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &ioStatus, IOCTL_DISK_GET_PARTITION_INFO, (PVOID) NULL, 0, &partitionInfo, sizeof( PARTITION_INFORMATION ) ); if (status == STATUS_PENDING) { (VOID) NtWaitForSingleObject( deviceHandle, FALSE, (PLARGE_INTEGER) NULL ); status = ioStatus.Status; } NtClose( deviceHandle ); dcb->PartitionOffset = partitionInfo.StartingOffset; // // Determine the name of the device itself to get the disk's signature. // partitionName = strstr( (PCHAR) scratch, "partition(" ); *partitionName = '\0'; RtlInitAnsiString( &ansiString, (PCHAR) scratch ); RtlAnsiStringToUnicodeString( &tmpName, &ansiString, TRUE ); InitializeObjectAttributes( &objectAttributes, &tmpName, 0, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtOpenFile( &deviceHandle, FILE_READ_DATA | SYNCHRONIZE, &objectAttributes, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); RtlFreeUnicodeString( &tmpName ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not open boot device partition 0, %s\n", (PCHAR) scratch ); #endif // DBG ExFreePool( dcb ); ExFreePool( scratch ); return TRUE; } status = NtDeviceIoControlFile( deviceHandle, (HANDLE) NULL, (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &ioStatus, IOCTL_DISK_GET_DRIVE_LAYOUT, (PVOID) NULL, 0, scratch, PAGE_SIZE ); if (status == STATUS_PENDING) { (VOID) NtWaitForSingleObject( deviceHandle, FALSE, (PLARGE_INTEGER) NULL ); status = ioStatus.Status; } dcb->DiskSignature = ((PDRIVE_LAYOUT_INFORMATION) scratch)->Signature; // // Get the adapter object and base mapping registers for the disk from // the disk driver. These will be used to call the HAL once the system // system has crashed, since it is not possible at that point to recreate // them from scratch. // (VOID) ObReferenceObjectByHandle( deviceHandle, 0, IoFileObjectType, KernelMode, (PVOID *) &fileObject, NULL ); deviceObject = IoGetRelatedDeviceObject( fileObject ); KeInitializeEvent( &event, NotificationEvent, FALSE ); irp = IoBuildDeviceIoControlRequest( IOCTL_SCSI_GET_DUMP_POINTERS, deviceObject, (PVOID) NULL, 0, &dumpPointers, sizeof( DUMP_POINTERS ), FALSE, &event, &ioStatus ); status = IoCallDriver( deviceObject, irp ); if (status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); status = ioStatus.Status; } ObDereferenceObject( fileObject ); NtClose( deviceHandle ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not get dump pointers; error = %x\n", status ); #endif // DBG ExFreePool( dcb ); ExFreePool( scratch ); return TRUE; } dcb->AdapterObject = dumpPointers.AdapterObject; dcb->MappedRegisterBase = dumpPointers.MappedRegisterBase; dcb->PortConfiguration = dumpPointers.PortConfiguration; // // // Scan the list of mapped registers and get both a count of the entries // and produce a checksum. // addresses = * (PMAPPED_ADDRESS *) dcb->MappedRegisterBase; while (addresses) { dcb->MappedAddressCount++; dcb->MappedAddressChecksum += IopChecksum( addresses, sizeof( MAPPED_ADDRESS ) ); addresses = addresses->NextMappedAddress; } // // Scan the list of loaded modules in the system and change their names // so that they do not appear to exist for the purpose of lookups. This // will guarantee that no boot drivers accidentally resolve their names // to components loaded at this point. // nextEntry = PsLoadedModuleList.Flink; savedModuleCount = 0; while (nextEntry != &PsLoadedModuleList) { loaderEntry = CONTAINING_RECORD( nextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); if (savedModuleCount >= 2) { loaderEntry->BaseDllName.MaximumLength = loaderEntry->BaseDllName.Length; loaderEntry->BaseDllName.Length = 0; } savedModuleCount++; nextEntry = nextEntry->Flink; } // // Load the boot disk and port driver to be used by the various // miniports for writing memory to the disk. // RtlInitUnicodeString( &driverName, L"\\SystemRoot\\System32\\Drivers\\diskdump.sys" ); status = MmLoadSystemImage( &driverName, &imageHandle, &imageBaseAddress ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not load diskdump.sys driver; error = %x\n", status ); #endif // DBG IopRevertModuleList( savedModuleCount ); ExFreePool( dcb ); ExFreePool( scratch ); return TRUE; } dcb->DiskDumpDriver = imageHandle; RtlInitUnicodeString( &tmpName, L"scsiport.sys" ); RtlCopyUnicodeString( &imageHandle->BaseDllName, &tmpName ); dcb->DiskDumpChecksum = IopChecksum( imageHandle->DllBase, imageHandle->SizeOfImage ); // // The disk and port dump driver has been loaded. Load the appropriate // miniport driver as well so that the boot device can be accessed. // buffer = ExAllocatePoolWithTag( NonPagedPool, 256, 'pmuD' ); driverName.Length = 0; driverName.Buffer = buffer; driverName.MaximumLength = 256; mpNode = (PMINIPORT_NODE) (dcb + 1); // // Determine whether or not the system booted from SCSI. If so, determine // the name of the miniport driver that is driving the system disk and // load a private copy. // if (scsiDump) { PFILE_OBJECT fileObject; PDRIVER_OBJECT driverObject; PWCHAR mpName; PWCHAR nameOffset; // // The system was booted from SCSI. Get the name of the appropriate // miniport driver and load it. // sprintf( (PCHAR) buffer, "\\Device\\ScsiPort%d", scsiAddress.PortNumber ); RtlInitAnsiString( &ansiString, (PCHAR) buffer ); RtlAnsiStringToUnicodeString( &tmpName, &ansiString, TRUE ); InitializeObjectAttributes( &objectAttributes, &tmpName, 0, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtOpenFile( &deviceHandle, FILE_READ_ATTRIBUTES, &objectAttributes, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); RtlFreeUnicodeString( &tmpName ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not open SCSI port %d, error = %x\n", scsiAddress.PortNumber, status ); #endif // DBG ExFreePool( buffer ); ExFreePool( dcb ); IopRevertModuleList( savedModuleCount ); return TRUE; } // // Convert the file handle into a pointer to the device object, and // get the name of the driver from its driver object. // ObReferenceObjectByHandle( deviceHandle, 0, IoFileObjectType, KernelMode, (PVOID *) &fileObject, NULL ); driverObject = fileObject->DeviceObject->DriverObject; ObDereferenceObject( fileObject ); NtClose( deviceHandle ); // // Loop through the name of the driver looking for the end of the name, // which is the name of the miniport image. // mpName = driverObject->DriverName.Buffer; while ( nameOffset = wcsstr( mpName, L"\\" )) { mpName = ++nameOffset; } RtlAppendUnicodeToString( &driverName, L"\\SystemRoot\\System32\\Drivers\\" ); RtlAppendUnicodeToString( &driverName, mpName ); RtlAppendUnicodeToString( &driverName, L".sys" ); status = MmLoadSystemImage( &driverName, &imageHandle, &imageBaseAddress ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not load miniport driver %wZ\n", &driverName ); #endif // DBG } else { // // The crash dump miniport driver was successfully loaded. // Place the miniport node entry on the list and // initialize it. // InsertTailList( &dcb->MiniportQueue, &mpNode->ListEntry ); mpNode->DriverEntry = imageHandle; mpNode->DriverChecksum = IopChecksum( imageHandle->DllBase, imageHandle->SizeOfImage ); dcb->Flags |= dumpControl; } } else { // // The system was not booted from a SCSI device. For this case, simply // load the general 'AT disk' miniport driver. // RtlAppendUnicodeToString( &driverName, L"\\SystemRoot\\System32\\Drivers\\atapi.sys" ); status = MmLoadSystemImage( &driverName, &imageHandle, &imageBaseAddress ); if (!NT_SUCCESS( status )) { #if DBG DbgPrint( "IOINIT: Could not load dump driver ATAPI.SYS\n" ); #endif // DBG } else { // // The crash dump miniport driver was successfully loaded. Place // the miniport node entry on the list and initialize it. // InsertTailList( &dcb->MiniportQueue, &mpNode->ListEntry ); mpNode->DriverEntry = imageHandle; mpNode->DriverChecksum = IopChecksum( imageHandle->DllBase, imageHandle->SizeOfImage ); dcb->Flags |= dumpControl; } } // // Free the driver name buffer and restore the system's loaded module list. // ExFreePool( buffer ); IopRevertModuleList( savedModuleCount ); // // If everything worked, then allocate the various buffer to be used by // the crash dump code, etc. // if (NT_SUCCESS( status )) { // // If there is an adapter object then call the common buffer routine // to allocate the memory. Otherwise we can just call some "normal" // memory allocation routines. // if (dcb->AdapterObject) { dcb->NonCachedBufferVa1 = HalAllocateCommonBuffer( dcb->AdapterObject, 0x2000, &dcb->NonCachedBufferPa1, FALSE ); if (!dcb->NonCachedBufferVa1) { #if DBG DbgPrint( "IOINIT: Could not allocate 8k noncached buffer\n" ); #endif // DBG ExFreePool( dcb ); dcb = NULL; } else { dcb->NonCachedBufferVa2 = HalAllocateCommonBuffer( dcb->AdapterObject, 0x2000, &dcb->NonCachedBufferPa2, FALSE ); if (!dcb->NonCachedBufferVa2) { #if DBG DbgPrint( "IOINIT: Could not allocate 8k noncached buffer\n" ); #endif // DBG ExFreePool( dcb ); dcb = NULL; } } } else { // // Allocate the various buffers to be used by the crash dump code. // paTemp.QuadPart = 0x1000000 - 1; dcb->NonCachedBufferVa1 = MmAllocateContiguousMemory( 0x2000, paTemp ); if (!dcb->NonCachedBufferVa1) { dcb->NonCachedBufferVa1 = MmAllocateNonCachedMemory( 0x2000 ); } if (!dcb->NonCachedBufferVa1) { #if DBG DbgPrint( "IOINIT: Could not allocate 8k noncached buffer\n" ); #endif // DBG ExFreePool( dcb ); dcb = NULL; } else { paTemp.QuadPart = 0x1000000 - 1; dcb->NonCachedBufferVa2 = MmAllocateContiguousMemory( 0x2000, paTemp ); if (!dcb->NonCachedBufferVa2) { dcb->NonCachedBufferVa2 = MmAllocateNonCachedMemory( 0x2000 ); } if (!dcb->NonCachedBufferVa2) { #if DBG DbgPrint( "IOINIT: Could not allocate 8k noncached buffer\n" ); #endif // DBG ExFreePool( dcb ); dcb = NULL; } } } if (dcb) { // // If we still have a dcb that meant we could allocate our buffers. // // Get the physical addresses that correspond to the virtual addresses // of the buffers. // dcb->NonCachedBufferPa1 = MmGetPhysicalAddress(dcb->NonCachedBufferVa1); dcb->NonCachedBufferPa2 = MmGetPhysicalAddress(dcb->NonCachedBufferVa2); dcb->HeaderPage = ExAllocatePoolWithTag( NonPagedPool, PAGE_SIZE, 'pmuD' ); page = MmGetPhysicalAddress( dcb->HeaderPage ); page.QuadPart >>= PAGE_SHIFT; dcb->HeaderPfn = page.LowPart; dcb->Buffer = scratch; } } } // // Generate the checksum for the entire dump control block structure // itself. // IopDumpControlBlock = dcb; if (dcb) { IopDumpControlBlockChecksum = IopChecksum( dcb, dcbSize ); } 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. --*/ { PHANDLE driverList; PHANDLE savedList; NTSTATUS status; PKEY_VALUE_FULL_INFORMATION keyValueInformation; UNICODE_STRING groupName; PTREE_ENTRY treeEntry; // // 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, until there are // no more drivers in the list. // for (savedList = driverList; *driverList; driverList++) { 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 ) )) { if (treeEntry) { treeEntry->DriversLoaded++; } } } } // // Finally, free the pool that was allocated for the list and return // an indicator the load operation worked. // ExFreePool( (PVOID) savedList ); 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) &deviceNameUnicodeString, status, 0, 0 ); } RtlFreeUnicodeString( &deviceNameUnicodeString ); // // 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 )) { return FALSE; } // // Mark the device object represented by the file object. // fileObject->DeviceObject->Flags |= DO_SYSTEM_BOOT_PARTITION; // // 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 IopRevertModuleList( IN ULONG ListCount ) /*++ Routine Description: This routine is invoked to revert the system's loaded module list to its form before attempt to load dump drivers. Arguments: ListCount - The number of loaded modules to fixup (and keep). Return Value: None. --*/ { PLIST_ENTRY nextEntry; PLDR_DATA_TABLE_ENTRY loaderEntry; // // Scan the loaded module list and revert the base DLL names of all of // the modules. // nextEntry = PsLoadedModuleList.Flink; while (ListCount--) { loaderEntry = CONTAINING_RECORD( nextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); loaderEntry->BaseDllName.Length = loaderEntry->BaseDllName.MaximumLength; if (ListCount) { nextEntry = nextEntry->Flink; } } // // Now remove all of the remaining entries from the list, if any. // while (loaderEntry->InLoadOrderLinks.Flink != &PsLoadedModuleList) { RemoveHeadList( &loaderEntry->InLoadOrderLinks ); } } #ifdef _PNP_POWER_ VOID IopVerifyBuiltInBuses( VOID ) /*++ Routine Description: This routine checks each bus instance as reported for the HalQuerySystemInformation class, HalInstalledBusInformation. Its purpose is to determine whether control for a built-in bus (BIB) has been taken over by an installed bus extender. If this is the case, then the bus list and registry structures are updated accordingly. The caller must have acquired both the bus list AND PnP registry resources for exclusive (write) access. Arguments: None. Return Value: None. --*/ { NTSTATUS Status; PHAL_BUS_INFORMATION BusInfoList; ULONG BusInfoCount, i, TmpDwordValue, ServiceInstanceOrdinal; PWCHAR p, BufferEnd, StringStart; PDRIVER_OBJECT DriverObject; PLUGPLAY_BUS_INSTANCE BusInstToFind; PPLUGPLAY_BUS_ENUMERATOR BusEnumeratorNode, CurBusEnumeratorNode; PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR BusInstanceNode; PLIST_ENTRY CurBusListEntry; BOOLEAN NewBusExtenderFound, DevicePathFound; HANDLE SystemEnumHandle, DevInstHandle, ServiceEnumHandle; UNICODE_STRING TempUnicodeString; PKEY_VALUE_FULL_INFORMATION KeyValueInformation; WCHAR UnicodeBuffer[20]; PFILE_OBJECT FileObject; PBUS_HANDLER BusHandler = NULL; // // First, retrieve the list of installed bus instances. // Status = PiGetInstalledBusInformation(&BusInfoList, &BusInfoCount ); if(!NT_SUCCESS(Status)) { // // Couldn't get info on installed buses--output a debug message and return. // #if DBG DbgPrint("IopVerifyBuiltInBuses: Couldn't get list of installed buses.\n"); #endif // DBG return; } // // Open the HKLM\System\CurrentControlSet\Enum key, to be used as a base handle // for opening device instance keys, should any modifications be required later. // Status = IopOpenRegistryKey(&SystemEnumHandle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_ALL_ACCESS, FALSE ); if(!NT_SUCCESS(Status)) { #if DBG DbgPrint("IopVerifyBuiltInBuses: couldn't open HKLM\\System\\Enum key.\n"); #endif // DBG goto PrepareForReturn; } for(i = 0; i < BusInfoCount; i++) { if (BusHandler) { HalDereferenceBusHandler (BusHandler); } // // Examine this bus instance to see whether it's still being controlled by // the built-in bus extender for which we originally registered it. // BusHandler = HalReferenceHandlerForBus (BusInfoList[i].BusType, BusInfoList[i].BusNumber ); if (BusHandler == NULL) { continue; } else { if (BusHandler->DeviceObject == NULL) { // // Nothing to be done for such a bus instance--continue with next one. // continue; } } #if 0 // // Retrieve the corresponding driver object for this bus instance. // DriverObject = BusInfoList[i].DeviceObject->DriverObject; #endif // // Now find the corresponding bus instance node // BusInstToFind.BusNumber = BusInfoList[i].BusNumber; BusInstToFind.BusType.BusClass = SystemBus; BusInstToFind.BusType.SystemBusType = BusInfoList[i].BusType; Status = PiFindBusInstanceNode(&BusInstToFind, NULL, &BusEnumeratorNode, &BusInstanceNode ); if(!NT_SUCCESS(Status)) { #if 1 // // We couldn't find this bus instance in our list. Apparently a bus extender // registered the bus instance in its DriverEntry. We need to add this bus // instance to the appropriate bus enumerator. // Status = IopRegisterNewBusInstance(BusHandler); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("IopVerifyBuiltInBuses: add new bus instance failed:\n"); DbgPrint(" BusType = %x, BusNumber = %u.\n", (ULONG)BusInfoList[i].BusType, BusInfoList[i].BusNumber ); } #endif // DBG #else // // We couldn't find this bus instance in our list. Apparently, one of // the installable bus extenders isn't playing by the rules (i.e., they're // going out and grabbing non-BIB buses at init time. For now, just output // a debug message, and continue on. // #if DBG DbgPrint("IopVerifyBuiltInBuses: non-registered bus instance found:\n"); DbgPrint(" BusType = %x, BusNumber = %u.\n", (ULONG)BusInfoList[i].BusType, BusInfoList[i].BusNumber ); #endif // DBG #endif continue; } #if 1 // // See if the driver object for the bus instance matches the bus enumerator's // driver object to which it's currently attached. // if (BusEnumeratorNode->DriverName.Length != 0) { // // If EnumeratorNode does not have DriverName, it is a built-in bus enumerator // and its bus instance has been taken over by some bus extender. Otherwise, // the device object of the bus instance should be NULL. // DriverObject = IopReferenceDriverObjectByName (&BusEnumeratorNode->DriverName); if (DriverObject == NULL) { #if DBG DbgPrint("IopVerifyBuiltInBuses: Couldn't reference bus enumerator's driver object by name %wZ\n", BusEnumeratorNode->DriverName.Buffer); #endif // DBG // // Can not reference the driver object by name. The bus extender // must have been unloaded. // If the bus enumerator specified any Plug&Play IDs for buses it // controlled, then we will leave it in our bus list, so that if // enumeration turns up a bus of this type, we will be able to match it // up with its corresponding extender, and then inform the user that // there is a problem with the bus driver for the newly found bus. // if (!BusEnumeratorNode->PlugPlayIDCount) { // // There's no need to keep this around, so remove from the bus // extender list, and free its memory. // RemoveEntryList(&(BusEnumeratorNode->BusEnumeratorListEntry)); ExFreePool(BusEnumeratorNode->ServiceName.Buffer); RtlFreeUnicodeString(&BusEnumeratorNode->DriverName); ExFreePool(BusEnumeratorNode); } continue; } else { if (DriverObject == BusHandler->DeviceObject->DriverObject) { // // The Driver object of the bus instance matches the bus enumerator's // driver object to which it's currently attached. // ObDereferenceObject (DriverObject); continue; } } ObDereferenceObject (DriverObject); } #else // // See if the driver object for the bus instance matches the bus enumerator's // driver object to which it's currently attached. // if(BusEnumeratorNode->DriverObject == DriverObject) { // // Then all's well, and we can continue on with the next bus instance. // continue; } #endif // 1 // // An installable bus extender has taken control away from the built-in // bus extender for this bus instance. Search through our bus enumerator // list, looking for the new controlling driver object. // NewBusExtenderFound = FALSE; for(CurBusListEntry = PpBusListHead.Flink; CurBusListEntry != &PpBusListHead; CurBusListEntry = CurBusListEntry->Flink) { CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry, PLUGPLAY_BUS_ENUMERATOR, BusEnumeratorListEntry ); #if 1 if (RtlEqualUnicodeString(&BusHandler->DeviceObject->DriverObject->DriverExtension->ServiceKeyName, &CurBusEnumeratorNode->ServiceName, TRUE )) { #else if(CurBusEnumeratorNode->DriverObject == DriverObject) { #endif // // We've found the new controlling bus extender for this // bus instance. // NewBusExtenderFound = TRUE; break; } } if(!NewBusExtenderFound) { // // We can't find out who took control of this bus instance, so // just output a debug message and continue with next bus instance. // #if DBG DbgPrint("IopVerifyBuiltInBuses: could not locate new owner of bus\n"); DbgPrint(" instance %wZ\n", &(BusInstanceNode->DeviceInstancePath) ); #endif // DBG continue; } // // Open this device instance key, so that we can modify the information // that has changed. // Status = IopOpenRegistryKey(&DevInstHandle, SystemEnumHandle, &(BusInstanceNode->DeviceInstancePath), KEY_ALL_ACCESS, FALSE ); if(!NT_SUCCESS(Status)) { #if DBG DbgPrint("IopVerifyBuiltInBuses: couldn't open device instance key\n"); DbgPrint(" %wZ for modification.\n", &(BusInstanceNode->DeviceInstancePath) ); #endif // DBG continue; } // // See if the associated configuration space is different now that the new // bus extender has control (unlikely). If so, write out a new // BusDataType value entry. // if(BusInstanceNode->AssociatedConfigurationSpace != BusInfoList[i].ConfigurationType) { #if DBG DbgPrint("IopVerifyBuiltInBuses: ConfigurationType for bus instance %wZ\n", &(BusInstanceNode->DeviceInstancePath) ); DbgPrint(" changed from %x to %x.\n", BusInstanceNode->AssociatedConfigurationSpace, BusInfoList[i].ConfigurationType ); #endif // DBG BusInstanceNode->AssociatedConfigurationSpace = BusInfoList[i].ConfigurationType; TmpDwordValue = (ULONG)(BusInfoList[i].ConfigurationType); PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_BUSDATATYPE); ZwSetValueKey(DevInstHandle, &TempUnicodeString, TITLE_INDEX_VALUE, REG_DWORD, &TmpDwordValue, sizeof(TmpDwordValue) ); } // // Write the name of the new controlling service for this bus instance to the // registry. // PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SERVICE); Status = ZwSetValueKey(DevInstHandle, &TempUnicodeString, TITLE_INDEX_VALUE, REG_SZ, CurBusEnumeratorNode->ServiceName.Buffer, CurBusEnumeratorNode->ServiceName.MaximumLength ); if(!NT_SUCCESS(Status)) { #if DBG DbgPrint("IopVerifyBuiltInBuses: couldn't write service name to registry.\n"); #endif // DBG goto ContinueWithNextBus0; } // // Now, register this device instance, creating a service instance value entry // in the controlling service's volatile Enum subkey. // // Open up the service's Enum subkey. // Status = IopOpenServiceEnumKeys(&(CurBusEnumeratorNode->ServiceName), KEY_ALL_ACCESS, NULL, &ServiceEnumHandle, TRUE ); if(!NT_SUCCESS(Status)) { #if DBG DbgPrint("IopVerifyBuiltInBuses: couldn't open Enum subkey for service:\n"); DbgPrint(" %wZ\n", &(CurBusEnumeratorNode->ServiceName) ); #endif // DBG // // Clean up what we've done, so we don't have dangling registry references. // PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SERVICE); ZwDeleteValueKey(DevInstHandle, &TempUnicodeString); goto ContinueWithNextBus0; } // // Retrieve the next available service instance ordinal. // ServiceInstanceOrdinal = 0; Status = IopGetRegistryValue(ServiceEnumHandle, REGSTR_VALUE_COUNT, &KeyValueInformation ); if(NT_SUCCESS(Status)) { if((KeyValueInformation->Type == REG_DWORD) && (KeyValueInformation->DataLength >= sizeof(ULONG))) { ServiceInstanceOrdinal = *(PULONG)KEY_VALUE_DATA(KeyValueInformation); } ExFreePool(KeyValueInformation); } BusInstanceNode->ServiceInstanceOrdinal = ServiceInstanceOrdinal; // // Add a new service instance entry to the service's Enum list. // PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, sizeof(UnicodeBuffer), ServiceInstanceOrdinal ); Status = ZwSetValueKey(ServiceEnumHandle, &TempUnicodeString, TITLE_INDEX_VALUE, REG_SZ, BusInstanceNode->DeviceInstancePath.Buffer, BusInstanceNode->DeviceInstancePath.MaximumLength ); if(NT_SUCCESS(Status)) { // // Now we can increment the instance count, and save it back. // ServiceInstanceOrdinal++; PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_COUNT); Status = ZwSetValueKey(ServiceEnumHandle, &TempUnicodeString, TITLE_INDEX_VALUE, REG_DWORD, &ServiceInstanceOrdinal, sizeof(ServiceInstanceOrdinal) ); } if(!NT_SUCCESS(Status)) { // // Clean up what we've done, so we don't have dangling registry references. // PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SERVICE); ZwDeleteValueKey(DevInstHandle, &TempUnicodeString); BusInstanceNode->ServiceInstanceOrdinal = PLUGPLAY_NO_INSTANCE; goto ContinueWithNextBus1; } // // The bus extender controlling this bus instance created a device object for it. // We will now find the corresponding NT device path under the service's Enum key, // and move it to its correct location in the NtDevicePaths value entry of the // device instance key. // Status = IopGetRegistryValue(ServiceEnumHandle, REGSTR_VALUE_NTDEVICEPATHS, &KeyValueInformation ); if(NT_SUCCESS(Status)) { if((KeyValueInformation->Type == REG_MULTI_SZ) && (KeyValueInformation->DataLength > sizeof(WCHAR))) { // // Examine each NT device path in this list, and see if its corresponding // device object is the same as the device object for this bus instance. // StringStart = p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation); BufferEnd = (PWCHAR)((PUCHAR)p + KeyValueInformation->DataLength); while(p < BufferEnd) { if(!*p) { if(TmpDwordValue = (PUCHAR)p - (PUCHAR)StringStart) { TempUnicodeString.MaximumLength = (TempUnicodeString.Length = (USHORT)TmpDwordValue) + sizeof(UNICODE_NULL); TempUnicodeString.Buffer = StringStart; // // We have an NT device path, now retrieve the corresponding // device object. // Status = PiGetDeviceObjectFilePointer(&TempUnicodeString, &FileObject ); if(NT_SUCCESS(Status)) { if(FileObject->DeviceObject == BusHandler->DeviceObject) { // // Then we've found the device path for this device object. // DevicePathFound = TRUE; // // Add this device path to the NtDevicePaths value // entry under the device instance key. // Status = IopAppendStringToValueKey(DevInstHandle, REGSTR_VALUE_NT_PHYSICAL_DEVICE_PATHS, &TempUnicodeString, TRUE ); if(NT_SUCCESS(Status)) { // // We successfully added the path to the device // instance key, so now remove it from the service's // Enum key. // IopRemoveStringFromValueKey(ServiceEnumHandle, REGSTR_VALUE_NTDEVICEPATHS, &TempUnicodeString ); } } else { DevicePathFound = FALSE; } ObDereferenceObject(FileObject); if(DevicePathFound) { break; } } StringStart = p + 1; } else { break; } } p++; } } ExFreePool(KeyValueInformation); } // // All the registry modifications are complete. Now remove the bus instance // node from its old bus enumerator list, and insert it into the new bus // enumerator list. // RemoveEntryList(&(BusInstanceNode->BusInstanceListEntry)); InsertTailList(&(CurBusEnumeratorNode->BusInstanceListEntry), &(BusInstanceNode->BusInstanceListEntry) ); ContinueWithNextBus1: ZwClose(ServiceEnumHandle); ContinueWithNextBus0: ZwClose(DevInstHandle); } if (BusHandler) { HalDereferenceBusHandler(BusHandler); } ZwClose(SystemEnumHandle); PrepareForReturn: ExFreePool(BusInfoList); } NTSTATUS IopRegisterNewBusInstance( PBUS_HANDLER BusHandler ) /*++ Routine Description: This routine adds the bus instance specified by BusHandler to the appropriate enumberator in our global enumberator list. The caller must have acquired both the bus list AND PnP registry resources for exclusive (write) access. Arguments: BusHandler - Supplies a Bushandler to specified the new bus instance to be registered. Return Value: NTSTATUS code. --*/ { PLIST_ENTRY currentPnPBusListEntry, currentPnPBusInstance; PPLUGPLAY_BUS_ENUMERATOR curBusEnumerator; PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR curBusInstNode, newBusInstanceNode; PUNICODE_STRING serviceName; UNICODE_STRING deviceInstancePath; BOOLEAN found = FALSE; ULONG count, i; NTSTATUS status; HANDLE serviceHandle, devInstKeyHandle; PKEY_VALUE_FULL_INFORMATION keyValueInformation; PWCHAR p, bufferEnd, stringStart; ULONG stringLength; UNICODE_STRING devicePathString; PFILE_OBJECT fileObject; serviceName = &BusHandler->DeviceObject->DriverObject->DriverExtension->ServiceKeyName; // // Search our enumerator list to find the enumerator for the new bus instance. // for (currentPnPBusListEntry = PpBusListHead.Flink; currentPnPBusListEntry != &PpBusListHead; currentPnPBusListEntry = currentPnPBusListEntry->Flink) { curBusEnumerator = CONTAINING_RECORD(currentPnPBusListEntry, PLUGPLAY_BUS_ENUMERATOR, BusEnumeratorListEntry ); if (RtlEqualUnicodeString(serviceName, &curBusEnumerator->ServiceName, TRUE)) { found = TRUE; break; } } if (!found) { return STATUS_NO_SUCH_DEVICE; } // // Search the device instance path and device instance ordinal for the bus // status = IoQueryDeviceEnumInfo(serviceName, &count); if (!NT_SUCCESS(status)) { return status; } status = IopOpenServiceEnumKeys ( serviceName, KEY_READ, &serviceHandle, NULL, FALSE ); if (!NT_SUCCESS(status)) { return status; } for (i = 0; i < count; i++) { // // Open the subkey specified by ServiceName\Enum instance number under // System\Enum subtree. // status = IopServiceInstanceToDeviceInstance (serviceHandle, NULL, i, NULL, &devInstKeyHandle, KEY_READ ); if (!NT_SUCCESS(status)) { ZwClose(serviceHandle); return status; } // // Retrieve the NT physical device path(s) from the device instance key (this should've been // placed here by the bus extender's calling IoRegisterDevicePath). // status = IopGetRegistryValue(devInstKeyHandle, REGSTR_VALUE_NT_PHYSICAL_DEVICE_PATHS, &keyValueInformation ); if (!NT_SUCCESS(status)) { ZwClose(serviceHandle); ZwClose (devInstKeyHandle); return status; } else if((keyValueInformation->Type != REG_MULTI_SZ) || (keyValueInformation->DataLength <= sizeof(WCHAR))) { ExFreePool(keyValueInformation); ZwClose(serviceHandle); ZwClose (devInstKeyHandle); return STATUS_NO_SUCH_DEVICE; } // // For each device path in this list, retrieve the corresponding device object, // and search for it in the list of installed buses. // found = FALSE; stringStart = p = (PWCHAR)KEY_VALUE_DATA(keyValueInformation); bufferEnd = (PWCHAR)((PUCHAR)p + keyValueInformation->DataLength); while (p < bufferEnd) { if (!*p) { if (stringLength = (PUCHAR)p - (PUCHAR)stringStart) { devicePathString.MaximumLength = (devicePathString.Length = (USHORT)stringLength) + sizeof(UNICODE_NULL); devicePathString.Buffer = stringStart; // // We have an NT device path, now retrieve the corresponding // file object (from which we can get the device object). // status = PiGetDeviceObjectFilePointer(&devicePathString, &fileObject ); if (NT_SUCCESS(status)) { // // Now, compare this device object pointer with bus handler's // device object. // if (fileObject->DeviceObject == BusHandler->DeviceObject) { // // Then we've found the device instance for the specified bus. // found = TRUE; } ObDereferenceObject(fileObject); if (found) { break; } } stringStart = p + 1; } else { break; } } p++; } ExFreePool(keyValueInformation); ZwClose(devInstKeyHandle); if (found) { break; } } if (!found) { ZwClose(serviceHandle); return STATUS_NO_SUCH_DEVICE; } status = IopServiceInstanceToDeviceInstance ( serviceHandle, NULL, i, &deviceInstancePath, NULL, KEY_READ ); ZwClose(serviceHandle); if (!NT_SUCCESS(status)) { return status; } // // Now, create a new PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR for this bus instance, // and add it to the bus enumerator's bus list. // newBusInstanceNode = ExAllocatePool(PagedPool, sizeof(PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR) ); if (!newBusInstanceNode) { return STATUS_INSUFFICIENT_RESOURCES; } if(!IopConcatenateUnicodeStrings(&(newBusInstanceNode->DeviceInstancePath), &deviceInstancePath, NULL)) { ExFreePool(newBusInstanceNode); return STATUS_INSUFFICIENT_RESOURCES; } newBusInstanceNode->RootBus = TRUE; newBusInstanceNode->Processed = FALSE; newBusInstanceNode->ServiceInstanceOrdinal = i; newBusInstanceNode->AssociatedConfigurationSpace = BusHandler->ConfigurationType; newBusInstanceNode->BusInstanceInformation.BusNumber = BusHandler->BusNumber; newBusInstanceNode->BusInstanceInformation.BusType.BusClass = SystemBus; newBusInstanceNode->BusInstanceInformation.BusType.SystemBusType = BusHandler->InterfaceType; PiGetBusDescription(BusHandler->InterfaceType, newBusInstanceNode->BusInstanceInformation.BusName ); // // Finally, insert this node at the end of the bus enumerator's list. // InsertTailList(&(curBusEnumerator->BusInstanceListEntry), &(newBusInstanceNode->BusInstanceListEntry) ); return STATUS_SUCCESS; } #endif // _PNP_POWER_ 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. 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). Return Value: None. --*/ { NTSTATUS status; HANDLE systemHandle, setupHandle; UNICODE_STRING nameString; // // 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 HKLM\SYSTEM key. // status = IopOpenRegistryKey(&systemHandle, NULL, &CmRegistryMachineSystemName, KEY_ALL_ACCESS, FALSE ); 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 = IopOpenRegistryKeyPersist(&setupHandle, systemHandle, &nameString, KEY_ALL_ACCESS, TRUE, 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, NtSystemPartitionDeviceName->Buffer, NtSystemPartitionDeviceName->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); }