[NTOS:IO][NTOS:PNP] Implement PiCallDriverAddDevice
authorVictor Perevertkin <victor.perevertkin@reactos.org>
Thu, 26 Nov 2020 01:39:38 +0000 (04:39 +0300)
committerVictor Perevertkin <victor.perevertkin@reactos.org>
Mon, 4 Jan 2021 13:50:33 +0000 (16:50 +0300)
- Move the driver's name obtaining logic into the IopGetDriverNames
  function
- Create a new PiCallDriverAddDevice instead of PipCallDriverAddDevice
  and move it to pnpmgr/devaction.c file. Move around all its internal
  helpers too
- Support a proper Windows-compatible driver loading order for a PDO
  (lower filters, main service, upper filters, etc.)
- Set a correct Problem for the DeviceNode, in case of an error during
  driver loading
- Check the Start Type for all drivers before loading
- Do not try to load drivers during the early boot stage when there is
  no disk subsystem initialized

ntoskrnl/include/internal/io.h
ntoskrnl/io/iomgr/driver.c
ntoskrnl/io/pnpmgr/devaction.c
ntoskrnl/io/pnpmgr/devnode.c
ntoskrnl/io/pnpmgr/pnpinit.c
ntoskrnl/io/pnpmgr/pnpmgr.c

