From ee4c112846a0f2ac4fe5601918b0a2642ac8e2ed Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 7 Sep 2017 14:27:09 -0600 Subject: vhost: Release memory references on cleanup vhost registers a MemoryListener where it adds and removes references to MemoryRegions as the MemoryRegionSections pass through. The region_add callback is invoked for each existing section when the MemoryListener is registered, but unregistering the MemoryListener performs no reciprocal region_del callback. It's therefore the owner of the MemoryListener's responsibility to cleanup any persistent changes, such as these memory references, after unregistering. The consequence of this bug is that if we have both a vhost device and a vfio device, the vhost device will reference any mmap'd MMIO of the vfio device via this MemoryListener. If the vhost device is then removed, those references remain outstanding. If we then attempt to remove the vfio device, it never gets finalized and the only way to release the kernel file descriptors is to terminate the QEMU process. Fixes: dfde4e6e1a86 ("memory: add ref/unref calls") Cc: Michael S. Tsirkin Cc: Paolo Bonzini Cc: qemu-stable@nongnu.org # v1.6.0+ Signed-off-by: Alex Williamson Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'hw') diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 0049a2c..5fd69f0 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1356,6 +1356,10 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) if (hdev->mem) { /* those are only safe after successful init */ memory_listener_unregister(&hdev->memory_listener); + for (i = 0; i < hdev->n_mem_sections; ++i) { + MemoryRegionSection *section = &hdev->mem_sections[i]; + memory_region_unref(section->mr); + } QLIST_REMOVE(hdev, entry); } if (hdev->migration_blocker) { -- cgit v1.1 From a6fd5b0e050abc892ae3a64547631d2332b893de Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Wed, 6 Sep 2017 17:26:57 +0300 Subject: pc: add 2.11 machine types Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 15 ++++++++++++--- hw/i386/pc_q35.c | 13 +++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 46dfd2c..b03cc04 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -436,21 +436,30 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_10_machine_options(MachineClass *m) +static void pc_i440fx_2_11_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", NULL, + pc_i440fx_2_11_machine_options); + +static void pc_i440fx_2_10_machine_options(MachineClass *m) +{ + pc_i440fx_2_11_machine_options(m); + m->is_default = 0; + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_10); +} + DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", NULL, pc_i440fx_2_10_machine_options); static void pc_i440fx_2_9_machine_options(MachineClass *m) { pc_i440fx_2_10_machine_options(m); - m->is_default = 0; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_9); m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 169a214..c1cba58 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -302,10 +302,20 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_2_10_machine_options(MachineClass *m) +static void pc_q35_2_11_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; +} + +DEFINE_Q35_MACHINE(v2_11, "pc-q35-2.11", NULL, + pc_q35_2_11_machine_options); + +static void pc_q35_2_10_machine_options(MachineClass *m) +{ + pc_q35_2_11_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_10); m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; } @@ -315,7 +325,6 @@ DEFINE_Q35_MACHINE(v2_10, "pc-q35-2.10", NULL, static void pc_q35_2_9_machine_options(MachineClass *m) { pc_q35_2_10_machine_options(m); - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_9); } -- cgit v1.1 From f5855994fee2f8815dc86b8453e4a63e290aea05 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 6 Sep 2017 14:40:31 +0100 Subject: hw/acpi: Limit hotplug to root bus on legacy mode Signed-off-by: Anthony PERARD Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index c420a38..9db3c2e 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -273,7 +273,7 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data, addr, data); break; case PCI_SEL_BASE: - s->hotplug_select = data; + s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data; ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, data); default: -- cgit v1.1 From ab938ae43f8a3a71a3525566edf586081b7a7452 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 6 Sep 2017 14:40:32 +0100 Subject: hw/acpi: Move acpi_set_pci_info to pcihp HW part of ACPI PCI hotplug in QEMU depends on ACPI_PCIHP_PROP_BSEL being set on a PCI bus that supports ACPI hotplug. It should work regardless of the source of ACPI tables (QEMU generator/legacy SeaBIOS/Xen). So move ACPI_PCIHP_PROP_BSEL initialization into HW ACPI implementation part from QEMU's ACPI table generator. To do PCI passthrough with Xen, the property ACPI_PCIHP_PROP_BSEL needs to be set, but this was done only when ACPI tables are built which is not needed for a Xen guest. The need for the property starts with commit "pc: pcihp: avoid adding ACPI_PCIHP_PROP_BSEL twice" (f0c9d64a68b776374ec4732424a3e27753ce37b6). Adding find_i440fx into stubs so that mips-softmmu target can be built. Reported-by: Sander Eikelenboom Signed-off-by: Anthony PERARD Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 38 ++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 32 -------------------------------- 2 files changed, 38 insertions(+), 32 deletions(-) (limited to 'hw') diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 9db3c2e..7da51c0 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -75,6 +75,43 @@ static int acpi_pcihp_get_bsel(PCIBus *bus) } } +/* Assign BSEL property to all buses. In the future, this can be changed + * to only assign to buses that support hotplug. + */ +static void *acpi_set_bsel(PCIBus *bus, void *opaque) +{ + unsigned *bsel_alloc = opaque; + unsigned *bus_bsel; + + if (qbus_is_hotpluggable(BUS(bus))) { + bus_bsel = g_malloc(sizeof *bus_bsel); + + *bus_bsel = (*bsel_alloc)++; + object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, &error_abort); + } + + return bsel_alloc; +} + +static void acpi_set_pci_info(void) +{ + static bool bsel_is_set; + PCIBus *bus; + unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT; + + if (bsel_is_set) { + return; + } + bsel_is_set = true; + + bus = find_i440fx(); /* TODO: Q35 support */ + if (bus) { + /* Scan all PCI buses. Set property to enable acpi based hotplug. */ + pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); + } +} + static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque) { AcpiPciHpFind *find = opaque; @@ -177,6 +214,7 @@ static void acpi_pcihp_update(AcpiPciHpState *s) void acpi_pcihp_reset(AcpiPciHpState *s) { + acpi_set_pci_info(); acpi_pcihp_update(s); } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 98dd424..4d19d91 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -493,36 +493,6 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) table_data->len - madt_start, 1, NULL, NULL); } -/* Assign BSEL property to all buses. In the future, this can be changed - * to only assign to buses that support hotplug. - */ -static void *acpi_set_bsel(PCIBus *bus, void *opaque) -{ - unsigned *bsel_alloc = opaque; - unsigned *bus_bsel; - - if (qbus_is_hotpluggable(BUS(bus))) { - bus_bsel = g_malloc(sizeof *bus_bsel); - - *bus_bsel = (*bsel_alloc)++; - object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, &error_abort); - } - - return bsel_alloc; -} - -static void acpi_set_pci_info(void) -{ - PCIBus *bus = find_i440fx(); /* TODO: Q35 support */ - unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT; - - if (bus) { - /* Scan all PCI buses. Set property to enable acpi based hotplug. */ - pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); - } -} - static void build_append_pcihp_notify_entry(Aml *method, int slot) { Aml *if_ctx; @@ -2888,8 +2858,6 @@ void acpi_setup(void) build_state = g_malloc0(sizeof *build_state); - acpi_set_pci_info(); - acpi_build_tables_init(&tables); acpi_build(&tables, MACHINE(pcms)); -- cgit v1.1 From 2bed1ba77fae50bc8b5e68ede2d80b652b30c3b8 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 6 Sep 2017 14:40:33 +0100 Subject: Revert "ACPI: don't call acpi_pcihp_device_plug_cb on xen" This reverts commit 153eba4726dfa1bdfc31d1fe973b2a61b9035492. This patch prevents PCI passthrough hotplug on Xen. Even if the Xen tool stack prepares its own ACPI tables, we still rely on QEMU for hotplug ACPI notifications. The original issue is fixed by the two previous patch: hw/acpi: Limit hotplug to root bus on legacy mode hw/acpi: Move acpi_set_pci_info to pcihp Signed-off-by: Anthony PERARD Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/piix4.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index f276967..f4fd590 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -385,10 +385,7 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, dev, errp); } } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - if (!xen_enabled()) { - acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, - errp); - } + acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { if (s->cpu_hotplug_legacy) { legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp); @@ -411,10 +408,8 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_request_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - if (!xen_enabled()) { - acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, - errp); - } + acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, + errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !s->cpu_hotplug_legacy) { acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); -- cgit v1.1 From a35fe226558ac85436ea01af8977f1834927f53f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:47 +0300 Subject: hw/pci: introduce pcie-pci-bridge device Introduce a new PCIExpress-to-PCI Bridge device, which is a hot-pluggable PCI Express device and supports devices hot-plug with SHPC. This device is intended to replace the DMI-to-PCI Bridge. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/Makefile.objs | 2 +- hw/pci-bridge/pcie_pci_bridge.c | 192 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 hw/pci-bridge/pcie_pci_bridge.c (limited to 'hw') diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index c4683cf..666db37 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y += pci_bridge_dev.o +common-obj-y += pci_bridge_dev.o pcie_pci_bridge.o common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o common-obj-$(CONFIG_PXB) += pci_expander_bridge.o common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c new file mode 100644 index 0000000..9aa5cc3 --- /dev/null +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -0,0 +1,192 @@ +/* + * QEMU Generic PCIE-PCI Bridge + * + * Copyright (c) 2017 Aleksandr Bezzubikov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/msi.h" +#include "hw/pci/shpc.h" +#include "hw/pci/slotid_cap.h" + +typedef struct PCIEPCIBridge { + /*< private >*/ + PCIBridge parent_obj; + + OnOffAuto msi; + MemoryRegion shpc_bar; + /*< public >*/ +} PCIEPCIBridge; + +#define TYPE_PCIE_PCI_BRIDGE_DEV "pcie-pci-bridge" +#define PCIE_PCI_BRIDGE_DEV(obj) \ + OBJECT_CHECK(PCIEPCIBridge, (obj), TYPE_PCIE_PCI_BRIDGE_DEV) + +static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp) +{ + PCIBridge *br = PCI_BRIDGE(d); + PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d); + int rc, pos; + + pci_bridge_initfn(d, TYPE_PCI_BUS); + + d->config[PCI_INTERRUPT_PIN] = 0x1; + memory_region_init(&pcie_br->shpc_bar, OBJECT(d), "shpc-bar", + shpc_bar_size(d)); + rc = shpc_init(d, &br->sec_bus, &pcie_br->shpc_bar, 0, errp); + if (rc) { + goto error; + } + + rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, errp); + if (rc < 0) { + goto cap_error; + } + + pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, errp); + if (pos < 0) { + goto pm_error; + } + d->exp.pm_cap = pos; + pci_set_word(d->config + pos + PCI_PM_PMC, 0x3); + + pcie_cap_arifwd_init(d); + pcie_cap_deverr_init(d); + + rc = pcie_aer_init(d, PCI_ERR_VER, 0x100, PCI_ERR_SIZEOF, errp); + if (rc < 0) { + goto aer_error; + } + + if (pcie_br->msi != ON_OFF_AUTO_OFF) { + rc = msi_init(d, 0, 1, true, true, errp); + if (rc < 0) { + goto msi_error; + } + } + pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &pcie_br->shpc_bar); + return; + +msi_error: + pcie_aer_exit(d); +aer_error: +pm_error: + pcie_cap_exit(d); +cap_error: + shpc_free(d); +error: + pci_bridge_exitfn(d); +} + +static void pcie_pci_bridge_exit(PCIDevice *d) +{ + PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d); + pcie_cap_exit(d); + shpc_cleanup(d, &bridge_dev->shpc_bar); + pci_bridge_exitfn(d); +} + +static void pcie_pci_bridge_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + pci_bridge_reset(qdev); + msi_reset(d); + shpc_reset(d); +} + +static void pcie_pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + msi_write_config(d, address, val, len); + shpc_cap_write_config(d, address, val, len); +} + +static Property pcie_pci_bridge_dev_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_ON), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription pcie_pci_bridge_dev_vmstate = { + .name = TYPE_PCIE_PCI_BRIDGE_DEV, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), + SHPC_VMSTATE(shpc, PCIDevice, NULL), + VMSTATE_END_OF_LIST() + } +}; + +static void pcie_pci_bridge_hotplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + if (!shpc_present(pci_hotplug_dev)) { + error_setg(errp, "standard hotplug controller has been disabled for " + "this %s", TYPE_PCIE_PCI_BRIDGE_DEV); + return; + } + shpc_device_hotplug_cb(hotplug_dev, dev, errp); +} + +static void pcie_pci_bridge_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, + Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + if (!shpc_present(pci_hotplug_dev)) { + error_setg(errp, "standard hotplug controller has been disabled for " + "this %s", TYPE_PCIE_PCI_BRIDGE_DEV); + return; + } + shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); +} + +static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE; + k->realize = pcie_pci_bridge_realize; + k->exit = pcie_pci_bridge_exit; + k->config_write = pcie_pci_bridge_write_config; + dc->vmsd = &pcie_pci_bridge_dev_vmstate; + dc->props = pcie_pci_bridge_dev_properties; + dc->vmsd = &pcie_pci_bridge_dev_vmstate; + dc->reset = &pcie_pci_bridge_reset; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + hc->plug = pcie_pci_bridge_hotplug_cb; + hc->unplug_request = pcie_pci_bridge_hot_unplug_request_cb; +} + +static const TypeInfo pcie_pci_bridge_info = { + .name = TYPE_PCIE_PCI_BRIDGE_DEV, + .parent = TYPE_PCI_BRIDGE, + .instance_size = sizeof(PCIEPCIBridge), + .class_init = pcie_pci_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { }, + } +}; + +static void pciepci_register(void) +{ + type_register_static(&pcie_pci_bridge_info); +} + +type_init(pciepci_register); -- cgit v1.1 From 70e1ee59bb9490d9ac529e96820a03b346086ca1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:48 +0300 Subject: hw/pci: introduce bridge-only vendor-specific capability to provide some hints to firmware On PCI init PCI bridges may need some extra info about bus number, IO, memory and prefetchable memory to reserve. QEMU can provide this with a special vendor-specific PCI capability. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci_bridge.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'hw') diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 720119b..17feae5 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -408,6 +408,52 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, br->bus_name = bus_name; } + +int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, + uint32_t bus_reserve, uint64_t io_reserve, + uint32_t mem_non_pref_reserve, + uint32_t mem_pref_32_reserve, + uint64_t mem_pref_64_reserve, + Error **errp) +{ + if (mem_pref_32_reserve != (uint32_t)-1 && + mem_pref_64_reserve != (uint64_t)-1) { + error_setg(errp, + "PCI resource reserve cap: PREF32 and PREF64 conflict"); + return -EINVAL; + } + + if (bus_reserve == (uint32_t)-1 && + io_reserve == (uint64_t)-1 && + mem_non_pref_reserve == (uint32_t)-1 && + mem_pref_32_reserve == (uint32_t)-1 && + mem_pref_64_reserve == (uint64_t)-1) { + return 0; + } + + size_t cap_len = sizeof(PCIBridgeQemuCap); + PCIBridgeQemuCap cap = { + .len = cap_len, + .type = REDHAT_PCI_CAP_RESOURCE_RESERVE, + .bus_res = bus_reserve, + .io = io_reserve, + .mem = mem_non_pref_reserve, + .mem_pref_32 = mem_pref_32_reserve, + .mem_pref_64 = mem_pref_64_reserve + }; + + int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, + cap_offset, cap_len, errp); + if (offset < 0) { + return offset; + } + + memcpy(dev->config + offset + PCI_CAP_FLAGS, + (char *)&cap + PCI_CAP_FLAGS, + cap_len - PCI_CAP_FLAGS); + return 0; +} + static const TypeInfo pci_bridge_type_info = { .name = TYPE_PCI_BRIDGE, .parent = TYPE_PCI_DEVICE, -- cgit v1.1 From 226263fb5cdaa4a4a95f1680fabbc9dd2123fd67 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:49 +0300 Subject: hw/pci: add QEMU-specific PCI capability to the Generic PCI Express Root Port To enable hotplugging of a newly created pcie-pci-bridge, we need to tell firmware (e.g. SeaBIOS) to reserve additional buses or IO/MEM/PREF space for pcie-root-port. Additional bus reservation allows us to hotplug pcie-pci-bridge into this root port. The number of buses and IO/MEM/PREF space to reserve are provided to the device via a corresponding property, and to the firmware via new PCI capability. The properties' default values are -1 to keep default behavior unchanged. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/gen_pcie_root_port.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'hw') diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index cb694d6..ed03ffc 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -16,6 +16,8 @@ #include "hw/pci/pcie_port.h" #define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port" +#define GEN_PCIE_ROOT_PORT(obj) \ + OBJECT_CHECK(GenPCIERootPort, (obj), TYPE_GEN_PCIE_ROOT_PORT) #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 @@ -26,6 +28,13 @@ typedef struct GenPCIERootPort { /*< public >*/ bool migrate_msix; + + /* additional resources to reserve on firmware init */ + uint32_t bus_reserve; + uint64_t io_reserve; + uint64_t mem_reserve; + uint64_t pref32_reserve; + uint64_t pref64_reserve; } GenPCIERootPort; static uint8_t gen_rp_aer_vector(const PCIDevice *d) @@ -60,6 +69,24 @@ static bool gen_rp_test_migrate_msix(void *opaque, int version_id) return rp->migrate_msix; } +static void gen_rp_realize(DeviceState *dev, Error **errp) +{ + PCIDevice *d = PCI_DEVICE(dev); + GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); + + rpc->parent_realize(dev, errp); + + int rc = pci_bridge_qemu_reserve_cap_init(d, 0, grp->bus_reserve, + grp->io_reserve, grp->mem_reserve, grp->pref32_reserve, + grp->pref64_reserve, errp); + + if (rc < 0) { + rpc->parent_class.exit(d); + return; + } +} + static const VMStateDescription vmstate_rp_dev = { .name = "pcie-root-port", .version_id = 1, @@ -78,6 +105,11 @@ static const VMStateDescription vmstate_rp_dev = { static Property gen_rp_props[] = { DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), + DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, bus_reserve, -1), + DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, io_reserve, -1), + DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, mem_reserve, -1), + DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, pref32_reserve, -1), + DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, pref64_reserve, -1), DEFINE_PROP_END_OF_LIST() }; @@ -92,6 +124,10 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Express Root Port"; dc->vmsd = &vmstate_rp_dev; dc->props = gen_rp_props; + + rpc->parent_realize = dc->realize; + dc->realize = gen_rp_realize; + rpc->aer_vector = gen_rp_aer_vector; rpc->interrupts_init = gen_rp_interrupts_init; rpc->interrupts_uninit = gen_rp_interrupts_uninit; -- cgit v1.1 From 66a4a0318e6b9539505491e4576fb93a708095d8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 17 Aug 2017 13:56:14 +0800 Subject: intel_iommu: fix missing BQL in pt fast path In vtd_switch_address_space() we did the memory region switch, however it's possible that the caller of it has not taken the BQL at all. Make sure we have it. CC: Paolo Bonzini CC: Jason Wang CC: Michael S. Tsirkin Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'hw') diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a7bf87a..3a5bb0b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -957,6 +957,8 @@ static bool vtd_dev_pt_enabled(VTDAddressSpace *as) static bool vtd_switch_address_space(VTDAddressSpace *as) { bool use_iommu; + /* Whether we need to take the BQL on our own */ + bool take_bql = !qemu_mutex_iothread_locked(); assert(as); @@ -967,6 +969,15 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) VTD_PCI_FUNC(as->devfn), use_iommu); + /* + * It's possible that we reach here without BQL, e.g., when called + * from vtd_pt_enable_fast_path(). However the memory APIs need + * it. We'd better make sure we have had it already, or, take it. + */ + if (take_bql) { + qemu_mutex_lock_iothread(); + } + /* Turn off first then on the other */ if (use_iommu) { memory_region_set_enabled(&as->sys_alias, false); @@ -976,6 +987,10 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) memory_region_set_enabled(&as->sys_alias, true); } + if (take_bql) { + qemu_mutex_unlock_iothread(); + } + return use_iommu; } -- cgit v1.1 From 0b4a775188cbe411d9ce7073ea1a24b72848327e Mon Sep 17 00:00:00 2001 From: Yoni Bettan Date: Tue, 5 Sep 2017 11:46:34 +0300 Subject: acpi/vmgenid: change device category to misc Moved vmgenid from uncategorized to misc category in QEMU help menu Signed-off-by: Yoni Bettan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/vmgenid.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hw') diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index a32b847..876723a 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -240,6 +240,7 @@ static void vmgenid_device_class_init(ObjectClass *klass, void *data) dc->realize = vmgenid_realize; dc->hotpluggable = false; dc->props = vmgenid_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); object_class_property_add_str(klass, VMGENID_GUID, NULL, vmgenid_set_guid, NULL); -- cgit v1.1 From c8389550dedc65892fba9c3df29423efd802f544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 7 Aug 2017 18:45:13 +0200 Subject: vmgenid: replace x-write-pointer-available hack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This compat property sole function is to prevent the device from being instantiated. Instead of requiring an extra compat property, check if fw_cfg has DMA enabled. fw_cfg is a built-in device that is initialized very early by the machine init code. We have at least one other device that also assumes fw_cfg_find() can be safely used on realize: pvpanic. This has the additional benefit of handling other cases properly, like: $ qemu-system-x86_64 -device vmgenid -machine none qemu-system-x86_64: -device vmgenid: vmgenid requires DMA write support in fw_cfg, which this machine type does not provide $ qemu-system-x86_64 -device vmgenid -machine pc-i440fx-2.9 -global fw_cfg.dma_enabled=off qemu-system-x86_64: -device vmgenid: vmgenid requires DMA write support in fw_cfg, which this machine type does not provide $ qemu-system-x86_64 -device vmgenid -machine pc-i440fx-2.6 -global fw_cfg.dma_enabled=on [boots normally] Suggested-by: Eduardo Habkost Signed-off-by: Marc-André Lureau Reviewed-by: Michael S. Tsirkin Reviewed-by: Eduardo Habkost Reviewed-by: Ben Warren Reviewed-by: Laszlo Ersek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/bios-linker-loader.c | 10 ++++++++++ hw/acpi/vmgenid.c | 9 +-------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index 046183a..d16b8bb 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -169,6 +169,16 @@ bios_linker_find_file(const BIOSLinker *linker, const char *name) } /* + * board code must realize fw_cfg first, as a fixed device, before + * another device realize function call bios_linker_loader_can_write_pointer() + */ +bool bios_linker_loader_can_write_pointer(void) +{ + FWCfgState *fw_cfg = fw_cfg_find(); + return fw_cfg && fw_cfg_dma_enabled(fw_cfg); +} + +/* * bios_linker_loader_alloc: ask guest to load file into guest memory. * * @linker: linker object instance diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 876723a..2876d8a 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -205,17 +205,11 @@ static void vmgenid_handle_reset(void *opaque) memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le)); } -static Property vmgenid_properties[] = { - DEFINE_PROP_BOOL("x-write-pointer-available", VmGenIdState, - write_pointer_available, true), - DEFINE_PROP_END_OF_LIST(), -}; - static void vmgenid_realize(DeviceState *dev, Error **errp) { VmGenIdState *vms = VMGENID(dev); - if (!vms->write_pointer_available) { + if (!bios_linker_loader_can_write_pointer()) { error_setg(errp, "%s requires DMA write support in fw_cfg, " "which this machine type does not provide", VMGENID_DEVICE); return; @@ -239,7 +233,6 @@ static void vmgenid_device_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_vmgenid; dc->realize = vmgenid_realize; dc->hotpluggable = false; - dc->props = vmgenid_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); object_class_property_add_str(klass, VMGENID_GUID, NULL, -- cgit v1.1 From 9b717a3a1318455afce761301fec114982ccbf1f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 16 Jul 2017 21:27:33 +0100 Subject: pci: move check for existing devfn into new pci_bus_devfn_available() helper Also touch up the logic in do_pci_register_device() accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 26f346d..002e669 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -953,6 +953,11 @@ uint16_t pci_requester_id(PCIDevice *dev) return pci_req_id_cache_extract(&dev->requester_id_cache); } +static bool pci_bus_devfn_available(PCIBus *bus, int devfn) +{ + return !(bus->devices[devfn]); +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn, @@ -976,14 +981,15 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); devfn += PCI_FUNC_MAX) { - if (!bus->devices[devfn]) + if (pci_bus_devfn_available(bus, devfn)) { goto found; + } } error_setg(errp, "PCI: no slot/function available for %s, all in use", name); return NULL; found: ; - } else if (bus->devices[devfn]) { + } else if (!pci_bus_devfn_available(bus, devfn)) { error_setg(errp, "PCI: slot %d function %d not available for %s," " in use by %s", PCI_SLOT(devfn), PCI_FUNC(devfn), name, -- cgit v1.1 From 8b8849844fd6a31956e934885f2a7ae9ac1a95d8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 16 Jul 2017 21:27:34 +0100 Subject: pci: add reserved slot check to do_pci_register_device() Add a new slot_reserved_mask bitmask to PCIBus indicating whether or not each PCI slot on the bus is reserved. Ensure that it is initialised to zero to maintain the existing behaviour that all slots are available by default, and add the additional check with appropriate error reporting to do_pci_register_device(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'hw') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 002e669..21e203b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -373,6 +373,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, { assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; + bus->slot_reserved_mask = 0x0; bus->address_space_mem = address_space_mem; bus->address_space_io = address_space_io; @@ -958,6 +959,11 @@ static bool pci_bus_devfn_available(PCIBus *bus, int devfn) return !(bus->devices[devfn]); } +static bool pci_bus_devfn_reserved(PCIBus *bus, int devfn) +{ + return bus->slot_reserved_mask & (1UL << PCI_SLOT(devfn)); +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn, @@ -981,14 +987,20 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); devfn += PCI_FUNC_MAX) { - if (pci_bus_devfn_available(bus, devfn)) { + if (pci_bus_devfn_available(bus, devfn) && + !pci_bus_devfn_reserved(bus, devfn)) { goto found; } } - error_setg(errp, "PCI: no slot/function available for %s, all in use", - name); + error_setg(errp, "PCI: no slot/function available for %s, all in use " + "or reserved", name); return NULL; found: ; + } else if (pci_bus_devfn_reserved(bus, devfn)) { + error_setg(errp, "PCI: slot %d function %d not available for %s," + " reserved", + PCI_SLOT(devfn), PCI_FUNC(devfn), name); + return NULL; } else if (!pci_bus_devfn_available(bus, devfn)) { error_setg(errp, "PCI: slot %d function %d not available for %s," " in use by %s", -- cgit v1.1 From 6f6f4aec749ba9a4fb58c7c20536a61b0381ff35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 7 Aug 2017 20:16:11 +0200 Subject: fw_cfg: rename read callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The callback is called on select. Furthermore, the next patch introduced a new callback, so rename the function type with a generic name. Signed-off-by: Marc-André Lureau Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/loader.c | 2 +- hw/nvram/fw_cfg.c | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'hw') diff --git a/hw/core/loader.c b/hw/core/loader.c index ebe574c..4593061 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -989,7 +989,7 @@ err: MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, - FWCfgReadCallback fw_callback, void *callback_opaque, + FWCfgCallback fw_callback, void *callback_opaque, AddressSpace *as, bool read_only) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 5bd9044..e3bd626 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -55,7 +55,7 @@ struct FWCfgEntry { bool allow_write; uint8_t *data; void *callback_opaque; - FWCfgReadCallback read_callback; + FWCfgCallback select_cb; }; #define JPG_FILE 0 @@ -236,8 +236,8 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key) /* entry successfully selected, now run callback if present */ arch = !!(key & FW_CFG_ARCH_LOCAL); e = &s->entries[arch][key & FW_CFG_ENTRY_MASK]; - if (e->read_callback) { - e->read_callback(e->callback_opaque); + if (e->select_cb) { + e->select_cb(e->callback_opaque); } } @@ -568,11 +568,11 @@ static const VMStateDescription vmstate_fw_cfg = { } }; -static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, - FWCfgReadCallback callback, - void *callback_opaque, - void *data, size_t len, - bool read_only) +static void fw_cfg_add_bytes_callback(FWCfgState *s, uint16_t key, + FWCfgCallback select_cb, + void *callback_opaque, + void *data, size_t len, + bool read_only) { int arch = !!(key & FW_CFG_ARCH_LOCAL); @@ -583,7 +583,7 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, s->entries[arch][key].data = data; s->entries[arch][key].len = (uint32_t)len; - s->entries[arch][key].read_callback = callback; + s->entries[arch][key].select_cb = select_cb; s->entries[arch][key].callback_opaque = callback_opaque; s->entries[arch][key].allow_write = !read_only; } @@ -610,7 +610,7 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) { - fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len, true); + fw_cfg_add_bytes_callback(s, key, NULL, NULL, data, len, true); } void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) @@ -736,7 +736,8 @@ static int get_fw_cfg_order(FWCfgState *s, const char *name) } void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, - FWCfgReadCallback callback, void *callback_opaque, + FWCfgCallback select_cb, + void *callback_opaque, void *data, size_t len, bool read_only) { int i, index, count; @@ -798,9 +799,10 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, } } - fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, - callback, callback_opaque, data, len, - read_only); + fw_cfg_add_bytes_callback(s, FW_CFG_FILE_FIRST + index, + select_cb, + callback_opaque, data, len, + read_only); s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); -- cgit v1.1