[PATCH v4 3/5] ntoskrnl.exe: Load a driver's root PnP devices when the driver is started.

Zebediah Figura z.figura12 at gmail.com
Sat Jun 22 10:12:32 CDT 2019


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
Devices whose device ID begins with ROOT are called root-enumerated
devices. Bus drivers—that is, the lowest in the tree, also act as function
drivers for these lowest-level root-enumerated devices. The PDO is created by
ntoskrnl itself, and is passed to the driver in its AddDevice callback. On
startup, ntoskrnl enumerates all root-enumerated devices, creates PDOs for
each, and starts their drivers. This is also done in SetupDiInstallDevice().

On a native machine, root devices include for example the ACPI bus driver, as
well as the "swenum" driver used for kernel streaming.

The process for installing a root-enumerated device looks like this: user-mode
code calls SetupDiCreateDevice(), with a device ID beginning with "ROOT". This
is key: the device ID *must* begin with ROOT, otherwise the device is not
root-enumerated, and nothing will happen. It then sets the hardware and
compatible IDs using SetupDiSetDeviceRegistryProperty(). Finally, it calls
SetupDiRegisterDevice() and UpdateDriverForPlugAndPlayDevices(), which
performs the rest of the installation process, including calling
SetupDiInstallDevice(). After this the root device is loaded (though
presumably this may require a reboot sometimes, given the final parameter to
UpdateDriver...), and may start receiving IOCTLs and interrupts, polling the
hardware for child devices, etc.

Native PnP bus drivers expect to receive a PDO in their AddDevice callback, or
they won't do anything else. This PDO is also passed to
IoInvalidateDeviceRelations(), whose argument we currently misinterpret as
being the new child to be added, but should really be the PDO of its parent.

 dlls/ntoskrnl.exe/ntoskrnl.c         |   2 +
 dlls/ntoskrnl.exe/ntoskrnl_private.h |   1 +
 dlls/ntoskrnl.exe/pnp.c              | 109 ++++++++++++++++++++++++---
 3 files changed, 103 insertions(+), 9 deletions(-)

diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 65239c92558..2e1c9a72dfb 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -3610,6 +3610,8 @@ NTSTATUS WINAPI ZwLoadDriver( const UNICODE_STRING *service_name )
     driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
     driver->service_handle = service_handle;
 
+    pnp_manager_enumerate_root_devices( service_name->Buffer + wcslen( servicesW ) );
+
     set_service_status( service_handle, SERVICE_RUNNING,
                         SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
     return STATUS_SUCCESS;
diff --git a/dlls/ntoskrnl.exe/ntoskrnl_private.h b/dlls/ntoskrnl.exe/ntoskrnl_private.h
index 1c5952f7c10..b5244ef1641 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl_private.h
+++ b/dlls/ntoskrnl.exe/ntoskrnl_private.h
@@ -76,6 +76,7 @@ extern POBJECT_TYPE SeTokenObjectType;
 
 void ObReferenceObject( void *obj ) DECLSPEC_HIDDEN;
 
+void pnp_manager_enumerate_root_devices( const WCHAR *driver_name ) DECLSPEC_HIDDEN;
 void pnp_manager_start(void) DECLSPEC_HIDDEN;
 void pnp_manager_stop(void) DECLSPEC_HIDDEN;
 
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c
index 6f2283bc834..48430f31123 100644
--- a/dlls/ntoskrnl.exe/pnp.c
+++ b/dlls/ntoskrnl.exe/pnp.c
@@ -375,6 +375,18 @@ static BOOL install_device_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVIN
     return TRUE;
 }
 
+/* Load the function driver for a newly created PDO, if one is present, and
+ * send IRPs to start the device. */
+static void start_device( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
+{
+    load_function_driver( device, set, sp_device );
+    if (device->DriverObject)
+    {
+        send_pnp_irp( device, IRP_MN_START_DEVICE );
+        send_power_irp( device, PowerDeviceD0 );
+    }
+}
+
 static void handle_bus_relations( DEVICE_OBJECT *device )
 {
     static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0};
@@ -420,19 +432,14 @@ static void handle_bus_relations( DEVICE_OBJECT *device )
         return;
     }
 
-    load_function_driver( device, set, &sp_device );
-    if (device->DriverObject)
-    {
-        send_pnp_irp( device, IRP_MN_START_DEVICE );
-        send_power_irp( device, PowerDeviceD0 );
-    }
+    start_device( device, set, &sp_device );
 
     SetupDiDestroyDeviceInfoList( set );
 }
 
