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 This program and the accompanying materials are licensed and made
8 available under the terms and conditions of the BSD License which
9 accompanies this distribution. The full text of the license may be
10 found at http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
13 BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
18 #include <Protocol/GraphicsOutput.h>
20 #include <Library/BaseLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/DevicePathLib.h>
24 #include <Library/FrameBufferBltLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Library/QemuFwCfgLib.h>
29 #include <Guid/QemuRamfb.h>
31 #define RAMFB_FORMAT 0x34325258 /* DRM_FORMAT_XRGB8888 */
35 typedef struct RAMFB_CONFIG
{
45 STATIC EFI_HANDLE mRamfbHandle
;
46 STATIC EFI_HANDLE mGopHandle
;
47 STATIC FRAME_BUFFER_CONFIGURE
*mQemuRamfbFrameBufferBltConfigure
;
48 STATIC UINTN mQemuRamfbFrameBufferBltConfigureSize
;
49 STATIC FIRMWARE_CONFIG_ITEM mRamfbFwCfgItem
;
51 STATIC EFI_GRAPHICS_OUTPUT_MODE_INFORMATION mQemuRamfbModeInfo
[] = {
54 640, // HorizontalResolution
55 480, // VerticalResolution
58 800, // HorizontalResolution
59 600, // VerticalResolution
62 1024, // HorizontalResolution
63 768, // VerticalResolution
67 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mQemuRamfbMode
= {
68 ARRAY_SIZE (mQemuRamfbModeInfo
), // MaxMode
70 mQemuRamfbModeInfo
, // Info
71 sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
), // SizeOfInfo
77 QemuRamfbGraphicsOutputQueryMode (
78 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
80 OUT UINTN
*SizeOfInfo
,
81 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
**Info
84 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*ModeInfo
;
86 if (Info
== NULL
|| SizeOfInfo
== NULL
||
87 ModeNumber
>= mQemuRamfbMode
.MaxMode
) {
88 return EFI_INVALID_PARAMETER
;
90 ModeInfo
= &mQemuRamfbModeInfo
[ModeNumber
];
92 *Info
= AllocateCopyPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
),
95 return EFI_OUT_OF_RESOURCES
;
97 *SizeOfInfo
= sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
);
105 QemuRamfbGraphicsOutputSetMode (
106 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
110 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*ModeInfo
;
112 EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black
;
113 RETURN_STATUS Status
;
115 if (ModeNumber
>= mQemuRamfbMode
.MaxMode
) {
116 return EFI_UNSUPPORTED
;
118 ModeInfo
= &mQemuRamfbModeInfo
[ModeNumber
];
120 DEBUG ((DEBUG_INFO
, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber
,
121 ModeInfo
->HorizontalResolution
, ModeInfo
->VerticalResolution
));
123 Config
.Address
= SwapBytes64 (mQemuRamfbMode
.FrameBufferBase
);
124 Config
.FourCC
= SwapBytes32 (RAMFB_FORMAT
);
125 Config
.Flags
= SwapBytes32 (0);
126 Config
.Width
= SwapBytes32 (ModeInfo
->HorizontalResolution
);
127 Config
.Height
= SwapBytes32 (ModeInfo
->VerticalResolution
);
128 Config
.Stride
= SwapBytes32 (ModeInfo
->HorizontalResolution
* RAMFB_BPP
);
130 Status
= FrameBufferBltConfigure (
131 (VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
,
133 mQemuRamfbFrameBufferBltConfigure
,
134 &mQemuRamfbFrameBufferBltConfigureSize
137 if (Status
== RETURN_BUFFER_TOO_SMALL
) {
138 if (mQemuRamfbFrameBufferBltConfigure
!= NULL
) {
139 FreePool (mQemuRamfbFrameBufferBltConfigure
);
141 mQemuRamfbFrameBufferBltConfigure
=
142 AllocatePool (mQemuRamfbFrameBufferBltConfigureSize
);
143 if (mQemuRamfbFrameBufferBltConfigure
== NULL
) {
144 mQemuRamfbFrameBufferBltConfigureSize
= 0;
145 return EFI_OUT_OF_RESOURCES
;
148 Status
= FrameBufferBltConfigure (
149 (VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
,
151 mQemuRamfbFrameBufferBltConfigure
,
152 &mQemuRamfbFrameBufferBltConfigureSize
155 if (RETURN_ERROR (Status
)) {
156 ASSERT (Status
== RETURN_UNSUPPORTED
);
160 mQemuRamfbMode
.Mode
= ModeNumber
;
161 mQemuRamfbMode
.Info
= ModeInfo
;
163 QemuFwCfgSelectItem (mRamfbFwCfgItem
);
164 QemuFwCfgWriteBytes (sizeof (Config
), &Config
);
169 ZeroMem (&Black
, sizeof (Black
));
170 Status
= FrameBufferBlt (
171 mQemuRamfbFrameBufferBltConfigure
,
174 0, // SourceX -- ignored
175 0, // SourceY -- ignored
178 ModeInfo
->HorizontalResolution
, // Width
179 ModeInfo
->VerticalResolution
, // Height
180 0 // Delta -- ignored
182 if (RETURN_ERROR (Status
)) {
183 DEBUG ((DEBUG_WARN
, "%a: clearing the screen failed: %r\n",
184 __FUNCTION__
, Status
));
193 QemuRamfbGraphicsOutputBlt (
194 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
195 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
, OPTIONAL
196 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation
,
199 IN UINTN DestinationX
,
200 IN UINTN DestinationY
,
206 return FrameBufferBlt (
207 mQemuRamfbFrameBufferBltConfigure
,
220 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL mQemuRamfbGraphicsOutput
= {
221 QemuRamfbGraphicsOutputQueryMode
,
222 QemuRamfbGraphicsOutputSetMode
,
223 QemuRamfbGraphicsOutputBlt
,
229 InitializeQemuRamfb (
230 IN EFI_HANDLE ImageHandle
,
231 IN EFI_SYSTEM_TABLE
*SystemTable
234 EFI_DEVICE_PATH_PROTOCOL
*RamfbDevicePath
;
235 EFI_DEVICE_PATH_PROTOCOL
*GopDevicePath
;
237 VENDOR_DEVICE_PATH VendorDeviceNode
;
238 ACPI_ADR_DEVICE_PATH AcpiDeviceNode
;
240 EFI_PHYSICAL_ADDRESS FbBase
;
241 UINTN FbSize
, MaxFbSize
, Pages
;
245 if (!QemuFwCfgIsAvailable ()) {
246 DEBUG ((DEBUG_INFO
, "Ramfb: no FwCfg\n"));
247 return EFI_NOT_FOUND
;
250 Status
= QemuFwCfgFindFile ("etc/ramfb", &mRamfbFwCfgItem
, &FwCfgSize
);
251 if (EFI_ERROR (Status
)) {
252 return EFI_NOT_FOUND
;
254 if (FwCfgSize
!= sizeof (RAMFB_CONFIG
)) {
255 DEBUG ((DEBUG_ERROR
, "Ramfb: FwCfg size mismatch (expected %lu, got %lu)\n",
256 (UINT64
)sizeof (RAMFB_CONFIG
), (UINT64
)FwCfgSize
));
257 return EFI_PROTOCOL_ERROR
;
261 for (Index
= 0; Index
< ARRAY_SIZE (mQemuRamfbModeInfo
); Index
++) {
262 mQemuRamfbModeInfo
[Index
].PixelsPerScanLine
=
263 mQemuRamfbModeInfo
[Index
].HorizontalResolution
;
264 mQemuRamfbModeInfo
[Index
].PixelFormat
=
265 PixelBlueGreenRedReserved8BitPerColor
;
267 mQemuRamfbModeInfo
[Index
].HorizontalResolution
*
268 mQemuRamfbModeInfo
[Index
].VerticalResolution
;
269 if (MaxFbSize
< FbSize
) {
272 DEBUG ((DEBUG_INFO
, "Ramfb: Mode %lu: %ux%u, %lu kB\n", (UINT64
)Index
,
273 mQemuRamfbModeInfo
[Index
].HorizontalResolution
,
274 mQemuRamfbModeInfo
[Index
].VerticalResolution
,
275 (UINT64
)(FbSize
/ 1024)));
278 Pages
= EFI_SIZE_TO_PAGES (MaxFbSize
);
279 MaxFbSize
= EFI_PAGES_TO_SIZE (Pages
);
280 FbBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedPages (Pages
);
282 DEBUG ((DEBUG_ERROR
, "Ramfb: memory allocation failed\n"));
283 return EFI_OUT_OF_RESOURCES
;
285 DEBUG ((DEBUG_INFO
, "Ramfb: Framebuffer at 0x%lx, %lu kB, %lu pages\n",
286 (UINT64
)FbBase
, (UINT64
)(MaxFbSize
/ 1024), (UINT64
)Pages
));
287 mQemuRamfbMode
.FrameBufferSize
= MaxFbSize
;
288 mQemuRamfbMode
.FrameBufferBase
= FbBase
;
293 QemuRamfbGraphicsOutputSetMode (&mQemuRamfbGraphicsOutput
, 1);
296 // ramfb vendor devpath
298 VendorDeviceNode
.Header
.Type
= HARDWARE_DEVICE_PATH
;
299 VendorDeviceNode
.Header
.SubType
= HW_VENDOR_DP
;
300 CopyGuid (&VendorDeviceNode
.Guid
, &gQemuRamfbGuid
);
301 SetDevicePathNodeLength (&VendorDeviceNode
.Header
,
302 sizeof (VENDOR_DEVICE_PATH
));
304 RamfbDevicePath
= AppendDevicePathNode (NULL
,
305 (EFI_DEVICE_PATH_PROTOCOL
*) &VendorDeviceNode
);
306 if (RamfbDevicePath
== NULL
) {
307 Status
= EFI_OUT_OF_RESOURCES
;
308 goto FreeFramebuffer
;
311 Status
= gBS
->InstallMultipleProtocolInterfaces (
313 &gEfiDevicePathProtocolGuid
,
317 if (EFI_ERROR (Status
)) {
318 DEBUG ((DEBUG_ERROR
, "Ramfb: install Ramfb Vendor DevicePath failed: %r\n",
320 goto FreeRamfbDevicePath
;
324 // gop devpath + protocol
326 AcpiDeviceNode
.Header
.Type
= ACPI_DEVICE_PATH
;
327 AcpiDeviceNode
.Header
.SubType
= ACPI_ADR_DP
;
328 AcpiDeviceNode
.ADR
= ACPI_DISPLAY_ADR (
334 ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL
, // Type
338 SetDevicePathNodeLength (&AcpiDeviceNode
.Header
,
339 sizeof (ACPI_ADR_DEVICE_PATH
));
341 GopDevicePath
= AppendDevicePathNode (RamfbDevicePath
,
342 (EFI_DEVICE_PATH_PROTOCOL
*) &AcpiDeviceNode
);
343 if (GopDevicePath
== NULL
) {
344 Status
= EFI_OUT_OF_RESOURCES
;
345 goto FreeRamfbHandle
;
348 Status
= gBS
->InstallMultipleProtocolInterfaces (
350 &gEfiDevicePathProtocolGuid
,
352 &gEfiGraphicsOutputProtocolGuid
,
353 &mQemuRamfbGraphicsOutput
,
356 if (EFI_ERROR (Status
)) {
357 DEBUG ((DEBUG_ERROR
, "Ramfb: install GOP DevicePath failed: %r\n",
359 goto FreeGopDevicePath
;
362 Status
= gBS
->OpenProtocol (
364 &gEfiDevicePathProtocolGuid
,
368 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
370 if (EFI_ERROR (Status
)) {
371 DEBUG ((DEBUG_ERROR
, "Ramfb: OpenProtocol failed: %r\n", Status
));
378 gBS
->UninstallMultipleProtocolInterfaces (
380 &gEfiDevicePathProtocolGuid
,
382 &gEfiGraphicsOutputProtocolGuid
,
383 &mQemuRamfbGraphicsOutput
,
387 FreePool (GopDevicePath
);
389 gBS
->UninstallMultipleProtocolInterfaces (
391 &gEfiDevicePathProtocolGuid
,
396 FreePool (RamfbDevicePath
);
398 FreePages ((VOID
*)(UINTN
)mQemuRamfbMode
.FrameBufferBase
, Pages
);