Plug and Play and Wine

Damjan Jovanovic damjan.jov at gmail.com
Tue Dec 22 12:13:47 CST 2015


On Sun, Dec 20, 2015 at 9:16 PM, Aric Stewart <aric at codeweavers.com> wrote:

> Hello All,
>
> Yay 1.8!
>
> So in discussions with Alexandre, he expressed that he would really like
> there to be a Plug and Play system in wine to support the HID work I have
> been doing. Others at WineConf also expressed a desire for a plug and play
> system. So I have been doing research, reading and work to try to design
> out how this system will work. I promised him that while I would do
> research and testing I would wait until after the release before initiating
> this conversation.
>
> We also discussed at WineConf that design processes like this should
> happen more out in the open so here is my proposed design, with my
> thinking, so that people who have interest and maybe more knowledge about
> parts of this than I can help improve the design. I have done some basic
> proof of concept coding on most of this so that I know it can work, but
> have flexibility.
>
> Long discussion below.
>
> The main thing I have learned is that this side of Plug and Play (The
> system side) is very complicated and pretty much completely undocumented.
> So this beginning design is going to be greatly simplified but hopefully in
> enough of a good framework state that it can be expanded as needed.
>
> A few definitions, these are borrowed terms but may not be specifically
> defined exactly the same as on windows:
>
>   Bus driver : this is whatever lump of code that it talking to the
> underlying devices, this should be talking to platform native devices.  For
> the purposes of our PnP the bus driver will be only really responsible for
> enumerating devices, detecting addition and removal of devices and being
> able to generate device IDs for these devices. This will all be wine code
> as far as I can determine.
>
>   Device Driver: This is the code that is actually trying to do something
> with the device enumerated but the bus driver. This too will likely be wine
> code. Think a hid minidriver or usb.sys. Generally any supported native
> drivers will have to sit above this particular device driver level.  But
> that could maybe change, we will see
>
>   Plug and Play manager: This is the chunk of code that manages all the
> driver, devices and does the plug and play work.  I have chosen to make the
> existing Programs/plugplay.exe be the plug and play manager.
>
> No INF processing or install:
>
>  The most important part of this that is missing and that I am not
> addressing is anything about driver installation or inf files. Right now
> everything in this process is wine builtin code, so there is no need to
> have inf processing, and that is a seriously complicated part of this
> process. I have not fully figured out how we should manage the driver store
> and do the full driver on demand installation and such. But a basic
> framework can be put together without it.
>
> The Plug and Play process, (As I see it for wine)
>
> Ok, here is the meat of what is interesting. Starting at the bottom.  Plug
> and Play bus drivers will be written for OS/X HID devices as well as udev
> hidraw devices. When I discussed this with Alexandre he did not feel like
> these need to be individual dlls, so they will be included in the
> plugplay.exe manager. However they will be designed in such a way that if a
> compelling reason why we need to pull them out becomes apparent it will not
> be difficult. These bus drivers will not be full device driver really, they
> just handle the Plug and play aspects of the bus, the
> device->DeviceExtension will be simply be a handle to the native device in
> question. The driver will only handle IRP_MJ_PNP ioctls.
> Having these be loaded into the plugplay.exe manager process space makes
> many things much easier.
>
> When the bus driver detects a new device it will call
> PNP_IoInvalidateDeviceRelations, and internal function inside of
> plugplay.exe that mirrors IoInvalidateDeviceRelations. Because the bus
> drivers are living inside of the plugplay manager there is no need to
> implement and deal with the ntoskrnl.exe version of
> IoInvalidateDeviceRelations. Again this will be coded to be able to be
> pulled out if needed. If we decide to handle any bus drivers that live
> outside of the plugplay.exe process space then we will need to have this
> functionality, probably via RPC like the service manager.
>
> Here is where the fun comes.  Because we are not handling installing
> drivers (yet) we can 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 will 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 will specify a [Service] value. All the first
> round builtin drivers will specify the [Service]. Further implementations
> when we start supporting inf files will also look at supporting the
> [ClassGUID] value where a driver inf can be specified.
>
> That Service will specify a key in [CurrentControlSet\Services]. These
> services will need to be [type] of 0x1 (device driver) and [start] of 0x3
> (on demand), finally they will have an [ImagePath] that points to the .sys
> file for this driver.  The plug and play manager will determine if it has
> loaded that driver or not, and if not then it will load the driver into its
> process space and call the drivers DriverEntry. Part of this will be done
> via the advapi service APIs to query the service and then I was planing to
> reuse code from winedevice.exe for the actual loading of the driver.sys
> into the plugplay.exe process space.
>
>
I've already done that. Attached are 2 patches that make driver loading
independent of global variables, and move driver loading code from
winedevice.exe to ntoskrnl.exe where it can be reused to load drivers from
anything that links to ntoskrnl.exe.


