diff options
Diffstat (limited to 'hw')
48 files changed, 1653 insertions, 453 deletions
diff --git a/hw/acpi.c b/hw/acpi.c index 53e47d5857..856da81f0c 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -23,10 +23,14 @@ #include "hw/pc.h" #include "hw/acpi.h" #include "monitor/monitor.h" +#include "qemu/config-file.h" +#include "qapi/opts-visitor.h" +#include "qapi/dealloc-visitor.h" +#include "qapi-visit.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ - /* XXX why we have 2 length fields here? */ + /* allows easier parsing for fw_cfg clients */ char sig[4]; /* ACPI signature (4 ASCII characters) */ uint32_t length; /* Length of table, in bytes, including header */ uint8_t revision; /* ACPI Specification minor version # */ @@ -41,16 +45,29 @@ struct acpi_table_header { #define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header) #define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */ -static const char dfl_hdr[ACPI_TABLE_HDR_SIZE] = - "\0\0" /* fake _length (2) */ +static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] = "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */ "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */ "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */ ; -char *acpi_tables; +char unsigned *acpi_tables; size_t acpi_tables_len; +static QemuOptsList qemu_acpi_opts = { + .name = "acpi", + .implied_opt_name = "data", + .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head), + .desc = { { 0 } } /* validated with OptsVisitor */ +}; + +static void acpi_register_config(void) +{ + qemu_add_opts(&qemu_acpi_opts); +} + +machine_init(acpi_register_config); + static int acpi_checksum(const uint8_t *data, int len) { int sum, i; @@ -61,184 +78,235 @@ static int acpi_checksum(const uint8_t *data, int len) return (-sum) & 0xff; } -/* XXX fixme: this function uses obsolete argument parsing interface */ -int acpi_table_add(const char *t) -{ - char buf[1024], *p, *f; - unsigned long val; - size_t len, start, allen; - bool has_header; - int changed; - int r; - struct acpi_table_header hdr; - - r = 0; - r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0; - r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0; - switch (r) { - case 0: - buf[0] = '\0'; - /* fallthrough for default behavior */ - case 1: - has_header = false; - break; - case 2: - has_header = true; - break; - default: - fprintf(stderr, "acpitable: both data and file are specified\n"); - return -1; - } - if (!acpi_tables) { - allen = sizeof(uint16_t); - acpi_tables = g_malloc0(allen); +/* Install a copy of the ACPI table specified in @blob. + * + * If @has_header is set, @blob starts with the System Description Table Header + * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field + * is optionally overwritten from @hdrs. + * + * It is valid to call this function with + * (@blob == NULL && bloblen == 0 && !has_header). + * + * @hdrs->file and @hdrs->data are ignored. + * + * SIZE_MAX is considered "infinity" in this function. + * + * The number of tables that can be installed is not limited, but the 16-bit + * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX. + */ +static void acpi_table_install(const char unsigned *blob, size_t bloblen, + bool has_header, + const struct AcpiTableOptions *hdrs, + Error **errp) +{ + size_t body_start; + const char unsigned *hdr_src; + size_t body_size, acpi_payload_size; + struct acpi_table_header *ext_hdr; + unsigned changed_fields; + + /* Calculate where the ACPI table body starts within the blob, plus where + * to copy the ACPI table header from. + */ + if (has_header) { + /* _length | ACPI header in blob | blob body + * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ + * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size + * == body_start + * + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * acpi_payload_size == bloblen + */ + body_start = sizeof dfl_hdr; + + if (bloblen < body_start) { + error_setg(errp, "ACPI table claiming to have header is too " + "short, available: %zu, expected: %zu", bloblen, + body_start); + return; + } + hdr_src = blob; } else { - allen = acpi_tables_len; + /* _length | ACPI header in template | blob body + * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ + * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size + * == bloblen + * + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * acpi_payload_size + */ + body_start = 0; + hdr_src = dfl_hdr; } + body_size = bloblen - body_start; + acpi_payload_size = sizeof dfl_hdr + body_size; - start = allen; - acpi_tables = g_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE); - allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE; + if (acpi_payload_size > UINT16_MAX) { + error_setg(errp, "ACPI table too big, requested: %zu, max: %u", + acpi_payload_size, (unsigned)UINT16_MAX); + return; + } - /* now read in the data files, reallocating buffer as needed */ + /* We won't fail from here on. Initialize / extend the globals. */ + if (acpi_tables == NULL) { + acpi_tables_len = sizeof(uint16_t); + acpi_tables = g_malloc0(acpi_tables_len); + } - for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) { - int fd = open(f, O_RDONLY | O_BINARY); + acpi_tables = g_realloc(acpi_tables, acpi_tables_len + + ACPI_TABLE_PFX_SIZE + + sizeof dfl_hdr + body_size); - if (fd < 0) { - fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno)); - return -1; - } + ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len); + acpi_tables_len += ACPI_TABLE_PFX_SIZE; - for (;;) { - char data[8192]; - r = read(fd, data, sizeof(data)); - if (r == 0) { - break; - } else if (r > 0) { - acpi_tables = g_realloc(acpi_tables, allen + r); - memcpy(acpi_tables + allen, data, r); - allen += r; - } else if (errno != EINTR) { - fprintf(stderr, "can't read file %s: %s\n", - f, strerror(errno)); - close(fd); - return -1; - } - } + memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr); + acpi_tables_len += sizeof dfl_hdr; - close(fd); + if (blob != NULL) { + memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size); + acpi_tables_len += body_size; } - /* now fill in the header fields */ + /* increase number of tables */ + cpu_to_le16wu((uint16_t *)acpi_tables, + le16_to_cpupu((uint16_t *)acpi_tables) + 1u); - f = acpi_tables + start; /* start of the table */ - changed = 0; + /* Update the header fields. The strings need not be NUL-terminated. */ + changed_fields = 0; + ext_hdr->_length = cpu_to_le16(acpi_payload_size); - /* copy the header to temp place to align the fields */ - memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE); + if (hdrs->has_sig) { + strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig); + ++changed_fields; + } - /* length of the table minus our prefix */ - len = allen - start - ACPI_TABLE_PFX_SIZE; + if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) { + fprintf(stderr, + "warning: ACPI table has wrong length, header says " + "%" PRIu32 ", actual size %zu bytes\n", + le32_to_cpu(ext_hdr->length), acpi_payload_size); + } + ext_hdr->length = cpu_to_le32(acpi_payload_size); - hdr._length = cpu_to_le16(len); + if (hdrs->has_rev) { + ext_hdr->revision = hdrs->rev; + ++changed_fields; + } - if (get_param_value(buf, sizeof(buf), "sig", t)) { - /* strncpy is justified: the field need not be NUL-terminated. */ - strncpy(hdr.sig, buf, sizeof(hdr.sig)); - ++changed; + ext_hdr->checksum = 0; + + if (hdrs->has_oem_id) { + strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id); + ++changed_fields; + } + if (hdrs->has_oem_table_id) { + strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id, + sizeof ext_hdr->oem_table_id); + ++changed_fields; + } + if (hdrs->has_oem_rev) { + ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev); + ++changed_fields; + } + if (hdrs->has_asl_compiler_id) { + strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id, + sizeof ext_hdr->asl_compiler_id); + ++changed_fields; + } + if (hdrs->has_asl_compiler_rev) { + ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev); + ++changed_fields; } - /* length of the table including header, in bytes */ - if (has_header) { - /* check if actual length is correct */ - val = le32_to_cpu(hdr.length); - if (val != len) { - fprintf(stderr, - "warning: acpitable has wrong length," - " header says %lu, actual size %zu bytes\n", - val, len); - ++changed; - } + if (!has_header && changed_fields == 0) { + fprintf(stderr, "warning: ACPI table: no headers are specified\n"); } - /* we may avoid putting length here if has_header is true */ - hdr.length = cpu_to_le32(len); - if (get_param_value(buf, sizeof(buf), "rev", t)) { - val = strtoul(buf, &p, 0); - if (val > 255 || *p) { - fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf); - return -1; - } - hdr.revision = (uint8_t)val; - ++changed; + /* recalculate checksum */ + ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr + + ACPI_TABLE_PFX_SIZE, acpi_payload_size); +} + +void acpi_table_add(const QemuOpts *opts, Error **errp) +{ + AcpiTableOptions *hdrs = NULL; + Error *err = NULL; + char **pathnames = NULL; + char **cur; + size_t bloblen = 0; + char unsigned *blob = NULL; + + { + OptsVisitor *ov; + + ov = opts_visitor_new(opts); + visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err); + opts_visitor_cleanup(ov); } - if (get_param_value(buf, sizeof(buf), "oem_id", t)) { - /* strncpy is justified: the field need not be NUL-terminated. */ - strncpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); - ++changed; + if (err) { + goto out; + } + if (hdrs->has_file == hdrs->has_data) { + error_setg(&err, "'-acpitable' requires one of 'data' or 'file'"); + goto out; } - if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { - /* strncpy is justified: the field need not be NUL-terminated. */ - strncpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); - ++changed; + pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0); + if (pathnames == NULL || pathnames[0] == NULL) { + error_setg(&err, "'-acpitable' requires at least one pathname"); + goto out; } - if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { - val = strtol(buf, &p, 0); - if (*p) { - fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf); - return -1; + /* now read in the data files, reallocating buffer as needed */ + for (cur = pathnames; *cur; ++cur) { + int fd = open(*cur, O_RDONLY | O_BINARY); + + if (fd < 0) { + error_setg(&err, "can't open file %s: %s", *cur, strerror(errno)); + goto out; } - hdr.oem_revision = cpu_to_le32(val); - ++changed; - } - if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { - /* strncpy is justified: the field need not be NUL-terminated. */ - strncpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); - ++changed; - } + for (;;) { + char unsigned data[8192]; + ssize_t r; - if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { - val = strtol(buf, &p, 0); - if (*p) { - fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n", - "asl_compiler_rev", buf); - return -1; + r = read(fd, data, sizeof data); + if (r == 0) { + break; + } else if (r > 0) { + blob = g_realloc(blob, bloblen + r); + memcpy(blob + bloblen, data, r); + bloblen += r; + } else if (errno != EINTR) { + error_setg(&err, "can't read file %s: %s", + *cur, strerror(errno)); + close(fd); + goto out; + } } - hdr.asl_compiler_revision = cpu_to_le32(val); - ++changed; - } - if (!has_header && !changed) { - fprintf(stderr, "warning: acpitable: no table headers are specified\n"); + close(fd); } + acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, &err); - /* now calculate checksum of the table, complete with the header */ - /* we may as well leave checksum intact if has_header is true */ - /* alternatively there may be a way to set cksum to a given value */ - hdr.checksum = 0; /* for checksum calculation */ +out: + g_free(blob); + g_strfreev(pathnames); - /* put header back */ - memcpy(f, &hdr, sizeof(hdr)); + if (hdrs != NULL) { + QapiDeallocVisitor *dv; - if (changed || !has_header || 1) { - ((struct acpi_table_header *)f)->checksum = - acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len); + dv = qapi_dealloc_visitor_new(); + visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL, + NULL); + qapi_dealloc_visitor_cleanup(dv); } - /* increase number of tables */ - (*(uint16_t *)acpi_tables) = - cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1); - - acpi_tables_len = allen; - return 0; - + error_propagate(errp, err); } static void acpi_notify_wakeup(Notifier *notifier, void *data) @@ -472,8 +540,9 @@ static const MemoryRegionOps acpi_pm_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent) +void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val) { + ar->pm1.cnt.s4_val = s4_val; ar->wakeup.notify = acpi_notify_wakeup; qemu_register_wakeup_notifier(&ar->wakeup); memory_region_init_io(&ar->pm1.cnt.io, &acpi_pm_cnt_ops, ar, "acpi-cnt", 2); diff --git a/hw/acpi.h b/hw/acpi.h index c3628d070d..e18ef28fd3 100644 --- a/hw/acpi.h +++ b/hw/acpi.h @@ -142,7 +142,7 @@ void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, MemoryRegion *parent); /* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */ -void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent); +void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val); void acpi_pm1_cnt_update(ACPIREGS *ar, bool sci_enable, bool sci_disable); void acpi_pm1_cnt_reset(ACPIREGS *ar); diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c index 29f84ffb45..7b34a03866 100644 --- a/hw/acpi_ich9.c +++ b/hw/acpi_ich9.c @@ -212,7 +212,7 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); - acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io); + acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2); acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); memory_region_init_io(&pm->io_gpe, &ich9_gpe_ops, pm, "apci-gpe0", diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 7a4b712919..48a32b5aa0 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -418,7 +418,7 @@ static int piix4_pm_initfn(PCIDevice *dev) acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io); + acpi_pm1_cnt_init(&s->ar, &s->io, s->s4_val); acpi_gpe_init(&s->ar, GPE_LEN); s->powerdown_notifier.notify = piix4_pm_powerdown_req; diff --git a/hw/apic_common.c b/hw/apic_common.c index d0c261602c..37985097ca 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -218,7 +218,7 @@ static void apic_reset_common(DeviceState *d) bool bsp; bsp = cpu_is_bsp(s->cpu); - s->apicbase = 0xfee00000 | + s->apicbase = APIC_DEFAULT_ADDRESS | (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; s->vapic_paddr = 0; diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 6747c1c547..b28e7d372f 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -129,8 +129,6 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level) /* TODO: this seems to actually be connected to the menelaus, to * which also both MMC slots connect. */ omap_mmc_enable((struct omap_mmc_s *) opaque, !level); - - printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1); } static void n8x0_gpio_setup(struct n800_s *s) @@ -428,9 +426,6 @@ struct mipid_s { static void mipid_reset(struct mipid_s *s) { - if (!s->sleep) - fprintf(stderr, "%s: Display off\n", __FUNCTION__); - s->pm = 0; s->cmd = 0; @@ -578,11 +573,9 @@ static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len) case 0x28: /* DISPOFF */ s->onoff = 0; - fprintf(stderr, "%s: Display off\n", __FUNCTION__); break; case 0x29: /* DISPON */ s->onoff = 1; - fprintf(stderr, "%s: Display on\n", __FUNCTION__); break; case 0x2a: /* CASET */ @@ -669,7 +662,8 @@ static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len) default: bad_cmd: - fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unknown command %02x\n", __func__, s->cmd); break; } @@ -1347,7 +1341,6 @@ static void n8x0_init(QEMUMachineInitArgs *args, if (option_rom[0].name && (args->boot_device[0] == 'n' || !args->kernel_filename)) { - int rom_size; uint8_t nolo_tags[0x10000]; /* No, wait, better start at the ROM. */ s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; @@ -1361,10 +1354,9 @@ static void n8x0_init(QEMUMachineInitArgs *args, * * The code above is for loading the `zImage' file from Nokia * images. */ - rom_size = load_image_targphys(option_rom[0].name, - OMAP2_Q2_BASE + 0x400000, - sdram_size - 0x400000); - printf("%i bytes of image loaded\n", rom_size); + load_image_targphys(option_rom[0].name, + OMAP2_Q2_BASE + 0x400000, + sdram_size - 0x400000); n800_setup_nolo_tags(nolo_tags); cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 7467cca4f7..b7ca511d45 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1981,9 +1981,11 @@ static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000); memory_region_add_subregion(sysmem, base, &s->iomem); - if (chr) + if (chr) { + qemu_chr_fe_claim_no_fail(chr); qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, pxa2xx_fir_rx, pxa2xx_fir_event, s); + } register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save, pxa2xx_fir_load, s); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 6f362865f9..5b9257a9de 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -86,8 +86,7 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, spi = (SSIBus *)qdev_get_child_bus(dev, bus_name); for (j = 0; j < num_ss; ++j) { - flash_dev = ssi_create_slave_no_init(spi, "n25q128"); - qdev_init_nofail(flash_dev); + flash_dev = ssi_create_slave(spi, "n25q128"); cs_line = qdev_get_gpio_in(flash_dev, 0); sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line); diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c index f2dc8bf555..71594f1f87 100644 --- a/hw/arm_gic_common.c +++ b/hw/arm_gic_common.c @@ -20,90 +20,65 @@ #include "hw/arm_gic_internal.h" -static void gic_save(QEMUFile *f, void *opaque) +static void gic_pre_save(void *opaque) { GICState *s = (GICState *)opaque; ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); - int i; - int j; if (c->pre_save) { c->pre_save(s); } - - qemu_put_be32(f, s->enabled); - for (i = 0; i < s->num_cpu; i++) { - qemu_put_be32(f, s->cpu_enabled[i]); - for (j = 0; j < GIC_INTERNAL; j++) { - qemu_put_be32(f, s->priority1[j][i]); - } - for (j = 0; j < s->num_irq; j++) { - qemu_put_be32(f, s->last_active[j][i]); - } - qemu_put_be32(f, s->priority_mask[i]); - qemu_put_be32(f, s->running_irq[i]); - qemu_put_be32(f, s->running_priority[i]); - qemu_put_be32(f, s->current_pending[i]); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - qemu_put_be32(f, s->priority2[i]); - } - for (i = 0; i < s->num_irq; i++) { - qemu_put_be32(f, s->irq_target[i]); - qemu_put_byte(f, s->irq_state[i].enabled); - qemu_put_byte(f, s->irq_state[i].pending); - qemu_put_byte(f, s->irq_state[i].active); - qemu_put_byte(f, s->irq_state[i].level); - qemu_put_byte(f, s->irq_state[i].model); - qemu_put_byte(f, s->irq_state[i].trigger); - } } -static int gic_load(QEMUFile *f, void *opaque, int version_id) +static int gic_post_load(void *opaque, int version_id) { GICState *s = (GICState *)opaque; ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); - int i; - int j; - - if (version_id != 3) { - return -EINVAL; - } - - s->enabled = qemu_get_be32(f); - for (i = 0; i < s->num_cpu; i++) { - s->cpu_enabled[i] = qemu_get_be32(f); - for (j = 0; j < GIC_INTERNAL; j++) { - s->priority1[j][i] = qemu_get_be32(f); - } - for (j = 0; j < s->num_irq; j++) { - s->last_active[j][i] = qemu_get_be32(f); - } - s->priority_mask[i] = qemu_get_be32(f); - s->running_irq[i] = qemu_get_be32(f); - s->running_priority[i] = qemu_get_be32(f); - s->current_pending[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - s->priority2[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq; i++) { - s->irq_target[i] = qemu_get_be32(f); - s->irq_state[i].enabled = qemu_get_byte(f); - s->irq_state[i].pending = qemu_get_byte(f); - s->irq_state[i].active = qemu_get_byte(f); - s->irq_state[i].level = qemu_get_byte(f); - s->irq_state[i].model = qemu_get_byte(f); - s->irq_state[i].trigger = qemu_get_byte(f); - } if (c->post_load) { c->post_load(s); } - return 0; } +static const VMStateDescription vmstate_gic_irq_state = { + .name = "arm_gic_irq_state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(enabled, gic_irq_state), + VMSTATE_UINT8(pending, gic_irq_state), + VMSTATE_UINT8(active, gic_irq_state), + VMSTATE_UINT8(level, gic_irq_state), + VMSTATE_BOOL(model, gic_irq_state), + VMSTATE_BOOL(trigger, gic_irq_state), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_gic = { + .name = "arm_gic", + .version_id = 4, + .minimum_version_id = 4, + .pre_save = gic_pre_save, + .post_load = gic_post_load, + .fields = (VMStateField[]) { + VMSTATE_BOOL(enabled, GICState), + VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, NCPU), + VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, + vmstate_gic_irq_state, gic_irq_state), + VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), + VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, NCPU), + VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL), + VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, NCPU), + VMSTATE_UINT16_ARRAY(priority_mask, GICState, NCPU), + VMSTATE_UINT16_ARRAY(running_irq, GICState, NCPU), + VMSTATE_UINT16_ARRAY(running_priority, GICState, NCPU), + VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU), + VMSTATE_END_OF_LIST() + } +}; + static void arm_gic_common_realize(DeviceState *dev, Error **errp) { GICState *s = ARM_GIC_COMMON(dev); @@ -131,8 +106,6 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) num_irq); return; } - - register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); } static void arm_gic_common_reset(DeviceState *dev) @@ -149,7 +122,7 @@ static void arm_gic_common_reset(DeviceState *dev) s->current_pending[i] = 1023; s->running_irq[i] = 1023; s->running_priority[i] = 0x100; - s->cpu_enabled[i] = 0; + s->cpu_enabled[i] = false; } for (i = 0; i < 16; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); @@ -161,7 +134,7 @@ static void arm_gic_common_reset(DeviceState *dev) s->irq_target[i] = 1; } } - s->enabled = 0; + s->enabled = false; } static Property arm_gic_common_properties[] = { @@ -182,6 +155,7 @@ static void arm_gic_common_class_init(ObjectClass *klass, void *data) dc->reset = arm_gic_common_reset; dc->realize = arm_gic_common_realize; dc->props = arm_gic_common_properties; + dc->vmsd = &vmstate_gic; dc->no_user = 1; } diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h index 3e1928b7eb..99a3bc362b 100644 --- a/hw/arm_gic_internal.h +++ b/hw/arm_gic_internal.h @@ -45,14 +45,14 @@ #define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) #define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) #define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 +#define GIC_SET_MODEL(irq) s->irq_state[irq].model = true +#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false #define GIC_TEST_MODEL(irq) s->irq_state[irq].model #define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) #define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) #define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 -#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 +#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = true +#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = false #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger #define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ s->priority1[irq][cpu] : \ @@ -61,30 +61,30 @@ typedef struct gic_irq_state { /* The enable bits are only banked for per-cpu interrupts. */ - unsigned enabled:NCPU; - unsigned pending:NCPU; - unsigned active:NCPU; - unsigned level:NCPU; - unsigned model:1; /* 0 = N:N, 1 = 1:N */ - unsigned trigger:1; /* nonzero = edge triggered. */ + uint8_t enabled; + uint8_t pending; + uint8_t active; + uint8_t level; + bool model; /* 0 = N:N, 1 = 1:N */ + bool trigger; /* nonzero = edge triggered. */ } gic_irq_state; typedef struct GICState { SysBusDevice busdev; qemu_irq parent_irq[NCPU]; - int enabled; - int cpu_enabled[NCPU]; + bool enabled; + bool cpu_enabled[NCPU]; gic_irq_state irq_state[GIC_MAXIRQ]; - int irq_target[GIC_MAXIRQ]; - int priority1[GIC_INTERNAL][NCPU]; - int priority2[GIC_MAXIRQ - GIC_INTERNAL]; - int last_active[GIC_MAXIRQ][NCPU]; - - int priority_mask[NCPU]; - int running_irq[NCPU]; - int running_priority[NCPU]; - int current_pending[NCPU]; + uint8_t irq_target[GIC_MAXIRQ]; + uint8_t priority1[GIC_INTERNAL][NCPU]; + uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL]; + uint16_t last_active[GIC_MAXIRQ][NCPU]; + + uint16_t priority_mask[NCPU]; + uint16_t running_irq[NCPU]; + uint16_t running_priority[NCPU]; + uint16_t current_pending[NCPU]; uint32_t num_cpu; diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c index f59a9f11f0..317f5e43ed 100644 --- a/hw/arm_mptimer.c +++ b/hw/arm_mptimer.c @@ -253,14 +253,15 @@ static int arm_mptimer_init(SysBusDevice *dev) static const VMStateDescription vmstate_timerblock = { .name = "arm_mptimer_timerblock", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(count, TimerBlock), VMSTATE_UINT32(load, TimerBlock), VMSTATE_UINT32(control, TimerBlock), VMSTATE_UINT32(status, TimerBlock), VMSTATE_INT64(tick, TimerBlock), + VMSTATE_TIMER(timer, TimerBlock), VMSTATE_END_OF_LIST() } }; diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index d198cfd96e..235120024f 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -458,10 +458,10 @@ static void armv7m_nvic_reset(DeviceState *dev) * as enabled by default, and with a priority mask which allows * all interrupts through. */ - s->gic.cpu_enabled[0] = 1; + s->gic.cpu_enabled[0] = true; s->gic.priority_mask[0] = 0x100; /* The NVIC as a whole is always enabled. */ - s->gic.enabled = 1; + s->gic.enabled = true; systick_reset(s); } diff --git a/hw/bt-hci-csr.c b/hw/bt-hci-csr.c index e4ada3c731..55c819b085 100644 --- a/hw/bt-hci-csr.c +++ b/hw/bt-hci-csr.c @@ -439,6 +439,7 @@ CharDriverState *uart_hci_init(qemu_irq wakeup) s->chr.opaque = s; s->chr.chr_write = csrhci_write; s->chr.chr_ioctl = csrhci_ioctl; + s->chr.avail_connections = 1; s->hci = qemu_next_hci(); s->hci->opaque = s; diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c index 5426f10018..421ec998d6 100644 --- a/hw/cadence_uart.c +++ b/hw/cadence_uart.c @@ -157,6 +157,7 @@ static void uart_rx_reset(UartState *s) { s->rx_wpos = 0; s->rx_count = 0; + qemu_chr_accept_input(s->chr); s->r[R_SR] |= UART_SR_INTR_REMPTY; s->r[R_SR] &= ~UART_SR_INTR_RFUL; diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index c151c95c3e..cc95e5c3f0 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -13,8 +13,6 @@ #include "sysemu/kvm.h" #include "hw/apic_internal.h" -#define APIC_DEFAULT_ADDRESS 0xfee00000 - #define VAPIC_IO_PORT 0x7e #define VAPIC_CPU_SHIFT 7 diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b1e06fa0e7..ebbf059225 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -51,6 +51,7 @@ #include "exec/address-spaces.h" #include "sysemu/arch_init.h" #include "qemu/bitmap.h" +#include "qemu/config-file.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -71,6 +72,8 @@ #define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) #define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) +#define IO_APIC_DEFAULT_ADDRESS 0xfec00000 + #define E820_NR_ENTRIES 16 struct e820_entry { @@ -888,7 +891,7 @@ void pc_cpus_init(const char *cpu_model) void pc_acpi_init(const char *default_dsdt) { - char *filename = NULL, *arg = NULL; + char *filename; if (acpi_tables != NULL) { /* manually set via -acpitable, leave it alone */ @@ -898,15 +901,26 @@ void pc_acpi_init(const char *default_dsdt) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, default_dsdt); if (filename == NULL) { fprintf(stderr, "WARNING: failed to find %s\n", default_dsdt); - return; - } + } else { + char *arg; + QemuOpts *opts; + Error *err = NULL; - arg = g_strdup_printf("file=%s", filename); - if (acpi_table_add(arg) != 0) { - fprintf(stderr, "WARNING: failed to load %s\n", filename); + arg = g_strdup_printf("file=%s", filename); + + /* creates a deep copy of "arg" */ + opts = qemu_opts_parse(qemu_find_opts("acpi"), arg, 0); + g_assert(opts != NULL); + + acpi_table_add(opts, &err); + if (err) { + fprintf(stderr, "WARNING: failed to load %s: %s\n", filename, + error_get_pretty(err)); + error_free(err); + } + g_free(arg); + g_free(filename); } - g_free(arg); - g_free(filename); } void *pc_memory_init(MemoryRegion *system_memory, @@ -1158,7 +1172,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) } qdev_init_nofail(dev); d = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(d, 0, 0xfec00000); + sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); for (i = 0; i < IOAPIC_NUM_PINS; i++) { gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); diff --git a/hw/ipoctal232.c b/hw/ipoctal232.c index 1da6a99175..685fee2d2e 100644 --- a/hw/ipoctal232.c +++ b/hw/ipoctal232.c @@ -93,7 +93,6 @@ typedef struct SCC2698Block SCC2698Block; struct SCC2698Channel { IPOctalState *ipoctal; CharDriverState *dev; - char *devpath; bool rx_enabled; uint8_t mr[2]; uint8_t mr_idx; @@ -545,25 +544,12 @@ static int ipoctal_init(IPackDevice *ip) ch->ipoctal = s; /* Redirect IP-Octal channels to host character devices */ - if (ch->devpath) { - const char chr_name[] = "ipoctal"; - char label[ARRAY_SIZE(chr_name) + 2]; - static int index; - - snprintf(label, sizeof(label), "%s%d", chr_name, index); - - ch->dev = qemu_chr_new(label, ch->devpath, NULL); - - if (ch->dev) { - index++; - qemu_chr_add_handlers(ch->dev, hostdev_can_receive, - hostdev_receive, hostdev_event, ch); - DPRINTF("Redirecting channel %u to %s (%s)\n", - i, ch->devpath, label); - } else { - DPRINTF("Could not redirect channel %u to %s\n", - i, ch->devpath); - } + if (ch->dev) { + qemu_chr_add_handlers(ch->dev, hostdev_can_receive, + hostdev_receive, hostdev_event, ch); + DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); + } else { + DPRINTF("Could not redirect channel %u, no chardev set\n", i); } } @@ -571,14 +557,14 @@ static int ipoctal_init(IPackDevice *ip) } static Property ipoctal_properties[] = { - DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath), - DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath), - DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath), - DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath), - DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath), - DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath), - DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath), - DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath), + DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev), + DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev), + DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev), + DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev), + DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev), + DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev), + DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev), + DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 68a2cf2e69..af2789e9ac 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -292,6 +292,7 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd); exit(-1); } + qemu_chr_fe_claim_no_fail(chr); /* if MSI is supported we need multiple interrupts */ if (ivshmem_has_feature(s, IVSHMEM_MSI)) { diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c index aacf0f05ed..e5de801671 100644 --- a/hw/mcf_uart.c +++ b/hw/mcf_uart.c @@ -280,6 +280,7 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) s->chr = chr; s->irq = irq; if (chr) { + qemu_chr_fe_claim_no_fail(chr); qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, mcf_uart_event, s); } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index cfc02207ab..07dc808405 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -158,8 +158,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) for (i = 0; i < NUM_SPI_FLASHES; i++) { qemu_irq cs_line; - dev = ssi_create_slave_no_init(spi, "n25q128"); - qdev_init_nofail(dev); + dev = ssi_create_slave(spi, "n25q128"); cs_line = qdev_get_gpio_in(dev, 0); sysbus_connect_irq(busdev, i+1, cs_line); } diff --git a/hw/nand.c b/hw/nand.c index de3e502596..63620938fb 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -297,6 +297,7 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_BLOCKERASE2: + s->addr &= (1ull << s->addrlen * 8) - 1; if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) s->addr <<= 16; else diff --git a/hw/onenand.c b/hw/onenand.c index ddba366ef5..57a346d7da 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -185,7 +185,8 @@ static const VMStateDescription vmstate_onenand = { VMSTATE_UINT8(ecc.cp, OneNANDState), VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2), VMSTATE_UINT16(ecc.count, OneNANDState), - VMSTATE_BUFFER_UNSAFE(otp, OneNANDState, 0, ((64 + 2) << PAGE_SHIFT)), + VMSTATE_BUFFER_POINTER_UNSAFE(otp, OneNANDState, 0, + ((64 + 2) << PAGE_SHIFT)), VMSTATE_END_OF_LIST() } }; diff --git a/hw/pc.h b/hw/pc.h index 8e1dd4cad4..55964ced93 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -109,11 +109,11 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); /* acpi.c */ extern int acpi_enabled; -extern char *acpi_tables; +extern char unsigned *acpi_tables; extern size_t acpi_tables_len; void acpi_bios_init(void); -int acpi_table_add(const char *table_desc); +void acpi_table_add(const QemuOpts *opts, Error **errp); /* acpi_piix.c */ diff --git a/hw/pci/pci-hotplug.c b/hw/pci/pci-hotplug.c index f38df30540..180ee07fef 100644 --- a/hw/pci/pci-hotplug.c +++ b/hw/pci/pci-hotplug.c @@ -99,7 +99,7 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter, dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); dinfo->bus = scsibus->busnr; scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, - false, -1); + false, -1, NULL); if (!scsidev) { return -1; } diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c index 5d57babe07..646dc794bf 100644 --- a/hw/pflash_cfi01.c +++ b/hw/pflash_cfi01.c @@ -67,8 +67,7 @@ struct pflash_t { uint64_t sector_len; uint8_t width; uint8_t be; - int wcycle; /* if 0, the flash is read normally */ - int bypass; + uint8_t wcycle; /* if 0, the flash is read normally */ int ro; uint8_t cmd; uint8_t status; @@ -78,7 +77,7 @@ struct pflash_t { uint16_t ident3; uint8_t cfi_len; uint8_t cfi_table[0x52]; - hwaddr counter; + uint64_t counter; unsigned int writeblock_size; QEMUTimer *timer; MemoryRegion mem; @@ -86,6 +85,19 @@ struct pflash_t { void *storage; }; +static const VMStateDescription vmstate_pflash = { + .name = "pflash_cfi01", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(wcycle, pflash_t), + VMSTATE_UINT8(cmd, pflash_t), + VMSTATE_UINT8(status, pflash_t), + VMSTATE_UINT64(counter, pflash_t), + VMSTATE_END_OF_LIST() + } +}; + static void pflash_timer (void *opaque) { pflash_t *pfl = opaque; @@ -93,12 +105,8 @@ static void pflash_timer (void *opaque) DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); /* Reset flash */ pfl->status ^= 0x80; - if (pfl->bypass) { - pfl->wcycle = 2; - } else { - memory_region_rom_device_set_readable(&pfl->mem, true); - pfl->wcycle = 0; - } + memory_region_rom_device_set_readable(&pfl->mem, true); + pfl->wcycle = 0; pfl->cmd = 0; } @@ -228,7 +236,7 @@ static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, uint8_t *p = pfl->storage; DPRINTF("%s: block write offset " TARGET_FMT_plx - " value %x counter " TARGET_FMT_plx "\n", + " value %x counter %016" PRIx64 "\n", __func__, offset, value, pfl->counter); switch (width) { case 1: @@ -452,7 +460,6 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, reset_flash: memory_region_rom_device_set_readable(&pfl->mem, true); - pfl->bypass = 0; pfl->wcycle = 0; pfl->cmd = 0; } @@ -707,6 +714,7 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) k->init = pflash_cfi01_init; dc->props = pflash_cfi01_properties; + dc->vmsd = &vmstate_pflash; } diff --git a/hw/pl050.c b/hw/pl050.c index bc31ab6885..76735a0eda 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -24,14 +24,13 @@ typedef struct { static const VMStateDescription vmstate_pl050 = { .name = "pl050", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(cr, pl050_state), VMSTATE_UINT32(clk, pl050_state), VMSTATE_UINT32(last, pl050_state), VMSTATE_INT32(pending, pl050_state), - VMSTATE_INT32(is_mouse, pl050_state), VMSTATE_END_OF_LIST() } }; diff --git a/hw/pl330.c b/hw/pl330.c index 1a04773a71..60aa4a8f9f 100644 --- a/hw/pl330.c +++ b/hw/pl330.c @@ -870,9 +870,8 @@ static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->parent->int_status |= (1 << ev_id); DB_PRINT("event interrupt raised %d\n", ev_id); qemu_irq_raise(ch->parent->irq[ev_id]); - } else { - ch->parent->ev_status |= (1 << ev_id); } + ch->parent->ev_status |= (1 << ev_id); } static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c index d9934b5b9c..a22b155b6f 100644 --- a/hw/qdev-properties-system.c +++ b/hw/qdev-properties-system.c @@ -123,11 +123,10 @@ static int parse_chr(DeviceState *dev, const char *str, void **ptr) if (chr == NULL) { return -ENOENT; } - if (chr->avail_connections < 1) { + if (qemu_chr_fe_claim(chr) != 0) { return -EEXIST; } *ptr = chr; - --chr->avail_connections; return 0; } @@ -140,7 +139,7 @@ static void release_chr(Object *obj, const char *name, void *opaque) if (chr) { qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL); - ++chr->avail_connections; + qemu_chr_fe_release(chr); } } diff --git a/hw/qdev.c b/hw/qdev.c index 9b0f65246a..e2bb37dc37 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -767,7 +767,7 @@ static void device_unparent(Object *obj) DeviceClass *dc = DEVICE_GET_CLASS(dev); BusState *bus; QObject *event_data; - gchar *path = object_get_canonical_path(obj); + bool have_realized = dev->realized; while (dev->num_child_bus) { bus = QLIST_FIRST(&dev->child_bus); @@ -787,15 +787,20 @@ static void device_unparent(Object *obj) dev->parent_bus = NULL; } - if (dev->id) { - event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }", - dev->id, path); - } else { - event_data = qobject_from_jsonf("{ 'path': %s }", path); + /* Only send event if the device had been completely realized */ + if (have_realized) { + gchar *path = object_get_canonical_path(OBJECT(dev)); + + if (dev->id) { + event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }", + dev->id, path); + } else { + event_data = qobject_from_jsonf("{ 'path': %s }", path); + } + monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data); + qobject_decref(event_data); + g_free(path); } - monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data); - qobject_decref(event_data); - g_free(path); } static void device_class_init(ObjectClass *class, void *data) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 08787c2a9b..ac2093a5ef 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -207,7 +207,8 @@ static int scsi_qdev_exit(DeviceState *qdev) /* handle legacy '-drive if=scsi,...' cmd line args */ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, - int unit, bool removable, int bootindex) + int unit, bool removable, int bootindex, + const char *serial) { const char *driver; DeviceState *dev; @@ -221,6 +222,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, if (object_property_find(OBJECT(dev), "removable", NULL)) { qdev_prop_set_bit(dev, "removable", removable); } + if (serial) { + qdev_prop_set_string(dev, "serial", serial); + } if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { qdev_free(dev); return NULL; @@ -243,7 +247,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) continue; } qemu_opts_loc_restore(dinfo->opts); - if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1)) { + if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1, NULL)) { res = -1; break; } diff --git a/hw/scsi.h b/hw/scsi.h index 33e2e0bdf1..02a1497d7a 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -160,7 +160,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) } SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, - int unit, bool removable, int bootindex); + int unit, bool removable, int bootindex, + const char *serial); int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); /* diff --git a/hw/sd.c b/hw/sd.c index a895123867..66c4014fbe 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -478,7 +478,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT64(data_start, SDState), VMSTATE_UINT32(data_offset, SDState), VMSTATE_UINT8_ARRAY(data, SDState, 512), - VMSTATE_BUFFER_UNSAFE(buf, SDState, 1, 512), + VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512), VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() } diff --git a/hw/sh_serial.c b/hw/sh_serial.c index 40e797c5a2..462969557c 100644 --- a/hw/sh_serial.c +++ b/hw/sh_serial.c @@ -396,9 +396,11 @@ void sh_serial_init(MemoryRegion *sysmem, s->chr = chr; - if (chr) + if (chr) { + qemu_chr_fe_claim_no_fail(chr); qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, sh_serial_event, s); + } s->eri = eri_source; s->rxi = rxi_source; diff --git a/hw/usb.h b/hw/usb.h index 1b10684dde..4d9d05e9bc 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -337,6 +337,7 @@ typedef struct USBPortOps { struct USBPort { USBDevice *dev; int speedmask; + int hubcount; char path[16]; USBPortOps *ops; void *opaque; diff --git a/hw/usb/bus.c b/hw/usb/bus.c index e58cd9ade2..b10c290cf4 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -341,8 +341,10 @@ void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) if (upstream) { snprintf(downstream->path, sizeof(downstream->path), "%s.%d", upstream->path, portnr); + downstream->hubcount = upstream->hubcount + 1; } else { snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); + downstream->hubcount = 0; } } diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 9701048887..317b4740e2 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = { .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = 0xa0, + .bmAttributes = 0x80, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface_tablet2, diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 504c98c350..0b71abd028 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -25,6 +25,7 @@ #include "trace.h" #include "hw/usb.h" #include "hw/usb/desc.h" +#include "qemu/error-report.h" #define NUM_PORTS 8 @@ -32,6 +33,7 @@ typedef struct USBHubPort { USBPort port; uint16_t wPortStatus; uint16_t wPortChange; + uint16_t wPortChange_reported; } USBHubPort; typedef struct USBHubState { @@ -466,8 +468,11 @@ static void usb_hub_handle_data(USBDevice *dev, USBPacket *p) status = 0; for(i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; - if (port->wPortChange) + if (port->wPortChange && + port->wPortChange_reported != port->wPortChange) { status |= (1 << (i + 1)); + } + port->wPortChange_reported = port->wPortChange; } if (status != 0) { for(i = 0; i < n; i++) { @@ -514,6 +519,11 @@ static int usb_hub_initfn(USBDevice *dev) USBHubPort *port; int i; + if (dev->port->hubcount == 5) { + error_report("usb hub chain too deep"); + return -1; + } + usb_desc_create_serial(dev); usb_desc_init(dev); s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index d3f01aa2a7..21651b3637 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -625,7 +625,7 @@ static int usb_msd_initfn_storage(USBDevice *dev) usb_desc_init(dev); scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info_storage); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable, - s->conf.bootindex); + s->conf.bootindex, s->serial); if (!scsi_dev) { return -1; } diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 5aa342bda5..efd4b0dbde 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -452,7 +452,6 @@ struct XHCIState { MemoryRegion mem_oper; MemoryRegion mem_runtime; MemoryRegion mem_doorbell; - const char *name; unsigned int devaddr; /* properties */ @@ -1172,8 +1171,6 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, uint32_t ctx[5]; uint32_t ctx2[2]; - fprintf(stderr, "%s: epid %d, state %d\n", - __func__, epctx->epid, state); xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); ctx[0] &= ~EP_STATE_MASK; ctx[0] |= state; @@ -2568,7 +2565,7 @@ static void xhci_process_commands(XHCIState *xhci) } break; default: - fprintf(stderr, "xhci: unimplemented command %d\n", type); + trace_usb_xhci_unimplemented("command", type); event.ccode = CC_TRB_ERROR; break; } @@ -2767,7 +2764,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x00000000; /* reserved */ break; default: - fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg); + trace_usb_xhci_unimplemented("cap read", reg); ret = 0; } @@ -2790,8 +2787,7 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size) break; case 0x0c: /* reserved */ default: - fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n", - port->portnr, (uint32_t)reg); + trace_usb_xhci_unimplemented("port read", reg); ret = 0; } @@ -2831,8 +2827,7 @@ static void xhci_port_write(void *ptr, hwaddr reg, case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ default: - fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n", - port->portnr, (uint32_t)reg); + trace_usb_xhci_unimplemented("port write", reg); } } @@ -2870,7 +2865,7 @@ static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size) ret = xhci->config; break; default: - fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg); + trace_usb_xhci_unimplemented("oper read", reg); ret = 0; } @@ -2935,7 +2930,7 @@ static void xhci_oper_write(void *ptr, hwaddr reg, xhci->config = val & 0xff; break; default: - fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg); + trace_usb_xhci_unimplemented("oper write", reg); } } @@ -2951,8 +2946,7 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, ret = xhci_mfindex_get(xhci) & 0x3fff; break; default: - fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", - (int)reg); + trace_usb_xhci_unimplemented("runtime read", reg); break; } } else { @@ -2996,7 +2990,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg, trace_usb_xhci_runtime_write(reg, val); if (reg < 0x20) { - fprintf(stderr, "%s: reg 0x%x unimplemented\n", __func__, (int)reg); + trace_usb_xhci_unimplemented("runtime write", reg); return; } @@ -3038,8 +3032,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg, xhci_events_update(xhci, v); break; default: - fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", - (int)reg); + trace_usb_xhci_unimplemented("oper write", reg); } } @@ -3290,6 +3283,9 @@ static int usb_xhci_initfn(struct PCIDevice *dev) if (xhci->numintrs > MAXINTRS) { xhci->numintrs = MAXINTRS; } + while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */ + xhci->numintrs++; + } if (xhci->numintrs < 1) { xhci->numintrs = 1; } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index d02a7b94c4..0ddb0818d8 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -104,6 +104,8 @@ struct USBRedirDevice { /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ const uint8_t *read_buf; int read_buf_size; + /* Active chardev-watch-tag */ + guint watch; /* For async handling of close */ QEMUBH *chardev_close_bh; /* To delay the usb attach in case of quick chardev close + open */ @@ -254,9 +256,21 @@ static int usbredir_read(void *priv, uint8_t *data, int count) return count; } +static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, + void *opaque) +{ + USBRedirDevice *dev = opaque; + + dev->watch = 0; + usbredirparser_do_write(dev->parser); + + return FALSE; +} + static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; + int r; if (!dev->cs->be_open) { return 0; @@ -267,7 +281,17 @@ static int usbredir_write(void *priv, uint8_t *data, int count) return 0; } - return qemu_chr_fe_write(dev->cs, data, count); + r = qemu_chr_fe_write(dev->cs, data, count); + if (r < count) { + if (!dev->watch) { + dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT, + usbredir_write_unblocked, dev); + } + if (r < 0) { + r = 0; + } + } + return r; } /* @@ -1085,6 +1109,10 @@ static void usbredir_chardev_close_bh(void *opaque) usbredirparser_destroy(dev->parser); dev->parser = NULL; } + if (dev->watch) { + g_source_remove(dev->watch); + dev->watch = 0; + } } static void usbredir_create_parser(USBRedirDevice *dev) @@ -1317,6 +1345,9 @@ static void usbredir_handle_destroy(USBDevice *udev) if (dev->parser) { usbredirparser_destroy(dev->parser); } + if (dev->watch) { + g_source_remove(dev->watch); + } free(dev->filter_rules); } @@ -1973,6 +2004,10 @@ static int usbredir_post_load(void *priv, int version_id) { USBRedirDevice *dev = priv; + if (dev->parser == NULL) { + return 0; + } + switch (dev->device_info.speed) { case usb_redir_speed_low: dev->dev.speed = USB_SPEED_LOW; diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c index 288361d0fb..693a9ffdda 100644 --- a/hw/vfio_pci.c +++ b/hw/vfio_pci.c @@ -19,25 +19,26 @@ */ #include <dirent.h> -#include <unistd.h> +#include <linux/vfio.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> -#include <linux/vfio.h> +#include <unistd.h> #include "config.h" -#include "qemu/event_notifier.h" #include "exec/address-spaces.h" -#include "sysemu/kvm.h" #include "exec/memory.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci.h" #include "qemu-common.h" #include "qemu/error-report.h" +#include "qemu/event_notifier.h" #include "qemu/queue.h" #include "qemu/range.h" +#include "sysemu/kvm.h" +#include "sysemu/sysemu.h" /* #define DEBUG_VFIO */ #ifdef DEBUG_VFIO @@ -48,6 +49,20 @@ do { } while (0) #endif +/* Extra debugging, trap acceleration paths for more logging */ +#define VFIO_ALLOW_MMAP 1 +#define VFIO_ALLOW_KVM_INTX 1 + +struct VFIODevice; + +typedef struct VFIOQuirk { + MemoryRegion mem; + struct VFIODevice *vdev; + QLIST_ENTRY(VFIOQuirk) next; + uint32_t data; + uint32_t data2; +} VFIOQuirk; + typedef struct VFIOBAR { off_t fd_offset; /* offset of BAR within device fd */ int fd; /* device fd, allows us to pass VFIOBAR as opaque data */ @@ -57,8 +72,22 @@ typedef struct VFIOBAR { size_t size; uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ uint8_t nr; /* cache the BAR number for debug */ + QLIST_HEAD(, VFIOQuirk) quirks; } VFIOBAR; +typedef struct VFIOVGARegion { + MemoryRegion mem; + off_t offset; + int nr; + QLIST_HEAD(, VFIOQuirk) quirks; +} VFIOVGARegion; + +typedef struct VFIOVGA { + off_t fd_offset; + int fd; + VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS]; +} VFIOVGA; + typedef struct VFIOINTx { bool pending; /* interrupt pending */ bool kvm_accel; /* set when QEMU bypass through KVM enabled */ @@ -70,8 +99,6 @@ typedef struct VFIOINTx { QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */ } VFIOINTx; -struct VFIODevice; - typedef struct VFIOMSIVector { EventNotifier interrupt; /* eventfd triggered on interrupt */ struct VFIODevice *vdev; /* back pointer to device */ @@ -117,6 +144,7 @@ typedef struct VFIODevice { int fd; VFIOINTx intx; unsigned int config_size; + uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */ off_t config_offset; /* Offset of config space region within device fd */ unsigned int rom_size; off_t rom_offset; /* Offset of ROM region within device fd */ @@ -126,10 +154,17 @@ typedef struct VFIODevice { int nr_vectors; /* Number of MSI/MSIX vectors currently in use */ int interrupt; /* Current interrupt type */ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ + VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */ PCIHostDeviceAddress host; QLIST_ENTRY(VFIODevice) next; struct VFIOGroup *group; + uint32_t features; +#define VFIO_FEATURE_ENABLE_VGA_BIT 0 +#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) + int32_t bootindex; + uint8_t pm_cap; bool reset_works; + bool has_vga; } VFIODevice; typedef struct VFIOGroup { @@ -151,6 +186,8 @@ static QLIST_HEAD(, VFIOGroup) static void vfio_disable_interrupts(VFIODevice *vdev); static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); +static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, + uint32_t val, int len); static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled); /* @@ -275,7 +312,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) int ret, argsz; int32_t *pfd; - if (!kvm_irqfds_enabled() || + if (!VFIO_ALLOW_KVM_INTX || !kvm_irqfds_enabled() || vdev->intx.route.mode != PCI_INTX_ENABLED || !kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) { return; @@ -895,8 +932,16 @@ static void vfio_bar_write(void *opaque, hwaddr addr, __func__, addr, data, size); } - DPRINTF("%s(BAR%d+0x%"HWADDR_PRIx", 0x%"PRIx64", %d)\n", - __func__, bar->nr, addr, data, size); +#ifdef DEBUG_VFIO + { + VFIODevice *vdev = container_of(bar, VFIODevice, bars[bar->nr]); + + DPRINTF("%s(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"PRIx64 + ", %d)\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, bar->nr, addr, + data, size); + } +#endif /* * A read or write to a BAR always signals an INTx EOI. This will @@ -942,8 +987,16 @@ static uint64_t vfio_bar_read(void *opaque, break; } - DPRINTF("%s(BAR%d+0x%"HWADDR_PRIx", %d) = 0x%"PRIx64"\n", - __func__, bar->nr, addr, size, data); +#ifdef DEBUG_VFIO + { + VFIODevice *vdev = container_of(bar, VFIODevice, bars[bar->nr]); + + DPRINTF("%s(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx + ", %d) = 0x%"PRIx64"\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, + bar->nr, addr, size, data); + } +#endif /* Same as write above */ vfio_eoi(container_of(bar, VFIODevice, bars[bar->nr])); @@ -957,51 +1010,813 @@ static const MemoryRegionOps vfio_bar_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void vfio_vga_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOVGARegion *region = opaque; + VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + off_t offset = vga->fd_offset + region->offset + addr; + + switch (size) { + case 1: + buf.byte = data; + break; + case 2: + buf.word = cpu_to_le16(data); + break; + case 4: + buf.dword = cpu_to_le32(data); + break; + default: + hw_error("vfio: unsupported write size, %d bytes\n", size); + break; + } + + if (pwrite(vga->fd, &buf, size, offset) != size) { + error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", + __func__, region->offset + addr, data, size); + } + + DPRINTF("%s(0x%"HWADDR_PRIx", 0x%"PRIx64", %d)\n", + __func__, region->offset + addr, data, size); +} + +static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) +{ + VFIOVGARegion *region = opaque; + VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + uint64_t data = 0; + off_t offset = vga->fd_offset + region->offset + addr; + + if (pread(vga->fd, &buf, size, offset) != size) { + error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", + __func__, region->offset + addr, size); + return (uint64_t)-1; + } + + switch (size) { + case 1: + data = buf.byte; + break; + case 2: + data = le16_to_cpu(buf.word); + break; + case 4: + data = le32_to_cpu(buf.dword); + break; + default: + hw_error("vfio: unsupported read size, %d bytes\n", size); + break; + } + + DPRINTF("%s(0x%"HWADDR_PRIx", %d) = 0x%"PRIx64"\n", + __func__, region->offset + addr, size, data); + + return data; +} + +static const MemoryRegionOps vfio_vga_ops = { + .read = vfio_vga_read, + .write = vfio_vga_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + /* - * PCI config space + * Device specific quirks */ -static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) + +#define PCI_VENDOR_ID_ATI 0x1002 + +/* + * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon + * HD 5450/6350]) reports the upper byte of the physical address of the + * I/O port BAR4 through VGA register 0x3c3. The BAR is 256 bytes, so the + * lower byte is known to be zero. Probing for this quirk reads 0xff from + * port 0x3c3 on some devices so we store the physical address and replace + * reads with the virtual address any time it matches. XXX Research when + * to enable quirk. + */ +static uint64_t vfio_ati_3c3_quirk_read(void *opaque, + hwaddr addr, unsigned size) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); - uint32_t val = 0; + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], + addr + 0x3, size); + + if (data == quirk->data) { + data = pci_get_byte(pdev->config + PCI_BASE_ADDRESS_4 + 1); + DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data); + } + + return data; +} + +static const MemoryRegionOps vfio_ati_3c3_quirk = { + .read = vfio_ati_3c3_quirk_read, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_4; + uint32_t physbar; + VFIOQuirk *quirk; + + if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI || + vdev->bars[4].size < 256) { + return; + } + + /* Get I/O port BAR physical address */ + if (pread(vdev->fd, &physbar, 4, physoffset) != 4) { + error_report("vfio: probe failed for ATI/AMD 0x3c3 quirk on device " + "%04x:%02x:%02x.%x", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + quirk->data = (physbar >> 8) & 0xff; + + memory_region_init_io(&quirk->mem, &vfio_ati_3c3_quirk, quirk, + "vfio-ati-3c3-quirk", 1); + memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, 3, + &quirk->mem); + + QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, + quirk, next); + + DPRINTF("Enabled ATI/AMD quirk 0x3c3 for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon + * HD 5450/6350]) reports the physical address of MMIO BAR0 through a + * write/read operation on I/O port BAR4. When uint32_t 0x4010 is written + * to offset 0x0, the subsequent read from offset 0x4 returns the contents + * of BAR0. Test for this quirk on all ATI/AMD devices. XXX - Note that + * 0x10 is the offset of BAR0 in config sapce, is this a window to all of + * config space? + */ +static uint64_t vfio_ati_4010_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + uint64_t data = vfio_bar_read(&vdev->bars[4], addr, size); + + if (addr == 4 && size == 4 && quirk->data) { + data = pci_get_long(pdev->config + PCI_BASE_ADDRESS_0); + DPRINTF("%s(BAR4+0x4) = 0x%"PRIx64"\n", __func__, data); + } + + quirk->data = 0; + + return data; +} + +static void vfio_ati_4010_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + + vfio_bar_write(&vdev->bars[4], addr, data, size); + + quirk->data = (addr == 0 && size == 4 && data == 0x4010) ? 1 : 0; +} + +static const MemoryRegionOps vfio_ati_4010_quirk = { + .read = vfio_ati_4010_quirk_read, + .write = vfio_ati_4010_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_ati_4010_quirk(VFIODevice *vdev, int nr) +{ + PCIDevice *pdev = &vdev->pdev; + off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0; + uint32_t physbar0; + uint64_t data; + VFIOQuirk *quirk; + + if (!vdev->has_vga || nr != 4 || !vdev->bars[0].size || + pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) { + return; + } + + /* Get I/O port BAR physical address */ + if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) { + error_report("vfio: probe failed for ATI/AMD 0x4010 quirk on device " + "%04x:%02x:%02x.%x", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + return; + } + + /* Write 0x4010 to I/O port BAR offset 0 */ + vfio_bar_write(&vdev->bars[4], 0, 0x4010, 4); + /* Read back result */ + data = vfio_bar_read(&vdev->bars[4], 4, 4); + + /* If the register matches the physical address of BAR0, we need a quirk */ + if (data != physbar0) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_ati_4010_quirk, quirk, + "vfio-ati-4010-quirk", 8); + memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + DPRINTF("Enabled ATI/AMD quirk 0x4010 for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * Device 1002:5b63 (Advanced Micro Devices [AMD] nee ATI RV370 [Radeon X550]) + * retrieves the upper half of the MMIO BAR0 physical address by writing + * 0xf10 to I/O port BAR1 offset 0 and reading the result from offset 6. + * XXX - 0x10 is the offset of BAR0 in PCI config space, this could provide + * full access to config space. Config space is little endian, so the data + * register probably starts at 0x4. + */ +static uint64_t vfio_ati_f10_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + uint64_t data = vfio_bar_read(&vdev->bars[1], addr, size); + + if (addr == 6 && size == 2 && quirk->data) { + data = pci_get_word(pdev->config + PCI_BASE_ADDRESS_0 + 2); + DPRINTF("%s(BAR1+0x6) = 0x%"PRIx64"\n", __func__, data); + } + + quirk->data = 0; + + return data; +} + +static void vfio_ati_f10_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + + vfio_bar_write(&vdev->bars[1], addr, data, size); + + quirk->data = (addr == 0 && size == 4 && data == 0xf10) ? 1 : 0; +} + +static const MemoryRegionOps vfio_ati_f10_quirk = { + .read = vfio_ati_f10_quirk_read, + .write = vfio_ati_f10_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_ati_f10_quirk(VFIODevice *vdev, int nr) +{ + PCIDevice *pdev = &vdev->pdev; + off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0; + uint32_t physbar0; + uint64_t data; + VFIOQuirk *quirk; + + if (!vdev->has_vga || nr != 1 || !vdev->bars[0].size || + pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) { + return; + } + + /* Get I/O port BAR physical address */ + if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) { + error_report("vfio: probe failed for ATI/AMD 0xf10 quirk on device " + "%04x:%02x:%02x.%x", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + return; + } + + vfio_bar_write(&vdev->bars[1], 0, 0xf10, 4); + data = vfio_bar_read(&vdev->bars[1], 0x6, 2); + + /* If the register matches the physical address of BAR0, we need a quirk */ + if (data != (le32_to_cpu(physbar0) >> 16)) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_ati_f10_quirk, quirk, + "vfio-ati-f10-quirk", 8); + memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + DPRINTF("Enabled ATI/AMD quirk 0xf10 for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +#define PCI_VENDOR_ID_NVIDIA 0x10de + +/* + * Nvidia has several different methods to get to config space, the + * nouveu project has several of these documented here: + * https://github.com/pathscale/envytools/tree/master/hwdocs + * + * The first quirk is actually not documented in envytools and is found + * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an + * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access + * the mirror of PCI config space found at BAR0 offset 0x1800. The access + * sequence first writes 0x338 to I/O port 0x3d4. The target offset is + * then written to 0x3d0. Finally 0x538 is written for a read and 0x738 + * is written for a write to 0x3d4. The BAR0 offset is then accessible + * through 0x3d0. This quirk doesn't seem to be necessary on newer cards + * that use the I/O port BAR5 window but it doesn't hurt to leave it. + */ +enum { + NV_3D0_NONE, + NV_3D0_SELECT, + NV_3D0_WINDOW, + NV_3D0_READ, + NV_3D0_WRITE, +}; + +static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], + addr + 0x10, size); + + if (quirk->data == NV_3D0_READ && addr == 0) { + data = vfio_pci_read_config(pdev, quirk->data2, size); + DPRINTF("%s(0x3d0, %d) = 0x%"PRIx64"\n", __func__, size, data); + } + + quirk->data = NV_3D0_NONE; + + return data; +} + +static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + + switch (quirk->data) { + case NV_3D0_NONE: + if (addr == 4 && data == 0x338) { + quirk->data = NV_3D0_SELECT; + } + break; + case NV_3D0_SELECT: + quirk->data = NV_3D0_NONE; + if (addr == 0 && (data & ~0xff) == 0x1800) { + quirk->data = NV_3D0_WINDOW; + quirk->data2 = data & 0xff; + } + break; + case NV_3D0_WINDOW: + quirk->data = NV_3D0_NONE; + if (addr == 4) { + if (data == 0x538) { + quirk->data = NV_3D0_READ; + } else if (data == 0x738) { + quirk->data = NV_3D0_WRITE; + } + } + break; + case NV_3D0_WRITE: + quirk->data = NV_3D0_NONE; + if (addr == 0) { + vfio_pci_write_config(pdev, quirk->data2, data, size); + DPRINTF("%s(0x3d0, 0x%"PRIx64", %d)\n", __func__, data, size); + return; + } + break; + default: + quirk->data = NV_3D0_NONE; + } + + vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], + addr + 0x10, data, size); +} + +static const MemoryRegionOps vfio_nvidia_3d0_quirk = { + .read = vfio_nvidia_3d0_quirk_read, + .write = vfio_nvidia_3d0_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_vga_probe_nvidia_3d0_quirk(VFIODevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOQuirk *quirk; + + if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA || + !vdev->bars[1].size) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_nvidia_3d0_quirk, quirk, + "vfio-nvidia-3d0-quirk", 6); + memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, + 0x10, &quirk->mem); + + QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, + quirk, next); + + DPRINTF("Enabled NVIDIA VGA 0x3d0 quirk for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * The second quirk is documented in envytools. The I/O port BAR5 is just + * a set of address/data ports to the MMIO BARs. The BAR we care about is + * again BAR0. This backdoor is apparently a bit newer than the one above + * so we need to not only trap 256 bytes @0x1800, but all of PCI config + * space, including extended space is available at the 4k @0x88000. + */ +enum { + NV_BAR5_ADDRESS = 0x1, + NV_BAR5_ENABLE = 0x2, + NV_BAR5_MASTER = 0x4, + NV_BAR5_VALID = 0x7, +}; + +static uint64_t vfio_nvidia_bar5_window_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + uint64_t data = vfio_bar_read(&vdev->bars[5], addr, size); + + if (addr == 0xc && quirk->data == NV_BAR5_VALID) { + data = vfio_pci_read_config(&vdev->pdev, quirk->data2, size); + DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", %d) = 0x%" + PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, size, data); + } + + return data; +} + +static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; /* - * We only need QEMU PCI config support for the ROM BAR, the MSI and MSIX - * capabilities, and the multifunction bit below. We let VFIO handle - * virtualizing everything else. Performance is not a concern here. + * Use quirk->data to track enables and quirk->data2 for the offset */ - if (ranges_overlap(addr, len, PCI_ROM_ADDRESS, 4) || - (pdev->cap_present & QEMU_PCI_CAP_MSIX && - ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) || - (pdev->cap_present & QEMU_PCI_CAP_MSI && - ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size))) { + switch (addr) { + case 0x0: + if (data & 0x1) { + quirk->data |= NV_BAR5_MASTER; + } else { + quirk->data &= ~NV_BAR5_MASTER; + } + break; + case 0x4: + if (data & 0x1) { + quirk->data |= NV_BAR5_ENABLE; + } else { + quirk->data &= ~NV_BAR5_ENABLE; + } + break; + case 0x8: + if (quirk->data & NV_BAR5_MASTER) { + if ((data & ~0xfff) == 0x88000) { + quirk->data |= NV_BAR5_ADDRESS; + quirk->data2 = data & 0xfff; + } else if ((data & ~0xff) == 0x1800) { + quirk->data |= NV_BAR5_ADDRESS; + quirk->data2 = data & 0xff; + } else { + quirk->data &= ~NV_BAR5_ADDRESS; + } + } + break; + case 0xc: + if (quirk->data == NV_BAR5_VALID) { + vfio_pci_write_config(&vdev->pdev, quirk->data2, data, size); + DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", 0x%" + PRIx64", %d)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, + addr, data, size); + return; + } + } + + vfio_bar_write(&vdev->bars[5], addr, data, size); +} + +static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = { + .read = vfio_nvidia_bar5_window_quirk_read, + .write = vfio_nvidia_bar5_window_quirk_write, + .valid.min_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOQuirk *quirk; + + if (!vdev->has_vga || nr != 5 || + pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_nvidia_bar5_window_quirk, quirk, + "vfio-nvidia-bar5-window-quirk", 16); + memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + DPRINTF("Enabled NVIDIA BAR5 window quirk for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * Finally, BAR0 itself. We want to redirect any accesses to either + * 0x1800 or 0x88000 through the PCI config space access functions. + * + * NB - quirk at a page granularity or else they don't seem to work when + * BARs are mmap'd + * + * Here's offset 0x88000... + */ +static uint64_t vfio_nvidia_bar0_88000_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + hwaddr base = 0x88000 & TARGET_PAGE_MASK; + hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK; + uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size); + + if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) { + data = vfio_pci_read_config(&vdev->pdev, addr - offset, size); + + DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%" + PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr + base, size, data); + } + + return data; +} + +static void vfio_nvidia_bar0_88000_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + hwaddr base = 0x88000 & TARGET_PAGE_MASK; + hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK; + + if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) { + vfio_pci_write_config(&vdev->pdev, addr - offset, data, size); - val = pci_default_read_config(pdev, addr, len); + DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%" + PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr + base, data, size); } else { - if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) { - error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function, addr, len); - return -errno; - } - val = le32_to_cpu(val); + vfio_bar_write(&vdev->bars[0], addr + base, data, size); } +} + +static const MemoryRegionOps vfio_nvidia_bar0_88000_quirk = { + .read = vfio_nvidia_bar0_88000_quirk_read, + .write = vfio_nvidia_bar0_88000_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; - /* Multifunction bit is virualized in QEMU */ - if (unlikely(ranges_overlap(addr, len, PCI_HEADER_TYPE, 1))) { - uint32_t mask = PCI_HEADER_TYPE_MULTI_FUNCTION; +static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOQuirk *quirk; - if (len == 4) { - mask <<= 16; + if (!vdev->has_vga || nr != 0 || + pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_nvidia_bar0_88000_quirk, quirk, + "vfio-nvidia-bar0-88000-quirk", + TARGET_PAGE_ALIGN(PCIE_CONFIG_SPACE_SIZE)); + memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + 0x88000 & TARGET_PAGE_MASK, + &quirk->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + DPRINTF("Enabled NVIDIA BAR0 0x88000 quirk for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * And here's the same for BAR0 offset 0x1800... + */ +static uint64_t vfio_nvidia_bar0_1800_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + hwaddr base = 0x1800 & TARGET_PAGE_MASK; + hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK; + uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size); + + if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) { + data = vfio_pci_read_config(&vdev->pdev, addr - offset, size); + + DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%" + PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr + base, size, data); + } + + return data; +} + +static void vfio_nvidia_bar0_1800_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + hwaddr base = 0x1800 & TARGET_PAGE_MASK; + hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK; + + if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) { + vfio_pci_write_config(&vdev->pdev, addr - offset, data, size); + + DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%" + PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr + base, data, size); + } else { + vfio_bar_write(&vdev->bars[0], addr + base, data, size); + } +} + +static const MemoryRegionOps vfio_nvidia_bar0_1800_quirk = { + .read = vfio_nvidia_bar0_1800_quirk_read, + .write = vfio_nvidia_bar0_1800_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOQuirk *quirk; + + if (!vdev->has_vga || nr != 0 || + pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { + return; + } + + /* Log the chipset ID */ + DPRINTF("Nvidia NV%02x\n", + (unsigned int)(vfio_bar_read(&vdev->bars[0], 0, 4) >> 20) & 0xff); + + quirk = g_malloc0(sizeof(*quirk)); + quirk->vdev = vdev; + + memory_region_init_io(&quirk->mem, &vfio_nvidia_bar0_1800_quirk, quirk, + "vfio-nvidia-bar0-1800-quirk", + TARGET_PAGE_ALIGN(PCI_CONFIG_SPACE_SIZE)); + memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + 0x1800 & TARGET_PAGE_MASK, + &quirk->mem, 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + DPRINTF("Enabled NVIDIA BAR0 0x1800 quirk for device %04x:%02x:%02x.%x\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); +} + +/* + * TODO - Some Nvidia devices provide config access to their companion HDA + * device and even to their parent bridge via these config space mirrors. + * Add quirks for those regions. + */ + +/* + * Common quirk probe entry points. + */ +static void vfio_vga_quirk_setup(VFIODevice *vdev) +{ + vfio_vga_probe_ati_3c3_quirk(vdev); + vfio_vga_probe_nvidia_3d0_quirk(vdev); +} + +static void vfio_vga_quirk_teardown(VFIODevice *vdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) { + while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) { + VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks); + memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem); + QLIST_REMOVE(quirk, next); + g_free(quirk); } + } +} - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - val |= mask; - } else { - val &= ~mask; +static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr) +{ + vfio_probe_ati_4010_quirk(vdev, nr); + vfio_probe_ati_f10_quirk(vdev, nr); + vfio_probe_nvidia_bar5_window_quirk(vdev, nr); + vfio_probe_nvidia_bar0_88000_quirk(vdev, nr); + vfio_probe_nvidia_bar0_1800_quirk(vdev, nr); +} + +static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + + while (!QLIST_EMPTY(&bar->quirks)) { + VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks); + memory_region_del_subregion(&bar->mem, &quirk->mem); + QLIST_REMOVE(quirk, next); + g_free(quirk); + } +} + +/* + * PCI config space + */ +static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val; + + memcpy(&emu_bits, vdev->emulated_config_bits + addr, len); + emu_bits = le32_to_cpu(emu_bits); + + if (emu_bits) { + emu_val = pci_default_read_config(pdev, addr, len); + } + + if (~emu_bits & (0xffffffffU >> (32 - len * 8))) { + ssize_t ret; + + ret = pread(vdev->fd, &phys_val, len, vdev->config_offset + addr); + if (ret != len) { + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, len); + return -errno; } + phys_val = le32_to_cpu(phys_val); } + val = (emu_val & emu_bits) | (phys_val & ~emu_bits); + DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, addr, len, val); @@ -1026,12 +1841,6 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, vdev->host.slot, vdev->host.function, addr, val, len); } - /* Write standard header bits to emulation */ - if (addr < PCI_CONFIG_HEADER_SIZE) { - pci_default_write_config(pdev, addr, val, len); - return; - } - /* MSI/MSI-X Enabling/Disabling */ if (pdev->cap_present & QEMU_PCI_CAP_MSI && ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) { @@ -1046,9 +1855,7 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, } else if (was_enabled && !is_enabled) { vfio_disable_msi(vdev); } - } - - if (pdev->cap_present & QEMU_PCI_CAP_MSIX && + } else if (pdev->cap_present & QEMU_PCI_CAP_MSIX && ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) { int is_enabled, was_enabled = msix_enabled(pdev); @@ -1061,6 +1868,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, } else if (was_enabled && !is_enabled) { vfio_disable_msix(vdev); } + } else { + /* Write everything to QEMU to keep emulated bits correct */ + pci_default_write_config(pdev, addr, val, len); } } @@ -1130,7 +1940,7 @@ static void vfio_listener_region_add(MemoryListener *listener, int ret; if (vfio_listener_skipped_section(section)) { - DPRINTF("vfio: SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n", + DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n", section->offset_within_address_space, section->offset_within_address_space + section->size - 1); return; @@ -1154,7 +1964,7 @@ static void vfio_listener_region_add(MemoryListener *listener, section->offset_within_region + (iova - section->offset_within_address_space); - DPRINTF("vfio: region_add %"HWADDR_PRIx" - %"HWADDR_PRIx" [%p]\n", + DPRINTF("region_add %"HWADDR_PRIx" - %"HWADDR_PRIx" [%p]\n", iova, end - 1, vaddr); ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); @@ -1174,7 +1984,7 @@ static void vfio_listener_region_del(MemoryListener *listener, int ret; if (vfio_listener_skipped_section(section)) { - DPRINTF("vfio: SKIPPING region_del %"HWADDR_PRIx" - %"PRIx64"\n", + DPRINTF("SKIPPING region_del %"HWADDR_PRIx" - %"PRIx64"\n", section->offset_within_address_space, section->offset_within_address_space + section->size - 1); return; @@ -1194,7 +2004,7 @@ static void vfio_listener_region_del(MemoryListener *listener, return; } - DPRINTF("vfio: region_del %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", + DPRINTF("region_del %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", iova, end - 1); ret = vfio_dma_unmap(container, iova, end - iova); @@ -1378,6 +2188,8 @@ static void vfio_unmap_bar(VFIODevice *vdev, int nr) return; } + vfio_bar_quirk_teardown(vdev, nr); + memory_region_del_subregion(&bar->mem, &bar->mmap_mem); munmap(bar->mmap, memory_region_size(&bar->mmap_mem)); @@ -1395,7 +2207,7 @@ static int vfio_mmap_bar(VFIOBAR *bar, MemoryRegion *mem, MemoryRegion *submem, { int ret = 0; - if (size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) { + if (VFIO_ALLOW_MMAP && size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) { int prot = 0; if (bar->flags & VFIO_REGION_INFO_FLAG_READ) { @@ -1488,6 +2300,8 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) error_report("%s unsupported. Performance may be slow", name); } } + + vfio_bar_quirk_setup(vdev, nr); } static void vfio_map_bars(VFIODevice *vdev) @@ -1497,6 +2311,29 @@ static void vfio_map_bars(VFIODevice *vdev) for (i = 0; i < PCI_ROM_SLOT; i++) { vfio_map_bar(vdev, i); } + + if (vdev->has_vga) { + memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_MEM].mem, + &vfio_vga_ops, + &vdev->vga.region[QEMU_PCI_VGA_MEM], + "vfio-vga-mmio@0xa0000", + QEMU_PCI_VGA_MEM_SIZE); + memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem, + &vfio_vga_ops, + &vdev->vga.region[QEMU_PCI_VGA_IO_LO], + "vfio-vga-io@0x3b0", + QEMU_PCI_VGA_IO_LO_SIZE); + memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, + &vfio_vga_ops, + &vdev->vga.region[QEMU_PCI_VGA_IO_HI], + "vfio-vga-io@0x3c0", + QEMU_PCI_VGA_IO_HI_SIZE); + + pci_register_vga(&vdev->pdev, &vdev->vga.region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem); + vfio_vga_quirk_setup(vdev); + } } static void vfio_unmap_bars(VFIODevice *vdev) @@ -1506,6 +2343,14 @@ static void vfio_unmap_bars(VFIODevice *vdev) for (i = 0; i < PCI_ROM_SLOT; i++) { vfio_unmap_bar(vdev, i); } + + if (vdev->has_vga) { + vfio_vga_quirk_teardown(vdev); + pci_unregister_vga(&vdev->pdev); + memory_region_destroy(&vdev->vga.region[QEMU_PCI_VGA_MEM].mem); + memory_region_destroy(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem); + memory_region_destroy(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem); + } } /* @@ -1525,6 +2370,124 @@ static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos) return next - pos; } +static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask) +{ + pci_set_word(buf, (pci_get_word(buf) & ~mask) | val); +} + +static void vfio_add_emulated_word(VFIODevice *vdev, int pos, + uint16_t val, uint16_t mask) +{ + vfio_set_word_bits(vdev->pdev.config + pos, val, mask); + vfio_set_word_bits(vdev->pdev.wmask + pos, ~mask, mask); + vfio_set_word_bits(vdev->emulated_config_bits + pos, mask, mask); +} + +static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask) +{ + pci_set_long(buf, (pci_get_long(buf) & ~mask) | val); +} + +static void vfio_add_emulated_long(VFIODevice *vdev, int pos, + uint32_t val, uint32_t mask) +{ + vfio_set_long_bits(vdev->pdev.config + pos, val, mask); + vfio_set_long_bits(vdev->pdev.wmask + pos, ~mask, mask); + vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); +} + +static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size) +{ + uint16_t flags; + uint8_t type; + + flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS); + type = (flags & PCI_EXP_FLAGS_TYPE) >> 4; + + if (type != PCI_EXP_TYPE_ENDPOINT && + type != PCI_EXP_TYPE_LEG_END && + type != PCI_EXP_TYPE_RC_END) { + + error_report("vfio: Assignment of PCIe type 0x%x " + "devices is not currently supported", type); + return -EINVAL; + } + + if (!pci_bus_is_express(vdev->pdev.bus)) { + /* + * Use express capability as-is on PCI bus. It doesn't make much + * sense to even expose, but some drivers (ex. tg3) depend on it + * and guests don't seem to be particular about it. We'll need + * to revist this or force express devices to express buses if we + * ever expose an IOMMU to the guest. + */ + } else if (pci_bus_is_root(vdev->pdev.bus)) { + /* + * On a Root Complex bus Endpoints become Root Complex Integrated + * Endpoints, which changes the type and clears the LNK & LNK2 fields. + */ + if (type == PCI_EXP_TYPE_ENDPOINT) { + vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS, + PCI_EXP_TYPE_RC_END << 4, + PCI_EXP_FLAGS_TYPE); + + /* Link Capabilities, Status, and Control goes away */ + if (size > PCI_EXP_LNKCTL) { + vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, 0, ~0); + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, 0, ~0); + +#ifndef PCI_EXP_LNKCAP2 +#define PCI_EXP_LNKCAP2 44 +#endif +#ifndef PCI_EXP_LNKSTA2 +#define PCI_EXP_LNKSTA2 50 +#endif + /* Link 2 Capabilities, Status, and Control goes away */ + if (size > PCI_EXP_LNKCAP2) { + vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP2, 0, ~0); + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL2, 0, ~0); + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA2, 0, ~0); + } + } + + } else if (type == PCI_EXP_TYPE_LEG_END) { + /* + * Legacy endpoints don't belong on the root complex. Windows + * seems to be happier with devices if we skip the capability. + */ + return 0; + } + + } else { + /* + * Convert Root Complex Integrated Endpoints to regular endpoints. + * These devices don't support LNK/LNK2 capabilities, so make them up. + */ + if (type == PCI_EXP_TYPE_RC_END) { + vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS, + PCI_EXP_TYPE_ENDPOINT << 4, + PCI_EXP_FLAGS_TYPE); + vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, + PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25, ~0); + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); + } + + /* Mark the Link Status bits as emulated to allow virtual negotiation */ + vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, + pci_get_word(vdev->pdev.config + pos + + PCI_EXP_LNKSTA), + PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); + } + + pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size); + if (pos >= 0) { + vdev->pdev.exp.exp_cap = pos; + } + + return pos; +} + static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) { PCIDevice *pdev = &vdev->pdev; @@ -1555,16 +2518,27 @@ static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) return ret; } } else { - pdev->config[PCI_CAPABILITY_LIST] = 0; /* Begin the rebuild */ + /* Begin the rebuild, use QEMU emulated list bits */ + pdev->config[PCI_CAPABILITY_LIST] = 0; + vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff; + vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST; } + /* Use emulated next pointer to allow dropping caps */ + pci_set_byte(vdev->emulated_config_bits + pos + 1, 0xff); + switch (cap_id) { case PCI_CAP_ID_MSI: ret = vfio_setup_msi(vdev, pos); break; + case PCI_CAP_ID_EXP: + ret = vfio_setup_pcie_cap(vdev, pos, size); + break; case PCI_CAP_ID_MSIX: ret = vfio_setup_msix(vdev, pos); break; + case PCI_CAP_ID_PM: + vdev->pm_cap = pos; default: ret = pci_add_capability(pdev, cap_id, pos, size); break; @@ -1867,6 +2841,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) vdev->bars[i].fd_offset = reg_info.offset; vdev->bars[i].fd = vdev->fd; vdev->bars[i].nr = i; + QLIST_INIT(&vdev->bars[i].quirks); } reg_info.index = VFIO_PCI_ROM_REGION_INDEX; @@ -1904,6 +2879,47 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) } vdev->config_offset = reg_info.offset; + if ((vdev->features & VFIO_FEATURE_ENABLE_VGA) && + dev_info.num_regions > VFIO_PCI_VGA_REGION_INDEX) { + struct vfio_region_info vga_info = { + .argsz = sizeof(vga_info), + .index = VFIO_PCI_VGA_REGION_INDEX, + }; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &vga_info); + if (ret) { + error_report( + "vfio: Device does not support requested feature x-vga"); + goto error; + } + + if (!(vga_info.flags & VFIO_REGION_INFO_FLAG_READ) || + !(vga_info.flags & VFIO_REGION_INFO_FLAG_WRITE) || + vga_info.size < 0xbffff + 1) { + error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx", + (unsigned long)vga_info.flags, + (unsigned long)vga_info.size); + goto error; + } + + vdev->vga.fd_offset = vga_info.offset; + vdev->vga.fd = vdev->fd; + + vdev->vga.region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; + vdev->vga.region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; + QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_MEM].quirks); + + vdev->vga.region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE; + vdev->vga.region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO; + QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].quirks); + + vdev->vga.region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE; + vdev->vga.region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI; + QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks); + + vdev->has_vga = true; + } + error: if (ret) { QLIST_REMOVE(vdev, next); @@ -2003,6 +3019,16 @@ static int vfio_initfn(PCIDevice *pdev) goto out_put; } + /* vfio emulates a lot for us, but some bits need extra love */ + vdev->emulated_config_bits = g_malloc0(vdev->config_size); + + /* QEMU can choose to expose the ROM or not */ + memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4); + + /* QEMU can change multi-function devices to single function, or reverse */ + vdev->emulated_config_bits[PCI_HEADER_TYPE] = + PCI_HEADER_TYPE_MULTI_FUNCTION; + /* * Clear host resource mapping info. If we choose not to register a * BAR, such as might be the case with the option ROM, we can get @@ -2025,6 +3051,17 @@ static int vfio_initfn(PCIDevice *pdev) goto out_teardown; } + /* QEMU emulates all of MSI & MSIX */ + if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { + memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, + MSIX_CAP_LENGTH); + } + + if (pdev->cap_present & QEMU_PCI_CAP_MSI) { + memset(vdev->emulated_config_bits + pdev->msi_cap, 0xff, + vdev->msi_cap_size); + } + if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock, vfio_intx_mmap_enable, vdev); @@ -2035,6 +3072,8 @@ static int vfio_initfn(PCIDevice *pdev) } } + add_boot_device_path(vdev->bootindex, &pdev->qdev, NULL); + return 0; out_teardown: @@ -2042,6 +3081,7 @@ out_teardown: vfio_teardown_msi(vdev); vfio_unmap_bars(vdev); out_put: + g_free(vdev->emulated_config_bits); vfio_put_device(vdev); vfio_put_group(group); return ret; @@ -2059,6 +3099,7 @@ static void vfio_exitfn(PCIDevice *pdev) } vfio_teardown_msi(vdev); vfio_unmap_bars(vdev); + g_free(vdev->emulated_config_bits); vfio_put_device(vdev); vfio_put_group(group); } @@ -2074,6 +3115,26 @@ static void vfio_pci_reset(DeviceState *dev) vfio_disable_interrupts(vdev); + /* Make sure the device is in D0 */ + if (vdev->pm_cap) { + uint16_t pmcsr; + uint8_t state; + + pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + state = pmcsr & PCI_PM_CTRL_STATE_MASK; + if (state) { + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); + /* vfio handles the necessary delay here */ + pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + state = pmcsr & PCI_PM_CTRL_STATE_MASK; + if (state) { + error_report("vfio: Unable to power on device, stuck in D%d\n", + state); + } + } + } + /* * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master. * Also put INTx Disable in known state. @@ -2098,6 +3159,9 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIODevice, intx.mmap_timeout, 1100), + DEFINE_PROP_BIT("x-vga", VFIODevice, features, + VFIO_FEATURE_ENABLE_VGA_BIT, false), + DEFINE_PROP_INT32("bootindex", VFIODevice, bootindex, -1), /* * TODO - support passed fds... is this necessary? * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name), diff --git a/hw/vmxnet3.c b/hw/vmxnet3.c index 925be8084a..bdd256e9d5 100644 --- a/hw/vmxnet3.c +++ b/hw/vmxnet3.c @@ -969,7 +969,7 @@ vmxnet3_indicate_packet(VMXNET3State *s) struct Vmxnet3_RxDesc rxd; bool is_head = true; uint32_t rxd_idx; - uint32_t rx_ridx; + uint32_t rx_ridx = 0; struct Vmxnet3_RxCompDesc rxcd; uint32_t new_rxcd_gen = VMXNET3_INIT_GEN; diff --git a/hw/vt82c686.c b/hw/vt82c686.c index 452950826c..9d9b64eedd 100644 --- a/hw/vt82c686.c +++ b/hw/vt82c686.c @@ -360,7 +360,7 @@ static int vt82c686b_pm_initfn(PCIDevice *dev) acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io); + acpi_pm1_cnt_init(&s->ar, &s->io, 2); return 0; } diff --git a/hw/xen_backend.c b/hw/xen_backend.c index 24381b55e5..02693d7565 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -85,12 +85,20 @@ char *xenstore_read_str(const char *base, const char *node) int xenstore_write_int(const char *base, const char *node, int ival) { - char val[32]; + char val[12]; snprintf(val, sizeof(val), "%d", ival); return xenstore_write_str(base, node, val); } +int xenstore_write_int64(const char *base, const char *node, int64_t ival) +{ + char val[21]; + + snprintf(val, sizeof(val), "%"PRId64, ival); + return xenstore_write_str(base, node, val); +} + int xenstore_read_int(const char *base, const char *node, int *ival) { char *val; @@ -114,6 +122,11 @@ int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) return xenstore_write_int(xendev->be, node, ival); } +int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival) +{ + return xenstore_write_int64(xendev->be, node, ival); +} + char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) { return xenstore_read_str(xendev->be, node); diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 6d5c699c51..d04b985d10 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -63,11 +63,13 @@ extern const char *xen_protocol; /* xenstore helper functions */ int xenstore_write_str(const char *base, const char *node, const char *val); int xenstore_write_int(const char *base, const char *node, int ival); +int xenstore_write_int64(const char *base, const char *node, int64_t ival); char *xenstore_read_str(const char *base, const char *node); int xenstore_read_int(const char *base, const char *node, int *ival); int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival); char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); diff --git a/hw/xen_console.c b/hw/xen_console.c index a8db6f8d8f..c56ef4737f 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -241,9 +241,17 @@ static int con_initialise(struct XenDevice *xendev) return -1; xen_be_bind_evtchn(&con->xendev); - if (con->chr) - qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, - NULL, con); + if (con->chr) { + if (qemu_chr_fe_claim(con->chr) == 0) { + qemu_chr_add_handlers(con->chr, xencons_can_receive, + xencons_receive, NULL, con); + } else { + xen_be_printf(xendev, 0, + "xen_console_init error chardev %s already used\n", + con->chr->label); + con->chr = NULL; + } + } xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", con->ring_ref, @@ -260,8 +268,10 @@ static void con_disconnect(struct XenDevice *xendev) if (!xendev->dev) { return; } - if (con->chr) + if (con->chr) { qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(con->chr); + } xen_be_unbind_evtchn(&con->xendev); if (con->sring) { diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 83329e2e69..47a51cf014 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -700,7 +700,7 @@ static void blk_alloc(struct XenDevice *xendev) static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int index, qflags, info = 0; + int info = 0; /* read xenstore entries */ if (blkdev->params == NULL) { @@ -743,10 +743,7 @@ static int blk_init(struct XenDevice *xendev) } /* read-only ? */ - qflags = BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO; - if (strcmp(blkdev->mode, "w") == 0) { - qflags |= BDRV_O_RDWR; - } else { + if (strcmp(blkdev->mode, "w")) { info |= VDISK_READONLY; } @@ -755,6 +752,41 @@ static int blk_init(struct XenDevice *xendev) info |= VDISK_CDROM; } + blkdev->file_blk = BLOCK_SIZE; + + /* fill info + * blk_connect supplies sector-size and sectors + */ + xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); + xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); + xenstore_write_be_int(&blkdev->xendev, "info", info); + return 0; + +out_error: + g_free(blkdev->params); + blkdev->params = NULL; + g_free(blkdev->mode); + blkdev->mode = NULL; + g_free(blkdev->type); + blkdev->type = NULL; + g_free(blkdev->dev); + blkdev->dev = NULL; + g_free(blkdev->devtype); + blkdev->devtype = NULL; + return -1; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int pers, index, qflags; + + /* read-only ? */ + qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO; + if (strcmp(blkdev->mode, "w") == 0) { + qflags |= BDRV_O_RDWR; + } + /* init qemu block driver */ index = (blkdev->xendev.dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); @@ -770,7 +802,7 @@ static int blk_init(struct XenDevice *xendev) } } if (!blkdev->bs) { - goto out_error; + return -1; } } else { /* setup via qemu cmdline -> already setup for us */ @@ -778,7 +810,6 @@ static int blk_init(struct XenDevice *xendev) blkdev->bs = blkdev->dinfo->bdrv; } bdrv_attach_dev_nofail(blkdev->bs, blkdev); - blkdev->file_blk = BLOCK_SIZE; blkdev->file_size = bdrv_getlength(blkdev->bs); if (blkdev->file_size < 0) { xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", @@ -792,33 +823,10 @@ static int blk_init(struct XenDevice *xendev) blkdev->type, blkdev->fileproto, blkdev->filename, blkdev->file_size, blkdev->file_size >> 20); - /* fill info */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); - xenstore_write_be_int(&blkdev->xendev, "info", info); - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int(&blkdev->xendev, "sectors", - blkdev->file_size / blkdev->file_blk); - return 0; - -out_error: - g_free(blkdev->params); - blkdev->params = NULL; - g_free(blkdev->mode); - blkdev->mode = NULL; - g_free(blkdev->type); - blkdev->type = NULL; - g_free(blkdev->dev); - blkdev->dev = NULL; - g_free(blkdev->devtype); - blkdev->devtype = NULL; - return -1; -} - -static int blk_connect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers; + /* Fill in number of sector size and number of sectors */ + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int64(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { return -1; diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 5785290224..07c4badd98 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -516,6 +516,8 @@ static void enet_write(void *opaque, hwaddr addr, s->rcw[addr & 1] = value; if ((addr & 1) && value & RCW1_RST) { axienet_rx_reset(s); + } else { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } break; |