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