From 1d25ff51af5c0e7944caa830c1c485cb9882de7d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jun 2018 09:29:34 +0200 Subject: [PATCH] OvmfPkg: add QemuRamfbDxe Add a driver for the qemu ramfb display device. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Gerd Hoffmann [lersek@redhat.com: fix INF banner typo] [lersek@redhat.com: make some local variable definitions more idiomatic] Reviewed-by: Laszlo Ersek Tested-by: Laszlo Ersek --- OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32.fdf | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.fdf | 1 + OvmfPkg/OvmfPkgX64.dsc | 1 + OvmfPkg/OvmfPkgX64.fdf | 1 + OvmfPkg/QemuRamfbDxe/QemuRamfb.c | 400 ++++++++++++++++++++++++++ OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf | 53 ++++ 8 files changed, 459 insertions(+) create mode 100644 OvmfPkg/QemuRamfbDxe/QemuRamfb.c create mode 100644 OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index a2c995b910..7ddda89999 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -745,6 +745,7 @@ MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf + OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf OvmfPkg/VirtioGpuDxe/VirtioGpu.inf # diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index b199713925..52b8b1fea1 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -351,6 +351,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf !endif INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf +INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf INF OvmfPkg/PlatformDxe/Platform.inf INF OvmfPkg/IoMmuDxe/IoMmuDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index bc7db229d2..3481cdc36b 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -754,6 +754,7 @@ MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf + OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf OvmfPkg/VirtioGpuDxe/VirtioGpu.inf # diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index 4ebf64b2b9..70845d6972 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -357,6 +357,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf !endif INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf +INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf INF OvmfPkg/PlatformDxe/Platform.inf INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 0767b34d18..8b0895b0ff 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -752,6 +752,7 @@ MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf + OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf OvmfPkg/VirtioGpuDxe/VirtioGpu.inf # diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 9ca96f9282..1eb46ac9a2 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -357,6 +357,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf !endif INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf +INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf INF OvmfPkg/PlatformDxe/Platform.inf INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf b/OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf new file mode 100644 index 0000000000..013edef72b --- /dev/null +++ b/OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf @@ -0,0 +1,53 @@ +## @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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = QemuRamfbDxe + FILE_GUID = dce1b094-7dc6-45d0-9fdd-d7fc3cc3e4ef + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeQemuRamfb + +[Sources] + QemuRamfb.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + FrameBufferBltLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + QemuFwCfgLib + +[Protocols] + gEfiGraphicsOutputProtocolGuid ## PRODUCES + +[Guids] + gQemuRamfbGuid + +[Depex] + TRUE -- 2.39.2