index ad51265..6500537 100644 (file)
@@ -568,12 +568,9 @@ IopDetectResourceConflict(
 // PNP Routines
 //
 NTSTATUS
-NTAPI
-PipCallDriverAddDevice(
-    IN PDEVICE_NODE DeviceNode,
-    IN BOOLEAN LoadDriver,
-    IN PDRIVER_OBJECT DriverObject
-);
+PiCallDriverAddDevice(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ BOOLEAN LoadDrivers);
 
 NTSTATUS
 NTAPI
@@ -612,6 +609,15 @@ PiInsertDevNode(
     _In_ PDEVICE_NODE DeviceNode,
     _In_ PDEVICE_NODE ParentNode);
 
+VOID
+PiSetDevNodeProblem(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ UINT32 Problem);
+
+VOID
+PiClearDevNodeProblem(
+    _In_ PDEVICE_NODE DeviceNode);
+
 NTSTATUS
 IopFreeDeviceNode(
     IN PDEVICE_NODE DeviceNode
@@ -811,13 +817,6 @@ IopReadyDeviceObjects(
     IN PDRIVER_OBJECT Driver
 );
 
-NTSTATUS
-FASTCALL
-IopInitializeDevice(
-    IN PDEVICE_NODE DeviceNode,
-    IN PDRIVER_OBJECT DriverObject
-);
-
 NTSTATUS
 IopStartDevice(
     IN PDEVICE_NODE DeviceNode
@@ -1110,26 +1109,17 @@ IopDeleteDriver(
     IN PVOID ObjectBody
 );
 
-NTSTATUS
-FASTCALL
-IopGetDriverObject(
-    OUT PDRIVER_OBJECT *DriverObject,
-    IN PUNICODE_STRING ServiceName,
-    IN BOOLEAN FileSystem
-);
-
-NTSTATUS
-FASTCALL
-IopLoadServiceModule(
-    IN PUNICODE_STRING ServiceName,
-    OUT PLDR_DATA_TABLE_ENTRY *ModuleObject
-);
-
 NTSTATUS
 IopLoadDriver(
     _In_ HANDLE ServiceHandle,
     _Out_ PDRIVER_OBJECT *DriverObject);
 
+NTSTATUS
+IopGetDriverNames(
+    _In_ HANDLE ServiceHandle,
+    _Out_ PUNICODE_STRING DriverName,
+    _Out_opt_ PUNICODE_STRING ServiceName);
+
 NTSTATUS
 IopInitializeDriverModule(
     _In_ PLDR_DATA_TABLE_ENTRY ModuleObject,
index 994d737..1e02c1c 100644 (file)
@@ -122,64 +122,127 @@ IopDeleteDriver(IN PVOID ObjectBody)
 }
 
 NTSTATUS
-FASTCALL
-IopGetDriverObject(
-    PDRIVER_OBJECT *DriverObject,
-    PUNICODE_STRING ServiceName,
-    BOOLEAN FileSystem)
+IopGetDriverNames(
+    _In_ HANDLE ServiceHandle,
+    _Out_ PUNICODE_STRING DriverName,
+    _Out_opt_ PUNICODE_STRING ServiceName)
 {
-    PDRIVER_OBJECT Object;
-    UNICODE_STRING Prefix;
-    UNICODE_STRING DriverName;
-    NTSTATUS Status;
+    UNICODE_STRING driverName = {.Buffer = NULL}, serviceName;
+    PKEY_VALUE_FULL_INFORMATION kvInfo;
+    NTSTATUS status;
 
-    DPRINT("IopGetDriverObject(%p '%wZ' %x)\n",
-           DriverObject, ServiceName, FileSystem);
+    PAGED_CODE();
 
-    *DriverObject = NULL;
+    // 1. Check the "ObjectName" field in the driver's registry key (it has the priority)
+    status = IopGetRegistryValue(ServiceHandle, L"ObjectName", &kvInfo);
+    if (NT_SUCCESS(status))
+    {
+        // we're got the ObjectName. Use it to create the DRIVER_OBJECT
+        if (kvInfo->Type != REG_SZ || kvInfo->DataLength == 0)
+        {
+            ExFreePool(kvInfo);
+            return STATUS_ILL_FORMED_SERVICE_ENTRY;
+        }
 
-    /* Create ModuleName string */
-    if (ServiceName == NULL || ServiceName->Buffer == NULL)
-        /* We don't know which DriverObject we have to open */
-        return STATUS_INVALID_PARAMETER_2;
+        driverName.Length = kvInfo->DataLength - sizeof(WCHAR),
+        driverName.MaximumLength = kvInfo->DataLength,
+        driverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, driverName.MaximumLength, TAG_IO);
+        if (!driverName.Buffer)
+        {
+            ExFreePool(kvInfo);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
 
-    if (FileSystem != FALSE)
-        RtlInitUnicodeString(&Prefix, FILESYSTEM_ROOT_NAME);
-    else
-        RtlInitUnicodeString(&Prefix, DRIVER_ROOT_NAME);
+        RtlMoveMemory(driverName.Buffer,
+                      (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
+                      driverName.Length);
+        ExFreePool(kvInfo);
+    }
 
-    DriverName.Length = 0;
-    DriverName.MaximumLength = Prefix.Length + ServiceName->Length + sizeof(UNICODE_NULL);
-    ASSERT(DriverName.MaximumLength > ServiceName->Length);
-    DriverName.Buffer = ExAllocatePoolWithTag(PagedPool, DriverName.MaximumLength, TAG_IO);
-    if (DriverName.Buffer == NULL)
+    // check if we need to get ServiceName as well
+    PKEY_BASIC_INFORMATION basicInfo;
+    if (!NT_SUCCESS(status) || ServiceName != NULL)
     {
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    RtlAppendUnicodeStringToString(&DriverName, &Prefix);
-    RtlAppendUnicodeStringToString(&DriverName, ServiceName);
+        ULONG infoLength;
+        status = ZwQueryKey(ServiceHandle, KeyBasicInformation, NULL, 0, &infoLength);
+        if (status == STATUS_BUFFER_TOO_SMALL)
+        {
+            basicInfo = ExAllocatePoolWithTag(PagedPool, infoLength, TAG_IO);
+            if (!basicInfo)
+            {
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
 
-    DPRINT("Driver name: '%wZ'\n", &DriverName);
+            status = ZwQueryKey(ServiceHandle, KeyBasicInformation, basicInfo, infoLength, &infoLength);
+            if (!NT_SUCCESS(status))
+            {
+                ExFreePoolWithTag(basicInfo, TAG_IO);
+                return status;
+            }
 
-    /* Open driver object */
-    Status = ObReferenceObjectByName(&DriverName,
-                                     OBJ_OPENIF | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, /* Attributes */
-                                     NULL, /* PassedAccessState */
-                                     0, /* DesiredAccess */
-                                     IoDriverObjectType,
-                                     KernelMode,
-                                     NULL, /* ParseContext */
-                                     (PVOID*)&Object);
-    ExFreePoolWithTag(DriverName.Buffer, TAG_IO);
-    if (!NT_SUCCESS(Status))
+            serviceName.Length = basicInfo->NameLength;
+            serviceName.MaximumLength = basicInfo->NameLength;
+            serviceName.Buffer = basicInfo->Name;
+        }
+        else
+        {
+            return NT_SUCCESS(status) ? STATUS_UNSUCCESSFUL : status;
+        }
+    }
+
+    // 2. there is no "ObjectName" - construct it ourselves. Depending on a driver type,
+    // it will be either "\Driver\<ServiceName>" or "\FileSystem\<ServiceName>"
+    if (driverName.Buffer == NULL)
     {
-        DPRINT("Failed to reference driver object, status=0x%08x\n", Status);
-        return Status;
+        status = IopGetRegistryValue(ServiceHandle, L"Type", &kvInfo);
+        if (!NT_SUCCESS(status) || kvInfo->Type != REG_DWORD)
+        {
+            ExFreePool(kvInfo);
+            ExFreePoolWithTag(basicInfo, TAG_IO); // container for serviceName
+            return STATUS_ILL_FORMED_SERVICE_ENTRY;
+        }
+
+        UINT32 driverType;
+        RtlMoveMemory(&driverType, (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset), sizeof(UINT32));
+        ExFreePool(kvInfo);
+
+        driverName.Length = 0;
+        if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
+            driverName.MaximumLength = sizeof(FILESYSTEM_ROOT_NAME) + serviceName.Length;
+        else
+            driverName.MaximumLength = sizeof(DRIVER_ROOT_NAME) + serviceName.Length;
+        driverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, driverName.MaximumLength, TAG_IO);
+        if (!driverName.Buffer)
+        {
+            ExFreePoolWithTag(basicInfo, TAG_IO); // container for serviceName
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
+            RtlAppendUnicodeToString(&driverName, FILESYSTEM_ROOT_NAME);
+        else
+            RtlAppendUnicodeToString(&driverName, DRIVER_ROOT_NAME);
+
+        RtlAppendUnicodeStringToString(&driverName, &serviceName);
     }
 
-    *DriverObject = Object;
+    if (ServiceName)
+    {
+        PWCHAR buf = ExAllocatePoolWithTag(PagedPool, serviceName.Length, TAG_IO);
+        if (!buf)
+        {
+            ExFreePoolWithTag(basicInfo, TAG_IO); // container for serviceName
+            ExFreePoolWithTag(driverName.Buffer, TAG_IO);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        RtlMoveMemory(buf, serviceName.Buffer, serviceName.Length);
+        ServiceName->MaximumLength = serviceName.Length;
+        ServiceName->Length = serviceName.Length;
+        ServiceName->Buffer = buf;
+    }
+    ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
 
-    DPRINT("Driver Object: %p\n", Object);
+    *DriverName = driverName;
 
     return STATUS_SUCCESS;
 }
@@ -317,162 +380,6 @@ IopNormalizeImagePath(
     return STATUS_SUCCESS;
 }
 
-NTSTATUS
-IopQueryServiceSettings(
-    _In_ PUNICODE_STRING ServiceName,
-    _Out_ PUNICODE_STRING ServiceImagePath,
-    _Out_ PULONG ServiceStart)
-{
-    NTSTATUS Status;
-    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
-    UNICODE_STRING CCSName = RTL_CONSTANT_STRING(
-        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
-    HANDLE CCSKey, ServiceKey;
-
-    /* Open CurrentControlSet */
-    Status = IopOpenRegistryKeyEx(&CCSKey, NULL, &CCSName, KEY_READ);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                &CCSName, Status);
-        return Status;
-    }
-
-    /* Open service key */
-    Status = IopOpenRegistryKeyEx(&ServiceKey, CCSKey, ServiceName, KEY_READ);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                ServiceName, Status);
-        ZwClose(CCSKey);
-        return Status;
-    }
-
-    /*
-     * Get information about the service.
-     */
-    RtlZeroMemory(QueryTable, sizeof(QueryTable));
-
-    RtlInitUnicodeString(ServiceImagePath, NULL);
-
-    QueryTable[0].Name = L"Start";
-    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
-    QueryTable[0].EntryContext = ServiceStart;
-
-    QueryTable[1].Name = L"ImagePath";
-    QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
-    QueryTable[1].EntryContext = ServiceImagePath;
-
-    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
-                                    (PWSTR)ServiceKey,
-                                    QueryTable,
-                                    NULL,
-                                    NULL);
-
-    ZwClose(ServiceKey);
-    ZwClose(CCSKey);
-
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("RtlQueryRegistryValues() failed for '%wZ' (Status %lx)\n", ServiceName, Status);
-        return Status;
-    }
-
-    return Status;
-}
-
-/*
- * IopLoadServiceModule
- *
- * Load a module specified by registry settings for service.
- *
- * Parameters
- *    ServiceName
- *       Name of the service to load.
- *
- * Return Value
- *    Status
- */
-NTSTATUS
-FASTCALL
-IopLoadServiceModule(
-    IN PUNICODE_STRING ServiceName,
-    OUT PLDR_DATA_TABLE_ENTRY *ModuleObject)
-{
-    ULONG ServiceStart;
-    UNICODE_STRING ServiceImagePath;
-    NTSTATUS Status;
-    PVOID BaseAddress;
-
-    ASSERT(ExIsResourceAcquiredExclusiveLite(&IopDriverLoadResource));
-    ASSERT(ServiceName->Length);
-    DPRINT("IopLoadServiceModule(%wZ, 0x%p)\n", ServiceName, ModuleObject);
-
-    if (ExpInTextModeSetup)
-    {
-        /* We have no registry, but luckily we know where all the drivers are */
-        DPRINT1("IopLoadServiceModule(%wZ, 0x%p) called in ExpInTextModeSetup mode...\n", ServiceName, ModuleObject);
-
-        /* ServiceStart < 4 is all that matters */
-        ServiceStart = 0;
-
-        /* IopNormalizeImagePath will do all of the work for us if we give it an empty string */
-        RtlInitEmptyUnicodeString(&ServiceImagePath, NULL, 0);
-    }
-    else
-    {
-        Status = IopQueryServiceSettings(ServiceName, &ServiceImagePath, &ServiceStart);
-        if (!NT_SUCCESS(Status))
-        {
-            DPRINT("IopQueryServiceSettings() failed for '%wZ' (Status %lx)\n", ServiceName, Status);
-            return Status;
-        }
-    }
-
-    /*
-     * Normalize the image path for all later processing.
-     */
-    Status = IopNormalizeImagePath(&ServiceImagePath, ServiceName);
-
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
-        return Status;
-    }
-
-    /*
-     * Case for disabled drivers
-     */
-    if (ServiceStart >= 4)
-    {
-        /* We can't load this */
-        Status = STATUS_DRIVER_UNABLE_TO_LOAD;
-    }
-    else
-    {
-        DPRINT("Loading module from %wZ\n", &ServiceImagePath);
-        Status = MmLoadSystemImage(&ServiceImagePath, NULL, NULL, 0, (PVOID)ModuleObject, &BaseAddress);
-        if (NT_SUCCESS(Status))
-        {
-            IopDisplayLoadingMessage(ServiceName);
-        }
-    }
-
-    ExFreePool(ServiceImagePath.Buffer);
-
-    /*
-     * Now check if the module was loaded successfully.
-     */
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT("Module loading failed (Status %x)\n", Status);
-    }
-
-    DPRINT("Module loading (Status %x)\n", Status);
-
-    return Status;
-}
-
 /**
  * @brief      Initialize a loaded driver
  *
@@ -504,112 +411,18 @@ IopInitializeDriverModule(
 
     PAGED_CODE();
 
-    // get the ServiceName
-    PKEY_BASIC_INFORMATION basicInfo;
-    ULONG infoLength;
-    Status = ZwQueryKey(ServiceHandle, KeyBasicInformation, NULL, 0, &infoLength);
-    if (Status == STATUS_BUFFER_TOO_SMALL)
-    {
-        basicInfo = ExAllocatePoolWithTag(PagedPool, infoLength, TAG_IO);
-        if (!basicInfo)
-        {
-            MmUnloadSystemImage(ModuleObject);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-
-        Status = ZwQueryKey(ServiceHandle, KeyBasicInformation, basicInfo, infoLength, &infoLength);
-        if (!NT_SUCCESS(Status))
-        {
-            ExFreePoolWithTag(basicInfo, TAG_IO);
-            MmUnloadSystemImage(ModuleObject);
-            return Status;
-        }
-
-        ServiceName.Length = basicInfo->NameLength;
-        ServiceName.MaximumLength = basicInfo->NameLength;
-        ServiceName.Buffer = basicInfo->Name;
-    }
-    else
+    Status = IopGetDriverNames(ServiceHandle, &DriverName, &ServiceName);
+    if (!NT_SUCCESS(Status))
     {
         MmUnloadSystemImage(ModuleObject);
-        return NT_SUCCESS(Status) ? STATUS_UNSUCCESSFUL : Status;
-    }
-
-    // Make the DriverName field of a DRIVER_OBJECT
-    PKEY_VALUE_FULL_INFORMATION kvInfo;
-
-    // 1. Check the "ObjectName" field in the driver's registry key (it has the priority)
-    Status = IopGetRegistryValue(ServiceHandle, L"ObjectName", &kvInfo);
-    if (NT_SUCCESS(Status))
-    {
-        // we're got the ObjectName. Use it to create the DRIVER_OBJECT
-        if (kvInfo->Type != REG_SZ || kvInfo->DataLength == 0)
-        {
-            ExFreePool(kvInfo);
-            ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
-            MmUnloadSystemImage(ModuleObject);
-            return STATUS_ILL_FORMED_SERVICE_ENTRY;
-        }
-
-        DriverName.Length = kvInfo->DataLength - sizeof(WCHAR),
-        DriverName.MaximumLength = kvInfo->DataLength,
-        DriverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, DriverName.MaximumLength, TAG_IO);
-        if (!DriverName.Buffer)
-        {
-            ExFreePool(kvInfo);
-            ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
-            MmUnloadSystemImage(ModuleObject);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-
-        RtlMoveMemory(DriverName.Buffer,
-                      (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
-                      DriverName.Length);
-        ExFreePool(kvInfo);
-    }
-    else
-    {
-        // 2. there is no "ObjectName" - construct it ourselves. Depending on a driver type,
-        // it will be either "\Driver\<ServiceName>" or "\FileSystem\<ServiceName>"
-
-        Status = IopGetRegistryValue(ServiceHandle, L"Type", &kvInfo);
-        if (!NT_SUCCESS(Status) || kvInfo->Type != REG_DWORD)
-        {
-            ExFreePool(kvInfo);
-            ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
-            MmUnloadSystemImage(ModuleObject);
-            return STATUS_ILL_FORMED_SERVICE_ENTRY;
-        }
-
-        UINT32 driverType;
-        RtlMoveMemory(&driverType, (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset), sizeof(UINT32));
-        ExFreePool(kvInfo);
-
-        DriverName.Length = 0;
-        if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
-            DriverName.MaximumLength = sizeof(FILESYSTEM_ROOT_NAME) + ServiceName.Length;
-        else
-            DriverName.MaximumLength = sizeof(DRIVER_ROOT_NAME) + ServiceName.Length;
-        DriverName.Buffer = ExAllocatePoolWithTag(NonPagedPool, DriverName.MaximumLength, TAG_IO);
-        if (!DriverName.Buffer)
-        {
-            ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
-            MmUnloadSystemImage(ModuleObject);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-
-        if (driverType == SERVICE_RECOGNIZER_DRIVER || driverType == SERVICE_FILE_SYSTEM_DRIVER)
-            RtlAppendUnicodeToString(&DriverName, FILESYSTEM_ROOT_NAME);
-        else
-            RtlAppendUnicodeToString(&DriverName, DRIVER_ROOT_NAME);
-
-        RtlAppendUnicodeStringToString(&DriverName, &ServiceName);
+        return Status;
     }
 
     DPRINT("Driver name: '%wZ'\n", &DriverName);
 
     // obtain the registry path for the DriverInit routine
     PKEY_NAME_INFORMATION nameInfo;
+    ULONG infoLength;
     Status = ZwQueryKey(ServiceHandle, KeyNameInformation, NULL, 0, &infoLength);
     if (Status == STATUS_BUFFER_TOO_SMALL)
     {
@@ -644,7 +457,7 @@ IopInitializeDriverModule(
 
     if (!NT_SUCCESS(Status))
     {
-        ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+        RtlFreeUnicodeString(&ServiceName);
         RtlFreeUnicodeString(&DriverName);
         MmUnloadSystemImage(ModuleObject);
         return Status;
@@ -672,7 +485,7 @@ IopInitializeDriverModule(
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
-        ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+        RtlFreeUnicodeString(&ServiceName);
         RtlFreeUnicodeString(&DriverName);
         MmUnloadSystemImage(ModuleObject);
         DPRINT1("Error while creating driver object \"%wZ\" status %x\n", &DriverName, Status);
@@ -706,7 +519,7 @@ IopInitializeDriverModule(
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(nameInfo, TAG_IO);
-        ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+        RtlFreeUnicodeString(&ServiceName);
         RtlFreeUnicodeString(&DriverName);
         return Status;
     }
@@ -725,7 +538,7 @@ IopInitializeDriverModule(
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
-        ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+        RtlFreeUnicodeString(&ServiceName);
         RtlFreeUnicodeString(&DriverName);
         return Status;
     }
@@ -743,14 +556,14 @@ IopInitializeDriverModule(
         ObMakeTemporaryObject(driverObject);
         ObDereferenceObject(driverObject);
         ExFreePoolWithTag(nameInfo, TAG_IO); // container for RegistryPath
-        ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+        RtlFreeUnicodeString(&ServiceName);
         RtlFreeUnicodeString(&DriverName);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
     /* Copy the name and set it in the driver extension */
     RtlCopyUnicodeString(&serviceKeyName, &ServiceName);
