diff options
author | Anthony Liguori <anthony@codemonkey.ws> | 2013-09-30 17:15:01 -0500 |
---|---|---|
committer | Anthony Liguori <anthony@codemonkey.ws> | 2013-09-30 17:15:01 -0500 |
commit | eb322b8155120166fa259a8e96040f76ba4fde64 (patch) | |
tree | 7ad01ba082b3e4db3ef593377b81cb74ff862652 /hw | |
parent | 4235d77349e93e7157555f20f1892088f55edff4 (diff) | |
parent | e26d3e734650640fabd7d95ace4f3a6f88725e0b (diff) |
Merge remote-tracking branch 'mst/tags/for_anthony' into staging
pc,pci,virtio fixes and cleanups
This includes pc and pci cleanups and enhancements,
and a virtio-net bugfix related to softmac programming.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# gpg: Signature made Sun 29 Sep 2013 01:51:16 AM CDT using RSA key ID D28D5469
# gpg: Can't check signature: public key not found
# By Michael S. Tsirkin (8) and others
# Via Michael S. Tsirkin
* mst/tags/for_anthony:
smbios: Factor out smbios_maybe_add_str()
smbios: Make multiple -smbios type= accumulate sanely
smbios: Improve diagnostics for conflicting entries
smbios: Convert to QemuOpts
smbios: Normalize smbios_entry_add()'s error handling to exit(1)
virtio-net: fix up HMP NIC info string on reset
pci: remove explicit check to 64K ioport size
piix4: disable io on reset
piix: use 64 bit window programmed by guest
q35: use 64 bit window programmed by guest
pci: add helper to retrieve the 64-bit range
range: add min/max operations on ranges
range: add Range to typedefs
q35: make pci window address/size match guest cfg
Message-id: 1380437951-21788-1-git-send-email-mst@redhat.com
Diffstat (limited to 'hw')
-rw-r--r-- | hw/acpi/piix4.c | 1 | ||||
-rw-r--r-- | hw/i386/smbios.c | 339 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 1 | ||||
-rw-r--r-- | hw/pci-host/piix.c | 14 | ||||
-rw-r--r-- | hw/pci-host/q35.c | 24 | ||||
-rw-r--r-- | hw/pci/pci.c | 56 |
6 files changed, 327 insertions, 108 deletions
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 0b8d1d9da9..b46bd5ea6a 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -380,6 +380,7 @@ static void piix4_reset(void *opaque) /* Mark SMM as already inited (until KVM supports SMM). */ pci_conf[0x5B] = 0x02; } + pm_io_space_update(s); piix4_update_hotplug(s); } diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index e708cb8919..d3f1ee65c6 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -2,9 +2,11 @@ * SMBIOS Support * * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. * * Authors: * Alex Williamson <alex.williamson@hp.com> + * Markus Armbruster <armbru@redhat.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. @@ -13,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/config-file.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/i386/smbios.h" @@ -41,10 +44,117 @@ struct smbios_table { #define SMBIOS_FIELD_ENTRY 0 #define SMBIOS_TABLE_ENTRY 1 - static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; +static bool smbios_immutable; + +static struct { + bool seen; + int headertype; + Location loc; +} first_opt[2]; + +static struct { + const char *vendor, *version, *date; + bool have_major_minor; + uint8_t major, minor; +} type0; + +static struct { + const char *manufacturer, *product, *version, *serial, *sku, *family; + /* uuid is in qemu_uuid[] */ +} type1; + +static QemuOptsList qemu_smbios_opts = { + .name = "smbios", + .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + } +}; + +static const QemuOptDesc qemu_smbios_file_opts[] = { + { + .name = "file", + .type = QEMU_OPT_STRING, + .help = "binary file containing an SMBIOS element", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type0_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "vendor", + .type = QEMU_OPT_STRING, + .help = "vendor name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "date", + .type = QEMU_OPT_STRING, + .help = "release date", + },{ + .name = "release", + .type = QEMU_OPT_STRING, + .help = "revision number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type1_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "uuid", + .type = QEMU_OPT_STRING, + .help = "UUID", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + },{ + .name = "family", + .type = QEMU_OPT_STRING, + .help = "family name", + }, + { /* end of list */ } +}; + +static void smbios_register_config(void) +{ + qemu_add_opts(&qemu_smbios_opts); +} + +machine_init(smbios_register_config); static void smbios_validate_table(void) { @@ -54,57 +164,33 @@ static void smbios_validate_table(void) } } -uint8_t *smbios_get_table(size_t *length) -{ - smbios_validate_table(); - *length = smbios_entries_len; - return smbios_entries; -} - /* * To avoid unresolvable overlaps in data, don't allow both * tables and fields for the same smbios type. */ static void smbios_check_collision(int type, int entry) { - uint16_t *num_entries = (uint16_t *)smbios_entries; - struct smbios_header *header; - char *p; - int i; - - if (!num_entries) - return; - - p = (char *)(num_entries + 1); - - for (i = 0; i < *num_entries; i++) { - header = (struct smbios_header *)p; - if (entry == SMBIOS_TABLE_ENTRY && header->type == SMBIOS_FIELD_ENTRY) { - struct smbios_field *field = (void *)header; - if (type == field->type) { - error_report("SMBIOS type %d field already defined, " - "cannot add table", type); - exit(1); - } - } else if (entry == SMBIOS_FIELD_ENTRY && - header->type == SMBIOS_TABLE_ENTRY) { - struct smbios_structure_header *table = (void *)(header + 1); - if (type == table->type) { - error_report("SMBIOS type %d table already defined, " - "cannot add field", type); + if (type < ARRAY_SIZE(first_opt)) { + if (first_opt[type].seen) { + if (first_opt[type].headertype != entry) { + error_report("Can't mix file= and type= for same type"); + loc_push_restore(&first_opt[type].loc); + error_report("This is the conflicting setting"); + loc_pop(&first_opt[type].loc); exit(1); } + } else { + first_opt[type].seen = true; + first_opt[type].headertype = entry; + loc_save(&first_opt[type].loc); } - p += le16_to_cpu(header->length); } } -void smbios_add_field(int type, int offset, const void *data, size_t len) +static void smbios_add_field(int type, int offset, const void *data, size_t len) { struct smbios_field *field; - smbios_check_collision(type, SMBIOS_FIELD_ENTRY); - if (!smbios_entries) { smbios_entries_len = sizeof(uint16_t); smbios_entries = g_malloc0(smbios_entries_len); @@ -124,76 +210,94 @@ void smbios_add_field(int type, int offset, const void *data, size_t len) cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); } -static void smbios_build_type_0_fields(const char *t) +static void smbios_maybe_add_str(int type, int offset, const char *data) { - char buf[1024]; - unsigned char major, minor; - - if (get_param_value(buf, sizeof(buf), "vendor", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "date", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, + if (data) { + smbios_add_field(type, offset, data, strlen(data) + 1); + } +} + +static void smbios_build_type_0_fields(void) +{ + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), + type0.vendor); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), + type0.version); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_release_date_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "release", t)) { - if (sscanf(buf, "%hhu.%hhu", &major, &minor) != 2) { - error_report("Invalid release"); - exit(1); - } + type0.date); + if (type0.have_major_minor) { smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_major_release), - &major, 1); + &type0.major, 1); smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_minor_release), - &minor, 1); + &type0.minor, 1); } } -static void smbios_build_type_1_fields(const char *t) +static void smbios_build_type_1_fields(void) { - char buf[1024]; - - if (get_param_value(buf, sizeof(buf), "manufacturer", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "product", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "serial", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "uuid", t)) { - if (qemu_uuid_parse(buf, qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); - } + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), + type1.manufacturer); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), + type1.product); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), + type1.version); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), + type1.serial); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), + type1.sku); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), + type1.family); + if (qemu_uuid_set) { + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), + qemu_uuid, 16); + } +} + +uint8_t *smbios_get_table(size_t *length) +{ + if (!smbios_immutable) { + smbios_build_type_0_fields(); + smbios_build_type_1_fields(); + smbios_validate_table(); + smbios_immutable = true; + } + *length = smbios_entries_len; + return smbios_entries; +} + +static void save_opt(const char **dest, QemuOpts *opts, const char *name) +{ + const char *val = qemu_opt_get(opts, name); + + if (val) { + *dest = val; } - if (get_param_value(buf, sizeof(buf), "sku", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "family", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, family_str), - buf, strlen(buf) + 1); } -int smbios_entry_add(const char *t) +void smbios_entry_add(QemuOpts *opts) { - char buf[1024]; + Error *local_err = NULL; + const char *val; - if (get_param_value(buf, sizeof(buf), "file", t)) { + assert(!smbios_immutable); + val = qemu_opt_get(opts, "file"); + if (val) { struct smbios_structure_header *header; struct smbios_table *table; - int size = get_image_size(buf); + int size; + qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + + size = get_image_size(val); if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", buf); + error_report("Cannot read SMBIOS file %s", val); exit(1); } @@ -208,8 +312,8 @@ int smbios_entry_add(const char *t) table->header.type = SMBIOS_TABLE_ENTRY; table->header.length = cpu_to_le16(sizeof(*table) + size); - if (load_image(buf, table->data) != size) { - error_report("Failed to load SMBIOS file %s", buf); + if (load_image(val, table->data) != size) { + error_report("Failed to load SMBIOS file %s", val); exit(1); } @@ -222,18 +326,57 @@ int smbios_entry_add(const char *t) smbios_entries_len += sizeof(*table) + size; (*(uint16_t *)smbios_entries) = cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - return 0; + return; } - if (get_param_value(buf, sizeof(buf), "type", t)) { - unsigned long type = strtoul(buf, NULL, 0); + val = qemu_opt_get(opts, "type"); + if (val) { + unsigned long type = strtoul(val, NULL, 0); + + smbios_check_collision(type, SMBIOS_FIELD_ENTRY); + switch (type) { case 0: - smbios_build_type_0_fields(t); - return 0; + qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type0.vendor, opts, "vendor"); + save_opt(&type0.version, opts, "version"); + save_opt(&type0.date, opts, "date"); + + val = qemu_opt_get(opts, "release"); + if (val) { + if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { + error_report("Invalid release"); + exit(1); + } + type0.have_major_minor = true; + } + return; case 1: - smbios_build_type_1_fields(t); - return 0; + qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type1.manufacturer, opts, "manufacturer"); + save_opt(&type1.product, opts, "product"); + save_opt(&type1.version, opts, "version"); + save_opt(&type1.serial, opts, "serial"); + save_opt(&type1.sku, opts, "sku"); + save_opt(&type1.family, opts, "family"); + + val = qemu_opt_get(opts, "uuid"); + if (val) { + if (qemu_uuid_parse(val, qemu_uuid) != 0) { + error_report("Invalid UUID"); + exit(1); + } + qemu_uuid_set = true; + } + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); @@ -242,5 +385,5 @@ int smbios_entry_add(const char *t) } error_report("Must specify type= or file="); - return -1; + exit(1); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index dd41008fb0..22dbd053d4 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -314,6 +314,7 @@ static void virtio_net_reset(VirtIODevice *vdev) n->mac_table.uni_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); memset(n->vlans, 0, MAX_VLAN >> 3); } diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 221d82b637..c041149320 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -235,18 +235,24 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->pci_info.w64.begin, name, errp); + visit_type_uint64(v, &w64.begin, name, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->pci_info.w64.end, name, errp); + visit_type_uint64(v, &w64.end, name, errp); } static void i440fx_pcihost_initfn(Object *obj) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 0cb652d7f0..ad703a4bf7 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -89,18 +89,24 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - Q35PCIHost *s = Q35_HOST_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->mch.pci_info.w64.begin, name, errp); + visit_type_uint64(v, &w64.begin, name, errp); } static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - Q35PCIHost *s = Q35_HOST_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; - visit_type_uint64(v, &s->mch.pci_info.w64.end, name, errp); + pci_bus_get_w64_range(h->bus, &w64); + + visit_type_uint64(v, &w64.end, name, errp); } static Property mch_props[] = { @@ -214,6 +220,16 @@ static void mch_update_pciexbar(MCHPCIState *mch) } addr = pciexbar & addr_mask; pcie_host_mmcfg_update(pehb, enable, addr, length); + /* Leave enough space for the MCFG BAR */ + /* + * TODO: this matches current bios behaviour, but it's not a power of two, + * which means an MTRR can't cover it exactly. + */ + if (enable) { + mch->pci_info.w32.begin = addr + length; + } else { + mch->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; + } } /* PAM */ diff --git a/hw/pci/pci.c b/hw/pci/pci.c index ad1c1ca91e..00554a05ac 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1028,8 +1028,10 @@ static pcibus_t pci_bar_address(PCIDevice *d, } new_addr = pci_get_long(d->config + bar) & ~(size - 1); last_addr = new_addr + size - 1; - /* NOTE: we have only 64K ioports on PC */ - if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) { + /* Check if 32 bit BAR wraps around explicitly. + * TODO: make priorities correct and remove this work around. + */ + if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) { return PCI_BAR_UNMAPPED; } return new_addr; @@ -2257,6 +2259,56 @@ void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) bus->iommu_opaque = opaque; } +static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) +{ + Range *range = opaque; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); + int r; + + if (!(cmd & PCI_COMMAND_MEMORY)) { + return; + } + + if (pc->is_bridge) { + pcibus_t base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + pcibus_t limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + base = MAX(base, 0x1ULL << 32); + + if (limit >= base) { + Range pref_range; + pref_range.begin = base; + pref_range.end = limit + 1; + range_extend(range, &pref_range); + } + } + for (r = 0; r < PCI_NUM_REGIONS; ++r) { + PCIIORegion *region = &dev->io_regions[r]; + Range region_range; + + if (!region->size || + (region->type & PCI_BASE_ADDRESS_SPACE_IO) || + !(region->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { + continue; + } + region_range.begin = pci_get_quad(dev->config + pci_bar(dev, r)); + region_range.end = region_range.begin + region->size; + + region_range.begin = MAX(region_range.begin, 0x1ULL << 32); + + if (region_range.end - 1 >= region_range.begin) { + range_extend(range, ®ion_range); + } + } +} + +void pci_bus_get_w64_range(PCIBus *bus, Range *range) +{ + range->begin = range->end = 0; + pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, |