> With the loaded driver we then call the driver’s extension.AddDevice entry
> point with the new bus device. The drivers AddDevice function will be well
> behaved and call IoAttachDeviceToDeviceStack. The Plug and Play manager
> will look at the bus device after AddDevice and if there is an
> AttachedDevice then will proceed to call IRP_MN_START_DEVICE on the
> attached device. Then a IRP_MJ_POWER/IRP_MN_SET_POWER to PowerDeviceD0.
>
> It is an important point that having the bus drivers and function drivers
> all loaded into the same process space means that they have a common
> process stack for sharing memory and handles and such. Driver will expect
> this so if we end up having to change this we will have to deal with the
> complexities of the memory management.
>
>
My work on USB uses a similar idea.

In my design, usbhub.sys (my "bus driver") is loaded at Wine start-up. It
is loaded by winedevice.exe, in its own separate winedevice.exe process
just like every driver currently is, but it is able to (using my previously
mentioned patches) load USB device drivers into its own process, and
leading to a hybrid driver architecture where copy protection drivers
should continue to work as they get loaded into their own winedevice
processes and crashes won't affect other drivers, while usbhub.sys can load
USB drivers into its own process as needed to provide hardware support.

Also I planned to detect device additions and removals by listing all USB
devices every second, and detecting what changed since the previous
listing. This is because there is no other portable way to read device
addition/removal events: there used to be HAL but now we've degenerated to
the Linux-only udev :-(. I was planning to hack the plug and play side of
things ;-). A background thread in usbhub.sys would do this polling for USB
devices and build device objects and load drivers.

We should probably develop the plug and play system before USB though?


> Device removal will be more simplistic. When the bus driver detects that a
> device has been removed then we  again make a
> PNP_IoInvalidateDeviceRelations. Then the plug and play manager will send a
> IRP_MN_SURPRISE_REMOVAL followed by a IRP_MN_REMOVE_DEVICE ioctl to the
> AttachedDevice.
>
> That will represent the entirety of the process, as I see it on the first
> pass, for plug and play. Some of the many things this does not do, it does
> not make use of or maintain a driver store (I think maintained as keys
> under the [CurrentControlSet/Control/Class/<GUID>] keys). It does not do
> any of the complicated driver INF, install on demand work and it does not
> allow for new drivers to be installed via any of the installation
> processes.  But it does not prevent this from being expanded on for next
> passes.
>
> Another big unanswered part of this is the integration with the Plug and
> Play configuration manager (cfgmgr32.dll) and all the CM_XXX functions.
>
> I am only working on the HID bus and drivers, however I feel like we could
> expand this pretty easily into USB or even mountmgr bus and driver
> architectures as we move forward.
>
> Comments? Thoughts? Anyone actually get to the end here?
>
> -aric
>
>
>

Damjan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20151222/900b4927/attachment-0001.html>
-------------- next part --------------
From 0b8426d44c4bd887d067c8886a500437a42e630e Mon Sep 17 00:00:00 2001
From: Damjan Jovanovic <damjan.jov at gmail.com>
Date: Sun, 1 Mar 2015 20:25:02 +0200
Subject: winedevice: Make driver loading independent of global variables.
To: wine-patches at winehq.org
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.6.3"

