diff --git a/OvmfPkg/QemuVideoDxe/Driver.c b/OvmfPkg/QemuVideoDxe/Driver.c index fc8025ec46..0dce80e59b 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, @@ -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 ( diff --git a/OvmfPkg/QemuVideoDxe/Gop.c b/OvmfPkg/QemuVideoDxe/Gop.c index 359e9217d3..512fd27acb 100644 --- a/OvmfPkg/QemuVideoDxe/Gop.c +++ b/OvmfPkg/QemuVideoDxe/Gop.c @@ -13,6 +13,7 @@ **/ +#include #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. 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 2ce37defc5..7fbb25b3ef 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 { @@ -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