-    ExFreePoolWithTag(basicInfo, TAG_IO); // container for ServiceName
+    RtlFreeUnicodeString(&ServiceName);
     driverObject->DriverExtension->ServiceKeyName = serviceKeyName;
 
     /* Make a copy of the driver name to store in the driver object */
@@ -836,186 +649,6 @@ IopInitializeDriverModule(
     return STATUS_SUCCESS;
 }
 
-/*
- * IopAttachFilterDriversCallback
- *
- * Internal routine used by IopAttachFilterDrivers.
- */
-NTSTATUS
-NTAPI
-IopAttachFilterDriversCallback(
-    PWSTR ValueName,
-    ULONG ValueType,
-    PVOID ValueData,
-    ULONG ValueLength,
-    PVOID Context,
-    PVOID EntryContext)
-{
-    PDEVICE_NODE DeviceNode = Context;
-    UNICODE_STRING ServiceName;
-    PWCHAR Filters;
-    PLDR_DATA_TABLE_ENTRY ModuleObject;
-    PDRIVER_OBJECT DriverObject;
-    NTSTATUS Status;
-
-    /* No filter value present */
-    if (ValueType == REG_NONE)
-        return STATUS_SUCCESS;
-
-    for (Filters = ValueData;
-         ((ULONG_PTR)Filters - (ULONG_PTR)ValueData) < ValueLength &&
-         *Filters != 0;
-         Filters += (ServiceName.Length / sizeof(WCHAR)) + 1)
-    {
-        DPRINT("Filter Driver: %S (%wZ)\n", Filters, &DeviceNode->InstancePath);
-
-        ServiceName.Buffer = Filters;
-        ServiceName.MaximumLength =
-        ServiceName.Length = (USHORT)wcslen(Filters) * sizeof(WCHAR);
-
-        UNICODE_STRING RegistryPath;
-
-        // Make the registry path for the driver
-        RegistryPath.Length = 0;
-        RegistryPath.MaximumLength = sizeof(ServicesKeyName) + ServiceName.Length;
-        RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, RegistryPath.MaximumLength, TAG_IO);
-        if (RegistryPath.Buffer == NULL)
-        {
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-        RtlAppendUnicodeToString(&RegistryPath, ServicesKeyName);
-        RtlAppendUnicodeStringToString(&RegistryPath, &ServiceName);
-
-        HANDLE serviceHandle;
-        Status = IopOpenRegistryKeyEx(&serviceHandle, NULL, &RegistryPath, KEY_READ);
-        RtlFreeUnicodeString(&RegistryPath);
-        if (!NT_SUCCESS(Status))
-        {
-            return Status;
-        }
-
-        Status = IopGetDriverObject(&DriverObject,
-                                    &ServiceName,
-                                    FALSE);
-        if (!NT_SUCCESS(Status))
-        {
-            KeEnterCriticalRegion();
-            ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
-
-            /* Load and initialize the filter driver */
-            Status = IopLoadServiceModule(&ServiceName, &ModuleObject);
-            if (!NT_SUCCESS(Status))
-            {
-                ExReleaseResourceLite(&IopDriverLoadResource);
-                KeLeaveCriticalRegion();
-                ZwClose(serviceHandle);
-                return Status;
-            }
-
-            NTSTATUS driverEntryStatus;
-            Status = IopInitializeDriverModule(ModuleObject,
-                                               serviceHandle,
-                                               &DriverObject,
-                                               &driverEntryStatus);
-
-            ExReleaseResourceLite(&IopDriverLoadResource);
-            KeLeaveCriticalRegion();
-
-            if (!NT_SUCCESS(Status))
-            {
-                ZwClose(serviceHandle);
-                return Status;
-            }
-        }
-
-        ZwClose(serviceHandle);
-
-        Status = IopInitializeDevice(DeviceNode, DriverObject);
-
-        /* Remove extra reference */
-        ObDereferenceObject(DriverObject);
-
-        if (!NT_SUCCESS(Status))
-            return Status;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-/*
- * IopAttachFilterDrivers
- *
- * Load filter drivers for specified device node.
- *
- * Parameters
- *    Lower
- *       Set to TRUE for loading lower level filters or FALSE for upper
- *       level filters.
- */
-NTSTATUS
-FASTCALL
-IopAttachFilterDrivers(
-    PDEVICE_NODE DeviceNode,
-    HANDLE EnumSubKey,
-    HANDLE ClassKey,
-    BOOLEAN Lower)
-{
-    RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { NULL, 0, NULL, NULL, 0, NULL, 0 }, };
-    NTSTATUS Status;
-
-    /*
-     * First load the device filters
-     */
-    QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
-    if (Lower)
-        QueryTable[0].Name = L"LowerFilters";
-    else
-        QueryTable[0].Name = L"UpperFilters";
-    QueryTable[0].Flags = 0;
-    QueryTable[0].DefaultType = REG_NONE;
-
-    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
-                                    (PWSTR)EnumSubKey,
-                                    QueryTable,
-                                    DeviceNode,
-                                    NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("Failed to load device %s filters: %08X\n",
-                Lower ? "lower" : "upper", Status);
-        return Status;
-    }
-
-    QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
-    if (Lower)
-        QueryTable[0].Name = L"LowerFilters";
-    else
-        QueryTable[0].Name = L"UpperFilters";
-    QueryTable[0].EntryContext = NULL;
-    QueryTable[0].Flags = 0;
-    QueryTable[0].DefaultType = REG_NONE;
-
-    if (ClassKey == NULL)
-    {
-        return STATUS_SUCCESS;
-    }
-
-    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
-                                    (PWSTR)ClassKey,
-                                    QueryTable,
-                                    DeviceNode,
-                                    NULL);
-
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("Failed to load class %s filters: %08X\n",
-                Lower ? "lower" : "upper", Status);
-        return Status;
-    }
-
-    return STATUS_SUCCESS;
-}
-
 NTSTATUS
 NTAPI
 MiResolveImageReferences(IN PVOID ImageBase,
index 1734757..12f3e44 100644 (file)
@@ -49,6 +49,8 @@ KSPIN_LOCK IopDeviceActionLock;
 KEVENT PiEnumerationFinished;
 static const WCHAR ServicesKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
 
+#define TAG_PNP_DEVACTION 'aDpP'
+
 /* TYPES *********************************************************************/
 
 typedef struct _DEVICE_ACTION_REQUEST
@@ -60,6 +62,29 @@ typedef struct _DEVICE_ACTION_REQUEST
     DEVICE_ACTION Action;
 } DEVICE_ACTION_REQUEST, *PDEVICE_ACTION_REQUEST;
 