This is a multi-part message in MIME format.
--------------2.6.3
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 programs/winedevice/device.c | 60 ++++++++++++++++++++++++++++----------------
 1 file changed, 38 insertions(+), 22 deletions(-)


--------------2.6.3
Content-Type: text/x-patch; name="0001-winedevice-Make-driver-loading-independent-of-global-v.txt"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-winedevice-Make-driver-loading-independent-of-global-v.txt"

diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index ef1e1ef..a1508a7 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -42,10 +42,7 @@ extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
 
 static WCHAR *driver_name;
 static SERVICE_STATUS_HANDLE service_handle;
-static HKEY driver_hkey;
 static HANDLE stop_event;
-static DRIVER_OBJECT driver_obj;
-static DRIVER_EXTENSION driver_extension;
 
 /* find the LDR_MODULE corresponding to the driver module */
 static LDR_MODULE *find_ldr_module( HMODULE module )
@@ -131,39 +128,51 @@ error:
 }
 
 /* call the driver init entry point */
-static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname )
+static NTSTATUS init_driver( HMODULE module, LPCWSTR driver_name, DRIVER_OBJECT **p_driver_obj,
+                             UNICODE_STRING *keyname )
 {
     unsigned int i;
+    DRIVER_OBJECT *driver_obj = NULL;
     NTSTATUS status;
     const IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
 
     if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
 
-    driver_obj.Size            = sizeof(driver_obj);
-    driver_obj.DriverSection   = find_ldr_module( module );
-    driver_obj.DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
-    driver_obj.DriverExtension = &driver_extension;
+    driver_obj = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION) );
+    if (driver_obj == NULL)
+        return STATUS_NO_MEMORY;
 
-    driver_extension.DriverObject   = &driver_obj;
-    driver_extension.ServiceKeyName = *keyname;
+    driver_obj->Size            = sizeof(*driver_obj);
+    /* FIXME: DriverSection is wrong, see http://stackoverflow.com/questions/9017952/driver-object-driversection */
+    driver_obj->DriverSection   = find_ldr_module( module );
+    driver_obj->DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
+    driver_obj->DriverExtension = (DRIVER_EXTENSION *)(driver_obj + 1);
+
+    driver_obj->DriverExtension->DriverObject   = driver_obj;
+    driver_obj->DriverExtension->ServiceKeyName = *keyname;
 
     if (WINE_TRACE_ON(relay))
         WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
-                      driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer) );
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer) );
 
-    status = driver_obj.DriverInit( &driver_obj, keyname );
+    status = driver_obj->DriverInit( driver_obj, keyname );
 
     if (WINE_TRACE_ON(relay))
         WINE_DPRINTF( "%04x:Ret  driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
-                      driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer), status );
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer), status );
 
-    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), &driver_obj );
-    WINE_TRACE( "- DriverInit = %p\n", driver_obj.DriverInit );
-    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj.DriverStartIo );
-    WINE_TRACE( "- DriverUnload = %p\n", driver_obj.DriverUnload );
+    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_obj );
+    WINE_TRACE( "- DriverInit = %p\n", driver_obj->DriverInit );
+    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj->DriverStartIo );
+    WINE_TRACE( "- DriverUnload = %p\n", driver_obj->DriverUnload );
     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
-        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj.MajorFunction[i] );
+        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj->MajorFunction[i] );
 
+    if (status == STATUS_SUCCESS)
+        *p_driver_obj = driver_obj;
+    else
+        RtlFreeHeap( GetProcessHeap(), 0, driver_obj );
     return status;
 }
 
@@ -186,7 +195,7 @@ static void unload_driver( HMODULE module, DRIVER_OBJECT *driver_obj )
 }
 
 /* load the .sys module for a device driver */
