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