aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2023-11-07 09:41:33 +0800
committerStefan Hajnoczi <stefanha@redhat.com>2023-11-07 09:41:34 +0800
commit9f33cf2a89b5f8b437f3c62158c9ac2aa6ba9d48 (patch)
tree593c31d74fab229910dead8aba3641c77a88ce30 /hw
parent54e97162db562cd6215be77ab1aeb344e01ab3df (diff)
parent10b9ddbc83b94986cbdf989e26fb7269fb2e9f72 (diff)
downloadqemu-9f33cf2a89b5f8b437f3c62158c9ac2aa6ba9d48.zip
qemu-9f33cf2a89b5f8b437f3c62158c9ac2aa6ba9d48.tar.gz
qemu-9f33cf2a89b5f8b437f3c62158c9ac2aa6ba9d48.tar.bz2
Merge tag 'gpu-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
GPU pull request Includes: - [PATCH] virtio-gpu-rutabaga: Add empty interface to fix arm64 crash - [PATCH v2 0/4] Misc ati-vga patches - [PATCH v2 0/5] virtio-gpu: add blob migration support # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmVI6rYcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5VdjEAC6xsnITkX5FgJVjDo2 # XQWGz2MgWIFcqMXYVbz21UgY86KjCE7jVbGL6BkWO8N/XzsP6EUEIcUcHm7KcdwU # +csMTjgnZUb97Ov/d27Ge9UYr/9rgozyoQRwvvQYXRFfxTur+b+poodcAOn3Ml3Q # vXBTYb0z2FYd85VcVx59w5lFlonER1z4wnQuF4GLCBNIW/GX+S87+xyv1FZCgWku # luBhWa7ihVrlrYA2UaoQS7sI2ch/wI9NnfP1p+31Iy/w1wLwQOj9ofTslbehTcvS # uDU/+WMkhmlB8LOl7mx8bGHeBab9iUvSz5H5MIfTgrk6hRaaMJp3y2lefsc9cWuA # 5aPvj0qouTEkIZ+2aPClcZk11mIbH5lT9eeEYc16Ztrt5VJHXmR7RwF74hHQbxY3 # LDiRlpvhBfX4muaPw4L1max9kJZdOvo9aQVqKL7hkrTBL/xBmvg40ZRrWDrIvZZg # 8TZ8woD2ORiic/hTxmI7waG+f3WozH/drStJrOFpJA7+iFefrHgkv2K7ze9LhWmZ # ookM8K96NV6AnPQ39N2nuvQ/Fndqv7Kg4AiOwzj3epfg/rDscaJw9J2JQuCGEiEF # KEIt3hyIo/HOXXiwmGqP3BWs90EmMvZTDmAt5e/cT0eQ1jWwJ3Tj8ShVg2S8QW0h # TMXdPV7bepd49aaynpGFeADhfw== # =k/gN # -----END PGP SIGNATURE----- # gpg: Signature made Mon 06 Nov 2023 21:31:34 HKT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * tag 'gpu-pull-request' of https://gitlab.com/marcandre.lureau/qemu: Revert "virtio-gpu: block migration of VMs with blob=true" virtio-gpu: add virtio-gpu/blob vmstate subsection virtio-gpu: move scanout restoration to post_load virtio-gpu: factor out restore mapping virtio-gpu: block migration of VMs with blob=true ati-vga: Implement fallback for pixman routines ati-vga: Add 30 bit palette access register ati-vga: Support unaligned access to GPIO DDC registers ati-vga: Fix aperture sizes virtio-gpu-rutabaga: Add empty interface to fix arm64 crash Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/display/ati.c61
-rw-r--r--hw/display/ati_2d.c75
-rw-r--r--hw/display/ati_dbg.c2
-rw-r--r--hw/display/ati_int.h2
-rw-r--r--hw/display/ati_regs.h2
-rw-r--r--hw/display/virtio-gpu-pci-rutabaga.c1
-rw-r--r--hw/display/virtio-gpu.c174
7 files changed, 250 insertions, 67 deletions
diff --git a/hw/display/ati.c b/hw/display/ati.c
index 6e38e00..9a87a55 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -319,11 +319,13 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
case DAC_CNTL:
val = s->regs.dac_cntl;
break;
- case GPIO_VGA_DDC:
- val = s->regs.gpio_vga_ddc;
+ case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3:
+ val = ati_reg_read_offs(s->regs.gpio_vga_ddc,
+ addr - GPIO_VGA_DDC, size);
break;
- case GPIO_DVI_DDC:
- val = s->regs.gpio_dvi_ddc;
+ case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3:
+ val = ati_reg_read_offs(s->regs.gpio_dvi_ddc,
+ addr - GPIO_DVI_DDC, size);
break;
case GPIO_MONID ... GPIO_MONID + 3:
val = ati_reg_read_offs(s->regs.gpio_monid,
@@ -337,6 +339,9 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
case PALETTE_DATA:
val = vga_ioport_read(&s->vga, VGA_PEL_D);
break;
+ case PALETTE_30_DATA:
+ val = s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IR)];
+ break;
case CNFG_CNTL:
val = s->regs.config_cntl;
break;
@@ -349,14 +354,17 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
PCI_BASE_ADDRESS_0, size) & 0xfffffff0;
break;
case CONFIG_APER_SIZE:
- val = s->vga.vram_size;
+ val = s->vga.vram_size / 2;
break;
case CONFIG_REG_1_BASE:
val = pci_default_read_config(&s->dev,
PCI_BASE_ADDRESS_2, size) & 0xfffffff0;
break;
case CONFIG_REG_APER_SIZE:
- val = memory_region_size(&s->mm);
+ val = memory_region_size(&s->mm) / 2;
+ break;
+ case HOST_PATH_CNTL:
+ val = BIT(23); /* Radeon HDP_APER_CNTL */
break;
case MC_STATUS:
val = 5;
@@ -612,29 +620,34 @@ static void ati_mm_write(void *opaque, hwaddr addr,
s->regs.dac_cntl = data & 0xffffe3ff;
s->vga.dac_8bit = !!(data & DAC_8BIT_EN);
break;
- case GPIO_VGA_DDC:
+ /*
+ * GPIO regs for DDC access. Because some drivers access these via
+ * multiple byte writes we have to be careful when we send bits to
+ * avoid spurious changes in bitbang_i2c state. Only do it when either
+ * the enable bits are changed or output bits changed while enabled.
+ */
+ case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3:
if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
/* FIXME: Maybe add a property to select VGA or DVI port? */
}
break;
- case GPIO_DVI_DDC:
+ case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3:
if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
- s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c, data, 0);
+ ati_reg_write_offs(&s->regs.gpio_dvi_ddc,
+ addr - GPIO_DVI_DDC, data, size);
+ if ((addr <= GPIO_DVI_DDC + 2 && addr + size > GPIO_DVI_DDC + 2) ||
+ (addr == GPIO_DVI_DDC && (s->regs.gpio_dvi_ddc & 0x30000))) {
+ s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c,
+ s->regs.gpio_dvi_ddc, 0);
+ }
}
break;
case GPIO_MONID ... GPIO_MONID + 3:
/* FIXME What does Radeon have here? */
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ /* Rage128p accesses DDC via MONID(1-2) with additional mask bit */
ati_reg_write_offs(&s->regs.gpio_monid,
addr - GPIO_MONID, data, size);
- /*
- * Rage128p accesses DDC used to get EDID via these bits.
- * Because some drivers access this via multiple byte writes
- * we have to be careful when we send bits to avoid spurious
- * changes in bitbang_i2c state. So only do it when mask is set
- * and either the enable bits are changed or output bits changed
- * while enabled.
- */
if ((s->regs.gpio_monid & BIT(25)) &&
((addr <= GPIO_MONID + 2 && addr + size > GPIO_MONID + 2) ||
(addr == GPIO_MONID && (s->regs.gpio_monid & 0x60000)))) {
@@ -663,6 +676,12 @@ static void ati_mm_write(void *opaque, hwaddr addr,
data >>= 8;
vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff);
break;
+ case PALETTE_30_DATA:
+ s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IW)] = data;
+ vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 22) & 0xff);
+ vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 12) & 0xff);
+ vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 2) & 0xff);
+ break;
case CNFG_CNTL:
s->regs.config_cntl = data;
break;
@@ -1014,6 +1033,7 @@ static Property ati_vga_properties[] = {
DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id,
PCI_DEVICE_ID_ATI_RAGE128_PF),
DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false),
+ DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, 3),
DEFINE_PROP_END_OF_LIST()
};
@@ -1035,11 +1055,18 @@ static void ati_vga_class_init(ObjectClass *klass, void *data)
k->exit = ati_vga_exit;
}
+static void ati_vga_init(Object *o)
+{
+ object_property_set_description(o, "x-pixman", "Use pixman for: "
+ "1: fill, 2: blit");
+}
+
static const TypeInfo ati_vga_info = {
.name = TYPE_ATI_VGA,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(ATIVGAState),
.class_init = ati_vga_class_init,
+ .instance_init = ati_vga_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index 7d78665..0e6b8e4 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -92,6 +92,7 @@ void ati_2d_blt(ATIVGAState *s)
switch (s->regs.dp_mix & GMC_ROP3_MASK) {
case ROP3_SRCCOPY:
{
+ bool fallback = false;
unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width);
unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
@@ -122,27 +123,50 @@ void ati_2d_blt(ATIVGAState *s)
src_bits, dst_bits, src_stride, dst_stride, bpp, bpp,
src_x, src_y, dst_x, dst_y,
s->regs.dst_width, s->regs.dst_height);
- if (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT &&
+ if ((s->use_pixman & BIT(1)) &&
+ s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT &&
s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) {
- pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits,
- src_stride, dst_stride, bpp, bpp,
- src_x, src_y, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height);
- } else {
+ fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits,
+ src_stride, dst_stride, bpp, bpp,
+ src_x, src_y, dst_x, dst_y,
+ s->regs.dst_width, s->regs.dst_height);
+ } else if (s->use_pixman & BIT(1)) {
/* FIXME: We only really need a temporary if src and dst overlap */
int llb = s->regs.dst_width * (bpp / 8);
int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t));
uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) *
s->regs.dst_height);
- pixman_blt((uint32_t *)src_bits, tmp,
- src_stride, tmp_stride, bpp, bpp,
- src_x, src_y, 0, 0,
- s->regs.dst_width, s->regs.dst_height);
- pixman_blt(tmp, (uint32_t *)dst_bits,
- tmp_stride, dst_stride, bpp, bpp,
- 0, 0, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height);
+ fallback = !pixman_blt((uint32_t *)src_bits, tmp,
+ src_stride, tmp_stride, bpp, bpp,
+ src_x, src_y, 0, 0,
+ s->regs.dst_width, s->regs.dst_height);
+ if (!fallback) {
+ fallback = !pixman_blt(tmp, (uint32_t *)dst_bits,
+ tmp_stride, dst_stride, bpp, bpp,
+ 0, 0, dst_x, dst_y,
+ s->regs.dst_width, s->regs.dst_height);
+ }
g_free(tmp);
+ } else {
+ fallback = true;
+ }
+ if (fallback) {
+ unsigned int y, i, j, bypp = bpp / 8;
+ unsigned int src_pitch = src_stride * sizeof(uint32_t);
+ unsigned int dst_pitch = dst_stride * sizeof(uint32_t);
+
+ for (y = 0; y < s->regs.dst_height; y++) {
+ i = dst_x * bypp;
+ j = src_x * bypp;
+ if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) {
+ i += (dst_y + y) * dst_pitch;
+ j += (src_y + y) * src_pitch;
+ } else {
+ i += (dst_y + s->regs.dst_height - 1 - y) * dst_pitch;
+ j += (src_y + s->regs.dst_height - 1 - y) * src_pitch;
+ }
+ memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp);
+ }
}
if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
@@ -180,14 +204,21 @@ void ati_2d_blt(ATIVGAState *s)
dst_stride /= sizeof(uint32_t);
DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n",
- dst_bits, dst_stride, bpp,
- dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height,
- filler);
- pixman_fill((uint32_t *)dst_bits, dst_stride, bpp,
- dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height,
- filler);
+ dst_bits, dst_stride, bpp, dst_x, dst_y,
+ s->regs.dst_width, s->regs.dst_height, filler);
+ if (!(s->use_pixman & BIT(0)) ||
+ !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y,
+ s->regs.dst_width, s->regs.dst_height, filler)) {
+ /* fallback when pixman failed or we don't want to call it */
+ unsigned int x, y, i, bypp = bpp / 8;
+ unsigned int dst_pitch = dst_stride * sizeof(uint32_t);
+ for (y = 0; y < s->regs.dst_height; y++) {
+ i = dst_x * bypp + (dst_y + y) * dst_pitch;
+ for (x = 0; x < s->regs.dst_width; x++, i += bypp) {
+ stn_he_p(&dst_bits[i], bypp, filler);
+ }
+ }
+ }
if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c
index bd0ecd4..3ffa7f3 100644
--- a/hw/display/ati_dbg.c
+++ b/hw/display/ati_dbg.c
@@ -30,6 +30,7 @@ static struct ati_regdesc ati_reg_names[] = {
{"AMCGPIO_EN_MIR", 0x00a8},
{"PALETTE_INDEX", 0x00b0},
{"PALETTE_DATA", 0x00b4},
+ {"PALETTE_30_DATA", 0x00b8},
{"CNFG_CNTL", 0x00e0},
{"GEN_RESET_CNTL", 0x00f0},
{"CNFG_MEMSIZE", 0x00f8},
@@ -38,6 +39,7 @@ static struct ati_regdesc ati_reg_names[] = {
{"CONFIG_APER_SIZE", 0x0108},
{"CONFIG_REG_1_BASE", 0x010c},
{"CONFIG_REG_APER_SIZE", 0x0110},
+ {"HOST_PATH_CNTL", 0x0130},
{"MEM_CNTL", 0x0140},
{"MC_FB_LOCATION", 0x0148},
{"MC_AGP_LOCATION", 0x014C},
diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h
index e8d3c7a..f5a47b8 100644
--- a/hw/display/ati_int.h
+++ b/hw/display/ati_int.h
@@ -44,6 +44,7 @@ typedef struct ATIVGARegs {
uint32_t gpio_dvi_ddc;
uint32_t gpio_monid;
uint32_t config_cntl;
+ uint32_t palette[256];
uint32_t crtc_h_total_disp;
uint32_t crtc_h_sync_strt_wid;
uint32_t crtc_v_total_disp;
@@ -89,6 +90,7 @@ struct ATIVGAState {
char *model;
uint16_t dev_id;
uint8_t mode;
+ uint8_t use_pixman;
bool cursor_guest_mode;
uint16_t cursor_size;
uint32_t cursor_offset;
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index d6282b2..d712774 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -48,6 +48,7 @@
#define AMCGPIO_EN_MIR 0x00a8
#define PALETTE_INDEX 0x00b0
#define PALETTE_DATA 0x00b4
+#define PALETTE_30_DATA 0x00b8
#define CNFG_CNTL 0x00e0
#define GEN_RESET_CNTL 0x00f0
#define CNFG_MEMSIZE 0x00f8
@@ -56,6 +57,7 @@
#define CONFIG_APER_SIZE 0x0108
#define CONFIG_REG_1_BASE 0x010c
#define CONFIG_REG_APER_SIZE 0x0110
+#define HOST_PATH_CNTL 0x0130
#define MEM_CNTL 0x0140
#define MC_FB_LOCATION 0x0148
#define MC_AGP_LOCATION 0x014C
diff --git a/hw/display/virtio-gpu-pci-rutabaga.c b/hw/display/virtio-gpu-pci-rutabaga.c
index c96729e..abbb898 100644
--- a/hw/display/virtio-gpu-pci-rutabaga.c
+++ b/hw/display/virtio-gpu-pci-rutabaga.c
@@ -36,6 +36,7 @@ static const TypeInfo virtio_gpu_rutabaga_pci_info[] = {
.instance_init = virtio_gpu_rutabaga_initfn,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
}
},
};
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 4265316..2707bce 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1213,6 +1213,9 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
assert(QTAILQ_EMPTY(&g->cmdq));
QTAILQ_FOREACH(res, &g->reslist, next) {
+ if (res->blob_size) {
+ continue;
+ }
qemu_put_be32(f, res->resource_id);
qemu_put_be32(f, res->width);
qemu_put_be32(f, res->height);
@@ -1230,12 +1233,40 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL);
}
+static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
+ struct virtio_gpu_simple_resource *res)
+{
+ int i;
+
+ for (i = 0; i < res->iov_cnt; i++) {
+ hwaddr len = res->iov[i].iov_len;
+ res->iov[i].iov_base =
+ dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len,
+ DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
+
+ if (!res->iov[i].iov_base || len != res->iov[i].iov_len) {
+ /* Clean up the half-a-mapping we just created... */
+ if (res->iov[i].iov_base) {
+ dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, res->iov[i].iov_base,
+ len, DMA_DIRECTION_TO_DEVICE, 0);
+ }
+ /* ...and the mappings for previous loop iterations */
+ res->iov_cnt = i;
+ virtio_gpu_cleanup_mapping(g, res);
+ return false;
+ }
+ }
+
+ QTAILQ_INSERT_HEAD(&g->reslist, res, next);
+ g->hostmem += res->hostmem;
+ return true;
+}
+
static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
const VMStateField *field)
{
VirtIOGPU *g = opaque;
struct virtio_gpu_simple_resource *res;
- struct virtio_gpu_scanout *scanout;
uint32_t resource_id, pformat;
void *bits = NULL;
int i;
@@ -1294,40 +1325,96 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
qemu_get_buffer(f, (void *)pixman_image_get_data(res->image),
pixman_image_get_stride(res->image) * res->height);
- /* restore mapping */
- for (i = 0; i < res->iov_cnt; i++) {
- hwaddr len = res->iov[i].iov_len;
- res->iov[i].iov_base =
- dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len,
- DMA_DIRECTION_TO_DEVICE,
- MEMTXATTRS_UNSPECIFIED);
-
- if (!res->iov[i].iov_base || len != res->iov[i].iov_len) {
- /* Clean up the half-a-mapping we just created... */
- if (res->iov[i].iov_base) {
- dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as,
- res->iov[i].iov_base,
- len,
- DMA_DIRECTION_TO_DEVICE,
- 0);
- }
- /* ...and the mappings for previous loop iterations */
- res->iov_cnt = i;
- virtio_gpu_cleanup_mapping(g, res);
- pixman_image_unref(res->image);
- g_free(res);
- return -EINVAL;
- }
+ if (!virtio_gpu_load_restore_mapping(g, res)) {
+ pixman_image_unref(res->image);
+ g_free(res);
+ return -EINVAL;
}
- QTAILQ_INSERT_HEAD(&g->reslist, res, next);
- g->hostmem += res->hostmem;
-
resource_id = qemu_get_be32(f);
}
/* load & apply scanout state */
vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1);
+
+ return 0;
+}
+
+static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, JSONWriter *vmdesc)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_simple_resource *res;
+ int i;
+
+ /* in 2d mode we should never find unprocessed commands here */
+ assert(QTAILQ_EMPTY(&g->cmdq));
+
+ QTAILQ_FOREACH(res, &g->reslist, next) {
+ if (!res->blob_size) {
+ continue;
+ }
+ qemu_put_be32(f, res->resource_id);
+ qemu_put_be32(f, res->blob_size);
+ qemu_put_be32(f, res->iov_cnt);
+ for (i = 0; i < res->iov_cnt; i++) {
+ qemu_put_be64(f, res->addrs[i]);
+ qemu_put_be32(f, res->iov[i].iov_len);
+ }
+ }
+ qemu_put_be32(f, 0); /* end of list */
+
+ return 0;
+}
+
+static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_simple_resource *res;
+ uint32_t resource_id;
+ int i;
+
+ resource_id = qemu_get_be32(f);
+ while (resource_id != 0) {
+ res = virtio_gpu_find_resource(g, resource_id);
+ if (res) {
+ return -EINVAL;
+ }
+
+ res = g_new0(struct virtio_gpu_simple_resource, 1);
+ res->resource_id = resource_id;
+ res->blob_size = qemu_get_be32(f);
+ res->iov_cnt = qemu_get_be32(f);
+ res->addrs = g_new(uint64_t, res->iov_cnt);
+ res->iov = g_new(struct iovec, res->iov_cnt);
+
+ /* read data */
+ for (i = 0; i < res->iov_cnt; i++) {
+ res->addrs[i] = qemu_get_be64(f);
+ res->iov[i].iov_len = qemu_get_be32(f);
+ }
+
+ if (!virtio_gpu_load_restore_mapping(g, res)) {
+ g_free(res);
+ return -EINVAL;
+ }
+
+ virtio_gpu_init_udmabuf(res);
+
+ resource_id = qemu_get_be32(f);
+ }
+
+ return 0;
+}
+
+static int virtio_gpu_post_load(void *opaque, int version_id)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_scanout *scanout;
+ struct virtio_gpu_simple_resource *res;
+ int i;
+
for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
/* FIXME: should take scanout.r.{x,y} into account */
scanout = &g->parent_obj.scanout[i];
@@ -1475,6 +1562,32 @@ virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
}
}
+static bool virtio_gpu_blob_state_needed(void *opaque)
+{
+ VirtIOGPU *g = VIRTIO_GPU(opaque);
+
+ return virtio_gpu_blob_enabled(g->parent_obj.conf);
+}
+
+const VMStateDescription vmstate_virtio_gpu_blob_state = {
+ .name = "virtio-gpu/blob",
+ .minimum_version_id = VIRTIO_GPU_VM_VERSION,
+ .version_id = VIRTIO_GPU_VM_VERSION,
+ .needed = virtio_gpu_blob_state_needed,
+ .fields = (const VMStateField[]){
+ {
+ .name = "virtio-gpu/blob",
+ .info = &(const VMStateInfo) {
+ .name = "blob",
+ .get = virtio_gpu_blob_load,
+ .put = virtio_gpu_blob_save,
+ },
+ .flags = VMS_SINGLE,
+ } /* device */,
+ VMSTATE_END_OF_LIST()
+ },
+};
+
/*
* For historical reasons virtio_gpu does not adhere to virtio migration
* scheme as described in doc/virtio-migration.txt, in a sense that no
@@ -1500,6 +1613,11 @@ static const VMStateDescription vmstate_virtio_gpu = {
} /* device */,
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_virtio_gpu_blob_state,
+ NULL
+ },
+ .post_load = virtio_gpu_post_load,
};
static Property virtio_gpu_properties[] = {