-static HMODULE load_driver(void)
+static HMODULE load_driver( LPCWSTR driver_name, DRIVER_OBJECT **driver_obj )
 {
     static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
     static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
@@ -201,6 +210,7 @@ static HMODULE load_driver(void)
 
     UNICODE_STRING keypath;
     HMODULE module;
+    HKEY driver_hkey;
     LPWSTR path = NULL, str;
     DWORD type, size;
 
@@ -269,7 +279,11 @@ static HMODULE load_driver(void)
     HeapFree( GetProcessHeap(), 0, path );
     if (!module) return NULL;
 
-    init_driver( module, &keypath );
+    if (init_driver( module, driver_name, driver_obj, &keypath ) != STATUS_SUCCESS)
+    {
+        FreeLibrary(module);
+        return NULL;
+    }
     return module;
 }
 
@@ -306,6 +320,7 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
 {
     SERVICE_STATUS status;
     HMODULE driver_module;
+    DRIVER_OBJECT *driver_obj;
 
     WINE_TRACE( "starting service %s\n", wine_dbgstr_w(driver_name) );
 
@@ -324,7 +339,7 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
     status.dwWaitHint                = 10000;
     SetServiceStatus( service_handle, &status );
 
-    driver_module = load_driver();
+    driver_module = load_driver( driver_name, &driver_obj );
     if (driver_module)
     {
         status.dwCurrentState     = SERVICE_RUNNING;
@@ -332,7 +347,8 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
         SetServiceStatus( service_handle, &status );
 
         wine_ntoskrnl_main_loop( stop_event );
-        unload_driver( driver_module, &driver_obj );
+        unload_driver( driver_module, driver_obj );
+        RtlFreeHeap( GetProcessHeap(), 0, driver_obj );
     }
     else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) );
 

--------------2.6.3--


-------------- next part --------------
From 14447b1088e07e8ce50e712b6271589d8fba88bf Mon Sep 17 00:00:00 2001
From: Damjan Jovanovic <damjan.jov at gmail.com>
Date: Sat, 3 May 2014 20:18:26 +0200
Subject: ntoskrnl.exe: migrate driver loading to ntoskrnl.exe so it can be
 reused
To: wine-patches at winehq.org
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.6.3"

This is a multi-part message in MIME format.
--------------2.6.3
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 dlls/ntoskrnl.exe/Makefile.in       |   1 +
 dlls/ntoskrnl.exe/ntoskrnl.c        | 230 ++++++++++++++++++++++++++++++++++++
 dlls/ntoskrnl.exe/ntoskrnl.exe.spec |   1 +
 programs/winedevice/device.c        | 228 +----------------------------------
 4 files changed, 234 insertions(+), 226 deletions(-)


--------------2.6.3
Content-Type: text/x-patch; name="0002-ntoskrnl.exe-migrate-driver-loading-to-ntoskrnl.exe-so.txt"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0002-ntoskrnl.exe-migrate-driver-loading-to-ntoskrnl.exe-so.txt"

diff --git a/dlls/ntoskrnl.exe/Makefile.in b/dlls/ntoskrnl.exe/Makefile.in
index adb1327..799c264 100644
--- a/dlls/ntoskrnl.exe/Makefile.in
+++ b/dlls/ntoskrnl.exe/Makefile.in
@@ -1,5 +1,6 @@
 MODULE    = ntoskrnl.exe
 IMPORTLIB = ntoskrnl.exe
+IMPORTS   = advapi32
 
 C_SRCS = \
 	instr.c \
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 04f0198..eb981dd 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -30,10 +30,12 @@
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
+#include "winbase.h"
 #include "winternl.h"
 #include "excpt.h"
 #include "winioctl.h"
 #include "ddk/csq.h"
+#include "winreg.h"
 #include "ddk/ntddk.h"
 #include "ddk/ntifs.h"
 #include "ddk/wdm.h"
@@ -502,6 +504,234 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event )
 }
 
 
