[3/3] ntoskrnl.exe: Implement loading plug and play devices. (v6)

Sebastian Lackner sebastian at fds-team.de
Wed Sep 14 02:30:43 CDT 2016


From: Aric Stewart <aric at codeweavers.com>

Signed-off-by: Aric Stewart <aric at codeweavers.com>
Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
---

Changes in v6:
* Replace "failed to open critical device database" warning - its misleading because it
  appears even when the critical device database is present in the registry.

 dlls/ntoskrnl.exe/ntoskrnl.c |  187 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 186 insertions(+), 1 deletion(-)

diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index d5236e6..f81d668 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -48,6 +48,7 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl);
 WINE_DECLARE_DEBUG_CHANNEL(relay);
+WINE_DECLARE_DEBUG_CHANNEL(plugplay);
 
 BOOLEAN KdDebuggerEnabled = FALSE;
 ULONG InitSafeBootMode = 0;
@@ -76,6 +77,8 @@ static const WCHAR servicesW[] = {'\\','R','e','g','i','s','t','r','y',
                                   '\\','S','e','r','v','i','c','e','s',
                                   '\\',0};
 
+#define MAX_SERVICE_NAME 260
+
 /* tid of the thread running client request */
 static DWORD request_thread;
 
@@ -2819,10 +2822,192 @@ done:
 }
 
 
