d3d10core/tests: Add tests for GenerateMips().
[wine.git] / programs / winedevice / device.c
blob397676d4c0a3946e21454a86d571ef13157e3301
1 /*
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winternl.h"
32 #include "winreg.h"
33 #include "winnls.h"
34 #include "winsvc.h"
35 #include "ddk/wdm.h"
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;
53 struct wine_driver
55 struct wine_rb_entry entry;
57 SERVICE_STATUS_HANDLE handle;
58 DRIVER_OBJECT *driver_obj;
59 WCHAR name[1];
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 =
73 0, 0, &drivers_cs,
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 )
82 LDR_MODULE *ldr;
83 ULONG_PTR magic;
85 LdrLockLoaderLock( 0, NULL, &magic );
86 if (LdrFindEntryForAddress( module, &ldr ))
88 WARN( "module not found for %p\n", module );
89 ldr = NULL;
91 LdrUnlockLoaderLock( 0, magic );
93 return ldr;
96 /* load the driver module file */
97 static HMODULE load_driver_module( const WCHAR *name )
99 IMAGE_NT_HEADERS *nt;
100 const IMAGE_IMPORT_DESCRIPTOR *imports;
101 SYSTEM_BASIC_INFORMATION info;
102 int i;
103 INT_PTR delta;
104 ULONG size;
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))
119 DWORD old;
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 ) );
158 return module;
160 error:
161 FreeLibrary( module );
162 return NULL;
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};
173 HKEY driver_hkey;
174 HMODULE module;
175 LPWSTR path = NULL, str;
176 DWORD type, size;
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() );
181 return NULL;
184 /* read the executable path from memory */
185 size = 0;
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 );
196 if (!path)
198 RegCloseKey( driver_hkey );
199 return NULL;
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))
209 * sizeof(WCHAR));
210 lstrcpyW(str, buffer);
211 lstrcatW(str, path + 11);
212 HeapFree( GetProcessHeap(), 0, path );
213 path = str;
215 else if (!strncmpW( path, ntprefixW, 4 ))
216 str = path + 4;
217 else
218 str = path;
220 else
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)
227 *sizeof(WCHAR));
228 lstrcpyW(path, buffer);
229 lstrcatW(path, driversW);
230 lstrcatW(path, driver_name);
231 lstrcatW(path, postfixW);
232 str = path;
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 );
240 return module;
243 /* call the driver init entry point */
244 static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING *keyname )
246 unsigned int i;
247 NTSTATUS status;
248 const IMAGE_NT_HEADERS *nt;
249 const WCHAR *driver_name;
250 HMODULE module;
252 /* Retrieve driver name from the keyname */
253 driver_name = strrchrW( keyname->Buffer, '\\' );
254 driver_name++;
256 module = load_driver( driver_name, keyname );
257 if (!module)
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 TRACE_(relay)( "\1Call driver init %p (obj=%p,str=%s)\n",
267 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer) );
269 status = driver_object->DriverInit( driver_object, keyname );
271 TRACE_(relay)( "\1Ret driver init %p (obj=%p,str=%s) retval=%08x\n",
272 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer), status );
274 WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_object );
275 WINE_TRACE( "- DriverInit = %p\n", driver_object->DriverInit );
276 WINE_TRACE( "- DriverStartIo = %p\n", driver_object->DriverStartIo );
277 WINE_TRACE( "- DriverUnload = %p\n", driver_object->DriverUnload );
278 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
279 WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_object->MajorFunction[i] );
281 return status;
284 /* helper function to update service status */
285 static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted )
287 SERVICE_STATUS status;
288 status.dwServiceType = SERVICE_WIN32;
289 status.dwCurrentState = state;
290 status.dwControlsAccepted = accepted;
291 status.dwWin32ExitCode = 0;
292 status.dwServiceSpecificExitCode = 0;
293 status.dwCheckPoint = 0;
294 status.dwWaitHint = (state == SERVICE_START_PENDING) ? 10000 : 0;
295 SetServiceStatus( handle, &status );
298 static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *context )
300 struct wine_driver *driver = context;
301 DRIVER_OBJECT *driver_obj = driver->driver_obj;
302 LDR_MODULE *ldr;
304 TRACE_(relay)( "\1Call driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
306 driver_obj->DriverUnload( driver_obj );
308 TRACE_(relay)( "\1Ret driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
310 ldr = driver_obj->DriverSection;
311 FreeLibrary( ldr->BaseAddress );
312 IoDeleteDriver( driver_obj );
313 ObDereferenceObject( driver_obj );
315 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
316 CloseServiceHandle( (void *)driver->handle );
317 HeapFree( GetProcessHeap(), 0, driver );
320 /* call the driver unload function */
321 static NTSTATUS unload_driver( struct wine_rb_entry *entry, BOOL destroy )
323 TP_CALLBACK_ENVIRON environment;
324 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
325 DRIVER_OBJECT *driver_obj = driver->driver_obj;
327 if (!driver_obj)
329 TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver->name) );
330 return STATUS_UNSUCCESSFUL;
332 if (!driver_obj->DriverUnload)
334 TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) );
335 return STATUS_UNSUCCESSFUL;
338 TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) );
339 set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
341 if (destroy)
343 async_unload_driver( NULL, driver );
344 return STATUS_SUCCESS;
347 wine_rb_remove( &wine_drivers, &driver->entry );
349 memset( &environment, 0, sizeof(environment) );
350 environment.Version = 1;
351 environment.CleanupGroup = cleanup_group;
353 /* don't block the service control handler */
354 if (!TrySubmitThreadpoolCallback( async_unload_driver, driver, &environment ))
355 async_unload_driver( NULL, driver );
357 return STATUS_SUCCESS;
360 static void WINAPI async_create_driver( PTP_CALLBACK_INSTANCE instance, void *context )
362 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
363 struct wine_driver *driver = context;
364 DRIVER_OBJECT *driver_obj;
365 UNICODE_STRING drv_name;
366 NTSTATUS status;
367 WCHAR *str;
369 if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) )))
370 goto error;
372 lstrcpyW( str, driverW);
373 lstrcatW( str, driver->name );
374 RtlInitUnicodeString( &drv_name, str );
376 status = IoCreateDriver( &drv_name, init_driver );
377 if (status != STATUS_SUCCESS)
379 ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status );
380 RtlFreeUnicodeString( &drv_name );
381 goto error;
384 status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
385 0, NULL, KernelMode, NULL, (void **)&driver_obj );
386 RtlFreeUnicodeString( &drv_name );
387 if (status != STATUS_SUCCESS)
389 ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status );
390 goto error;
393 EnterCriticalSection( &drivers_cs );
394 driver->driver_obj = driver_obj;
395 set_service_status( driver->handle, SERVICE_RUNNING,
396 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
397 LeaveCriticalSection( &drivers_cs );
398 return;
400 error:
401 EnterCriticalSection( &drivers_cs );
402 wine_rb_remove( &wine_drivers, &driver->entry );
403 LeaveCriticalSection( &drivers_cs );
405 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
406 CloseServiceHandle( (void *)driver->handle );
407 HeapFree( GetProcessHeap(), 0, driver );
410 /* load a driver and notify services.exe about the status change */
411 static NTSTATUS create_driver( const WCHAR *driver_name )
413 TP_CALLBACK_ENVIRON environment;
414 struct wine_driver *driver;
415 DWORD length;
417 length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] );
418 if (!(driver = HeapAlloc( GetProcessHeap(), 0, length )))
419 return STATUS_NO_MEMORY;
421 strcpyW( driver->name, driver_name );
422 driver->driver_obj = NULL;
424 if (!(driver->handle = (void *)OpenServiceW( manager_handle, driver_name, SERVICE_SET_STATUS )))
426 HeapFree( GetProcessHeap(), 0, driver );
427 return STATUS_UNSUCCESSFUL;
430 if (wine_rb_put( &wine_drivers, driver_name, &driver->entry ))
432 CloseServiceHandle( (void *)driver->handle );
433 HeapFree( GetProcessHeap(), 0, driver );
434 return STATUS_UNSUCCESSFUL;
437 TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) );
438 set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
440 memset( &environment, 0, sizeof(environment) );
441 environment.Version = 1;
442 environment.CleanupGroup = cleanup_group;
444 /* don't block the service control handler */
445 if (!TrySubmitThreadpoolCallback( async_create_driver, driver, &environment ))
446 async_create_driver( NULL, driver );
448 return STATUS_SUCCESS;
451 static void wine_drivers_rb_destroy( struct wine_rb_entry *entry, void *context )
453 if (unload_driver( entry, TRUE ) != STATUS_SUCCESS)
455 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
456 ObDereferenceObject( driver->driver_obj );
457 CloseServiceHandle( (void *)driver->handle );
458 HeapFree( GetProcessHeap(), 0, driver );
462 static void WINAPI async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance, void *context )
464 CloseThreadpoolCleanupGroupMembers( cleanup_group, FALSE, NULL );
466 EnterCriticalSection( &drivers_cs );
467 wine_rb_destroy( &wine_drivers, wine_drivers_rb_destroy, NULL );
468 LeaveCriticalSection( &drivers_cs );
470 SetEvent( stop_event );
473 static void shutdown_drivers( void )
475 if (shutdown_in_progress) return;
477 /* don't block the service control handler */
478 if (!TrySubmitThreadpoolCallback( async_shutdown_drivers, NULL, NULL ))
479 async_shutdown_drivers( NULL, NULL );
481 shutdown_in_progress = TRUE;
484 static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
486 struct wine_rb_entry *entry;
487 DWORD result = NO_ERROR;
489 if (shutdown_in_progress)
490 return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
492 EnterCriticalSection( &drivers_cs );
493 entry = wine_rb_get( &wine_drivers, driver_name );
495 switch (ctrl)
497 case SERVICE_CONTROL_START:
498 if (entry) break;
499 result = RtlNtStatusToDosError(create_driver( driver_name ));
500 break;
502 case SERVICE_CONTROL_STOP:
503 if (!entry) break;
504 result = RtlNtStatusToDosError(unload_driver( entry, FALSE ));
505 break;
507 default:
508 FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
509 break;
511 LeaveCriticalSection( &drivers_cs );
512 return result;
515 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
517 const WCHAR *service_group = context;
519 if (ctrl & SERVICE_CONTROL_FORWARD_FLAG)
521 if (!event_data) return ERROR_INVALID_PARAMETER;
522 return device_handler( ctrl & ~SERVICE_CONTROL_FORWARD_FLAG, (const WCHAR *)event_data );
525 switch (ctrl)
527 case SERVICE_CONTROL_STOP:
528 case SERVICE_CONTROL_SHUTDOWN:
529 TRACE( "shutting down %s\n", wine_dbgstr_w(service_group) );
530 set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
531 shutdown_drivers();
532 return NO_ERROR;
533 default:
534 FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(service_group) );
535 set_service_status( service_handle, SERVICE_RUNNING,
536 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
537 return NO_ERROR;
541 static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
543 const WCHAR *service_group = (argc >= 2) ? argv[1] : argv[0];
545 if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
546 return;
547 if (!(cleanup_group = CreateThreadpoolCleanupGroup()))
548 return;
549 if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
550 return;
551 if (!(service_handle = RegisterServiceCtrlHandlerExW( winedeviceW, service_handler, (void *)service_group )))
552 return;
554 TRACE( "starting service group %s\n", wine_dbgstr_w(service_group) );
555 set_service_status( service_handle, SERVICE_RUNNING,
556 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
558 wine_ntoskrnl_main_loop( stop_event );
560 TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) );
561 set_service_status( service_handle, SERVICE_STOPPED, 0 );
562 CloseServiceHandle( manager_handle );
563 CloseThreadpoolCleanupGroup( cleanup_group );
564 CloseHandle( stop_event );
567 int wmain( int argc, WCHAR *argv[] )
569 SERVICE_TABLE_ENTRYW service_table[2];
571 service_table[0].lpServiceName = (void *)winedeviceW;
572 service_table[0].lpServiceProc = ServiceMain;
573 service_table[1].lpServiceName = NULL;
574 service_table[1].lpServiceProc = NULL;
576 StartServiceCtrlDispatcherW( service_table );
577 return 0;