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