[WIN32K]
[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 if (_wcsnicmp(pwszDriverName + cwcLength - 4, L".dll", 4) != 0)
360 {
361 /* Append the .dll suffix */
362 RtlAppendUnicodeToString(&strDriverName, L".dll");
363 }
364
365 /* Lock loader */
366 EngAcquireSemaphore(ghsemLDEVList);
367
368 /* Search the List of LDEVS for the driver name */
369 for (pldev = gpldevHead; pldev != NULL; pldev = pldev->pldevNext)
370 {
371 /* Check if the ldev is associated with a file */
372 if (pldev->pGdiDriverInfo)
373 {
374 /* Check for match (case insensative) */
375 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, 1))
376 {
377 /* Image found in LDEV list */
378 break;
379 }
380 }
381 }
382
383 /* Did we find one? */
384 if (!pldev)
385 {
386 /* No, allocate a new LDEVOBJ */
387 pldev = LDEVOBJ_AllocLDEV(ldevtype);
388 if (!pldev)
389 {
390 DPRINT1("Could not allocate LDEV\n");
391 goto leave;
392 }
393
394 /* Load the image */
395 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
396 {
397 LDEVOBJ_vFreeLDEV(pldev);
398 pldev = NULL;
399 DPRINT1("LDEVOBJ_bLoadImage failed\n");
400 goto leave;
401 }
402
403 /* Shall we load a driver? */
404 if (ldevtype != LDEV_IMAGE)
405 {
406 /* Load the driver */
407 if (!LDEVOBJ_bLoadDriver(pldev))
408 {
409 DPRINT1("LDEVOBJ_bLoadDriver failed\n");
410 LDEVOBJ_vFreeLDEV(pldev);
411 pldev = NULL;
412 goto leave;
413 }
414 }
415
416 /* Insert the LDEV into the global list */
417 pldev->pldevPrev = NULL;
418 pldev->pldevNext = gpldevHead;
419 gpldevHead = pldev;
420 }
421
422 /* Increase ref count */
423 pldev->cRefs++;
424
425 leave:
426 /* Unlock loader */
427 EngReleaseSemaphore(ghsemLDEVList);
428
429 DPRINT("EngLoadImageEx returning %p\n", pldev);
430
431 return pldev;
432 }
433
434
435 /** Exported functions ********************************************************/
436
437 HANDLE
438 APIENTRY
439 EngLoadImage(
440 LPWSTR pwszDriverName)
441 {
442 return (HANDLE)EngLoadImageEx(pwszDriverName, LDEV_IMAGE);
443 }
444
445
446 VOID
447 APIENTRY
448 EngUnloadImage(
449 IN HANDLE hModule)
450 {
451 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
452
453 /* Make sure the LDEV is in the list */
454 ASSERT(pldev->pldevPrev || pldev->pldevNext);
455
456 /* Lock loader */
457 EngAcquireSemaphore(ghsemLDEVList);
458
459 /* Decrement reference count */
460 pldev->cRefs--;
461
462 /* No more references left? */
463 if (pldev->cRefs == 0)
464 {
465 /* Remove ldev from the list */
466 if (pldev->pldevPrev)
467 pldev->pldevPrev->pldevNext = pldev->pldevNext;
468 if (pldev->pldevNext)
469 pldev->pldevNext->pldevPrev = pldev->pldevPrev;
470
471 /* Unload the image */
472 LDEVOBJ_vUnloadImage(pldev);
473 }
474
475 /* Unlock loader */
476 EngReleaseSemaphore(ghsemLDEVList);
477 }
478
479
480 PVOID
481 APIENTRY
482 EngFindImageProcAddress(
483 IN HANDLE hModule,
484 IN LPSTR lpProcName)
485 {
486 PLDEVOBJ pldev = (PLDEVOBJ)hModule;
487
488 ASSERT(gpldevWin32k != NULL);
489
490 /* Check if win32k is requested */
491 if (!pldev)
492 {
493 pldev = gpldevWin32k;
494 }
495
496 /* Check if the drivers entry point is requested */
497 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0)
498 {
499 return pldev->pGdiDriverInfo->EntryPoint;
500 }
501
502 /* Try to find the address */
503 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName);
504 }
505