- Isolate driver name for sysparams test crash, one more try. CORE-7574
[reactos.git] / reactos / win32ss / gdi / eng / ldevobj.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Support for logical devices
5 * FILE: subsystems/win32/win32k/eng/ldevobj.c
6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 #include <win32k.h>
10 #define NDEBUG
11 #include <debug.h>
12 DBG_DEFAULT_CHANNEL(EngLDev);
13
14 #ifndef RVA_TO_ADDR
15 #define RVA_TO_ADDR(Base,Rva) ((PVOID)(((ULONG_PTR)(Base)) + (Rva)))
16 #endif
17
18 /** Globals *******************************************************************/
19
20 static HSEMAPHORE ghsemLDEVList;
21 static LDEVOBJ *gpldevHead = NULL;
22 static LDEVOBJ *gpldevWin32k = NULL;
23
24
25 /** Private functions *********************************************************/
26
27 INIT_FUNCTION
28 NTSTATUS
29 NTAPI
30 InitLDEVImpl(VOID)
31 {
32 ULONG cbSize;
33
34 /* Initialize the loader lock */
35 ghsemLDEVList = EngCreateSemaphore();
36 if (!ghsemLDEVList)
37 {
38 ERR("Failed to create ghsemLDEVList\n");
39 return STATUS_INSUFFICIENT_RESOURCES;
40 }
41
42 /* Allocate a LDEVOBJ for win32k */
43 gpldevWin32k = ExAllocatePoolWithTag(PagedPool,
44 sizeof(LDEVOBJ) +
45 sizeof(SYSTEM_GDI_DRIVER_INFORMATION),
46 GDITAG_LDEV);
47 if (!gpldevWin32k)
48 {
49 return STATUS_NO_MEMORY;
50 }
51
52 /* Initialize the LDEVOBJ for win32k */
53 gpldevWin32k->pldevNext = NULL;
54 gpldevWin32k->pldevPrev = NULL;
55 gpldevWin32k->ldevtype = LDEV_DEVICE_DISPLAY;
56 gpldevWin32k->cRefs = 1;
57 gpldevWin32k->ulDriverVersion = GDI_ENGINE_VERSION;
58 gpldevWin32k->pGdiDriverInfo = (PVOID)(gpldevWin32k + 1);
59 RtlInitUnicodeString(&gpldevWin32k->pGdiDriverInfo->DriverName,
60 L"\\SystemRoot\\System32\\win32k.sys");
61 gpldevWin32k->pGdiDriverInfo->ImageAddress = &__ImageBase;
62 gpldevWin32k->pGdiDriverInfo->SectionPointer = NULL;
63 gpldevWin32k->pGdiDriverInfo->EntryPoint = (PVOID)DriverEntry;
64 gpldevWin32k->pGdiDriverInfo->ExportSectionPointer =
65 RtlImageDirectoryEntryToData(&__ImageBase,
66 TRUE,
67 IMAGE_DIRECTORY_ENTRY_EXPORT,
68 &cbSize);
69 gpldevWin32k->pGdiDriverInfo->ImageLength = 0; // FIXME
70
71 return STATUS_SUCCESS;
72 }
73
74 static
75 PLDEVOBJ
76 LDEVOBJ_AllocLDEV(
77 _In_ LDEVTYPE ldevtype)
78 {
79 PLDEVOBJ pldev;
80
81 /* Allocate the structure from paged pool */
82 pldev = ExAllocatePoolWithTag(PagedPool, sizeof(LDEVOBJ), GDITAG_LDEV);
83 if (!pldev)
84 {
85 ERR("Failed to allocate LDEVOBJ.\n");
86 return NULL;
87 }
88
89 /* Zero out the structure */
90 RtlZeroMemory(pldev, sizeof(LDEVOBJ));
91
92 /* Set the ldevtype */
93 pldev->ldevtype = ldevtype;
94
95 return pldev;
96 }
97
98 static
99 VOID
100 LDEVOBJ_vFreeLDEV(
101 _In_ _Post_ptr_invalid_ PLDEVOBJ pldev)
102 {
103 /* Make sure we don't have a driver loaded */
104 ASSERT(pldev && pldev->pGdiDriverInfo == NULL);
105 ASSERT(pldev->cRefs == 0);
106
107 /* Free the memory */
108 ExFreePoolWithTag(pldev, GDITAG_LDEV);
109 }
110
111 PDEVMODEINFO
112 NTAPI
113 LDEVOBJ_pdmiGetModes(
114 _In_ PLDEVOBJ pldev,
115 _In_ HANDLE hDriver)
116 {
117 ULONG cbSize, cbFull;
118 PDEVMODEINFO pdminfo;
119
120 TRACE("LDEVOBJ_pdmiGetModes(%p, %p)\n", pldev, hDriver);
121
122 /* Call the driver to get the required size */
123 cbSize = pldev->pfn.GetModes(hDriver, 0, NULL);
124 if (!cbSize)
125 {
126 ERR("DrvGetModes returned 0\n");
127 return NULL;
128 }
129
130 /* Add space for the header */
131 cbFull = cbSize + FIELD_OFFSET(DEVMODEINFO, adevmode);
132
133 /* Allocate a buffer for the DEVMODE array */
134 pdminfo = ExAllocatePoolWithTag(PagedPool, cbFull, GDITAG_DEVMODE);
135 if (!pdminfo)
136 {
137 ERR("Could not allocate devmodeinfo\n");
138 return NULL;
139 }
140
141 pdminfo->pldev = pldev;
142 pdminfo->cbdevmode = cbSize;
143
144 /* Call the driver again to fill the buffer */
145 cbSize = pldev->pfn.GetModes(hDriver, cbSize, pdminfo->adevmode);
146 if (!cbSize)
147 {
148 /* Could not get modes */
149 ERR("returned size %lu(%lu)\n", cbSize, pdminfo->cbdevmode);
150 ExFreePoolWithTag(pdminfo, GDITAG_DEVMODE);
151 pdminfo = NULL;
152 }
153
154 return pdminfo;
155 }
156
157 static
158 BOOL
159 LDEVOBJ_bLoadImage(
160 _Inout_ PLDEVOBJ pldev,
161 _In_ PUNICODE_STRING pustrPathName)
162 {
163 PSYSTEM_GDI_DRIVER_INFORMATION pDriverInfo;
164 NTSTATUS Status;
165 ULONG cbSize;
166
167 /* Make sure no image is loaded yet */
168 ASSERT(pldev && pldev->pGdiDriverInfo == NULL);
169
170 /* Allocate a SYSTEM_GDI_DRIVER_INFORMATION structure */
171 cbSize = sizeof(SYSTEM_GDI_DRIVER_INFORMATION) + pustrPathName->Length;
172 pDriverInfo = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_LDEV);
173 if (!pDriverInfo)
174 {
175 ERR("Failed to allocate SYSTEM_GDI_DRIVER_INFORMATION\n");
176 return FALSE;
177 }
178
179 /* Initialize the UNICODE_STRING and copy the driver name */
180 RtlInitEmptyUnicodeString(&pDriverInfo->DriverName,
181 (PWSTR)(pDriverInfo + 1),
182 pustrPathName->Length);
183 RtlCopyUnicodeString(&pDriverInfo->DriverName, pustrPathName);
184
185 /* Try to load the driver */
186 Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation,
187 pDriverInfo,
188 sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
189
190 if (!NT_SUCCESS(Status))
191 {
192 ERR("Failed to load a GDI driver: '%wZ', Status = 0x%lx\n",
193 pustrPathName, Status);
194
195 /* Free the allocated memory */
196 ExFreePoolWithTag(pDriverInfo, GDITAG_LDEV);
197 return FALSE;
198 }
199
200 /* Set the driver info */
201 pldev->pGdiDriverInfo = pDriverInfo;
202
203 /* Return success. */
204 return TRUE;
205 }
206
207 static
208 VOID
209 LDEVOBJ_vUnloadImage(
210 _Inout_ PLDEVOBJ pldev)
211 {
212 NTSTATUS Status;
213
214 /* Make sure we have a driver info */
215 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
216
217 /* Check if we have loaded a driver */
218 if (pldev->pfn.DisableDriver)
219 {
220 /* Call the unload function */
221 pldev->pfn.DisableDriver();
222 }
223
224 /* Unload the driver */
225 Status = ZwSetSystemInformation(SystemUnloadGdiDriverInformation,
226 &pldev->pGdiDriverInfo->SectionPointer,
227 sizeof(HANDLE));
228 if (!NT_SUCCESS(Status))
229 {
230 ERR("Failed to unload the driver, this is bad.\n");
231 }
232
233 /* Free the driver info structure */
234 ExFreePoolWithTag(pldev->pGdiDriverInfo, GDITAG_LDEV);
235 pldev->pGdiDriverInfo = NULL;
236 }
237
238 static
239 BOOL
240 LDEVOBJ_bEnableDriver(
241 _Inout_ PLDEVOBJ pldev)
242 {
243 PFN_DrvEnableDriver pfnEnableDriver;
244 DRVENABLEDATA ded;
245 ULONG i;
246
247 /* Make sure we have a driver info */
248 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
249
250 /* Call the drivers DrvEnableDriver function */
251 RtlZeroMemory(&ded, sizeof(ded));
252 pfnEnableDriver = pldev->pGdiDriverInfo->EntryPoint;
253 if (!pfnEnableDriver(GDI_ENGINE_VERSION, sizeof(ded), &ded))
254 {
255 ERR("DrvEnableDriver failed\n");
256 return FALSE;
257 }
258
259 /* Copy the returned driver version */
260 pldev->ulDriverVersion = ded.iDriverVersion;
261
262 /* Fill the driver function array */
263 for (i = 0; i < ded.c; i++)
264 {
265 pldev->apfn[ded.pdrvfn[i].iFunc] = ded.pdrvfn[i].pfn;
266 }
267
268 /* Return success. */
269 return TRUE;
270 }
271
272 static
273 PVOID
274 LDEVOBJ_pvFindImageProcAddress(
275 _In_ PLDEVOBJ pldev,
276 _In_z_ LPSTR pszProcName)
277 {
278 PVOID pvImageBase;
279 PIMAGE_EXPORT_DIRECTORY pExportDir;
280 PVOID pvProcAdress = NULL;
281 PUSHORT pOrdinals;
282 PULONG pNames, pAddresses;
283 ULONG i;
284
285 /* Make sure we have a driver info */
286 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
287
288 /* Get the pointer to the export directory */
289 pvImageBase = pldev->pGdiDriverInfo->ImageAddress;
290 pExportDir = pldev->pGdiDriverInfo->ExportSectionPointer;
291 if (!pExportDir)
292 {
293 return NULL;
294 }
295
296 /* Get pointers to some tables */
297 pNames = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNames);
298 pOrdinals = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNameOrdinals);
299 pAddresses = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfFunctions);
300
301 /* Loop the export table */
302 for (i = 0; i < pExportDir->NumberOfNames; i++)
303 {
304 /* Compare the name */
305 if (_stricmp(pszProcName, RVA_TO_ADDR(pvImageBase, pNames[i])) == 0)
306 {
307 /* Found! Calculate the procedure address */
308 pvProcAdress = RVA_TO_ADDR(pvImageBase, pAddresses[pOrdinals[i]]);
309 break;
310 }
311 }
312
313 /* Return the address */
314 return pvProcAdress;
315 }
316
317 PLDEVOBJ
318 NTAPI
319 EngLoadImageEx(
320 _In_z_ LPWSTR pwszDriverName,
321 _In_ ULONG ldevtype)
322 {
323 WCHAR acwBuffer[MAX_PATH];
324 PLDEVOBJ pldev;
325 UNICODE_STRING strDriverName;
326 SIZE_T cwcLength;
327 LPWSTR pwsz;
328
329 TRACE("EngLoadImageEx(%ls, %lu)\n", pwszDriverName, ldevtype);
330 ASSERT(pwszDriverName);
331
332 /* Initialize buffer for the the driver name */
333 RtlInitEmptyUnicodeString(&strDriverName, acwBuffer, sizeof(acwBuffer));
334
335 /* Start path with systemroot */
336 RtlAppendUnicodeToString(&strDriverName, L"\\SystemRoot\\System32\\");
337
338 /* Get Length of given string */
339 cwcLength = wcslen(pwszDriverName);
340
341 /* Check if we have a system32 path given */
342 pwsz = pwszDriverName + cwcLength;
343 while (pwsz > pwszDriverName)
344 {
345 if ((*pwsz == L'\\') && (_wcsnicmp(pwsz, L"\\system32\\", 10) == 0))
346 {
347 /* Driver name starts after system32 */
348 pwsz += 10;
349 break;
350 }
351 pwsz--;
352 }
353
354 /* Append the driver name */
355 RtlAppendUnicodeToString(&strDriverName, pwsz);
356
357 /* MSDN says "The driver must include this suffix in the pwszDriver string."
358 But in fact it's optional. The function can also load .sys files without
359 appending the .dll extension. */
360 if ((cwcLength < 4) ||
361 ((_wcsnicmp(pwszDriverName + cwcLength - 4, L".dll", 4) != 0) &&
362 (_wcsnicmp(pwszDriverName + cwcLength - 4, L".sys", 4) != 0)) )
363 {
364 /* Append the .dll suffix */
365 RtlAppendUnicodeToString(&strDriverName, L".dll");
366 }
367
368 /* Lock loader */
369 EngAcquireSemaphore(ghsemLDEVList);
370
371 /* Search the List of LDEVS for the driver name */
372 for (pldev = gpldevHead; pldev != NULL; pldev = pldev->pldevNext)
373 {
374 /* Check if the ldev is associated with a file */
375 if (pldev->pGdiDriverInfo)
376 {
377 ERR("Driver Name 1 %wZ\n", &strDriverName);
378 ERR("Driver Name 2 %wZ\n", &pldev->pGdiDriverInfo->DriverName);
379 /* Check for match (case insensative) */
380 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, 1))
381 {
382 /* Image found in LDEV list */
383 break;
384 }
385 }
386 }
387
388 /* Did we find one? */
389 if (!pldev)
390 {
391 /* No, allocate a new LDEVOBJ */
392 pldev = LDEVOBJ_AllocLDEV(ldevtype);
393 if (!pldev)
394 {
395 ERR("Could not allocate LDEV\n");
396 goto leave;
397 }
398
399 /* Load the image */
400 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
401 {
402 LDEVOBJ_vFreeLDEV(pldev);
403 pldev = NULL;
404 ERR("LDEVOBJ_bLoadImage failed\n");
405 goto leave;
406 }
407
408 /* Shall we load a driver? */
409 if (ldevtype != LDEV_IMAGE)
410 {
411 /* Load the driver */
412 if (!LDEVOBJ_bEnableDriver(pldev))
413 {
414 ERR("LDEVOBJ_bEnableDriver failed\n");
415
416 /* Unload the image. */
417 LDEVOBJ_vUnloadImage(pldev);
418 LDEVOBJ_vFreeLDEV(pldev);
419 pldev = NULL;
420 goto leave;
421 }
422 }
423
424 /* Insert the LDEV into the global list */
425 pldev->pldevPrev = NULL;
426 pldev->pldevNext = gpldevHead;
427 gpldevHead = pldev;
428 }
429
430 /* Increase ref count */
431 pldev->cRefs++;
432
433 leave:
434 /* Unlock loader */
435 EngReleaseSemaphore(ghsemLDEVList);
436
437 TRACE("EngLoadImageEx returning %p\n", pldev);
438 return pldev;
439 }
440
441
442 /** Exported functions ********************************************************/
443
444 HANDLE
445 APIENTRY
446 EngLoadImage(
447 _In_ LPWSTR pwszDriverName)
448 {
449 return (HANDLE)EngLoadImageEx(pwszDriverName, LDEV_IMAGE);
450 }
451
452
453 VOID
454 APIENTRY
455 EngUnloadImage(
456 _In_ HANDLE hModule)
457 {
458 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
459
460 /* Make sure the LDEV is in the list */
461 ASSERT(pldev->pldevPrev || pldev->pldevNext);
462
463 /* Lock loader */
464 EngAcquireSemaphore(ghsemLDEVList);
465
466 /* Decrement reference count */
467 pldev->cRefs--;
468
469 /* No more references left? */
470 if (pldev->cRefs == 0)
471 {
472 /* Remove ldev from the list */
473 if (pldev->pldevPrev)
474 pldev->pldevPrev->pldevNext = pldev->pldevNext;
475 if (pldev->pldevNext)
476 pldev->pldevNext->pldevPrev = pldev->pldevPrev;
477
478 /* Unload the image and free the LDEV */
479 LDEVOBJ_vUnloadImage(pldev);
480 LDEVOBJ_vFreeLDEV(pldev);
481 }
482
483 /* Unlock loader */
484 EngReleaseSemaphore(ghsemLDEVList);
485 }
486
487
488 PVOID
489 APIENTRY
490 EngFindImageProcAddress(
491 _In_ HANDLE hModule,
492 _In_ LPSTR lpProcName)
493 {
494 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
495
496 ASSERT(gpldevWin32k != NULL);
497
498 /* Check if win32k is requested */
499 if (!pldev)
500 {
501 pldev = gpldevWin32k;
502 }
503
504 /* Check if the drivers entry point is requested */
505 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0)
506 {
507 return pldev->pGdiDriverInfo->EntryPoint;
508 }
509
510 /* Try to find the address */
511 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName);
512 }
513
514 /* EOF */