Signed-off-by: Zebediah Figura z.figura12@gmail.com --- loader/wine.inf.in | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 11298301f55..9f97b346f93 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -465,6 +465,8 @@ HKLM,System\CurrentControlSet\Control\Class{4d36e967-e325-11ce-bfc1-08002be1031 HKLM,System\CurrentControlSet\Control\Class{4d36e967-e325-11ce-bfc1-08002be10318},"Class",,"DiskDrive" HKLM,System\CurrentControlSet\Control\Class{4d36e978-e325-11ce-bfc1-08002be10318},,,"Ports (COM & LPT)" HKLM,System\CurrentControlSet\Control\Class{4d36e978-e325-11ce-bfc1-08002be10318},"Class",,"Ports" +HKLM,System\CurrentControlSet\Control\Class{4d36e97d-e325-11ce-bfc1-08002be10318},,,"System devices" +HKLM,System\CurrentControlSet\Control\Class{4d36e97d-e325-11ce-bfc1-08002be10318},"Class",,"System" HKLM,System\CurrentControlSet\Control\Class{6bdd1fc6-810f-11d0-bec7-08002be2092f},,,"Imaging devices" HKLM,System\CurrentControlSet\Control\Class{6bdd1fc6-810f-11d0-bec7-08002be2092f},"Class",,"Image" HKLM,System\CurrentControlSet\Control\Class{745a17a0-74d3-11d0-b6fe-00a0c90f57da},,,"Human Interface Devices"
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntoskrnl.exe/ntoskrnl.c | 5 ++++ dlls/ntoskrnl.exe/ntoskrnl_private.h | 3 +++ dlls/ntoskrnl.exe/pnp.c | 36 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 6daa25e8055..65239c92558 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -850,6 +850,8 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) PsInitialSystemProcess = IoGetCurrentProcess(); request_thread = GetCurrentThreadId();
+ pnp_manager_start(); + handles[0] = stop_event; handles[1] = manager;
@@ -917,6 +919,9 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) }
done: + /* Native PnP drivers expect that all of their devices will be removed when + * their unload routine is called, so we must stop the PnP manager first. */ + pnp_manager_stop(); wine_rb_destroy( &wine_drivers, unload_driver, NULL ); return status; } diff --git a/dlls/ntoskrnl.exe/ntoskrnl_private.h b/dlls/ntoskrnl.exe/ntoskrnl_private.h index 2823a1cd4f1..1c5952f7c10 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl_private.h +++ b/dlls/ntoskrnl.exe/ntoskrnl_private.h @@ -76,6 +76,9 @@ extern POBJECT_TYPE SeTokenObjectType;
void ObReferenceObject( void *obj ) DECLSPEC_HIDDEN;
+void pnp_manager_start(void) DECLSPEC_HIDDEN; +void pnp_manager_stop(void) DECLSPEC_HIDDEN; + 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', diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index e1b348b82e3..6f2283bc834 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -3,6 +3,7 @@ * * Copyright 2016 Sebastian Lackner * Copyright 2016 Aric Stewart for CodeWeavers + * Copyright 2019 Zebediah Figura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -751,3 +752,38 @@ POWER_STATE WINAPI PoSetPowerState( DEVICE_OBJECT *device, POWER_STATE_TYPE type FIXME("device %p, type %u, state %u, stub!\n", device, type, state.DeviceState); return state; } + +static DRIVER_OBJECT *pnp_manager; + +static NTSTATUS WINAPI pnp_manager_device_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + + TRACE("device %p, irp %p, minor function %#x.\n", device, irp, stack->MinorFunction); + + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return irp->IoStatus.u.Status; +} + +static NTSTATUS WINAPI pnp_manager_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *keypath ) +{ + pnp_manager = driver; + driver->MajorFunction[IRP_MJ_PNP] = pnp_manager_device_pnp; + return STATUS_SUCCESS; +} + +void pnp_manager_start(void) +{ + static const WCHAR driver_nameW[] = {'\','D','r','i','v','e','r','\','P','n','p','M','a','n','a','g','e','r',0}; + UNICODE_STRING driver_nameU; + NTSTATUS status; + + RtlInitUnicodeString( &driver_nameU, driver_nameW ); + if ((status = IoCreateDriver( &driver_nameU, pnp_manager_driver_entry ))) + ERR("Failed to create PnP manager driver, status %#x.\n", status); +} + +void pnp_manager_stop(void) +{ + IoDeleteDriver( pnp_manager ); +}
Signed-off-by: Zebediah Figura z.figura12@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); +}
We want to load setupapi from services.exe. We do not want to initialize user32 there, as that results in the creation of a window station.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/setupapi/Makefile.in b/dlls/setupapi/Makefile.in index bf157cb7767..6bb9d59bf59 100644 --- a/dlls/setupapi/Makefile.in +++ b/dlls/setupapi/Makefile.in @@ -1,8 +1,8 @@ EXTRADEFS = -D_SETUPAPI_ MODULE = setupapi.dll IMPORTLIB = setupapi -IMPORTS = uuid user32 version advapi32 rpcrt4 -DELAYIMPORTS = shell32 wintrust ole32 winspool comdlg32 +IMPORTS = uuid version advapi32 rpcrt4 +DELAYIMPORTS = shell32 wintrust ole32 winspool comdlg32 user32
C_SRCS = \ devinst.c \
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- Windows starts up all root PnP drivers on startup. Native root PnP drivers are generally marked as SERVICE_DEMAND_START, so they will not be started up otherwise.
If using setupapi from services.exe is unpalatable or architecturally wrong, I can send a patch to duplicate the logic, so that we check the registry directly. I would appreciate an explanation of why this is the case, however. If loading root PnP drivers on startup itself from services.exe is unpalatable or architecturally wrong, we will have to warn users that any root PnP drivers they install will have to have their startup type changed from SERVICE_DEMAND_START.
programs/services/Makefile.in | 2 +- programs/services/services.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/programs/services/Makefile.in b/programs/services/Makefile.in index e06514cbdd1..560e277a9bf 100644 --- a/programs/services/Makefile.in +++ b/programs/services/Makefile.in @@ -1,5 +1,5 @@ MODULE = services.exe -IMPORTS = rpcrt4 advapi32 userenv +IMPORTS = rpcrt4 advapi32 userenv setupapi
EXTRADLLFLAGS = -mconsole -mno-cygwin
diff --git a/programs/services/services.c b/programs/services/services.c index 2dff8115595..96a18bcd18e 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -27,6 +27,7 @@ #include <winsvc.h> #include <rpc.h> #include <userenv.h> +#include <setupapi.h>
#include "wine/debug.h" #include "wine/heap.h" @@ -423,25 +424,50 @@ static BOOL schedule_delayed_autostart(struct service_entry **services, unsigned return TRUE; }
+static BOOL is_root_pnp_service(const struct service_entry *service, HDEVINFO set) +{ + SP_DEVINFO_DATA device = {sizeof(device)}; + WCHAR name[MAX_SERVICE_NAME]; + unsigned int i; + + for (i = 0; SetupDiEnumDeviceInfo(set, i, &device); ++i) + { + if (SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_SERVICE, NULL, + (BYTE *)name, sizeof(name), NULL) + && !wcsicmp(name, service->name)) + { + return TRUE; + } + } + + return FALSE; +} + static void scmdatabase_autostart_services(struct scmdatabase *db) { + static const WCHAR rootW[] = {'R','O','O','T',0}; struct service_entry **services_list; unsigned int i = 0; unsigned int size = 32; unsigned int delayed_cnt = 0; struct service_entry *service; + HDEVINFO set;
services_list = HeapAlloc(GetProcessHeap(), 0, size * sizeof(services_list[0])); if (!services_list) return;
+ if ((set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES )) == INVALID_HANDLE_VALUE) + WINE_ERR("Failed to enumerate devices, error %#x.\n", GetLastError()); + scmdatabase_lock(db);
LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry) { if (service->config.dwStartType == SERVICE_BOOT_START || service->config.dwStartType == SERVICE_SYSTEM_START || - service->config.dwStartType == SERVICE_AUTO_START) + service->config.dwStartType == SERVICE_AUTO_START || + (set != INVALID_HANDLE_VALUE && is_root_pnp_service(set, service))) { if (i+1 >= size) { @@ -482,6 +508,7 @@ static void scmdatabase_autostart_services(struct scmdatabase *db)
if (!delayed_cnt || !schedule_delayed_autostart(services_list, delayed_cnt)) heap_free(services_list); + SetupDiDestroyDeviceInfoList(set); }
static void scmdatabase_wait_terminate(struct scmdatabase *db)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=54026
Your paranoid android.
=== debian9 (32 bit report) ===
services.exe: service.c:422: Test failed: service: Unexpected GetLastError: 0x6.
=== debian9 (32 bit Chinese:China report) ===
services.exe: service.c:422: Test failed: service: Unexpected GetLastError: 0x6.
=== debian9 (32 bit WoW report) ===
services.exe: service.c:422: Test failed: service: Unexpected GetLastError: 0x6.
=== debian9 (64 bit WoW report) ===
services.exe: service.c:422: Test failed: service: Unexpected GetLastError: 0x6.