diff mbox

[edk2,v2] OvmfPkg: QemuVideoDxe: work around misreported QXL framebuffer size

Message ID 1409041347-1638-1-git-send-email-lersek@redhat.com
State New
Headers show

Commit Message

Laszlo Ersek Aug. 26, 2014, 8:22 a.m. UTC
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 VBE_DISPI_INDEX_VIDEO_MEMORY_64K register of both stdvga and QXL is
supposed to report the size of the drawable, VGA-compatibility
framebuffer. Instead, up to and including qemu-2.1, this register actually
reports the full video RAM (PCI BAR 0) size.

In case of stdvga, this happens to be correct, because on that card the
full PCI BAR 0 is usable for drawing; there is no difference between
"drawable framebuffer size" and "video RAM (PCI BAR 0) size".

However, on the QXL card, only an initial portion of the video RAM is
suitable for drawing, as compatibility framebuffer; and the value
currently reported by VBE_DISPI_INDEX_VIDEO_MEMORY_64K overshoots the
valid size. Beyond the drawable range, the video RAM contains buffers and
structures for the QXL guest-host protocol.

Luckily, the size of the drawable QXL framebuffer can also be read from a
register in the QXL ROM BAR (PCI BAR 2), so let's retrieve it from there.

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 <lersek@redhat.com>
---

Notes:
    v2:
    - clarify in the commit message and the QemuVideoBochsModeSetup()
      comment that this is a workaround for a QEMU bug [Gerd]
    - Calculate IsQxl more explicitly in QemuVideoControllerDriverStart()
      [Jordan]

 OvmfPkg/QemuVideoDxe/Qemu.h       |  3 ++-
 OvmfPkg/QemuVideoDxe/Driver.c     |  9 +++++++-
 OvmfPkg/QemuVideoDxe/Initialize.c | 47 +++++++++++++++++++++++++++++++++++----
 3 files changed, 53 insertions(+), 6 deletions(-)

Comments

Gerd Hoffmann Aug. 26, 2014, 9:52 a.m. UTC | #1
On Di, 2014-08-26 at 10:22 +0200, Laszlo Ersek wrote:
> 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 VBE_DISPI_INDEX_VIDEO_MEMORY_64K register of both stdvga and QXL
> is
> supposed to report the size of the drawable, VGA-compatibility
> framebuffer. Instead, up to and including qemu-2.1, this register
> actually
> reports the full video RAM (PCI BAR 0) size.
> 
> In case of stdvga, this happens to be correct, because on that card
> the
> full PCI BAR 0 is usable for drawing; there is no difference between
> "drawable framebuffer size" and "video RAM (PCI BAR 0) size".
> 
> However, on the QXL card, only an initial portion of the video RAM is
> suitable for drawing, as compatibility framebuffer; and the value
> currently reported by VBE_DISPI_INDEX_VIDEO_MEMORY_64K overshoots the
> valid size. Beyond the drawable range, the video RAM contains buffers
> and
> structures for the QXL guest-host protocol.
> 
> Luckily, the size of the drawable QXL framebuffer can also be read
> from a
> register in the QXL ROM BAR (PCI BAR 2), so let's retrieve it from
> there.
> 
> 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 <lersek@redhat.com>

Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>


------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
Laszlo Ersek Aug. 29, 2014, 4:05 p.m. UTC | #2
Jordan,

On 08/26/14 11:52, Gerd Hoffmann wrote:
> On Di, 2014-08-26 at 10:22 +0200, Laszlo Ersek wrote:
>> 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 VBE_DISPI_INDEX_VIDEO_MEMORY_64K register of both stdvga and QXL
>> is
>> supposed to report the size of the drawable, VGA-compatibility
>> framebuffer. Instead, up to and including qemu-2.1, this register
>> actually
>> reports the full video RAM (PCI BAR 0) size.
>>
>> In case of stdvga, this happens to be correct, because on that card
>> the
>> full PCI BAR 0 is usable for drawing; there is no difference between
>> "drawable framebuffer size" and "video RAM (PCI BAR 0) size".
>>
>> However, on the QXL card, only an initial portion of the video RAM is
>> suitable for drawing, as compatibility framebuffer; and the value
>> currently reported by VBE_DISPI_INDEX_VIDEO_MEMORY_64K overshoots the
>> valid size. Beyond the drawable range, the video RAM contains buffers
>> and
>> structures for the QXL guest-host protocol.
>>
>> Luckily, the size of the drawable QXL framebuffer can also be read
>> from a
>> register in the QXL ROM BAR (PCI BAR 2), so let's retrieve it from
>> there.
>>
>> 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 <lersek@redhat.com>
> 
> Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>

