2 This driver is a implementation of the Graphics Output Protocol
3 for the QEMU ramfb device.
5 Copyright (c) 2018, Red Hat Inc.
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Protocol/GraphicsOutput.h>
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/FrameBufferBltLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/QemuFwCfgLib.h>
22 #include <Guid/QemuRamfb.h>
24 #define RAMFB_FORMAT 0x34325258 /* DRM_FORMAT_XRGB8888 */
28 typedef struct RAMFB_CONFIG
{
38 STATIC EFI_HANDLE mRamfbHandle
;
39 STATIC EFI_HANDLE mGopHandle
;
40 STATIC FRAME_BUFFER_CONFIGURE
*mQemuRamfbFrameBufferBltConfigure
;
41 STATIC UINTN mQemuRamfbFrameBufferBltConfigureSize
;
42 STATIC FIRMWARE_CONFIG_ITEM mRamfbFwCfgItem
;
44 STATIC EFI_GRAPHICS_OUTPUT_MODE_INFORMATION mQemuRamfbModeInfo
[] = {
47 640, // HorizontalResolution
48 480, // VerticalResolution
51 800, // HorizontalResolution
52 600, // VerticalResolution
55 1024, // HorizontalResolution
56 768, // VerticalResolution
60 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mQemuRamfbMode
= {
61 ARRAY_SIZE (mQemuRamfbModeInfo
), // MaxMode
63 mQemuRamfbModeInfo
, // Info
64 sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
), // SizeOfInfo
70 QemuRamfbGraphicsOutputQueryMode (
71 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
73 OUT UINTN
*SizeOfInfo
,
74 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
**Info
77 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*ModeInfo
;
79 if (Info
== NULL
|| SizeOfInfo
== NULL
||
80 ModeNumber
>= mQemuRamfbMode
.MaxMode
) {
81 return EFI_INVALID_PARAMETER
;
83 ModeInfo
= &mQemuRamfbModeInfo
[ModeNumber
];
85 *Info
= AllocateCopyPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
),
88 return EFI_OUT_OF_RESOURCES
;
90 *SizeOfInfo
= sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
);
98 QemuRamfbGraphicsOutputSetMode (
99 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
103 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*ModeInfo
;
105 EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black
;
106 RETURN_STATUS Status
;
108 if (ModeNumber
>= mQemuRamfbMode
.MaxMode
) {
109 return EFI_UNSUPPORTED
;
111 ModeInfo
= &mQemuRamfbModeInfo
[ModeNumber
];
113 DEBUG ((DEBUG_INFO
, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber
,
114 ModeInfo
->HorizontalResolution
, ModeInfo
->VerticalResolution
));
116 Config
.Address
= SwapBytes64 (mQemuRamfbMode
.FrameBufferBase
);
117 Config
.FourCC
= SwapBytes32 (RAMFB_FORMAT
);
118 Config
.Flags
= SwapBytes32 (0);
119 Config
.Width
= SwapBytes32 (ModeInfo
->HorizontalResolution
);
120 Config
.Height
= SwapBytes32 (ModeInfo
->VerticalResolution
);
121 Config
.Stride
= SwapBytes32 (ModeInfo
->HorizontalResolution
* RAMFB_BPP
);
123 Status
= FrameBufferBltConfigure (
124 (VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
,
126 mQemuRamfbFrameBufferBltConfigure
,
127 &mQemuRamfbFrameBufferBltConfigureSize
130 if (Status
== RETURN_BUFFER_TOO_SMALL
) {
131 if (mQemuRamfbFrameBufferBltConfigure
!= NULL
) {
132 FreePool (mQemuRamfbFrameBufferBltConfigure
);
134 mQemuRamfbFrameBufferBltConfigure
=
135 AllocatePool (mQemuRamfbFrameBufferBltConfigureSize
);
136 if (mQemuRamfbFrameBufferBltConfigure
== NULL
) {
137 mQemuRamfbFrameBufferBltConfigureSize
= 0;
138 return EFI_OUT_OF_RESOURCES
;
141 Status
= FrameBufferBltConfigure (
142 (VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
,
144 mQemuRamfbFrameBufferBltConfigure
,
145 &mQemuRamfbFrameBufferBltConfigureSize
148 if (RETURN_ERROR (Status
)) {
149 ASSERT (Status
== RETURN_UNSUPPORTED
);
153 mQemuRamfbMode
.Mode
= ModeNumber
;
154 mQemuRamfbMode
.Info
= ModeInfo
;
156 QemuFwCfgSelectItem (mRamfbFwCfgItem
);
157 QemuFwCfgWriteBytes (sizeof (Config
), &Config
);
162 ZeroMem (&Black
, sizeof (Black
));
163 Status
= FrameBufferBlt (
164 mQemuRamfbFrameBufferBltConfigure
,
167 0, // SourceX -- ignored
168 0, // SourceY -- ignored
171 ModeInfo
->HorizontalResolution
, // Width
172 ModeInfo
->VerticalResolution
, // Height
173 0 // Delta -- ignored
175 if (RETURN_ERROR (Status
)) {
176 DEBUG ((DEBUG_WARN
, "%a: clearing the screen failed: %r\n",
177 __FUNCTION__
, Status
));
186 QemuRamfbGraphicsOutputBlt (
187 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
188 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
, OPTIONAL
189 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation
,
192 IN UINTN DestinationX
,
193 IN UINTN DestinationY
,
199 return FrameBufferBlt (
200 mQemuRamfbFrameBufferBltConfigure
,
213 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL mQemuRamfbGraphicsOutput
= {
214 QemuRamfbGraphicsOutputQueryMode
,
215 QemuRamfbGraphicsOutputSetMode
,
216 QemuRamfbGraphicsOutputBlt
,
222 InitializeQemuRamfb (
223 IN EFI_HANDLE ImageHandle
,
224 IN EFI_SYSTEM_TABLE
*SystemTable
227 EFI_DEVICE_PATH_PROTOCOL
*RamfbDevicePath
;
228 EFI_DEVICE_PATH_PROTOCOL
*GopDevicePath
;
230 VENDOR_DEVICE_PATH VendorDeviceNode
;
231 ACPI_ADR_DEVICE_PATH AcpiDeviceNode
;
233 EFI_PHYSICAL_ADDRESS FbBase
;
234 UINTN FbSize
, MaxFbSize
, Pages
;
238 if (!QemuFwCfgIsAvailable ()) {
239 DEBUG ((DEBUG_INFO
, "Ramfb: no FwCfg\n"));
240 return EFI_NOT_FOUND
;
243 Status
= QemuFwCfgFindFile ("etc/ramfb", &mRamfbFwCfgItem
, &FwCfgSize
);
244 if (EFI_ERROR (Status
)) {
245 return EFI_NOT_FOUND
;
247 if (FwCfgSize
!= sizeof (RAMFB_CONFIG
)) {
248 DEBUG ((DEBUG_ERROR
, "Ramfb: FwCfg size mismatch (expected %lu, got %lu)\n",
249 (UINT64
)sizeof (RAMFB_CONFIG
), (UINT64
)FwCfgSize
));
250 return EFI_PROTOCOL_ERROR
;
254 for (Index
= 0; Index
< ARRAY_SIZE (mQemuRamfbModeInfo
); Index
++) {
255 mQemuRamfbModeInfo
[Index
].PixelsPerScanLine
=
256 mQemuRamfbModeInfo
[Index
].HorizontalResolution
;
257 mQemuRamfbModeInfo
[Index
].PixelFormat
=
258 PixelBlueGreenRedReserved8BitPerColor
;
260 mQemuRamfbModeInfo
[Index
].HorizontalResolution
*
261 mQemuRamfbModeInfo
[Index
].VerticalResolution
;
262 if (MaxFbSize
< FbSize
) {
265 DEBUG ((DEBUG_INFO
, "Ramfb: Mode %lu: %ux%u, %lu kB\n", (UINT64
)Index
,
266 mQemuRamfbModeInfo
[Index
].HorizontalResolution
,
267 mQemuRamfbModeInfo
[Index
].VerticalResolution
,
268 (UINT64
)(FbSize
/ 1024)));
271 Pages
= EFI_SIZE_TO_PAGES (MaxFbSize
);
272 MaxFbSize
= EFI_PAGES_TO_SIZE (Pages
);
273 FbBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedPages (Pages
);
275 DEBUG ((DEBUG_ERROR
, "Ramfb: memory allocation failed\n"));
276 return EFI_OUT_OF_RESOURCES
;
278 DEBUG ((DEBUG_INFO
, "Ramfb: Framebuffer at 0x%lx, %lu kB, %lu pages\n",
279 (UINT64
)FbBase
, (UINT64
)(MaxFbSize
/ 1024), (UINT64
)Pages
));
280 mQemuRamfbMode
.FrameBufferSize
= MaxFbSize
;
281 mQemuRamfbMode
.FrameBufferBase
= FbBase
;
286 QemuRamfbGraphicsOutputSetMode (&mQemuRamfbGraphicsOutput
, 1);
289 // ramfb vendor devpath
291 VendorDeviceNode
.Header
.Type
= HARDWARE_DEVICE_PATH
;
292 VendorDeviceNode
.Header
.SubType
= HW_VENDOR_DP
;
293 CopyGuid (&VendorDeviceNode
.Guid
, &gQemuRamfbGuid
);
294 SetDevicePathNodeLength (&VendorDeviceNode
.Header
,
295 sizeof (VENDOR_DEVICE_PATH
));
297 RamfbDevicePath
= AppendDevicePathNode (NULL
,
298 (EFI_DEVICE_PATH_PROTOCOL
*) &VendorDeviceNode
);
299 if (RamfbDevicePath
== NULL
) {
300 Status
= EFI_OUT_OF_RESOURCES
;
301 goto FreeFramebuffer
;
304 Status
= gBS
->InstallMultipleProtocolInterfaces (
306 &gEfiDevicePathProtocolGuid
,
310 if (EFI_ERROR (Status
)) {
311 DEBUG ((DEBUG_ERROR
, "Ramfb: install Ramfb Vendor DevicePath failed: %r\n",
313 goto FreeRamfbDevicePath
;
317 // gop devpath + protocol
319 AcpiDeviceNode
.Header
.Type
= ACPI_DEVICE_PATH
;
320 AcpiDeviceNode
.Header
.SubType
= ACPI_ADR_DP
;
321 AcpiDeviceNode
.ADR
= ACPI_DISPLAY_ADR (
327 ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL
, // Type
331 SetDevicePathNodeLength (&AcpiDeviceNode
.Header
,
332 sizeof (ACPI_ADR_DEVICE_PATH
));
334 GopDevicePath
= AppendDevicePathNode (RamfbDevicePath
,
335 (EFI_DEVICE_PATH_PROTOCOL
*) &AcpiDeviceNode
);
336 if (GopDevicePath
== NULL
) {
337 Status
= EFI_OUT_OF_RESOURCES
;
338 goto FreeRamfbHandle
;
341 Status
= gBS
->InstallMultipleProtocolInterfaces (
343 &gEfiDevicePathProtocolGuid
,
345 &gEfiGraphicsOutputProtocolGuid
,
346 &mQemuRamfbGraphicsOutput
,
349 if (EFI_ERROR (Status
)) {
350 DEBUG ((DEBUG_ERROR
, "Ramfb: install GOP DevicePath failed: %r\n",
352 goto FreeGopDevicePath
;
355 Status
= gBS
->OpenProtocol (
357 &gEfiDevicePathProtocolGuid
,
361 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
363 if (EFI_ERROR (Status
)) {
364 DEBUG ((DEBUG_ERROR
, "Ramfb: OpenProtocol failed: %r\n", Status
));
371 gBS
->UninstallMultipleProtocolInterfaces (
373 &gEfiDevicePathProtocolGuid
,
375 &gEfiGraphicsOutputProtocolGuid
,
376 &mQemuRamfbGraphicsOutput
,
380 FreePool (GopDevicePath
);
382 gBS
->UninstallMultipleProtocolInterfaces (
384 &gEfiDevicePathProtocolGuid
,
389 FreePool (RamfbDevicePath
);
391 FreePages ((VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
, Pages
);