394 lines
12 KiB
C
394 lines
12 KiB
C
|
/** @file
|
||
|
This driver is a implementation of the Graphics Output Protocol
|
||
|
for the QEMU ramfb device.
|
||
|
|
||
|
Copyright (c) 2018, Red Hat Inc.
|
||
|
|
||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
|
||
|
**/
|
||
|
|
||
|
#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;
|
||
|
}
|