5b354c79c5e965dccf263dfcd303f91e3e4da745
[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 /* 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 ERR("LDEVOBJ_pvFindImageProcAddress: no export section found\n");
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 _In_z_ LPWSTR pwszDriverName,
324 _In_ ULONG ldevtype)
325 {
326 WCHAR acwBuffer[MAX_PATH];
327 PLIST_ENTRY pleLink;
328 PLDEVOBJ pldev;
329 UNICODE_STRING strDriverName;
330 SIZE_T cwcLength;
331 LPWSTR pwsz;
332
333 TRACE("EngLoadImageEx(%ls, %lu)\n", pwszDriverName, ldevtype);
334 ASSERT(pwszDriverName);
335
336 /* Initialize buffer for the the driver name */
337 RtlInitEmptyUnicodeString(&strDriverName, acwBuffer, sizeof(acwBuffer));
338
339 /* Start path with systemroot */
340 RtlAppendUnicodeToString(&strDriverName, L"\\SystemRoot\\System32\\");
341
342 /* Get Length of given string */
343 cwcLength = wcslen(pwszDriverName);
344
345 /* Check if we have a system32 path given */
346 pwsz = pwszDriverName + cwcLength;
347 while (pwsz > pwszDriverName)
348 {
349 if ((*pwsz == L'\\') && (_wcsnicmp(pwsz, L"\\system32\\", 10) == 0))
350 {
351 /* Driver name starts after system32 */
352 pwsz += 10;
353 break;
354 }
355 pwsz--;
356 }
357
358 /* Append the driver name */
359 RtlAppendUnicodeToString(&strDriverName, pwsz);
360
361 /* MSDN says "The driver must include this suffix in the pwszDriver string."
362 But in fact it's optional. The function can also load .sys files without
363 appending the .dll extension. */
364 if ((cwcLength < 4) ||
365 ((_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 (pleLink = gleLdevListHead.Flink;
377 pleLink != &gleLdevListHead;
378 pleLink = pleLink->Flink)
379 {
380 pldev = CONTAINING_RECORD(pleLink, LDEVOBJ, leLink);
381
382 /* Check if the ldev is associated with a file */
383 if (pldev->pGdiDriverInfo)
384 {
385 /* Check for match (case insensative) */
386 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, TRUE))
387 {
388 /* Image found in LDEV list */
389 break;
390 }
391 }
392 }
393
394 /* Did we find one? */
395 if (pleLink == &gleLdevListHead)
396 {
397 /* No, allocate a new LDEVOBJ */
398 pldev = LDEVOBJ_AllocLDEV(ldevtype);
399 if (!pldev)
400 {
401 ERR("Could not allocate LDEV\n");
402 goto leave;
403 }
404
405 /* Load the image */
406 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
407 {
408 LDEVOBJ_vFreeLDEV(pldev);
409 pldev = NULL;
410 ERR("LDEVOBJ_bLoadImage failed\n");
411 goto leave;
412 }
413
414 /* Shall we load a driver? */
415 if (ldevtype != LDEV_IMAGE)
416 {
417 /* Load the driver */
418 if (!LDEVOBJ_bEnableDriver(pldev))
419 {
420 ERR("LDEVOBJ_bEnableDriver failed\n");
421
422 /* Unload the image. */
423 LDEVOBJ_vUnloadImage(pldev);
424 LDEVOBJ_vFreeLDEV(pldev);
425 pldev = NULL;
426 goto leave;
427 }
428 }
429
430 /* Insert the LDEV into the global list */
431 InsertHeadList(&gleLdevListHead, &pldev->leLink);
432 }
433
434 /* Increase ref count */
435 pldev->cRefs++;
436
437 leave:
438 /* Unlock loader */
439 EngReleaseSemaphore(ghsemLDEVList);
440
441 TRACE("EngLoadImageEx returning %p\n", pldev);
442 return pldev;
443 }
444
445
446 /** Exported functions ********************************************************/
447
448 HANDLE
449 APIENTRY
450 EngLoadImage(
451 _In_ LPWSTR pwszDriverName)
452 {
453 return (HANDLE)EngLoadImageEx(pwszDriverName, LDEV_IMAGE);
454 }
455
456
457 VOID
458 APIENTRY
459 EngUnloadImage(
460 _In_ HANDLE hModule)
461 {
462 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
463
464 /* Make sure the LDEV is in the list */
465 ASSERT((pldev->leLink.Flink != NULL) && (pldev->leLink.Blink != NULL));
466
467 /* Lock loader */
468 EngAcquireSemaphore(ghsemLDEVList);
469
470 /* Decrement reference count */
471 pldev->cRefs--;
472
473 /* No more references left? */
474 if (pldev->cRefs == 0)
475 {
476 /* Remove ldev from the list */
477 RemoveEntryList(&pldev->leLink);
478
479 /* Unload the image and free the LDEV */
480 LDEVOBJ_vUnloadImage(pldev);
481 LDEVOBJ_vFreeLDEV(pldev);
482 }
483
484 /* Unlock loader */
485 EngReleaseSemaphore(ghsemLDEVList);
486 }
487
488
489 PVOID
490 APIENTRY
491 EngFindImageProcAddress(
492 _In_ HANDLE hModule,
493 _In_ LPSTR lpProcName)
494 {
495 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
496
497 ASSERT(gpldevWin32k != NULL);
498
499 /* Check if win32k is requested */
500 if (!pldev)
501 {
502 pldev = gpldevWin32k;
503 }
504
505 /* Check if the drivers entry point is requested */
506 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0)
507 {
508 return pldev->pGdiDriverInfo->EntryPoint;
509 }
510
511 /* Try to find the address */
512 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName);
513 }
514
515 /* EOF */