42d087fcd346708d9e1d53a696296eb1b7a5936f
[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 /* Check for match (case insensative) */
378 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, 1))
379 {
380 /* Image found in LDEV list */
381 break;
382 }
383 }
384 }
385
386 /* Did we find one? */
387 if (!pldev)
388 {
389 /* No, allocate a new LDEVOBJ */
390 pldev = LDEVOBJ_AllocLDEV(ldevtype);
391 if (!pldev)
392 {
393 ERR("Could not allocate LDEV\n");
394 goto leave;
395 }
396
397 /* Load the image */
398 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
399 {
400 LDEVOBJ_vFreeLDEV(pldev);
401 pldev = NULL;
402 ERR("LDEVOBJ_bLoadImage failed\n");
403 goto leave;
404 }
405
406 /* Shall we load a driver? */
407 if (ldevtype != LDEV_IMAGE)
408 {
409 /* Load the driver */
410 if (!LDEVOBJ_bEnableDriver(pldev))
411 {
412 ERR("LDEVOBJ_bEnableDriver failed\n");
413
414 /* Unload the image. */
415 LDEVOBJ_vUnloadImage(pldev);
416 LDEVOBJ_vFreeLDEV(pldev);
417 pldev = NULL;
418 goto leave;
419 }
420 }
421
422 /* Insert the LDEV into the global list */
423 pldev->pldevPrev = NULL;
424 pldev->pldevNext = gpldevHead;
425 gpldevHead = pldev;
426 }
427
428 /* Increase ref count */
429 pldev->cRefs++;
430
431 leave:
432 /* Unlock loader */
433 EngReleaseSemaphore(ghsemLDEVList);
434
435 TRACE("EngLoadImageEx returning %p\n", pldev);
436 return pldev;
437 }
438
439
440 /** Exported functions ********************************************************/
441
442 HANDLE
443 APIENTRY
444 EngLoadImage(
445 _In_ LPWSTR pwszDriverName)
446 {
447 return (HANDLE)EngLoadImageEx(pwszDriverName, LDEV_IMAGE);
448 }
449
450
451 VOID
452 APIENTRY
453 EngUnloadImage(
454 _In_ HANDLE hModule)
455 {
456 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
457
458 /* Make sure the LDEV is in the list */
459 ASSERT(pldev->pldevPrev || pldev->pldevNext);
460
461 /* Lock loader */
462 EngAcquireSemaphore(ghsemLDEVList);
463
464 /* Decrement reference count */
465 pldev->cRefs--;
466
467 /* No more references left? */
468 if (pldev->cRefs == 0)
469 {
470 /* Remove ldev from the list */
471 if (pldev->pldevPrev)
472 pldev->pldevPrev->pldevNext = pldev->pldevNext;
473 if (pldev->pldevNext)
474 pldev->pldevNext->pldevPrev = pldev->pldevPrev;
475
476 /* Unload the image and free the LDEV */
477 LDEVOBJ_vUnloadImage(pldev);
478 LDEVOBJ_vFreeLDEV(pldev);
479 }
480
481 /* Unlock loader */
482 EngReleaseSemaphore(ghsemLDEVList);
483 }
484
485
486 PVOID
487 APIENTRY
488 EngFindImageProcAddress(
489 _In_ HANDLE hModule,
490 _In_ LPSTR lpProcName)
491 {
492 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
493
494 ASSERT(gpldevWin32k != NULL);
495
496 /* Check if win32k is requested */
497 if (!pldev)
498 {
499 pldev = gpldevWin32k;
500 }
501
502 /* Check if the drivers entry point is requested */
503 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0)
504 {
505 return pldev->pGdiDriverInfo->EntryPoint;
506 }
507
508 /* Try to find the address */
509 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName);
510 }
511
512 /* EOF */