+/* find the LDR_MODULE corresponding to the driver module */
+static LDR_MODULE *find_ldr_module( HMODULE module )
+{
+    LIST_ENTRY *entry, *list = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+
+    for (entry = list->Flink; entry != list; entry = entry->Flink)
+    {
+        LDR_MODULE *ldr = CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList);
+        if (ldr->BaseAddress == module) return ldr;
+        if (ldr->BaseAddress > (void *)module) break;
+    }
+    return NULL;
+}
+
+/* load the driver module file */
+static HMODULE load_driver_module( const WCHAR *name )
+{
+    IMAGE_NT_HEADERS *nt;
+    const IMAGE_IMPORT_DESCRIPTOR *imports;
+    SYSTEM_BASIC_INFORMATION info;
+    int i;
+    INT_PTR delta;
+    ULONG size;
+    HMODULE module = LoadLibraryW( name );
+
+    if (!module) return NULL;
+    nt = RtlImageNtHeader( module );
+
+    if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module;
+
+    /* the loader does not apply relocations to non page-aligned binaries or executables,
+     * we have to do it ourselves */
+
+    NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
+    if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
+        !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
+    {
+        DWORD old;
+        IMAGE_BASE_RELOCATION *rel, *end;
+
+        if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
+        {
+            WINE_TRACE( "%s: relocating from %p to %p\n",
+                        wine_dbgstr_w(name), (char *)module - delta, module );
+            end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
+            while (rel < end && rel->SizeOfBlock)
+            {
+                void *page = (char *)module + rel->VirtualAddress;
+                VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
+                rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
+                                                 (USHORT *)(rel + 1), delta );
+                if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
+                if (!rel) goto error;
+            }
+            /* make sure we don't try again */
+            size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
+            VirtualProtect( nt, size, PAGE_READWRITE, &old );
+            nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
+            VirtualProtect( nt, size, old, &old );
+        }
+    }
+
+    /* make sure imports are relocated too */
+
+    if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
+    {
+        for (i = 0; imports[i].Name && imports[i].FirstThunk; i++)
+        {
+            char *name = (char *)module + imports[i].Name;
+            WCHAR buffer[32], *p = buffer;
+
+            while (p < buffer + 32) if (!(*p++ = *name++)) break;
+            if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) );
+        }
+    }
+
+    return module;
+
+error:
+    FreeLibrary( module );
+    return NULL;
+}
+
+/* call the driver init entry point */
+static NTSTATUS init_driver( HMODULE module, LPCWSTR driver_name, DRIVER_OBJECT **p_driver_obj,
+                             UNICODE_STRING *keyname )
+{
+    unsigned int i;
+    DRIVER_OBJECT *driver_obj = NULL;
+    NTSTATUS status;
+    const IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
+
+    if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
+
+    driver_obj = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION) );
+    if (driver_obj == NULL)
+        return STATUS_NO_MEMORY;
+
+    driver_obj->Size            = sizeof(*driver_obj);
+    /* FIXME: DriverSection is wrong, see http://stackoverflow.com/questions/9017952/driver-object-driversection */
+    driver_obj->DriverSection   = find_ldr_module( module );
+    driver_obj->DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
+    driver_obj->DriverExtension = (DRIVER_EXTENSION *)(driver_obj + 1);
+
+    driver_obj->DriverExtension->DriverObject   = driver_obj;
+    driver_obj->DriverExtension->ServiceKeyName = *keyname;
+
+    if (WINE_TRACE_ON(relay))
+        WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer) );
+
+    status = driver_obj->DriverInit( driver_obj, keyname );
+
+    if (WINE_TRACE_ON(relay))
+        WINE_DPRINTF( "%04x:Ret  driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer), status );
+
+    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_obj );
+    WINE_TRACE( "- DriverInit = %p\n", driver_obj->DriverInit );
+    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj->DriverStartIo );
+    WINE_TRACE( "- DriverUnload = %p\n", driver_obj->DriverUnload );
+    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj->MajorFunction[i] );
+
+    if (status == STATUS_SUCCESS)
+        *p_driver_obj = driver_obj;
+    else
+        RtlFreeHeap( GetProcessHeap(), 0, driver_obj );
+    return status;
+}
+
+/***********************************************************************
+ *           wine_ntoskrnl_load_driver   (Not a Windows API)
+ */
+HMODULE CDECL wine_ntoskrnl_load_driver( LPCWSTR driver_name, DRIVER_OBJECT **driver_obj )
+{
+    static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
+    static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
+    static const WCHAR postfixW[] = {'.','s','y','s',0};
+    static const WCHAR ntprefixW[] = {'\\','?','?','\\',0};
+    static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',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};
+
+    UNICODE_STRING keypath;
+    HMODULE module;
+    HKEY driver_hkey;
+    LPWSTR path = NULL, str;
+    DWORD type, size;
+
+    str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) );
+    lstrcpyW( str, servicesW );
+    lstrcatW( str, driver_name );
+
+    if (RegOpenKeyW( HKEY_LOCAL_MACHINE, str + 18 /* skip \registry\machine */, &driver_hkey ))
+    {
+        WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(str), GetLastError() );
+        HeapFree( GetProcessHeap(), 0, str);
+        return FALSE;
+    }
+    RtlInitUnicodeString( &keypath, str );
+
+    /* read the executable path from memory */
+    size = 0;
+    if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size ))
+    {
+        str = HeapAlloc( GetProcessHeap(), 0, size );
+        if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size ))
+        {
+            size = ExpandEnvironmentStringsW(str,NULL,0);
+            path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
+            ExpandEnvironmentStringsW(str,path,size);
+        }
+        HeapFree( GetProcessHeap(), 0, str );
+        if (!path) return FALSE;
+
+        if (!strncmpiW( path, systemrootW, 12 ))
+        {
+            WCHAR buffer[MAX_PATH];
+
+            GetWindowsDirectoryW(buffer, MAX_PATH);
+
+            str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer))
+                                                        * sizeof(WCHAR));
+            lstrcpyW(str, buffer);
+            lstrcatW(str, path + 11);
+            HeapFree( GetProcessHeap(), 0, path );
+            path = str;
+        }
+        else if (!strncmpW( path, ntprefixW, 4 ))
+            str = path + 4;
+        else
+            str = path;
+    }
+    else
+    {
+        /* default is to use the driver name + ".sys" */
+        WCHAR buffer[MAX_PATH];
+        GetSystemDirectoryW(buffer, MAX_PATH);
+        path = HeapAlloc(GetProcessHeap(),0,
+          (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
+          *sizeof(WCHAR));
+        lstrcpyW(path, buffer);
+        lstrcatW(path, driversW);
+        lstrcatW(path, driver_name);
+        lstrcatW(path, postfixW);
+        str = path;
+    }
+
+    WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) );
+
+    module = load_driver_module( str );
+    HeapFree( GetProcessHeap(), 0, path );
+    if (!module) return NULL;
+
+    if (init_driver( module, driver_name, driver_obj, &keypath ) != STATUS_SUCCESS)
+    {
+        FreeLibrary(module);
+        return NULL;
+    }
+    return module;
+}
+
+
 /***********************************************************************
  *           IoAcquireCancelSpinLock  (NTOSKRNL.EXE.@)
  */
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index db15265..93f5965 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -1490,3 +1490,4 @@
 # or 'wine_' (for user-visible functions) to avoid namespace conflicts.
 
 @ cdecl wine_ntoskrnl_main_loop(long)
