public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Phil Dennis-Jordan <lists@philjordan.eu>
To: edk2-devel@lists.01.org
Cc: Phil Dennis-Jordan <phil@philjordan.eu>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Laszlo Ersek <lersek@redhat.com>
Subject: [PATCH v2 3/3] OvmfPkg/QemuVideoDxe: VMWare SVGA II device support.
Date: Mon,  3 Apr 2017 10:44:57 +1200	[thread overview]
Message-ID: <1491173097-37305-4-git-send-email-lists@philjordan.eu> (raw)
In-Reply-To: <1491173097-37305-1-git-send-email-lists@philjordan.eu>

From: Phil Dennis-Jordan <phil@philjordan.eu>

In addition to the QXL, Cirrus, etc. VGA adapters, Qemu also implements
a basic version of VMWare's SVGA2 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 SVGA2 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>
---

Notes:
    v2:
    - Unaligned I/O helper functions moved to separate commit [Laszlo]
    - Multi-line function call whitespace fixes.

 OvmfPkg/QemuVideoDxe/Qemu.h       |  37 +++++++
 OvmfPkg/QemuVideoDxe/Driver.c     |  76 ++++++++++++++
 OvmfPkg/QemuVideoDxe/Gop.c        |  74 +++++++++++++-
 OvmfPkg/QemuVideoDxe/Initialize.c | 107 ++++++++++++++++++++
 4 files changed, 293 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/QemuVideoDxe/Qemu.h b/OvmfPkg/QemuVideoDxe/Qemu.h
index 2ce37defc5b8..b5c84c9e695e 100644
--- a/OvmfPkg/QemuVideoDxe/Qemu.h
+++ b/OvmfPkg/QemuVideoDxe/Qemu.h
@@ -56,6 +56,10 @@ typedef struct {
   UINT32  HorizontalResolution;
   UINT32  VerticalResolution;
   UINT32  ColorDepth;
+  //
+  // VMWare specific:
+  //
+  UINT32  PixelsPerLine; // includes any dead space
 } QEMU_VIDEO_MODE_DATA;
 
 #define PIXEL_RED_SHIFT   0
