OvmfPkg/QemuVideoDxe: VMWare SVGA device support

In addition to the QXL, Cirrus, etc. VGA adapters, Qemu also implements
a basic version of VMWare's SVGA display device. Drivers for this
device exist for some guest OSes which do not support Qemu's other
display adapters, so supporting it in OVMF is useful in conjunction
with those OSes.

This change adds support for the SVGA device's framebuffer to
QemuVideoDxe's graphics output protocol implementation, based on
VMWare's documentation. The most basic initialisation, framebuffer
layout query, and mode setting operations are implemented.

The device relies on port-based 32-bit I/O, unfortunately on misaligned
addresses. This limits the driver's support to the x86 family of
platforms.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
This commit is contained in:
Phil Dennis-Jordan 2017-04-07 18:25:58 +12:00 committed by Laszlo Ersek
parent 05a5379458
commit c137d95081
4 changed files with 374 additions and 7 deletions

View File

@ -14,8 +14,10 @@
**/
#include "Qemu.h"
#include <IndustryStandard/VmwareSvga.h>
#include <IndustryStandard/Acpi.h>
#include "Qemu.h"
#include "UnalignedIoInternal.h"
EFI_DRIVER_BINDING_PROTOCOL gQemuVideoDriverBinding = {
QemuVideoControllerDriverSupported,
@ -57,6 +59,11 @@ QEMU_VIDEO_CARD gQemuVideoCardList[] = {
0x1050,
QEMU_VIDEO_BOCHS_MMIO,
L"QEMU VirtIO VGA"
},{
VMWARE_PCI_VENDOR_ID_VMWARE,
VMWARE_PCI_DEVICE_ID_VMWARE_SVGA2,
QEMU_VIDEO_VMWARE_SVGA,
L"QEMU VMWare SVGA"
},{
0 /* end of list */
}
@ -242,6 +249,7 @@ QemuVideoControllerDriverStart (
goto ClosePciIo;
}
Private->Variant = Card->Variant;
Private->FrameBufferVramBarIndex = PCI_BAR_IDX0;
//
// IsQxl is based on the detected Card->Variant, which at a later point might
@ -316,6 +324,58 @@ QemuVideoControllerDriverStart (
}
}
//
// Check if accessing Vmware SVGA interface works
//
if (Private->Variant == QEMU_VIDEO_VMWARE_SVGA) {
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *IoDesc;
UINT32 TargetId;
UINT32 SvgaIdRead;
IoDesc = NULL;
Status = Private->PciIo->GetBarAttributes (
Private->PciIo,
PCI_BAR_IDX0,
NULL,
(VOID**) &IoDesc
);
if (EFI_ERROR (Status) ||
IoDesc->ResType != ACPI_ADDRESS_SPACE_TYPE_IO ||
IoDesc->AddrRangeMin > MAX_UINT16 + 1 - (VMWARE_SVGA_VALUE_PORT + 4)) {
if (IoDesc != NULL) {
FreePool (IoDesc);
}
Status = EFI_DEVICE_ERROR;
goto RestoreAttributes;
}
Private->VmwareSvgaBasePort = (UINT16) IoDesc->AddrRangeMin;
FreePool (IoDesc);
TargetId = VMWARE_SVGA_ID_2;
while (TRUE) {
VmwareSvgaWrite (Private, VmwareSvgaRegId, TargetId);
SvgaIdRead = VmwareSvgaRead (Private, VmwareSvgaRegId);
if ((SvgaIdRead == TargetId) || (TargetId <= VMWARE_SVGA_ID_0)) {
break;
}
TargetId--;
}
if (SvgaIdRead != TargetId) {
DEBUG ((
DEBUG_ERROR,
"QemuVideo: QEMU_VIDEO_VMWARE_SVGA ID mismatch "
"(got 0x%x, base address 0x%x)\n",
SvgaIdRead,
Private->VmwareSvgaBasePort
));
Status = EFI_DEVICE_ERROR;
goto RestoreAttributes;
}
Private->FrameBufferVramBarIndex = PCI_BAR_IDX1;
}
//
// Get ParentDevicePath
//
@ -371,6 +431,9 @@ QemuVideoControllerDriverStart (
case QEMU_VIDEO_BOCHS:
Status = QemuVideoBochsModeSetup (Private, IsQxl);
break;
case QEMU_VIDEO_VMWARE_SVGA:
Status = QemuVideoVmwareSvgaModeSetup (Private);
break;
default:
ASSERT (FALSE);
Status = EFI_DEVICE_ERROR;
@ -432,6 +495,9 @@ DestructQemuVideoGraphics:
FreeModeData:
FreePool (Private->ModeData);
if (Private->VmwareSvgaModeInfo != NULL) {
FreePool (Private->VmwareSvgaModeInfo);
}
UninstallGopDevicePath:
gBS->UninstallProtocolInterface (Private->Handle,
@ -553,6 +619,9 @@ QemuVideoControllerDriverStop (
);
FreePool (Private->ModeData);
if (Private->VmwareSvgaModeInfo != NULL) {
FreePool (Private->VmwareSvgaModeInfo);
}
gBS->UninstallProtocolInterface (Private->Handle,
&gEfiDevicePathProtocolGuid, Private->GopDevicePath);
FreePool (Private->GopDevicePath);
@ -750,7 +819,7 @@ ClearScreen (
Private->PciIo->Mem.Write (
Private->PciIo,
EfiPciIoWidthFillUint32,
0,
Private->FrameBufferVramBarIndex,
0,
0x400000 >> 2,
&Color
@ -887,6 +956,38 @@ BochsRead (
return Data;
}
VOID
VmwareSvgaWrite (
QEMU_VIDEO_PRIVATE_DATA *Private,
UINT16 Register,
UINT32 Value
)
{
UnalignedIoWrite32 (
Private->VmwareSvgaBasePort + VMWARE_SVGA_INDEX_PORT,
Register
);
UnalignedIoWrite32 (
Private->VmwareSvgaBasePort + VMWARE_SVGA_VALUE_PORT,
Value
);
}
UINT32
VmwareSvgaRead (
QEMU_VIDEO_PRIVATE_DATA *Private,
UINT16 Register
)
{
UnalignedIoWrite32 (
Private->VmwareSvgaBasePort + VMWARE_SVGA_INDEX_PORT,
Register
);
return UnalignedIoRead32 (
Private->VmwareSvgaBasePort + VMWARE_SVGA_VALUE_PORT
);
}
VOID
VgaOutb (
QEMU_VIDEO_PRIVATE_DATA *Private,
@ -941,6 +1042,35 @@ InitializeBochsGraphicsMode (
ClearScreen (Private);
}
VOID
InitializeVmwareSvgaGraphicsMode (
QEMU_VIDEO_PRIVATE_DATA *Private,
QEMU_VIDEO_BOCHS_MODES *ModeData
)
{
UINT32 Capabilities;
VmwareSvgaWrite (Private, VmwareSvgaRegWidth, ModeData->Width);
VmwareSvgaWrite (Private, VmwareSvgaRegHeight, ModeData->Height);
Capabilities = VmwareSvgaRead (
Private,
VmwareSvgaRegCapabilities
);
if ((Capabilities & VMWARE_SVGA_CAP_8BIT_EMULATION) != 0) {
VmwareSvgaWrite (
Private,
VmwareSvgaRegBitsPerPixel,
ModeData->ColorDepth
);
}
VmwareSvgaWrite (Private, VmwareSvgaRegEnable, 1);
SetDefaultPalette (Private);
ClearScreen (Private);
}
EFI_STATUS
EFIAPI
InitializeQemuVideo (

View File

@ -13,6 +13,7 @@
**/
#include <IndustryStandard/VmwareSvga.h>
#include "Qemu.h"
STATIC
@ -75,6 +76,42 @@ QemuVideoCompleteModeData (
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
QemuVideoVmwareSvgaCompleteModeData (
IN QEMU_VIDEO_PRIVATE_DATA *Private,
OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode
)
{
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc;
UINT32 BytesPerLine, FbOffset, BytesPerPixel;
Info = Mode->Info;
CopyMem (Info, &Private->VmwareSvgaModeInfo[Mode->Mode], sizeof (*Info));
BytesPerPixel = Private->ModeData[Mode->Mode].ColorDepth / 8;
BytesPerLine = Info->PixelsPerScanLine * BytesPerPixel;
FbOffset = VmwareSvgaRead (Private, VmwareSvgaRegFbOffset);
Status = Private->PciIo->GetBarAttributes (
Private->PciIo,
PCI_BAR_IDX1,
NULL,
(VOID**) &FrameBufDesc
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin + FbOffset;
Mode->FrameBufferSize = BytesPerLine * Info->VerticalResolution;
FreePool (FrameBufDesc);
return Status;
}
//
// Graphics Output Protocol Member Functions
@ -124,10 +161,14 @@ Routine Description:
*SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
ModeData = &Private->ModeData[ModeNumber];
(*Info)->HorizontalResolution = ModeData->HorizontalResolution;
(*Info)->VerticalResolution = ModeData->VerticalResolution;
QemuVideoCompleteModeInfo (ModeData, *Info);
if (Private->Variant == QEMU_VIDEO_VMWARE_SVGA) {
CopyMem (*Info, &Private->VmwareSvgaModeInfo[ModeNumber], sizeof (**Info));
} else {
ModeData = &Private->ModeData[ModeNumber];
(*Info)->HorizontalResolution = ModeData->HorizontalResolution;
(*Info)->VerticalResolution = ModeData->VerticalResolution;
QemuVideoCompleteModeInfo (ModeData, *Info);
}
return EFI_SUCCESS;
}
@ -176,6 +217,12 @@ Routine Description:
case QEMU_VIDEO_BOCHS:
InitializeBochsGraphicsMode (Private, &QemuVideoBochsModes[ModeData->InternalModeIndex]);
break;
case QEMU_VIDEO_VMWARE_SVGA:
InitializeVmwareSvgaGraphicsMode (
Private,
&QemuVideoBochsModes[ModeData->InternalModeIndex]
);
break;
default:
ASSERT (FALSE);
return EFI_DEVICE_ERROR;
@ -186,7 +233,11 @@ Routine Description:
This->Mode->Info->VerticalResolution = ModeData->VerticalResolution;
This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
QemuVideoCompleteModeData (Private, This->Mode);
if (Private->Variant == QEMU_VIDEO_VMWARE_SVGA) {
QemuVideoVmwareSvgaCompleteModeData (Private, This->Mode);
} else {
QemuVideoCompleteModeData (Private, This->Mode);
}
//
// Re-initialize the frame buffer configure when mode changes.

View File

@ -13,6 +13,7 @@
**/
#include <IndustryStandard/VmwareSvga.h>
#include "Qemu.h"
@ -346,3 +347,159 @@ QemuVideoBochsModeSetup (
return EFI_SUCCESS;
}
EFI_STATUS
QemuVideoVmwareSvgaModeSetup (
QEMU_VIDEO_PRIVATE_DATA *Private
)
{
EFI_STATUS Status;
UINT32 FbSize;
UINT32 MaxWidth, MaxHeight;
UINT32 Capabilities;
UINT32 BitsPerPixel;
UINT32 Index;
QEMU_VIDEO_MODE_DATA *ModeData;
QEMU_VIDEO_BOCHS_MODES *VideoMode;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo;
VmwareSvgaWrite (Private, VmwareSvgaRegEnable, 0);
Private->ModeData =
AllocatePool (sizeof (Private->ModeData[0]) * QEMU_VIDEO_BOCHS_MODE_COUNT);
if (Private->ModeData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ModeDataAllocError;
}
Private->VmwareSvgaModeInfo =
AllocatePool (
sizeof (Private->VmwareSvgaModeInfo[0]) * QEMU_VIDEO_BOCHS_MODE_COUNT
);
if (Private->VmwareSvgaModeInfo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ModeInfoAllocError;
}
FbSize = VmwareSvgaRead (Private, VmwareSvgaRegFbSize);
MaxWidth = VmwareSvgaRead (Private, VmwareSvgaRegMaxWidth);
MaxHeight = VmwareSvgaRead (Private, VmwareSvgaRegMaxHeight);
Capabilities = VmwareSvgaRead (Private, VmwareSvgaRegCapabilities);
if ((Capabilities & VMWARE_SVGA_CAP_8BIT_EMULATION) != 0) {
BitsPerPixel = VmwareSvgaRead (
Private,
VmwareSvgaRegHostBitsPerPixel
);
VmwareSvgaWrite (
Private,
VmwareSvgaRegBitsPerPixel,
BitsPerPixel
);
} else {
BitsPerPixel = VmwareSvgaRead (
Private,
VmwareSvgaRegBitsPerPixel
);
}
if (FbSize == 0 ||
MaxWidth == 0 ||
MaxHeight == 0 ||
BitsPerPixel == 0 ||
BitsPerPixel % 8 != 0) {
Status = EFI_DEVICE_ERROR;
goto Rollback;
}
ModeData = Private->ModeData;
ModeInfo = Private->VmwareSvgaModeInfo;
VideoMode = &QemuVideoBochsModes[0];
for (Index = 0; Index < QEMU_VIDEO_BOCHS_MODE_COUNT; Index++) {
UINTN RequiredFbSize;
RequiredFbSize = (UINTN) VideoMode->Width * VideoMode->Height *
(BitsPerPixel / 8);
if (RequiredFbSize <= FbSize &&
VideoMode->Width <= MaxWidth &&
VideoMode->Height <= MaxHeight) {
UINT32 BytesPerLine;
UINT32 RedMask, GreenMask, BlueMask, PixelMask;
VmwareSvgaWrite (
Private,
VmwareSvgaRegWidth,
VideoMode->Width
);
VmwareSvgaWrite (
Private,
VmwareSvgaRegHeight,
VideoMode->Height
);
ModeData->InternalModeIndex = Index;
ModeData->HorizontalResolution = VideoMode->Width;
ModeData->VerticalResolution = VideoMode->Height;
ModeData->ColorDepth = BitsPerPixel;
//
// Setting VmwareSvgaRegWidth/VmwareSvgaRegHeight actually changes
// the device's display mode, so we save all properties of each mode up
// front to avoid inadvertent mode changes later.
//
ModeInfo->Version = 0;
ModeInfo->HorizontalResolution = ModeData->HorizontalResolution;
ModeInfo->VerticalResolution = ModeData->VerticalResolution;
ModeInfo->PixelFormat = PixelBitMask;
RedMask = VmwareSvgaRead (Private, VmwareSvgaRegRedMask);
ModeInfo->PixelInformation.RedMask = RedMask;
GreenMask = VmwareSvgaRead (Private, VmwareSvgaRegGreenMask);
ModeInfo->PixelInformation.GreenMask = GreenMask;
BlueMask = VmwareSvgaRead (Private, VmwareSvgaRegBlueMask);
ModeInfo->PixelInformation.BlueMask = BlueMask;
//
// Reserved mask is whatever bits in the pixel not containing RGB data,
// so start with binary 1s for every bit in the pixel, then mask off
// bits already used for RGB. Special case 32 to avoid undefined
// behaviour in the shift.
//
if (BitsPerPixel == 32) {
if (BlueMask == 0xff && GreenMask == 0xff00 && RedMask == 0xff0000) {
ModeInfo->PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
} else if (BlueMask == 0xff0000 &&
GreenMask == 0xff00 &&
RedMask == 0xff) {
ModeInfo->PixelFormat = PixelRedGreenBlueReserved8BitPerColor;
}
PixelMask = MAX_UINT32;
} else {
PixelMask = (1u << BitsPerPixel) - 1;
}
ModeInfo->PixelInformation.ReservedMask =
PixelMask & ~(RedMask | GreenMask | BlueMask);
BytesPerLine = VmwareSvgaRead (Private, VmwareSvgaRegBytesPerLine);
ModeInfo->PixelsPerScanLine = BytesPerLine / (BitsPerPixel / 8);
ModeData++;
ModeInfo++;
}
VideoMode++;
}
Private->MaxMode = ModeData - Private->ModeData;
return EFI_SUCCESS;
Rollback:
FreePool (Private->VmwareSvgaModeInfo);
Private->VmwareSvgaModeInfo = NULL;
ModeInfoAllocError:
FreePool (Private->ModeData);
Private->ModeData = NULL;
ModeDataAllocError:
return Status;
}

View File

@ -92,6 +92,7 @@ typedef enum {
QEMU_VIDEO_CIRRUS_5446,
QEMU_VIDEO_BOCHS,
QEMU_VIDEO_BOCHS_MMIO,
QEMU_VIDEO_VMWARE_SVGA,
} QEMU_VIDEO_VARIANT;
typedef struct {
@ -115,10 +116,13 @@ typedef struct {
//
UINTN MaxMode;
QEMU_VIDEO_MODE_DATA *ModeData;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *VmwareSvgaModeInfo;
QEMU_VIDEO_VARIANT Variant;
FRAME_BUFFER_CONFIGURE *FrameBufferBltConfigure;
UINTN FrameBufferBltConfigureSize;
UINT8 FrameBufferVramBarIndex;
UINT16 VmwareSvgaBasePort;
} QEMU_VIDEO_PRIVATE_DATA;
///
@ -502,9 +506,34 @@ QemuVideoBochsModeSetup (
BOOLEAN IsQxl
);
EFI_STATUS
QemuVideoVmwareSvgaModeSetup (
QEMU_VIDEO_PRIVATE_DATA *Private
);
VOID
InstallVbeShim (
IN CONST CHAR16 *CardName,
IN EFI_PHYSICAL_ADDRESS FrameBufferBase
);
VOID
VmwareSvgaWrite (
QEMU_VIDEO_PRIVATE_DATA *Private,
UINT16 Register,
UINT32 Value
);
UINT32
VmwareSvgaRead (
QEMU_VIDEO_PRIVATE_DATA *Private,
UINT16 Register
);
VOID
InitializeVmwareSvgaGraphicsMode (
QEMU_VIDEO_PRIVATE_DATA *Private,
QEMU_VIDEO_BOCHS_MODES *ModeData
);
#endif