+@ cdecl wine_ntoskrnl_load_driver(wstr ptr)
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index a1508a7..67844f5 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -39,143 +39,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(winedevice);
 WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
+extern HMODULE CDECL wine_ntoskrnl_load_driver( LPCWSTR driver_name, DRIVER_OBJECT **driver_obj );
 
 static WCHAR *driver_name;
 static SERVICE_STATUS_HANDLE service_handle;
 static HANDLE stop_event;
 
-/* find the LDR_MODULE corresponding to the driver module */
-static LDR_MODULE *find_ldr_module( HMODULE module )
-{
-    LIST_ENTRY *entry, *list = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
-
-    for (entry = list->Flink; entry != list; entry = entry->Flink)
-    {
-        LDR_MODULE *ldr = CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList);
-        if (ldr->BaseAddress == module) return ldr;
-        if (ldr->BaseAddress > (void *)module) break;
-    }
-    return NULL;
-}
-
-/* load the driver module file */
-static HMODULE load_driver_module( const WCHAR *name )
-{
-    IMAGE_NT_HEADERS *nt;
-    const IMAGE_IMPORT_DESCRIPTOR *imports;
-    SYSTEM_BASIC_INFORMATION info;
-    int i;
-    INT_PTR delta;
-    ULONG size;
-    HMODULE module = LoadLibraryW( name );
-
-    if (!module) return NULL;
-    nt = RtlImageNtHeader( module );
-
-    if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module;
-
-    /* the loader does not apply relocations to non page-aligned binaries or executables,
-     * we have to do it ourselves */
-
-    NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
-    if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
-        !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
-    {
-        DWORD old;
-        IMAGE_BASE_RELOCATION *rel, *end;
-
-        if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
-        {
-            WINE_TRACE( "%s: relocating from %p to %p\n",
-                        wine_dbgstr_w(name), (char *)module - delta, module );
-            end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
-            while (rel < end && rel->SizeOfBlock)
-            {
-                void *page = (char *)module + rel->VirtualAddress;
-                VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
-                rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
-                                                 (USHORT *)(rel + 1), delta );
-                if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
-                if (!rel) goto error;
-            }
-            /* make sure we don't try again */
-            size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
-            VirtualProtect( nt, size, PAGE_READWRITE, &old );
-            nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
-            VirtualProtect( nt, size, old, &old );
-        }
-    }
-
-    /* make sure imports are relocated too */
-
-    if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
-    {
-        for (i = 0; imports[i].Name && imports[i].FirstThunk; i++)
-        {
-            char *name = (char *)module + imports[i].Name;
-            WCHAR buffer[32], *p = buffer;
-
-            while (p < buffer + 32) if (!(*p++ = *name++)) break;
-            if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) );
-        }
-    }
-
-    return module;
-
-error:
-    FreeLibrary( module );
-    return NULL;
-}
-
-/* call the driver init entry point */
-static NTSTATUS init_driver( HMODULE module, LPCWSTR driver_name, DRIVER_OBJECT **p_driver_obj,
-                             UNICODE_STRING *keyname )
-{
-    unsigned int i;
-    DRIVER_OBJECT *driver_obj = NULL;
-    NTSTATUS status;
-    const IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
-
-    if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
-
-    driver_obj = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                  sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION) );
-    if (driver_obj == NULL)
-        return STATUS_NO_MEMORY;
-
-    driver_obj->Size            = sizeof(*driver_obj);
-    /* FIXME: DriverSection is wrong, see http://stackoverflow.com/questions/9017952/driver-object-driversection */
-    driver_obj->DriverSection   = find_ldr_module( module );
-    driver_obj->DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
-    driver_obj->DriverExtension = (DRIVER_EXTENSION *)(driver_obj + 1);
-
-    driver_obj->DriverExtension->DriverObject   = driver_obj;
-    driver_obj->DriverExtension->ServiceKeyName = *keyname;
-
-    if (WINE_TRACE_ON(relay))
-        WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
-                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer) );
-
-    status = driver_obj->DriverInit( driver_obj, keyname );
-
-    if (WINE_TRACE_ON(relay))
-        WINE_DPRINTF( "%04x:Ret  driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
-                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer), status );
-
-    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_obj );
-    WINE_TRACE( "- DriverInit = %p\n", driver_obj->DriverInit );
-    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj->DriverStartIo );
-    WINE_TRACE( "- DriverUnload = %p\n", driver_obj->DriverUnload );
-    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
-        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj->MajorFunction[i] );
-
-    if (status == STATUS_SUCCESS)
-        *p_driver_obj = driver_obj;
-    else
-        RtlFreeHeap( GetProcessHeap(), 0, driver_obj );
-    return status;
-}
-
 /* call the driver unload function */
 static void unload_driver( HMODULE module, DRIVER_OBJECT *driver_obj )
 {
@@ -194,99 +63,6 @@ static void unload_driver( HMODULE module, DRIVER_OBJECT *driver_obj )
     FreeLibrary( module );
 }
 
