[PATCH v2 2/4] ntoskrnl.exe: Implement loading plug and play devices

Sebastian Lackner sebastian at fds-team.de
Thu Sep 1 02:41:54 CDT 2016


On 31.08.2016 18:38, Aric Stewart wrote:
> v2: No longer maintaining a device store
>     Suggestions from Sebastian Lackner
> 
> We are not handling installing drivers just yet, so we just make use
> of the critical device store. These are drivers that, on Windows, are
> automatically loaded on boot every time.  This database is maintained
> in [CurrentControlSet/Control/CriticalDeviceDatabase] The keys in this
> registry key represent a HardwareID that match a device. We query the
> IDs from the bus device’s BusQueryHardwareIDs and if we find a match
> (from most specific to most general) we look at that registry key. The
> CriticalDeviceDatabase entry specify a [Service] value representing
> the driver.
> 
> Signed-off-by: Aric Stewart <aric at codeweavers.com>
> ---
>  dlls/ntoskrnl.exe/pnp_manager.c | 196 +++++++++++++++++++++++++++++++++++++++-
>  include/ddk/wdm.h               |   3 +-
>  2 files changed, 197 insertions(+), 2 deletions(-)
> 
> 
> 
> v2-0002-ntoskrnl.exe-Implement-loading-plug-and-play-device.txt
> 
> 
> diff --git a/dlls/ntoskrnl.exe/pnp_manager.c b/dlls/ntoskrnl.exe/pnp_manager.c
> index 7ebd07d..04b218b 100644
> --- a/dlls/ntoskrnl.exe/pnp_manager.c
> +++ b/dlls/ntoskrnl.exe/pnp_manager.c
> @@ -17,6 +17,7 @@
>   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
>   */
>  
> +#define NONAMELESSUNION
>  #include <stdarg.h>
>  #include "ntstatus.h"
>  #define WIN32_NO_STATUS
> @@ -24,14 +25,207 @@
>  #include "winbase.h"
>  #include "winternl.h"
>  #include "ddk/wdm.h"
> +#include "wine/unicode.h"
>  #include "wine/debug.h"
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
>  
> +#define MAX_SERVICE_NAME 260
> +
> +static NTSTATUS WINAPI internal_complete(DEVICE_OBJECT *deviceObject, 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);
> +
> +    irp->UserEvent = event;
> +    irpsp = IoGetNextIrpStackLocation(irp);
> +    irpsp->CompletionRoutine = internal_complete;
> +    irpsp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR;
> +
> +    IoCallDriver(device, irp);
> +
> +    if (irp->IoStatus.u.Status == STATUS_PENDING)
> +        WaitForSingleObject(event, INFINITE);
> +
> +    status = irp->IoStatus.u.Status;
> +    if (info)
> +        *info = irp->IoStatus.Information;
> +    IoCompleteRequest(irp, IO_NO_INCREMENT);
> +    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;
> +
> +    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status);
> +    if (irp == NULL)
> +        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 NTSTATUS find_driver_for_id(const WCHAR *id, WCHAR *driver)
> +{
> +    NTSTATUS rc;
> +    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};
> +
> +    HANDLE hkey;
> +    WCHAR *regW;
> +    UNICODE_STRING key;
> +    OBJECT_ATTRIBUTES attr;
> +
> +    regW = HeapAlloc(GetProcessHeap(), 0, sizeof(critical_fmtW) + strlenW(id) * sizeof(WCHAR));
> +    sprintfW(regW, critical_fmtW, id);
> +    RtlInitUnicodeString(&key, regW);
> +
> +    InitializeObjectAttributes(&attr, &key, OBJ_CASE_INSENSITIVE, NULL, NULL);
> +    rc = ZwOpenKey(&hkey, KEY_ALL_ACCESS, &attr);
> +    if (rc == STATUS_SUCCESS)
> +    {
> +        static const WCHAR serviceW[] = {'S','e','r','v','i','c','e',0};
> +        static const UNICODE_STRING serviceStr = {sizeof(serviceW) - sizeof(WCHAR), sizeof(serviceW), (WCHAR*)serviceW};
> +        KEY_VALUE_PARTIAL_INFORMATION *info;
> +        DWORD total_size = sizeof(*info) + MAX_SERVICE_NAME * sizeof(WCHAR);
> +
> +        info = HeapAlloc(GetProcessHeap(), 0, total_size);
> +
> +        rc = ZwQueryValueKey(hkey, &serviceStr, KeyValuePartialInformation, info, total_size, &total_size);
> +        if (rc == STATUS_SUCCESS && info->Type == REG_SZ && info->DataLength < MAX_SERVICE_NAME * sizeof(WCHAR))
> +        {
> +            WCHAR *service = (WCHAR*)info->Data;
> +            TRACE("Got service %s\n",debugstr_w(service));
> +            strcpyW(driver, service);
> +        }
> +        else
> +        {
> +            if (info->Type != REG_SZ)
> +                ERR("Failed to get Service from registry entry, Bad type\n");
> +            else if (info->DataLength >= MAX_SERVICE_NAME * sizeof(WCHAR))
> +                ERR("Failed to get Service from registry entry, Buffer too small\n");
> +            else
> +                ERR("Failed to get Service from registry entry (%x)\n",rc);

I don't think it makes sense to trace this an error, especially when you use a TRACE for the
lack of a critical device database key.

> +        }
> +        HeapFree(GetProcessHeap(), 0, info);
> +        ZwClose(hkey);
> +    }
> +    else
> +        TRACE("Failed to find Critical Device Database entry for %s\n",debugstr_w(id));
> +    HeapFree(GetProcessHeap(), 0, regW);
> +    return rc;
> +}
> +
> +static void handle_bus_relations(DEVICE_OBJECT *device)
> +{
> +    static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
> +    static const WCHAR servicesW[] = {'\\','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',
> +                              '\\','S','e','r','v','i','c','e','s',
> +                              '\\',0};
> +
> +    NTSTATUS status;
> +    WCHAR *id, *idptr;
> +    WCHAR driver[MAX_SERVICE_NAME] = {0};
> +    WCHAR fullname[MAX_PATH];
> +    DRIVER_OBJECT *driver_obj;
> +    UNICODE_STRING driverName;
> +    WCHAR driverKey[MAX_PATH];
> +    UNICODE_STRING driverPath;

You'll have to allocate bigger buffers for the concatenated strings. In fact,
a single buffer should be sufficient for both the fullname and the driver key.

> +
> +    TRACE("Device %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, &id);
> +    if (status != ERROR_SUCCESS || !id)
> +    {
> +        ERR("Failed to get device IDs\n");
> +        return;
> +    }
> +
> +    idptr = id;
> +    while(*idptr != 0)
> +    {
> +        TRACE("Checking for id %s\n",debugstr_w(idptr));
> +        if (find_driver_for_id(idptr, driver) == STATUS_SUCCESS)
> +            break;
> +        idptr += (strlenW(idptr) + 1);
> +    }
> +    HeapFree(GetProcessHeap(), 0, id);
> +
> +    if (!driver[0])
> +    {
> +        ERR("No matching driver found for device\n");
> +        return;
> +    }
> +
> +    strcpyW(driverKey, servicesW);
> +    strcatW(driverKey, driver);
> +    RtlInitUnicodeString(&driverPath, driverKey);
> +    if (ZwLoadDriver(&driverPath) != STATUS_SUCCESS)
> +    {
> +        ERR("Failed to load driver %s\n", debugstr_w(driver));
> +        return;
> +    }
> +
> +    strcpyW(fullname, driverW);
> +    strcatW(fullname, driver);
> +    RtlInitUnicodeString(&driverName, fullname);
> +    if (ObReferenceObjectByName(&driverName, OBJ_CASE_INSENSITIVE, NULL,
> +        0, NULL, KernelMode, NULL, (void**)&driver_obj) != STATUS_SUCCESS)
> +    {
> +        ERR("Failed to locate loaded driver %s\n", debugstr_w(driver));
> +        return;
> +    }
> +
> +    if (!driver_obj->DriverExtension->AddDevice)
> +        status = STATUS_NOT_IMPLEMENTED;
> +    else
> +        status = driver_obj->DriverExtension->AddDevice(driver_obj, device);
> +
> +    ObDereferenceObject(driver_obj);
> +
> +    if (status != STATUS_SUCCESS)
> +        ERR("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);
> +    }
>  }
> diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h
> index bd3b323..e893ba2 100644
> --- a/include/ddk/wdm.h
> +++ b/include/ddk/wdm.h
> @@ -48,6 +48,7 @@ typedef NTSTATUS (WINAPI *PDRIVER_INITIALIZE)(struct _DRIVER_OBJECT *, PUNICODE_
>  typedef NTSTATUS (WINAPI *PDRIVER_DISPATCH)(struct _DEVICE_OBJECT *, struct _IRP *);
>  typedef void (WINAPI *PDRIVER_STARTIO)(struct _DEVICE_OBJECT *, struct _IRP *);
>  typedef void (WINAPI *PDRIVER_UNLOAD)(struct _DRIVER_OBJECT *);
> +typedef NTSTATUS (WINAPI *PDRIVER_ADD_DEVICE)(struct _DRIVER_OBJECT *, struct _DEVICE_OBJECT *);
>  
>  typedef struct _DISPATCHER_HEADER {
>    UCHAR  Type;
> @@ -336,7 +337,7 @@ typedef struct _DEVICE_RELATIONS *PDEVICE_RELATIONS;
>  
>  typedef struct _DRIVER_EXTENSION {
>    struct _DRIVER_OBJECT  *DriverObject;
> -  PVOID  AddDevice;
> +  PDRIVER_ADD_DEVICE AddDevice;
>    ULONG  Count;
>    UNICODE_STRING  ServiceKeyName;
>  } DRIVER_EXTENSION, *PDRIVER_EXTENSION;
> 
> 
> 




More information about the wine-devel mailing list