diff options
Diffstat (limited to 'OvmfPkg/QemuRamfbDxe/QemuRamfb.c')
-rw-r--r-- | OvmfPkg/QemuRamfbDxe/QemuRamfb.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/OvmfPkg/QemuRamfbDxe/QemuRamfb.c b/OvmfPkg/QemuRamfbDxe/QemuRamfb.c new file mode 100644 index 0000000000..b49f2ca6e8 --- /dev/null +++ b/OvmfPkg/QemuRamfbDxe/QemuRamfb.c @@ -0,0 +1,400 @@ +/** @file
+ This driver is a implementation of the Graphics Output Protocol
+ for the QEMU ramfb device.
+
+ Copyright (c) 2018, Red Hat Inc.
+
+ This program and the accompanying materials are licensed and made
+ available under the terms and conditions of the BSD License which
+ accompanies this distribution. The full text of the license may be
+ found at http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+ EXPRESS OR IMPLIED.
+
+**/
+
+#include <Protocol/GraphicsOutput.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/FrameBufferBltLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+#include <Guid/QemuRamfb.h>
+
+#define RAMFB_FORMAT 0x34325258 /* DRM_FORMAT_XRGB8888 */
+#define RAMFB_BPP 4
+
+#pragma pack (1)
+typedef struct RAMFB_CONFIG {
+ UINT64 Address;
+ UINT32 FourCC;
+ UINT32 Flags;
+ UINT32 Width;
+ UINT32 Height;
+ UINT32 Stride;
+} RAMFB_CONFIG;
+#pragma pack ()
+
+STATIC EFI_HANDLE mRamfbHandle;
+STATIC EFI_HANDLE mGopHandle;
+STATIC FRAME_BUFFER_CONFIGURE *mQemuRamfbFrameBufferBltConfigure;
+STATIC UINTN mQemuRamfbFrameBufferBltConfigureSize;
+STATIC FIRMWARE_CONFIG_ITEM mRamfbFwCfgItem;
+
+STATIC EFI_GRAPHICS_OUTPUT_MODE_INFORMATION mQemuRamfbModeInfo[] = {
+ {
+ 0, // Version
+ 640, // HorizontalResolution
+ 480, // VerticalResolution
+ },{
+ 0, // Version
+ 800, // HorizontalResolution
+ 600, // VerticalResolution
+ },{
+ 0, // Version
+ 1024, // HorizontalResolution
+ 768, // VerticalResolution
+ }
+};
+
+STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mQemuRamfbMode = {
+ ARRAY_SIZE (mQemuRamfbModeInfo), // MaxMode
+ 0, // Mode
+ mQemuRamfbModeInfo, // Info
+ sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), // SizeOfInfo
+};
+
+STATIC
+EFI_STATUS
+EFIAPI
+QemuRamfbGraphicsOutputQueryMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber,
+ OUT UINTN *SizeOfInfo,
+ OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
+ )
+{
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo;
+
+ if (Info == NULL || SizeOfInfo == NULL ||
+ ModeNumber >= mQemuRamfbMode.MaxMode) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ModeInfo = &mQemuRamfbModeInfo[ModeNumber];
+
+ *Info = AllocateCopyPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
+ ModeInfo);
+ if (*Info == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+QemuRamfbGraphicsOutputSetMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber
+ )
+{
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo;
+ RAMFB_CONFIG Config;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black;
+ RETURN_STATUS Status;
+
+ if (ModeNumber >= mQemuRamfbMode.MaxMode) {
+ return EFI_UNSUPPORTED;
+ }
+ ModeInfo = &mQemuRamfbModeInfo[ModeNumber];
+
+ DEBUG ((DEBUG_INFO, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber,
+ ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution));
+
+ Config.Address = SwapBytes64 (mQemuRamfbMode.FrameBufferBase);
+ Config.FourCC = SwapBytes32 (RAMFB_FORMAT);
+ Config.Flags = SwapBytes32 (0);
+ Config.Width = SwapBytes32 (ModeInfo->HorizontalResolution);
+ Config.Height = SwapBytes32 (ModeInfo->VerticalResolution);
+ Config.Stride = SwapBytes32 (ModeInfo->HorizontalResolution * RAMFB_BPP);
+
+ Status = FrameBufferBltConfigure (
+ (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
+ ModeInfo,
+ mQemuRamfbFrameBufferBltConfigure,
+ &mQemuRamfbFrameBufferBltConfigureSize
+ );
+
+ if (Status == RETURN_BUFFER_TOO_SMALL) {
+ if (mQemuRamfbFrameBufferBltConfigure != NULL) {
+ FreePool (mQemuRamfbFrameBufferBltConfigure);
+ }
+ mQemuRamfbFrameBufferBltConfigure =
+ AllocatePool (mQemuRamfbFrameBufferBltConfigureSize);
+ if (mQemuRamfbFrameBufferBltConfigure == NULL) {
+ mQemuRamfbFrameBufferBltConfigureSize = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = FrameBufferBltConfigure (
+ (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
+ ModeInfo,
+ mQemuRamfbFrameBufferBltConfigure,
+ &mQemuRamfbFrameBufferBltConfigureSize
+ );
+ }
+ if (RETURN_ERROR (Status)) {
+ ASSERT (Status == RETURN_UNSUPPORTED);
+ return Status;
+ }
+
+ mQemuRamfbMode.Mode = ModeNumber;
+ mQemuRamfbMode.Info = ModeInfo;
+
+ QemuFwCfgSelectItem (mRamfbFwCfgItem);
+ QemuFwCfgWriteBytes (sizeof (Config), &Config);
+
+ //
+ // clear screen
+ //
+ ZeroMem (&Black, sizeof (Black));
+ Status = FrameBufferBlt (
+ mQemuRamfbFrameBufferBltConfigure,
+ &Black,
+ EfiBltVideoFill,
+ 0, // SourceX -- ignored
+ 0, // SourceY -- ignored
+ 0, // DestinationX
+ 0, // DestinationY
+ ModeInfo->HorizontalResolution, // Width
+ ModeInfo->VerticalResolution, // Height
+ 0 // Delta -- ignored
+ );
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: clearing the screen failed: %r\n",
+ __FUNCTION__, Status));
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+QemuRamfbGraphicsOutputBlt (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta
+ )
+{
+ return FrameBufferBlt (
+ mQemuRamfbFrameBufferBltConfigure,
+ BltBuffer,
+ BltOperation,
+ SourceX,
+ SourceY,
+ DestinationX,
+ DestinationY,
+ Width,
+ Height,
+ Delta
+ );
+}
+
+STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL mQemuRamfbGraphicsOutput = {
+ QemuRamfbGraphicsOutputQueryMode,
+ QemuRamfbGraphicsOutputSetMode,
+ QemuRamfbGraphicsOutputBlt,
+ &mQemuRamfbMode,
+};
+
+EFI_STATUS
+EFIAPI
+InitializeQemuRamfb (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *RamfbDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;
+ VOID *DevicePath;
+ VENDOR_DEVICE_PATH VendorDeviceNode;
+ ACPI_ADR_DEVICE_PATH AcpiDeviceNode;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS FbBase;
+ UINTN FbSize, MaxFbSize, Pages;
+ UINTN FwCfgSize;
+ UINTN Index;
+
+ if (!QemuFwCfgIsAvailable ()) {
+ DEBUG ((DEBUG_INFO, "Ramfb: no FwCfg\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ Status = QemuFwCfgFindFile ("etc/ramfb", &mRamfbFwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+ if (FwCfgSize != sizeof (RAMFB_CONFIG)) {
+ DEBUG ((DEBUG_ERROR, "Ramfb: FwCfg size mismatch (expected %lu, got %lu)\n",
+ (UINT64)sizeof (RAMFB_CONFIG), (UINT64)FwCfgSize));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ MaxFbSize = 0;
+ for (Index = 0; Index < ARRAY_SIZE (mQemuRamfbModeInfo); Index++) {
+ mQemuRamfbModeInfo[Index].PixelsPerScanLine =
+ mQemuRamfbModeInfo[Index].HorizontalResolution;
+ mQemuRamfbModeInfo[Index].PixelFormat =
+ PixelBlueGreenRedReserved8BitPerColor;
+ FbSize = RAMFB_BPP *
+ mQemuRamfbModeInfo[Index].HorizontalResolution *
+ mQemuRamfbModeInfo[Index].VerticalResolution;
+ if (MaxFbSize < FbSize) {
+ MaxFbSize = FbSize;
+ }
+ DEBUG ((DEBUG_INFO, "Ramfb: Mode %lu: %ux%u, %lu kB\n", (UINT64)Index,
+ mQemuRamfbModeInfo[Index].HorizontalResolution,
+ mQemuRamfbModeInfo[Index].VerticalResolution,
+ (UINT64)(FbSize / 1024)));
+ }
+
+ Pages = EFI_SIZE_TO_PAGES (MaxFbSize);
+ MaxFbSize = EFI_PAGES_TO_SIZE (Pages);
+ FbBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedPages (Pages);
+ if (FbBase == 0) {
+ DEBUG ((DEBUG_ERROR, "Ramfb: memory allocation failed\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ DEBUG ((DEBUG_INFO, "Ramfb: Framebuffer at 0x%lx, %lu kB, %lu pages\n",
+ (UINT64)FbBase, (UINT64)(MaxFbSize / 1024), (UINT64)Pages));
+ mQemuRamfbMode.FrameBufferSize = MaxFbSize;
+ mQemuRamfbMode.FrameBufferBase = FbBase;
+
+ //
+ // 800 x 600
+ //
+ QemuRamfbGraphicsOutputSetMode (&mQemuRamfbGraphicsOutput, 1);
+
+ //
+ // ramfb vendor devpath
+ //
+ VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;
+ VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
+ CopyGuid (&VendorDeviceNode.Guid, &gQemuRamfbGuid);
+ SetDevicePathNodeLength (&VendorDeviceNode.Header,
+ sizeof (VENDOR_DEVICE_PATH));
+
+ RamfbDevicePath = AppendDevicePathNode (NULL,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode);
+ if (RamfbDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeFramebuffer;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mRamfbHandle,
+ &gEfiDevicePathProtocolGuid,
+ RamfbDevicePath,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Ramfb: install Ramfb Vendor DevicePath failed: %r\n",
+ Status));
+ goto FreeRamfbDevicePath;
+ }
+
+ //
+ // gop devpath + protocol
+ //
+ AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH;
+ AcpiDeviceNode.Header.SubType = ACPI_ADR_DP;
+ AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (
+ 1, // DeviceIdScheme
+ 0, // HeadId
+ 0, // NonVgaOutput
+ 1, // BiosCanDetect
+ 0, // VendorInfo
+ ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type
+ 0, // Port
+ 0 // Index
+ );
+ SetDevicePathNodeLength (&AcpiDeviceNode.Header,
+ sizeof (ACPI_ADR_DEVICE_PATH));
+
+ GopDevicePath = AppendDevicePathNode (RamfbDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode);
+ if (GopDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeRamfbHandle;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mGopHandle,
+ &gEfiDevicePathProtocolGuid,
+ GopDevicePath,
+ &gEfiGraphicsOutputProtocolGuid,
+ &mQemuRamfbGraphicsOutput,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Ramfb: install GOP DevicePath failed: %r\n",
+ Status));
+ goto FreeGopDevicePath;
+ }
+
+ Status = gBS->OpenProtocol (
+ mRamfbHandle,
+ &gEfiDevicePathProtocolGuid,
+ &DevicePath,
+ gImageHandle,
+ mGopHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Ramfb: OpenProtocol failed: %r\n", Status));
+ goto FreeGopHandle;
+ }
+
+ return EFI_SUCCESS;
+
+FreeGopHandle:
+ gBS->UninstallMultipleProtocolInterfaces (
+ mGopHandle,
+ &gEfiDevicePathProtocolGuid,
+ GopDevicePath,
+ &gEfiGraphicsOutputProtocolGuid,
+ &mQemuRamfbGraphicsOutput,
+ NULL
+ );
+FreeGopDevicePath:
+ FreePool (GopDevicePath);
+FreeRamfbHandle:
+ gBS->UninstallMultipleProtocolInterfaces (
+ mRamfbHandle,
+ &gEfiDevicePathProtocolGuid,
+ RamfbDevicePath,
+ NULL
+ );
+FreeRamfbDevicePath:
+ FreePool (RamfbDevicePath);
+FreeFramebuffer:
+ FreePages ((VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase, Pages);
+ return Status;
+}
|