/* * PROJECT: ReactOS Kernel * COPYRIGHT: GPL - See COPYING in the top level directory * FILE: ntoskrnl/io/pnpmgr/pnproot.c * PURPOSE: PnP manager root device * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) * Copyright 2007 Herv? Poussineau (hpoussin@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* GLOBALS *******************************************************************/ #define ENUM_NAME_ROOT L"Root" /* DATA **********************************************************************/ typedef struct _PNPROOT_DEVICE { // Entry on device list LIST_ENTRY ListEntry; // Physical Device Object of device PDEVICE_OBJECT Pdo; // Device ID UNICODE_STRING DeviceID; // Instance ID UNICODE_STRING InstanceID; // Device description UNICODE_STRING DeviceDescription; // Resource requirement list PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList; // Associated resource list PCM_RESOURCE_LIST ResourceList; ULONG ResourceListSize; } PNPROOT_DEVICE, *PPNPROOT_DEVICE; typedef enum { dsStopped, dsStarted, dsPaused, dsRemoved, dsSurpriseRemoved } PNPROOT_DEVICE_STATE; typedef struct _PNPROOT_COMMON_DEVICE_EXTENSION { // Wether this device extension is for an FDO or PDO BOOLEAN IsFDO; } PNPROOT_COMMON_DEVICE_EXTENSION, *PPNPROOT_COMMON_DEVICE_EXTENSION; /* Physical Device Object device extension for a child device */ typedef struct _PNPROOT_PDO_DEVICE_EXTENSION { // Common device data PNPROOT_COMMON_DEVICE_EXTENSION Common; // Informations about the device PPNPROOT_DEVICE DeviceInfo; } PNPROOT_PDO_DEVICE_EXTENSION, *PPNPROOT_PDO_DEVICE_EXTENSION; /* Physical Device Object device extension for the Root bus device object */ typedef struct _PNPROOT_FDO_DEVICE_EXTENSION { // Common device data PNPROOT_COMMON_DEVICE_EXTENSION Common; // Lower device object PDEVICE_OBJECT Ldo; // Current state of the driver PNPROOT_DEVICE_STATE State; // Namespace device list LIST_ENTRY DeviceListHead; // Number of (not removed) devices in device list ULONG DeviceListCount; // Lock for namespace device list KGUARDED_MUTEX DeviceListLock; } PNPROOT_FDO_DEVICE_EXTENSION, *PPNPROOT_FDO_DEVICE_EXTENSION; typedef struct _BUFFER { PVOID *Data; PULONG Length; } BUFFER, *PBUFFER; static PDEVICE_OBJECT PnpRootDeviceObject = NULL; /* FUNCTIONS *****************************************************************/ static NTSTATUS LocateChildDevice( IN PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension, IN PCWSTR DeviceId, IN PCWSTR InstanceId, OUT PPNPROOT_DEVICE* ChildDevice) { PPNPROOT_DEVICE Device; UNICODE_STRING DeviceIdU, InstanceIdU; PLIST_ENTRY NextEntry; /* Initialize the strings to compare */ RtlInitUnicodeString(&DeviceIdU, DeviceId); RtlInitUnicodeString(&InstanceIdU, InstanceId); /* Start looping */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &DeviceExtension->DeviceListHead; NextEntry = NextEntry->Flink) { /* Get the entry */ Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry); /* See if the strings match */ if (RtlEqualUnicodeString(&DeviceIdU, &Device->DeviceID, TRUE) && RtlEqualUnicodeString(&InstanceIdU, &Device->InstanceID, TRUE)) { /* They do, so set the pointer and return success */ *ChildDevice = Device; return STATUS_SUCCESS; } } /* No device found */ return STATUS_NO_SUCH_DEVICE; } NTSTATUS PnpRootRegisterDevice( IN PDEVICE_OBJECT DeviceObject) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension = PnpRootDeviceObject->DeviceExtension; PPNPROOT_DEVICE Device; PDEVICE_NODE DeviceNode; PWSTR InstancePath; UNICODE_STRING InstancePathCopy; Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT); if (!Device) return STATUS_NO_MEMORY; DeviceNode = IopGetDeviceNode(DeviceObject); if (!RtlCreateUnicodeString(&InstancePathCopy, DeviceNode->InstancePath.Buffer)) { ExFreePoolWithTag(Device, TAG_PNP_ROOT); return STATUS_NO_MEMORY; } InstancePath = wcsrchr(InstancePathCopy.Buffer, L'\\'); ASSERT(InstancePath); if (!RtlCreateUnicodeString(&Device->InstanceID, InstancePath + 1)) { RtlFreeUnicodeString(&InstancePathCopy); ExFreePoolWithTag(Device, TAG_PNP_ROOT); return STATUS_NO_MEMORY; } InstancePath[0] = UNICODE_NULL; if (!RtlCreateUnicodeString(&Device->DeviceID, InstancePathCopy.Buffer)) { RtlFreeUnicodeString(&InstancePathCopy); RtlFreeUnicodeString(&Device->InstanceID); ExFreePoolWithTag(Device, TAG_PNP_ROOT); return STATUS_NO_MEMORY; } InstancePath[0] = L'\\'; Device->Pdo = DeviceObject; KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock); InsertTailList(&DeviceExtension->DeviceListHead, &Device->ListEntry); DeviceExtension->DeviceListCount++; KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock); RtlFreeUnicodeString(&InstancePathCopy); return STATUS_SUCCESS; } /* Creates a new PnP device for a legacy driver */ NTSTATUS PnpRootCreateDevice( IN PUNICODE_STRING ServiceName, IN OPTIONAL PDRIVER_OBJECT DriverObject, OUT PDEVICE_OBJECT *PhysicalDeviceObject, OUT OPTIONAL PUNICODE_STRING FullInstancePath) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension; WCHAR DevicePath[MAX_PATH + 1]; WCHAR InstancePath[5]; PPNPROOT_DEVICE Device = NULL; NTSTATUS Status; UNICODE_STRING PathSep = RTL_CONSTANT_STRING(L"\\"); ULONG NextInstance; UNICODE_STRING EnumKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_SYSTEMENUM); HANDLE EnumHandle, DeviceKeyHandle = NULL, InstanceKeyHandle; RTL_QUERY_REGISTRY_TABLE QueryTable[2]; OBJECT_ATTRIBUTES ObjectAttributes; DeviceExtension = PnpRootDeviceObject->DeviceExtension; KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock); DPRINT("Creating a PnP root device for service '%wZ'\n", ServiceName); _snwprintf(DevicePath, sizeof(DevicePath) / sizeof(WCHAR), L"%s\\%wZ", REGSTR_KEY_ROOTENUM, ServiceName); /* Initialize a PNPROOT_DEVICE structure */ Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT); if (!Device) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE)); if (!RtlCreateUnicodeString(&Device->DeviceID, DevicePath)) { Status = STATUS_NO_MEMORY; goto cleanup; } Status = IopOpenRegistryKeyEx(&EnumHandle, NULL, &EnumKeyName, KEY_READ); if (NT_SUCCESS(Status)) { InitializeObjectAttributes(&ObjectAttributes, &Device->DeviceID, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, EnumHandle, NULL); Status = ZwCreateKey(&DeviceKeyHandle, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL); ObCloseHandle(EnumHandle, KernelMode); } if (!NT_SUCCESS(Status)) { DPRINT1("Failed to open registry key\n"); goto cleanup; } tryagain: RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].Name = L"NextInstance"; QueryTable[0].EntryContext = &NextInstance; QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)DeviceKeyHandle, QueryTable, NULL, NULL); if (!NT_SUCCESS(Status)) { for (NextInstance = 0; NextInstance <= 9999; NextInstance++) { _snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", NextInstance); Status = LocateChildDevice(DeviceExtension, DevicePath, InstancePath, &Device); if (Status == STATUS_NO_SUCH_DEVICE) break; } if (NextInstance > 9999) { DPRINT1("Too many legacy devices reported for service '%wZ'\n", ServiceName); Status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } } _snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", NextInstance); Status = LocateChildDevice(DeviceExtension, DevicePath, InstancePath, &Device); if (Status != STATUS_NO_SUCH_DEVICE || NextInstance > 9999) { DPRINT1("NextInstance value is corrupt! (%lu)\n", NextInstance); RtlDeleteRegistryValue(RTL_REGISTRY_HANDLE, (PWSTR)DeviceKeyHandle, L"NextInstance"); goto tryagain; } NextInstance++; Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, (PWSTR)DeviceKeyHandle, L"NextInstance", REG_DWORD, &NextInstance, sizeof(NextInstance)); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to write new NextInstance value! (0x%x)\n", Status); goto cleanup; } if (!RtlCreateUnicodeString(&Device->InstanceID, InstancePath)) { Status = STATUS_NO_MEMORY; goto cleanup; } /* Finish creating the instance path in the registry */ InitializeObjectAttributes(&ObjectAttributes, &Device->InstanceID, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, DeviceKeyHandle, NULL); Status = ZwCreateKey(&InstanceKeyHandle, KEY_QUERY_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to create instance path (0x%x)\n", Status); goto cleanup; } /* Just close the handle */ ObCloseHandle(InstanceKeyHandle, KernelMode); if (FullInstancePath) { FullInstancePath->MaximumLength = Device->DeviceID.Length + PathSep.Length + Device->InstanceID.Length; FullInstancePath->Length = 0; FullInstancePath->Buffer = ExAllocatePool(PagedPool, FullInstancePath->MaximumLength); if (!FullInstancePath->Buffer) { Status = STATUS_NO_MEMORY; goto cleanup; } RtlAppendUnicodeStringToString(FullInstancePath, &Device->DeviceID); RtlAppendUnicodeStringToString(FullInstancePath, &PathSep); RtlAppendUnicodeStringToString(FullInstancePath, &Device->InstanceID); } /* Initialize a device object */ Status = IoCreateDevice( DriverObject ? DriverObject : PnpRootDeviceObject->DriverObject, sizeof(PNPROOT_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &Device->Pdo); if (!NT_SUCCESS(Status)) { DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status); Status = STATUS_NO_MEMORY; goto cleanup; } PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension; RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION)); PdoDeviceExtension->Common.IsFDO = FALSE; PdoDeviceExtension->DeviceInfo = Device; Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE; Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING; InsertTailList( &DeviceExtension->DeviceListHead, &Device->ListEntry); DeviceExtension->DeviceListCount++; *PhysicalDeviceObject = Device->Pdo; DPRINT("Created PDO %p (%wZ\\%wZ)\n", *PhysicalDeviceObject, &Device->DeviceID, &Device->InstanceID); Device = NULL; Status = STATUS_SUCCESS; cleanup: KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock); if (Device) { if (Device->Pdo) IoDeleteDevice(Device->Pdo); RtlFreeUnicodeString(&Device->DeviceID); RtlFreeUnicodeString(&Device->InstanceID); ExFreePoolWithTag(Device, TAG_PNP_ROOT); } if (DeviceKeyHandle != NULL) ObCloseHandle(DeviceKeyHandle, KernelMode); return Status; } static NTSTATUS NTAPI QueryStringCallback( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext) { PUNICODE_STRING Destination = (PUNICODE_STRING)EntryContext; UNICODE_STRING Source; if (ValueType != REG_SZ || ValueLength == 0 || ValueLength % sizeof(WCHAR) != 0) { Destination->Length = 0; Destination->MaximumLength = 0; Destination->Buffer = NULL; return STATUS_SUCCESS; } Source.MaximumLength = Source.Length = (USHORT)ValueLength; Source.Buffer = ValueData; return RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &Source, Destination); } static NTSTATUS NTAPI QueryBinaryValueCallback( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext) { PBUFFER Buffer = (PBUFFER)EntryContext; PVOID BinaryValue; if (ValueLength == 0) { *Buffer->Data = NULL; return STATUS_SUCCESS; } BinaryValue = ExAllocatePoolWithTag(PagedPool, ValueLength, TAG_PNP_ROOT); if (BinaryValue == NULL) return STATUS_NO_MEMORY; RtlCopyMemory(BinaryValue, ValueData, ValueLength); *Buffer->Data = BinaryValue; if (Buffer->Length) *Buffer->Length = ValueLength; return STATUS_SUCCESS; } static NTSTATUS EnumerateDevices( IN PDEVICE_OBJECT DeviceObject) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; PKEY_BASIC_INFORMATION KeyInfo = NULL, SubKeyInfo = NULL; UNICODE_STRING LegacyU = RTL_CONSTANT_STRING(L"LEGACY_"); UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_SYSTEMENUM L"\\" REGSTR_KEY_ROOTENUM); UNICODE_STRING SubKeyName; WCHAR DevicePath[MAX_PATH + 1]; RTL_QUERY_REGISTRY_TABLE QueryTable[4]; PPNPROOT_DEVICE Device = NULL; HANDLE KeyHandle = NULL; HANDLE SubKeyHandle = NULL; HANDLE DeviceKeyHandle = NULL; ULONG BufferSize; ULONG ResultSize; ULONG Index1, Index2; BUFFER Buffer1, Buffer2; NTSTATUS Status = STATUS_UNSUCCESSFUL; DPRINT("EnumerateDevices(FDO %p)\n", DeviceObject); DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock); BufferSize = sizeof(KEY_BASIC_INFORMATION) + (MAX_PATH + 1) * sizeof(WCHAR); KeyInfo = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_PNP_ROOT); if (!KeyInfo) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } SubKeyInfo = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_PNP_ROOT); if (!SubKeyInfo) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } Status = IopOpenRegistryKeyEx(&KeyHandle, NULL, &KeyName, KEY_ENUMERATE_SUB_KEYS); if (!NT_SUCCESS(Status)) { DPRINT("IopOpenRegistryKeyEx(%wZ) failed with status 0x%08lx\n", &KeyName, Status); goto cleanup; } /* Devices are sub-sub-keys of 'KeyName'. KeyName is already opened as * KeyHandle. We'll first do a first enumeration to have first level keys, * and an inner one to have the real devices list. */ Index1 = 0; while (TRUE) { Status = ZwEnumerateKey( KeyHandle, Index1, KeyBasicInformation, KeyInfo, BufferSize, &ResultSize); if (Status == STATUS_NO_MORE_ENTRIES) { Status = STATUS_SUCCESS; break; } else if (!NT_SUCCESS(Status)) { DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status); goto cleanup; } /* Terminate the string */ KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = 0; /* Check if it is a legacy driver */ RtlInitUnicodeString(&SubKeyName, KeyInfo->Name); if (RtlPrefixUnicodeString(&LegacyU, &SubKeyName, FALSE)) { DPRINT("Ignoring legacy driver '%wZ'\n", &SubKeyName); Index1++; continue; } /* Open the key */ Status = IopOpenRegistryKeyEx(&SubKeyHandle, KeyHandle, &SubKeyName, KEY_ENUMERATE_SUB_KEYS); if (!NT_SUCCESS(Status)) { DPRINT("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n", &SubKeyName, Status); break; } /* Enumerate the sub-keys */ Index2 = 0; while (TRUE) { Status = ZwEnumerateKey( SubKeyHandle, Index2, KeyBasicInformation, SubKeyInfo, BufferSize, &ResultSize); if (Status == STATUS_NO_MORE_ENTRIES) break; else if (!NT_SUCCESS(Status)) { DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status); break; } /* Terminate the string */ SubKeyInfo->Name[SubKeyInfo->NameLength / sizeof(WCHAR)] = 0; _snwprintf(DevicePath, sizeof(DevicePath) / sizeof(WCHAR), L"%s\\%s", REGSTR_KEY_ROOTENUM, KeyInfo->Name); DPRINT("Found device %S\\%s!\n", DevicePath, SubKeyInfo->Name); if (LocateChildDevice(DeviceExtension, DevicePath, SubKeyInfo->Name, &Device) == STATUS_NO_SUCH_DEVICE) { /* Create a PPNPROOT_DEVICE object, and add if in the list of known devices */ Device = (PPNPROOT_DEVICE)ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT); if (!Device) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE)); /* Fill device ID and instance ID */ if (!RtlCreateUnicodeString(&Device->DeviceID, DevicePath)) { DPRINT1("RtlCreateUnicodeString() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } if (!RtlCreateUnicodeString(&Device->InstanceID, SubKeyInfo->Name)) { DPRINT1("RtlCreateUnicodeString() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } /* Open registry key to fill other informations */ Status = IopOpenRegistryKeyEx(&DeviceKeyHandle, SubKeyHandle, &Device->InstanceID, KEY_READ); if (!NT_SUCCESS(Status)) { DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n", &Device->InstanceID, Status); break; } /* Fill information from the device instance key */ RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].QueryRoutine = QueryStringCallback; QueryTable[0].Name = L"DeviceDesc"; QueryTable[0].EntryContext = &Device->DeviceDescription; RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PCWSTR)DeviceKeyHandle, QueryTable, NULL, NULL); /* Fill information from the LogConf subkey */ Buffer1.Data = (PVOID *)&Device->ResourceRequirementsList; Buffer1.Length = NULL; Buffer2.Data = (PVOID *)&Device->ResourceList; Buffer2.Length = &Device->ResourceListSize; RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; QueryTable[0].Name = L"LogConf"; QueryTable[1].QueryRoutine = QueryBinaryValueCallback; QueryTable[1].Name = L"BasicConfigVector"; QueryTable[1].EntryContext = &Buffer1; QueryTable[2].QueryRoutine = QueryBinaryValueCallback; QueryTable[2].Name = L"BootConfig"; QueryTable[2].EntryContext = &Buffer2; if (!NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PCWSTR)DeviceKeyHandle, QueryTable, NULL, NULL))) { /* Non-fatal error */ DPRINT1("Failed to read the LogConf key for %S\\%S\n", DevicePath, SubKeyInfo->Name); } ZwClose(DeviceKeyHandle); DeviceKeyHandle = NULL; /* Insert the newly created device into the list */ InsertTailList( &DeviceExtension->DeviceListHead, &Device->ListEntry); DeviceExtension->DeviceListCount++; } Device = NULL; Index2++; } ZwClose(SubKeyHandle); SubKeyHandle = NULL; Index1++; } cleanup: if (Device) { /* We have a device that has not been added to device list. We need to clean it up */ /* FIXME */ ExFreePoolWithTag(Device, TAG_PNP_ROOT); } if (DeviceKeyHandle != NULL) ZwClose(DeviceKeyHandle); if (SubKeyHandle != NULL) ZwClose(SubKeyHandle); if (KeyHandle != NULL) ZwClose(KeyHandle); if (KeyInfo) ExFreePoolWithTag(KeyInfo, TAG_PNP_ROOT); if (SubKeyInfo) ExFreePoolWithTag(SubKeyInfo, TAG_PNP_ROOT); KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock); return Status; } /* FUNCTION: Handle IRP_MN_QUERY_DEVICE_RELATIONS IRPs for the root bus device object * ARGUMENTS: * DeviceObject = Pointer to functional device object of the root bus driver * Irp = Pointer to IRP that should be handled * RETURNS: * Status */ static NTSTATUS PnpRootQueryDeviceRelations( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension; PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; PDEVICE_RELATIONS Relations = NULL, OtherRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information; PPNPROOT_DEVICE Device = NULL; ULONG Size; NTSTATUS Status; PLIST_ENTRY NextEntry; DPRINT("PnpRootQueryDeviceRelations(FDO %p, Irp %p)\n", DeviceObject, Irp); Status = EnumerateDevices(DeviceObject); if (!NT_SUCCESS(Status)) { DPRINT("EnumerateDevices() failed with status 0x%08lx\n", Status); return Status; } DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + sizeof(PDEVICE_OBJECT) * DeviceExtension->DeviceListCount; if (OtherRelations) { /* Another bus driver has already created a DEVICE_RELATIONS * structure so we must merge this structure with our own */ Size += sizeof(PDEVICE_OBJECT) * OtherRelations->Count; } Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size); if (!Relations) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; goto cleanup; } RtlZeroMemory(Relations, Size); if (OtherRelations) { Relations->Count = OtherRelations->Count; RtlCopyMemory(Relations->Objects, OtherRelations->Objects, sizeof(PDEVICE_OBJECT) * OtherRelations->Count); } KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock); /* Start looping */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &DeviceExtension->DeviceListHead; NextEntry = NextEntry->Flink) { /* Get the entry */ Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry); if (!Device->Pdo) { /* Create a physical device object for the * device as it does not already have one */ Status = IoCreateDevice( DeviceObject->DriverObject, sizeof(PNPROOT_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &Device->Pdo); if (!NT_SUCCESS(Status)) { DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status); break; } PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension; RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION)); PdoDeviceExtension->Common.IsFDO = FALSE; PdoDeviceExtension->DeviceInfo = Device; Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE; Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING; } /* Reference the physical device object. The PnP manager will dereference it again when it is no longer needed */ ObReferenceObject(Device->Pdo); Relations->Objects[Relations->Count++] = Device->Pdo; } KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock); Irp->IoStatus.Information = (ULONG_PTR)Relations; cleanup: if (!NT_SUCCESS(Status)) { if (OtherRelations) ExFreePool(OtherRelations); if (Relations) ExFreePool(Relations); if (Device && Device->Pdo) { IoDeleteDevice(Device->Pdo); Device->Pdo = NULL; } } return Status; } /* * FUNCTION: Handle Plug and Play IRPs for the root bus device object * ARGUMENTS: * DeviceObject = Pointer to functional device object of the root bus driver * Irp = Pointer to IRP that should be handled * RETURNS: * Status */ static NTSTATUS PnpRootFdoPnpControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpSp; NTSTATUS Status; DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; Status = Irp->IoStatus.Status; IrpSp = IoGetCurrentIrpStackLocation(Irp); switch (IrpSp->MinorFunction) { case IRP_MN_QUERY_DEVICE_RELATIONS: DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n"); Status = PnpRootQueryDeviceRelations(DeviceObject, Irp); break; case IRP_MN_START_DEVICE: DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); if (!IoForwardIrpSynchronously(DeviceExtension->Ldo, Irp)) Status = STATUS_UNSUCCESSFUL; else { Status = Irp->IoStatus.Status; if (NT_SUCCESS(Status)) DeviceExtension->State = dsStarted; } Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; case IRP_MN_STOP_DEVICE: DPRINT("IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n"); /* Root device cannot be stopped */ Irp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; default: DPRINT("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction); break; } if (Status != STATUS_PENDING) { Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status; } static NTSTATUS PdoQueryDeviceRelations( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PDEVICE_RELATIONS Relations; NTSTATUS Status = Irp->IoStatus.Status; if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) return Status; DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / TargetDeviceRelation\n"); Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); if (!Relations) { DPRINT("ExAllocatePoolWithTag() failed\n"); Status = STATUS_NO_MEMORY; } else { ObReferenceObject(DeviceObject); Relations->Count = 1; Relations->Objects[0] = DeviceObject; Status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR)Relations; } return Status; } static NTSTATUS PdoQueryCapabilities( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PDEVICE_CAPABILITIES DeviceCapabilities; DeviceCapabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities; if (DeviceCapabilities->Version != 1) return STATUS_REVISION_MISMATCH; DeviceCapabilities->UniqueID = TRUE; /* FIXME: Fill other fields */ return STATUS_SUCCESS; } static NTSTATUS PdoQueryResources( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension; PCM_RESOURCE_LIST ResourceList; DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; if (DeviceExtension->DeviceInfo->ResourceList) { /* Copy existing resource requirement list */ ResourceList = ExAllocatePool( PagedPool, DeviceExtension->DeviceInfo->ResourceListSize); if (!ResourceList) return STATUS_NO_MEMORY; RtlCopyMemory( ResourceList, DeviceExtension->DeviceInfo->ResourceList, DeviceExtension->DeviceInfo->ResourceListSize); Irp->IoStatus.Information = (ULONG_PTR)ResourceList; return STATUS_SUCCESS; } else { /* No resources so just return without changing the status */ return Irp->IoStatus.Status; } } static NTSTATUS PdoQueryResourceRequirements( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension; PIO_RESOURCE_REQUIREMENTS_LIST ResourceList; DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; if (DeviceExtension->DeviceInfo->ResourceRequirementsList) { /* Copy existing resource requirement list */ ResourceList = ExAllocatePool(PagedPool, DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize); if (!ResourceList) return STATUS_NO_MEMORY; RtlCopyMemory( ResourceList, DeviceExtension->DeviceInfo->ResourceRequirementsList, DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize); Irp->IoStatus.Information = (ULONG_PTR)ResourceList; return STATUS_SUCCESS; } else { /* No resource requirements so just return without changing the status */ return Irp->IoStatus.Status; } } static NTSTATUS PdoQueryDeviceText( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension; DEVICE_TEXT_TYPE DeviceTextType; NTSTATUS Status = Irp->IoStatus.Status; DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; DeviceTextType = IrpSp->Parameters.QueryDeviceText.DeviceTextType; switch (DeviceTextType) { case DeviceTextDescription: { UNICODE_STRING String; DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n"); if (DeviceExtension->DeviceInfo->DeviceDescription.Buffer != NULL) { Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &DeviceExtension->DeviceInfo->DeviceDescription, &String); Irp->IoStatus.Information = (ULONG_PTR)String.Buffer; } break; } case DeviceTextLocationInformation: { DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextLocationInformation\n"); break; } default: { DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown query id type 0x%lx\n", DeviceTextType); } } return Status; } static NTSTATUS PdoQueryId( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension; BUS_QUERY_ID_TYPE IdType; NTSTATUS Status = Irp->IoStatus.Status; DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; IdType = IrpSp->Parameters.QueryId.IdType; switch (IdType) { case BusQueryDeviceID: { UNICODE_STRING String; DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n"); Status = RtlDuplicateUnicodeString( RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &DeviceExtension->DeviceInfo->DeviceID, &String); Irp->IoStatus.Information = (ULONG_PTR)String.Buffer; break; } case BusQueryHardwareIDs: case BusQueryCompatibleIDs: { /* Optional, do nothing */ break; } case BusQueryInstanceID: { UNICODE_STRING String; DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n"); Status = RtlDuplicateUnicodeString( RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &DeviceExtension->DeviceInfo->InstanceID, &String); Irp->IoStatus.Information = (ULONG_PTR)String.Buffer; break; } default: { DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n", IdType); } } return Status; } static NTSTATUS PdoQueryBusInformation( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) { PPNP_BUS_INFORMATION BusInfo; NTSTATUS Status; BusInfo = (PPNP_BUS_INFORMATION)ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION), TAG_PNP_ROOT); if (!BusInfo) Status = STATUS_NO_MEMORY; else { RtlCopyMemory( &BusInfo->BusTypeGuid, &GUID_BUS_TYPE_INTERNAL, sizeof(BusInfo->BusTypeGuid)); BusInfo->LegacyBusType = PNPBus; /* We're the only root bus enumerator on the computer */ BusInfo->BusNumber = 0; Irp->IoStatus.Information = (ULONG_PTR)BusInfo; Status = STATUS_SUCCESS; } return Status; } /* * FUNCTION: Handle Plug and Play IRPs for the child device * ARGUMENTS: * DeviceObject = Pointer to physical device object of the child device * Irp = Pointer to IRP that should be handled * RETURNS: * Status */ static NTSTATUS PnpRootPdoPnpControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension; PPNPROOT_FDO_DEVICE_EXTENSION FdoDeviceExtension; PIO_STACK_LOCATION IrpSp; NTSTATUS Status; DeviceExtension = DeviceObject->DeviceExtension; FdoDeviceExtension = PnpRootDeviceObject->DeviceExtension; Status = Irp->IoStatus.Status; IrpSp = IoGetCurrentIrpStackLocation(Irp); switch (IrpSp->MinorFunction) { case IRP_MN_START_DEVICE: /* 0x00 */ DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_DEVICE_RELATIONS: /* 0x07 */ Status = PdoQueryDeviceRelations(DeviceObject, Irp, IrpSp); break; case IRP_MN_QUERY_CAPABILITIES: /* 0x09 */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n"); Status = PdoQueryCapabilities(DeviceObject, Irp, IrpSp); break; case IRP_MN_QUERY_RESOURCES: /* 0x0a */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n"); Status = PdoQueryResources(DeviceObject, Irp, IrpSp); break; case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: /* 0x0b */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n"); Status = PdoQueryResourceRequirements(DeviceObject, Irp, IrpSp); break; case IRP_MN_QUERY_DEVICE_TEXT: /* 0x0c */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n"); Status = PdoQueryDeviceText(DeviceObject, Irp, IrpSp); break; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* 0x0d */ DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n"); break; case IRP_MN_REMOVE_DEVICE: /* Remove the device from the device list and decrement the device count*/ KeAcquireGuardedMutex(&FdoDeviceExtension->DeviceListLock); RemoveEntryList(&DeviceExtension->DeviceInfo->ListEntry); FdoDeviceExtension->DeviceListCount--; KeReleaseGuardedMutex(&FdoDeviceExtension->DeviceListLock); /* Free some strings we created */ RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->DeviceDescription); RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->DeviceID); RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->InstanceID); /* Free the resource requirements list */ if (DeviceExtension->DeviceInfo->ResourceRequirementsList != NULL) ExFreePool(DeviceExtension->DeviceInfo->ResourceRequirementsList); /* Free the boot resources list */ if (DeviceExtension->DeviceInfo->ResourceList != NULL) ExFreePool(DeviceExtension->DeviceInfo->ResourceList); /* Free the device info */ ExFreePool(DeviceExtension->DeviceInfo); /* Finally, delete the device object */ IoDeleteDevice(DeviceObject); /* Return success */ Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_ID: /* 0x13 */ Status = PdoQueryId(DeviceObject, Irp, IrpSp); break; case IRP_MN_QUERY_PNP_DEVICE_STATE: /* 0x14 */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n"); break; case IRP_MN_QUERY_BUS_INFORMATION: /* 0x15 */ DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_BUS_INFORMATION\n"); Status = PdoQueryBusInformation(DeviceObject, Irp, IrpSp); break; default: DPRINT1("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction); break; } if (Status != STATUS_PENDING) { Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status; } /* * FUNCTION: Handle Plug and Play IRPs * ARGUMENTS: * DeviceObject = Pointer to PDO or FDO * Irp = Pointer to IRP that should be handled * RETURNS: * Status */ static NTSTATUS NTAPI PnpRootPnpControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PPNPROOT_COMMON_DEVICE_EXTENSION DeviceExtension; NTSTATUS Status; DeviceExtension = (PPNPROOT_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; if (DeviceExtension->IsFDO) Status = PnpRootFdoPnpControl(DeviceObject, Irp); else Status = PnpRootPdoPnpControl(DeviceObject, Irp); return Status; } /* * FUNCTION: Handle Power IRPs * ARGUMENTS: * DeviceObject = Pointer to PDO or FDO * Irp = Pointer to IRP that should be handled * RETURNS: * Status */ static NTSTATUS NTAPI PnpRootPowerControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpSp; NTSTATUS Status; DeviceExtension = DeviceObject->DeviceExtension; Status = Irp->IoStatus.Status; IrpSp = IoGetCurrentIrpStackLocation(Irp); if (DeviceExtension->Common.IsFDO) { ASSERT(!DeviceExtension->Common.IsFDO); PoStartNextPowerIrp(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); Status = PoCallDriver(DeviceExtension->Ldo, Irp); } else { switch (IrpSp->MinorFunction) { case IRP_MN_QUERY_POWER: case IRP_MN_SET_POWER: Status = STATUS_SUCCESS; break; } Irp->IoStatus.Status = Status; PoStartNextPowerIrp(Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status; } NTSTATUS NTAPI PnpRootAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) { PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension; NTSTATUS Status; DPRINT("PnpRootAddDevice(DriverObject %p, Pdo %p)\n", DriverObject, PhysicalDeviceObject); if (!PhysicalDeviceObject) { DPRINT("PhysicalDeviceObject 0x%p\n", PhysicalDeviceObject); Status = STATUS_INSUFFICIENT_RESOURCES; KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0); } Status = IoCreateDevice( DriverObject, sizeof(PNPROOT_FDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_BUS_EXTENDER, FILE_DEVICE_SECURE_OPEN, TRUE, &PnpRootDeviceObject); if (!NT_SUCCESS(Status)) { DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status); KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0); } DPRINT("Created FDO %p\n", PnpRootDeviceObject); DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)PnpRootDeviceObject->DeviceExtension; RtlZeroMemory(DeviceExtension, sizeof(PNPROOT_FDO_DEVICE_EXTENSION)); DeviceExtension->Common.IsFDO = TRUE; DeviceExtension->State = dsStopped; InitializeListHead(&DeviceExtension->DeviceListHead); DeviceExtension->DeviceListCount = 0; KeInitializeGuardedMutex(&DeviceExtension->DeviceListLock); Status = IoAttachDeviceToDeviceStackSafe( PnpRootDeviceObject, PhysicalDeviceObject, &DeviceExtension->Ldo); if (!NT_SUCCESS(Status)) { DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0); } PnpRootDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; DPRINT("Done AddDevice()\n"); return STATUS_SUCCESS; } #if MI_TRACE_PFNS PDEVICE_OBJECT IopPfnDumpDeviceObject; NTSTATUS NTAPI PnpRootCreateClose( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { PIO_STACK_LOCATION IoStack; if (DeviceObject != IopPfnDumpDeviceObject) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } IoStack = IoGetCurrentIrpStackLocation(Irp); if (IoStack->MajorFunction == IRP_MJ_CREATE) { MmDumpArmPfnDatabase(TRUE); } Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } #endif NTSTATUS NTAPI PnpRootDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { #if MI_TRACE_PFNS NTSTATUS Status; UNICODE_STRING PfnDumpDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PfnDump"); #endif DPRINT("PnpRootDriverEntry(%p %wZ)\n", DriverObject, RegistryPath); IopRootDriverObject = DriverObject; DriverObject->DriverExtension->AddDevice = PnpRootAddDevice; #if MI_TRACE_PFNS DriverObject->MajorFunction[IRP_MJ_CREATE] = PnpRootCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = PnpRootCreateClose; #endif DriverObject->MajorFunction[IRP_MJ_PNP] = PnpRootPnpControl; DriverObject->MajorFunction[IRP_MJ_POWER] = PnpRootPowerControl; #if MI_TRACE_PFNS Status = IoCreateDevice(DriverObject, 0, &PfnDumpDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &IopPfnDumpDeviceObject); if (!NT_SUCCESS(Status)) { DPRINT1("Creating PFN Dump device failed with %lx\n", Status); } else { IopPfnDumpDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; } #endif return STATUS_SUCCESS; }