When setting up the list of GOP modes offered on QEMU's stdvga ("VGA") and
QXL ("qxl-vga") video devices, QemuVideoBochsModeSetup() filters those
modes against the available framebuffer size. (Refer to SVN r15288 / git
commit ec88061e.)

The current calculation is correct for stdvga, because on stdvga the value
returned by VBE_DISPI_INDEX_VIDEO_MEMORY_64K, ie. the full video RAM, is
usable for drawing.

However, on the QXL card, only an initial portion of the video RAM is
suitable for drawing, as compatibility frame buffer. The size of this
segment can be read from a register in the QXL ROM bar (bar 2). Beyond
this range, the video RAM contains buffers and structures for the QXL
guest-host protocol.

Without this fix, OVMF offers too large resolutions on the QXL card (up to
the full size of the video RAM). If a GOP client selects such a resolution
and draws into the video RAM past the compatibility segment, then the
guest corrupts its communication structures (which is invalid guest
behavior).

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <[email protected]>
---
 OvmfPkg/QemuVideoDxe/Qemu.h       |  3 ++-
 OvmfPkg/QemuVideoDxe/Driver.c     |  3 ++-
 OvmfPkg/QemuVideoDxe/Initialize.c | 41 +++++++++++++++++++++++++++++++++++----
 3 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/OvmfPkg/QemuVideoDxe/Qemu.h b/OvmfPkg/QemuVideoDxe/Qemu.h
index 4bf51c7..52ee20d 100644
--- a/OvmfPkg/QemuVideoDxe/Qemu.h
+++ b/OvmfPkg/QemuVideoDxe/Qemu.h
@@ -499,7 +499,8 @@ QemuVideoCirrusModeSetup (
 
 EFI_STATUS
 QemuVideoBochsModeSetup (
-  QEMU_VIDEO_PRIVATE_DATA  *Private
+  QEMU_VIDEO_PRIVATE_DATA  *Private,
+  BOOLEAN                  IsQxl
   );
 
 VOID
diff --git a/OvmfPkg/QemuVideoDxe/Driver.c b/OvmfPkg/QemuVideoDxe/Driver.c
index 2194cbe..abe2c9d 100644
--- a/OvmfPkg/QemuVideoDxe/Driver.c
+++ b/OvmfPkg/QemuVideoDxe/Driver.c
@@ -354,7 +354,8 @@ QemuVideoControllerDriverStart (
     break;
   case QEMU_VIDEO_BOCHS_MMIO:
   case QEMU_VIDEO_BOCHS:
-    Status = QemuVideoBochsModeSetup (Private);
+    Status = QemuVideoBochsModeSetup (Private,
+               (BOOLEAN)(Card->Variant == QEMU_VIDEO_BOCHS));
     break;
   default:
     ASSERT (FALSE);
diff --git a/OvmfPkg/QemuVideoDxe/Initialize.c 
b/OvmfPkg/QemuVideoDxe/Initialize.c
index a536d47..6111d06 100644
--- a/OvmfPkg/QemuVideoDxe/Initialize.c
+++ b/OvmfPkg/QemuVideoDxe/Initialize.c
@@ -253,7 +253,8 @@ QEMU_VIDEO_BOCHS_MODES  QemuVideoBochsModes[] = {
 
 EFI_STATUS
 QemuVideoBochsModeSetup (
-  QEMU_VIDEO_PRIVATE_DATA  *Private
+  QEMU_VIDEO_PRIVATE_DATA  *Private,
+  BOOLEAN                  IsQxl
   )
 {
   UINT32                                 AvailableFbSize;
@@ -262,10 +263,42 @@ QemuVideoBochsModeSetup (
   QEMU_VIDEO_BOCHS_MODES                 *VideoMode;
 
   //
-  // fetch available framebuffer size
+  // Fetch the available framebuffer size.
   //
-  AvailableFbSize  = BochsRead (Private, VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
-  AvailableFbSize *= SIZE_64KB;
+  // On stdvga, the full memory size returned by the
+  // VBE_DISPI_INDEX_VIDEO_MEMORY_64K is usable for drawing.
+  //
+  // On QXL however, only a leading segment, "surface 0", can be used for
+  // drawing; the rest of the buffer is used for the QXL guest-host protocol.
+  // The size of the drawable portion is available from a field in the QXL ROM
+  // bar.
+  //
+  if (IsQxl) {
+    UINT32 Signature;
+    UINT32 DrawStart;
+
+    Signature = 0;
+    DrawStart = 0xFFFFFFFF;
+    AvailableFbSize = 0;
+    if (EFI_ERROR (
+          Private->PciIo->Mem.Read (Private->PciIo, EfiPciIoWidthUint32,
+                                PCI_BAR_IDX2, 0, 1, &Signature)) ||
+        Signature != SIGNATURE_32 ('Q', 'X', 'R', 'O') ||
+        EFI_ERROR (
+          Private->PciIo->Mem.Read (Private->PciIo, EfiPciIoWidthUint32,
+                                PCI_BAR_IDX2, 36, 1, &DrawStart)) ||
+        DrawStart != 0 ||
+        EFI_ERROR (
+          Private->PciIo->Mem.Read (Private->PciIo, EfiPciIoWidthUint32,
+                                PCI_BAR_IDX2, 40, 1, &AvailableFbSize))) {
+      DEBUG ((EFI_D_ERROR, "%a: can't read size of drawable buffer from QXL "
+        "ROM\n", __FUNCTION__));
+      return EFI_NOT_FOUND;
+    }
+  } else {
+    AvailableFbSize  = BochsRead (Private, VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
+    AvailableFbSize *= SIZE_64KB;
+  }
   DEBUG ((EFI_D_VERBOSE, "%a: AvailableFbSize=0x%x\n", __FUNCTION__,
     AvailableFbSize));
 
-- 
1.8.3.1


------------------------------------------------------------------------------
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to