@@ -92,6 +96,7 @@ typedef enum {
   QEMU_VIDEO_CIRRUS_5446,
   QEMU_VIDEO_BOCHS,
   QEMU_VIDEO_BOCHS_MMIO,
+  QEMU_VIDEO_VMWARE_SVGA2,
 } QEMU_VIDEO_VARIANT;
 
 typedef struct {
@@ -119,6 +124,8 @@ typedef struct {
   QEMU_VIDEO_VARIANT                    Variant;
   FRAME_BUFFER_CONFIGURE                *FrameBufferBltConfigure;
   UINTN                                 FrameBufferBltConfigureSize;
+
+  UINT16                                VMWareSVGA2_BasePort;
 } QEMU_VIDEO_PRIVATE_DATA;
 
 ///
@@ -502,9 +509,39 @@ QemuVideoBochsModeSetup (
   BOOLEAN                  IsQxl
   );
 
+EFI_STATUS
+QemuVideoVmwareModeSetup (
+  QEMU_VIDEO_PRIVATE_DATA *Private
+  );
+
 VOID
 InstallVbeShim (
   IN CONST CHAR16         *CardName,
   IN EFI_PHYSICAL_ADDRESS FrameBufferBase
   );
+
+VOID
+QemuVideoVMWSVGA2RegisterWrite (
+  QEMU_VIDEO_PRIVATE_DATA *Private,
+  UINT16                  reg,
+  UINT32                  value
+  );
+
+UINT32
+QemuVideoVMWSVGA2RegisterRead (
+  QEMU_VIDEO_PRIVATE_DATA *Private,
+  UINT16                  reg
+  );
+
+EFI_STATUS
+QemuVideoVMWSVGA2CompleteModeData (
+  IN  QEMU_VIDEO_PRIVATE_DATA           *Private,
+  OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode
+  );
+
+void InitializeVMWSVGA2GraphicsMode (
+  QEMU_VIDEO_PRIVATE_DATA  *Private,
+  QEMU_VIDEO_BOCHS_MODES   *ModeData
+  );
+
 #endif
diff --git a/OvmfPkg/QemuVideoDxe/Driver.c b/OvmfPkg/QemuVideoDxe/Driver.c
index fc8025ec46de..f01aec6f7b0e 100644
--- a/OvmfPkg/QemuVideoDxe/Driver.c
+++ b/OvmfPkg/QemuVideoDxe/Driver.c
@@ -15,6 +15,7 @@
 **/
 
 #include "Qemu.h"
+#include <IndustryStandard/VMWareSVGA2.h>
 #include <IndustryStandard/Acpi.h>
 
 EFI_DRIVER_BINDING_PROTOCOL gQemuVideoDriverBinding = {
@@ -58,6 +59,16 @@ QEMU_VIDEO_CARD gQemuVideoCardList[] = {
         QEMU_VIDEO_BOCHS_MMIO,
         L"QEMU VirtIO VGA"
     },{
+#if defined MDE_CPU_IA32 || defined MDE_CPU_X64
+        //
+        // Support only platforms which can do unaligned port I/O
+        //
+        PCI_VENDOR_ID_VMWARE,
+        PCI_DEVICE_ID_VMWARE_SVGA2,
+        QEMU_VIDEO_VMWARE_SVGA2,
+        L"QEMU VMWare SVGA2"
+    },{
+#endif
         0 /* end of list */
     }
 };
@@ -317,6 +328,45 @@ QemuVideoControllerDriverStart (
   }
 
   //
+  // Check if accessing VMWARE_SVGA2 interface works
+  //
+  if (Private->Variant == QEMU_VIDEO_VMWARE_SVGA2) {
+    EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *iodesc;
+    UINT32 TargetId;
+    UINT32 Svga2IDRead;
+
+    Private->PciIo->GetBarAttributes (
+                      Private->PciIo,
+                      PCI_BAR_IDX0,
+                      NULL,
+                      (VOID**) &iodesc
+                      );
+    Private->VMWareSVGA2_BasePort = iodesc->AddrRangeMin;
+
+    TargetId = SVGA_ID_2;
+    while (1) {
+      QemuVideoVMWSVGA2RegisterWrite (Private, SVGA_REG_ID, TargetId);
+      Svga2IDRead = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_ID);
+      if ((Svga2IDRead == TargetId) || (TargetId <= SVGA_ID_0)) {
+        break;
+      }
+      --TargetId;
+    }
+
+    if (Svga2IDRead != TargetId) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "QemuVideo: QEMU_VIDEO_VMWARE_SVGA2 ID mismatch "
+        "(got 0x%x, base address 0x%x)\n",
+        Svga2IDRead,
+        Private->VMWareSVGA2_BasePort
+        ));
+      Status = EFI_DEVICE_ERROR;
+      goto RestoreAttributes;
+    }
+  }
+
+  //
   // Get ParentDevicePath
   //
   Status = gBS->HandleProtocol (
@@ -371,6 +421,9 @@ QemuVideoControllerDriverStart (
   case QEMU_VIDEO_BOCHS:
     Status = QemuVideoBochsModeSetup (Private, IsQxl);
     break;
+  case QEMU_VIDEO_VMWARE_SVGA2:
+    Status = QemuVideoVmwareModeSetup (Private);
+    break;
   default:
     ASSERT (FALSE);
     Status = EFI_DEVICE_ERROR;
@@ -975,3 +1028,26 @@ InitializeQemuVideo (
 
   return Status;
 }
+
+void InitializeVMWSVGA2GraphicsMode (
+  QEMU_VIDEO_PRIVATE_DATA   *Private,
+  QEMU_VIDEO_BOCHS_MODES    *ModeData
+  )
+{
+  QemuVideoVMWSVGA2RegisterWrite (Private, SVGA_REG_WIDTH, ModeData->Width);
+  QemuVideoVMWSVGA2RegisterWrite (Private, SVGA_REG_HEIGHT, ModeData->Height);
+
+  UINT32 capabilities = QemuVideoVMWSVGA2RegisterRead (
+                          Private,
+                          SVGA_REG_CAPABILITIES
+                          );
+  if ((capabilities & SVGA_CAP_8BIT_EMULATION) != 0) {
+    QemuVideoVMWSVGA2RegisterWrite(
+      Private,
+      SVGA_REG_BITS_PER_PIXEL,
+      ModeData->ColorDepth
+      );
+  }
+  SetDefaultPalette (Private);
+  ClearScreen (Private);
+}
diff --git a/OvmfPkg/QemuVideoDxe/Gop.c b/OvmfPkg/QemuVideoDxe/Gop.c
index 359e9217d3d1..59918676e764 100644
--- a/OvmfPkg/QemuVideoDxe/Gop.c
+++ b/OvmfPkg/QemuVideoDxe/Gop.c
@@ -14,6 +14,7 @@
 **/
 
 #include "Qemu.h"
+#include <IndustryStandard/VMWareSVGA2.h>
 
 STATIC
 VOID
@@ -76,6 +77,63 @@ QemuVideoCompleteModeData (
 }
 
 
+EFI_STATUS
+QemuVMSVGA2VideoCompleteModeData (
+  IN  QEMU_VIDEO_PRIVATE_DATA           *Private,
+  OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode
+  )
+{
+  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *Info;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR     *FrameBufDesc;
+  UINT32                                RedMask, GreenMask, BlueMask;
+  UINT32                                BitsPerPixel, BytesPerLine, FBOffset;
+
+  Info = Mode->Info;
+  Info->Version = 0;
+  Info->PixelFormat = PixelBitMask;
+
+  RedMask = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_RED_MASK);
+  Info->PixelInformation.RedMask = RedMask;
+
+  GreenMask = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_GREEN_MASK);
+  Info->PixelInformation.GreenMask = GreenMask;
+
+  BlueMask = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_BLUE_MASK);
+  Info->PixelInformation.BlueMask = BlueMask;
+
+  QemuVideoVMWSVGA2RegisterWrite (Private, SVGA_REG_ENABLE, 1);
+
+  FBOffset = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_FB_OFFSET);
+  BytesPerLine = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_BYTES_PER_LINE);
+  BitsPerPixel = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_BITS_PER_PIXEL);
+
+  if (BitsPerPixel == 32) {
+    if (BlueMask == 0xff && GreenMask == 0xff00 && RedMask == 0xff0000) {
+      Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
+    } else if (BlueMask == 0xff0000 && GreenMask == 0xff00 && RedMask == 0xff) {
+      Info->PixelFormat = PixelRedGreenBlueReserved8BitPerColor;
+    }
+  }
+
+  Info->PixelInformation.ReservedMask = ((0x2u << (BitsPerPixel - 1u)) - 1u)
+                                         & ~(RedMask | GreenMask | BlueMask);
+  Info->PixelsPerScanLine = BytesPerLine / (BitsPerPixel / 8);
+
+  Private->PciIo->GetBarAttributes (
+                        Private->PciIo,
+                        PCI_BAR_IDX1,
+                        NULL,
+                        (VOID**) &FrameBufDesc
+                        );
+
+  Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin + FBOffset;
+  Mode->FrameBufferSize = BytesPerLine * Info->VerticalResolution;
+
+  FreePool (FrameBufDesc);
+  return EFI_SUCCESS;
+}
+
+
 //
 // Graphics Output Protocol Member Functions
 //
