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