diff options
-rw-r--r-- | MAINTAINERS | 12 | ||||
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | docs/multiseat.txt | 19 | ||||
-rw-r--r-- | docs/specs/pci-ids.txt | 1 | ||||
-rw-r--r-- | hw/core/qdev-properties.c | 2 | ||||
-rw-r--r-- | hw/core/sysbus.c | 11 | ||||
-rw-r--r-- | hw/i386/Makefile.objs | 3 | ||||
-rw-r--r-- | hw/i386/acpi-build.c | 40 | ||||
-rw-r--r-- | hw/i386/ssdt-tpm-common.dsl | 36 | ||||
-rw-r--r-- | hw/i386/ssdt-tpm.dsl | 29 | ||||
-rw-r--r-- | hw/i386/ssdt-tpm.hex.generated | 109 | ||||
-rw-r--r-- | hw/i386/ssdt-tpm2.dsl | 29 | ||||
-rw-r--r-- | hw/i386/ssdt-tpm2.hex.generated | 109 | ||||
-rw-r--r-- | hw/pci-bridge/pci_bridge_dev.c | 117 | ||||
-rw-r--r-- | hw/pci-bridge/pci_expander_bridge.c | 57 | ||||
-rw-r--r-- | hw/virtio/vhost.c | 8 | ||||
-rw-r--r-- | hw/virtio/virtio-balloon.c | 4 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 4 | ||||
-rw-r--r-- | include/hw/pci/pci.h | 1 | ||||
-rw-r--r-- | include/hw/pci/pci_bridge.h | 4 | ||||
-rw-r--r-- | include/hw/pci/shpc.h | 11 | ||||
-rw-r--r-- | include/hw/sysbus.h | 17 | ||||
-rw-r--r-- | include/hw/virtio/virtio-balloon.h | 1 | ||||
-rw-r--r-- | include/migration/vmstate.h | 7 |
24 files changed, 263 insertions, 370 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 36391730c7..3d48a6bd65 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -644,7 +644,19 @@ M: Michael S. Tsirkin <mst@redhat.com> S: Supported F: include/hw/pci/* F: hw/pci/* + +ACPI +M: Michael S. Tsirkin <mst@redhat.com> +M: Igor Mammedov <imammedo@redhat.com> +S: Supported +F: include/hw/acpi/* +F: hw/mem/* F: hw/acpi/* +F: hw/i386/acpi-build.[hc] +F: hw/i386/*dsl +F: hw/arm/virt-acpi-build.c +F: include/hw/arm/virt-acpi-build.h +F: scripts/acpi*py ppc4xx M: Alexander Graf <agraf@suse.de> @@ -4773,7 +4773,7 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi -if test "glib_subprocess" = "yes" ; then +if test "$glib_subprocess" = "yes" ; then echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak fi echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak diff --git a/docs/multiseat.txt b/docs/multiseat.txt index b963665ef2..814496e94c 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -106,6 +106,25 @@ the devices attached to the seat. Background info is here: http://www.freedesktop.org/wiki/Software/systemd/multiseat/ + +guest side with pci-bridge-seat +------------------------------- + +Qemu version FIXME and newer has a new pci-bridge-seat device which +can be used instead of pci-bridge. Just swap the device name in the +qemu command line above. The only difference between the two devices +is the pci id. We can match the pci id instead of the device path +with a nice generic rule now, which simplifies the guest +configuration: + + [root@fedora ~]# cat /etc/udev/rules.d/70-qemu-pci-bridge-seat.rules + SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", \ + TAG+="seat", ENV{ID_AUTOSEAT}="1" + +Patch with this rule will be submitted to upstream udev/systemd, so +long-term, when systemd with this lands in distros, things will work +just fine without any manual guest configuration. + Enjoy! -- diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index e4a44908cb..0adcb89aac 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -47,6 +47,7 @@ PCI devices (other than virtio): 1b36:0005 PCI test device (docs/specs/pci-testdev.txt) 1b36:0006 PCI Rocker Ethernet switch device 1b36:0007 PCI SD Card Host Controller Interface (SDHCI) +1b36:000a PCI-PCI bridge (multiseat) All these devices are documented in docs/specs. diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 47c1e8f3c5..e9e686f260 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -131,7 +131,7 @@ PropertyInfo qdev_prop_bit = { static uint64_t qdev_get_prop_mask64(Property *prop) { assert(prop->info == &qdev_prop_bit); - return 0x1 << prop->bitnr; + return 0x1ull << prop->bitnr; } static void bit64_prop_set(DeviceState *dev, Property *props, bool val) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 92eced9424..278a2d1bdd 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -281,6 +281,9 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) static char *sysbus_get_fw_dev_path(DeviceState *dev) { SysBusDevice *s = SYS_BUS_DEVICE(dev); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s); + /* for the explicit unit address fallback case: */ + char *addr, *fw_dev_path; if (s->num_mmio) { return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), @@ -289,6 +292,14 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) if (s->num_pio) { return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); } + if (sbc->explicit_ofw_unit_address) { + addr = sbc->explicit_ofw_unit_address(s); + if (addr) { + fw_dev_path = g_strdup_printf("%s@%s", qdev_fw_name(dev), addr); + g_free(addr); + return fw_dev_path; + } + } return g_strdup(qdev_fw_name(dev)); } diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 0be5d97c59..bd4f147f9d 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -8,8 +8,7 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o obj-y += acpi-build.o hw/i386/acpi-build.o: hw/i386/acpi-build.c \ - hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex \ - hw/i386/ssdt-tpm.hex hw/i386/ssdt-tpm2.hex + hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ; then echo "$(2)"; else echo "$(3)"; fi ;) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b71e942567..00818b925b 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -433,9 +433,6 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, table_data->len - madt_start, 1); } -#include "hw/i386/ssdt-tpm.hex" -#include "hw/i386/ssdt-tpm2.hex" - /* Assign BSEL property to all buses. In the future, this can be changed * to only assign to buses that support hotplug. */ @@ -1328,6 +1325,19 @@ build_ssdt(GArray *table_data, GArray *linker, Aml *scope = aml_scope("PCI0"); /* Scan all PCI buses. Generate tables to support hotplug. */ build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); + + if (misc->tpm_version != TPM_VERSION_UNSPEC) { + dev = aml_device("ISA.TPM"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31"))); + aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, + TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); + aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + } + aml_append(sb_scope, scope); } } @@ -1383,22 +1393,9 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) } static void -build_tpm_ssdt(GArray *table_data, GArray *linker) -{ - void *tpm_ptr; - - tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm_aml)); - memcpy(tpm_ptr, ssdt_tpm_aml, sizeof(ssdt_tpm_aml)); -} - -static void build_tpm2(GArray *table_data, GArray *linker) { Acpi20TPM2 *tpm2_ptr; - void *tpm_ptr; - - tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm2_aml)); - memcpy(tpm_ptr, ssdt_tpm2_aml, sizeof(ssdt_tpm2_aml)); tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr); @@ -1726,16 +1723,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog); - acpi_add_table(table_offsets, tables_blob); - switch (misc.tpm_version) { - case TPM_VERSION_1_2: - build_tpm_ssdt(tables_blob, tables->linker); - break; - case TPM_VERSION_2_0: + if (misc.tpm_version == TPM_VERSION_2_0) { + acpi_add_table(table_offsets, tables_blob); build_tpm2(tables_blob, tables->linker); - break; - default: - assert(false); } } if (guest_info->numa_nodes) { diff --git a/hw/i386/ssdt-tpm-common.dsl b/hw/i386/ssdt-tpm-common.dsl deleted file mode 100644 index 9da49700d1..0000000000 --- a/hw/i386/ssdt-tpm-common.dsl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Common parts for TPM 1.2 and TPM 2 (with slight differences for PPI) - * to be #included - */ - - - External(\_SB.PCI0.ISA, DeviceObj) - Scope(\_SB.PCI0.ISA) { - /* TPM with emulated TPM TIS interface */ - Device (TPM) { - Name (_HID, EisaID ("PNP0C31")) - Name (_CRS, ResourceTemplate () - { - Memory32Fixed (ReadWrite, TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) - IRQNoFlags () {TPM_TIS_IRQ} - }) - Method (_STA, 0, NotSerialized) { - Return (0x0F) - } - } - } diff --git a/hw/i386/ssdt-tpm.dsl b/hw/i386/ssdt-tpm.dsl deleted file mode 100644 index d81478c1b5..0000000000 --- a/hw/i386/ssdt-tpm.dsl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "hw/acpi/tpm.h" - -ACPI_EXTRACT_ALL_CODE ssdt_tpm_aml - -DefinitionBlock ( - "ssdt-tpm.aml", // Output Filename - "SSDT", // Signature - 0x01, // SSDT Compliance Revision - "BXPC", // OEMID - "BXSSDT", // TABLE ID - 0x1 // OEM Revision - ) -{ -#include "ssdt-tpm-common.dsl" -} diff --git a/hw/i386/ssdt-tpm.hex.generated b/hw/i386/ssdt-tpm.hex.generated deleted file mode 100644 index 874418c946..0000000000 --- a/hw/i386/ssdt-tpm.hex.generated +++ /dev/null @@ -1,109 +0,0 @@ -static unsigned char ssdt_tpm_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x6b, -0x0, -0x0, -0x0, -0x1, -0x37, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x46, -0x4, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x33, -0x54, -0x50, -0x4d, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0x31, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x14, -0xa, -0x11, -0x86, -0x9, -0x0, -0x1, -0x0, -0x0, -0xd4, -0xfe, -0x0, -0x50, -0x0, -0x0, -0x22, -0x20, -0x0, -0x79, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf -}; diff --git a/hw/i386/ssdt-tpm2.dsl b/hw/i386/ssdt-tpm2.dsl deleted file mode 100644 index 58bbbf806d..0000000000 --- a/hw/i386/ssdt-tpm2.dsl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "hw/acpi/tpm.h" - -ACPI_EXTRACT_ALL_CODE ssdt_tpm2_aml - -DefinitionBlock ( - "ssdt-tpm2.aml", // Output Filename - "SSDT", // Signature - 0x01, // SSDT Compliance Revision - "BXPC", // OEMID - "BXSSDT", // TABLE ID - 0x1 // OEM Revision - ) -{ -#include "ssdt-tpm-common.dsl" -} diff --git a/hw/i386/ssdt-tpm2.hex.generated b/hw/i386/ssdt-tpm2.hex.generated deleted file mode 100644 index 9ea827151a..0000000000 --- a/hw/i386/ssdt-tpm2.hex.generated +++ /dev/null @@ -1,109 +0,0 @@ -static unsigned char ssdt_tpm2_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x6b, -0x0, -0x0, -0x0, -0x1, -0x37, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x46, -0x4, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x33, -0x54, -0x50, -0x4d, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0x31, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x14, -0xa, -0x11, -0x86, -0x9, -0x0, -0x1, -0x0, -0x0, -0xd4, -0xfe, -0x0, -0x50, -0x0, -0x0, -0x22, -0x20, -0x0, -0x79, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf -}; diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 36f73e1f8b..26aded9f00 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -28,7 +28,8 @@ #include "hw/pci/pci_bus.h" #include "hw/hotplug.h" -#define TYPE_PCI_BRIDGE_DEV "pci-bridge" +#define TYPE_PCI_BRIDGE_DEV "pci-bridge" +#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat" #define PCI_BRIDGE_DEV(obj) \ OBJECT_CHECK(PCIBridgeDev, (obj), TYPE_PCI_BRIDGE_DEV) @@ -40,6 +41,7 @@ struct PCIBridgeDev { MemoryRegion bar; uint8_t chassis_nr; #define PCI_BRIDGE_DEV_F_MSI_REQ 0 +#define PCI_BRIDGE_DEV_F_SHPC_REQ 1 uint32_t flags; }; typedef struct PCIBridgeDev PCIBridgeDev; @@ -54,11 +56,17 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) if (err) { goto bridge_error; } - dev->config[PCI_INTERRUPT_PIN] = 0x1; - memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); - err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); - if (err) { - goto shpc_error; + if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { + dev->config[PCI_INTERRUPT_PIN] = 0x1; + memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", + shpc_bar_size(dev)); + err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); + if (err) { + goto shpc_error; + } + } else { + /* MSI is not applicable without SHPC */ + bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); } err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); if (err) { @@ -71,15 +79,19 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) goto msi_error; } } - /* TODO: spec recommends using 64 bit prefetcheable BAR. - * Check whether that works well. */ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); + if (shpc_present(dev)) { + /* TODO: spec recommends using 64 bit prefetcheable BAR. + * Check whether that works well. */ + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); + } return 0; msi_error: slotid_cap_cleanup(dev); slotid_error: - shpc_cleanup(dev, &bridge_dev->bar); + if (shpc_present(dev)) { + shpc_cleanup(dev, &bridge_dev->bar); + } shpc_error: pci_bridge_exitfn(dev); bridge_error: @@ -93,12 +105,15 @@ static void pci_bridge_dev_exitfn(PCIDevice *dev) msi_uninit(dev); } slotid_cap_cleanup(dev); - shpc_cleanup(dev, &bridge_dev->bar); + if (shpc_present(dev)) { + shpc_cleanup(dev, &bridge_dev->bar); + } pci_bridge_exitfn(dev); } static void pci_bridge_dev_instance_finalize(Object *obj) { + /* this function is idempotent and handles (PCIDevice.shpc == NULL) */ shpc_free(PCI_DEVICE(obj)); } @@ -109,7 +124,9 @@ static void pci_bridge_dev_write_config(PCIDevice *d, if (msi_present(d)) { msi_write_config(d, address, val, len); } - shpc_cap_write_config(d, address, val, len); + if (shpc_present(d)) { + shpc_cap_write_config(d, address, val, len); + } } static void qdev_pci_bridge_dev_reset(DeviceState *qdev) @@ -117,25 +134,65 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev) PCIDevice *dev = PCI_DEVICE(qdev); pci_bridge_reset(qdev); - shpc_reset(dev); + if (shpc_present(dev)) { + shpc_reset(dev); + } } static Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ - DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0), - DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, + 0), + DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, + PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, + PCI_BRIDGE_DEV_F_SHPC_REQ, true), DEFINE_PROP_END_OF_LIST(), }; +static bool pci_device_shpc_present(void *opaque, int version_id) +{ + PCIDevice *dev = opaque; + + return shpc_present(dev); +} + static const VMStateDescription pci_bridge_dev_vmstate = { .name = "pci_bridge", .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), - SHPC_VMSTATE(shpc, PCIDevice), + SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present), VMSTATE_END_OF_LIST() } }; +static void pci_bridge_dev_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_PCI_BRIDGE_DEV); + return; + } + shpc_device_hotplug_cb(hotplug_dev, dev, errp); +} + +static void pci_bridge_dev_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_PCI_BRIDGE_DEV); + return; + } + shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); +} + static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -154,8 +211,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->props = pci_bridge_dev_properties; dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hc->plug = shpc_device_hotplug_cb; - hc->unplug_request = shpc_device_hot_unplug_request_cb; + hc->plug = pci_bridge_dev_hotplug_cb; + hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb; } static const TypeInfo pci_bridge_dev_info = { @@ -170,9 +227,31 @@ static const TypeInfo pci_bridge_dev_info = { } }; +/* + * Multiseat bridge. Same as the standard pci bridge, only with a + * different pci id, so we can match it easily in the guest for + * automagic multiseat configuration. See docs/multiseat.txt for more. + */ +static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT; + dc->desc = "Standard PCI Bridge (multiseat)"; +} + +static const TypeInfo pci_bridge_dev_seat_info = { + .name = TYPE_PCI_BRIDGE_SEAT_DEV, + .parent = TYPE_PCI_BRIDGE_DEV, + .instance_size = sizeof(PCIBridgeDev), + .class_init = pci_bridge_dev_seat_class_init, +}; + static void pci_bridge_dev_register(void) { type_register_static(&pci_bridge_dev_info); + type_register_static(&pci_bridge_dev_seat_info); } type_init(pci_bridge_dev_register); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index ec2bb458f7..57f8a3762b 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -14,6 +14,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" #include "hw/i386/pc.h" #include "qemu/range.h" #include "qemu/error-report.h" @@ -42,6 +43,8 @@ typedef struct PXBDev { uint16_t numa_node; } PXBDev; +static GList *pxb_dev_list; + #define TYPE_PXB_HOST "pxb-host" static int pxb_bus_num(PCIBus *bus) @@ -88,12 +91,45 @@ static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, return bus->bus_path; } +static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) +{ + const PCIHostState *pxb_host; + const PCIBus *pxb_bus; + const PXBDev *pxb_dev; + int position; + const DeviceState *pxb_dev_base; + const PCIHostState *main_host; + const SysBusDevice *main_host_sbd; + + pxb_host = PCI_HOST_BRIDGE(dev); + pxb_bus = pxb_host->bus; + pxb_dev = PXB_DEV(pxb_bus->parent_dev); + position = g_list_index(pxb_dev_list, pxb_dev); + assert(position >= 0); + + pxb_dev_base = DEVICE(pxb_dev); + main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent); + main_host_sbd = SYS_BUS_DEVICE(main_host); + + if (main_host_sbd->num_mmio > 0) { + return g_strdup_printf(TARGET_FMT_plx ",%x", + main_host_sbd->mmio[0].addr, position + 1); + } + if (main_host_sbd->num_pio > 0) { + return g_strdup_printf("i%04x,%x", + main_host_sbd->pio[0], position + 1); + } + return NULL; +} + static void pxb_host_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); dc->fw_name = "pci"; + sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address; hc->root_bus_path = pxb_host_root_bus_path; } @@ -148,6 +184,15 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) return pin - PCI_SLOT(pxb->devfn); } +static gint pxb_compare(gconstpointer a, gconstpointer b) +{ + const PXBDev *pxb_a = a, *pxb_b = b; + + return pxb_a->bus_nr < pxb_b->bus_nr ? -1 : + pxb_a->bus_nr > pxb_b->bus_nr ? 1 : + 0; +} + static int pxb_dev_initfn(PCIDevice *dev) { PXBDev *pxb = PXB_DEV(dev); @@ -175,7 +220,8 @@ static int pxb_dev_initfn(PCIDevice *dev) bds = qdev_create(BUS(bus), "pci-bridge"); bds->id = dev_name; - qdev_prop_set_uint8(bds, "chassis_nr", pxb->bus_nr); + qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); + qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); PCI_HOST_BRIDGE(ds)->bus = bus; @@ -190,9 +236,17 @@ static int pxb_dev_initfn(PCIDevice *dev) PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); + pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); return 0; } +static void pxb_dev_exitfn(PCIDevice *pci_dev) +{ + PXBDev *pxb = PXB_DEV(pci_dev); + + pxb_dev_list = g_list_remove(pxb_dev_list, pxb); +} + static Property pxb_dev_properties[] = { /* Note: 0 is not a legal a PXB bus number. */ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), @@ -206,6 +260,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = pxb_dev_initfn; + k->exit = pxb_dev_exitfn; k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_PXB; k->class_id = PCI_CLASS_BRIDGE_HOST; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index a6dcc79399..2712c6fc0a 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -999,7 +999,7 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int i, r; + int i, r, e; if (!k->set_host_notifier) { fprintf(stderr, "binding does not support host notifiers\n"); r = -ENOSYS; @@ -1017,12 +1017,12 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_vq: while (--i >= 0) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - if (r < 0) { + e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); + if (e < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); fflush(stderr); } - assert (r >= 0); + assert (e >= 0); } fail: return r; diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 78bc14fc85..2990f8de5d 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -312,6 +312,8 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f) { + VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); + f |= dev->host_features; virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ); return f; } @@ -423,6 +425,8 @@ static void virtio_balloon_instance_init(Object *obj) } static Property virtio_balloon_properties[] = { + DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, + VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 70bc6d801e..6a0174e9cc 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -977,7 +977,7 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, val = proxy->gfselect; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { val = proxy->guest_features[proxy->gfselect]; } break; @@ -1052,7 +1052,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, proxy->gfselect = val; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { proxy->guest_features[proxy->gfselect] = val; virtio_set_features(vdev, (((uint64_t)proxy->guest_features[1]) << 32) | diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d44bc84d1e..551cb3d608 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -92,6 +92,7 @@ #define PCI_DEVICE_ID_REDHAT_SDHCI 0x0007 #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 #define PCI_DEVICE_ID_REDHAT_PXB 0x0009 +#define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index 1d8f9973c7..93b621cef3 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -28,6 +28,10 @@ #include "hw/pci/pci.h" +#define PCI_BRIDGE_DEV_PROP_CHASSIS_NR "chassis_nr" +#define PCI_BRIDGE_DEV_PROP_MSI "msi" +#define PCI_BRIDGE_DEV_PROP_SHPC "shpc" + int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, uint16_t svid, uint16_t ssid); diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index 9bbea39996..2c871b947b 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -6,6 +6,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "hw/hotplug.h" +#include "hw/pci/pci.h" struct SHPCDevice { /* Capability offset in device's config space */ @@ -51,7 +52,13 @@ void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); extern VMStateInfo shpc_vmstate_info; -#define SHPC_VMSTATE(_field, _type) \ - VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0) +#define SHPC_VMSTATE(_field, _type, _test) \ + VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _type, _test, 0, \ + shpc_vmstate_info, 0) + +static inline bool shpc_present(const PCIDevice *dev) +{ + return dev->cap_present & QEMU_PCI_CAP_SHPC; +} #endif diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index d1f3f000f9..34f93c39bf 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -41,6 +41,23 @@ typedef struct SysBusDeviceClass { /*< public >*/ int (*init)(SysBusDevice *dev); + + /* + * Let the sysbus device format its own non-PIO, non-MMIO unit address. + * + * Sometimes a class of SysBusDevices has neither MMIO nor PIO resources, + * yet instances of it would like to distinguish themselves, in + * OpenFirmware device paths, from other instances of the same class on the + * sysbus. For that end we expose this callback. + * + * The implementation is not supposed to change *@dev, or incur other + * observable change. + * + * The function returns a dynamically allocated string. On error, NULL + * should be returned; the unit address portion of the OFW node will be + * omitted then. (This is not considered a fatal error.) + */ + char *(*explicit_ofw_unit_address)(const SysBusDevice *dev); } SysBusDeviceClass; struct SysBusDevice { diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 346a9fdb7d..09c2ce4dcd 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -42,6 +42,7 @@ typedef struct VirtIOBalloon { QEMUTimer *stats_timer; int64_t stats_last_update; int64_t stats_poll_interval; + uint32_t host_features; } VirtIOBalloon; #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 7153b1e145..0695d7c3de 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -500,9 +500,10 @@ extern const VMStateInfo vmstate_info_bitmap; .start = (_start), \ } -#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \ +#define VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _state, _test, _version, _info, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ + .field_exists = (_test), \ .size = (_size), \ .info = &(_info), \ .flags = VMS_BUFFER, \ @@ -562,6 +563,10 @@ extern const VMStateInfo vmstate_info_bitmap; VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \ _vmsd, _type) +#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) \ + VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _state, NULL, _version, _info, \ + _size) + #define VMSTATE_BOOL_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_bool, bool) |