+typedef enum _ADD_DEV_DRIVER_TYPE
+{
+    LowerFilter,
+    LowerClassFilter,
+    DeviceDriver,
+    UpperFilter,
+    UpperClassFilter
+} ADD_DEV_DRIVER_TYPE;
+
+typedef struct _ADD_DEV_DRIVERS_LIST
+{
+    LIST_ENTRY ListEntry;
+    PDRIVER_OBJECT DriverObject;
+    ADD_DEV_DRIVER_TYPE DriverType;
+} ADD_DEV_DRIVERS_LIST, *PADD_DEV_DRIVERS_LIST;
+
+typedef struct _ATTACH_FILTER_DRIVERS_CONTEXT
+{
+    ADD_DEV_DRIVER_TYPE DriverType;
+    PDEVICE_NODE DeviceNode;
+    PLIST_ENTRY DriversListHead;
+} ATTACH_FILTER_DRIVERS_CONTEXT, *PATTACH_FILTER_DRIVERS_CONTEXT;
+
 /* FUNCTIONS *****************************************************************/
 
 PDEVICE_OBJECT
@@ -325,6 +350,500 @@ IopCreateDeviceInstancePath(
     return STATUS_SUCCESS;
 }
 
+/**
+ * @brief      Loads and/or returns the driver associated with the registry entry if the driver
+ *             is enabled. In case of an error, sets up a corresponding Problem to the DeviceNode
+ */
+static
+NTSTATUS
+NTAPI
+PiAttachFilterDriversCallback(
+    PWSTR ValueName,
+    ULONG ValueType,
+    PVOID ValueData,
+    ULONG ValueLength,
+    PVOID Ctx,
+    PVOID EntryContext)
+{
+    PATTACH_FILTER_DRIVERS_CONTEXT context = Ctx;
+    PDRIVER_OBJECT DriverObject;
+    NTSTATUS Status;
+    BOOLEAN loadDrivers = (BOOLEAN)(ULONG_PTR)EntryContext;
+
+    PAGED_CODE();
+
+    // No filter value present
+    if (ValueType != REG_SZ)
+        return STATUS_SUCCESS;
+
+    if (ValueLength <= sizeof(WCHAR))
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+
+    // open the service registry key
+    UNICODE_STRING serviceName = { .Length = 0 }, servicesKeyName;
+    RtlInitUnicodeString(&serviceName, ValueData);
+    RtlInitUnicodeString(&servicesKeyName, ServicesKeyName);
+
+    HANDLE ccsServicesHandle, serviceHandle = NULL;
+
+    Status = IopOpenRegistryKeyEx(&ccsServicesHandle, NULL, &servicesKeyName, KEY_READ);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open a registry key for \"%wZ\" (status %x)\n", &serviceName, Status);
+        return Status;
+    }
+
+    Status = IopOpenRegistryKeyEx(&serviceHandle, ccsServicesHandle, &serviceName, KEY_READ);
+    ZwClose(ccsServicesHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open a registry key for \"%wZ\" (status %x)\n", &serviceName, Status);
+        return Status;
+    }
+
+    PADD_DEV_DRIVERS_LIST driverEntry = ExAllocatePoolWithTag(PagedPool,
+                                                              sizeof(*driverEntry),
+                                                              TAG_PNP_DEVACTION);
+
+    if (!driverEntry)
+    {
+        DPRINT1("Failed to allocate driverEntry for \"%wZ\"\n", &serviceName);
+        ZwClose(serviceHandle);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // check if the driver is disabled
+    PKEY_VALUE_FULL_INFORMATION kvInfo;
+    SERVICE_LOAD_TYPE startType = DisableLoad;
+
+    Status = IopGetRegistryValue(serviceHandle, L"Start", &kvInfo);
+    if (NT_SUCCESS(Status) && kvInfo->Type == REG_DWORD)
+    {
+        RtlMoveMemory(&startType,
+                      (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
+                      sizeof(startType));
+        ExFreePool(kvInfo);
+    }
+
+    // TODO: take into account other start types (like SERVICE_DEMAND_START)
+    if (startType >= DisableLoad)
+    {
+        if (!(context->DeviceNode->Flags & DNF_HAS_PROBLEM))
+        {
+            PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DISABLED_SERVICE);
+        }
+
+        DPRINT("Service \"%wZ\" is disabled (start type %u)\n", &serviceName, startType);
+        Status = STATUS_UNSUCCESSFUL;
+        goto Cleanup;
+    }
+
+    // check if the driver is already loaded
+    UNICODE_STRING driverName;
+    Status = IopGetDriverNames(serviceHandle, &driverName, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Unable to obtain the driver name for \"%wZ\"\n", &serviceName);
+        goto Cleanup;
+    }
+
+    // try to open it
+    Status = ObReferenceObjectByName(&driverName,
+                                     OBJ_OPENIF | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                                     NULL, /* PassedAccessState */
+                                     0, /* DesiredAccess */
+                                     IoDriverObjectType,
+                                     KernelMode,
+                                     NULL, /* ParseContext */
+                                     (PVOID*)&DriverObject);
+    RtlFreeUnicodeString(&driverName);
+
+    // the driver was not probably loaded, try to load
+    if (!NT_SUCCESS(Status))
+    {
+        if (loadDrivers)
+        {
+            Status = IopLoadDriver(serviceHandle, &DriverObject);
+        }
+        else
+        {
+            DPRINT("Service \"%wZ\" will not be loaded now\n", &serviceName);
+            // return failure, the driver will be loaded later (in a subsequent call)
+            Status = STATUS_UNSUCCESSFUL;
+            goto Cleanup;
+        }
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        driverEntry->DriverObject = DriverObject;
+        driverEntry->DriverType = context->DriverType;
+        InsertTailList(context->DriversListHead, &driverEntry->ListEntry);
+        ZwClose(serviceHandle);
+        return STATUS_SUCCESS;
+    }
+    else
+    {
+        if (!(context->DeviceNode->Flags & DNF_HAS_PROBLEM))
+        {
+            switch (Status)
+            {
+                case STATUS_INSUFFICIENT_RESOURCES:
+                    PiSetDevNodeProblem(context->DeviceNode, CM_PROB_OUT_OF_MEMORY);
+                    break;
+                case STATUS_FAILED_DRIVER_ENTRY:
+                    PiSetDevNodeProblem(context->DeviceNode, CM_PROB_FAILED_DRIVER_ENTRY);
+                    break;
+                case STATUS_ILL_FORMED_SERVICE_ENTRY:
+                    PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DRIVER_SERVICE_KEY_INVALID);
+                    break;
+                default:
+                    PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DRIVER_FAILED_LOAD);
+                    break;
+            }
+        }
+
+        DPRINT1("Failed to load driver \"%wZ\" for %wZ (status %x)\n",
+            &serviceName, &context->DeviceNode->InstancePath, Status);
+    }
+
+Cleanup:
+    ExFreePoolWithTag(driverEntry, TAG_PNP_DEVACTION);
+    if (serviceHandle)
+    {
+        ZwClose(serviceHandle);
+    }
+    return Status;
+}
+
+
+/**
+ * @brief      Calls PiAttachFilterDriversCallback for filter drivers (if any)
+ */
+static
+NTSTATUS
+PiAttachFilterDrivers(
+    PLIST_ENTRY DriversListHead,
+    PDEVICE_NODE DeviceNode,
+    HANDLE EnumSubKey,
+    HANDLE ClassKey,
+    BOOLEAN Lower,
+    BOOLEAN LoadDrivers)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { NULL, 0, NULL, NULL, 0, NULL, 0 }, };
+    ATTACH_FILTER_DRIVERS_CONTEXT routineContext;
+    NTSTATUS Status;
+
+    PAGED_CODE();
+
+    routineContext.DriversListHead = DriversListHead;
+    routineContext.DeviceNode = DeviceNode;
+
+    // First add device filters
+    routineContext.DriverType = Lower ? LowerFilter : UpperFilter;
+    QueryTable[0] = (RTL_QUERY_REGISTRY_TABLE){
+        .QueryRoutine = PiAttachFilterDriversCallback,
+        .Name = Lower ? L"LowerFilters" : L"UpperFilters",
+        .DefaultType = REG_NONE,
+        .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
+    };
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+                                    (PWSTR)EnumSubKey,
+                                    QueryTable,
+                                    &routineContext,
+                                    NULL);
+    if (ClassKey == NULL)
+    {
+        return Status;
+    }
+
+    // Then add device class filters
+    routineContext.DriverType = Lower ? LowerClassFilter : UpperClassFilter;
+    QueryTable[0] = (RTL_QUERY_REGISTRY_TABLE){
+        .QueryRoutine = PiAttachFilterDriversCallback,
+        .Name = Lower ? L"LowerFilters" : L"UpperFilters",
+        .DefaultType = REG_NONE,
+        .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
+    };
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+                                    (PWSTR)ClassKey,
+                                    QueryTable,
+                                    &routineContext,
+                                    NULL);
+    return Status;
+}
+
+/**
+ * @brief      Loads all drivers for a device node (actual service and filters)
+ *             and calls their AddDevice routine
+ *
+ * @param[in]  DeviceNode   The device node
+ * @param[in]  LoadDrivers  Whether to load drivers if they are not loaded yet
+ *                          (used when storage subsystem is not yet initialized)
+ */
+NTSTATUS
+PiCallDriverAddDevice(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ BOOLEAN LoadDrivers)
+{
+    NTSTATUS Status;
+    HANDLE EnumRootKey, SubKey;
+    HANDLE ClassKey = NULL;
+    UNICODE_STRING EnumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
+    static UNICODE_STRING ccsControlClass =
+    RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
+    PKEY_VALUE_FULL_INFORMATION kvInfo = NULL;
+
+    PAGED_CODE();
+
+    // open the enumeration root key
+    Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL, &EnumRoot, KEY_READ);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n", &EnumRoot, Status);
+        return Status;
+    }
+
+    // open an instance subkey
+    Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey, &DeviceNode->InstancePath, KEY_READ);
+    ZwClose(EnumRootKey);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open a devnode instance key for \"%wZ\" (status %x)\n",
+                &DeviceNode->InstancePath, Status);
+        return Status;
+    }
+
+    // try to get the class GUID of an instance and its registry key
+    Status = IopGetRegistryValue(SubKey, REGSTR_VAL_CLASSGUID, &kvInfo);
+    if (NT_SUCCESS(Status) && kvInfo->Type == REG_SZ && kvInfo->DataLength > sizeof(WCHAR))
+    {
+        UNICODE_STRING classGUID = {
+            .MaximumLength = kvInfo->DataLength,
+            .Length = kvInfo->DataLength - sizeof(UNICODE_NULL),
+            .Buffer = (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset)
+        };
+        HANDLE ccsControlHandle;
+
+        Status = IopOpenRegistryKeyEx(&ccsControlHandle, NULL, &ccsControlClass, KEY_READ);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n",
+                    &ccsControlClass, Status);
+        }
+        else
+        {
+            // open the CCS\Constol\Class\<ClassGUID> key
+            Status = IopOpenRegistryKeyEx(&ClassKey, ccsControlHandle, &classGUID, KEY_READ);
+            ZwClose(ccsControlHandle);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("Failed to open class key \"%wZ\" (status %x)\n", &classGUID, Status);
+            }
+        }
+
+        if (ClassKey)
+        {
+            // Check the Properties key of a class too
+            // Windows fills some device properties from this key (which is protected)
+            // TODO: add the device properties from this key
+
+            UNICODE_STRING properties = RTL_CONSTANT_STRING(REGSTR_KEY_DEVICE_PROPERTIES);
+            HANDLE propertiesHandle;
+
+            Status = IopOpenRegistryKeyEx(&propertiesHandle, ClassKey, &properties, KEY_READ);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT("Properties key failed to open for \"%wZ\" (status %x)\n",
+                       &classGUID, Status);
+            }
+            else
+            {
+                ZwClose(propertiesHandle);
+            }
+        }
+        ExFreePool(kvInfo);
+    }
+
+    // the driver loading order:
+    // 1. LowerFilters
+    // 2. LowerClassFilters
+    // 3. Device driver (only one service!)
+    // 4. UpperFilters
+    // 5. UpperClassFilters
+
+    LIST_ENTRY drvListHead;
+    InitializeListHead(&drvListHead);
+
+    // lower (class) filters
+    Status = PiAttachFilterDrivers(&drvListHead, DeviceNode, SubKey, ClassKey, TRUE, LoadDrivers);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    ATTACH_FILTER_DRIVERS_CONTEXT routineContext = {
+        .DriversListHead = &drvListHead,
+        .DriverType = DeviceDriver,
+        .DeviceNode = DeviceNode
+    };
+
+    RTL_QUERY_REGISTRY_TABLE queryTable[2] = {{
+        .QueryRoutine = PiAttachFilterDriversCallback,
+        .Name = L"Service",
+        .Flags = RTL_QUERY_REGISTRY_REQUIRED,
+        .DefaultType = REG_SZ, // REG_MULTI_SZ is not allowed here
+        .DefaultData = L"",
+        .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
+    },};
+
+    // device driver
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+                                    (PWSTR)SubKey,
+                                    queryTable,
+                                    &routineContext,
+                                    NULL);
+    if (NT_SUCCESS(Status))
+    {
+        // do nothing
+    }
+    // if a driver is not found, but a device allows raw access -> proceed
+    else if (Status == STATUS_OBJECT_NAME_NOT_FOUND &&
+             (DeviceNode->CapabilityFlags & 0x00000040)) // CM_DEVCAP_RAWDEVICEOK
+    {
+        // add a dummy entry to the drivers list (need for later processing)
+        PADD_DEV_DRIVERS_LIST driverEntry = ExAllocatePoolZero(PagedPool,
+                                                               sizeof(*driverEntry),
+                                                               TAG_PNP_DEVACTION);
+        driverEntry->DriverType = DeviceDriver;
+        InsertTailList(&drvListHead, &driverEntry->ListEntry);
+        DPRINT("No service for \"%wZ\" (RawDeviceOK)\n", &DeviceNode->InstancePath);
+    }
+    else
+    {
+        if (Status == STATUS_OBJECT_TYPE_MISMATCH && !(DeviceNode->Flags & DNF_HAS_PROBLEM))
+        {
+            PiSetDevNodeProblem(DeviceNode, CM_PROB_REGISTRY);
+        }
+        DPRINT("No service for \"%wZ\" (loadDrv: %u)\n", &DeviceNode->InstancePath, LoadDrivers);
+        goto Cleanup;
+    }
+
+    // upper (class) filters
+    Status = PiAttachFilterDrivers(&drvListHead, DeviceNode, SubKey, ClassKey, FALSE, LoadDrivers);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    // finally loop through the stack and call AddDevice for every driver
+    for (PLIST_ENTRY listEntry = drvListHead.Flink;
+         listEntry != &drvListHead;
+         listEntry = listEntry->Flink)
+    {
+        PADD_DEV_DRIVERS_LIST driverEntry;
+        driverEntry = CONTAINING_RECORD(listEntry, ADD_DEV_DRIVERS_LIST, ListEntry);
+        PDRIVER_OBJECT driverObject = driverEntry->DriverObject;
+
+        // FIXME: ReactOS is not quite ready for this assert
+        // (legacy drivers should not have AddDevice routine)
+        // ASSERT(!(DriverObject->Flags & DRVO_LEGACY_DRIVER));
+
+        if (driverObject && driverObject->DriverExtension->AddDevice)
+        {
+            Status = driverObject->DriverExtension->AddDevice(driverEntry->DriverObject,
+                                                              DeviceNode->PhysicalDeviceObject);
+        }
+        else if (driverObject == NULL)
+        {
+            // valid only for DeviceDriver
+            ASSERT(driverEntry->DriverType == DeviceDriver);
+            ASSERT(DeviceNode->CapabilityFlags & 0x00000040); // CM_DEVCAP_RAWDEVICEOK
+            Status = STATUS_SUCCESS;
+        }
+        else
+        {
+            // HACK: the driver doesn't have a AddDevice routine. We shouldn't be here,
+            // but ReactOS' PnP stack is not that correct yet
+            DeviceNode->Flags |= DNF_LEGACY_DRIVER;
+            Status = STATUS_UNSUCCESSFUL;
+        }
+
+        // for filter drivers we don't care about the AddDevice result
+        if (driverEntry->DriverType == DeviceDriver)
+        {
+            if (NT_SUCCESS(Status))
+            {
+                PDEVICE_OBJECT fdo = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
+
+                // HACK: Check if we have a ACPI device (needed for power management)
+                if (fdo->DeviceType == FILE_DEVICE_ACPI)
+                {
+                    static BOOLEAN SystemPowerDeviceNodeCreated = FALSE;
+
+                    // There can be only one system power device
+                    if (!SystemPowerDeviceNodeCreated)
+                    {
+                        PopSystemPowerDeviceNode = DeviceNode;
+                        ObReferenceObject(PopSystemPowerDeviceNode->PhysicalDeviceObject);
+                        SystemPowerDeviceNodeCreated = TRUE;
+                    }
+                }
+
+                ObDereferenceObject(fdo);
+
+                IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
+            }
+            else
+            {
+                // lower filters (if already started) will be removed upon this request
+                PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_ADD);
+                IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
+                IopRemoveDevice(DeviceNode);
+                break;
+            }
+        }
+
+#if DBG
+        PDEVICE_OBJECT attachedDO = IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject);
+        if (attachedDO->Flags & DO_DEVICE_INITIALIZING)
+        {
+            DPRINT1("DO_DEVICE_INITIALIZING is not cleared on a device 0x%p!\n", attachedDO);
+        }
+#endif
+    }
+
+Cleanup:
+    while (!IsListEmpty(&drvListHead))
+    {
+        PLIST_ENTRY listEntry = RemoveHeadList(&drvListHead);
+        PADD_DEV_DRIVERS_LIST driverEntry;
+        driverEntry = CONTAINING_RECORD(listEntry, ADD_DEV_DRIVERS_LIST, ListEntry);
+
+        // drivers which don't have any devices (in case of failure) will be cleaned up
+        if (driverEntry->DriverObject)
+        {
+            ObDereferenceObject(driverEntry->DriverObject);
+        }
+        ExFreePoolWithTag(driverEntry, TAG_PNP_DEVACTION);
+    }
+
+    ZwClose(SubKey);
+    if (ClassKey != NULL)
+    {
+        ZwClose(ClassKey);
+    }
+
+    if (DeviceNode->Flags & DNF_ADDED)
+    {
+        IopStartDevice(DeviceNode);
+    }
+
+    return Status;
+}
+
 NTSTATUS
 NTAPI
 IopQueryDeviceCapabilities(PDEVICE_NODE DeviceNode,
@@ -1008,8 +1527,6 @@ IopActionInitChildServices(PDEVICE_NODE DeviceNode,
                            PVOID Context)
 {
     PDEVICE_NODE ParentDeviceNode;
-    NTSTATUS Status;
-    BOOLEAN BootDrivers = !PnpSystemInit;
 
     DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
 
@@ -1043,114 +1560,7 @@ IopActionInitChildServices(PDEVICE_NODE DeviceNode,
         IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
         return STATUS_SUCCESS;
 
-    if (DeviceNode->ServiceName.Buffer == NULL)
-    {
-        /* We don't need to worry about loading the driver because we're
-         * being driven in raw mode so our parent must be loaded to get here */
-        Status = IopInitializeDevice(DeviceNode, NULL);
-        if (NT_SUCCESS(Status))
-        {
-            Status = IopStartDevice(DeviceNode);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("IopStartDevice(%wZ) failed with status 0x%08x\n",
-                        &DeviceNode->InstancePath, Status);
-            }
-        }
-    }
-    else
-    {
-        PLDR_DATA_TABLE_ENTRY ModuleObject;
-        PDRIVER_OBJECT DriverObject;
-
-        /* Get existing DriverObject pointer (in case the driver has
-           already been loaded and initialized) */
-        Status = IopGetDriverObject(
-            &DriverObject,
-            &DeviceNode->ServiceName,
-            FALSE);
-
-        if (!NT_SUCCESS(Status))
-        {
-            KeEnterCriticalRegion();
-            ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
-
-            /* Driver is not initialized, try to load it */
-            Status = IopLoadServiceModule(&DeviceNode->ServiceName, &ModuleObject);
-
-            if (NT_SUCCESS(Status) || Status == STATUS_IMAGE_ALREADY_LOADED)
-            {
-                UNICODE_STRING RegistryPath;
-
-                // obtain a handle for driver's RegistryPath
-                RegistryPath.Length = 0;
-                RegistryPath.MaximumLength = sizeof(ServicesKeyName) + DeviceNode->ServiceName.Length;
-                RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, RegistryPath.MaximumLength, TAG_IO);
-                if (RegistryPath.Buffer == NULL)
-                {
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto OpenHandleFail;
-                }
-                RtlAppendUnicodeToString(&RegistryPath, ServicesKeyName);
-                RtlAppendUnicodeStringToString(&RegistryPath, &DeviceNode->ServiceName);
-
-                HANDLE serviceHandle;
-                Status = IopOpenRegistryKeyEx(&serviceHandle, NULL, &RegistryPath, KEY_READ);
-                RtlFreeUnicodeString(&RegistryPath);
-                if (!NT_SUCCESS(Status))
-                {
-                    goto OpenHandleFail;
-                }
-
-                /* Initialize the driver */
-                NTSTATUS driverEntryStatus;
-                Status = IopInitializeDriverModule(ModuleObject,
-                                                   serviceHandle,
-                                                   &DriverObject,
-                                                   &driverEntryStatus);
-                ZwClose(serviceHandle);
-
-                if (!NT_SUCCESS(Status))
-                    DeviceNode->Problem = CM_PROB_FAILED_DRIVER_ENTRY;
-            }
-            else if (Status == STATUS_DRIVER_UNABLE_TO_LOAD)
-            {
-                DPRINT1("Service '%wZ' is disabled\n", &DeviceNode->ServiceName);
-                DeviceNode->Problem = CM_PROB_DISABLED_SERVICE;
-            }
-            else
-            {
-                DPRINT("IopLoadServiceModule(%wZ) failed with status 0x%08x\n",
-                       &DeviceNode->ServiceName, Status);
-                if (!BootDrivers)
-                    DeviceNode->Problem = CM_PROB_DRIVER_FAILED_LOAD;
-            }
-OpenHandleFail:
-            ExReleaseResourceLite(&IopDriverLoadResource);
-            KeLeaveCriticalRegion();
-        }
-
-        /* Driver is loaded and initialized at this point */
-        if (NT_SUCCESS(Status))
-        {
-            /* Initialize the device, including all filters */
-            Status = PipCallDriverAddDevice(DeviceNode, FALSE, DriverObject);
-
-            /* Remove the extra reference */
-            ObDereferenceObject(DriverObject);
-        }
-        else
-        {
-            /*
-             * Don't disable when trying to load only boot drivers
-             */
-            if (!BootDrivers)
-            {
-                IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-            }
-        }
-    }
-
+    PiCallDriverAddDevice(DeviceNode, PnPBootDriversLoaded);
     return STATUS_SUCCESS;
 }
 