can you please apply this?

Thanks,
Laszlo


------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
Jordan Justen Aug. 29, 2014, 5:29 p.m. UTC | #3
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>

Committed in r15978. Thanks!

On Fri, Aug 29, 2014 at 9:05 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> Jordan,
>
> On 08/26/14 11:52, Gerd Hoffmann wrote:
>> On Di, 2014-08-26 at 10:22 +0200, Laszlo Ersek wrote:
>>> 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 VBE_DISPI_INDEX_VIDEO_MEMORY_64K register of both stdvga and QXL
>>> is
>>> supposed to report the size of the drawable, VGA-compatibility
>>> framebuffer. Instead, up to and including qemu-2.1, this register
>>> actually
>>> reports the full video RAM (PCI BAR 0) size.
>>>
>>> In case of stdvga, this happens to be correct, because on that card
>>> the
>>> full PCI BAR 0 is usable for drawing; there is no difference between
>>> "drawable framebuffer size" and "video RAM (PCI BAR 0) size".
>>>
>>> However, on the QXL card, only an initial portion of the video RAM is
>>> suitable for drawing, as compatibility framebuffer; and the value
>>> currently reported by VBE_DISPI_INDEX_VIDEO_MEMORY_64K overshoots the
>>> valid size. Beyond the drawable range, the video RAM contains buffers
>>> and
>>> structures for the QXL guest-host protocol.
>>>
>>> Luckily, the size of the drawable QXL framebuffer can also be read
>>> from a
>>> register in the QXL ROM BAR (PCI BAR 2), so let's retrieve it from
>>> there.
>>>
>>> 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 <lersek@redhat.com>
>>
>> Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
>
> can you please apply this?
>
> Thanks,
> Laszlo
>
>
> ------------------------------------------------------------------------------
> Slashdot TV.
> Video for Nerds.  Stuff that matters.
> http://tv.slashdot.org/
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/edk2-devel

------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
diff mbox

Patch

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..17bd4cc 100644
--- a/OvmfPkg/QemuVideoDxe/Driver.c
+++ b/OvmfPkg/QemuVideoDxe/Driver.c
@@ -173,6 +173,7 @@  QemuVideoControllerDriverStart (
   EFI_TPL                           OldTpl;
   EFI_STATUS                        Status;
   QEMU_VIDEO_PRIVATE_DATA           *Private;
+  BOOLEAN                           IsQxl;
   EFI_DEVICE_PATH_PROTOCOL          *ParentDevicePath;
   ACPI_ADR_DEVICE_PATH              AcpiDeviceNode;
   PCI_TYPE00                        Pci;
@@ -235,6 +236,12 @@  QemuVideoControllerDriverStart (
   Private->Variant = Card->Variant;
 
   //
+  // IsQxl is based on the detected Card->Variant, which at a later point might
+  // not match Private->Variant.
+  //
+  IsQxl = (BOOLEAN)(Card->Variant == QEMU_VIDEO_BOCHS);
+
+  //
   // Save original PCI attributes
   //
   Status = Private->PciIo->Attributes (
@@ -354,7 +361,7 @@  QemuVideoControllerDriverStart (
     break;
   case QEMU_VIDEO_BOCHS_MMIO:
   case QEMU_VIDEO_BOCHS:
-    Status = QemuVideoBochsModeSetup (Private);
+    Status = QemuVideoBochsModeSetup (Private, IsQxl);
     break;
   default:
     ASSERT (FALSE);
diff --git a/OvmfPkg/QemuVideoDxe/Initialize.c b/OvmfPkg/QemuVideoDxe/Initialize.c
index a536d47..9e0c3aa 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,48 @@  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;
+  // VBE_DISPI_INDEX_VIDEO_MEMORY_64K is expected to return the size of the
+  // drawable framebuffer. Up to and including qemu-2.1 however it used to
+  // return the size of PCI BAR 0 (ie. the full video RAM size).
+  //
+  // On stdvga the two concepts coincide with each other; the full memory size
+  // is usable for drawing.
+  //
+  // On QXL however, only a leading segment, "surface 0", can be used for
+  // drawing; the rest of the video memory is used for the QXL guest-host
+  // protocol. VBE_DISPI_INDEX_VIDEO_MEMORY_64K should report the size of
+  // "surface 0", but since it doesn't (up to and including qemu-2.1), we
+  // retrieve the size of the drawable portion from a field in the QXL ROM BAR,
+  // where it is also available.
+  //
+  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));