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