[CONSRV]
[reactos.git] / 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 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 /* Call the driver to get the required size */
126 cbSize = pldev->pfn.GetModes(hDriver, 0, NULL);
127 if (!cbSize)
128 {
129 ERR("DrvGetModes returned 0\n");
130 return NULL;
131 }
132
133 /* Add space for the header */
134 cbFull = cbSize + FIELD_OFFSET(DEVMODEINFO, adevmode);
135
136 /* Allocate a buffer for the DEVMODE array */
137 pdminfo = ExAllocatePoolWithTag(PagedPool, cbFull, GDITAG_DEVMODE);
138 if (!pdminfo)
139 {
140 ERR("Could not allocate devmodeinfo\n");
141 return NULL;
142 }
143
144 pdminfo->pldev = pldev;
145 pdminfo->cbdevmode = cbSize;
146
147 /* Call the driver again to fill the buffer */
148 cbSize = pldev->pfn.GetModes(hDriver, cbSize, pdminfo->adevmode);
149 if (!cbSize)
150 {
151 /* Could not get modes */
152 ERR("returned size %lu(%lu)\n", cbSize, pdminfo->cbdevmode);
153 ExFreePoolWithTag(pdminfo, GDITAG_DEVMODE);
154 pdminfo = NULL;
155 }
156
157 return pdminfo;
158 }
159
160 static
161 BOOL
162 LDEVOBJ_bLoadImage(
163 _Inout_ PLDEVOBJ pldev,
164 _In_ PUNICODE_STRING pustrPathName)
165 {
166 PSYSTEM_GDI_DRIVER_INFORMATION pDriverInfo;
167 NTSTATUS Status;
168 ULONG cbSize;
169
170 /* Make sure no image is loaded yet */
171 ASSERT(pldev && pldev->pGdiDriverInfo == NULL);
172
173 /* Allocate a SYSTEM_GDI_DRIVER_INFORMATION structure */
174 cbSize = sizeof(SYSTEM_GDI_DRIVER_INFORMATION) + pustrPathName->Length;
175 pDriverInfo = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_LDEV);
176 if (!pDriverInfo)
177 {
178 ERR("Failed to allocate SYSTEM_GDI_DRIVER_INFORMATION\n");
179 return FALSE;
180 }
181
182 /* Initialize the UNICODE_STRING and copy the driver name */
183 RtlInitEmptyUnicodeString(&pDriverInfo->DriverName,
184 (PWSTR)(pDriverInfo + 1),
185 pustrPathName->Length);
186 RtlCopyUnicodeString(&pDriverInfo->DriverName, pustrPathName);
187
188 /* Try to load the driver */
189 Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation,
190 pDriverInfo,
191 sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
192 if (!NT_SUCCESS(Status))
193 {
194 ERR("Failed to load a GDI driver: '%wZ', Status = 0x%lx\n",
195 pustrPathName, Status);
196
197 /* Free the allocated memory */
198 ExFreePoolWithTag(pDriverInfo, GDITAG_LDEV);
199 return FALSE;
200 }
201
202 /* Set the driver info */
203 pldev->pGdiDriverInfo = pDriverInfo;
204
205 /* Return success. */
206 return TRUE;
207 }
208
209 static
210 VOID
211 LDEVOBJ_vUnloadImage(
212 _Inout_ PLDEVOBJ pldev)
213 {
214 NTSTATUS Status;
215
216 /* Make sure we have a driver info */
217 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
218
219 /* Check if we have loaded a driver */
220 if (pldev->pfn.DisableDriver)
221 {
222 /* Call the unload function */
223 pldev->pfn.DisableDriver();
224 }
225
226 /* Unload the driver */
227 Status = ZwSetSystemInformation(SystemUnloadGdiDriverInformation,
228 &pldev->pGdiDriverInfo->SectionPointer,
229 sizeof(HANDLE));
230 if (!NT_SUCCESS(Status))
231 {
232 ERR("Failed to unload the driver, this is bad.\n");
233 }
234
235 /* Free the driver info structure */
236 ExFreePoolWithTag(pldev->pGdiDriverInfo, GDITAG_LDEV);
237 pldev->pGdiDriverInfo = NULL;
238 }
239
240 static
241 BOOL
242 LDEVOBJ_bEnableDriver(
243 _Inout_ PLDEVOBJ pldev)
244 {
245 PFN_DrvEnableDriver pfnEnableDriver;
246 DRVENABLEDATA ded;
247 ULONG i;
248
249 /* Make sure we have a driver info */
250 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
251
252 /* Call the drivers DrvEnableDriver function */
253 RtlZeroMemory(&ded, sizeof(ded));
254 pfnEnableDriver = pldev->pGdiDriverInfo->EntryPoint;
255 if (!pfnEnableDriver(GDI_ENGINE_VERSION, sizeof(ded), &ded))
256 {
257 ERR("DrvEnableDriver failed\n");
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 static
275 PVOID
276 LDEVOBJ_pvFindImageProcAddress(
277 _In_ PLDEVOBJ pldev,
278 _In_z_ LPSTR pszProcName)
279 {
280 PVOID pvImageBase;
281 PIMAGE_EXPORT_DIRECTORY pExportDir;
282 PVOID pvProcAdress = NULL;
283 PUSHORT pOrdinals;
284 PULONG pNames, pAddresses;
285 ULONG i;
286
287 /* Make sure we have a driver info */
288 ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
289
290 /* Get the pointer to the export directory */
291 pvImageBase = pldev->pGdiDriverInfo->ImageAddress;
292 pExportDir = pldev->pGdiDriverInfo->ExportSectionPointer;
293 if (!pExportDir)
294 {
295 return NULL;
296 }
297
298 /* Get pointers to some tables */
299 pNames = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNames);
300 pOrdinals = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNameOrdinals);
301 pAddresses = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfFunctions);
302
303 /* Loop the export table */
304 for (i = 0; i < pExportDir->NumberOfNames; i++)
305 {
306 /* Compare the name */
307 if (_stricmp(pszProcName, RVA_TO_ADDR(pvImageBase, pNames[i])) == 0)
308 {
309 /* Found! Calculate the procedure address */
310 pvProcAdress = RVA_TO_ADDR(pvImageBase, pAddresses[pOrdinals[i]]);
311 break;
312 }
313 }
314
315 /* Return the address */
316 return pvProcAdress;
317 }
318
319 PLDEVOBJ
320 NTAPI
321 EngLoadImageEx(
322 _In_z_ LPWSTR pwszDriverName,
323 _In_ ULONG ldevtype)
324 {
325 WCHAR acwBuffer[MAX_PATH];
326 PLIST_ENTRY pleLink;
327 PLDEVOBJ pldev;
328 UNICODE_STRING strDriverName;
329 SIZE_T cwcLength;
330 LPWSTR pwsz;
331
332 TRACE("EngLoadImageEx(%ls, %lu)\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 ((*pwsz == L'\\') && (_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. The function can also load .sys files without
362 appending the .dll extension. */
363 if ((cwcLength < 4) ||
364 ((_wcsnicmp(pwszDriverName + cwcLength - 4, L".dll", 4) != 0) &&
365 (_wcsnicmp(pwszDriverName + cwcLength - 4, L".sys", 4) != 0)) )
366 {
367 /* Append the .dll suffix */
368 RtlAppendUnicodeToString(&strDriverName, L".dll");
369 }
370
371 /* Lock loader */
372 EngAcquireSemaphore(ghsemLDEVList);
373
374 /* Search the List of LDEVS for the driver name */
375 for (pleLink = gleLdevListHead.Flink;
376 pleLink != &gleLdevListHead;
377 pleLink = pleLink->Flink)
378 {
379 pldev = CONTAINING_RECORD(pleLink, LDEVOBJ, leLink);
380
381 /* Check if the ldev is associated with a file */
382 if (pldev->pGdiDriverInfo)
383 {
384 /* Check for match (case insensative) */
385 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, TRUE))
386 {
387 /* Image found in LDEV list */
388 break;
389 }
390 }
391 }
392
393 /* Did we find one? */
394 if (pleLink == &gleLdevListHead)
395 {
396 /* No, allocate a new LDEVOBJ */
397 pldev = LDEVOBJ_AllocLDEV(ldevtype);
398 if (!pldev)
399 {
400 ERR("Could not allocate LDEV\n");
401 goto leave;
402 }
403
404 /* Load the image */
405 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
406 {
407 LDEVOBJ_vFreeLDEV(pldev);
408 pldev = NULL;
409 ERR("LDEVOBJ_bLoadImage failed\n");
410 goto leave;
411 }
412
413 /* Shall we load a driver? */
414 if (ldevtype != LDEV_IMAGE)
415 {
416 /* Load the driver */
417 if (!LDEVOBJ_bEnableDriver(pldev))
418 {
419 ERR("LDEVOBJ_bEnableDriver failed\n");
420
421 /* Unload the image. */
422 LDEVOBJ_vUnloadImage(pldev);
423 LDEVOBJ_vFreeLDEV(pldev);
424 pldev = NULL;
425 goto leave;
426 }
427 }
428
429 /* Insert the LDEV into the global list */
430 InsertHeadList(&gleLdevListHead, &pldev->leLink);
431 }
432
433 /* Increase ref count */
434 pldev->cRefs++;
435
436 leave:
437 /* Unlock loader */
438 EngReleaseSemaphore(ghsemLDEVList);
439
440 TRACE("EngLoadImageEx returning %p\n", pldev);
441 return pldev;
442 }
443
444
445 /** Exported functions ********************************************************/
446
447 HANDLE
448 APIENTRY
449 EngLoadImage(
450 _In_ LPWSTR pwszDriverName)
451 {
452 return (HANDLE)EngLoadImageEx(pwszDriverName, LDEV_IMAGE);
453 }
454
455
456 VOID
457 APIENTRY
458 EngUnloadImage(
459 _In_ HANDLE hModule)
460 {
461 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
462
463 /* Make sure the LDEV is in the list */
464 ASSERT((pldev->leLink.Flink != NULL) && (pldev->leLink.Blink != NULL));
465
466 /* Lock loader */
467 EngAcquireSemaphore(ghsemLDEVList);
468
469 /* Decrement reference count */
470 pldev->cRefs--;
471
472 /* No more references left? */
473 if (pldev->cRefs == 0)
474 {
475 /* Remove ldev from the list */
476 RemoveEntryList(&pldev->leLink);
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 */