index 0442e17..090419f 100644 (file)
@@ -100,6 +100,23 @@ PiInsertDevNode(
     DeviceNode->Level = ParentNode->Level + 1;
 }
 
+VOID
+PiSetDevNodeProblem(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ UINT32 Problem)
+{
+    DeviceNode->Flags |= DNF_HAS_PROBLEM;
+    DeviceNode->Problem = Problem;
+}
+
+VOID
+PiClearDevNodeProblem(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    DeviceNode->Flags &= ~DNF_HAS_PROBLEM;
+    DeviceNode->Problem = 0;
+}
+
 /**
  * @brief      Creates a device node
  *
index dd33a4b..e32c11e 100644 (file)
@@ -286,145 +286,6 @@ Quickie:
     return i;
 }
 
-NTSTATUS
-NTAPI
-PipCallDriverAddDevice(IN PDEVICE_NODE DeviceNode,
-                       IN BOOLEAN LoadDriver,
-                       IN PDRIVER_OBJECT DriverObject)
-{
-    NTSTATUS Status;
-    HANDLE EnumRootKey, SubKey;
-    HANDLE ControlKey, ClassKey = NULL, PropertiesKey;
-    UNICODE_STRING ClassGuid, Properties;
-    UNICODE_STRING EnumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
-    UNICODE_STRING ControlClass =
-    RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
-    PKEY_VALUE_FULL_INFORMATION KeyValueInformation = NULL;
-    PWCHAR Buffer;
-
-    /* Open enumeration root key */
-    Status = IopOpenRegistryKeyEx(&EnumRootKey,
-                                  NULL,
-                                  &EnumRoot,
-                                  KEY_READ);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                &EnumRoot, Status);
-        return Status;
-    }
-
-    /* Open instance subkey */
-    Status = IopOpenRegistryKeyEx(&SubKey,
-                                  EnumRootKey,
-                                  &DeviceNode->InstancePath,
-                                  KEY_READ);
-    ZwClose(EnumRootKey);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                &DeviceNode->InstancePath, Status);
-        return Status;
-    }
-
-    /* Get class GUID */
-    Status = IopGetRegistryValue(SubKey,
-                                 REGSTR_VAL_CLASSGUID,
-                                 &KeyValueInformation);
-    if (NT_SUCCESS(Status))
-    {
-        /* Convert to unicode string */
-        Buffer = (PVOID)((ULONG_PTR)KeyValueInformation + KeyValueInformation->DataOffset);
-        PnpRegSzToString(Buffer, KeyValueInformation->DataLength, &ClassGuid.Length);
-        ClassGuid.MaximumLength = (USHORT)KeyValueInformation->DataLength;
-        ClassGuid.Buffer = Buffer;
-
-        /* Open the key */
-        Status = IopOpenRegistryKeyEx(&ControlKey,
-                                      NULL,
-                                      &ControlClass,
-                                      KEY_READ);
-        if (!NT_SUCCESS(Status))
-        {
-            /* No class key */
-            DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                    &ControlClass, Status);
-        }
-        else
-        {
-            /* Open the class key */
-            Status = IopOpenRegistryKeyEx(&ClassKey,
-                                          ControlKey,
-                                          &ClassGuid,
-                                          KEY_READ);
-            ZwClose(ControlKey);
-            if (!NT_SUCCESS(Status))
-            {
-                /* No class key */
-                DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                        &ClassGuid, Status);
-            }
-        }
-
-        /* Check if we made it till here */
-        if (ClassKey)
-        {
-            /* Get the device properties */
-            RtlInitUnicodeString(&Properties, REGSTR_KEY_DEVICE_PROPERTIES);
-            Status = IopOpenRegistryKeyEx(&PropertiesKey,
-                                          ClassKey,
-                                          &Properties,
-                                          KEY_READ);
-            if (!NT_SUCCESS(Status))
-            {
-                /* No properties */
-                DPRINT("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
-                       &Properties, Status);
-                PropertiesKey = NULL;
-            }
-            else
-            {
-                ZwClose(PropertiesKey);
-            }
-        }
-
-        /* Free the registry data */
-        ExFreePool(KeyValueInformation);
-    }
-
-    /* Do ReactOS-style setup */
-    Status = IopAttachFilterDrivers(DeviceNode, SubKey, ClassKey, TRUE);
-    if (!NT_SUCCESS(Status))
-    {
-        IopRemoveDevice(DeviceNode);
-        goto Exit;
-    }
-
-    Status = IopInitializeDevice(DeviceNode, DriverObject);
-    if (!NT_SUCCESS(Status))
-    {
-        goto Exit;
-    }
-
-    Status = IopAttachFilterDrivers(DeviceNode, SubKey, ClassKey, FALSE);
-    if (!NT_SUCCESS(Status))
-    {
-        IopRemoveDevice(DeviceNode);
-        goto Exit;
-    }
-
-    Status = IopStartDevice(DeviceNode);
-
-Exit:
-    /* Close keys and return status */
-    ZwClose(SubKey);
-    if (ClassKey != NULL)
-    {
-        ZwClose(ClassKey);
-    }
-    return Status;
-}
-
 CODE_SEG("INIT")
 NTSTATUS
 NTAPI