@@ -129,6 +187,10 @@ Routine Description:
   (*Info)->VerticalResolution   = ModeData->VerticalResolution;
   QemuVideoCompleteModeInfo (ModeData, *Info);
 
+  if (Private->Variant == QEMU_VIDEO_VMWARE_SVGA2) {
+    (*Info)->PixelsPerScanLine = ModeData->PixelsPerLine;
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -176,6 +238,12 @@ Routine Description:
   case QEMU_VIDEO_BOCHS:
     InitializeBochsGraphicsMode (Private, &QemuVideoBochsModes[ModeData->InternalModeIndex]);
     break;
+  case QEMU_VIDEO_VMWARE_SVGA2:
+    InitializeVMWSVGA2GraphicsMode (
+      Private,
+      &QemuVideoBochsModes[ModeData->InternalModeIndex]
+      );
+    break;
   default:
     ASSERT (FALSE);
     return EFI_DEVICE_ERROR;
@@ -186,7 +254,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_SVGA2) {
+    QemuVMSVGA2VideoCompleteModeData(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 d5d8cfef9661..8a6db566ab05 100644
--- a/OvmfPkg/QemuVideoDxe/Initialize.c
+++ b/OvmfPkg/QemuVideoDxe/Initialize.c
@@ -14,6 +14,8 @@
 **/
 
 #include "Qemu.h"
+#include "UnalignedIoInternal.h"
+#include <IndustryStandard/VMWareSVGA2.h>
 
 
 ///
@@ -346,3 +348,108 @@ QemuVideoBochsModeSetup (
   return EFI_SUCCESS;
 }
 
+VOID
+QemuVideoVMWSVGA2RegisterWrite (
+  QEMU_VIDEO_PRIVATE_DATA   *Private,
+  UINT16                    reg,
+  UINT32                    value
+  )
+{
+  UnalignedIoWrite32 (Private->VMWareSVGA2_BasePort + SVGA_INDEX_PORT, reg);
+  UnalignedIoWrite32 (Private->VMWareSVGA2_BasePort + SVGA_VALUE_PORT, value);
+}
+
+UINT32
+QemuVideoVMWSVGA2RegisterRead (
+  QEMU_VIDEO_PRIVATE_DATA   *Private,
+  UINT16                    reg
+  )
+{
+  UnalignedIoWrite32 (Private->VMWareSVGA2_BasePort + SVGA_INDEX_PORT, reg);
+  return UnalignedIoRead32 (Private->VMWareSVGA2_BasePort + SVGA_VALUE_PORT);
+}
+
+EFI_STATUS
+QemuVideoVmwareModeSetup (
+  QEMU_VIDEO_PRIVATE_DATA *Private
+  )
+{
+  UINT32                 FBSize;
+  UINT32                 MaxWidth, MaxHeight;
+  UINT32                 Capabilities;
+  UINT32                 HostBitsPerPixel;
+  UINT32                 Index;
+  QEMU_VIDEO_MODE_DATA   *ModeData;
+  QEMU_VIDEO_BOCHS_MODES *VideoMode;
+
+  QemuVideoVMWSVGA2RegisterWrite (Private, SVGA_REG_ENABLE, 0);
+
+  Private->ModeData =
+    AllocatePool (sizeof (Private->ModeData[0]) * QEMU_VIDEO_BOCHS_MODE_COUNT);
+  if (Private->ModeData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  FBSize =       QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_FB_SIZE);
+  MaxWidth =     QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_MAX_WIDTH);
+  MaxHeight =    QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_MAX_HEIGHT);
+  Capabilities = QemuVideoVMWSVGA2RegisterRead (Private, SVGA_REG_CAPABILITIES);
+  if ((Capabilities & SVGA_CAP_8BIT_EMULATION) != 0) {
+    HostBitsPerPixel = QemuVideoVMWSVGA2RegisterRead(
+                         Private,
+                         SVGA_REG_HOST_BITS_PER_PIXEL
+                         );
+    QemuVideoVMWSVGA2RegisterWrite (
+      Private,
+      SVGA_REG_BITS_PER_PIXEL,
+      HostBitsPerPixel
+      );
+  } else {
+    HostBitsPerPixel = QemuVideoVMWSVGA2RegisterRead(
+                         Private,
+                         SVGA_REG_BITS_PER_PIXEL
+                         );
+  }
+
+  ModeData = Private->ModeData;
+  VideoMode = &QemuVideoBochsModes[0];
+  for (Index = 0; Index < QEMU_VIDEO_BOCHS_MODE_COUNT; Index ++) {
+    UINTN RequiredFbSize;
+
+    ASSERT (HostBitsPerPixel % 8 == 0);
+    RequiredFbSize = (UINTN) VideoMode->Width * VideoMode->Height *
+                     (HostBitsPerPixel / 8);
+    if (RequiredFbSize <= FBSize
+     && VideoMode->Width <= MaxWidth
+     && VideoMode->Height <= MaxHeight)
+    {
+      UINT32 BytesPerLine;
+
+      QemuVideoVMWSVGA2RegisterWrite (
+        Private,
+        SVGA_REG_WIDTH,
+        VideoMode->Width
+        );
+      QemuVideoVMWSVGA2RegisterWrite (
+        Private,
+        SVGA_REG_HEIGHT,
+        VideoMode->Height
+        );
+      BytesPerLine = QemuVideoVMWSVGA2RegisterRead (
+                       Private,
+                       SVGA_REG_BYTES_PER_LINE
+                       );
+      ModeData->PixelsPerLine = BytesPerLine / (HostBitsPerPixel / 8);
+
+      ModeData->InternalModeIndex    = Index;
+      ModeData->HorizontalResolution = VideoMode->Width;
+      ModeData->VerticalResolution   = VideoMode->Height;
+      ModeData->ColorDepth           = HostBitsPerPixel;
+
+      ModeData ++;
+    }
+    VideoMode ++;
+  }
+  Private->MaxMode = ModeData - Private->ModeData;
+  return EFI_SUCCESS;
+}
-- 
2.3.2 (Apple Git-55)



  parent reply	other threads:[~2017-04-02 22:45 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-02 22:44 [PATCH v2 0/3] OvmfPkg/QemuVideoDxe: Add VMWare SVGA2 framebuffer support Phil Dennis-Jordan
2017-04-02 22:44 ` [PATCH v2 1/3] OvmfPkg: VMWare SVGA2 display device register definitions Phil Dennis-Jordan
2017-04-03 23:17   ` Jordan Justen
2017-04-04 10:40     ` Laszlo Ersek
2017-04-04  7:54   ` Laszlo Ersek
2017-04-02 22:44 ` [PATCH v2 2/3] OvmfPkg/QemuVideoDxe: Helper functions for unaligned port I/O Phil Dennis-Jordan
2017-04-04  8:19   ` Laszlo Ersek
2017-04-05  9:16     ` Phil Dennis-Jordan
2017-04-05  9:37       ` Laszlo Ersek
2017-04-02 22:44 ` Phil Dennis-Jordan [this message]
2017-04-04 10:13   ` [PATCH v2 3/3] OvmfPkg/QemuVideoDxe: VMWare SVGA II device support Laszlo Ersek
2017-04-02 22:48 ` [PATCH v2 0/3] OvmfPkg/QemuVideoDxe: Add VMWare SVGA2 framebuffer support Phil Dennis-Jordan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1491173097-37305-4-git-send-email-lists@philjordan.eu \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox