diff mbox

[08/10] qxl: add vgamem_size_mb and vgamem_size

Message ID 1339487474-8481-9-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann June 12, 2012, 7:51 a.m. UTC
From: Alon Levy <alevy@redhat.com>

In preperation for supporting a larger framebuffer for multiple monitors
on a single card, add a property to qxl vgamem_size_mb, and corresponding
byte sized vgamem_size, and use instead of VGA_RAM_SIZE.

[ kraxel: simplify property handling, add sanity checks ]
[ kraxel: fix mode copying ]

Signed-off-by: Alon Levy <alevy@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/qxl.c |   72 ++++++++++++++++++++++++++++++++++++-------------------------
 hw/qxl.h |    2 +
 2 files changed, 44 insertions(+), 30 deletions(-)

Comments

Daniel P. Berrangé June 12, 2012, 9:37 a.m. UTC | #1
On Tue, Jun 12, 2012 at 09:51:12AM +0200, Gerd Hoffmann wrote:
> From: Alon Levy <alevy@redhat.com>
> 
> In preperation for supporting a larger framebuffer for multiple monitors
> on a single card, add a property to qxl vgamem_size_mb, and corresponding
> byte sized vgamem_size, and use instead of VGA_RAM_SIZE.

How is this new property different from the existing 'vram_size' property
we can set for QXL ?

>          DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
>          DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
>          DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
> +        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 8),
>          DEFINE_PROP_END_OF_LIST(),

Or indeed, can you describe the semantics of each of the different
ram size parameters here ?

Daniel
Gerd Hoffmann June 12, 2012, 11:55 a.m. UTC | #2
On 06/12/12 11:37, Daniel P. Berrange wrote:
> On Tue, Jun 12, 2012 at 09:51:12AM +0200, Gerd Hoffmann wrote:
>> From: Alon Levy <alevy@redhat.com>
>>
>> In preperation for supporting a larger framebuffer for multiple monitors
>> on a single card, add a property to qxl vgamem_size_mb, and corresponding
>> byte sized vgamem_size, and use instead of VGA_RAM_SIZE.
> 
> How is this new property different from the existing 'vram_size' property
> we can set for QXL ?
> 
>>          DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
>>          DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
>>          DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
>> +        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 8),
>>          DEFINE_PROP_END_OF_LIST(),
> 
> Or indeed, can you describe the semantics of each of the different
> ram size parameters here ?

Documenting this being a good idea just came across my mind these days
too.  Ok here we go (should probably cut+paste to docs/qxl.txt):

The qxl device has two large memory regions:

Region #1 is called "ram" and is mapped to PCI bar 0.  This is again
splitted into three parts:  The framebuffer at the start, the command
rings at the end, and storage area for spice rendering commands and
image data inbetween.

Region #2 is called "vram".  This is storage for images, called
"surfaces" in spice.  Surfaces can be both source and target for spice
rendering operations.  X11 can store offscreen pixmaps there for
example.  Once qxl gets 3D support surfaces can also be used for textures.

Now for the properties:

vgamem_mb
  specifies the size of the framebuffer portion of the "ram" region, in
  megabytes.  Must be big enougth to hold the maximum display
  resolution you want to use.  Replaces the fixed VGA_RAM_SIZE define.
  Default is 8 or 16 MB depending on machine type with all patches of
  this series applied (see last patch).

ram_size_mb
  specifies the total size of the "ram" region, in megabytes.  Defaults
  to 64 MB.  Must be larger than vgamem_mb obviously.

vram_size_mb
  specifies the total size of the "vram" region, in megabytes.
  Defaults to 64 MB.

vram64_size_mb
  if this one is present and larger than vram_size_mb qxl will get an
  additional 64bit pci bar.  Both 32bit and 64bit vram pci bars are
  backed by the "vram" memory region, the 32bit bar is an alias mapping
  for the first part of the 64bit pci bar.  This can be used to give
  guests *lots* of vram without exhausting 32bit pci address space.
  Obviously only useful for 64bit guests.  Requires cutting edge
  seabios to get the 64bit bar actually mapped above 4G.

ram_size
  specifies the total size of the "ram" region, in bytes.  For
  compatibility with older qemu versions, ignored if ram_size_mb
  property is present.

vram_size
  specifies the total size of the "vram" region, in bytes.  For
  compatibility with older qemu versions, ignored if vram_size_mb
  property is present.

cheers,
  Gerd
Alon Levy June 13, 2012, 11:48 a.m. UTC | #3
On Tue, Jun 12, 2012 at 01:55:23PM +0200, Gerd Hoffmann wrote:
> On 06/12/12 11:37, Daniel P. Berrange wrote:
> > On Tue, Jun 12, 2012 at 09:51:12AM +0200, Gerd Hoffmann wrote:
> >> From: Alon Levy <alevy@redhat.com>
> >>
> >> In preperation for supporting a larger framebuffer for multiple monitors
> >> on a single card, add a property to qxl vgamem_size_mb, and corresponding
> >> byte sized vgamem_size, and use instead of VGA_RAM_SIZE.
> > 
> > How is this new property different from the existing 'vram_size' property
> > we can set for QXL ?
> > 
> >>          DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
> >>          DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
> >>          DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
> >> +        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 8),
> >>          DEFINE_PROP_END_OF_LIST(),
> > 
> > Or indeed, can you describe the semantics of each of the different
> > ram size parameters here ?
> 
> Documenting this being a good idea just came across my mind these days
> too.  Ok here we go (should probably cut+paste to docs/qxl.txt):

Great idea!

> 
> The qxl device has two large memory regions:
> 
> Region #1 is called "ram" and is mapped to PCI bar 0.  This is again
> splitted into three parts:  The framebuffer at the start, the command
> rings at the end, and storage area for spice rendering commands and
> image data inbetween.
> 
> Region #2 is called "vram".  This is storage for images, called
> "surfaces" in spice.  Surfaces can be both source and target for spice
> rendering operations.  X11 can store offscreen pixmaps there for
> example.  Once qxl gets 3D support surfaces can also be used for textures.
> 
> Now for the properties:
> 
> vgamem_mb
>   specifies the size of the framebuffer portion of the "ram" region, in
>   megabytes.  Must be big enougth to hold the maximum display
s/enougth/enough/

>   resolution you want to use.  Replaces the fixed VGA_RAM_SIZE define.
>   Default is 8 or 16 MB depending on machine type with all patches of
>   this series applied (see last patch).
> 
> ram_size_mb
>   specifies the total size of the "ram" region, in megabytes.  Defaults
>   to 64 MB.  Must be larger than vgamem_mb obviously.
> 
> vram_size_mb
>   specifies the total size of the "vram" region, in megabytes.
>   Defaults to 64 MB.
> 
> vram64_size_mb
>   if this one is present and larger than vram_size_mb qxl will get an
>   additional 64bit pci bar.  Both 32bit and 64bit vram pci bars are
>   backed by the "vram" memory region, the 32bit bar is an alias mapping
>   for the first part of the 64bit pci bar.  This can be used to give
>   guests *lots* of vram without exhausting 32bit pci address space.
>   Obviously only useful for 64bit guests.  Requires cutting edge
>   seabios to get the 64bit bar actually mapped above 4G.
> 
> ram_size
>   specifies the total size of the "ram" region, in bytes.  For
>   compatibility with older qemu versions, ignored if ram_size_mb
>   property is present.
> 
> vram_size
>   specifies the total size of the "vram" region, in bytes.  For
>   compatibility with older qemu versions, ignored if vram_size_mb
>   property is present.
> 
> cheers,
>   Gerd
>
Alon Levy June 14, 2012, 1:44 p.m. UTC | #4
On Tue, Jun 12, 2012 at 09:51:12AM +0200, Gerd Hoffmann wrote:
> From: Alon Levy <alevy@redhat.com>
> 

Please don't push this, it breaks both drivers, leaving n_modes 0 in the
rom. I'll send a fixed version.

> In preperation for supporting a larger framebuffer for multiple monitors
> on a single card, add a property to qxl vgamem_size_mb, and corresponding
> byte sized vgamem_size, and use instead of VGA_RAM_SIZE.
> 
> [ kraxel: simplify property handling, add sanity checks ]
> [ kraxel: fix mode copying ]
> 
> Signed-off-by: Alon Levy <alevy@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  hw/qxl.c |   72 ++++++++++++++++++++++++++++++++++++-------------------------
>  hw/qxl.h |    2 +
>  2 files changed, 44 insertions(+), 30 deletions(-)
> 
> diff --git a/hw/qxl.c b/hw/qxl.c
> index c40cf55..c9028dd 100644
> --- a/hw/qxl.c
> +++ b/hw/qxl.c
> @@ -27,8 +27,6 @@
>  
>  #include "qxl.h"
>  
> -#define VGA_RAM_SIZE (8192 * 1024)
> -
>  /*
>   * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
>   * such can be changed by the guest, so to avoid a guest trigerrable
> @@ -116,20 +114,16 @@ static QXLMode qxl_modes[] = {
>      QXL_MODE_EX(1600, 1200),
>      QXL_MODE_EX(1680, 1050),
>      QXL_MODE_EX(1920, 1080),
> -#if VGA_RAM_SIZE >= (16 * 1024 * 1024)
>      /* these modes need more than 8 MB video memory */
>      QXL_MODE_EX(1920, 1200),
>      QXL_MODE_EX(1920, 1440),
>      QXL_MODE_EX(2048, 1536),
>      QXL_MODE_EX(2560, 1440),
>      QXL_MODE_EX(2560, 1600),
> -#endif
> -#if VGA_RAM_SIZE >= (32 * 1024 * 1024)
>      /* these modes need more than 16 MB video memory */
>      QXL_MODE_EX(2560, 2048),
>      QXL_MODE_EX(2800, 2100),
>      QXL_MODE_EX(3200, 2400),
> -#endif
>  };
>  
>  static PCIQXLDevice *qxl0;
> @@ -286,6 +280,7 @@ static inline uint32_t msb_mask(uint32_t val)
>  static ram_addr_t qxl_rom_size(void)
>  {
>      uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
> +
>      rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
>      rom_size = msb_mask(rom_size * 2 - 1);
>      return rom_size;
> @@ -298,8 +293,8 @@ static void init_qxl_rom(PCIQXLDevice *d)
>      uint32_t ram_header_size;
>      uint32_t surface0_area_size;
>      uint32_t num_pages;
> -    uint32_t fb, maxfb = 0;
> -    int i;
> +    uint32_t fb;
> +    int i, n;
>  
>      memset(rom, 0, d->rom_size);
>  
> @@ -314,26 +309,25 @@ static void init_qxl_rom(PCIQXLDevice *d)
>      rom->slots_end     = NUM_MEMSLOTS - 1;
>      rom->n_surfaces    = cpu_to_le32(NUM_SURFACES);
>  
> -    modes->n_modes     = cpu_to_le32(ARRAY_SIZE(qxl_modes));
> -    for (i = 0; i < modes->n_modes; i++) {
> +    for (i = 0, n = 0; i < modes->n_modes; i++) {
>          fb = qxl_modes[i].y_res * qxl_modes[i].stride;
> -        if (maxfb < fb) {
> -            maxfb = fb;
> +        if (fb > d->vgamem_size) {
> +            continue;
>          }
> -        modes->modes[i].id          = cpu_to_le32(i);
> -        modes->modes[i].x_res       = cpu_to_le32(qxl_modes[i].x_res);
> -        modes->modes[i].y_res       = cpu_to_le32(qxl_modes[i].y_res);
> -        modes->modes[i].bits        = cpu_to_le32(qxl_modes[i].bits);
> -        modes->modes[i].stride      = cpu_to_le32(qxl_modes[i].stride);
> -        modes->modes[i].x_mili      = cpu_to_le32(qxl_modes[i].x_mili);
> -        modes->modes[i].y_mili      = cpu_to_le32(qxl_modes[i].y_mili);
> -        modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation);
> -    }
> -    if (maxfb < VGA_RAM_SIZE && d->id == 0)
> -        maxfb = VGA_RAM_SIZE;
> +        modes->modes[n].id          = cpu_to_le32(i);
> +        modes->modes[n].x_res       = cpu_to_le32(qxl_modes[i].x_res);
> +        modes->modes[n].y_res       = cpu_to_le32(qxl_modes[i].y_res);
> +        modes->modes[n].bits        = cpu_to_le32(qxl_modes[i].bits);
> +        modes->modes[n].stride      = cpu_to_le32(qxl_modes[i].stride);
> +        modes->modes[n].x_mili      = cpu_to_le32(qxl_modes[i].x_mili);
> +        modes->modes[n].y_mili      = cpu_to_le32(qxl_modes[i].y_mili);
> +        modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation);
> +        n++;
> +    }
> +    modes->n_modes     = cpu_to_le32(n);
>  
>      ram_header_size    = ALIGN(sizeof(QXLRam), 4096);
> -    surface0_area_size = ALIGN(maxfb, 4096);
> +    surface0_area_size = ALIGN(d->vgamem_size, 4096);
>      num_pages          = d->vga.vram_size;
>      num_pages         -= ram_header_size;
>      num_pages         -= surface0_area_size;
> @@ -1205,6 +1199,16 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
>  {
>      QXLDevSurfaceCreate surface;
>      QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
> +    int size;
> +    int requested_height = le32_to_cpu(sc->height);
> +    int requested_stride = le32_to_cpu(sc->stride);
> +
> +    size = abs(requested_stride) * requested_height;
> +    if (size > qxl->vgamem_size) {
> +        qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer"
> +                               " size", __func__);
> +        return;
> +    }
>  
>      if (qxl->mode == QXL_MODE_NATIVE) {
>          qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
> @@ -1714,14 +1718,20 @@ static DisplayChangeListener display_listener = {
>      .dpy_refresh = display_refresh,
>  };
>  
> -static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
> +static void qxl_init_ramsize(PCIQXLDevice *qxl)
>  {
> -    /* vga ram (bar 0) */
> +    /* vga mode framebuffer / primary surface (bar 0, first part) */
> +    if (qxl->vgamem_size_mb < 8) {
> +        qxl->vgamem_size_mb = 8;
> +    }
> +    qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024;
> +
> +    /* vga ram (bar 0, total) */
>      if (qxl->ram_size_mb != -1) {
>          qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
>      }
> -    if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) {
> -        qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
> +    if (qxl->vga.vram_size < qxl->vgamem_size * 2) {
> +        qxl->vga.vram_size = qxl->vgamem_size * 2;
>      }
>  
>      /* vram32 (surfaces, 32bit, bar 1) */
> @@ -1744,6 +1754,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
>          qxl->vram32_size = 4096;
>          qxl->vram_size = 4096;
>      }
> +    qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1);
>      qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
>      qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
>      qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
> @@ -1855,7 +1866,7 @@ static int qxl_init_primary(PCIDevice *dev)
>      PortioList *qxl_vga_port_list = g_new(PortioList, 1);
>  
>      qxl->id = 0;
> -    qxl_init_ramsize(qxl, 32);
> +    qxl_init_ramsize(qxl);
>      vga->vram_size_mb = qxl->vga.vram_size >> 20;
>      vga_common_init(vga);
>      vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
> @@ -1878,7 +1889,7 @@ static int qxl_init_secondary(PCIDevice *dev)
>      PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
>  
>      qxl->id = device_id++;
> -    qxl_init_ramsize(qxl, 16);
> +    qxl_init_ramsize(qxl);
>      memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
>      vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
>      qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
> @@ -2056,6 +2067,7 @@ static Property qxl_properties[] = {
>          DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
>          DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
>          DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
> +        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 8),
>          DEFINE_PROP_END_OF_LIST(),
>  };
>  
> diff --git a/hw/qxl.h b/hw/qxl.h
> index a4ab7cc..172baf6 100644
> --- a/hw/qxl.h
> +++ b/hw/qxl.h
> @@ -84,6 +84,7 @@ typedef struct PCIQXLDevice {
>      QXLReleaseInfo     *last_release;
>      uint32_t           last_release_offset;
>      uint32_t           oom_running;
> +    uint32_t           vgamem_size;
>  
>      /* rom pci bar */
>      QXLRom             shadow_rom;
> @@ -105,6 +106,7 @@ typedef struct PCIQXLDevice {
>      uint32_t          ram_size_mb;
>      uint32_t          vram_size_mb;
>      uint32_t          vram32_size_mb;
> +    uint32_t          vgamem_size_mb;
>  
>      /* qxl_render_update state */
>      int                render_update_cookie_num;
> -- 
> 1.7.1
> 
>
diff mbox

Patch

diff --git a/hw/qxl.c b/hw/qxl.c
index c40cf55..c9028dd 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -27,8 +27,6 @@ 
 
 #include "qxl.h"
 
-#define VGA_RAM_SIZE (8192 * 1024)
-
 /*
  * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
  * such can be changed by the guest, so to avoid a guest trigerrable
@@ -116,20 +114,16 @@  static QXLMode qxl_modes[] = {
     QXL_MODE_EX(1600, 1200),
     QXL_MODE_EX(1680, 1050),
     QXL_MODE_EX(1920, 1080),
-#if VGA_RAM_SIZE >= (16 * 1024 * 1024)
     /* these modes need more than 8 MB video memory */
     QXL_MODE_EX(1920, 1200),
     QXL_MODE_EX(1920, 1440),
     QXL_MODE_EX(2048, 1536),
     QXL_MODE_EX(2560, 1440),
     QXL_MODE_EX(2560, 1600),
-#endif
-#if VGA_RAM_SIZE >= (32 * 1024 * 1024)
     /* these modes need more than 16 MB video memory */
     QXL_MODE_EX(2560, 2048),
     QXL_MODE_EX(2800, 2100),
     QXL_MODE_EX(3200, 2400),
-#endif
 };
 
 static PCIQXLDevice *qxl0;
@@ -286,6 +280,7 @@  static inline uint32_t msb_mask(uint32_t val)
 static ram_addr_t qxl_rom_size(void)
 {
     uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
+
     rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
     rom_size = msb_mask(rom_size * 2 - 1);
     return rom_size;
@@ -298,8 +293,8 @@  static void init_qxl_rom(PCIQXLDevice *d)
     uint32_t ram_header_size;
     uint32_t surface0_area_size;
     uint32_t num_pages;
-    uint32_t fb, maxfb = 0;
-    int i;
+    uint32_t fb;
+    int i, n;
 
     memset(rom, 0, d->rom_size);
 
@@ -314,26 +309,25 @@  static void init_qxl_rom(PCIQXLDevice *d)
     rom->slots_end     = NUM_MEMSLOTS - 1;
     rom->n_surfaces    = cpu_to_le32(NUM_SURFACES);
 
-    modes->n_modes     = cpu_to_le32(ARRAY_SIZE(qxl_modes));
-    for (i = 0; i < modes->n_modes; i++) {
+    for (i = 0, n = 0; i < modes->n_modes; i++) {
         fb = qxl_modes[i].y_res * qxl_modes[i].stride;
-        if (maxfb < fb) {
-            maxfb = fb;
+        if (fb > d->vgamem_size) {
+            continue;
         }
-        modes->modes[i].id          = cpu_to_le32(i);
-        modes->modes[i].x_res       = cpu_to_le32(qxl_modes[i].x_res);
-        modes->modes[i].y_res       = cpu_to_le32(qxl_modes[i].y_res);
-        modes->modes[i].bits        = cpu_to_le32(qxl_modes[i].bits);
-        modes->modes[i].stride      = cpu_to_le32(qxl_modes[i].stride);
-        modes->modes[i].x_mili      = cpu_to_le32(qxl_modes[i].x_mili);
-        modes->modes[i].y_mili      = cpu_to_le32(qxl_modes[i].y_mili);
-        modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation);
-    }
-    if (maxfb < VGA_RAM_SIZE && d->id == 0)
-        maxfb = VGA_RAM_SIZE;
+        modes->modes[n].id          = cpu_to_le32(i);
+        modes->modes[n].x_res       = cpu_to_le32(qxl_modes[i].x_res);
+        modes->modes[n].y_res       = cpu_to_le32(qxl_modes[i].y_res);
+        modes->modes[n].bits        = cpu_to_le32(qxl_modes[i].bits);
+        modes->modes[n].stride      = cpu_to_le32(qxl_modes[i].stride);
+        modes->modes[n].x_mili      = cpu_to_le32(qxl_modes[i].x_mili);
+        modes->modes[n].y_mili      = cpu_to_le32(qxl_modes[i].y_mili);
+        modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation);
+        n++;
+    }
+    modes->n_modes     = cpu_to_le32(n);
 
     ram_header_size    = ALIGN(sizeof(QXLRam), 4096);
-    surface0_area_size = ALIGN(maxfb, 4096);
+    surface0_area_size = ALIGN(d->vgamem_size, 4096);
     num_pages          = d->vga.vram_size;
     num_pages         -= ram_header_size;
     num_pages         -= surface0_area_size;
@@ -1205,6 +1199,16 @@  static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
 {
     QXLDevSurfaceCreate surface;
     QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
+    int size;
+    int requested_height = le32_to_cpu(sc->height);
+    int requested_stride = le32_to_cpu(sc->stride);
+
+    size = abs(requested_stride) * requested_height;
+    if (size > qxl->vgamem_size) {
+        qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer"
+                               " size", __func__);
+        return;
+    }
 
     if (qxl->mode == QXL_MODE_NATIVE) {
         qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
@@ -1714,14 +1718,20 @@  static DisplayChangeListener display_listener = {
     .dpy_refresh = display_refresh,
 };
 
-static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
+static void qxl_init_ramsize(PCIQXLDevice *qxl)
 {
-    /* vga ram (bar 0) */
+    /* vga mode framebuffer / primary surface (bar 0, first part) */
+    if (qxl->vgamem_size_mb < 8) {
+        qxl->vgamem_size_mb = 8;
+    }
+    qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024;
+
+    /* vga ram (bar 0, total) */
     if (qxl->ram_size_mb != -1) {
         qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
     }
-    if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) {
-        qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
+    if (qxl->vga.vram_size < qxl->vgamem_size * 2) {
+        qxl->vga.vram_size = qxl->vgamem_size * 2;
     }
 
     /* vram32 (surfaces, 32bit, bar 1) */
@@ -1744,6 +1754,7 @@  static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
         qxl->vram32_size = 4096;
         qxl->vram_size = 4096;
     }
+    qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1);
     qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
     qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
     qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
@@ -1855,7 +1866,7 @@  static int qxl_init_primary(PCIDevice *dev)
     PortioList *qxl_vga_port_list = g_new(PortioList, 1);
 
     qxl->id = 0;
-    qxl_init_ramsize(qxl, 32);
+    qxl_init_ramsize(qxl);
     vga->vram_size_mb = qxl->vga.vram_size >> 20;
     vga_common_init(vga);
     vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
@@ -1878,7 +1889,7 @@  static int qxl_init_secondary(PCIDevice *dev)
     PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
 
     qxl->id = device_id++;
-    qxl_init_ramsize(qxl, 16);
+    qxl_init_ramsize(qxl);
     memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
     vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
     qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
@@ -2056,6 +2067,7 @@  static Property qxl_properties[] = {
         DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
         DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
         DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
+        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 8),
         DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/qxl.h b/hw/qxl.h
index a4ab7cc..172baf6 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -84,6 +84,7 @@  typedef struct PCIQXLDevice {
     QXLReleaseInfo     *last_release;
     uint32_t           last_release_offset;
     uint32_t           oom_running;
+    uint32_t           vgamem_size;
 
     /* rom pci bar */
     QXLRom             shadow_rom;
@@ -105,6 +106,7 @@  typedef struct PCIQXLDevice {
     uint32_t          ram_size_mb;
     uint32_t          vram_size_mb;
     uint32_t          vram32_size_mb;
+    uint32_t          vgamem_size_mb;
 
     /* qxl_render_update state */
     int                render_update_cookie_num;