diff options
Diffstat (limited to 'hw')
64 files changed, 1241 insertions, 715 deletions
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 2ff0d5ce1b..ce428dfc18 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -238,10 +238,12 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, mdev->dimm = dev; mdev->is_enabled = true; - mdev->is_inserting = true; + if (dev->hotplugged) { + mdev->is_inserting = true; - /* do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); + /* do ACPI magic */ + acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); + } return; } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 5d38c47444..77d9267599 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -923,7 +923,7 @@ static void machvirt_init(MachineState *machine) qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); int gic_version = vms->gic_version; - int n; + int n, max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); const char *cpu_model = machine->cpu_model; VirtBoardInfo *vbi; @@ -957,6 +957,22 @@ static void machvirt_init(MachineState *machine) exit(1); } + /* The maximum number of CPUs depends on the GIC version, or on how + * many redistributors we can fit into the memory map. + */ + if (gic_version == 3) { + max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000; + } else { + max_cpus = GIC_NCPU; + } + + if (smp_cpus > max_cpus) { + error_report("Number of SMP CPUs requested (%d) exceeds max CPUs " + "supported by machine 'mach-virt' (%d)", + smp_cpus, max_cpus); + exit(1); + } + vbi->smp_cpus = smp_cpus; if (machine->ram_size > vbi->memmap[VIRT_MEM].size) { @@ -1155,10 +1171,11 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Virtual Machine", mc->init = machvirt_init; - /* Our maximum number of CPUs depends on how many redistributors - * we can fit into memory map + /* Start max_cpus at the maximum QEMU supports. We'll further restrict + * it later in machvirt_init, where we have more information about the + * configuration of the particular instance. */ - mc->max_cpus = a15memmap[VIRT_GIC_REDIST].size / 0x20000; + mc->max_cpus = MAX_CPUMASK_BITS; mc->has_dynamic_sysbus = true; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index b36ca3da74..87553bbc60 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -48,6 +48,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = { 21, 22, }; +static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = { + 0xFF160000, 0xFF170000, +}; + +static const int sdhci_intr[XLNX_ZYNQMP_NUM_SDHCI] = { + 48, 49, +}; + typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; @@ -97,6 +105,13 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI); qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]), + TYPE_SYSBUS_SDHCI); + qdev_set_parent_bus(DEVICE(&s->sdhci[i]), + sysbus_get_default()); + } } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -258,6 +273,19 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, SATA_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]); + + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + object_property_set_bool(OBJECT(&s->sdhci[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0, + sdhci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0, + gic_spi[sdhci_intr[i]]); + } } static Property xlnx_zynqmp_props[] = { diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 6106e46158..c42ddeb297 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -283,7 +283,8 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) /* Get this show started by hooking up our callbacks */ aio_context_acquire(s->ctx); - aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify); + aio_set_event_notifier(s->ctx, &s->host_notifier, true, + handle_notify); aio_context_release(s->ctx); return; @@ -319,7 +320,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) aio_context_acquire(s->ctx); /* Stop notifications for new requests from guest */ - aio_set_event_notifier(s->ctx, &s->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->host_notifier, true, NULL); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 6686a72803..4292eced32 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -192,6 +192,8 @@ typedef struct FDrive { uint8_t ro; /* Is read-only */ uint8_t media_changed; /* Is media changed */ uint8_t media_rate; /* Data rate of medium */ + + bool media_inserted; /* Is there a medium in the tray */ } FDrive; static void fd_init(FDrive *drv) @@ -261,7 +263,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, #endif drv->head = head; if (drv->track != track) { - if (drv->blk != NULL && blk_is_inserted(drv->blk)) { + if (drv->media_inserted) { drv->media_changed = 0; } ret = 1; @@ -270,7 +272,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, drv->sect = sect; } - if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { + if (!drv->media_inserted) { ret = 2; } @@ -296,7 +298,7 @@ static void fd_revalidate(FDrive *drv) ro = blk_is_read_only(drv->blk); pick_geometry(drv->blk, &nb_heads, &max_track, &last_sect, drv->drive, &drive, &rate); - if (!blk_is_inserted(drv->blk)) { + if (!drv->media_inserted) { FLOPPY_DPRINTF("No disk in drive\n"); } else { FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads, @@ -692,7 +694,7 @@ static bool fdrive_media_changed_needed(void *opaque) { FDrive *drive = opaque; - return (drive->blk != NULL && drive->media_changed != 1); + return (drive->media_inserted && drive->media_changed != 1); } static const VMStateDescription vmstate_fdrive_media_changed = { @@ -2184,12 +2186,21 @@ static void fdctrl_change_cb(void *opaque, bool load) { FDrive *drive = opaque; + drive->media_inserted = load && drive->blk && blk_is_inserted(drive->blk); + drive->media_changed = 1; fd_revalidate(drive); } +static bool fdctrl_is_tray_open(void *opaque) +{ + FDrive *drive = opaque; + return !drive->media_inserted; +} + static const BlockDevOps fdctrl_block_ops = { .change_media_cb = fdctrl_change_cb, + .is_tray_open = fdctrl_is_tray_open, }; /* Init functions */ @@ -2217,6 +2228,7 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) fdctrl_change_cb(drive, 0); if (drive->blk) { blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); + drive->media_inserted = blk_is_inserted(drive->blk); } } } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 8beb26b4db..093e475dc9 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -798,6 +798,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) static void virtio_blk_save(QEMUFile *f, void *opaque) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); + VirtIOBlock *s = VIRTIO_BLK(vdev); + + if (s->dataplane) { + virtio_blk_data_plane_stop(s->dataplane); + } virtio_save(vdev, f); } @@ -839,10 +844,7 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, req->next = s->rq; s->rq = req; - virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, - req->elem.in_num, 1); - virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, - req->elem.out_num, 0); + virtqueue_map(&req->elem); } return 0; @@ -975,7 +977,7 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), #ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true), + DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false), #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 36d7398f4f..1bbc111939 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -931,9 +931,11 @@ static int blk_connect(struct XenDevice *xendev) blk_attach_dev_nofail(blkdev->blk, blkdev); blkdev->file_size = blk_getlength(blkdev->blk); if (blkdev->file_size < 0) { + BlockDriverState *bs = blk_bs(blkdev->blk); + const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), - bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-"); + drv_name ?: "-"); blkdev->file_size = 0; } diff --git a/hw/char/escc.c b/hw/char/escc.c index ba653efd68..9816154206 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -1035,6 +1035,7 @@ static void escc_class_init(ObjectClass *klass, void *data) dc->reset = escc_reset; dc->vmsd = &vmstate_escc; dc->props = escc_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo escc_info = { diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index f0c4c722d6..f30f9c24be 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -22,25 +22,17 @@ #include "sysemu/sysemu.h" #include "sysemu/char.h" -//#define DEBUG_SERIAL 1 -#ifdef DEBUG_SERIAL -#define DPRINTF(fmt, args...) \ -do { printf("%s: " fmt , TYPE_IMX_SERIAL, ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_UART +#define DEBUG_IMX_UART 0 #endif -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -//#define DEBUG_IMPLEMENTATION 1 -#ifdef DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, TYPE_IMX_SERIAL, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_UART) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SERIAL, \ + __func__, ##args); \ + } \ + } while (0) static const VMStateDescription vmstate_imx_serial = { .name = TYPE_IMX_SERIAL, @@ -115,7 +107,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, IMXSerialState *s = (IMXSerialState *)opaque; uint32_t c; - DPRINTF("read(offset=%x)\n", offset >> 2); + DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); + switch (offset >> 2) { case 0x0: /* URXD */ c = s->readbuff; @@ -167,7 +160,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, return 0x0; /* TODO */ default: - IPRINTF("%s: bad offset: 0x%x\n", __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); return 0; } } @@ -178,9 +172,8 @@ static void imx_serial_write(void *opaque, hwaddr offset, IMXSerialState *s = (IMXSerialState *)opaque; unsigned char ch; - DPRINTF("write(offset=%x, value = %x) to %s\n", - offset >> 2, - (unsigned int)value, s->chr ? s->chr->label : "NODEV"); + DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", + offset, (unsigned int)value, s->chr ? s->chr->label : "NODEV"); switch (offset >> 2) { case 0x10: /* UTXD */ @@ -198,7 +191,9 @@ static void imx_serial_write(void *opaque, hwaddr offset, case 0x20: /* UCR1 */ s->ucr1 = value & 0xffff; + DPRINTF("write(ucr1=%x)\n", (unsigned int)value); + imx_update(s); break; @@ -266,12 +261,14 @@ static void imx_serial_write(void *opaque, hwaddr offset, case 0x2d: /* UTS1 */ case 0x23: /* UCR4 */ - IPRINTF("Unimplemented Register %x written to\n", offset >> 2); + qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); /* TODO */ break; default: - IPRINTF("%s: Bad offset 0x%x\n", __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); } } @@ -284,7 +281,9 @@ static int imx_can_receive(void *opaque) static void imx_put_data(void *opaque, uint32_t value) { IMXSerialState *s = (IMXSerialState *)opaque; + DPRINTF("received char\n"); + s->usr1 |= USR1_RRDY; s->usr2 |= USR2_RDR; s->uts1 &= ~UTS1_RXEMPTY; @@ -319,8 +318,7 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, imx_event, s); } else { - DPRINTF("No char dev for uart at 0x%lx\n", - (unsigned long)s->iomem.ram_addr); + DPRINTF("No char dev for uart\n"); } } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index be97058712..497b0afd9f 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -705,10 +705,7 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, qemu_get_buffer(f, (unsigned char *)&port->elem, sizeof(port->elem)); - virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, - port->elem.in_num, 1); - virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, - port->elem.out_num, 1); + virtqueue_map(&port->elem); /* * Port was throttled on source machine. Let's diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index d56ffcd8d7..3170585a27 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -29,11 +29,12 @@ typedef enum IMXGPIOLevel { } IMXGPIOLevel; #define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_GPIO) { \ - fprintf(stderr, "%s: " fmt , __func__, ##args); \ - } \ - } while (0) + do { \ + if (DEBUG_IMX_GPIO) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \ + __func__, ##args); \ + } \ + } while (0) static const char *imx_gpio_reg_name(uint32_t reg) { @@ -176,19 +177,19 @@ static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) if (s->has_edge_sel) { reg_value = s->edge_sel; } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not " "present on this version of GPIO device\n", TYPE_IMX_GPIO, __func__); } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", - TYPE_IMX_GPIO, __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); break; } - DPRINTF("(%s) = 0x%"PRIx32"\n", imx_gpio_reg_name(offset), reg_value); + DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value); return reg_value; } @@ -198,7 +199,7 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, { IMXGPIOState *s = IMX_GPIO(opaque); - DPRINTF("(%s, value = 0x%"PRIx32")\n", imx_gpio_reg_name(offset), + DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset), (uint32_t)value); switch (offset) { @@ -238,15 +239,15 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, s->edge_sel = value; imx_gpio_set_all_int_lines(s); } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not " "present on this version of GPIO device\n", TYPE_IMX_GPIO, __func__); } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", - TYPE_IMX_GPIO, __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); break; } diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 8474872e07..cb62c7a2c9 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -21,13 +21,17 @@ #include "hw/i2c/imx_i2c.h" #include "hw/i2c/i2c.h" -#ifndef IMX_I2C_DEBUG -#define IMX_I2C_DEBUG 0 +#ifndef DEBUG_IMX_I2C +#define DEBUG_IMX_I2C 0 #endif -#if IMX_I2C_DEBUG -#define DPRINT(fmt, args...) \ - do { fprintf(stderr, "%s: "fmt, __func__, ## args); } while (0) +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_I2C) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \ + __func__, ##args); \ + } \ + } while (0) static const char *imx_i2c_get_regname(unsigned offset) { @@ -46,9 +50,6 @@ static const char *imx_i2c_get_regname(unsigned offset) return "[?]"; } } -#else -#define DPRINT(fmt, args...) do { } while (0) -#endif static inline bool imx_i2c_is_enabled(IMXI2CState *s) { @@ -121,11 +122,11 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, if (s->address == ADDR_RESET) { /* something is wrong as the address is not set */ - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Trying to read " + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " "without specifying the slave address\n", TYPE_IMX_I2C, __func__); } else if (s->i2cr & I2CR_MTX) { - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Trying to read " + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " "but MTX is set\n", TYPE_IMX_I2C, __func__); } else { /* get the next byte */ @@ -134,7 +135,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, if (ret >= 0) { imx_i2c_raise_interrupt(s); } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: read failed " + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " "for device 0x%02x\n", TYPE_IMX_I2C, __func__, s->address); ret = 0xff; @@ -143,19 +144,19 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, s->i2dr_read = ret; } else { - qemu_log_mask(LOG_UNIMP, "%s[%s]: slave mode not implemented\n", + qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", TYPE_IMX_I2C, __func__); } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", - TYPE_IMX_I2C, __func__, s->address); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset); value = 0; break; } - DPRINT("read %s [0x%02x] -> 0x%02x\n", imx_i2c_get_regname(offset), - (unsigned int)offset, value); + DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n", + imx_i2c_get_regname(offset), offset, value); return (uint64_t)value; } @@ -165,8 +166,8 @@ static void imx_i2c_write(void *opaque, hwaddr offset, { IMXI2CState *s = IMX_I2C(opaque); - DPRINT("write %s [0x%02x] <- 0x%02x\n", imx_i2c_get_regname(offset), - (unsigned int)offset, (int)value); + DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n", + imx_i2c_get_regname(offset), offset, (int)value); value &= 0xff; @@ -264,13 +265,13 @@ static void imx_i2c_write(void *opaque, hwaddr offset, } } } else { - qemu_log_mask(LOG_UNIMP, "%s[%s]: slave mode not implemented\n", + qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", TYPE_IMX_I2C, __func__); } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", - TYPE_IMX_I2C, __func__, s->address); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset); break; } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3d958bae5b..0cb8afd2c2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1616,7 +1616,6 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); @@ -1632,8 +1631,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, - pcmc->inter_dimm_gap, &local_err); + pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); if (local_err) { goto out; } @@ -1953,7 +1951,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - pcmc->inter_dimm_gap = true; pcmc->get_hotplug_handler = mc->get_hotplug_handler; mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9d4425a5b9..393dcc4544 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -487,7 +487,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m) m->alias = NULL; m->is_default = 0; pcmc->broken_reserved_end = true; - pcmc->inter_dimm_gap = false; SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 3744abd397..2f8f3963c4 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -385,7 +385,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m) pc_q35_2_5_machine_options(m); m->alias = NULL; pcmc->broken_reserved_end = true; - pcmc->inter_dimm_gap = false; SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); } diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 8682c42e46..de83f4e3ee 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -33,6 +33,7 @@ #include "trace.h" #include "exec/address-spaces.h" #include "sysemu/block-backend.h" +#include "qemu/error-report.h" #include <xenguest.h> @@ -382,13 +383,16 @@ static const VMStateDescription vmstate_xen_platform = { } }; -static int xen_platform_initfn(PCIDevice *dev) +static void xen_platform_realize(PCIDevice *dev, Error **errp) { PCIXenPlatformState *d = XEN_PLATFORM(dev); uint8_t *pci_conf; /* Device will crash on reset if xen is not initialized */ - assert(xen_enabled()); + if (!xen_enabled()) { + error_setg(errp, "xen-platform device requires the Xen accelerator"); + return; + } pci_conf = dev->config; @@ -407,8 +411,6 @@ static int xen_platform_initfn(PCIDevice *dev) &d->mmio_bar); platform_fixed_ioport_init(d); - - return 0; } static void platform_reset(DeviceState *dev) @@ -423,7 +425,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = xen_platform_initfn; + k->realize = xen_platform_realize; k->vendor_id = PCI_VENDOR_ID_XEN; k->device_id = PCI_DEVICE_ID_XEN_PLATFORM; k->class_id = PCI_CLASS_OTHERS << 8 | 0x80; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 66fb9d96d5..27f3da21a7 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -417,6 +417,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) k->config_read = cmd646_pci_config_read; k->config_write = cmd646_pci_config_write; dc->props = cmd646_ide_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo cmd646_ide_info = { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 66ac2baa94..893c9b9bae 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -590,6 +590,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data) dc->realize = macio_ide_realizefn; dc->reset = macio_ide_reset; dc->vmsd = &vmstate_pmac; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo macio_ide_type_info = { diff --git a/hw/input/adb.c b/hw/input/adb.c index a18eea2652..09eead96b6 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -362,6 +362,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) akc->parent_realize = dc->realize; dc->realize = adb_kbd_realizefn; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_kbd_request; dc->reset = adb_kbd_reset; @@ -566,6 +567,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) amc->parent_realize = dc->realize; dc->realize = adb_mouse_realizefn; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_mouse_request; dc->reset = adb_mouse_reset; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index e8b2386908..0ceebbf87e 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -20,6 +20,7 @@ */ #include "hw/sysbus.h" +#include "migration/migration.h" #include "sysemu/kvm.h" #include "kvm_arm.h" #include "gic_internal.h" @@ -307,11 +308,6 @@ static void kvm_arm_gic_put(GICState *s) int num_cpu; int num_irq; - if (!kvm_arm_gic_can_save_restore(s)) { - DPRINTF("Cannot put kernel gic state, no kernel interface"); - return; - } - /* Note: We do the restore in a slightly different order than the save * (where the order doesn't matter and is simply ordered according to the * register offset values */ @@ -411,11 +407,6 @@ static void kvm_arm_gic_get(GICState *s) int i; int cpu; - if (!kvm_arm_gic_can_save_restore(s)) { - DPRINTF("Cannot get kernel gic state, no kernel interface"); - return; - } - /***************************************************************** * Distributor State */ @@ -503,7 +494,10 @@ static void kvm_arm_gic_reset(DeviceState *dev) KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); kgc->parent_reset(dev); - kvm_arm_gic_put(s); + + if (kvm_arm_gic_can_save_restore(s)) { + kvm_arm_gic_put(s); + } } static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) @@ -573,6 +567,12 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, s->dev_fd); + + if (!kvm_arm_gic_can_save_restore(s)) { + error_setg(&s->migration_blocker, "This operating system kernel does " + "not support vGICv2 migration"); + migrate_add_blocker(s->migration_blocker); + } } static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index 96c376b6af..e0535ffc57 100644 --- a/hw/intc/imx_avic.c +++ b/hw/intc/imx_avic.c @@ -17,27 +17,17 @@ #include "hw/intc/imx_avic.h" -#define DEBUG_INT 1 -#undef DEBUG_INT /* comment out for debugging */ - -#ifdef DEBUG_INT -#define DPRINTF(fmt, args...) \ -do { printf("%s: " fmt , TYPE_IMX_AVIC, ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_AVIC +#define DEBUG_IMX_AVIC 0 #endif -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, TYPE_IMX_AVIC, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_AVIC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \ + __func__, ##args); \ + } \ + } while (0) static const VMStateDescription vmstate_imx_avic = { .name = TYPE_IMX_AVIC, @@ -115,8 +105,8 @@ static uint64_t imx_avic_read(void *opaque, { IMXAVICState *s = (IMXAVICState *)opaque; + DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset); - DPRINTF("read(offset = 0x%x)\n", offset >> 2); switch (offset >> 2) { case 0: /* INTCNTL */ return s->intcntl; @@ -213,7 +203,8 @@ static uint64_t imx_avic_read(void *opaque, return 0x4; default: - IPRINTF("%s: Bad offset 0x%x\n", __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); return 0; } } @@ -225,13 +216,13 @@ static void imx_avic_write(void *opaque, hwaddr offset, /* Vector Registers not yet supported */ if (offset >= 0x100 && offset <= 0x2fc) { - IPRINTF("%s to vector register %d ignored\n", __func__, - (unsigned int)((offset - 0x100) >> 2)); + qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n", + TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2)); return; } - DPRINTF("%s(0x%x) = %x\n", __func__, - (unsigned int)offset>>2, (unsigned int)val); + DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val); + switch (offset >> 2) { case 0: /* Interrupt Control Register, INTCNTL */ s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM); @@ -305,7 +296,8 @@ static void imx_avic_write(void *opaque, hwaddr offset, return; default: - IPRINTF("%s: Bad offset %x\n", __func__, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); } imx_avic_update(s); } diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 14ab0e31b8..bfcf155356 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1643,6 +1643,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->props = openpic_properties; dc->reset = openpic_reset; dc->vmsd = &vmstate_openpic; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo openpic_info = { diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index f7cac585a9..649f476ac8 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -275,6 +275,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data) dc->realize = kvm_openpic_realize; dc->props = kvm_openpic_properties; dc->reset = kvm_openpic_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo kvm_openpic_info = { diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 2bae994667..80f424b442 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -33,8 +33,7 @@ typedef struct pc_dimms_capacity { } pc_dimms_capacity; void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, bool gap, - Error **errp) + MemoryRegion *mr, uint64_t align, Error **errp) { int slot; MachineState *machine = MACHINE(qdev_get_machine()); @@ -50,7 +49,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, addr = pc_dimm_get_free_addr(hpms->base, memory_region_size(&hpms->mr), - !addr ? NULL : &addr, align, gap, + !addr ? NULL : &addr, align, memory_region_size(mr), &local_err); if (local_err) { goto out; @@ -295,8 +294,8 @@ static int pc_dimm_built_list(Object *obj, void *opaque) uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, uint64_t address_space_size, - uint64_t *hint, uint64_t align, bool gap, - uint64_t size, Error **errp) + uint64_t *hint, uint64_t align, uint64_t size, + Error **errp) { GSList *list = NULL, *item; uint64_t new_addr, ret = 0; @@ -341,15 +340,13 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, goto out; } - if (ranges_overlap(dimm->addr, dimm_size, new_addr, - size + (gap ? 1 : 0))) { + if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { if (hint) { DeviceState *d = DEVICE(dimm); error_setg(errp, "address range conflicts with '%s'", d->id); goto out; } - new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size + (gap ? 1 : 0), - align); + new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align); } } ret = new_addr; diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index c1f570a79f..91c36baa55 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -901,7 +901,7 @@ static void main_cpu_reset(void *opaque) if (kvm_enabled()) { /* Start running from the bootloader we wrote to end of RAM */ - env->active_tc.PC = 0x40000000 + loaderparams.ram_size; + env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size; } } diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c index 2e19dbb1bb..4cc2bbc0ed 100644 --- a/hw/misc/imx_ccm.c +++ b/hw/misc/imx_ccm.c @@ -16,14 +16,18 @@ #define CKIH_FREQ 26000000 /* 26MHz crystal input */ #define CKIL_FREQ 32768 /* nominal 32khz clock */ -//#define DEBUG_CCM 1 -#ifdef DEBUG_CCM -#define DPRINTF(fmt, args...) \ -do { printf("%s: " fmt , TYPE_IMX_CCM, ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) +#ifndef DEBUG_IMX_CCM +#define DEBUG_IMX_CCM 0 #endif +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \ + __func__, ##args); \ + } \ + } while (0) + static int imx_ccm_post_load(void *opaque, int version_id); static const VMStateDescription vmstate_imx_ccm = { @@ -109,7 +113,7 @@ static void update_clocks(IMXCCMState *s) s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP)); s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG)); - DPRINTF("%s: mcu %uMHz, HSP %uMHz, IPG %uHz\n", __func__, + DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n", s->mcu_clk_freq / 1000000, s->hsp_clk_freq / 1000000, s->ipg_clk_freq); @@ -135,7 +139,8 @@ static uint64_t imx_ccm_read(void *opaque, hwaddr offset, { IMXCCMState *s = (IMXCCMState *)opaque; - DPRINTF("%s(offset=%x)", __func__, offset >> 2); + DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset); + switch (offset >> 2) { case 0: /* CCMR */ DPRINTF(" ccmr = 0x%x\n", s->ccmr); @@ -166,9 +171,11 @@ static uint64_t imx_ccm_read(void *opaque, hwaddr offset, case 23: DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0); return s->pmcr0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset); + return 0; } - DPRINTF(" return 0\n"); - return 0; } static void imx_ccm_write(void *opaque, hwaddr offset, @@ -176,8 +183,9 @@ static void imx_ccm_write(void *opaque, hwaddr offset, { IMXCCMState *s = (IMXCCMState *)opaque; - DPRINTF("%s(offset=%x, value = %x)\n", __func__, - offset >> 2, (unsigned int)value); + DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n", + offset, (unsigned int)value); + switch (offset >> 2) { case 0: s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff); @@ -205,6 +213,8 @@ static void imx_ccm_write(void *opaque, hwaddr offset, return; default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset); return; } update_clocks(s); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index cc76989a39..83d7bd3e5f 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -19,6 +19,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "sysemu/kvm.h" #include "migration/migration.h" @@ -26,6 +27,10 @@ #include "qemu/event_notifier.h" #include "qemu/fifo8.h" #include "sysemu/char.h" +#include "sysemu/hostmem.h" +#include "qapi/visitor.h" + +#include "hw/misc/ivshmem.h" #include <sys/mman.h> #include <sys/types.h> @@ -34,6 +39,7 @@ #define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET #define PCI_DEVICE_ID_IVSHMEM 0x1110 +#define IVSHMEM_MAX_PEERS G_MAXUINT16 #define IVSHMEM_IOEVENTFD 0 #define IVSHMEM_MSI 1 @@ -54,24 +60,26 @@ #define IVSHMEM(obj) \ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM) +#define IVSHMEM_MEMDEV_PROP "memdev" + typedef struct Peer { int nb_eventfds; EventNotifier *eventfds; } Peer; -typedef struct EventfdEntry { +typedef struct MSIVector { PCIDevice *pdev; - int vector; -} EventfdEntry; + int virq; +} MSIVector; typedef struct IVShmemState { /*< private >*/ PCIDevice parent_obj; /*< public >*/ + HostMemoryBackend *hostmem; uint32_t intrmask; uint32_t intrstatus; - uint32_t doorbell; CharDriverState **eventfd_chr; CharDriverState *server_chr; @@ -85,18 +93,15 @@ typedef struct IVShmemState { MemoryRegion bar; MemoryRegion ivshmem; uint64_t ivshmem_size; /* size of shared memory region */ - uint32_t ivshmem_attr; uint32_t ivshmem_64bit; - int shm_fd; /* shared memory file descriptor */ Peer *peers; - int nb_peers; /* how many guests we have space for */ - int max_peer; /* maximum numbered peer */ + int nb_peers; /* how many peers we have space for */ int vm_id; uint32_t vectors; uint32_t features; - EventfdEntry *eventfd_table; + MSIVector *msi_vectors; Error *migration_blocker; @@ -119,12 +124,8 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, return (ivs->features & (1 << feature)); } -static inline bool is_power_of_two(uint64_t x) { - return (x & (x - 1)) == 0; -} - /* accessing registers - based on rtl8139 */ -static void ivshmem_update_irq(IVShmemState *s, int val) +static void ivshmem_update_irq(IVShmemState *s) { PCIDevice *d = PCI_DEVICE(s); int isr; @@ -145,7 +146,7 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) s->intrmask = val; - ivshmem_update_irq(s, val); + ivshmem_update_irq(s); } static uint32_t ivshmem_IntrMask_read(IVShmemState *s) @@ -163,7 +164,7 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) s->intrstatus = val; - ivshmem_update_irq(s, val); + ivshmem_update_irq(s); } static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) @@ -173,7 +174,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) /* reading ISR clears all interrupts */ s->intrstatus = 0; - ivshmem_update_irq(s, 0); + ivshmem_update_irq(s); return ret; } @@ -201,7 +202,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, case DOORBELL: /* check that dest VM ID is reasonable */ - if (dest > s->max_peer) { + if (dest >= s->nb_peers) { IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); break; } @@ -210,10 +211,13 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, if (vector < s->peers[dest].nb_eventfds) { IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector); event_notifier_set(&s->peers[dest].eventfds[vector]); + } else { + IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n", + vector, dest); } break; default: - IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest); + IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr); } } @@ -236,7 +240,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, case IVPOSITION: /* return my VM ID if the memory is mapped */ - if (s->shm_fd > 0) { + if (memory_region_is_mapped(&s->ivshmem)) { ret = s->vm_id; } else { ret = -1; @@ -265,14 +269,14 @@ static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) { IVShmemState *s = opaque; - ivshmem_IntrStatus_write(s, *buf); + IVSHMEM_DPRINTF("ivshmem_receive 0x%02x size: %d\n", *buf, size); - IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); + ivshmem_IntrStatus_write(s, *buf); } static int ivshmem_can_receive(void * opaque) { - return 8; + return sizeof(int64_t); } static void ivshmem_event(void *opaque, int event) @@ -282,11 +286,70 @@ static void ivshmem_event(void *opaque, int event) static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { - EventfdEntry *entry = opaque; + MSIVector *entry = opaque; PCIDevice *pdev = entry->pdev; + IVShmemState *s = IVSHMEM(pdev); + int vector = entry - s->msi_vectors; + + IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); + msix_notify(pdev, vector); +} + +static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, + MSIMessage msg) +{ + IVShmemState *s = IVSHMEM(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + MSIVector *v = &s->msi_vectors[vector]; + int ret; + + IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); + + ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); + if (ret < 0) { + return ret; + } + + return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); +} + +static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) +{ + IVShmemState *s = IVSHMEM(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + int ret; + + IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); + + ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, + s->msi_vectors[vector].virq); + if (ret != 0) { + error_report("remove_irqfd_notifier_gsi failed"); + } +} + +static void ivshmem_vector_poll(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end) +{ + IVShmemState *s = IVSHMEM(dev); + unsigned int vector; + + IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); + + vector_end = MIN(vector_end, s->vectors); + + for (vector = vector_start; vector < vector_end; vector++) { + EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; + + if (!msix_is_masked(dev, vector)) { + continue; + } - IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector); - msix_notify(pdev, entry->vector); + if (event_notifier_test_and_clear(notifier)) { + msix_set_pending(dev, vector); + } + } } static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n, @@ -294,24 +357,26 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * { /* create a event character device based on the passed eventfd */ IVShmemState *s = opaque; - CharDriverState * chr; + PCIDevice *pdev = PCI_DEVICE(s); int eventfd = event_notifier_get_fd(n); + CharDriverState *chr; + + s->msi_vectors[vector].pdev = pdev; chr = qemu_chr_open_eventfd(eventfd); if (chr == NULL) { - error_report("creating eventfd for eventfd %d failed", eventfd); - exit(1); + error_report("creating chardriver for eventfd %d failed", eventfd); + return NULL; } qemu_chr_fe_claim_no_fail(chr); /* if MSI is supported we need multiple interrupts */ if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - s->eventfd_table[vector].pdev = PCI_DEVICE(s); - s->eventfd_table[vector].vector = vector; + s->msi_vectors[vector].pdev = PCI_DEVICE(s); qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, - ivshmem_event, &s->eventfd_table[vector]); + ivshmem_event, &s->msi_vectors[vector]); } else { qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, ivshmem_event, s); @@ -321,22 +386,23 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * } -static int check_shm_size(IVShmemState *s, int fd) { +static int check_shm_size(IVShmemState *s, int fd, Error **errp) +{ /* check that the guest isn't going to try and map more memory than the * the object has allocated return -1 to indicate error */ struct stat buf; if (fstat(fd, &buf) < 0) { - error_report("exiting: fstat on fd %d failed: %s", - fd, strerror(errno)); + error_setg(errp, "exiting: fstat on fd %d failed: %s", + fd, strerror(errno)); return -1; } if (s->ivshmem_size > buf.st_size) { - error_report("Requested memory size greater" - " than shared object size (%" PRIu64 " > %" PRIu64")", - s->ivshmem_size, (uint64_t)buf.st_size); + error_setg(errp, "Requested memory size greater" + " than shared object size (%" PRIu64 " > %" PRIu64")", + s->ivshmem_size, (uint64_t)buf.st_size); return -1; } else { return 0; @@ -345,13 +411,16 @@ static int check_shm_size(IVShmemState *s, int fd) { /* create the shared memory BAR when we are not using the server, so we can * create the BAR and map the memory immediately */ -static void create_shared_memory_BAR(IVShmemState *s, int fd) { - +static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr, + Error **errp) +{ void * ptr; - s->shm_fd = fd; - ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + error_setg_errno(errp, errno, "Failed to mmap shared memory"); + return -1; + } memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2", s->ivshmem_size, ptr); @@ -359,7 +428,9 @@ static void create_shared_memory_BAR(IVShmemState *s, int fd) { memory_region_add_subregion(&s->bar, 0, &s->ivshmem); /* region for shared memory */ - pci_register_bar(PCI_DEVICE(s), 2, s->ivshmem_attr, &s->bar); + pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar); + + return 0; } static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) @@ -382,25 +453,26 @@ static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i) &s->peers[posn].eventfds[i]); } -static void close_guest_eventfds(IVShmemState *s, int posn) +static void close_peer_eventfds(IVShmemState *s, int posn) { - int i, guest_curr_max; + int i, n; if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { return; } if (posn < 0 || posn >= s->nb_peers) { + error_report("invalid peer %d", posn); return; } - guest_curr_max = s->peers[posn].nb_eventfds; + n = s->peers[posn].nb_eventfds; memory_region_transaction_begin(); - for (i = 0; i < guest_curr_max; i++) { + for (i = 0; i < n; i++) { ivshmem_del_eventfd(s, posn, i); } memory_region_transaction_commit(); - for (i = 0; i < guest_curr_max; i++) { + for (i = 0; i < n; i++) { event_notifier_cleanup(&s->peers[posn].eventfds[i]); } @@ -409,125 +481,207 @@ static void close_guest_eventfds(IVShmemState *s, int posn) } /* this function increase the dynamic storage need to store data about other - * guests */ -static int increase_dynamic_storage(IVShmemState *s, int new_min_size) + * peers */ +static int resize_peers(IVShmemState *s, int new_min_size) { - int j, old_nb_alloc; + int j, old_size; - /* check for integer overflow */ - if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) { + /* limit number of max peers */ + if (new_min_size <= 0 || new_min_size > IVSHMEM_MAX_PEERS) { return -1; } - - old_nb_alloc = s->nb_peers; - - if (new_min_size >= s->nb_peers) { - /* +1 because #new_min_size is used as last array index */ - s->nb_peers = new_min_size + 1; - } else { + if (new_min_size <= s->nb_peers) { return 0; } - IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); + old_size = s->nb_peers; + s->nb_peers = new_min_size; + + IVSHMEM_DPRINTF("bumping storage to %d peers\n", s->nb_peers); + s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer)); - /* zero out new pointers */ - for (j = old_nb_alloc; j < s->nb_peers; j++) { - s->peers[j].eventfds = NULL; + for (j = old_size; j < s->nb_peers; j++) { + s->peers[j].eventfds = g_new0(EventNotifier, s->vectors); s->peers[j].nb_eventfds = 0; } return 0; } -static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size, + void *data, size_t len) { - IVShmemState *s = opaque; - int incoming_fd, tmp_fd; - int guest_max_eventfd; - long incoming_posn; + const uint8_t *p; + uint32_t num; - if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) { - memcpy(&incoming_posn, buf, size); - } else { - const uint8_t *p; - uint32_t num; + assert(len <= sizeof(int64_t)); /* limitation of the fifo */ + if (fifo8_is_empty(&s->incoming_fifo) && size == len) { + memcpy(data, buf, size); + return true; + } + + IVSHMEM_DPRINTF("short read of %d bytes\n", size); + + num = MIN(size, sizeof(int64_t) - fifo8_num_used(&s->incoming_fifo)); + fifo8_push_all(&s->incoming_fifo, buf, num); + + if (fifo8_num_used(&s->incoming_fifo) < len) { + assert(num == 0); + return false; + } + + size -= num; + buf += num; + p = fifo8_pop_buf(&s->incoming_fifo, len, &num); + assert(num == len); + + memcpy(data, p, len); + + if (size > 0) { + fifo8_push_all(&s->incoming_fifo, buf, size); + } + + return true; +} + +static bool fifo_update_and_get_i64(IVShmemState *s, + const uint8_t *buf, int size, int64_t *i64) +{ + if (fifo_update_and_get(s, buf, size, i64, sizeof(*i64))) { + *i64 = GINT64_FROM_LE(*i64); + return true; + } + + return false; +} - IVSHMEM_DPRINTF("short read of %d bytes\n", size); - num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo)); - fifo8_push_all(&s->incoming_fifo, buf, num); - if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) { +static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector) +{ + PCIDevice *pdev = PCI_DEVICE(s); + MSIMessage msg = msix_get_message(pdev, vector); + int ret; + + IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); + + if (s->msi_vectors[vector].pdev != NULL) { + return 0; + } + + ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev); + if (ret < 0) { + error_report("ivshmem: kvm_irqchip_add_msi_route failed"); + return -1; + } + + s->msi_vectors[vector].virq = ret; + s->msi_vectors[vector].pdev = pdev; + + return 0; +} + +static void setup_interrupt(IVShmemState *s, int vector) +{ + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + bool with_irqfd = kvm_msi_via_irqfd_enabled() && + ivshmem_has_feature(s, IVSHMEM_MSI); + PCIDevice *pdev = PCI_DEVICE(s); + + IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); + + if (!with_irqfd) { + IVSHMEM_DPRINTF("with eventfd"); + s->eventfd_chr[vector] = create_eventfd_chr_device(s, n, vector); + } else if (msix_enabled(pdev)) { + IVSHMEM_DPRINTF("with irqfd"); + if (ivshmem_add_kvm_msi_virq(s, vector) < 0) { return; } - size -= num; - buf += num; - p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num); - g_assert(num == sizeof(incoming_posn)); - memcpy(&incoming_posn, p, sizeof(incoming_posn)); - if (size > 0) { - fifo8_push_all(&s->incoming_fifo, buf, size); + + if (!msix_is_masked(pdev, vector)) { + kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, + s->msi_vectors[vector].virq); } + } else { + /* it will be delayed until msix is enabled, in write_config */ + IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled"); + } +} + +static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + int incoming_fd; + int new_eventfd; + int64_t incoming_posn; + Error *err = NULL; + Peer *peer; + + if (!fifo_update_and_get_i64(s, buf, size, &incoming_posn)) { + return; } if (incoming_posn < -1) { - IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn); + IVSHMEM_DPRINTF("invalid incoming_posn %" PRId64 "\n", incoming_posn); return; } /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ - tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr); - IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); + incoming_fd = qemu_chr_fe_get_msgfd(s->server_chr); + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", + incoming_posn, incoming_fd); - /* make sure we have enough space for this guest */ + /* make sure we have enough space for this peer */ if (incoming_posn >= s->nb_peers) { - if (increase_dynamic_storage(s, incoming_posn) < 0) { - error_report("increase_dynamic_storage() failed"); - if (tmp_fd != -1) { - close(tmp_fd); + if (resize_peers(s, incoming_posn + 1) < 0) { + error_report("failed to resize peers array"); + if (incoming_fd != -1) { + close(incoming_fd); } return; } } - if (tmp_fd == -1) { + peer = &s->peers[incoming_posn]; + + if (incoming_fd == -1) { /* if posn is positive and unseen before then this is our posn*/ - if ((incoming_posn >= 0) && - (s->peers[incoming_posn].eventfds == NULL)) { + if (incoming_posn >= 0 && s->vm_id == -1) { /* receive our posn */ s->vm_id = incoming_posn; - return; } else { - /* otherwise an fd == -1 means an existing guest has gone away */ - IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); - close_guest_eventfds(s, incoming_posn); - return; + /* otherwise an fd == -1 means an existing peer has gone away */ + IVSHMEM_DPRINTF("posn %" PRId64 " has gone away\n", incoming_posn); + close_peer_eventfds(s, incoming_posn); } - } - - /* because of the implementation of get_msgfd, we need a dup */ - incoming_fd = dup(tmp_fd); - - if (incoming_fd == -1) { - error_report("could not allocate file descriptor %s", strerror(errno)); - close(tmp_fd); return; } /* if the position is -1, then it's shared memory region fd */ if (incoming_posn == -1) { - void * map_ptr; - s->max_peer = 0; + if (memory_region_is_mapped(&s->ivshmem)) { + error_report("shm already initialized"); + close(incoming_fd); + return; + } - if (check_shm_size(s, incoming_fd) == -1) { - exit(1); + if (check_shm_size(s, incoming_fd, &err) == -1) { + error_report_err(err); + close(incoming_fd); + return; } /* mmap the region and map into the BAR2 */ map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, incoming_fd, 0); + if (map_ptr == MAP_FAILED) { + error_report("Failed to mmap shared memory %s", strerror(errno)); + close(incoming_fd); + return; + } memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2", s->ivshmem_size, map_ptr); vmstate_register_ram(&s->ivshmem, DEVICE(s)); @@ -537,44 +691,59 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size) memory_region_add_subregion(&s->bar, 0, &s->ivshmem); - /* only store the fd if it is successfully mapped */ - s->shm_fd = incoming_fd; + close(incoming_fd); + return; + } + /* each peer has an associated array of eventfds, and we keep + * track of how many eventfds received so far */ + /* get a new eventfd: */ + if (peer->nb_eventfds >= s->vectors) { + error_report("Too many eventfd received, device has %d vectors", + s->vectors); + close(incoming_fd); return; } - /* each guest has an array of eventfds, and we keep track of how many - * guests for each VM */ - guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; + new_eventfd = peer->nb_eventfds++; + + /* this is an eventfd for a particular peer VM */ + IVSHMEM_DPRINTF("eventfds[%" PRId64 "][%d] = %d\n", incoming_posn, + new_eventfd, incoming_fd); + event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd); + fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */ - if (guest_max_eventfd == 0) { - /* one eventfd per MSI vector */ - s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors); + if (incoming_posn == s->vm_id) { + setup_interrupt(s, new_eventfd); } - /* this is an eventfd for a particular guest VM */ - IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, - guest_max_eventfd, incoming_fd); - event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd], - incoming_fd); + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + ivshmem_add_eventfd(s, incoming_posn, new_eventfd); + } +} - /* increment count for particular guest */ - s->peers[incoming_posn].nb_eventfds++; +static void ivshmem_check_version(void *opaque, const uint8_t * buf, int size) +{ + IVShmemState *s = opaque; + int tmp; + int64_t version; - /* keep track of the maximum VM ID */ - if (incoming_posn > s->max_peer) { - s->max_peer = incoming_posn; + if (!fifo_update_and_get_i64(s, buf, size, &version)) { + return; } - if (incoming_posn == s->vm_id) { - s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, - &s->peers[s->vm_id].eventfds[guest_max_eventfd], - guest_max_eventfd); + tmp = qemu_chr_fe_get_msgfd(s->server_chr); + if (tmp != -1 || version != IVSHMEM_PROTOCOL_VERSION) { + fprintf(stderr, "incompatible version, you are connecting to a ivshmem-" + "server using a different protocol please check your setup\n"); + qemu_chr_delete(s->server_chr); + s->server_chr = NULL; + return; } - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd); - } + IVSHMEM_DPRINTF("version check ok, switch to real chardev handler\n"); + qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, + ivshmem_event, s); } /* Select the MSI-X vectors used by device. @@ -585,6 +754,7 @@ static void ivshmem_use_msix(IVShmemState * s) PCIDevice *d = PCI_DEVICE(s); int i; + IVSHMEM_DPRINTF("%s, msix present: %d\n", __func__, msix_present(d)); if (!msix_present(d)) { return; } @@ -599,128 +769,127 @@ static void ivshmem_reset(DeviceState *d) IVShmemState *s = IVSHMEM(d); s->intrstatus = 0; + s->intrmask = 0; ivshmem_use_msix(s); } -static uint64_t ivshmem_get_size(IVShmemState * s) { - - uint64_t value; - char *ptr; - - value = strtoull(s->sizearg, &ptr, 10); - switch (*ptr) { - case 0: case 'M': case 'm': - value <<= 20; - break; - case 'G': case 'g': - value <<= 30; - break; - default: - error_report("invalid ram size: %s", s->sizearg); - exit(1); - } - - /* BARs must be a power of 2 */ - if (!is_power_of_two(value)) { - error_report("size must be power of 2"); - exit(1); - } - - return value; -} - -static void ivshmem_setup_msi(IVShmemState * s) +static int ivshmem_setup_msi(IVShmemState * s) { if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) { - IVSHMEM_DPRINTF("msix initialization failed\n"); - exit(1); + return -1; } IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); /* allocate QEMU char devices for receiving interrupts */ - s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry)); + s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector)); ivshmem_use_msix(s); + return 0; } -static void ivshmem_save(QEMUFile* f, void *opaque) +static void ivshmem_enable_irqfd(IVShmemState *s) { - IVShmemState *proxy = opaque; - PCIDevice *pci_dev = PCI_DEVICE(proxy); - - IVSHMEM_DPRINTF("ivshmem_save\n"); - pci_device_save(pci_dev, f); + PCIDevice *pdev = PCI_DEVICE(s); + int i; - if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { - msix_save(pci_dev, f); - } else { - qemu_put_be32(f, proxy->intrstatus); - qemu_put_be32(f, proxy->intrmask); + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + ivshmem_add_kvm_msi_virq(s, i); } + if (msix_set_vector_notifiers(pdev, + ivshmem_vector_unmask, + ivshmem_vector_mask, + ivshmem_vector_poll)) { + error_report("ivshmem: msix_set_vector_notifiers failed"); + } } -static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) +static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) { - IVSHMEM_DPRINTF("ivshmem_load\n"); - - IVShmemState *proxy = opaque; - PCIDevice *pci_dev = PCI_DEVICE(proxy); - int ret; + IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); - if (version_id > 0) { - return -EINVAL; + if (s->msi_vectors[vector].pdev == NULL) { + return; } - if (proxy->role_val == IVSHMEM_PEER) { - error_report("'peer' devices are not migratable"); - return -EINVAL; - } + /* it was cleaned when masked in the frontend. */ + kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); - ret = pci_device_load(pci_dev, f); - if (ret) { - return ret; - } + s->msi_vectors[vector].pdev = NULL; +} - if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { - msix_load(pci_dev, f); - ivshmem_use_msix(proxy); - } else { - proxy->intrstatus = qemu_get_be32(f); - proxy->intrmask = qemu_get_be32(f); +static void ivshmem_disable_irqfd(IVShmemState *s) +{ + PCIDevice *pdev = PCI_DEVICE(s); + int i; + + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + ivshmem_remove_kvm_msi_virq(s, i); } - return 0; + msix_unset_vector_notifiers(pdev); } -static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) +static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, + uint32_t val, int len) { - pci_default_write_config(pci_dev, address, val, len); + IVShmemState *s = IVSHMEM(pdev); + int is_enabled, was_enabled = msix_enabled(pdev); + + pci_default_write_config(pdev, address, val, len); + is_enabled = msix_enabled(pdev); + + if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) { + if (!was_enabled && is_enabled) { + ivshmem_enable_irqfd(s); + } else if (was_enabled && !is_enabled) { + ivshmem_disable_irqfd(s); + } + } } -static int pci_ivshmem_init(PCIDevice *dev) +static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM(dev); uint8_t *pci_conf; + uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH; - if (s->sizearg == NULL) - s->ivshmem_size = 4 << 20; /* 4 MB default */ - else { - s->ivshmem_size = ivshmem_get_size(s); + if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) { + error_setg(errp, "You must specify either a shmobj, a chardev" + " or a hostmem"); + return; } - fifo8_create(&s->incoming_fifo, sizeof(long)); + if (s->hostmem) { + MemoryRegion *mr; - register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load, - dev); + if (s->sizearg) { + g_warning("size argument ignored with hostmem"); + } + + mr = host_memory_backend_get_memory(s->hostmem, errp); + s->ivshmem_size = memory_region_size(mr); + } else if (s->sizearg == NULL) { + s->ivshmem_size = 4 << 20; /* 4 MB default */ + } else { + char *end; + int64_t size = qemu_strtosz(s->sizearg, &end); + if (size < 0 || *end != '\0' || !is_power_of_2(size)) { + error_setg(errp, "Invalid size %s", s->sizearg); + return; + } + s->ivshmem_size = size; + } + + fifo8_create(&s->incoming_fifo, sizeof(int64_t)); /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { - error_report("ioeventfd/irqfd requires MSI"); - exit(1); + error_setg(errp, "ioeventfd/irqfd requires MSI"); + return; } /* check that role is reasonable */ @@ -730,8 +899,8 @@ static int pci_ivshmem_init(PCIDevice *dev) } else if (strncmp(s->role, "master", 7) == 0) { s->role_val = IVSHMEM_MASTER; } else { - error_report("'role' must be 'peer' or 'master'"); - exit(1); + error_setg(errp, "'role' must be 'peer' or 'master'"); + return; } } else { s->role_val = IVSHMEM_MASTER; /* default */ @@ -748,8 +917,6 @@ static int pci_ivshmem_init(PCIDevice *dev) pci_config_set_interrupt_pin(pci_conf, 1); - s->shm_fd = 0; - memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); @@ -758,51 +925,51 @@ static int pci_ivshmem_init(PCIDevice *dev) &s->ivshmem_mmio); memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size); - s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH; if (s->ivshmem_64bit) { - s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; + attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } - if ((s->server_chr != NULL) && - (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { - /* if we get a UNIX socket as the parameter we will talk - * to the ivshmem server to receive the memory region */ + if (s->hostmem != NULL) { + MemoryRegion *mr; - if (s->shmobj != NULL) { - error_report("WARNING: do not specify both 'chardev' " - "and 'shm' with ivshmem"); + IVSHMEM_DPRINTF("using hostmem\n"); + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp); + vmstate_register_ram(mr, DEVICE(s)); + memory_region_add_subregion(&s->bar, 0, mr); + pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar); + } else if (s->server_chr != NULL) { + if (strncmp(s->server_chr->filename, "unix:", 5)) { + error_setg(errp, "chardev is not a unix client socket"); + return; } + /* if we get a UNIX socket as the parameter we will talk + * to the ivshmem server to receive the memory region */ + IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", s->server_chr->filename); - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_setup_msi(s); + if (ivshmem_has_feature(s, IVSHMEM_MSI) && + ivshmem_setup_msi(s)) { + error_setg(errp, "msix initialization failed"); + return; } - /* we allocate enough space for 16 guests and grow as needed */ - s->nb_peers = 16; + /* we allocate enough space for 16 peers and grow as needed */ + resize_peers(s, 16); s->vm_id = -1; - /* allocate/initialize space for interrupt handling */ - s->peers = g_malloc0(s->nb_peers * sizeof(Peer)); - - pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar); + pci_register_bar(dev, 2, attr, &s->bar); s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); - qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, - ivshmem_event, s); + qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, + ivshmem_check_version, ivshmem_event, s); } else { /* just map the file immediately, we're not using a server */ int fd; - if (s->shmobj == NULL) { - error_report("Must specify 'chardev' or 'shm' to ivshmem"); - exit(1); - } - IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); /* try opening with O_EXCL and if it succeeds zero the memory @@ -816,39 +983,155 @@ static int pci_ivshmem_init(PCIDevice *dev) } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { - error_report("could not open shared file"); - exit(1); + error_setg(errp, "could not open shared file"); + return; + } + if (check_shm_size(s, fd, errp) == -1) { + return; } - if (check_shm_size(s, fd) == -1) { - exit(1); + create_shared_memory_BAR(s, fd, attr, errp); + close(fd); + } +} + +static void pci_ivshmem_exit(PCIDevice *dev) +{ + IVShmemState *s = IVSHMEM(dev); + int i; + + fifo8_destroy(&s->incoming_fifo); + + if (s->migration_blocker) { + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + } + + if (memory_region_is_mapped(&s->ivshmem)) { + if (!s->hostmem) { + void *addr = memory_region_get_ram_ptr(&s->ivshmem); + + if (munmap(addr, s->ivshmem_size) == -1) { + error_report("Failed to munmap shared memory %s", + strerror(errno)); + } } - create_shared_memory_BAR(s, fd); + vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); + memory_region_del_subregion(&s->bar, &s->ivshmem); + } + if (s->eventfd_chr) { + for (i = 0; i < s->vectors; i++) { + if (s->eventfd_chr[i]) { + qemu_chr_free(s->eventfd_chr[i]); + } + } + g_free(s->eventfd_chr); } - dev->config_write = ivshmem_write_config; + if (s->peers) { + for (i = 0; i < s->nb_peers; i++) { + close_peer_eventfds(s, i); + } + g_free(s->peers); + } + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + msix_uninit_exclusive_bar(dev); + } + + g_free(s->msi_vectors); +} + +static bool test_msix(void *opaque, int version_id) +{ + IVShmemState *s = opaque; + + return ivshmem_has_feature(s, IVSHMEM_MSI); +} + +static bool test_no_msix(void *opaque, int version_id) +{ + return !test_msix(opaque, version_id); +} + +static int ivshmem_pre_load(void *opaque) +{ + IVShmemState *s = opaque; + + if (s->role_val == IVSHMEM_PEER) { + error_report("'peer' devices are not migratable"); + return -EINVAL; + } return 0; } -static void pci_ivshmem_uninit(PCIDevice *dev) +static int ivshmem_post_load(void *opaque, int version_id) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = opaque; - if (s->migration_blocker) { - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_use_msix(s); } - memory_region_del_subregion(&s->bar, &s->ivshmem); - vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); - unregister_savevm(DEVICE(dev), "ivshmem", s); - fifo8_destroy(&s->incoming_fifo); + return 0; } +static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) +{ + IVShmemState *s = opaque; + PCIDevice *pdev = PCI_DEVICE(s); + int ret; + + IVSHMEM_DPRINTF("ivshmem_load_old\n"); + + if (version_id != 0) { + return -EINVAL; + } + + if (s->role_val == IVSHMEM_PEER) { + error_report("'peer' devices are not migratable"); + return -EINVAL; + } + + ret = pci_device_load(pdev, f); + if (ret) { + return ret; + } + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + msix_load(pdev, f); + ivshmem_use_msix(s); + } else { + s->intrstatus = qemu_get_be32(f); + s->intrmask = qemu_get_be32(f); + } + + return 0; +} + +static const VMStateDescription ivshmem_vmsd = { + .name = "ivshmem", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + + VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix), + VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix), + VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix), + + VMSTATE_END_OF_LIST() + }, + .load_state_old = ivshmem_load_old, + .minimum_version_id_old = 0 +}; + static Property ivshmem_properties[] = { DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), DEFINE_PROP_STRING("size", IVShmemState, sizearg), @@ -866,20 +1149,50 @@ static void ivshmem_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_ivshmem_init; - k->exit = pci_ivshmem_uninit; + k->realize = pci_ivshmem_realize; + k->exit = pci_ivshmem_exit; + k->config_write = ivshmem_write_config; k->vendor_id = PCI_VENDOR_ID_IVSHMEM; k->device_id = PCI_DEVICE_ID_IVSHMEM; k->class_id = PCI_CLASS_MEMORY_RAM; dc->reset = ivshmem_reset; dc->props = ivshmem_properties; + dc->vmsd = &ivshmem_vmsd; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "Inter-VM shared memory"; +} + +static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, + Object *val, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp); + if (memory_region_is_mapped(mr)) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + +static void ivshmem_init(Object *obj) +{ + IVShmemState *s = IVSHMEM(obj); + + object_property_add_link(obj, IVSHMEM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, + (Object **)&s->hostmem, + ivshmem_check_memdev_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); } static const TypeInfo ivshmem_info = { .name = TYPE_IVSHMEM, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_init, .class_init = ivshmem_class_init, }; diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 5d7043e99c..0fd75b376f 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -738,6 +738,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) dc->reset = cuda_reset; dc->vmsd = &vmstate_cuda; dc->props = cuda_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo cuda_type_info = { diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index c661f86c21..adb990e565 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -393,6 +393,7 @@ static void macio_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; dc->props = macio_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo macio_oldworld_type_info = { diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 1127223cfd..3639fc17f0 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -964,6 +964,7 @@ static void gem_reset(DeviceState *d) { int i; CadenceGEMState *s = CADENCE_GEM(d); + const uint8_t *a; DB_PRINT("\n"); @@ -982,6 +983,11 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + /* Set MAC address */ + a = &s->conf.macaddr.a[0]; + s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + for (i = 0; i < 4; i++) { s->sar_active[i] = false; } diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 725f3fa335..c50bf7ff34 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -27,31 +27,29 @@ /* For crc32 */ #include <zlib.h> -#ifndef IMX_FEC_DEBUG -#define IMX_FEC_DEBUG 0 +#ifndef DEBUG_IMX_FEC +#define DEBUG_IMX_FEC 0 #endif -#ifndef IMX_PHY_DEBUG -#define IMX_PHY_DEBUG 0 -#endif - -#if IMX_FEC_DEBUG -#define FEC_PRINTF(fmt, ...) \ - do { fprintf(stderr, "%s[%s]: " fmt , TYPE_IMX_FEC, __func__, \ - ## __VA_ARGS__); \ +#define FEC_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_FEC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ } while (0) -#else -#define FEC_PRINTF(fmt, ...) do {} while (0) + +#ifndef DEBUG_IMX_PHY +#define DEBUG_IMX_PHY 0 #endif -#if IMX_PHY_DEBUG -#define PHY_PRINTF(fmt, ...) \ - do { fprintf(stderr, "%s.phy[%s]: " fmt , TYPE_IMX_FEC, __func__, \ - ## __VA_ARGS__); \ +#define PHY_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_PHY) { \ + fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ } while (0) -#else -#define PHY_PRINTF(fmt, ...) do {} while (0) -#endif static const VMStateDescription vmstate_imx_fec = { .name = TYPE_IMX_FEC, @@ -182,12 +180,12 @@ static uint32_t do_phy_read(IMXFECState *s, int reg) case 18: case 27: case 31: - qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n", + qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", TYPE_IMX_FEC, __func__, reg); val = 0; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", TYPE_IMX_FEC, __func__, reg); val = 0; break; @@ -230,11 +228,11 @@ static void do_phy_write(IMXFECState *s, int reg, uint32_t val) case 18: case 27: case 31: - qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n", + qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", TYPE_IMX_FEC, __func__, reg); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s.phy[%s]: Bad address at offset %d\n", + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", TYPE_IMX_FEC, __func__, reg); break; } @@ -357,7 +355,7 @@ static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) { IMXFECState *s = IMX_FEC(opaque); - FEC_PRINTF("reading from @ 0x%03x\n", (int)addr); + FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr); switch (addr & 0x3ff) { case 0x004: @@ -417,8 +415,8 @@ static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) case 0x308: return s->miigsk_enr; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, (int)addr); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); return 0; } } @@ -428,7 +426,7 @@ static void imx_fec_write(void *opaque, hwaddr addr, { IMXFECState *s = IMX_FEC(opaque); - FEC_PRINTF("writing 0x%08x @ 0x%03x\n", (int)value, (int)addr); + FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); switch (addr & 0x3ff) { case 0x004: /* EIR */ @@ -530,8 +528,8 @@ static void imx_fec_write(void *opaque, hwaddr addr, s->miigsk_enr = (value & 0x2) ? 0x6 : 0; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, (int)addr); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); break; } @@ -561,7 +559,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, FEC_PRINTF("len %d\n", (int)size); if (!s->rx_enabled) { - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Unexpected packet\n", + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", TYPE_IMX_FEC, __func__); return 0; } @@ -592,14 +590,16 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, * save the remainder for when more RX buffers are * available, or flag an error. */ - qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Lost end of frame\n", + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", TYPE_IMX_FEC, __func__); break; } buf_len = (size <= s->emrbr) ? size : s->emrbr; bd.length = buf_len; size -= buf_len; - FEC_PRINTF("rx_bd %x length %d\n", addr, bd.length); + + FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); + /* The last 4 bytes are the CRC. */ if (size < 4) { buf_len += size - 4; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 3c5e10dd6d..5e3a233237 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1289,6 +1289,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) static void vmxnet3_fill_stats(VMXNET3State *s) { int i; + + if (!s->device_active) + return; + for (i = 0; i < s->txq_num; i++) { cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, &s->txq_descr[i].txq_stats, diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index d35f8a3121..9f165664c6 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -123,6 +123,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data) dc->reset = macio_nvram_reset; dc->vmsd = &vmstate_macio_nvram; dc->props = macio_nvram_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo macio_nvram_type_info = { diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index bfe707a1a1..ea31b72e7c 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -146,8 +146,10 @@ static const TypeInfo grackle_pci_info = { static void pci_grackle_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->init = pci_grackle_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo grackle_pci_host_info = { diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index f0144eb7b0..215b64ffed 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -446,8 +446,10 @@ static const TypeInfo unin_internal_pci_host_info = { static void pci_unin_main_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_main_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_main_info = { @@ -460,8 +462,10 @@ static const TypeInfo pci_unin_main_info = { static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_u3_agp_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_u3_agp_info = { @@ -474,8 +478,10 @@ static const TypeInfo pci_u3_agp_info = { static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_agp_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_agp_info = { @@ -488,8 +494,10 @@ static const TypeInfo pci_unin_agp_info = { static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sbc->init = pci_unin_internal_init_device; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_internal_info = { diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 2fdada4e8f..64c93d83d6 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -200,8 +200,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, return pci_get_long(dev->msix_pba + addr); } +static void msix_pba_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + static const MemoryRegionOps msix_pba_mmio_ops = { .read = msix_pba_mmio_read, + .write = msix_pba_mmio_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index b0bf54061f..168b9cc56b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -847,6 +847,9 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCIConfigWriteFunc *config_write = pc->config_write; Error *local_err = NULL; AddressSpace *dma_as; + DeviceState *dev = DEVICE(pci_dev); + + pci_dev->bus = bus; if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); @@ -864,9 +867,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); return NULL; + } else if (dev->hotplugged && + pci_get_function_0(pci_dev)) { + error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," + " new func %s cannot be exposed to guest.", + PCI_SLOT(devfn), + bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name, + name); + + return NULL; } - pci_dev->bus = bus; pci_dev->devfn = devfn; dma_as = pci_device_iommu_address_space(pci_dev); @@ -2454,6 +2465,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range) pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); } +static bool pcie_has_upstream_port(PCIDevice *dev) +{ + PCIDevice *parent_dev = pci_bridge_get_device(dev->bus); + + /* Device associated with an upstream port. + * As there are several types of these, it's easier to check the + * parent device: upstream ports are always connected to + * root or downstream ports. + */ + return parent_dev && + pci_is_express(parent_dev) && + parent_dev->exp.exp_cap && + (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT || + pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM); +} + +PCIDevice *pci_get_function_0(PCIDevice *pci_dev) +{ + if(pcie_has_upstream_port(pci_dev)) { + /* With an upstream PCIe port, we only support 1 device at slot 0 */ + return pci_dev->bus->devices[0]; + } else { + /* Other bus types might support multiple devices at slots 0-31 */ + return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)]; + } +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 3e26f9256c..49f59a5dbc 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -20,6 +20,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" +#include "hw/pci/pci_bus.h" #include "trace.h" /* debug PCI */ @@ -52,6 +53,13 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, uint32_t limit, uint32_t val, uint32_t len) { assert(len <= 4); + /* non-zero functions are only exposed when function 0 is present, + * allowing direct removal of unexposed functions. + */ + if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { + return; + } + trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), addr, val); pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); @@ -63,6 +71,13 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, uint32_t ret; assert(len <= 4); + /* non-zero functions are only exposed when function 0 is present, + * allowing direct removal of unexposed functions. + */ + if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { + return ~0x0; + } + ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), addr, ret); diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 6e28985bd1..32c65c27a4 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -249,25 +249,43 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - /* TODO: multifunction hot-plug. - * Right now, only a device of function = 0 is allowed to be - * hot plugged/unplugged. + /* To enable multifunction hot-plug, we just ensure the function + * 0 added last. When function 0 is added, we set the sltsta and + * inform OS via event notification. */ - assert(PCI_FUNC(pci_dev->devfn) == 0); + if (pci_get_function_0(pci_dev)) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), + PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); + } +} - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), - PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); +static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) +{ + object_unparent(OBJECT(dev)); } void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { uint8_t *exp_cap; + PCIDevice *pci_dev = PCI_DEVICE(dev); + PCIBus *bus = pci_dev->bus; pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + /* In case user cancel the operation of multi-function hot-add, + * remove the function that is unexposed to guest individually, + * without interaction with guest. + */ + if (pci_dev->devfn && + !bus->devices[0]) { + pcie_unplug_device(bus, pci_dev, NULL); + + return; + } + pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev)); } @@ -378,11 +396,6 @@ void pcie_cap_slot_reset(PCIDevice *dev) hotplug_event_update_event_status(dev); } -static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) -{ - object_unparent(OBJECT(dev)); -} - void pcie_cap_slot_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index d95222bd7d..5ad28f75cf 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -42,11 +42,9 @@ #include "sysemu/arch_init.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" +#include "trace.h" #include "elf.h" -//#define HARD_DEBUG_PPC_IO -//#define DEBUG_PPC_IO - /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -57,26 +55,6 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 -#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) -#define DEBUG_PPC_IO -#endif - -#if defined (HARD_DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -do { \ - if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \ - qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \ - } else { \ - printf("%s : " fmt, __func__ , ## __VA_ARGS__); \ - } \ -} while (0) -#elif defined (DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__) -#else -#define PPC_IO_DPRINTF(fmt, ...) do { } while (0) -#endif - /* Constants for devices init */ static const int ide_iobase[2] = { 0x1f0, 0x170 }; static const int ide_iobase2[2] = { 0x3f6, 0x376 }; @@ -199,8 +177,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) { sysctrl_t *sysctrl = opaque; - PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, val); + trace_prep_io_800_writeb(addr - PPC_IO_BASE, val); switch (addr) { case 0x0092: /* Special port 92 */ @@ -327,8 +304,7 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); break; } - PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, retval); + trace_prep_io_800_readb(addr - PPC_IO_BASE, retval); return retval; } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3852ad1967..0ed8527969 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -597,6 +597,24 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + /* Note: we keep CI large pages off for now because a 64K capable guest + * provisioned with large pages might otherwise try to map a qemu + * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages + * even if that qemu runs on a 4k host. + * + * We can later add this bit back when we are confident this is not + * an issue (!HV KVM or 64K host) + */ + uint8_t pa_features_206[] = { 6, 0, + 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; + uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + uint8_t *pa_features; + size_t pa_size; + _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); @@ -625,6 +643,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr))); _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -662,6 +681,19 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } + /* Do the ibm,pa-features property, adjust it for ci-large-pages */ + if (env->mmu_model == POWERPC_MMU_2_06) { + pa_features = pa_features_206; + pa_size = sizeof(pa_features_206); + } else /* env->mmu_model == POWERPC_MMU_2_07 */ { + pa_features = pa_features_207; + pa_size = sizeof(pa_features_207); + } + if (env->ci_large_pages) { + pa_features[3] |= 0x20; + } + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); @@ -979,7 +1011,7 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) -static void spapr_reset_htab(sPAPRMachineState *spapr) +static void spapr_alloc_htab(sPAPRMachineState *spapr) { long shift; int index; @@ -992,20 +1024,47 @@ static void spapr_reset_htab(sPAPRMachineState *spapr) if (shift > 0) { /* Kernel handles htab, we don't need to allocate one */ + if (shift != spapr->htab_shift) { + error_setg(&error_abort, "Failed to allocate HTAB of requested size, try with smaller maxmem"); + } + spapr->htab_shift = shift; kvmppc_kern_htab = true; + } else { + /* Allocate htab */ + spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr)); + + /* And clear it */ + memset(spapr->htab, 0, HTAB_SIZE(spapr)); + + for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { + DIRTY_HPTE(HPTE(spapr->htab, index)); + } + } +} + +/* + * Clear HTAB entries during reset. + * + * If host kernel has allocated HTAB, KVM_PPC_ALLOCATE_HTAB ioctl is + * used to clear HTAB. Otherwise QEMU-allocated HTAB is cleared manually. + */ +static void spapr_reset_htab(sPAPRMachineState *spapr) +{ + long shift; + int index; + + shift = kvmppc_reset_htab(spapr->htab_shift); + if (shift > 0) { + if (shift != spapr->htab_shift) { + error_setg(&error_abort, "Requested HTAB allocation failed during reset"); + } /* Tell readers to update their file descriptor */ if (spapr->htab_fd >= 0) { spapr->htab_fd_stale = true; } } else { - if (!spapr->htab) { - /* Allocate an htab if we don't yet have one */ - spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr)); - } - - /* And clear it */ memset(spapr->htab, 0, HTAB_SIZE(spapr)); for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { @@ -1710,6 +1769,7 @@ static void ppc_spapr_init(MachineState *machine) } spapr->htab_shift++; } + spapr_alloc_htab(spapr); /* Set up Interrupt Controller before we create the VCPUs */ spapr->icp = xics_system_init(machine, @@ -2097,7 +2157,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, false, &local_err); + pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); if (local_err) { goto out; } @@ -2225,7 +2285,11 @@ static const TypeInfo spapr_machine_info = { }, }; +#define SPAPR_COMPAT_2_4 \ + HW_COMPAT_2_4 + #define SPAPR_COMPAT_2_3 \ + SPAPR_COMPAT_2_4 \ HW_COMPAT_2_3 \ {\ .driver = "spapr-pci-host-bridge",\ @@ -2339,11 +2403,16 @@ static const TypeInfo spapr_machine_2_3_info = { static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data) { + static GlobalProperty compat_props[] = { + SPAPR_COMPAT_2_4 + { /* end of list */ } + }; MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant) v2.4"; mc->alias = "pseries"; mc->is_default = 0; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_4_info = { diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index f61504e0c5..ed28565d8c 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -146,7 +146,7 @@ static int spapr_tce_table_realize(DeviceState *dev) tcet->table = kvmppc_create_spapr_tce(tcet->liobn, window_size, &tcet->fd, - tcet->vfio_accel); + tcet->need_vfio); } if (!tcet->table) { @@ -168,11 +168,43 @@ static int spapr_tce_table_realize(DeviceState *dev) return 0; } +void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) +{ + size_t table_size = tcet->nb_table * sizeof(uint64_t); + void *newtable; + + if (need_vfio == tcet->need_vfio) { + /* Nothing to do */ + return; + } + + if (!need_vfio) { + /* FIXME: We don't support transition back to KVM accelerated + * TCEs yet */ + return; + } + + tcet->need_vfio = true; + + if (tcet->fd < 0) { + /* Table is already in userspace, nothing to be do */ + return; + } + + newtable = g_malloc(table_size); + memcpy(newtable, tcet->table, table_size); + + kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table); + + tcet->fd = -1; + tcet->table = newtable; +} + sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint64_t bus_offset, uint32_t page_shift, uint32_t nb_table, - bool vfio_accel) + bool need_vfio) { sPAPRTCETable *tcet; char tmp[64]; @@ -192,7 +224,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, tcet->bus_offset = bus_offset; tcet->page_shift = page_shift; tcet->nb_table = nb_table; - tcet->vfio_accel = vfio_accel; + tcet->need_vfio = need_vfio; snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 617b7f3fdd..55fa8db9e2 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1083,6 +1083,12 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, void *fdt = NULL; int fdt_start_offset = 0, fdt_size; + if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn); + + spapr_tce_set_need_vfio(tcet, true); + } + if (dev->hotplugged) { fdt = create_device_tree(&fdt_size); fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); @@ -1387,7 +1393,7 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) sPAPRTCETable *tcet; uint32_t nb_table; - nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT; + nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT; tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false); if (!tcet) { @@ -1397,7 +1403,7 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) } /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, 0, + memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr, spapr_tce_get_iommu(tcet)); } @@ -1430,6 +1436,9 @@ static Property spapr_phb_properties[] = { SPAPR_PCI_IO_WIN_SIZE), DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled, true), + /* Default DMA window is 0..1GB */ + DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0), + DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index faba773592..84221f4f57 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -20,6 +20,7 @@ #include "qemu/config-file.h" #include "s390-pci-bus.h" #include "hw/s390x/storage-keys.h" +#include "hw/compat.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" @@ -236,6 +237,7 @@ static const TypeInfo ccw_machine_info = { }; #define CCW_COMPAT_2_4 \ + HW_COMPAT_2_4 \ {\ .driver = TYPE_S390_SKEYS,\ .property = "migration-enabled",\ diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 891424fae9..f4f5140a4a 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -750,7 +750,6 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) len = n+8; resp_data = g_malloc0(len); - memset(resp_data, 0, len); stl_be_p(resp_data, n); i = found_lun0 ? 8 : 16; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 1248fd93ee..0d8d71e245 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -60,7 +60,7 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, r = g_new(VirtIOSCSIVring, 1); r->host_notifier = *virtio_queue_get_host_notifier(vq); r->guest_notifier = *virtio_queue_get_guest_notifier(vq); - aio_set_event_notifier(s->ctx, &r->host_notifier, handler); + aio_set_event_notifier(s->ctx, &r->host_notifier, true, handler); r->parent = s; @@ -71,7 +71,7 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, return r; fail_vring: - aio_set_event_notifier(s->ctx, &r->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &r->host_notifier, true, NULL); k->set_host_notifier(qbus->parent, n, false); g_free(r); return NULL; @@ -162,14 +162,17 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s) int i; if (s->ctrl_vring) { - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, + true, NULL); } if (s->event_vring) { - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, + true, NULL); } if (s->cmd_vrings) { for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, + true, NULL); } } } @@ -290,10 +293,13 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) aio_context_acquire(s->ctx); - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, + true, NULL); + aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, + true, NULL); for (i = 0; i < vs->conf.num_queues; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); + aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, + true, NULL); } blk_drain_all(); /* ensure there are no in-flight requests */ diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 20885fb7f1..76554011cb 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -205,20 +205,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) assert(n < vs->conf.num_queues); req = virtio_scsi_init_req(s, vs->cmd_vqs[n]); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); - /* TODO: add a way for SCSIBusInfo's load_request to fail, - * and fail migration instead of asserting here. - * When we do, we might be able to re-enable NDEBUG below. - */ -#ifdef NDEBUG -#error building with NDEBUG is not supported -#endif - assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg)); - assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg)); - virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, - req->elem.in_num, 1); - virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, - req->elem.out_num, 0); + virtqueue_map(&req->elem); if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) { @@ -665,6 +653,11 @@ static void virtio_scsi_reset(VirtIODevice *vdev) static void virtio_scsi_save(QEMUFile *f, void *opaque) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + if (s->dataplane_started) { + virtio_scsi_dataplane_stop(s); + } virtio_save(vdev, f); } diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 2209ef1d52..b430d56687 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -28,7 +28,7 @@ #include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" enum { ENABLE_CMD_TX = (1<<0), diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 35d8033402..5bc47193f9 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -18,7 +18,7 @@ */ #include "hw/hw.h" #include "hw/arm/omap.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" struct omap_mmc_s { qemu_irq irq; diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 5242176a33..326c53ad92 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -10,7 +10,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sysbus.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" //#define DEBUG_PL181 1 diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index d1fe6d58e8..b217080075 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -12,7 +12,7 @@ #include "hw/hw.h" #include "hw/arm/pxa.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" #include "hw/qdev.h" struct PXA2xxMMCIState { diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 393a75c0bc..ce4d44be91 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -31,7 +31,7 @@ #include "hw/hw.h" #include "sysemu/block-backend.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" #include "qemu/bitmap.h" //#define DEBUG_SD 1 diff --git a/hw/sd/sdhci.h b/hw/sd/sdhci-internal.h index e2de92d553..c712daf4ee 100644 --- a/hw/sd/sdhci.h +++ b/hw/sd/sdhci-internal.h @@ -21,15 +21,10 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#ifndef SDHCI_INTERNAL_H +#define SDHCI_INTERNAL_H -#ifndef SDHCI_H -#define SDHCI_H - -#include "qemu-common.h" -#include "hw/block/block.h" -#include "hw/pci/pci.h" -#include "hw/sysbus.h" -#include "hw/sd.h" +#include "hw/sd/sdhci.h" /* R/W SDMA System Address register 0x0 */ #define SDHC_SYSAD 0x00 @@ -232,66 +227,6 @@ enum { sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */ }; -/* SD/MMC host controller state */ -typedef struct SDHCIState { - union { - PCIDevice pcidev; - SysBusDevice busdev; - }; - SDState *card; - MemoryRegion iomem; - BlockConf conf; - - QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ - QEMUTimer *transfer_timer; - qemu_irq eject_cb; - qemu_irq ro_cb; - qemu_irq irq; - - uint32_t sdmasysad; /* SDMA System Address register */ - uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */ - uint16_t blkcnt; /* Blocks count for current transfer */ - uint32_t argument; /* Command Argument Register */ - uint16_t trnmod; /* Transfer Mode Setting Register */ - uint16_t cmdreg; /* Command Register */ - uint32_t rspreg[4]; /* Response Registers 0-3 */ - uint32_t prnsts; /* Present State Register */ - uint8_t hostctl; /* Host Control Register */ - uint8_t pwrcon; /* Power control Register */ - uint8_t blkgap; /* Block Gap Control Register */ - uint8_t wakcon; /* WakeUp Control Register */ - uint16_t clkcon; /* Clock control Register */ - uint8_t timeoutcon; /* Timeout Control Register */ - uint8_t admaerr; /* ADMA Error Status Register */ - uint16_t norintsts; /* Normal Interrupt Status Register */ - uint16_t errintsts; /* Error Interrupt Status Register */ - uint16_t norintstsen; /* Normal Interrupt Status Enable Register */ - uint16_t errintstsen; /* Error Interrupt Status Enable Register */ - uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */ - uint16_t errintsigen; /* Error Interrupt Signal Enable Register */ - uint16_t acmd12errsts; /* Auto CMD12 error status register */ - uint64_t admasysaddr; /* ADMA System Address Register */ - - uint32_t capareg; /* Capabilities Register */ - uint32_t maxcurr; /* Maximum Current Capabilities Register */ - uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */ - uint32_t buf_maxsz; - uint16_t data_count; /* current element in FIFO buffer */ - uint8_t stopped_state;/* Current SDHC state */ - /* Buffer Data Port Register - virtual access point to R and W buffers */ - /* Software Reset Register - always reads as 0 */ - /* Force Event Auto CMD12 Error Interrupt Reg - write only */ - /* Force Event Error Interrupt Register- write only */ - /* RO Host Controller Version Register always reads as 0x2401 */ -} SDHCIState; - extern const VMStateDescription sdhci_vmstate; -#define TYPE_PCI_SDHCI "sdhci-pci" -#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI) - -#define TYPE_SYSBUS_SDHCI "generic-sdhci" -#define SYSBUS_SDHCI(obj) \ - OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI) - -#endif /* SDHCI_H */ +#endif diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 7f73527d44..d70d1a6ab9 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -29,8 +29,7 @@ #include "sysemu/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" - -#include "sdhci.h" +#include "sdhci-internal.h" /* host controller debug messages */ #ifndef SDHC_DEBUG diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index e4b2d4f83b..c49ff62f56 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -13,7 +13,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/ssi.h" -#include "hw/sd.h" +#include "hw/sd/sd.h" //#define DEBUG_SSI_SD 1 diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index 9649851526..967be4ad27 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -16,8 +16,17 @@ #include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" -#define DEBUG_TIMER 0 -#if DEBUG_TIMER +#ifndef DEBUG_IMX_EPIT +#define DEBUG_IMX_EPIT 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_EPIT) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_EPIT, \ + __func__, ##args); \ + } \ + } while (0) static char const *imx_epit_reg_name(uint32_t reg) { @@ -37,24 +46,6 @@ static char const *imx_epit_reg_name(uint32_t reg) } } -# define DPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0) -#else -# define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - /* * Exact clock frequencies vary from board to board. * These are typical. @@ -136,9 +127,8 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); uint32_t reg_value = 0; - uint32_t reg = offset >> 2; - switch (reg) { + switch (offset >> 2) { case 0: /* Control Register */ reg_value = s->cr; break; @@ -161,11 +151,12 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); break; } - DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value); + DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(offset >> 2), reg_value); return reg_value; } @@ -190,12 +181,12 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); - uint32_t reg = offset >> 2; uint64_t oldcr; - DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value); + DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2), + (uint32_t)value); - switch (reg) { + switch (offset >> 2) { case 0: /* CR */ oldcr = s->cr; @@ -271,7 +262,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); break; } diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 4bac67d333..7257f4201a 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -16,11 +16,17 @@ #include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" -/* - * Define to 1 for debug messages - */ -#define DEBUG_TIMER 0 -#if DEBUG_TIMER +#ifndef DEBUG_IMX_GPT +#define DEBUG_IMX_GPT 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_GPT) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \ + __func__, ##args); \ + } \ + } while (0) static char const *imx_gpt_reg_name(uint32_t reg) { @@ -50,24 +56,6 @@ static char const *imx_gpt_reg_name(uint32_t reg) } } -# define DPRINTF(fmt, args...) \ - do { printf("%s: " fmt , __func__, ##args); } while (0) -#else -# define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - static const VMStateDescription vmstate_imx_timer_gpt = { .name = TYPE_IMX_GPT, .version_id = 3, @@ -224,9 +212,8 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) { IMXGPTState *s = IMX_GPT(opaque); uint32_t reg_value = 0; - uint32_t reg = offset >> 2; - switch (reg) { + switch (offset >> 2) { case 0: /* Control Register */ reg_value = s->cr; break; @@ -256,12 +243,14 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) break; case 7: /* input Capture Register 1 */ - qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n"); + qemu_log_mask(LOG_UNIMP, "[%s]%s: icr1 feature is not implemented\n", + TYPE_IMX_GPT, __func__); reg_value = s->icr1; break; case 8: /* input Capture Register 2 */ - qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n"); + qemu_log_mask(LOG_UNIMP, "[%s]%s: icr2 feature is not implemented\n", + TYPE_IMX_GPT, __func__); reg_value = s->icr2; break; @@ -271,11 +260,12 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); break; } - DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(reg), reg_value); + DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value); return reg_value; } @@ -322,12 +312,11 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, { IMXGPTState *s = IMX_GPT(opaque); uint32_t oldreg; - uint32_t reg = offset >> 2; - DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg), + DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2), (uint32_t)value); - switch (reg) { + switch (offset >> 2) { case 0: oldreg = s->cr; s->cr = value & ~0x7c14; @@ -403,7 +392,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, break; default: - IPRINTF("Bad offset %x\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); break; } } diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 9a4e7dc0cb..597d8fd184 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -613,20 +613,22 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) return; } - bdrv_add_key(blk_bs(blk), NULL, &err); - if (err) { - if (monitor_cur_is_qmp()) { - error_propagate(errp, err); - return; - } - error_free(err); - err = NULL; - if (cur_mon) { - monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), - usb_msd_password_cb, s); - s->dev.auto_attach = 0; - } else { - autostart = 0; + if (blk_bs(blk)) { + bdrv_add_key(blk_bs(blk), NULL, &err); + if (err) { + if (monitor_cur_is_qmp()) { + error_propagate(errp, err); + return; + } + error_free(err); + err = NULL; + if (cur_mon) { + monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), + usb_msd_password_cb, s); + s->dev.auto_attach = 0; + } else { + autostart = 0; + } } } diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 78442ba980..83c84f1cd6 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -89,7 +89,7 @@ typedef struct VhostUserMsg { struct vhost_vring_state state; struct vhost_vring_addr addr; VhostUserMemory memory; - }; + } payload; } QEMU_PACKED VhostUserMsg; static VhostUserMsg m __attribute__ ((unused)); @@ -200,8 +200,8 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, VhostUserMsg msg = { .request = VHOST_USER_SET_LOG_BASE, .flags = VHOST_USER_VERSION, - .u64 = base, - .size = sizeof(m.u64), + .payload.u64 = base, + .size = sizeof(msg.payload.u64), }; if (shmfd && log->fd != -1) { @@ -247,17 +247,17 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, &ram_addr); fd = qemu_get_ram_fd(ram_addr); if (fd > 0) { - msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr; - msg.memory.regions[fd_num].memory_size = reg->memory_size; - msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; - msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr - + msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr; + msg.payload.memory.regions[fd_num].memory_size = reg->memory_size; + msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; + msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr - (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr); assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); fds[fd_num++] = fd; } } - msg.memory.nregions = fd_num; + msg.payload.memory.nregions = fd_num; if (!fd_num) { error_report("Failed initializing vhost-user memory map, " @@ -265,8 +265,8 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, return -1; } - msg.size = sizeof(m.memory.nregions); - msg.size += sizeof(m.memory.padding); + msg.size = sizeof(msg.payload.memory.nregions); + msg.size += sizeof(msg.payload.memory.padding); msg.size += fd_num * sizeof(VhostUserMemoryRegion); vhost_user_write(dev, &msg, fds, fd_num); @@ -280,8 +280,8 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev, VhostUserMsg msg = { .request = VHOST_USER_SET_VRING_ADDR, .flags = VHOST_USER_VERSION, - .addr = *addr, - .size = sizeof(*addr), + .payload.addr = *addr, + .size = sizeof(msg.payload.addr), }; vhost_user_write(dev, &msg, NULL, 0); @@ -303,8 +303,8 @@ static int vhost_set_vring(struct vhost_dev *dev, VhostUserMsg msg = { .request = request, .flags = VHOST_USER_VERSION, - .state = *ring, - .size = sizeof(*ring), + .payload.state = *ring, + .size = sizeof(msg.payload.state), }; vhost_user_write(dev, &msg, NULL, 0); @@ -345,8 +345,8 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, VhostUserMsg msg = { .request = VHOST_USER_GET_VRING_BASE, .flags = VHOST_USER_VERSION, - .state = *ring, - .size = sizeof(*ring), + .payload.state = *ring, + .size = sizeof(msg.payload.state), }; vhost_user_write(dev, &msg, NULL, 0); @@ -361,12 +361,12 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, return -1; } - if (msg.size != sizeof(m.state)) { + if (msg.size != sizeof(msg.payload.state)) { error_report("Received bad msg size."); return -1; } - *ring = msg.state; + *ring = msg.payload.state; return 0; } @@ -380,14 +380,14 @@ static int vhost_set_vring_file(struct vhost_dev *dev, VhostUserMsg msg = { .request = request, .flags = VHOST_USER_VERSION, - .u64 = file->index & VHOST_USER_VRING_IDX_MASK, - .size = sizeof(m.u64), + .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK, + .size = sizeof(msg.payload.u64), }; if (ioeventfd_enabled() && file->fd > 0) { fds[fd_num++] = file->fd; } else { - msg.u64 |= VHOST_USER_VRING_NOFD_MASK; + msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; } vhost_user_write(dev, &msg, fds, fd_num); @@ -412,8 +412,8 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64) VhostUserMsg msg = { .request = request, .flags = VHOST_USER_VERSION, - .u64 = u64, - .size = sizeof(m.u64), + .payload.u64 = u64, + .size = sizeof(msg.payload.u64), }; vhost_user_write(dev, &msg, NULL, 0); @@ -456,12 +456,12 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) return -1; } - if (msg.size != sizeof(m.u64)) { + if (msg.size != sizeof(msg.payload.u64)) { error_report("Received bad msg size."); return -1; } - *u64 = msg.u64; + *u64 = msg.payload.u64; return 0; } @@ -591,8 +591,8 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) VHOST_USER_PROTOCOL_F_RARP)) { msg.request = VHOST_USER_SEND_RARP; msg.flags = VHOST_USER_VERSION; - memcpy((char *)&msg.u64, mac_addr, 6); - msg.size = sizeof(m.u64); + memcpy((char *)&msg.payload.u64, mac_addr, 6); + msg.size = sizeof(msg.payload.u64); err = vhost_user_write(dev, &msg, NULL, 0); return err; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index d0bc72e0e4..939f802110 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -448,28 +448,59 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, return in_bytes <= in_total && out_bytes <= out_total; } -void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, - size_t num_sg, int is_write) +static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, + unsigned int *num_sg, unsigned int max_size, + int is_write) { unsigned int i; hwaddr len; - if (num_sg > VIRTQUEUE_MAX_SIZE) { - error_report("virtio: map attempt out of bounds: %zd > %d", - num_sg, VIRTQUEUE_MAX_SIZE); - exit(1); - } + /* Note: this function MUST validate input, some callers + * are passing in num_sg values received over the network. + */ + /* TODO: teach all callers that this can fail, and return failure instead + * of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(*num_sg <= max_size); - for (i = 0; i < num_sg; i++) { + for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); - if (sg[i].iov_base == NULL || len != sg[i].iov_len) { + if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); exit(1); } + if (len == sg[i].iov_len) { + continue; + } + if (*num_sg >= max_size) { + error_report("virtio: memory split makes iovec too large"); + exit(1); + } + memmove(sg + i + 1, sg + i, sizeof(*sg) * (*num_sg - i)); + memmove(addr + i + 1, addr + i, sizeof(*addr) * (*num_sg - i)); + assert(len < sg[i + 1].iov_len); + sg[i].iov_len = len; + addr[i + 1] += len; + sg[i + 1].iov_len -= len; + ++*num_sg; } } +void virtqueue_map(VirtQueueElement *elem) +{ + virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num, + MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)), + 1); + virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, + MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)), + 0); +} + int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) { unsigned int i, head, max; @@ -531,8 +562,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max); /* Now map what we have collected */ - virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1); - virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0); + virtqueue_map(elem); elem->index = head; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 2b54f52707..aa96288236 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -938,10 +938,18 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) dc->props = xen_pci_passthrough_properties; }; +static void xen_pci_passthrough_finalize(Object *obj) +{ + XenPCIPassthroughState *s = XEN_PT_DEVICE(obj); + + xen_pt_msix_delete(s); +} + static const TypeInfo xen_pci_passthrough_info = { .name = TYPE_XEN_PT_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPCIPassthroughState), + .instance_finalize = xen_pci_passthrough_finalize, .class_init = xen_pci_passthrough_class_init, }; diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 3bc22eb1d1..c545280085 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -305,6 +305,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s); int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base); void xen_pt_msix_delete(XenPCIPassthroughState *s); +void xen_pt_msix_unmap(XenPCIPassthroughState *s); int xen_pt_msix_update(XenPCIPassthroughState *s); int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index); void xen_pt_msix_disable(XenPCIPassthroughState *s); diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index 4a5bc11f30..0efee112fd 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -2079,7 +2079,7 @@ void xen_pt_config_delete(XenPCIPassthroughState *s) /* free MSI/MSI-X info table */ if (s->msix) { - xen_pt_msix_delete(s); + xen_pt_msix_unmap(s); } g_free(s->msi); diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index e3d71945cd..82de2bced3 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -610,7 +610,7 @@ error_out: return rc; } -void xen_pt_msix_delete(XenPCIPassthroughState *s) +void xen_pt_msix_unmap(XenPCIPassthroughState *s) { XenPTMSIX *msix = s->msix; @@ -627,6 +627,17 @@ void xen_pt_msix_delete(XenPCIPassthroughState *s) } memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio); +} + +void xen_pt_msix_delete(XenPCIPassthroughState *s) +{ + XenPTMSIX *msix = s->msix; + + if (!msix) { + return; + } + + object_unparent(OBJECT(&msix->mmio)); g_free(s->msix); s->msix = NULL; |