RHBZ 869981 Before this patch revision < 4 (4 is the default) would result in a wrong qxl_rom size of 16384 instead of 8192 when building with spice-protocol-0.12, due to the addition of fields in the rom for client capabilities and monitors config that were added between spice-protocol 0.10 and 0.12.
The solution is a bit involved, since I decided not to change QXLRom which is defined externally in spice-protocol. Instead for revision < 4 we allocate 72 bytes for the QXLRom on the qxl_rom bar (bytes [0,71]) and make sure no fields out of that range are accessed, via checking of the revision and nop-ing. Signed-off-by: Alon Levy <al...@redhat.com> --- hw/qxl.c | 37 ++++++++++++++++++++++++++++++++----- hw/qxl.h | 2 ++ trace-events | 2 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 3f835b8..4794f13 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -314,10 +314,26 @@ static inline uint32_t msb_mask(uint32_t val) return mask; } -static ram_addr_t qxl_rom_size(void) +static ram_addr_t init_qxl_rom_size(PCIQXLDevice *qxl) { - uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes); + uint32_t rom_size; + switch (qxl->revision) { + case 1: + case 2: + case 3: + /* rom_size ends up in [4096, 8192), so it fits all revisions <= 3 */ + qxl->qxl_rom_size = 72; + break; + case 4: + /* rom_size ends up >= 8192 for spice-protocol >= 12.1 because of added + * client capabilities */ + qxl->qxl_rom_size = sizeof(QXLRom); + break; + default: + abort(); + } + rom_size = qxl->qxl_rom_size + sizeof(QXLModes) + sizeof(qxl_modes); rom_size = MAX(rom_size, TARGET_PAGE_SIZE); rom_size = msb_mask(rom_size * 2 - 1); return rom_size; @@ -326,7 +342,7 @@ static ram_addr_t qxl_rom_size(void) static void init_qxl_rom(PCIQXLDevice *d) { QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar); - QXLModes *modes = (QXLModes *)(rom + 1); + QXLModes *modes = (QXLModes *)((void *)rom + d->qxl_rom_size); uint32_t ram_header_size; uint32_t surface0_area_size; uint32_t num_pages; @@ -338,7 +354,7 @@ static void init_qxl_rom(PCIQXLDevice *d) rom->magic = cpu_to_le32(QXL_ROM_MAGIC); rom->id = cpu_to_le32(d->id); rom->log_level = cpu_to_le32(d->guestdebug); - rom->modes_offset = cpu_to_le32(sizeof(QXLRom)); + rom->modes_offset = cpu_to_le32(d->qxl_rom_size); rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; rom->slot_id_bits = MEMSLOT_SLOT_BITS; @@ -981,6 +997,12 @@ static void interface_set_client_capabilities(QXLInstance *sin, { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + if (qxl->revision < 4) { + trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id, + qxl->revision); + return; + } + if (runstate_check(RUN_STATE_INMIGRATE) || runstate_check(RUN_STATE_POSTMIGRATE)) { return; @@ -1013,6 +1035,11 @@ static int interface_client_monitors_config(QXLInstance *sin, QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar); int i; + if (qxl->revision < 4) { + trace_qxl_client_monitors_config_unsupported_by_device(qxl->id, + qxl->revision); + return 0; + } /* * Older windows drivers set int_mask to 0 when their ISR is called, * then later set it to ~0. So it doesn't relate to the actual interrupts @@ -2031,7 +2058,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); - qxl->rom_size = qxl_rom_size(); + qxl->rom_size = init_qxl_rom_size(qxl); memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size); vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev); init_qxl_rom(qxl); diff --git a/hw/qxl.h b/hw/qxl.h index b3564fb..c9dee70 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -92,6 +92,8 @@ typedef struct PCIQXLDevice { QXLRom shadow_rom; QXLRom *rom; QXLModes *modes; + uint32_t qxl_rom_size; /* size allocated for QXLRom, + <= sizeof(QXLRom) */ uint32_t rom_size; MemoryRegion rom_bar; diff --git a/trace-events b/trace-events index 6c6cbf1..7d9d62d 100644 --- a/trace-events +++ b/trace-events @@ -1006,8 +1006,10 @@ qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d" qxl_set_guest_bug(int qid) "%d" qxl_interrupt_client_monitors_config(int qid, int num_heads, void *heads) "%d %d %p" qxl_client_monitors_config_unsupported_by_guest(int qid, uint32_t int_mask, void *client_monitors_config) "%d %X %p" +qxl_client_monitors_config_unsupported_by_device(int qid, int revision) "%d revision=%d" qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d" qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u" +qxl_set_client_capabilities_unsupported_by_revision(int qid, int revision) "%d revision=%d" # hw/qxl-render.c qxl_render_blit_guest_primary_initialized(void) "" -- 1.8.0.1