-static void handle_removal_relations( DEVICE_OBJECT *device )
+static void remove_device( DEVICE_OBJECT *device )
 {
-    TRACE( "(%p)\n", device );
+    TRACE("Removing device %p.\n", device);
 
     send_power_irp( device, PowerDeviceD3 );
     send_pnp_irp( device, IRP_MN_SURPRISE_REMOVAL );
@@ -452,7 +459,7 @@ void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RE
             handle_bus_relations( device_object );
             break;
         case RemovalRelations:
-            handle_removal_relations( device_object );
+            remove_device( device_object );
             break;
         default:
             FIXME("Unhandled relation %#x.\n", type);
@@ -755,6 +762,23 @@ POWER_STATE WINAPI PoSetPowerState( DEVICE_OBJECT *device, POWER_STATE_TYPE type
 
 static DRIVER_OBJECT *pnp_manager;
 
+struct root_pnp_device
+{
+    WCHAR id[MAX_DEVICE_ID_LEN];
+    struct wine_rb_entry entry;
+    DEVICE_OBJECT *device;
+};
+
+static int root_pnp_devices_rb_compare( const void *key, const struct wine_rb_entry *entry )
+{
+    const struct root_pnp_device *device = WINE_RB_ENTRY_VALUE( entry, const struct root_pnp_device, entry );
+    const WCHAR *k = key;
+
+    return wcsicmp( k, device->id );
+}
+
+static struct wine_rb_tree root_pnp_devices = { root_pnp_devices_rb_compare };
+
 static NTSTATUS WINAPI pnp_manager_device_pnp( DEVICE_OBJECT *device, IRP *irp )
 {
     IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
@@ -783,7 +807,74 @@ void pnp_manager_start(void)
         ERR("Failed to create PnP manager driver, status %#x.\n", status);
 }
 
+static void destroy_root_pnp_device( struct wine_rb_entry *entry, void *context )
+{
+    struct root_pnp_device *device = WINE_RB_ENTRY_VALUE(entry, struct root_pnp_device, entry);
+    remove_device( device->device );
+}
+
 void pnp_manager_stop(void)
 {
+    wine_rb_destroy( &root_pnp_devices, destroy_root_pnp_device, NULL );
     IoDeleteDriver( pnp_manager );
 }
+
+void pnp_manager_enumerate_root_devices( const WCHAR *driver_name )
+{
+    static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
+    static const WCHAR rootW[] = {'R','O','O','T',0};
+    WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(driverW)], id[MAX_DEVICE_ID_LEN];
+    SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
+    struct root_pnp_device *pnp_device;
+    DEVICE_OBJECT *device;
+    NTSTATUS status;
+    unsigned int i;
+    HDEVINFO set;
+
+    TRACE("Searching for new root-enumerated devices for driver %s.\n", debugstr_w(driver_name));
+
+    set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES );
+    if (set == INVALID_HANDLE_VALUE)
+    {
+        ERR("Failed to build device set, error %#x.\n", GetLastError());
+        return;
+    }
+
+    for (i = 0; SetupDiEnumDeviceInfo( set, i, &sp_device ); ++i)
+    {
+        if (!SetupDiGetDeviceRegistryPropertyW( set, &sp_device, SPDRP_SERVICE,
+                NULL, (BYTE *)buffer, sizeof(buffer), NULL )
+                || lstrcmpiW( buffer, driver_name ))
+        {
+            continue;
+        }
+
+        SetupDiGetDeviceInstanceIdW( set, &sp_device, id, ARRAY_SIZE(id), NULL );
+
+        if (wine_rb_get( &root_pnp_devices, id ))
+            continue;
+
+        TRACE("Adding new root-enumerated device %s.\n", debugstr_w(id));
+
+        if ((status = IoCreateDevice( pnp_manager, sizeof(struct root_pnp_device), NULL,
+                FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device )))
+        {
+            ERR("Failed to create root-enumerated PnP device %s, status %#x.\n", debugstr_w(id), status);
+            continue;
+        }
+
+        pnp_device = device->DeviceExtension;
+        wcscpy( pnp_device->id, id );
+        pnp_device->device = device;
+        if (wine_rb_put( &root_pnp_devices, id, &pnp_device->entry ))
+        {
+            ERR("Failed to insert device %s into tree.\n", debugstr_w(id));
+            IoDeleteDevice( device );
+            continue;
+        }
+
+        start_device( device, set, &sp_device );
+    }
+
+    SetupDiDestroyDeviceInfoList(set);
+}
-- 
2.21.0




More information about the wine-devel mailing list