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)
12 DBG_DEFAULT_CHANNEL(EngLDev
);
15 #define RVA_TO_ADDR(Base,Rva) ((PVOID)(((ULONG_PTR)(Base)) + (Rva)))
18 /** Globals *******************************************************************/
20 static HSEMAPHORE ghsemLDEVList
;
21 static LDEVOBJ
*gpldevHead
= NULL
;
22 static LDEVOBJ
*gpldevWin32k
= NULL
;
25 /** Private functions *********************************************************/
34 /* Initialize the loader lock */
35 ghsemLDEVList
= EngCreateSemaphore();
38 ERR("Failed to create ghsemLDEVList\n");
39 return STATUS_INSUFFICIENT_RESOURCES
;
42 /* Allocate a LDEVOBJ for win32k */
43 gpldevWin32k
= ExAllocatePoolWithTag(PagedPool
,
45 sizeof(SYSTEM_GDI_DRIVER_INFORMATION
),
49 return STATUS_NO_MEMORY
;
52 /* Initialize the LDEVOBJ for win32k */
53 gpldevWin32k
->pldevNext
= NULL
;
54 gpldevWin32k
->pldevPrev
= NULL
;
55 gpldevWin32k
->ldevtype
= LDEV_DEVICE_DISPLAY
;
56 gpldevWin32k
->cRefs
= 1;
57 gpldevWin32k
->ulDriverVersion
= GDI_ENGINE_VERSION
;
58 gpldevWin32k
->pGdiDriverInfo
= (PVOID
)(gpldevWin32k
+ 1);
59 RtlInitUnicodeString(&gpldevWin32k
->pGdiDriverInfo
->DriverName
,
60 L
"\\SystemRoot\\System32\\win32k.sys");
61 gpldevWin32k
->pGdiDriverInfo
->ImageAddress
= &__ImageBase
;
62 gpldevWin32k
->pGdiDriverInfo
->SectionPointer
= NULL
;
63 gpldevWin32k
->pGdiDriverInfo
->EntryPoint
= (PVOID
)DriverEntry
;
64 gpldevWin32k
->pGdiDriverInfo
->ExportSectionPointer
=
65 RtlImageDirectoryEntryToData(&__ImageBase
,
67 IMAGE_DIRECTORY_ENTRY_EXPORT
,
69 gpldevWin32k
->pGdiDriverInfo
->ImageLength
= 0; // FIXME
71 return STATUS_SUCCESS
;
77 _In_ LDEVTYPE ldevtype
)
81 /* Allocate the structure from paged pool */
82 pldev
= ExAllocatePoolWithTag(PagedPool
, sizeof(LDEVOBJ
), GDITAG_LDEV
);
85 ERR("Failed to allocate LDEVOBJ.\n");
89 /* Zero out the structure */
90 RtlZeroMemory(pldev
, sizeof(LDEVOBJ
));
92 /* Set the ldevtype */
93 pldev
->ldevtype
= ldevtype
;
101 _In_ _Post_ptr_invalid_ PLDEVOBJ pldev
)
103 /* Make sure we don't have a driver loaded */
104 ASSERT(pldev
&& pldev
->pGdiDriverInfo
== NULL
);
105 ASSERT(pldev
->cRefs
== 0);
107 /* Free the memory */
108 ExFreePoolWithTag(pldev
, GDITAG_LDEV
);
113 LDEVOBJ_pdmiGetModes(
117 ULONG cbSize
, cbFull
;
118 PDEVMODEINFO pdminfo
;
120 TRACE("LDEVOBJ_pdmiGetModes(%p, %p)\n", pldev
, hDriver
);
122 /* Call the driver to get the required size */
123 cbSize
= pldev
->pfn
.GetModes(hDriver
, 0, NULL
);
126 ERR("DrvGetModes returned 0\n");
130 /* Add space for the header */
131 cbFull
= cbSize
+ FIELD_OFFSET(DEVMODEINFO
, adevmode
);
133 /* Allocate a buffer for the DEVMODE array */
134 pdminfo
= ExAllocatePoolWithTag(PagedPool
, cbFull
, GDITAG_DEVMODE
);
137 ERR("Could not allocate devmodeinfo\n");
141 pdminfo
->pldev
= pldev
;
142 pdminfo
->cbdevmode
= cbSize
;
144 /* Call the driver again to fill the buffer */
145 cbSize
= pldev
->pfn
.GetModes(hDriver
, cbSize
, pdminfo
->adevmode
);
148 /* Could not get modes */
149 ERR("returned size %lu(%lu)\n", cbSize
, pdminfo
->cbdevmode
);
150 ExFreePoolWithTag(pdminfo
, GDITAG_DEVMODE
);
160 _Inout_ PLDEVOBJ pldev
,
161 _In_ PUNICODE_STRING pustrPathName
)
163 PSYSTEM_GDI_DRIVER_INFORMATION pDriverInfo
;
167 /* Make sure no image is loaded yet */
168 ASSERT(pldev
&& pldev
->pGdiDriverInfo
== NULL
);
170 /* Allocate a SYSTEM_GDI_DRIVER_INFORMATION structure */
171 cbSize
= sizeof(SYSTEM_GDI_DRIVER_INFORMATION
) + pustrPathName
->Length
;
172 pDriverInfo
= ExAllocatePoolWithTag(PagedPool
, cbSize
, GDITAG_LDEV
);
175 ERR("Failed to allocate SYSTEM_GDI_DRIVER_INFORMATION\n");
179 /* Initialize the UNICODE_STRING and copy the driver name */
180 RtlInitEmptyUnicodeString(&pDriverInfo
->DriverName
,
181 (PWSTR
)(pDriverInfo
+ 1),
182 pustrPathName
->Length
);
183 RtlCopyUnicodeString(&pDriverInfo
->DriverName
, pustrPathName
);
185 /* Try to load the driver */
186 Status
= ZwSetSystemInformation(SystemLoadGdiDriverInformation
,
188 sizeof(SYSTEM_GDI_DRIVER_INFORMATION
));
190 if (!NT_SUCCESS(Status
))
192 ERR("Failed to load a GDI driver: '%wZ', Status = 0x%lx\n",
193 pustrPathName
, Status
);
195 /* Free the allocated memory */
196 ExFreePoolWithTag(pDriverInfo
, GDITAG_LDEV
);
200 /* Set the driver info */
201 pldev
->pGdiDriverInfo
= pDriverInfo
;
203 /* Return success. */
209 LDEVOBJ_vUnloadImage(
210 _Inout_ PLDEVOBJ pldev
)
214 /* Make sure we have a driver info */
215 ASSERT(pldev
&& pldev
->pGdiDriverInfo
!= NULL
);
217 /* Check if we have loaded a driver */
218 if (pldev
->pfn
.DisableDriver
)
220 /* Call the unload function */
221 pldev
->pfn
.DisableDriver();
224 /* Unload the driver */
225 Status
= ZwSetSystemInformation(SystemUnloadGdiDriverInformation
,
226 &pldev
->pGdiDriverInfo
->SectionPointer
,
228 if (!NT_SUCCESS(Status
))
230 ERR("Failed to unload the driver, this is bad.\n");
233 /* Free the driver info structure */
234 ExFreePoolWithTag(pldev
->pGdiDriverInfo
, GDITAG_LDEV
);
235 pldev
->pGdiDriverInfo
= NULL
;
240 LDEVOBJ_bEnableDriver(
241 _Inout_ PLDEVOBJ pldev
)
243 PFN_DrvEnableDriver pfnEnableDriver
;
247 /* Make sure we have a driver info */
248 ASSERT(pldev
&& pldev
->pGdiDriverInfo
!= NULL
);
250 /* Call the drivers DrvEnableDriver function */
251 RtlZeroMemory(&ded
, sizeof(ded
));
252 pfnEnableDriver
= pldev
->pGdiDriverInfo
->EntryPoint
;
253 if (!pfnEnableDriver(GDI_ENGINE_VERSION
, sizeof(ded
), &ded
))
255 ERR("DrvEnableDriver failed\n");
259 /* Copy the returned driver version */
260 pldev
->ulDriverVersion
= ded
.iDriverVersion
;
262 /* Fill the driver function array */
263 for (i
= 0; i
< ded
.c
; i
++)
265 pldev
->apfn
[ded
.pdrvfn
[i
].iFunc
] = ded
.pdrvfn
[i
].pfn
;
268 /* Return success. */
274 LDEVOBJ_pvFindImageProcAddress(
276 _In_z_ LPSTR pszProcName
)
279 PIMAGE_EXPORT_DIRECTORY pExportDir
;
280 PVOID pvProcAdress
= NULL
;
282 PULONG pNames
, pAddresses
;
285 /* Make sure we have a driver info */
286 ASSERT(pldev
&& pldev
->pGdiDriverInfo
!= NULL
);
288 /* Get the pointer to the export directory */
289 pvImageBase
= pldev
->pGdiDriverInfo
->ImageAddress
;
290 pExportDir
= pldev
->pGdiDriverInfo
->ExportSectionPointer
;
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
);
301 /* Loop the export table */
302 for (i
= 0; i
< pExportDir
->NumberOfNames
; i
++)
304 /* Compare the name */
305 if (_stricmp(pszProcName
, RVA_TO_ADDR(pvImageBase
, pNames
[i
])) == 0)
307 /* Found! Calculate the procedure address */
308 pvProcAdress
= RVA_TO_ADDR(pvImageBase
, pAddresses
[pOrdinals
[i
]]);
313 /* Return the address */
320 _In_z_ LPWSTR pwszDriverName
,
323 WCHAR acwBuffer
[MAX_PATH
];
325 UNICODE_STRING strDriverName
;
329 TRACE("EngLoadImageEx(%ls, %lu)\n", pwszDriverName
, ldevtype
);
330 ASSERT(pwszDriverName
);
332 /* Initialize buffer for the the driver name */
333 RtlInitEmptyUnicodeString(&strDriverName
, acwBuffer
, sizeof(acwBuffer
));
335 /* Start path with systemroot */
336 RtlAppendUnicodeToString(&strDriverName
, L
"\\SystemRoot\\System32\\");
338 /* Get Length of given string */
339 cwcLength
= wcslen(pwszDriverName
);
341 /* Check if we have a system32 path given */
342 pwsz
= pwszDriverName
+ cwcLength
;
343 while (pwsz
> pwszDriverName
)
345 if ((*pwsz
== L
'\\') && (_wcsnicmp(pwsz
, L
"\\system32\\", 10) == 0))
347 /* Driver name starts after system32 */
354 /* Append the driver name */
355 RtlAppendUnicodeToString(&strDriverName
, pwsz
);
357 /* MSDN says "The driver must include this suffix in the pwszDriver string."
358 But in fact it's optional. The function can also load .sys files without
359 appending the .dll extension. */
360 if ((cwcLength
< 4) ||
361 ((_wcsnicmp(pwszDriverName
+ cwcLength
- 4, L
".dll", 4) != 0) &&
362 (_wcsnicmp(pwszDriverName
+ cwcLength
- 4, L
".sys", 4) != 0)) )
364 /* Append the .dll suffix */
365 RtlAppendUnicodeToString(&strDriverName
, L
".dll");
369 EngAcquireSemaphore(ghsemLDEVList
);
371 /* Search the List of LDEVS for the driver name */
372 for (pldev
= gpldevHead
; pldev
!= NULL
; pldev
= pldev
->pldevNext
)
374 /* Check if the ldev is associated with a file */
375 if (pldev
->pGdiDriverInfo
)
377 ERR("Driver Name 1 %wZ\n", &strDriverName
);
378 ERR("Driver Name 2 %wZ\n", &pldev
->pGdiDriverInfo
->DriverName
);
379 /* Check for match (case insensative) */
380 if (RtlEqualUnicodeString(&pldev
->pGdiDriverInfo
->DriverName
, &strDriverName
, 1))
382 /* Image found in LDEV list */
388 /* Did we find one? */
391 /* No, allocate a new LDEVOBJ */
392 pldev
= LDEVOBJ_AllocLDEV(ldevtype
);
395 ERR("Could not allocate LDEV\n");
400 if (!LDEVOBJ_bLoadImage(pldev
, &strDriverName
))
402 LDEVOBJ_vFreeLDEV(pldev
);
404 ERR("LDEVOBJ_bLoadImage failed\n");
408 /* Shall we load a driver? */
409 if (ldevtype
!= LDEV_IMAGE
)
411 /* Load the driver */
412 if (!LDEVOBJ_bEnableDriver(pldev
))
414 ERR("LDEVOBJ_bEnableDriver failed\n");
416 /* Unload the image. */
417 LDEVOBJ_vUnloadImage(pldev
);
418 LDEVOBJ_vFreeLDEV(pldev
);
424 /* Insert the LDEV into the global list */
425 pldev
->pldevPrev
= NULL
;
426 pldev
->pldevNext
= gpldevHead
;
430 /* Increase ref count */
435 EngReleaseSemaphore(ghsemLDEVList
);
437 TRACE("EngLoadImageEx returning %p\n", pldev
);
442 /** Exported functions ********************************************************/
447 _In_ LPWSTR pwszDriverName
)
449 return (HANDLE
)EngLoadImageEx(pwszDriverName
, LDEV_IMAGE
);
458 PLDEVOBJ pldev
= (PLDEVOBJ
)hModule
;
460 /* Make sure the LDEV is in the list */
461 ASSERT(pldev
->pldevPrev
|| pldev
->pldevNext
);
464 EngAcquireSemaphore(ghsemLDEVList
);
466 /* Decrement reference count */
469 /* No more references left? */
470 if (pldev
->cRefs
== 0)
472 /* Remove ldev from the list */
473 if (pldev
->pldevPrev
)
474 pldev
->pldevPrev
->pldevNext
= pldev
->pldevNext
;
475 if (pldev
->pldevNext
)
476 pldev
->pldevNext
->pldevPrev
= pldev
->pldevPrev
;
478 /* Unload the image and free the LDEV */
479 LDEVOBJ_vUnloadImage(pldev
);
480 LDEVOBJ_vFreeLDEV(pldev
);
484 EngReleaseSemaphore(ghsemLDEVList
);
490 EngFindImageProcAddress(
492 _In_ LPSTR lpProcName
)
494 PLDEVOBJ pldev
= (PLDEVOBJ
)hModule
;
496 ASSERT(gpldevWin32k
!= NULL
);
498 /* Check if win32k is requested */
501 pldev
= gpldevWin32k
;
504 /* Check if the drivers entry point is requested */
505 if (_strnicmp(lpProcName
, "DrvEnableDriver", 15) == 0)
507 return pldev
->pGdiDriverInfo
->EntryPoint
;
510 /* Try to find the address */
511 return LDEVOBJ_pvFindImageProcAddress(pldev
, lpProcName
);