2 * Service process to load a kernel driver
4 * Copyright 2007 Alexandre Julliard
5 * Copyright 2016 Sebastian Lackner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/port.h"
28 #define WIN32_NO_STATUS
36 #include "wine/rbtree.h"
37 #include "wine/svcctl.h"
38 #include "wine/unicode.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(winedevice
);
42 WINE_DECLARE_DEBUG_CHANNEL(relay
);
44 extern NTSTATUS CDECL
wine_ntoskrnl_main_loop( HANDLE stop_event
);
46 static const WCHAR winedeviceW
[] = {'w','i','n','e','d','e','v','i','c','e',0};
47 static SERVICE_STATUS_HANDLE service_handle
;
48 static PTP_CLEANUP_GROUP cleanup_group
;
49 static SC_HANDLE manager_handle
;
50 static BOOL shutdown_in_progress
;
51 static HANDLE stop_event
;
55 struct wine_rb_entry entry
;
57 SERVICE_STATUS_HANDLE handle
;
58 DRIVER_OBJECT
*driver_obj
;
62 static int wine_drivers_rb_compare( const void *key
, const struct wine_rb_entry
*entry
)
64 const struct wine_driver
*driver
= WINE_RB_ENTRY_VALUE( entry
, const struct wine_driver
, entry
);
65 return strcmpW( (const WCHAR
*)key
, driver
->name
);
68 static struct wine_rb_tree wine_drivers
= { wine_drivers_rb_compare
};
70 static CRITICAL_SECTION drivers_cs
;
71 static CRITICAL_SECTION_DEBUG critsect_debug
=
74 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
75 0, 0, { (DWORD_PTR
)(__FILE__
": drivers_cs") }
77 static CRITICAL_SECTION drivers_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
79 /* find the LDR_MODULE corresponding to the driver module */
80 static LDR_MODULE
*find_ldr_module( HMODULE module
)
85 LdrLockLoaderLock( 0, NULL
, &magic
);
86 if (LdrFindEntryForAddress( module
, &ldr
))
88 WARN( "module not found for %p\n", module
);
91 LdrUnlockLoaderLock( 0, magic
);
96 /* load the driver module file */
97 static HMODULE
load_driver_module( const WCHAR
*name
)
100 const IMAGE_IMPORT_DESCRIPTOR
*imports
;
101 SYSTEM_BASIC_INFORMATION info
;
105 HMODULE module
= LoadLibraryW( name
);
107 if (!module
) return NULL
;
108 nt
= RtlImageNtHeader( module
);
110 if (!(delta
= (char *)module
- (char *)nt
->OptionalHeader
.ImageBase
)) return module
;
112 /* the loader does not apply relocations to non page-aligned binaries or executables,
113 * we have to do it ourselves */
115 NtQuerySystemInformation( SystemBasicInformation
, &info
, sizeof(info
), NULL
);
116 if (nt
->OptionalHeader
.SectionAlignment
< info
.PageSize
||
117 !(nt
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
))
120 IMAGE_BASE_RELOCATION
*rel
, *end
;
122 if ((rel
= RtlImageDirectoryEntryToData( module
, TRUE
, IMAGE_DIRECTORY_ENTRY_BASERELOC
, &size
)))
124 WINE_TRACE( "%s: relocating from %p to %p\n",
125 wine_dbgstr_w(name
), (char *)module
- delta
, module
);
126 end
= (IMAGE_BASE_RELOCATION
*)((char *)rel
+ size
);
127 while (rel
< end
&& rel
->SizeOfBlock
)
129 void *page
= (char *)module
+ rel
->VirtualAddress
;
130 VirtualProtect( page
, info
.PageSize
, PAGE_EXECUTE_READWRITE
, &old
);
131 rel
= LdrProcessRelocationBlock( page
, (rel
->SizeOfBlock
- sizeof(*rel
)) / sizeof(USHORT
),
132 (USHORT
*)(rel
+ 1), delta
);
133 if (old
!= PAGE_EXECUTE_READWRITE
) VirtualProtect( page
, info
.PageSize
, old
, &old
);
134 if (!rel
) goto error
;
136 /* make sure we don't try again */
137 size
= FIELD_OFFSET( IMAGE_NT_HEADERS
, OptionalHeader
) + nt
->FileHeader
.SizeOfOptionalHeader
;
138 VirtualProtect( nt
, size
, PAGE_READWRITE
, &old
);
139 nt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= 0;
140 VirtualProtect( nt
, size
, old
, &old
);
144 /* make sure imports are relocated too */
146 if ((imports
= RtlImageDirectoryEntryToData( module
, TRUE
, IMAGE_DIRECTORY_ENTRY_IMPORT
, &size
)))
148 for (i
= 0; imports
[i
].Name
&& imports
[i
].FirstThunk
; i
++)
150 char *name
= (char *)module
+ imports
[i
].Name
;
151 WCHAR buffer
[32], *p
= buffer
;
153 while (p
< buffer
+ 32) if (!(*p
++ = *name
++)) break;
154 if (p
<= buffer
+ 32) FreeLibrary( load_driver_module( buffer
) );
161 FreeLibrary( module
);
165 /* load the .sys module for a device driver */
166 static HMODULE
load_driver( const WCHAR
*driver_name
, const UNICODE_STRING
*keyname
)
168 static const WCHAR driversW
[] = {'\\','d','r','i','v','e','r','s','\\',0};
169 static const WCHAR systemrootW
[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
170 static const WCHAR postfixW
[] = {'.','s','y','s',0};
171 static const WCHAR ntprefixW
[] = {'\\','?','?','\\',0};
172 static const WCHAR ImagePathW
[] = {'I','m','a','g','e','P','a','t','h',0};
175 LPWSTR path
= NULL
, str
;
178 if (RegOpenKeyW( HKEY_LOCAL_MACHINE
, keyname
->Buffer
+ 18 /* skip \registry\machine */, &driver_hkey
))
180 WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(keyname
->Buffer
), GetLastError() );
184 /* read the executable path from memory */
186 if (!RegQueryValueExW( driver_hkey
, ImagePathW
, NULL
, &type
, NULL
, &size
))
188 str
= HeapAlloc( GetProcessHeap(), 0, size
);
189 if (!RegQueryValueExW( driver_hkey
, ImagePathW
, NULL
, &type
, (LPBYTE
)str
, &size
))
191 size
= ExpandEnvironmentStringsW(str
,NULL
,0);
192 path
= HeapAlloc(GetProcessHeap(),0,size
*sizeof(WCHAR
));
193 ExpandEnvironmentStringsW(str
,path
,size
);
195 HeapFree( GetProcessHeap(), 0, str
);
198 RegCloseKey( driver_hkey
);
202 if (!strncmpiW( path
, systemrootW
, 12 ))
204 WCHAR buffer
[MAX_PATH
];
206 GetWindowsDirectoryW(buffer
, MAX_PATH
);
208 str
= HeapAlloc(GetProcessHeap(), 0, (size
-11 + strlenW(buffer
))
210 lstrcpyW(str
, buffer
);
211 lstrcatW(str
, path
+ 11);
212 HeapFree( GetProcessHeap(), 0, path
);
215 else if (!strncmpW( path
, ntprefixW
, 4 ))
222 /* default is to use the driver name + ".sys" */
223 WCHAR buffer
[MAX_PATH
];
224 GetSystemDirectoryW(buffer
, MAX_PATH
);
225 path
= HeapAlloc(GetProcessHeap(),0,
226 (strlenW(buffer
) + strlenW(driversW
) + strlenW(driver_name
) + strlenW(postfixW
) + 1)
228 lstrcpyW(path
, buffer
);
229 lstrcatW(path
, driversW
);
230 lstrcatW(path
, driver_name
);
231 lstrcatW(path
, postfixW
);
234 RegCloseKey( driver_hkey
);
236 WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str
) );
238 module
= load_driver_module( str
);
239 HeapFree( GetProcessHeap(), 0, path
);
243 /* call the driver init entry point */
244 static NTSTATUS WINAPI
init_driver( DRIVER_OBJECT
*driver_object
, UNICODE_STRING
*keyname
)
248 const IMAGE_NT_HEADERS
*nt
;
249 const WCHAR
*driver_name
;
252 /* Retrieve driver name from the keyname */
253 driver_name
= strrchrW( keyname
->Buffer
, '\\' );
256 module
= load_driver( driver_name
, keyname
);
258 return STATUS_DLL_INIT_FAILED
;
260 driver_object
->DriverSection
= find_ldr_module( module
);
262 nt
= RtlImageNtHeader( module
);
263 if (!nt
->OptionalHeader
.AddressOfEntryPoint
) return STATUS_SUCCESS
;
264 driver_object
->DriverInit
= (PDRIVER_INITIALIZE
)((char *)module
+ nt
->OptionalHeader
.AddressOfEntryPoint
);
266 if (WINE_TRACE_ON(relay
))
267 WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
268 driver_object
->DriverInit
, driver_object
, wine_dbgstr_w(keyname
->Buffer
) );
270 status
= driver_object
->DriverInit( driver_object
, keyname
);
272 if (WINE_TRACE_ON(relay
))
273 WINE_DPRINTF( "%04x:Ret driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
274 driver_object
->DriverInit
, driver_object
, wine_dbgstr_w(keyname
->Buffer
), status
);
276 WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name
), driver_object
);
277 WINE_TRACE( "- DriverInit = %p\n", driver_object
->DriverInit
);
278 WINE_TRACE( "- DriverStartIo = %p\n", driver_object
->DriverStartIo
);
279 WINE_TRACE( "- DriverUnload = %p\n", driver_object
->DriverUnload
);
280 for (i
= 0; i
<= IRP_MJ_MAXIMUM_FUNCTION
; i
++)
281 WINE_TRACE( "- MajorFunction[%d] = %p\n", i
, driver_object
->MajorFunction
[i
] );
286 /* helper function to update service status */
287 static void set_service_status( SERVICE_STATUS_HANDLE handle
, DWORD state
, DWORD accepted
)
289 SERVICE_STATUS status
;
290 status
.dwServiceType
= SERVICE_WIN32
;
291 status
.dwCurrentState
= state
;
292 status
.dwControlsAccepted
= accepted
;
293 status
.dwWin32ExitCode
= 0;
294 status
.dwServiceSpecificExitCode
= 0;
295 status
.dwCheckPoint
= 0;
296 status
.dwWaitHint
= (state
== SERVICE_START_PENDING
) ? 10000 : 0;
297 SetServiceStatus( handle
, &status
);
300 static void WINAPI
async_unload_driver( PTP_CALLBACK_INSTANCE instance
, void *context
)
302 struct wine_driver
*driver
= context
;
303 DRIVER_OBJECT
*driver_obj
= driver
->driver_obj
;
306 if (WINE_TRACE_ON(relay
))
307 WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n", GetCurrentThreadId(),
308 driver_obj
->DriverUnload
, driver_obj
);
310 driver_obj
->DriverUnload( driver_obj
);
312 if (WINE_TRACE_ON(relay
))
313 WINE_DPRINTF( "%04x:Ret driver unload %p (obj=%p)\n", GetCurrentThreadId(),
314 driver_obj
->DriverUnload
, driver_obj
);
316 ldr
= driver_obj
->DriverSection
;
317 FreeLibrary( ldr
->BaseAddress
);
318 IoDeleteDriver( driver_obj
);
319 ObDereferenceObject( driver_obj
);
321 set_service_status( driver
->handle
, SERVICE_STOPPED
, 0 );
322 CloseServiceHandle( (void *)driver
->handle
);
323 HeapFree( GetProcessHeap(), 0, driver
);
326 /* call the driver unload function */
327 static NTSTATUS
unload_driver( struct wine_rb_entry
*entry
, BOOL destroy
)
329 TP_CALLBACK_ENVIRON environment
;
330 struct wine_driver
*driver
= WINE_RB_ENTRY_VALUE( entry
, struct wine_driver
, entry
);
331 DRIVER_OBJECT
*driver_obj
= driver
->driver_obj
;
335 TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver
->name
) );
336 return STATUS_UNSUCCESSFUL
;
338 if (!driver_obj
->DriverUnload
)
340 TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver
->name
) );
341 return STATUS_UNSUCCESSFUL
;
344 TRACE( "stopping driver %s\n", wine_dbgstr_w(driver
->name
) );
345 set_service_status( driver
->handle
, SERVICE_STOP_PENDING
, 0 );
349 async_unload_driver( NULL
, driver
);
350 return STATUS_SUCCESS
;
353 wine_rb_remove( &wine_drivers
, &driver
->entry
);
355 memset( &environment
, 0, sizeof(environment
) );
356 environment
.Version
= 1;
357 environment
.CleanupGroup
= cleanup_group
;
359 /* don't block the service control handler */
360 if (!TrySubmitThreadpoolCallback( async_unload_driver
, driver
, &environment
))
361 async_unload_driver( NULL
, driver
);
363 return STATUS_SUCCESS
;
366 static void WINAPI
async_create_driver( PTP_CALLBACK_INSTANCE instance
, void *context
)
368 static const WCHAR driverW
[] = {'\\','D','r','i','v','e','r','\\',0};
369 struct wine_driver
*driver
= context
;
370 DRIVER_OBJECT
*driver_obj
;
371 UNICODE_STRING drv_name
;
375 if (!(str
= HeapAlloc( GetProcessHeap(), 0, sizeof(driverW
) + strlenW(driver
->name
)*sizeof(WCHAR
) )))
378 lstrcpyW( str
, driverW
);
379 lstrcatW( str
, driver
->name
);
380 RtlInitUnicodeString( &drv_name
, str
);
382 status
= IoCreateDriver( &drv_name
, init_driver
);
383 if (status
!= STATUS_SUCCESS
)
385 ERR( "failed to create driver %s: %08x\n", debugstr_w(driver
->name
), status
);
386 RtlFreeUnicodeString( &drv_name
);
390 status
= ObReferenceObjectByName( &drv_name
, OBJ_CASE_INSENSITIVE
, NULL
,
391 0, NULL
, KernelMode
, NULL
, (void **)&driver_obj
);
392 RtlFreeUnicodeString( &drv_name
);
393 if (status
!= STATUS_SUCCESS
)
395 ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver
->name
), status
);
399 EnterCriticalSection( &drivers_cs
);
400 driver
->driver_obj
= driver_obj
;
401 set_service_status( driver
->handle
, SERVICE_RUNNING
,
402 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
);
403 LeaveCriticalSection( &drivers_cs
);
407 EnterCriticalSection( &drivers_cs
);
408 wine_rb_remove( &wine_drivers
, &driver
->entry
);
409 LeaveCriticalSection( &drivers_cs
);
411 set_service_status( driver
->handle
, SERVICE_STOPPED
, 0 );
412 CloseServiceHandle( (void *)driver
->handle
);
413 HeapFree( GetProcessHeap(), 0, driver
);
416 /* load a driver and notify services.exe about the status change */
417 static NTSTATUS
create_driver( const WCHAR
*driver_name
)
419 TP_CALLBACK_ENVIRON environment
;
420 struct wine_driver
*driver
;
423 length
= FIELD_OFFSET( struct wine_driver
, name
[strlenW(driver_name
) + 1] );
424 if (!(driver
= HeapAlloc( GetProcessHeap(), 0, length
)))
425 return STATUS_NO_MEMORY
;
427 strcpyW( driver
->name
, driver_name
);
428 driver
->driver_obj
= NULL
;
430 if (!(driver
->handle
= (void *)OpenServiceW( manager_handle
, driver_name
, SERVICE_SET_STATUS
)))
432 HeapFree( GetProcessHeap(), 0, driver
);
433 return STATUS_UNSUCCESSFUL
;
436 if (wine_rb_put( &wine_drivers
, driver_name
, &driver
->entry
))
438 CloseServiceHandle( (void *)driver
->handle
);
439 HeapFree( GetProcessHeap(), 0, driver
);
440 return STATUS_UNSUCCESSFUL
;
443 TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name
) );
444 set_service_status( driver
->handle
, SERVICE_START_PENDING
, 0 );
446 memset( &environment
, 0, sizeof(environment
) );
447 environment
.Version
= 1;
448 environment
.CleanupGroup
= cleanup_group
;
450 /* don't block the service control handler */
451 if (!TrySubmitThreadpoolCallback( async_create_driver
, driver
, &environment
))
452 async_create_driver( NULL
, driver
);
454 return STATUS_SUCCESS
;
457 static void wine_drivers_rb_destroy( struct wine_rb_entry
*entry
, void *context
)
459 if (unload_driver( entry
, TRUE
) != STATUS_SUCCESS
)
461 struct wine_driver
*driver
= WINE_RB_ENTRY_VALUE( entry
, struct wine_driver
, entry
);
462 ObDereferenceObject( driver
->driver_obj
);
463 CloseServiceHandle( (void *)driver
->handle
);
464 HeapFree( GetProcessHeap(), 0, driver
);
468 static void WINAPI
async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance
, void *context
)
470 CloseThreadpoolCleanupGroupMembers( cleanup_group
, FALSE
, NULL
);
472 EnterCriticalSection( &drivers_cs
);
473 wine_rb_destroy( &wine_drivers
, wine_drivers_rb_destroy
, NULL
);
474 LeaveCriticalSection( &drivers_cs
);
476 SetEvent( stop_event
);
479 static void shutdown_drivers( void )
481 if (shutdown_in_progress
) return;
483 /* don't block the service control handler */
484 if (!TrySubmitThreadpoolCallback( async_shutdown_drivers
, NULL
, NULL
))
485 async_shutdown_drivers( NULL
, NULL
);
487 shutdown_in_progress
= TRUE
;
490 static DWORD
device_handler( DWORD ctrl
, const WCHAR
*driver_name
)
492 struct wine_rb_entry
*entry
;
493 DWORD result
= NO_ERROR
;
495 if (shutdown_in_progress
)
496 return ERROR_SERVICE_CANNOT_ACCEPT_CTRL
;
498 EnterCriticalSection( &drivers_cs
);
499 entry
= wine_rb_get( &wine_drivers
, driver_name
);
503 case SERVICE_CONTROL_START
:
505 result
= RtlNtStatusToDosError(create_driver( driver_name
));
508 case SERVICE_CONTROL_STOP
:
510 result
= RtlNtStatusToDosError(unload_driver( entry
, FALSE
));
514 FIXME( "got driver ctrl %x for %s\n", ctrl
, wine_dbgstr_w(driver_name
) );
517 LeaveCriticalSection( &drivers_cs
);
521 static DWORD WINAPI
service_handler( DWORD ctrl
, DWORD event_type
, LPVOID event_data
, LPVOID context
)
523 const WCHAR
*service_group
= context
;
525 if (ctrl
& SERVICE_CONTROL_FORWARD_FLAG
)
527 if (!event_data
) return ERROR_INVALID_PARAMETER
;
528 return device_handler( ctrl
& ~SERVICE_CONTROL_FORWARD_FLAG
, (const WCHAR
*)event_data
);
533 case SERVICE_CONTROL_STOP
:
534 case SERVICE_CONTROL_SHUTDOWN
:
535 TRACE( "shutting down %s\n", wine_dbgstr_w(service_group
) );
536 set_service_status( service_handle
, SERVICE_STOP_PENDING
, 0 );
540 FIXME( "got service ctrl %x for %s\n", ctrl
, wine_dbgstr_w(service_group
) );
541 set_service_status( service_handle
, SERVICE_RUNNING
,
542 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
);
547 static void WINAPI
ServiceMain( DWORD argc
, LPWSTR
*argv
)
549 const WCHAR
*service_group
= (argc
>= 2) ? argv
[1] : argv
[0];
551 if (!(stop_event
= CreateEventW( NULL
, TRUE
, FALSE
, NULL
)))
553 if (!(cleanup_group
= CreateThreadpoolCleanupGroup()))
555 if (!(manager_handle
= OpenSCManagerW( NULL
, NULL
, SC_MANAGER_CONNECT
)))
557 if (!(service_handle
= RegisterServiceCtrlHandlerExW( winedeviceW
, service_handler
, (void *)service_group
)))
560 TRACE( "starting service group %s\n", wine_dbgstr_w(service_group
) );
561 set_service_status( service_handle
, SERVICE_RUNNING
,
562 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
);
564 wine_ntoskrnl_main_loop( stop_event
);
566 TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group
) );
567 set_service_status( service_handle
, SERVICE_STOPPED
, 0 );
568 CloseServiceHandle( manager_handle
);
569 CloseThreadpoolCleanupGroup( cleanup_group
);
570 CloseHandle( stop_event
);
573 int wmain( int argc
, WCHAR
*argv
[] )
575 SERVICE_TABLE_ENTRYW service_table
[2];
577 service_table
[0].lpServiceName
= (void *)winedeviceW
;
578 service_table
[0].lpServiceProc
= ServiceMain
;
579 service_table
[1].lpServiceName
= NULL
;
580 service_table
[1].lpServiceProc
= NULL
;
582 StartServiceCtrlDispatcherW( service_table
);