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