+static NTSTATUS WINAPI internal_complete( DEVICE_OBJECT *device, IRP *irp, void *context )
+{
+    SetEvent( irp->UserEvent );
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+static NTSTATUS send_device_irp( DEVICE_OBJECT *device, IRP *irp, ULONG_PTR *info )
+{
+    NTSTATUS status;
+    IO_STACK_LOCATION *irpsp;
+    HANDLE event = CreateEventA( NULL, FALSE, FALSE, NULL );
+    DEVICE_OBJECT *toplevel_device;
+
+    irp->UserEvent = event;
+    irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
+    irpsp = IoGetNextIrpStackLocation( irp );
+    irpsp->CompletionRoutine = internal_complete;
+    irpsp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
+
+    toplevel_device = IoGetAttachedDeviceReference( device );
+    status = IoCallDriver( toplevel_device, irp );
+
+    if (status == STATUS_PENDING)
+        WaitForSingleObject( event, INFINITE );
+
+    status = irp->IoStatus.u.Status;
+    if (info)
+        *info = irp->IoStatus.Information;
+    IoCompleteRequest( irp, IO_NO_INCREMENT );
+    ObDereferenceObject( toplevel_device );
+    CloseHandle( event );
+    return status;
+}
+
+
+static NTSTATUS get_device_id( DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id )
+{
+    IO_STACK_LOCATION *irpsp;
+    IO_STATUS_BLOCK irp_status;
+    IRP *irp;
+
+    if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status )))
+        return STATUS_NO_MEMORY;
+
+    irpsp = IoGetNextIrpStackLocation( irp );
+    irpsp->MinorFunction = IRP_MN_QUERY_ID;
+    irpsp->Parameters.QueryId.IdType = type;
+
+    return send_device_irp( device, irp, (ULONG_PTR *)id );
+}
+
+
+static BOOL get_driver_for_id( const WCHAR *id, WCHAR *driver )
+{
+    static const WCHAR serviceW[] = {'S','e','r','v','i','c','e',0};
+    static const UNICODE_STRING service_str = { sizeof(serviceW) - sizeof(WCHAR), sizeof(serviceW), (WCHAR *)serviceW };
+    static const WCHAR critical_fmtW[] =
+        {'\\','R','e','g','i','s','t','r','y',
+         '\\','M','a','c','h','i','n','e',
+         '\\','S','y','s','t','e','m',
+         '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
+         '\\','C','o','n','t','r','o','l',
+         '\\','C','r','i','t','i','c','a','l','D','e','v','i','c','e','D','a','t','a','b','a','s','e',
+         '\\','%','s',0};
+    WCHAR buffer[FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data[MAX_SERVICE_NAME * sizeof(WCHAR)] )];
+    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING key;
+    NTSTATUS status;
+    HANDLE hkey;
+    WCHAR *keyW;
+    DWORD len;
+
+    if (!(keyW = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(critical_fmtW) + strlenW(id) * sizeof(WCHAR) )))
+        return STATUS_NO_MEMORY;
+
+    sprintfW( keyW, critical_fmtW, id );
+    RtlInitUnicodeString( &key, keyW );
+    InitializeObjectAttributes( &attr, &key, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );
+
+    status = ZwOpenKey( &hkey, KEY_ALL_ACCESS, &attr );
+    RtlFreeUnicodeString( &key );
+    if (status != STATUS_SUCCESS)
+    {
+        TRACE_(plugplay)( "no driver found for %s\n", debugstr_w(id) );
+        return FALSE;
+    }
+
+    status = ZwQueryValueKey( hkey, &service_str, KeyValuePartialInformation,
+                              info, sizeof(buffer) - sizeof(WCHAR), &len );
+    ZwClose( hkey );
+    if (status != STATUS_SUCCESS || info->Type != REG_SZ)
+    {
+        TRACE_(plugplay)( "no driver found for %s\n", debugstr_w(id) );
+        return FALSE;
+    }
+
+    memcpy( driver, info->Data, info->DataLength );
+    driver[ info->DataLength / sizeof(WCHAR) ] = 0;
+    TRACE_(plugplay)( "found driver %s for %s\n", debugstr_w(driver), debugstr_w(id) );
+    return TRUE;
+}
+
+
+static void handle_bus_relations( DEVICE_OBJECT *device )
+{
+    static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
+    WCHAR buffer[MAX_SERVICE_NAME + sizeof(servicesW)/sizeof(WCHAR)];
+    WCHAR driver[MAX_SERVICE_NAME] = {0};
+    DRIVER_OBJECT *driver_obj;
+    UNICODE_STRING string;
+    WCHAR *ids, *ptr;
+    NTSTATUS status;
+
+    TRACE_(plugplay)( "(%p)\n", device );
+
+    /* We could (should?) do a full IRP_MN_QUERY_DEVICE_RELATIONS query,
+     * but we dont have to, We have the DEVICE_OBJECT of the new device
+     * so we can simply handle the process here */
+
+    status = get_device_id( device, BusQueryCompatibleIDs, &ids );
+    if (status != STATUS_SUCCESS || !ids)
+    {
+        ERR_(plugplay)( "Failed to get device IDs\n" );
+        return;
+    }
+
+    for (ptr = ids; *ptr; ptr += strlenW(ptr) + 1)
+    {
+        if (get_driver_for_id( ptr, driver ))
+            break;
+    }
+    RtlFreeHeap( GetProcessHeap(), 0, ids );
+
+    if (!driver[0])
+    {
+        ERR_(plugplay)( "No matching driver found for device\n" );
+        return;
+    }
+
+    strcpyW( buffer, servicesW );
+    strcatW( buffer, driver );
+    RtlInitUnicodeString( &string, buffer );
+    if (ZwLoadDriver( &string ) != STATUS_SUCCESS)
+    {
+        ERR_(plugplay)( "Failed to load driver %s\n", debugstr_w(driver) );
+        return;
+    }
+
+    strcpyW( buffer, driverW );
+    strcatW( buffer, driver );
+    RtlInitUnicodeString( &string, buffer );
+    if (ObReferenceObjectByName( &string, OBJ_CASE_INSENSITIVE, NULL,
+                                 0, NULL, KernelMode, NULL, (void **)&driver_obj ) != STATUS_SUCCESS)
+    {
+        ERR_(plugplay)( "Failed to locate loaded driver %s\n", debugstr_w(driver) );
+        return;
+    }
+
+    if (driver_obj->DriverExtension->AddDevice)
+        status = driver_obj->DriverExtension->AddDevice( driver_obj, device );
+    else
+        status = STATUS_NOT_IMPLEMENTED;
+
+    ObDereferenceObject( driver_obj );
+
+    if (status != STATUS_SUCCESS)
+        ERR_(plugplay)( "AddDevice failed for driver %s\n", debugstr_w(driver) );
+}
+
+
 /***********************************************************************
  *           IoInvalidateDeviceRelations (NTOSKRNL.EXE.@)
  */
 void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RELATION_TYPE type )
 {
-    FIXME( "(%p, %i): stub\n", device_object, type );
+    TRACE( "(%p, %i)\n", device_object, type );
+
+    switch (type)
+    {
+        case BusRelations:
+            handle_bus_relations( device_object );
+            break;
+        default:
+            FIXME( "unhandled relation %i\n", type );
+            break;
+    }
 }
-- 
2.9.0



More information about the wine-patches mailing list