diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-12-09 20:08:54 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-12-09 20:08:54 +0000 |
commit | 5e7b204dbfae9a562fc73684986f936b97f63877 (patch) | |
tree | 6cbdd59e56f5b9342c8252deed085f21485ac0fb /hw | |
parent | 28db503fd7a750861aa4381653800d4eb28b5426 (diff) | |
parent | fdfa3b1d6f9edd97c807df496a0d8e9ea49240da (diff) |
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
pc,pci,virtio: fixes, cleanups
Lots of fixes, cleanups.
CPU hot-unplug improvements.
A new AER property for virtio devices, adding a dummy AER capability.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# gpg: Signature made Wed 09 Dec 2020 18:04:28 GMT
# gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg: issuer "mst@redhat.com"
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full]
# gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full]
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67
# Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469
* remotes/mst/tags/for_upstream: (65 commits)
hw/virtio-pci Added AER capability.
hw/virtio-pci Added counter for pcie capabilities offsets.
pcie_aer: Fix help message of pcie_aer_inject_error command
x86: ich9: let firmware negotiate 'CPU hot-unplug with SMI' feature
x86: ich9: factor out "guest_cpu_hotplug_features"
tests/acpi: update expected files
x86: acpi: let the firmware handle pending "CPU remove" events in SMM
tests/acpi: allow expected files change
x86: acpi: introduce AcpiPmInfo::smi_on_cpu_unplug
acpi: cpuhp: introduce 'firmware performs eject' status/control bits
hw/i386/pc: add max combined fw size as machine configuration option
block/export: avoid g_return_val_if() input validation
contrib/vhost-user-input: avoid g_return_val_if() input validation
contrib/vhost-user-gpu: avoid g_return_val_if() input validation
contrib/vhost-user-blk: avoid g_return_val_if() input validation
.gitlab-ci: add build-libvhost-user
libvhost-user: add a simple link test without glib
libvhost-user: make it a meson subproject
libvhost-user: drop qemu/osdep.h dependency
libvhost-user: remove qemu/compiler.h usage
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/acpi/aml-build.c | 285 | ||||
-rw-r--r-- | hw/acpi/cpu.c | 26 | ||||
-rw-r--r-- | hw/acpi/trace-events | 2 | ||||
-rw-r--r-- | hw/arm/smmu-common.c | 13 | ||||
-rw-r--r-- | hw/arm/smmuv3.c | 13 | ||||
-rw-r--r-- | hw/arm/virt-acpi-build.c | 31 | ||||
-rw-r--r-- | hw/arm/virt.c | 16 | ||||
-rw-r--r-- | hw/core/machine.c | 3 | ||||
-rw-r--r-- | hw/core/qdev.c | 19 | ||||
-rw-r--r-- | hw/i386/acpi-build.c | 298 | ||||
-rw-r--r-- | hw/i386/intel_iommu.c | 92 | ||||
-rw-r--r-- | hw/i386/pc.c | 74 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 14 | ||||
-rw-r--r-- | hw/i386/pc_q35.c | 13 | ||||
-rw-r--r-- | hw/i386/pc_sysfw.c | 15 | ||||
-rw-r--r-- | hw/isa/lpc_ich9.c | 16 | ||||
-rw-r--r-- | hw/misc/tz-mpc.c | 32 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 282 | ||||
-rw-r--r-- | hw/nvram/fw_cfg.c | 23 | ||||
-rw-r--r-- | hw/pci-host/gpex-acpi.c | 166 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 15 | ||||
-rw-r--r-- | hw/ppc/spapr_iommu.c | 15 | ||||
-rw-r--r-- | hw/s390x/s390-pci-inst.c | 27 | ||||
-rw-r--r-- | hw/s390x/s390-virtio-ccw.c | 14 | ||||
-rw-r--r-- | hw/virtio/vhost.c | 2 | ||||
-rw-r--r-- | hw/virtio/virtio-iommu.c | 30 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 20 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.h | 4 | ||||
-rw-r--r-- | hw/virtio/virtio.c | 15 |
29 files changed, 897 insertions, 678 deletions
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 3792ba96ce..f976aa667b 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -27,6 +27,9 @@ #include "sysemu/numa.h" #include "hw/boards.h" #include "hw/acpi/tpm.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" static GArray *build_alloc_array(void) { @@ -55,6 +58,128 @@ static void build_append_array(GArray *array, GArray *val) #define ACPI_NAMESEG_LEN 4 +void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit) +{ + CrsRangeEntry *entry; + + entry = g_malloc(sizeof(*entry)); + entry->base = base; + entry->limit = limit; + + g_ptr_array_add(ranges, entry); +} + +static void crs_range_free(gpointer data) +{ + CrsRangeEntry *entry = (CrsRangeEntry *)data; + g_free(entry); +} + +void crs_range_set_init(CrsRangeSet *range_set) +{ + range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + range_set->mem_64bit_ranges = + g_ptr_array_new_with_free_func(crs_range_free); +} + +void crs_range_set_free(CrsRangeSet *range_set) +{ + g_ptr_array_free(range_set->io_ranges, true); + g_ptr_array_free(range_set->mem_ranges, true); + g_ptr_array_free(range_set->mem_64bit_ranges, true); +} + +static gint crs_range_compare(gconstpointer a, gconstpointer b) +{ + CrsRangeEntry *entry_a = *(CrsRangeEntry **)a; + CrsRangeEntry *entry_b = *(CrsRangeEntry **)b; + + if (entry_a->base < entry_b->base) { + return -1; + } else if (entry_a->base > entry_b->base) { + return 1; + } else { + return 0; + } +} + +/* + * crs_replace_with_free_ranges - given the 'used' ranges within [start - end] + * interval, computes the 'free' ranges from the same interval. + * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function + * will return { [base - a1], [a2 - b1], [b2 - limit] }. + */ +void crs_replace_with_free_ranges(GPtrArray *ranges, + uint64_t start, uint64_t end) +{ + GPtrArray *free_ranges = g_ptr_array_new(); + uint64_t free_base = start; + int i; + + g_ptr_array_sort(ranges, crs_range_compare); + for (i = 0; i < ranges->len; i++) { + CrsRangeEntry *used = g_ptr_array_index(ranges, i); + + if (free_base < used->base) { + crs_range_insert(free_ranges, free_base, used->base - 1); + } + + free_base = used->limit + 1; + } + + if (free_base < end) { + crs_range_insert(free_ranges, free_base, end); + } + + g_ptr_array_set_size(ranges, 0); + for (i = 0; i < free_ranges->len; i++) { + g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); + } + + g_ptr_array_free(free_ranges, true); +} + +/* + * crs_range_merge - merges adjacent ranges in the given array. + * Array elements are deleted and replaced with the merged ranges. + */ +static void crs_range_merge(GPtrArray *range) +{ + GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; + uint64_t range_base, range_limit; + int i; + + if (!range->len) { + return; + } + + g_ptr_array_sort(range, crs_range_compare); + + entry = g_ptr_array_index(range, 0); + range_base = entry->base; + range_limit = entry->limit; + for (i = 1; i < range->len; i++) { + entry = g_ptr_array_index(range, i); + if (entry->base - 1 == range_limit) { + range_limit = entry->limit; + } else { + crs_range_insert(tmp, range_base, range_limit); + range_base = entry->base; + range_limit = entry->limit; + } + } + crs_range_insert(tmp, range_base, range_limit); + + g_ptr_array_set_size(range, 0); + for (i = 0; i < tmp->len; i++) { + entry = g_ptr_array_index(tmp, i); + crs_range_insert(range, entry->base, entry->limit); + } + g_ptr_array_free(tmp, true); +} + static void build_append_nameseg(GArray *array, const char *seg) { @@ -1951,6 +2076,166 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) tpm2_ptr, "TPM2", table_data->len - tpm2_start, 4, NULL, NULL); } +Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) +{ + Aml *crs = aml_resource_template(); + CrsRangeSet temp_range_set; + CrsRangeEntry *entry; + uint8_t max_bus = pci_bus_num(host->bus); + uint8_t type; + int devfn; + int i; + + crs_range_set_init(&temp_range_set); + for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { + uint64_t range_base, range_limit; + PCIDevice *dev = host->bus->devices[devfn]; + + if (!dev) { + continue; + } + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *r = &dev->io_regions[i]; + + range_base = r->addr; + range_limit = r->addr + r->size - 1; + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (!range_base || range_base > range_limit) { + continue; + } + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); + } else { /* "memory" */ + uint64_t length = range_limit - range_base + 1; + if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { + crs_range_insert(temp_range_set.mem_ranges, range_base, + range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } + } + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS]; + if (subordinate > max_bus) { + max_bus = subordinate; + } + + range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <= range_limit) { + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); + } + + range_base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + range_limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <= range_limit) { + uint64_t length = range_limit - range_base + 1; + if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } + } + + range_base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + range_limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <= range_limit) { + uint64_t length = range_limit - range_base + 1; + if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } + } + } + } + + crs_range_merge(temp_range_set.io_ranges); + for (i = 0; i < temp_range_set.io_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.io_ranges, i); + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->io_ranges, entry->base, entry->limit); + } + + crs_range_merge(temp_range_set.mem_ranges); + for (i = 0; i < temp_range_set.mem_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.mem_ranges, i); + assert(entry->limit <= UINT32_MAX && + (entry->limit - entry->base + 1) <= UINT32_MAX); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->mem_ranges, entry->base, entry->limit); + } + + crs_range_merge(temp_range_set.mem_64bit_ranges); + for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->mem_64bit_ranges, + entry->base, entry->limit); + } + + crs_range_set_free(&temp_range_set); + + aml_append(crs, + aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + 0, + pci_bus_num(host->bus), + max_bus, + 0, + max_bus - pci_bus_num(host->bus) + 1)); + + return crs; +} + /* ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors */ static Aml *aml_serial_bus_device(uint8_t serial_bus_type, uint8_t flags, uint16_t type_flags, diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index f099b50927..6350caa765 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -71,6 +71,7 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) val |= cdev->cpu ? 1 : 0; val |= cdev->is_inserting ? 2 : 0; val |= cdev->is_removing ? 4 : 0; + val |= cdev->fw_remove ? 16 : 0; trace_cpuhp_acpi_read_flags(cpu_st->selector, val); break; case ACPI_CPU_CMD_DATA_OFFSET_RW: @@ -148,6 +149,14 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, NULL); object_unparent(OBJECT(dev)); + cdev->fw_remove = false; + } else if (data & 16) { + if (!cdev->cpu || cdev->cpu == first_cpu) { + trace_cpuhp_acpi_fw_remove_invalid_cpu(cpu_st->selector); + break; + } + trace_cpuhp_acpi_fw_remove_cpu(cpu_st->selector); + cdev->fw_remove = true; } break; case ACPI_CPU_CMD_OFFSET_WR: @@ -159,7 +168,8 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, do { cdev = &cpu_st->devs[iter]; - if (cdev->is_inserting || cdev->is_removing) { + if (cdev->is_inserting || cdev->is_removing || + cdev->fw_remove) { cpu_st->selector = iter; trace_cpuhp_acpi_cpu_has_events(cpu_st->selector, cdev->is_inserting, cdev->is_removing); @@ -332,6 +342,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_INSERT_EVENT "CINS" #define CPU_REMOVE_EVENT "CRMV" #define CPU_EJECT_EVENT "CEJ0" +#define CPU_FW_EJECT_EVENT "CEJF" void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, @@ -384,7 +395,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(field, aml_named_field(CPU_REMOVE_EVENT, 1)); /* initiates device eject, write only */ aml_append(field, aml_named_field(CPU_EJECT_EVENT, 1)); - aml_append(field, aml_reserved_field(4)); + /* tell firmware to do device eject, write only */ + aml_append(field, aml_named_field(CPU_FW_EJECT_EVENT, 1)); + aml_append(field, aml_reserved_field(3)); aml_append(field, aml_named_field(CPU_COMMAND, 8)); aml_append(cpu_ctrl_dev, field); @@ -419,6 +432,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT); Aml *rm_evt = aml_name("%s.%s", cphp_res_path, CPU_REMOVE_EVENT); Aml *ej_evt = aml_name("%s.%s", cphp_res_path, CPU_EJECT_EVENT); + Aml *fw_ej_evt = aml_name("%s.%s", cphp_res_path, CPU_FW_EJECT_EVENT); aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010"))); aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05"))); @@ -461,7 +475,13 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); aml_append(method, aml_store(idx, cpu_selector)); - aml_append(method, aml_store(one, ej_evt)); + if (opts.fw_unplugs_cpu) { + aml_append(method, aml_store(one, fw_ej_evt)); + aml_append(method, aml_store(aml_int(OVMF_CPUHP_SMI_CMD), + aml_name("%s", opts.smi_path))); + } else { + aml_append(method, aml_store(one, ej_evt)); + } aml_append(method, aml_release(ctrl_lock)); } aml_append(cpus_dev, method); diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index afbc77de1c..f91ced477d 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -29,6 +29,8 @@ cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_fw_remove_invalid_cpu(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_fw_remove_cpu(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "idx[0x%"PRIx32"] OST EVENT: 0x%"PRIx32 cpuhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "idx[0x%"PRIx32"] OST STATUS: 0x%"PRIx32 diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3838db1395..405d5c5325 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -465,14 +465,15 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) /* Unmap the whole notifier's range */ static void smmu_unmap_notifier_range(IOMMUNotifier *n) { - IOMMUTLBEntry entry; + IOMMUTLBEvent event; - entry.target_as = &address_space_memory; - entry.iova = n->start; - entry.perm = IOMMU_NONE; - entry.addr_mask = n->end - n->start; + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.iova = n->start; + event.entry.perm = IOMMU_NONE; + event.entry.addr_mask = n->end - n->start; - memory_region_notify_one(n, &entry); + memory_region_notify_iommu_one(n, &event); } /* Unmap all notifiers attached to @mr */ diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 22607c3784..bbca0e9f20 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -800,7 +800,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, uint8_t tg, uint64_t num_pages) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); - IOMMUTLBEntry entry; + IOMMUTLBEvent event; uint8_t granule = tg; if (!tg) { @@ -823,12 +823,13 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, granule = tt->granule_sz; } - entry.target_as = &address_space_memory; - entry.iova = iova; - entry.addr_mask = num_pages * (1 << granule) - 1; - entry.perm = IOMMU_NONE; + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.iova = iova; + event.entry.addr_mask = num_pages * (1 << granule) - 1; + event.entry.perm = IOMMU_NONE; - memory_region_notify_one(n, &entry); + memory_region_notify_iommu_one(n, &event); } /* invalidate an asid/iova range tuple in all mr's */ diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9747a6458f..711cf2069f 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -57,6 +57,8 @@ #define ARM_SPI_BASE 32 +#define ACPI_BUILD_TABLE_SIZE 0x20000 + static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) { uint16_t i; @@ -153,7 +155,8 @@ static void acpi_dsdt_add_virtio(Aml *scope, } static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, - uint32_t irq, bool use_highmem, bool highmem_ecam) + uint32_t irq, bool use_highmem, bool highmem_ecam, + VirtMachineState *vms) { int ecam_id = VIRT_ECAM_ID(highmem_ecam); struct GPEXConfig cfg = { @@ -161,6 +164,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, .pio = memmap[VIRT_PCIE_PIO], .ecam = memmap[ecam_id], .irq = irq, + .bus = vms->bus, }; if (use_highmem) { @@ -609,7 +613,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), - vms->highmem, vms->highmem_ecam); + vms->highmem, vms->highmem_ecam, vms); if (vms->acpi_dev) { build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev), @@ -654,6 +658,15 @@ struct AcpiBuildState { bool patched; } AcpiBuildState; +static void acpi_align_size(GArray *blob, unsigned align) +{ + /* + * Align size to multiple of given size. This reduces the chance + * we need to change size in the future (breaking cross version migration). + */ + g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); +} + static void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) { @@ -741,6 +754,20 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) build_rsdp(tables->rsdp, tables->linker, &rsdp_data); } + /* + * The align size is 128, warn if 64k is not enough therefore + * the align size could be resized. + */ + if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { + warn_report("ACPI table size %u exceeds %d bytes," + " migration may not work", + tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); + error_printf("Try removing CPUs, NUMA nodes, memory slots" + " or PCI bridges."); + } + acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); + + /* Cleanup memory that's no longer used. */ g_array_free(table_offsets, true); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 27dbeb549e..22572c32b7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1289,7 +1289,8 @@ static void create_pcie(VirtMachineState *vms) } pci = PCI_HOST_BRIDGE(dev); - if (pci->bus) { + vms->bus = pci->bus; + if (vms->bus) { for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -1346,7 +1347,7 @@ static void create_pcie(VirtMachineState *vms) switch (vms->iommu) { case VIRT_IOMMU_SMMUV3: - create_smmu(vms, pci->bus); + create_smmu(vms, vms->bus); qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", 0x0, vms->iommu_phandle, 0x0, 0x10000); break; @@ -1481,6 +1482,8 @@ void virt_machine_done(Notifier *notifier, void *data) exit(1); } + fw_cfg_add_extra_pci_roots(vms->bus, vms->fw_cfg); + virt_acpi_setup(vms); virt_build_smbios(vms); } @@ -2587,10 +2590,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_6_0_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(6, 0) + static void virt_machine_5_2_options(MachineClass *mc) { + virt_machine_6_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(5, 2) +DEFINE_VIRT_MACHINE(5, 2) static void virt_machine_5_1_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index d0408049b5..9c41b94e0d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -28,6 +28,9 @@ #include "hw/mem/nvdimm.h" #include "migration/vmstate.h" +GlobalProperty hw_compat_5_2[] = {}; +const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2); + GlobalProperty hw_compat_5_1[] = { { "vhost-scsi", "num_queues", "1"}, { "vhost-user-blk", "num-queues", "1"}, diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 262bca716f..cbdff0b6c6 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -214,26 +214,17 @@ void device_listener_unregister(DeviceListener *listener) bool qdev_should_hide_device(QemuOpts *opts) { - int rc = -1; DeviceListener *listener; QTAILQ_FOREACH(listener, &device_listeners, link) { - if (listener->should_be_hidden) { - /* - * should_be_hidden_will return - * 1 if device matches opts and it should be hidden - * 0 if device matches opts and should not be hidden - * -1 if device doesn't match ops - */ - rc = listener->should_be_hidden(listener, opts); - } - - if (rc > 0) { - break; + if (listener->hide_device) { + if (listener->hide_device(listener, opts)) { + return true; + } } } - return rc > 0; + return false; } void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1f5c211245..f18b71dea9 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -96,6 +96,7 @@ typedef struct AcpiPmInfo { bool s4_disabled; bool pcihp_bridge_en; bool smi_on_cpuhp; + bool smi_on_cpu_unplug; bool pcihp_root_en; uint8_t s4_val; AcpiFadtData fadt; @@ -197,6 +198,7 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) pm->pcihp_io_base = 0; pm->pcihp_io_len = 0; pm->smi_on_cpuhp = false; + pm->smi_on_cpu_unplug = false; assert(obj); init_common_fadt_data(machine, obj, &pm->fadt); @@ -220,6 +222,8 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE; pm->smi_on_cpuhp = !!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT)); + pm->smi_on_cpu_unplug = + !!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)); } /* The above need not be conditional on machine type because the reset port @@ -613,299 +617,6 @@ static Aml *build_prt(bool is_pci0_prt) return method; } -typedef struct CrsRangeEntry { - uint64_t base; - uint64_t limit; -} CrsRangeEntry; - -static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit) -{ - CrsRangeEntry *entry; - - entry = g_malloc(sizeof(*entry)); - entry->base = base; - entry->limit = limit; - - g_ptr_array_add(ranges, entry); -} - -static void crs_range_free(gpointer data) -{ - CrsRangeEntry *entry = (CrsRangeEntry *)data; - g_free(entry); -} - -typedef struct CrsRangeSet { - GPtrArray *io_ranges; - GPtrArray *mem_ranges; - GPtrArray *mem_64bit_ranges; - } CrsRangeSet; - -static void crs_range_set_init(CrsRangeSet *range_set) -{ - range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free); - range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); - range_set->mem_64bit_ranges = - g_ptr_array_new_with_free_func(crs_range_free); -} - -static void crs_range_set_free(CrsRangeSet *range_set) -{ - g_ptr_array_free(range_set->io_ranges, true); - g_ptr_array_free(range_set->mem_ranges, true); - g_ptr_array_free(range_set->mem_64bit_ranges, true); -} - -static gint crs_range_compare(gconstpointer a, gconstpointer b) -{ - CrsRangeEntry *entry_a = *(CrsRangeEntry **)a; - CrsRangeEntry *entry_b = *(CrsRangeEntry **)b; - - if (entry_a->base < entry_b->base) { - return -1; - } else if (entry_a->base > entry_b->base) { - return 1; - } else { - return 0; - } -} - -/* - * crs_replace_with_free_ranges - given the 'used' ranges within [start - end] - * interval, computes the 'free' ranges from the same interval. - * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function - * will return { [base - a1], [a2 - b1], [b2 - limit] }. - */ -static void crs_replace_with_free_ranges(GPtrArray *ranges, - uint64_t start, uint64_t end) -{ - GPtrArray *free_ranges = g_ptr_array_new(); - uint64_t free_base = start; - int i; - - g_ptr_array_sort(ranges, crs_range_compare); - for (i = 0; i < ranges->len; i++) { - CrsRangeEntry *used = g_ptr_array_index(ranges, i); - - if (free_base < used->base) { - crs_range_insert(free_ranges, free_base, used->base - 1); - } - - free_base = used->limit + 1; - } - - if (free_base < end) { - crs_range_insert(free_ranges, free_base, end); - } - - g_ptr_array_set_size(ranges, 0); - for (i = 0; i < free_ranges->len; i++) { - g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); - } - - g_ptr_array_free(free_ranges, true); -} - -/* - * crs_range_merge - merges adjacent ranges in the given array. - * Array elements are deleted and replaced with the merged ranges. - */ -static void crs_range_merge(GPtrArray *range) -{ - GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); - CrsRangeEntry *entry; - uint64_t range_base, range_limit; - int i; - - if (!range->len) { - return; - } - - g_ptr_array_sort(range, crs_range_compare); - - entry = g_ptr_array_index(range, 0); - range_base = entry->base; - range_limit = entry->limit; - for (i = 1; i < range->len; i++) { - entry = g_ptr_array_index(range, i); - if (entry->base - 1 == range_limit) { - range_limit = entry->limit; - } else { - crs_range_insert(tmp, range_base, range_limit); - range_base = entry->base; - range_limit = entry->limit; - } - } - crs_range_insert(tmp, range_base, range_limit); - - g_ptr_array_set_size(range, 0); - for (i = 0; i < tmp->len; i++) { - entry = g_ptr_array_index(tmp, i); - crs_range_insert(range, entry->base, entry->limit); - } - g_ptr_array_free(tmp, true); -} - -static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) -{ - Aml *crs = aml_resource_template(); - CrsRangeSet temp_range_set; - CrsRangeEntry *entry; - uint8_t max_bus = pci_bus_num(host->bus); - uint8_t type; - int devfn; - int i; - - crs_range_set_init(&temp_range_set); - for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { - uint64_t range_base, range_limit; - PCIDevice *dev = host->bus->devices[devfn]; - - if (!dev) { - continue; - } - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - PCIIORegion *r = &dev->io_regions[i]; - - range_base = r->addr; - range_limit = r->addr + r->size - 1; - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (!range_base || range_base > range_limit) { - continue; - } - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - crs_range_insert(temp_range_set.io_ranges, - range_base, range_limit); - } else { /* "memory" */ - uint64_t length = range_limit - range_base + 1; - if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { - crs_range_insert(temp_range_set.mem_ranges, range_base, - range_limit); - } else { - crs_range_insert(temp_range_set.mem_64bit_ranges, - range_base, range_limit); - } - } - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS]; - if (subordinate > max_bus) { - max_bus = subordinate; - } - - range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <= range_limit) { - crs_range_insert(temp_range_set.io_ranges, - range_base, range_limit); - } - - range_base = - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - range_limit = - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <= range_limit) { - uint64_t length = range_limit - range_base + 1; - if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { - crs_range_insert(temp_range_set.mem_ranges, - range_base, range_limit); - } else { - crs_range_insert(temp_range_set.mem_64bit_ranges, - range_base, range_limit); - } - } - - range_base = - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - range_limit = - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <= range_limit) { - uint64_t length = range_limit - range_base + 1; - if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { - crs_range_insert(temp_range_set.mem_ranges, - range_base, range_limit); - } else { - crs_range_insert(temp_range_set.mem_64bit_ranges, - range_base, range_limit); - } - } - } - } - - crs_range_merge(temp_range_set.io_ranges); - for (i = 0; i < temp_range_set.io_ranges->len; i++) { - entry = g_ptr_array_index(temp_range_set.io_ranges, i); - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->io_ranges, entry->base, entry->limit); - } - - crs_range_merge(temp_range_set.mem_ranges); - for (i = 0; i < temp_range_set.mem_ranges->len; i++) { - entry = g_ptr_array_index(temp_range_set.mem_ranges, i); - assert(entry->limit <= UINT32_MAX && - (entry->limit - entry->base + 1) <= UINT32_MAX); - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->mem_ranges, entry->base, entry->limit); - } - - crs_range_merge(temp_range_set.mem_64bit_ranges); - for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) { - entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i); - aml_append(crs, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->mem_64bit_ranges, - entry->base, entry->limit); - } - - crs_range_set_free(&temp_range_set); - - aml_append(crs, - aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0, - pci_bus_num(host->bus), - max_bus, - 0, - max_bus - pci_bus_num(host->bus) + 1)); - - return crs; -} - static void build_hpet_aml(Aml *table) { Aml *crs; @@ -1582,6 +1293,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, CPUHotplugFeatures opts = { .acpi_1_compatible = true, .has_legacy_cphp = true, .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL, + .fw_unplugs_cpu = pm->smi_on_cpu_unplug, }; build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 70ac837733..0cc71e4057 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1073,7 +1073,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, } } -typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); +typedef int (*vtd_page_walk_hook)(IOMMUTLBEvent *event, void *private); /** * Constant information used during page walking @@ -1094,11 +1094,12 @@ typedef struct { uint16_t domain_id; } vtd_page_walk_info; -static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) +static int vtd_page_walk_one(IOMMUTLBEvent *event, vtd_page_walk_info *info) { VTDAddressSpace *as = info->as; vtd_page_walk_hook hook_fn = info->hook_fn; void *private = info->private; + IOMMUTLBEntry *entry = &event->entry; DMAMap target = { .iova = entry->iova, .size = entry->addr_mask, @@ -1107,7 +1108,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) }; DMAMap *mapped = iova_tree_find(as->iova_tree, &target); - if (entry->perm == IOMMU_NONE && !info->notify_unmap) { + if (event->type == IOMMU_NOTIFIER_UNMAP && !info->notify_unmap) { trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); return 0; } @@ -1115,7 +1116,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) assert(hook_fn); /* Update local IOVA mapped ranges */ - if (entry->perm) { + if (event->type == IOMMU_NOTIFIER_MAP) { if (mapped) { /* If it's exactly the same translation, skip */ if (!memcmp(mapped, &target, sizeof(target))) { @@ -1141,19 +1142,21 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) int ret; /* Emulate an UNMAP */ + event->type = IOMMU_NOTIFIER_UNMAP; entry->perm = IOMMU_NONE; trace_vtd_page_walk_one(info->domain_id, entry->iova, entry->translated_addr, entry->addr_mask, entry->perm); - ret = hook_fn(entry, private); + ret = hook_fn(event, private); if (ret) { return ret; } /* Drop any existing mapping */ iova_tree_remove(as->iova_tree, &target); - /* Recover the correct permission */ + /* Recover the correct type */ + event->type = IOMMU_NOTIFIER_MAP; entry->perm = cache_perm; } } @@ -1170,7 +1173,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) trace_vtd_page_walk_one(info->domain_id, entry->iova, entry->translated_addr, entry->addr_mask, entry->perm); - return hook_fn(entry, private); + return hook_fn(event, private); } /** @@ -1191,7 +1194,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, uint32_t offset; uint64_t slpte; uint64_t subpage_size, subpage_mask; - IOMMUTLBEntry entry; + IOMMUTLBEvent event; uint64_t iova = start; uint64_t iova_next; int ret = 0; @@ -1245,13 +1248,15 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, * * In either case, we send an IOTLB notification down. */ - entry.target_as = &address_space_memory; - entry.iova = iova & subpage_mask; - entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); - entry.addr_mask = ~subpage_mask; + event.entry.target_as = &address_space_memory; + event.entry.iova = iova & subpage_mask; + event.entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + event.entry.addr_mask = ~subpage_mask; /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); - ret = vtd_page_walk_one(&entry, info); + event.entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP : + IOMMU_NOTIFIER_UNMAP; + ret = vtd_page_walk_one(&event, info); } if (ret < 0) { @@ -1430,10 +1435,10 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } -static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, +static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event, void *private) { - memory_region_notify_iommu((IOMMUMemoryRegion *)private, 0, *entry); + memory_region_notify_iommu(private, 0, *event); return 0; } @@ -1473,6 +1478,10 @@ static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) VTDContextEntry ce; IOMMUNotifier *n; + if (!(vtd_as->iommu.iommu_notify_flags & IOMMU_NOTIFIER_IOTLB_EVENTS)) { + return 0; + } + ret = vtd_dev_to_context_entry(vtd_as->iommu_state, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); @@ -1993,14 +2002,17 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, * page tables. We just deliver the PSI down to * invalidate caches. */ - IOMMUTLBEntry entry = { - .target_as = &address_space_memory, - .iova = addr, - .translated_addr = 0, - .addr_mask = size - 1, - .perm = IOMMU_NONE, + IOMMUTLBEvent event = { + .type = IOMMU_NOTIFIER_UNMAP, + .entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = size - 1, + .perm = IOMMU_NONE, + }, }; - memory_region_notify_iommu(&vtd_as->iommu, 0, entry); + memory_region_notify_iommu(&vtd_as->iommu, 0, event); } } } @@ -2412,7 +2424,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { VTDAddressSpace *vtd_dev_as; - IOMMUTLBEntry entry; + IOMMUTLBEvent event; struct VTDBus *vtd_bus; hwaddr addr; uint64_t sz; @@ -2460,12 +2472,13 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, sz = VTD_PAGE_SIZE; } - entry.target_as = &vtd_dev_as->as; - entry.addr_mask = sz - 1; - entry.iova = addr; - entry.perm = IOMMU_NONE; - entry.translated_addr = 0; - memory_region_notify_iommu(&vtd_dev_as->iommu, 0, entry); + event.type = IOMMU_NOTIFIER_DEVIOTLB_UNMAP; + event.entry.target_as = &vtd_dev_as->as; + event.entry.addr_mask = sz - 1; + event.entry.iova = addr; + event.entry.perm = IOMMU_NONE; + event.entry.translated_addr = 0; + memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); done: return true; @@ -3485,19 +3498,20 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) size = remain = end - start + 1; while (remain >= VTD_PAGE_SIZE) { - IOMMUTLBEntry entry; + IOMMUTLBEvent event; uint64_t mask = get_naturally_aligned_size(start, remain, s->aw_bits); assert(mask); - entry.iova = start; - entry.addr_mask = mask - 1; - entry.target_as = &address_space_memory; - entry.perm = IOMMU_NONE; + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.iova = start; + event.entry.addr_mask = mask - 1; + event.entry.target_as = &address_space_memory; + event.entry.perm = IOMMU_NONE; /* This field is meaningless for unmap */ - entry.translated_addr = 0; + event.entry.translated_addr = 0; - memory_region_notify_one(n, &entry); + memory_region_notify_iommu_one(n, &event); start += mask; remain -= mask; @@ -3533,9 +3547,9 @@ static void vtd_address_space_refresh_all(IntelIOMMUState *s) vtd_switch_address_space_all(s); } -static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) +static int vtd_replay_hook(IOMMUTLBEvent *event, void *private) { - memory_region_notify_one((IOMMUNotifier *)private, entry); + memory_region_notify_iommu_one(private, event); return 0; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 17b514d1da..9e29f3792b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -97,6 +97,11 @@ #include "trace.h" #include CONFIG_DEVICES +GlobalProperty pc_compat_5_2[] = { + { "ICH9-LPC", "x-smi-cpu-hotunplug", "off" }, +}; +const size_t pc_compat_5_2_len = G_N_ELEMENTS(pc_compat_5_2); + GlobalProperty pc_compat_5_1[] = { { "ICH9-LPC", "x-smi-cpu-hotplug", "off" }, }; @@ -777,27 +782,11 @@ void pc_machine_done(Notifier *notifier, void *data) PCMachineState *pcms = container_of(notifier, PCMachineState, machine_done); X86MachineState *x86ms = X86_MACHINE(pcms); - PCIBus *bus = pcms->bus; /* set the number of CPUs */ x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - if (bus) { - int extra_hosts = 0; - - QLIST_FOREACH(bus, &bus->child, sibling) { - /* look for expander root buses */ - if (pci_bus_is_root(bus)) { - extra_hosts++; - } - } - if (extra_hosts && x86ms->fw_cfg) { - uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(x86ms->fw_cfg, - "etc/extra-pci-roots", val, sizeof(*val)); - } - } + fw_cfg_add_extra_pci_roots(pcms->bus, x86ms->fw_cfg); acpi_setup(); if (x86ms->fw_cfg) { @@ -1582,6 +1571,50 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, pcms->max_ram_below_4g = value; } +static void pc_machine_get_max_fw_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + uint64_t value = pcms->max_fw_size; + + visit_type_size(v, name, &value, errp); +} + +static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + Error *error = NULL; + uint64_t value; + + visit_type_size(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + /* + * We don't have a theoretically justifiable exact lower bound on the base + * address of any flash mapping. In practice, the IO-APIC MMIO range is + * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free + * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in + * size. + */ + if (value > 16 * MiB) { + error_setg(errp, + "User specified max allowed firmware size %" PRIu64 " is " + "greater than 16MiB. If combined firwmare size exceeds " + "16MiB the system may not boot, or experience intermittent" + "stability issues.", + value); + return; + } + + pcms->max_fw_size = value; +} + static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1597,6 +1630,7 @@ static void pc_machine_initfn(Object *obj) pcms->smbus_enabled = true; pcms->sata_enabled = true; pcms->pit_enabled = true; + pcms->max_fw_size = 8 * MiB; #ifdef CONFIG_HPET pcms->hpet_enabled = true; #endif @@ -1723,6 +1757,12 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "hpet", pc_machine_get_hpet, pc_machine_set_hpet); + + object_class_property_add(oc, PC_MACHINE_MAX_FW_SIZE, "size", + pc_machine_get_max_fw_size, pc_machine_set_max_fw_size, + NULL, NULL); + object_class_property_set_description(oc, PC_MACHINE_MAX_FW_SIZE, + "Maximum combined firmware size"); } static const TypeInfo pc_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 13d1628f13..6188c3e97e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -426,7 +426,7 @@ static void pc_i440fx_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); } -static void pc_i440fx_5_2_machine_options(MachineClass *m) +static void pc_i440fx_6_0_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); @@ -435,6 +435,18 @@ static void pc_i440fx_5_2_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL, + pc_i440fx_6_0_machine_options); + +static void pc_i440fx_5_2_machine_options(MachineClass *m) +{ + pc_i440fx_6_0_machine_options(m); + m->alias = NULL; + m->is_default = false; + compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); + compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); +} + DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", NULL, pc_i440fx_5_2_machine_options); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index a3f4959c43..0a212443aa 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -344,7 +344,7 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_5_2_machine_options(MachineClass *m) +static void pc_q35_6_0_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); @@ -352,6 +352,17 @@ static void pc_q35_5_2_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL, + pc_q35_6_0_machine_options); + +static void pc_q35_5_2_machine_options(MachineClass *m) +{ + pc_q35_6_0_machine_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); + compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); +} + DEFINE_Q35_MACHINE(v5_2, "pc-q35-5.2", NULL, pc_q35_5_2_machine_options); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index b6c0822fe3..f8bd3a8b85 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -39,15 +39,6 @@ #include "hw/block/flash.h" #include "sysemu/kvm.h" -/* - * We don't have a theoretically justifiable exact lower bound on the base - * address of any flash mapping. In practice, the IO-APIC MMIO range is - * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free - * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in - * size. - */ -#define FLASH_SIZE_LIMIT (8 * MiB) - #define FLASH_SECTOR_SIZE 4096 static void pc_isa_bios_init(MemoryRegion *rom_memory, @@ -140,7 +131,7 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms) * Stop at the first pcms->flash[0] lacking a block backend. * Set each flash's size from its block backend. Fatal error if the * size isn't a non-zero multiple of 4KiB, or the total size exceeds - * FLASH_SIZE_LIMIT. + * pcms->max_fw_size. * * If pcms->flash[0] has a block backend, its memory is passed to * pc_isa_bios_init(). Merging several flash devices for isa-bios is @@ -182,10 +173,10 @@ static void pc_system_flash_map(PCMachineState *pcms, } if ((hwaddr)size != size || total_size > HWADDR_MAX - size - || total_size + size > FLASH_SIZE_LIMIT) { + || total_size + size > pcms->max_fw_size) { error_report("combined size of system firmware exceeds " "%" PRIu64 " bytes", - FLASH_SIZE_LIMIT); + pcms->max_fw_size); exit(1); } diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 087a18d04d..d3145bf014 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -366,6 +366,7 @@ static void smi_features_ok_callback(void *opaque) { ICH9LPCState *lpc = opaque; uint64_t guest_features; + uint64_t guest_cpu_hotplug_features; if (lpc->smi_features_ok) { /* negotiation already complete, features locked */ @@ -378,9 +379,12 @@ static void smi_features_ok_callback(void *opaque) /* guest requests invalid features, leave @features_ok at zero */ return; } + + guest_cpu_hotplug_features = guest_features & + (BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT) | + BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)); if (!(guest_features & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT)) && - guest_features & (BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT) | - BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT))) { + guest_cpu_hotplug_features) { /* * cpu hot-[un]plug with SMI requires SMI broadcast, * leave @features_ok at zero @@ -388,6 +392,12 @@ static void smi_features_ok_callback(void *opaque) return; } + if (guest_cpu_hotplug_features == + BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)) { + /* cpu hot-unplug is unsupported without cpu-hotplug */ + return; + } + /* valid feature subset requested, lock it down, report success */ lpc->smi_negotiated_features = guest_features; lpc->smi_features_ok = 1; @@ -770,7 +780,7 @@ static Property ich9_lpc_properties[] = { DEFINE_PROP_BIT64("x-smi-cpu-hotplug", ICH9LPCState, smi_host_features, ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, true), DEFINE_PROP_BIT64("x-smi-cpu-hotunplug", ICH9LPCState, smi_host_features, - ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, false), + ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 98f151237f..30481e1c90 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -82,8 +82,10 @@ static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, /* Called when the LUT word at lutidx has changed from oldlut to newlut; * must call the IOMMU notifiers for the changed blocks. */ - IOMMUTLBEntry entry = { - .addr_mask = s->blocksize - 1, + IOMMUTLBEvent event = { + .entry = { + .addr_mask = s->blocksize - 1, + } }; hwaddr addr = lutidx * s->blocksize * 32; int i; @@ -100,26 +102,28 @@ static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, block_is_ns = newlut & (1 << i); trace_tz_mpc_iommu_notify(addr); - entry.iova = addr; - entry.translated_addr = addr; + event.entry.iova = addr; + event.entry.translated_addr = addr; - entry.perm = IOMMU_NONE; - memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); - memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.perm = IOMMU_NONE; + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event); - entry.perm = IOMMU_RW; + event.type = IOMMU_NOTIFIER_MAP; + event.entry.perm = IOMMU_RW; if (block_is_ns) { - entry.target_as = &s->blocked_io_as; + event.entry.target_as = &s->blocked_io_as; } else { - entry.target_as = &s->downstream_as; + event.entry.target_as = &s->downstream_as; } - memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event); if (block_is_ns) { - entry.target_as = &s->downstream_as; + event.entry.target_as = &s->downstream_as; } else { - entry.target_as = &s->blocked_io_as; + event.entry.target_as = &s->blocked_io_as; } - memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event); } } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 9179013ac4..044ac95f6f 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -788,93 +788,95 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) return virtio_net_guest_offloads_by_features(vdev->guest_features); } -static void failover_add_primary(VirtIONet *n, Error **errp) -{ - Error *err = NULL; - - if (n->primary_dev) { - return; - } - - n->primary_device_opts = qemu_opts_find(qemu_find_opts("device"), - n->primary_device_id); - if (n->primary_device_opts) { - n->primary_dev = qdev_device_add(n->primary_device_opts, &err); - if (err) { - qemu_opts_del(n->primary_device_opts); - } - if (n->primary_dev) { - n->primary_bus = n->primary_dev->parent_bus; - if (err) { - qdev_unplug(n->primary_dev, &err); - qdev_set_id(n->primary_dev, ""); - - } - } - } else { - error_setg(errp, "Primary device not found"); - error_append_hint(errp, "Virtio-net failover will not work. Make " - "sure primary device has parameter" - " failover_pair_id=<virtio-net-id>\n"); -} - error_propagate(errp, err); -} +typedef struct { + VirtIONet *n; + char *id; +} FailoverId; -static int is_my_primary(void *opaque, QemuOpts *opts, Error **errp) +/** + * Set the id of the failover primary device + * + * @opaque: FailoverId to setup + * @opts: opts for device we are handling + * @errp: returns an error if this function fails + */ +static int failover_set_primary(void *opaque, QemuOpts *opts, Error **errp) { - VirtIONet *n = opaque; - int ret = 0; - + FailoverId *fid = opaque; const char *standby_id = qemu_opt_get(opts, "failover_pair_id"); - if (standby_id != NULL && (g_strcmp0(standby_id, n->netclient_name) == 0)) { - n->primary_device_id = g_strdup(opts->id); - ret = 1; + if (g_strcmp0(standby_id, fid->n->netclient_name) == 0) { + fid->id = g_strdup(opts->id); + return 1; } - return ret; + return 0; } -static DeviceState *virtio_net_find_primary(VirtIONet *n, Error **errp) +/** + * Find the primary device id for this failover virtio-net + * + * @n: VirtIONet device + * @errp: returns an error if this function fails + */ +static char *failover_find_primary_device_id(VirtIONet *n) { - DeviceState *dev = NULL; Error *err = NULL; + FailoverId fid; - if (qemu_opts_foreach(qemu_find_opts("device"), - is_my_primary, n, &err)) { - if (err) { - error_propagate(errp, err); - return NULL; - } - if (n->primary_device_id) { - dev = qdev_find_recursive(sysbus_get_default(), - n->primary_device_id); - } else { - error_setg(errp, "Primary device id not found"); - return NULL; - } + fid.n = n; + if (!qemu_opts_foreach(qemu_find_opts("device"), + failover_set_primary, &fid, &err)) { + return NULL; } - return dev; + return fid.id; } +/** + * Find the primary device for this failover virtio-net + * + * @n: VirtIONet device + * @errp: returns an error if this function fails + */ +static DeviceState *failover_find_primary_device(VirtIONet *n) +{ + char *id = failover_find_primary_device_id(n); + + if (!id) { + return NULL; + } + return qdev_find_recursive(sysbus_get_default(), id); +} -static DeviceState *virtio_connect_failover_devices(VirtIONet *n, - DeviceState *dev, - Error **errp) +static void failover_add_primary(VirtIONet *n, Error **errp) { - DeviceState *prim_dev = NULL; Error *err = NULL; + QemuOpts *opts; + char *id; + DeviceState *dev = failover_find_primary_device(n); - prim_dev = virtio_net_find_primary(n, &err); - if (prim_dev) { - n->primary_device_id = g_strdup(prim_dev->id); - n->primary_device_opts = prim_dev->opts; - } else { - error_propagate(errp, err); + if (dev) { + return; } - return prim_dev; + id = failover_find_primary_device_id(n); + if (!id) { + return; + } + opts = qemu_opts_find(qemu_find_opts("device"), id); + if (opts) { + dev = qdev_device_add(opts, &err); + if (err) { + qemu_opts_del(opts); + } + } else { + error_setg(errp, "Primary device not found"); + error_append_hint(errp, "Virtio-net failover will not work. Make " + "sure primary device has parameter" + " failover_pair_id=<virtio-net-id>\n"); + } + error_propagate(errp, err); } static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) @@ -929,25 +931,12 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) { qapi_event_send_failover_negotiated(n->netclient_name); - qatomic_set(&n->primary_should_be_hidden, false); + qatomic_set(&n->failover_primary_hidden, false); failover_add_primary(n, &err); if (err) { - n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err); - if (err) { - goto out_err; - } - failover_add_primary(n, &err); - if (err) { - goto out_err; - } + warn_report_err(err); } } - return; - -out_err: - if (err) { - warn_report_err(err); - } } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, @@ -3095,17 +3084,17 @@ void virtio_net_set_netclient_name(VirtIONet *n, const char *name, n->netclient_type = g_strdup(type); } -static bool failover_unplug_primary(VirtIONet *n) +static bool failover_unplug_primary(VirtIONet *n, DeviceState *dev) { HotplugHandler *hotplug_ctrl; PCIDevice *pci_dev; Error *err = NULL; - hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev); + hotplug_ctrl = qdev_get_hotplug_handler(dev); if (hotplug_ctrl) { - pci_dev = PCI_DEVICE(n->primary_dev); + pci_dev = PCI_DEVICE(dev); pci_dev->partially_hotplugged = true; - hotplug_handler_unplug_request(hotplug_ctrl, n->primary_dev, &err); + hotplug_handler_unplug_request(hotplug_ctrl, dev, &err); if (err) { error_report_err(err); return false; @@ -3116,41 +3105,31 @@ static bool failover_unplug_primary(VirtIONet *n) return true; } -static bool failover_replug_primary(VirtIONet *n, Error **errp) +static bool failover_replug_primary(VirtIONet *n, DeviceState *dev, + Error **errp) { Error *err = NULL; HotplugHandler *hotplug_ctrl; - PCIDevice *pdev = PCI_DEVICE(n->primary_dev); + PCIDevice *pdev = PCI_DEVICE(dev); + BusState *primary_bus; if (!pdev->partially_hotplugged) { return true; } - if (!n->primary_device_opts) { - n->primary_device_opts = qemu_opts_from_qdict( - qemu_find_opts("device"), - n->primary_device_dict, errp); - if (!n->primary_device_opts) { - return false; - } - } - n->primary_bus = n->primary_dev->parent_bus; - if (!n->primary_bus) { + primary_bus = dev->parent_bus; + if (!primary_bus) { error_setg(errp, "virtio_net: couldn't find primary bus"); return false; } - qdev_set_parent_bus(n->primary_dev, n->primary_bus, &error_abort); - n->primary_should_be_hidden = false; - if (!qemu_opt_set_bool(n->primary_device_opts, - "partially_hotplugged", true, errp)) { - return false; - } - hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev); + qdev_set_parent_bus(dev, primary_bus, &error_abort); + qatomic_set(&n->failover_primary_hidden, false); + hotplug_ctrl = qdev_get_hotplug_handler(dev); if (hotplug_ctrl) { - hotplug_handler_pre_plug(hotplug_ctrl, n->primary_dev, &err); + hotplug_handler_pre_plug(hotplug_ctrl, dev, &err); if (err) { goto out; } - hotplug_handler_plug(hotplug_ctrl, n->primary_dev, &err); + hotplug_handler_plug(hotplug_ctrl, dev, &err); } out: @@ -3158,34 +3137,29 @@ out: return !err; } -static void virtio_net_handle_migration_primary(VirtIONet *n, - MigrationState *s) +static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s) { bool should_be_hidden; Error *err = NULL; + DeviceState *dev = failover_find_primary_device(n); - should_be_hidden = qatomic_read(&n->primary_should_be_hidden); - - if (!n->primary_dev) { - n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err); - if (!n->primary_dev) { - return; - } + if (!dev) { + return; } + should_be_hidden = qatomic_read(&n->failover_primary_hidden); + if (migration_in_setup(s) && !should_be_hidden) { - if (failover_unplug_primary(n)) { - vmstate_unregister(VMSTATE_IF(n->primary_dev), - qdev_get_vmsd(n->primary_dev), - n->primary_dev); - qapi_event_send_unplug_primary(n->primary_device_id); - qatomic_set(&n->primary_should_be_hidden, true); + if (failover_unplug_primary(n, dev)) { + vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); + qapi_event_send_unplug_primary(dev->id); + qatomic_set(&n->failover_primary_hidden, true); } else { warn_report("couldn't unplug primary device"); } } else if (migration_has_failed(s)) { /* We already unplugged the device let's plug it back */ - if (!failover_replug_primary(n, &err)) { + if (!failover_replug_primary(n, dev, &err)) { if (err) { error_report_err(err); } @@ -3200,55 +3174,22 @@ static void virtio_net_migration_state_notifier(Notifier *notifier, void *data) virtio_net_handle_migration_primary(n, s); } -static int virtio_net_primary_should_be_hidden(DeviceListener *listener, - QemuOpts *device_opts) +static bool failover_hide_primary_device(DeviceListener *listener, + QemuOpts *device_opts) { VirtIONet *n = container_of(listener, VirtIONet, primary_listener); - bool match_found = false; - bool hide = false; + const char *standby_id; if (!device_opts) { - return -1; - } - n->primary_device_dict = qemu_opts_to_qdict(device_opts, - n->primary_device_dict); - if (n->primary_device_dict) { - g_free(n->standby_id); - n->standby_id = g_strdup(qdict_get_try_str(n->primary_device_dict, - "failover_pair_id")); - } - if (g_strcmp0(n->standby_id, n->netclient_name) == 0) { - match_found = true; - } else { - match_found = false; - hide = false; - g_free(n->standby_id); - n->primary_device_dict = NULL; - goto out; + return false; } - - n->primary_device_opts = device_opts; - - /* primary_should_be_hidden is set during feature negotiation */ - hide = qatomic_read(&n->primary_should_be_hidden); - - if (n->primary_device_dict) { - g_free(n->primary_device_id); - n->primary_device_id = g_strdup(qdict_get_try_str( - n->primary_device_dict, "id")); - if (!n->primary_device_id) { - warn_report("primary_device_id not set"); - } + standby_id = qemu_opt_get(device_opts, "failover_pair_id"); + if (g_strcmp0(standby_id, n->netclient_name) != 0) { + return false; } -out: - if (match_found && hide) { - return 1; - } else if (match_found && !hide) { - return 0; - } else { - return -1; - } + /* failover_primary_hidden is set during feature negotiation */ + return qatomic_read(&n->failover_primary_hidden); } static void virtio_net_device_realize(DeviceState *dev, Error **errp) @@ -3285,9 +3226,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) } if (n->failover) { - n->primary_listener.should_be_hidden = - virtio_net_primary_should_be_hidden; - qatomic_set(&n->primary_should_be_hidden, true); + n->primary_listener.hide_device = failover_hide_primary_device; + qatomic_set(&n->failover_primary_hidden, true); device_listener_register(&n->primary_listener); n->migration_state.notify = virtio_net_migration_state_notifier; add_migration_state_change_notifier(&n->migration_state); @@ -3426,10 +3366,6 @@ static void virtio_net_device_unrealize(DeviceState *dev) if (n->failover) { device_listener_unregister(&n->primary_listener); - g_free(n->primary_device_id); - g_free(n->standby_id); - qobject_unref(n->primary_device_dict); - n->primary_device_dict = NULL; } max_queues = n->multiqueue ? n->max_queues : 1; @@ -3475,13 +3411,15 @@ static int virtio_net_pre_save(void *opaque) static bool primary_unplug_pending(void *opaque) { DeviceState *dev = opaque; + DeviceState *primary; VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIONet *n = VIRTIO_NET(vdev); if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_STANDBY)) { return false; } - return n->primary_dev ? n->primary_dev->pending_deleted_event : false; + primary = failover_find_primary_device(n); + return primary ? primary->pending_deleted_event : false; } static bool dev_unplug_pending(void *opaque) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 08539a1aab..282ba93e2e 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -40,6 +40,7 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "hw/acpi/aml-build.h" +#include "hw/pci/pci_bus.h" #define FW_CFG_FILE_SLOTS_DFLT 0x20 @@ -1061,6 +1062,28 @@ bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, return true; } +void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s) +{ + int extra_hosts = 0; + + if (!bus) { + return; + } + + QLIST_FOREACH(bus, &bus->child, sibling) { + /* look for expander root buses */ + if (pci_bus_is_root(bus)) { + extra_hosts++; + } + } + + if (extra_hosts && s) { + uint64_t *val = g_malloc(sizeof(*val)); + *val = cpu_to_le64(extra_hosts); + fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val)); + } +} + static void fw_cfg_machine_reset(void *opaque) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index dbb350a837..7f20ee1c98 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -1,22 +1,16 @@ #include "qemu/osdep.h" #include "hw/acpi/aml-build.h" #include "hw/pci-host/gpex.h" +#include "hw/arm/virt.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pcie_host.h" -void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) +static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) { - int nr_pcie_buses = cfg->ecam.size / PCIE_MMCFG_SIZE_MIN; - Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; + Aml *method, *crs; int i, slot_no; - Aml *dev = aml_device("%s", "PCI0"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); - aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); - aml_append(dev, aml_name_decl("_SEG", aml_int(0))); - aml_append(dev, aml_name_decl("_BBN", aml_int(0))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); - aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - /* Declare the PCI Routing Table. */ Aml *rt_pkg = aml_varpackage(PCI_SLOT_MAX * PCI_NUM_PINS); for (slot_no = 0; slot_no < PCI_SLOT_MAX; slot_no++) { @@ -34,7 +28,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) /* Create GSI link device */ for (i = 0; i < PCI_NUM_PINS; i++) { - uint32_t irqs = cfg->irq + i; + uint32_t irqs = irq + i; Aml *dev_gsi = aml_device("GSI%d", i); aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i))); @@ -52,43 +46,11 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) aml_append(dev_gsi, method); aml_append(dev, dev_gsi); } +} - method = aml_method("_CBA", 0, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(cfg->ecam.base))); - aml_append(dev, method); - - Aml *rbuf = aml_resource_template(); - aml_append(rbuf, - aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0x0000, 0x0000, nr_pcie_buses - 1, 0x0000, - nr_pcie_buses)); - if (cfg->mmio32.size) { - aml_append(rbuf, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, - cfg->mmio32.base, - cfg->mmio32.base + cfg->mmio32.size - 1, - 0x0000, - cfg->mmio32.size)); - } - if (cfg->pio.size) { - aml_append(rbuf, - aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - AML_ENTIRE_RANGE, 0x0000, 0x0000, - cfg->pio.size - 1, - cfg->pio.base, - cfg->pio.size)); - } - if (cfg->mmio64.size) { - aml_append(rbuf, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, - cfg->mmio64.base, - cfg->mmio64.base + cfg->mmio64.size - 1, - 0x0000, - cfg->mmio64.size)); - } - aml_append(dev, aml_name_decl("_CRS", rbuf)); +static void acpi_dsdt_add_pci_osc(Aml *dev) +{ + Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf; /* Declare an _OSC (OS Control Handoff) method */ aml_append(dev, aml_name_decl("SUPP", aml_int(0))); @@ -160,6 +122,112 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) buf = aml_buffer(1, byte_list); aml_append(method, aml_return(buf)); aml_append(dev, method); +} + +void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) +{ + int nr_pcie_buses = cfg->ecam.size / PCIE_MMCFG_SIZE_MIN; + Aml *method, *crs, *dev, *rbuf; + PCIBus *bus = cfg->bus; + CrsRangeSet crs_range_set; + + /* start to construct the tables for pxb */ + crs_range_set_init(&crs_range_set); + if (bus) { + QLIST_FOREACH(bus, &bus->child, sibling) { + uint8_t bus_num = pci_bus_num(bus); + uint8_t numa_node = pci_bus_numa_node(bus); + + if (!pci_bus_is_root(bus)) { + continue; + } + + /* + * 0 - (nr_pcie_buses - 1) is the bus range for the main + * host-bridge and it equals the MIN of the + * busNr defined for pxb-pcie. + */ + if (bus_num < nr_pcie_buses) { + nr_pcie_buses = bus_num; + } + + dev = aml_device("PC%.02X", bus_num); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); + aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); + aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); + aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); + aml_append(dev, aml_name_decl("_STR", aml_unicode("pxb Device"))); + if (numa_node != NUMA_NODE_UNASSIGNED) { + aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); + } + + acpi_dsdt_add_pci_route_table(dev, cfg->irq); + + /* + * Resources defined for PXBs are composed by the folling parts: + * 1. The resources the pci-brige/pcie-root-port need. + * 2. The resources the devices behind pxb need. + */ + crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set); + aml_append(dev, aml_name_decl("_CRS", crs)); + + acpi_dsdt_add_pci_osc(dev); + + aml_append(scope, dev); + } + } + crs_range_set_free(&crs_range_set); + + /* tables for the main */ + dev = aml_device("%s", "PCI0"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); + aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); + aml_append(dev, aml_name_decl("_SEG", aml_int(0))); + aml_append(dev, aml_name_decl("_BBN", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + + acpi_dsdt_add_pci_route_table(dev, cfg->irq); + + method = aml_method("_CBA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(cfg->ecam.base))); + aml_append(dev, method); + + rbuf = aml_resource_template(); + aml_append(rbuf, + aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + 0x0000, 0x0000, nr_pcie_buses - 1, 0x0000, + nr_pcie_buses)); + if (cfg->mmio32.size) { + aml_append(rbuf, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, + cfg->mmio32.base, + cfg->mmio32.base + cfg->mmio32.size - 1, + 0x0000, + cfg->mmio32.size)); + } + if (cfg->pio.size) { + aml_append(rbuf, + aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + AML_ENTIRE_RANGE, 0x0000, 0x0000, + cfg->pio.size - 1, + cfg->pio.base, + cfg->pio.size)); + } + if (cfg->mmio64.size) { + aml_append(rbuf, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, + cfg->mmio64.base, + cfg->mmio64.base + cfg->mmio64.size - 1, + 0x0000, + cfg->mmio64.size)); + } + aml_append(dev, aml_name_decl("_CRS", rbuf)); + + acpi_dsdt_add_pci_osc(dev); Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 12a012d9dd..c060702013 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4510,14 +4510,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) type_init(spapr_machine_register_##suffix) /* + * pseries-6.0 + */ +static void spapr_machine_6_0_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE(6_0, "6.0", true); + +/* * pseries-5.2 */ static void spapr_machine_5_2_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_6_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_SPAPR_MACHINE(5_2, "5.2", true); +DEFINE_SPAPR_MACHINE(5_2, "5.2", false); /* * pseries-5.1 diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 0790239ba5..30352df00e 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -445,7 +445,7 @@ static void spapr_tce_reset(DeviceState *dev) static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba, target_ulong tce) { - IOMMUTLBEntry entry; + IOMMUTLBEvent event; hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; @@ -457,12 +457,13 @@ static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba, tcet->table[index] = tce; - entry.target_as = &address_space_memory, - entry.iova = (ioba - tcet->bus_offset) & page_mask; - entry.translated_addr = tce & page_mask; - entry.addr_mask = ~page_mask; - entry.perm = spapr_tce_iommu_access_flags(tce); - memory_region_notify_iommu(&tcet->iommu, 0, entry); + event.entry.target_as = &address_space_memory, + event.entry.iova = (ioba - tcet->bus_offset) & page_mask; + event.entry.translated_addr = tce & page_mask; + event.entry.addr_mask = ~page_mask; + event.entry.perm = spapr_tce_iommu_access_flags(tce); + event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP; + memory_region_notify_iommu(&tcet->iommu, 0, event); return H_SUCCESS; } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 70bfd91bf7..d9e1e29f1e 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -602,15 +602,18 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) { S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova); - IOMMUTLBEntry notify = { - .target_as = &address_space_memory, - .iova = entry->iova, - .translated_addr = entry->translated_addr, - .perm = entry->perm, - .addr_mask = ~PAGE_MASK, + IOMMUTLBEvent event = { + .type = entry->perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP, + .entry = { + .target_as = &address_space_memory, + .iova = entry->iova, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + .addr_mask = ~PAGE_MASK, + }, }; - if (entry->perm == IOMMU_NONE) { + if (event.type == IOMMU_NOTIFIER_UNMAP) { if (!cache) { goto out; } @@ -623,9 +626,11 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, goto out; } - notify.perm = IOMMU_NONE; - memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); - notify.perm = entry->perm; + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.perm = IOMMU_NONE; + memory_region_notify_iommu(&iommu->iommu_mr, 0, event); + event.type = IOMMU_NOTIFIER_MAP; + event.entry.perm = entry->perm; } cache = g_new(S390IOTLBEntry, 1); @@ -637,7 +642,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, dec_dma_avail(iommu); } - memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); + memory_region_notify_iommu(&iommu->iommu_mr, 0, event); out: return iommu->dma_limit ? iommu->dma_limit->avail : 1; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4e140bbead..f0ee8dae68 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -789,14 +789,26 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_6_0_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_6_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(6_0, "6.0", true); + static void ccw_machine_5_2_instance_options(MachineState *machine) { + ccw_machine_6_0_instance_options(machine); } static void ccw_machine_5_2_class_options(MachineClass *mc) { + ccw_machine_6_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_CCW_MACHINE(5_2, "5.2", true); +DEFINE_CCW_MACHINE(5_2, "5.2", false); static void ccw_machine_5_1_instance_options(MachineState *machine) { diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 614ccc2bcb..28c7d78172 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -718,7 +718,7 @@ static void vhost_iommu_region_add(MemoryListener *listener, iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, - IOMMU_NOTIFIER_UNMAP, + IOMMU_NOTIFIER_DEVIOTLB_UNMAP, section->offset_within_region, int128_get64(end), iommu_idx); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index fc5c75d693..cea8811295 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -129,7 +129,7 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end, hwaddr paddr, uint32_t flags) { - IOMMUTLBEntry entry; + IOMMUTLBEvent event; IOMMUAccessFlags perm = IOMMU_ACCESS_FLAG(flags & VIRTIO_IOMMU_MAP_F_READ, flags & VIRTIO_IOMMU_MAP_F_WRITE); @@ -141,19 +141,20 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, trace_virtio_iommu_notify_map(mr->parent_obj.name, virt_start, virt_end, paddr, perm); - entry.target_as = &address_space_memory; - entry.addr_mask = virt_end - virt_start; - entry.iova = virt_start; - entry.perm = perm; - entry.translated_addr = paddr; + event.type = IOMMU_NOTIFIER_MAP; + event.entry.target_as = &address_space_memory; + event.entry.addr_mask = virt_end - virt_start; + event.entry.iova = virt_start; + event.entry.perm = perm; + event.entry.translated_addr = paddr; - memory_region_notify_iommu(mr, 0, entry); + memory_region_notify_iommu(mr, 0, event); } static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end) { - IOMMUTLBEntry entry; + IOMMUTLBEvent event; if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { return; @@ -161,13 +162,14 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, trace_virtio_iommu_notify_unmap(mr->parent_obj.name, virt_start, virt_end); - entry.target_as = &address_space_memory; - entry.addr_mask = virt_end - virt_start; - entry.iova = virt_start; - entry.perm = IOMMU_NONE; - entry.translated_addr = 0; + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.addr_mask = virt_end - virt_start; + event.entry.iova = virt_start; + event.entry.perm = IOMMU_NONE; + event.entry.translated_addr = 0; - memory_region_notify_iommu(mr, 0, entry); + memory_region_notify_iommu(mr, 0, event); } static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value, diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 36524a5728..f863f69ede 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1798,6 +1798,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) if (pcie_port && pci_is_express(pci_dev)) { int pos; + uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); @@ -1816,6 +1817,12 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) */ pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); + if (proxy->flags & VIRTIO_PCI_FLAG_AER) { + pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset, + PCI_ERR_SIZEOF, NULL); + last_pcie_cap_offset += PCI_ERR_SIZEOF; + } + if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) { /* Init error enabling flags */ pcie_cap_deverr_init(pci_dev); @@ -1833,7 +1840,8 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) } if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { - pcie_ats_init(pci_dev, 256); + pcie_ats_init(pci_dev, last_pcie_cap_offset); + last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) { @@ -1856,7 +1864,15 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) static void virtio_pci_exit(PCIDevice *pci_dev) { + VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); + bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && + !pci_bus_is_root(pci_get_bus(pci_dev)); + msix_uninit_exclusive_bar(pci_dev); + if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port && + pci_is_express(pci_dev)) { + pcie_aer_exit(pci_dev); + } } static void virtio_pci_reset(DeviceState *qdev) @@ -1909,6 +1925,8 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_INIT_PM_BIT, true), DEFINE_PROP_BIT("x-pcie-flr-init", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_INIT_FLR_BIT, true), + DEFINE_PROP_BIT("aer", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_AER_BIT, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 06e2af12de..d7d5d403a9 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -41,6 +41,7 @@ enum { VIRTIO_PCI_FLAG_INIT_LNKCTL_BIT, VIRTIO_PCI_FLAG_INIT_PM_BIT, VIRTIO_PCI_FLAG_INIT_FLR_BIT, + VIRTIO_PCI_FLAG_AER_BIT, }; /* Need to activate work-arounds for buggy guests at vmstate load. */ @@ -80,6 +81,9 @@ enum { /* Init Function Level Reset capability */ #define VIRTIO_PCI_FLAG_INIT_FLR (1 << VIRTIO_PCI_FLAG_INIT_FLR_BIT) +/* Advanced Error Reporting capability */ +#define VIRTIO_PCI_FLAG_AER (1 << VIRTIO_PCI_FLAG_AER_BIT) + typedef struct { MSIMessage msg; int virq; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ceb58fda6c..eff35fab7c 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3161,12 +3161,15 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; /* Check it isn't doing strange things with descriptor numbers. */ if (nheads > vdev->vq[i].vring.num) { - qemu_log_mask(LOG_GUEST_ERROR, - "VQ %d size 0x%x Guest index 0x%x " - "inconsistent with Host index 0x%x: delta 0x%x", - i, vdev->vq[i].vring.num, - vring_avail_idx(&vdev->vq[i]), - vdev->vq[i].last_avail_idx, nheads); + virtio_error(vdev, "VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: delta 0x%x", + i, vdev->vq[i].vring.num, + vring_avail_idx(&vdev->vq[i]), + vdev->vq[i].last_avail_idx, nheads); + vdev->vq[i].used_idx = 0; + vdev->vq[i].shadow_avail_idx = 0; + vdev->vq[i].inuse = 0; + continue; } vdev->vq[i].used_idx = vring_used_idx(&vdev->vq[i]); vdev->vq[i].shadow_avail_idx = vring_avail_idx(&vdev->vq[i]); |