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 /* Check for match (case insensative) */
378 if (RtlEqualUnicodeString(&pldev
->pGdiDriverInfo
->DriverName
, &strDriverName
, 1))
380 /* Image found in LDEV list */
386 /* Did we find one? */
389 /* No, allocate a new LDEVOBJ */
390 pldev
= LDEVOBJ_AllocLDEV(ldevtype
);
393 ERR("Could not allocate LDEV\n");
398 if (!LDEVOBJ_bLoadImage(pldev
, &strDriverName
))
400 LDEVOBJ_vFreeLDEV(pldev
);
402 ERR("LDEVOBJ_bLoadImage failed\n");
406 /* Shall we load a driver? */
407 if (ldevtype
!= LDEV_IMAGE
)
409 /* Load the driver */
410 if (!LDEVOBJ_bEnableDriver(pldev
))
412 ERR("LDEVOBJ_bEnableDriver failed\n");
414 /* Unload the image. */
415 LDEVOBJ_vUnloadImage(pldev
);
416 LDEVOBJ_vFreeLDEV(pldev
);
422 /* Insert the LDEV into the global list */
423 pldev
->pldevPrev
= NULL
;
424 pldev
->pldevNext
= gpldevHead
;
428 /* Increase ref count */
433 EngReleaseSemaphore(ghsemLDEVList
);
435 TRACE("EngLoadImageEx returning %p\n", pldev
);
440 /** Exported functions ********************************************************/
445 _In_ LPWSTR pwszDriverName
)
447 return (HANDLE
)EngLoadImageEx(pwszDriverName
, LDEV_IMAGE
);
456 PLDEVOBJ pldev
= (PLDEVOBJ
)hModule
;
458 /* Make sure the LDEV is in the list */
459 ASSERT(pldev
->pldevPrev
|| pldev
->pldevNext
);
462 EngAcquireSemaphore(ghsemLDEVList
);
464 /* Decrement reference count */
467 /* No more references left? */
468 if (pldev
->cRefs
== 0)
470 /* Remove ldev from the list */
471 if (pldev
->pldevPrev
)
472 pldev
->pldevPrev
->pldevNext
= pldev
->pldevNext
;
473 if (pldev
->pldevNext
)
474 pldev
->pldevNext
->pldevPrev
= pldev
->pldevPrev
;
476 /* Unload the image and free the LDEV */
477 LDEVOBJ_vUnloadImage(pldev
);
478 LDEVOBJ_vFreeLDEV(pldev
);
482 EngReleaseSemaphore(ghsemLDEVList
);
488 EngFindImageProcAddress(
490 _In_ LPSTR lpProcName
)
492 PLDEVOBJ pldev
= (PLDEVOBJ
)hModule
;
494 ASSERT(gpldevWin32k
!= NULL
);
496 /* Check if win32k is requested */
499 pldev
= gpldevWin32k
;
502 /* Check if the drivers entry point is requested */
503 if (_strnicmp(lpProcName
, "DrvEnableDriver", 15) == 0)
505 return pldev
->pGdiDriverInfo
->EntryPoint
;
508 /* Try to find the address */
509 return LDEVOBJ_pvFindImageProcAddress(pldev
, lpProcName
);