-/* load the .sys module for a device driver */
-static HMODULE load_driver( LPCWSTR driver_name, DRIVER_OBJECT **driver_obj )
-{
-    static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
-    static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
-    static const WCHAR postfixW[] = {'.','s','y','s',0};
-    static const WCHAR ntprefixW[] = {'\\','?','?','\\',0};
-    static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',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};
-
-    UNICODE_STRING keypath;
-    HMODULE module;
-    HKEY driver_hkey;
-    LPWSTR path = NULL, str;
-    DWORD type, size;
-
-    str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) );
-    lstrcpyW( str, servicesW );
-    lstrcatW( str, driver_name );
-
-    if (RegOpenKeyW( HKEY_LOCAL_MACHINE, str + 18 /* skip \registry\machine */, &driver_hkey ))
-    {
-        WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(str), GetLastError() );
-        HeapFree( GetProcessHeap(), 0, str);
-        return FALSE;
-    }
-    RtlInitUnicodeString( &keypath, str );
-
-    /* read the executable path from memory */
-    size = 0;
-    if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size ))
-    {
-        str = HeapAlloc( GetProcessHeap(), 0, size );
-        if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size ))
-        {
-            size = ExpandEnvironmentStringsW(str,NULL,0);
-            path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
-            ExpandEnvironmentStringsW(str,path,size);
-        }
-        HeapFree( GetProcessHeap(), 0, str );
-        if (!path) return FALSE;
-
-        if (!strncmpiW( path, systemrootW, 12 ))
-        {
-            WCHAR buffer[MAX_PATH];
-
-            GetWindowsDirectoryW(buffer, MAX_PATH);
-
-            str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer))
-                                                        * sizeof(WCHAR));
-            lstrcpyW(str, buffer);
-            lstrcatW(str, path + 11);
-            HeapFree( GetProcessHeap(), 0, path );
-            path = str;
-        }
-        else if (!strncmpW( path, ntprefixW, 4 ))
-            str = path + 4;
-        else
-            str = path;
-    }
-    else
-    {
-        /* default is to use the driver name + ".sys" */
-        WCHAR buffer[MAX_PATH];
-        GetSystemDirectoryW(buffer, MAX_PATH);
-        path = HeapAlloc(GetProcessHeap(),0,
-          (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
-          *sizeof(WCHAR));
-        lstrcpyW(path, buffer);
-        lstrcatW(path, driversW);
-        lstrcatW(path, driver_name);
-        lstrcatW(path, postfixW);
-        str = path;
-    }
-
-    WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) );
-
-    module = load_driver_module( str );
-    HeapFree( GetProcessHeap(), 0, path );
-    if (!module) return NULL;
-
-    if (init_driver( module, driver_name, driver_obj, &keypath ) != STATUS_SUCCESS)
-    {
-        FreeLibrary(module);
-        return NULL;
-    }
-    return module;
-}
-
 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
 {
     SERVICE_STATUS status;
@@ -339,7 +115,7 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
     status.dwWaitHint                = 10000;
     SetServiceStatus( service_handle, &status );
 
-    driver_module = load_driver( driver_name, &driver_obj );
+    driver_module = wine_ntoskrnl_load_driver( driver_name, &driver_obj );
     if (driver_module)
     {
         status.dwCurrentState     = SERVICE_RUNNING;

--------------2.6.3--




More information about the wine-devel mailing list