From 2e77f0e7b597ce5a1ece37842a0c68a5ac0325ac Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 9 Nov 2018 20:15:31 +0100 Subject: Reapply "OvmfPkg/QemuVideoDxe: VMWare SVGA device support" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 98856a724c2acdc0094220d4de615a557dad0f88, reapplying c137d95081690d4877fbeb5f1856972e84ac32f2. Note that the commit now being reverted is technically correct; the only reason we're reverting it is because it should not have been pushed past the Soft Feature Freeze for the edk2-stable201811 tag. Cc: Anthony Perard Cc: Ard Biesheuvel Cc: Jordan Justen Cc: Julien Grall Cc: Philippe Mathieu-Daudé Cc: yuchenlin Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1319 Signed-off-by: Laszlo Ersek Reviewed-by: Philippe Mathieu-Daudé Acked-by: Ard Biesheuvel Reviewed-by: yuchenlin --- OvmfPkg/QemuVideoDxe/Driver.c | 135 +++++++++++++++++++++++++++++++- OvmfPkg/QemuVideoDxe/Gop.c | 65 ++++++++++++++-- OvmfPkg/QemuVideoDxe/Initialize.c | 157 ++++++++++++++++++++++++++++++++++++++ OvmfPkg/QemuVideoDxe/Qemu.h | 29 +++++++ 4 files changed, 379 insertions(+), 7 deletions(-) (limited to 'OvmfPkg/QemuVideoDxe') diff --git a/OvmfPkg/QemuVideoDxe/Driver.c b/OvmfPkg/QemuVideoDxe/Driver.c index 2304afd1e6..73eb2cad05 100644 --- a/OvmfPkg/QemuVideoDxe/Driver.c +++ b/OvmfPkg/QemuVideoDxe/Driver.c @@ -14,8 +14,10 @@ **/ -#include "Qemu.h" +#include #include +#include "Qemu.h" +#include "UnalignedIoInternal.h" EFI_DRIVER_BINDING_PROTOCOL gQemuVideoDriverBinding = { QemuVideoControllerDriverSupported, @@ -69,6 +71,12 @@ QEMU_VIDEO_CARD gQemuVideoCardList[] = { 0x1050, QEMU_VIDEO_BOCHS_MMIO, L"QEMU VirtIO VGA" + },{ + PCI_CLASS_DISPLAY_VGA, + VMWARE_PCI_VENDOR_ID_VMWARE, + VMWARE_PCI_DEVICE_ID_VMWARE_SVGA2, + QEMU_VIDEO_VMWARE_SVGA, + L"QEMU VMWare SVGA" },{ 0 /* end of list */ } @@ -256,6 +264,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 @@ -330,6 +339,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 // @@ -385,6 +446,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; @@ -446,6 +510,9 @@ DestructQemuVideoGraphics: FreeModeData: FreePool (Private->ModeData); + if (Private->VmwareSvgaModeInfo != NULL) { + FreePool (Private->VmwareSvgaModeInfo); + } UninstallGopDevicePath: gBS->UninstallProtocolInterface (Private->Handle, @@ -567,6 +634,9 @@ QemuVideoControllerDriverStop ( ); FreePool (Private->ModeData); + if (Private->VmwareSvgaModeInfo != NULL) { + FreePool (Private->VmwareSvgaModeInfo); + } gBS->UninstallProtocolInterface (Private->Handle, &gEfiDevicePathProtocolGuid, Private->GopDevicePath); FreePool (Private->GopDevicePath); @@ -764,7 +834,7 @@ ClearScreen ( Private->PciIo->Mem.Write ( Private->PciIo, EfiPciIoWidthFillUint32, - 0, + Private->FrameBufferVramBarIndex, 0, 0x400000 >> 2, &Color @@ -901,6 +971,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, @@ -955,6 +1057,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 ( diff --git a/OvmfPkg/QemuVideoDxe/Gop.c b/OvmfPkg/QemuVideoDxe/Gop.c index d490fa7a2e..c9941ef138 100644 --- a/OvmfPkg/QemuVideoDxe/Gop.c +++ b/OvmfPkg/QemuVideoDxe/Gop.c @@ -13,6 +13,7 @@ **/ +#include #include "Qemu.h" STATIC @@ -78,6 +79,46 @@ 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; + Mode->FrameBufferSize = EFI_PAGES_TO_SIZE ( + EFI_SIZE_TO_PAGES (Mode->FrameBufferSize) + ); + + FreePool (FrameBufDesc); + return Status; +} + + // // Graphics Output Protocol Member Functions // @@ -126,10 +167,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; } @@ -179,6 +224,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; @@ -189,7 +240,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. diff --git a/OvmfPkg/QemuVideoDxe/Initialize.c b/OvmfPkg/QemuVideoDxe/Initialize.c index d5d8cfef96..357124d628 100644 --- a/OvmfPkg/QemuVideoDxe/Initialize.c +++ b/OvmfPkg/QemuVideoDxe/Initialize.c @@ -13,6 +13,7 @@ **/ +#include #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; +} diff --git a/OvmfPkg/QemuVideoDxe/Qemu.h b/OvmfPkg/QemuVideoDxe/Qemu.h index d7da761705..bc49f867a4 100644 --- a/OvmfPkg/QemuVideoDxe/Qemu.h +++ b/OvmfPkg/QemuVideoDxe/Qemu.h @@ -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 { @@ -116,10 +117,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; /// @@ -503,9 +507,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 -- cgit v1.2.3