index 1a72481..3729033 100644 (file)
@@ -390,75 +390,6 @@ IopInstallCriticalDevice(PDEVICE_NODE DeviceNode)
     ZwClose(CriticalDeviceKey);
 }
 
-NTSTATUS
-FASTCALL
-IopInitializeDevice(PDEVICE_NODE DeviceNode,
-                    PDRIVER_OBJECT DriverObject)
-{
-    PDEVICE_OBJECT Fdo;
-    NTSTATUS Status;
-
-    if (!DriverObject)
-    {
-        /* Special case for bus driven devices */
-        DeviceNode->Flags |= DNF_ADDED;
-        return STATUS_SUCCESS;
-    }
-
-    if (!DriverObject->DriverExtension->AddDevice)
-    {
-        DeviceNode->Flags |= DNF_LEGACY_DRIVER;
-    }
-
-    if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
-    {
-        DeviceNode->Flags |= (DNF_ADDED | DNF_STARTED);
-        return STATUS_SUCCESS;
-    }
-
-    /* This is a Plug and Play driver */
-    DPRINT("Plug and Play driver found\n");
-    ASSERT(DeviceNode->PhysicalDeviceObject);
-
-    DPRINT("Calling %wZ->AddDevice(%wZ)\n",
-           &DriverObject->DriverName,
-           &DeviceNode->InstancePath);
-    Status = DriverObject->DriverExtension->AddDevice(DriverObject,
-                                                      DeviceNode->PhysicalDeviceObject);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("%wZ->AddDevice(%wZ) failed with status 0x%x\n",
-                &DriverObject->DriverName,
-                &DeviceNode->InstancePath,
-                Status);
-        IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-        DeviceNode->Problem = CM_PROB_FAILED_ADD;
-        return Status;
-    }
-
-    Fdo = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
-
-    /* Check if we have a ACPI device (needed for power management) */
-    if (Fdo->DeviceType == FILE_DEVICE_ACPI)
-    {
-        static BOOLEAN SystemPowerDeviceNodeCreated = FALSE;
-
-        /* There can be only one system power device */
-        if (!SystemPowerDeviceNodeCreated)
-        {
-            PopSystemPowerDeviceNode = DeviceNode;
-            ObReferenceObject(PopSystemPowerDeviceNode->PhysicalDeviceObject);
-            SystemPowerDeviceNodeCreated = TRUE;
-        }
-    }
-
-    ObDereferenceObject(Fdo);
-
-    IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
-
-    return STATUS_SUCCESS;
-}
-
 NTSTATUS
 IopGetSystemPowerDeviceObject(PDEVICE_OBJECT *DeviceObject)
 {