aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/aml-build.c8
-rw-r--r--hw/acpi/erst.c5
-rw-r--r--hw/acpi/pcihp.c12
-rw-r--r--hw/arm/aspeed.c100
-rw-r--r--hw/arm/aspeed_ast2600.c2
-rw-r--r--hw/arm/aspeed_soc.c2
-rw-r--r--hw/audio/cs4231a.c2
-rw-r--r--hw/audio/gus.c2
-rw-r--r--hw/audio/sb16.c2
-rw-r--r--hw/block/fdc-isa.c2
-rw-r--r--hw/block/m25p80.c1
-rw-r--r--hw/char/parallel.c2
-rw-r--r--hw/char/serial-isa.c2
-rw-r--r--hw/gpio/aspeed_gpio.c2
-rw-r--r--hw/i386/acpi-build.c8
-rw-r--r--hw/i386/acpi-microvm.c6
-rw-r--r--hw/i386/intel_iommu.c14
-rw-r--r--hw/i386/intel_iommu_internal.h1
-rw-r--r--hw/i386/pc.c30
-rw-r--r--hw/i386/pc_piix.c1
-rw-r--r--hw/i386/x86.c16
-rw-r--r--hw/ide/isa.c2
-rw-r--r--hw/input/pckbd.c26
-rw-r--r--hw/ipmi/isa_ipmi_bt.c2
-rw-r--r--hw/ipmi/isa_ipmi_kcs.c2
-rw-r--r--hw/isa/isa-bus.c37
-rw-r--r--hw/isa/piix4.c56
-rw-r--r--hw/mips/gt64xxx_pci.c80
-rw-r--r--hw/mips/malta.c7
-rw-r--r--hw/misc/pvpanic-isa.c4
-rw-r--r--hw/misc/pvpanic-pci.c4
-rw-r--r--hw/misc/pvpanic.c5
-rw-r--r--hw/net/ne2000-isa.c2
-rw-r--r--hw/net/virtio-net.c13
-rw-r--r--hw/pci-bridge/pci_expander_bridge.c6
-rw-r--r--hw/pci-bridge/xio3130_downstream.c5
-rw-r--r--hw/pci-bridge/xio3130_upstream.c2
-rw-r--r--hw/pci/meson.build1
-rw-r--r--hw/pci/pci.c104
-rw-r--r--hw/pci/pcie.c16
-rw-r--r--hw/pci/pcie_sriov.c302
-rw-r--r--hw/pci/trace-events5
-rw-r--r--hw/rtc/m48t59-isa.c9
-rw-r--r--hw/rtc/mc146818rtc.c13
-rw-r--r--hw/smbios/smbios.c80
-rw-r--r--hw/ssi/aspeed_smc.c53
-rw-r--r--hw/tpm/tpm_tis_isa.c2
-rw-r--r--hw/virtio/trace-events4
-rw-r--r--hw/virtio/vhost-user-i2c.c11
-rw-r--r--hw/virtio/vhost-user.c61
-rw-r--r--hw/virtio/vhost-vdpa.c21
-rw-r--r--hw/virtio/vhost-vsock-common.c10
-rw-r--r--hw/virtio/vhost.c6
-rw-r--r--hw/virtio/virtio-bus.c12
-rw-r--r--hw/virtio/virtio-iommu.c99
55 files changed, 945 insertions, 337 deletions
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 8966e16320..1773cf55f1 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -2152,7 +2152,13 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
build_append_int_noprefix(tbl, 0, 1); /* DAY_ALRM */
build_append_int_noprefix(tbl, 0, 1); /* MON_ALRM */
build_append_int_noprefix(tbl, f->rtc_century, 1); /* CENTURY */
- build_append_int_noprefix(tbl, 0, 2); /* IAPC_BOOT_ARCH */
+ /* IAPC_BOOT_ARCH */
+ if (f->rev == 1) {
+ build_append_int_noprefix(tbl, 0, 2);
+ } else {
+ /* since ACPI v2.0 */
+ build_append_int_noprefix(tbl, f->iapc_boot_arch, 2);
+ }
build_append_int_noprefix(tbl, 0, 1); /* Reserved */
build_append_int_noprefix(tbl, f->flags, 4); /* Flags */
diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c
index c0a23cf467..de509c2b48 100644
--- a/hw/acpi/erst.c
+++ b/hw/acpi/erst.c
@@ -80,11 +80,6 @@
#define UEFI_CPER_RECORD_MIN_SIZE 128U
#define UEFI_CPER_RECORD_LENGTH_OFFSET 20U
#define UEFI_CPER_RECORD_ID_OFFSET 96U
-#define IS_UEFI_CPER_RECORD(ptr) \
- (((ptr)[0] == 'C') && \
- ((ptr)[1] == 'P') && \
- ((ptr)[2] == 'E') && \
- ((ptr)[3] == 'R'))
/*
* NOTE that when accessing CPER fields within a record, memcpy()
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
index 6befd23e16..6351bd3424 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -32,6 +32,7 @@
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pcie_port.h"
+#include "hw/pci-bridge/xio3130_downstream.h"
#include "hw/i386/acpi-build.h"
#include "hw/acpi/acpi.h"
#include "hw/pci/pci_bus.h"
@@ -336,6 +337,8 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
{
PCIDevice *pdev = PCI_DEVICE(dev);
int slot = PCI_SLOT(pdev->devfn);
+ PCIDevice *bridge;
+ PCIBus *bus;
int bsel;
/* Don't send event when device is enabled during qemu machine creation:
@@ -365,7 +368,14 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
return;
}
- bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
+ bus = pci_get_bus(pdev);
+ bridge = pci_bridge_get_device(bus);
+ if (object_dynamic_cast(OBJECT(bridge), TYPE_PCIE_ROOT_PORT) ||
+ object_dynamic_cast(OBJECT(bridge), TYPE_XIO3130_DOWNSTREAM)) {
+ pcie_cap_slot_enable_power(bridge);
+ }
+
+ bsel = acpi_pcihp_get_bsel(bus);
g_assert(bsel >= 0);
s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 11558b327b..d205384d98 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -167,6 +167,11 @@ struct AspeedMachineState {
#define FUJI_BMC_HW_STRAP1 0x00000000
#define FUJI_BMC_HW_STRAP2 0x00000000
+/* Bletchley hardware value */
+/* TODO: Leave same as EVB for now. */
+#define BLETCHLEY_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1
+#define BLETCHLEY_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2
+
/*
* The max ram region is for firmwares that scan the address space
* with load/store to guess how much RAM the SoC has.
@@ -246,7 +251,7 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size,
Error **errp)
{
BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
- uint8_t *storage;
+ g_autofree void *storage = NULL;
int64_t size;
/* The block backend size should have already been 'validated' by
@@ -262,23 +267,25 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size,
rom_size = size;
}
- storage = g_new0(uint8_t, rom_size);
+ storage = g_malloc0(rom_size);
if (blk_pread(blk, 0, storage, rom_size) < 0) {
error_setg(errp, "failed to read the initial flash content");
return;
}
rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr);
- g_free(storage);
}
-static void aspeed_board_init_flashes(AspeedSMCState *s,
- const char *flashtype,
- int unit0)
+static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype,
+ unsigned int count, int unit0)
{
- int i ;
+ int i;
+
+ if (!flashtype) {
+ return;
+ }
- for (i = 0; i < s->num_cs; ++i) {
+ for (i = 0; i < count; ++i) {
DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i);
qemu_irq cs_line;
DeviceState *dev;
@@ -345,8 +352,6 @@ static void aspeed_machine_init(MachineState *machine)
&error_abort);
object_property_set_int(OBJECT(&bmc->soc), "hw-strap2", amc->hw_strap2,
&error_abort);
- object_property_set_int(OBJECT(&bmc->soc), "num-cs", amc->num_cs,
- &error_abort);
object_property_set_link(OBJECT(&bmc->soc), "dram",
OBJECT(machine->ram), &error_abort);
if (machine->kernel_filename) {
@@ -374,10 +379,10 @@ static void aspeed_machine_init(MachineState *machine)
aspeed_board_init_flashes(&bmc->soc.fmc,
bmc->fmc_model ? bmc->fmc_model : amc->fmc_model,
- 0);
+ amc->num_cs, 0);
aspeed_board_init_flashes(&bmc->soc.spi[0],
bmc->spi_model ? bmc->spi_model : amc->spi_model,
- bmc->soc.fmc.num_cs);
+ 1, amc->num_cs);
/* Install first FMC flash content as a boot rom. */
if (drive0) {
@@ -897,6 +902,54 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
}
}
+#define TYPE_TMP421 "tmp421"
+
+static void bletchley_bmc_i2c_init(AspeedMachineState *bmc)
+{
+ AspeedSoCState *soc = &bmc->soc;
+ I2CBus *i2c[13] = {};
+ for (int i = 0; i < 13; i++) {
+ if ((i == 8) || (i == 11)) {
+ continue;
+ }
+ i2c[i] = aspeed_i2c_get_bus(&soc->i2c, i);
+ }
+
+ /* Bus 0 - 5 all have the same config. */
+ for (int i = 0; i < 6; i++) {
+ /* Missing model: ti,ina230 @ 0x45 */
+ /* Missing model: mps,mp5023 @ 0x40 */
+ i2c_slave_create_simple(i2c[i], TYPE_TMP421, 0x4f);
+ /* Missing model: nxp,pca9539 @ 0x76, but PCA9552 works enough */
+ i2c_slave_create_simple(i2c[i], TYPE_PCA9552, 0x76);
+ i2c_slave_create_simple(i2c[i], TYPE_PCA9552, 0x67);
+ /* Missing model: fsc,fusb302 @ 0x22 */
+ }
+
+ /* Bus 6 */
+ at24c_eeprom_init(i2c[6], 0x56, 65536);
+ /* Missing model: nxp,pcf85263 @ 0x51 , but ds1338 works enough */
+ i2c_slave_create_simple(i2c[6], "ds1338", 0x51);
+
+
+ /* Bus 7 */
+ at24c_eeprom_init(i2c[7], 0x54, 65536);
+
+ /* Bus 9 */
+ i2c_slave_create_simple(i2c[9], TYPE_TMP421, 0x4f);
+
+ /* Bus 10 */
+ i2c_slave_create_simple(i2c[10], TYPE_TMP421, 0x4f);
+ /* Missing model: ti,hdc1080 @ 0x40 */
+ i2c_slave_create_simple(i2c[10], TYPE_PCA9552, 0x67);
+
+ /* Bus 12 */
+ /* Missing model: adi,adm1278 @ 0x11 */
+ i2c_slave_create_simple(i2c[12], TYPE_TMP421, 0x4c);
+ i2c_slave_create_simple(i2c[12], TYPE_TMP421, 0x4d);
+ i2c_slave_create_simple(i2c[12], TYPE_PCA9552, 0x67);
+}
+
static bool aspeed_get_mmio_exec(Object *obj, Error **errp)
{
return ASPEED_MACHINE(obj)->mmio_exec;
@@ -1220,6 +1273,25 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data)
aspeed_soc_num_cpus(amc->soc_name);
};
+static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc);
+
+ mc->desc = "Facebook Bletchley BMC (Cortex-A7)";
+ amc->soc_name = "ast2600-a3";
+ amc->hw_strap1 = BLETCHLEY_BMC_HW_STRAP1;
+ amc->hw_strap2 = BLETCHLEY_BMC_HW_STRAP2;
+ amc->fmc_model = "w25q01jvq";
+ amc->spi_model = NULL;
+ amc->num_cs = 2;
+ amc->macs_mask = ASPEED_MAC2_ON;
+ amc->i2c_init = bletchley_bmc_i2c_init;
+ mc->default_ram_size = 512 * MiB;
+ mc->default_cpus = mc->min_cpus = mc->max_cpus =
+ aspeed_soc_num_cpus(amc->soc_name);
+}
+
static const TypeInfo aspeed_machine_types[] = {
{
.name = MACHINE_TYPE_NAME("palmetto-bmc"),
@@ -1274,6 +1346,10 @@ static const TypeInfo aspeed_machine_types[] = {
.parent = TYPE_ASPEED_MACHINE,
.class_init = aspeed_machine_fuji_class_init,
}, {
+ .name = MACHINE_TYPE_NAME("bletchley-bmc"),
+ .parent = TYPE_ASPEED_MACHINE,
+ .class_init = aspeed_machine_bletchley_class_init,
+ }, {
.name = TYPE_ASPEED_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(AspeedMachineState),
diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
index 21cd3342c5..c1e15e3773 100644
--- a/hw/arm/aspeed_ast2600.c
+++ b/hw/arm/aspeed_ast2600.c
@@ -163,7 +163,6 @@ static void aspeed_soc_ast2600_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
object_initialize_child(obj, "fmc", &s->fmc, typename);
- object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs");
for (i = 0; i < sc->spis_num; i++) {
snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
@@ -383,7 +382,6 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
for (i = 0; i < sc->spis_num; i++) {
object_property_set_link(OBJECT(&s->spi[i]), "dram",
OBJECT(s->dram_mr), &error_abort);
- object_property_set_int(OBJECT(&s->spi[i]), "num-cs", 1, &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) {
return;
}
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 7d53cf2f51..58714cb2a0 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -170,7 +170,6 @@ static void aspeed_soc_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
object_initialize_child(obj, "fmc", &s->fmc, typename);
- object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs");
for (i = 0; i < sc->spis_num; i++) {
snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
@@ -327,7 +326,6 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
/* SPI */
for (i = 0; i < sc->spis_num; i++) {
- object_property_set_int(OBJECT(&s->spi[i]), "num-cs", 1, &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) {
return;
}
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index 7d60ce6f0e..0723e39430 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -677,7 +677,7 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp)
return;
}
- isa_init_irq(d, &s->pic, s->irq);
+ s->pic = isa_get_irq(d, s->irq);
k = ISADMA_GET_CLASS(s->isa_dma);
k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index e8719ee117..42f010b671 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -282,7 +282,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
s->emu.himemaddr = s->himem;
s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
s->emu.opaque = s;
- isa_init_irq (d, &s->pic, s->emu.gusirq);
+ s->pic = isa_get_irq(d, s->emu.gusirq);
AUD_set_active_out (s->voice, 1);
}
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index 60f1f75e3a..2215386ddb 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -1408,7 +1408,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp)
return;
}
- isa_init_irq (isadev, &s->pic, s->irq);
+ s->pic = isa_get_irq(isadev, s->irq);
s->mixer_regs[0x80] = magic_of_irq (s->irq);
s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c
index ab663dce93..fa20450747 100644
--- a/hw/block/fdc-isa.c
+++ b/hw/block/fdc-isa.c
@@ -94,7 +94,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp)
isa->iobase, fdc_portio_list, fdctrl,
"fdc");
- isa_init_irq(isadev, &fdctrl->irq, isa->irq);
+ fdctrl->irq = isa_get_irq(isadev, isa->irq);
fdctrl->dma_chann = isa->dma;
if (fdctrl->dma_chann != -1) {
IsaDmaClass *k;
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index c6bf3c6bfa..7d3d8b12e0 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -340,6 +340,7 @@ static const FlashPartInfo known_devices[] = {
{ INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) },
{ INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K) },
+ { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K) },
};
typedef enum {
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index b45e67bfbb..adb9bd9be3 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -553,7 +553,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp)
index++;
base = isa->iobase;
- isa_init_irq(isadev, &s->irq, isa->isairq);
+ s->irq = isa_get_irq(isadev, isa->isairq);
qemu_register_reset(parallel_reset, s);
qemu_chr_fe_set_handlers(&s->chr, parallel_can_receive, NULL,
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
index 1b8b303079..7a7ed239cd 100644
--- a/hw/char/serial-isa.c
+++ b/hw/char/serial-isa.c
@@ -75,7 +75,7 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp)
}
index++;
- isa_init_irq(isadev, &s->irq, isa->isairq);
+ s->irq = isa_get_irq(isadev, isa->isairq);
qdev_realize(DEVICE(s), NULL, errp);
qdev_set_legacy_instance_id(dev, isa->iobase, 3);
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
index 911d21c8cf..c63634d3d3 100644
--- a/hw/gpio/aspeed_gpio.c
+++ b/hw/gpio/aspeed_gpio.c
@@ -571,7 +571,7 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
HWADDR_PRIx"\n", __func__, offset);
return 0;
- };
+ }
}
static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index ebd47aa26f..4ad4d7286c 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -38,6 +38,7 @@
#include "hw/nvram/fw_cfg.h"
#include "hw/acpi/bios-linker-loader.h"
#include "hw/isa/isa.h"
+#include "hw/input/i8042.h"
#include "hw/block/fdc.h"
#include "hw/acpi/memory_hotplug.h"
#include "sysemu/tpm.h"
@@ -192,6 +193,13 @@ static void init_common_fadt_data(MachineState *ms, Object *o,
.address = object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK, NULL)
},
};
+
+ /*
+ * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture
+ * Flags, bit offset 1 - 8042.
+ */
+ fadt.iapc_boot_arch = iapc_boot_arch_8042();
+
*data = fadt;
}
diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c
index 68ca7e7fc2..fb09185cbd 100644
--- a/hw/i386/acpi-microvm.c
+++ b/hw/i386/acpi-microvm.c
@@ -37,6 +37,7 @@
#include "hw/pci/pcie_host.h"
#include "hw/usb/xhci.h"
#include "hw/virtio/virtio-mmio.h"
+#include "hw/input/i8042.h"
#include "acpi-common.h"
#include "acpi-microvm.h"
@@ -187,6 +188,11 @@ static void acpi_build_microvm(AcpiBuildTables *tables,
.address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET,
},
.reset_val = ACPI_GED_RESET_VALUE,
+ /*
+ * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture
+ * Flags, bit offset 1 - 8042.
+ */
+ .iapc_boot_arch = iapc_boot_arch_8042(),
};
table_offsets = g_array_new(false, true /* clear */,
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 4c6c016388..32471a44cb 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3030,6 +3030,13 @@ static int vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
IntelIOMMUState *s = vtd_as->iommu_state;
+ /* TODO: add support for VFIO and vhost users */
+ if (s->snoop_control) {
+ error_setg_errno(errp, -ENOTSUP,
+ "Snoop Control with vhost or VFIO is not supported");
+ return -ENOTSUP;
+ }
+
/* Update per-address-space notifier flags */
vtd_as->notifier_flags = new;
@@ -3113,6 +3120,7 @@ static Property vtd_properties[] = {
VTD_HOST_ADDRESS_WIDTH),
DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE),
DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE),
+ DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false),
DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -3643,7 +3651,7 @@ static void vtd_init(IntelIOMMUState *s)
vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
- if (s->scalable_mode) {
+ if (s->scalable_mode || s->snoop_control) {
vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP;
@@ -3674,6 +3682,10 @@ static void vtd_init(IntelIOMMUState *s)
s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS;
}
+ if (s->snoop_control) {
+ s->ecap |= VTD_ECAP_SC;
+ }
+
vtd_reset_caches(s);
/* Define registers with default values and bit semantics */
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index a6c788049b..1ff13b40f9 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -188,6 +188,7 @@
#define VTD_ECAP_IR (1ULL << 3)
#define VTD_ECAP_EIM (1ULL << 4)
#define VTD_ECAP_PT (1ULL << 6)
+#define VTD_ECAP_SC (1ULL << 7)
#define VTD_ECAP_MHMV (15ULL << 20)
#define VTD_ECAP_SRS (1ULL << 31)
#define VTD_ECAP_SMTS (1ULL << 43)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c8696ac01e..fd55fc725c 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -318,8 +318,6 @@ GlobalProperty pc_compat_2_0[] = {
{ "pci-serial-4x", "prog_if", "0" },
{ "virtio-net-pci", "guest_announce", "off" },
{ "ICH9-LPC", "memory-hotplug-support", "off" },
- { "xio3130-downstream", COMPAT_PROP_PCP, "off" },
- { "ioh3420", COMPAT_PROP_PCP, "off" },
};
const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0);
@@ -1014,7 +1012,8 @@ static const MemoryRegionOps ioportF0_io_ops = {
},
};
-static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
+static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl,
+ bool create_i8042, bool no_vmport)
{
int i;
DriveInfo *fd[MAX_FD];
@@ -1036,6 +1035,10 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
}
}
+ if (!create_i8042) {
+ return;
+ }
+
i8042 = isa_create_simple(isa_bus, "i8042");
if (!no_vmport) {
isa_create_simple(isa_bus, TYPE_VMPORT);
@@ -1131,7 +1134,8 @@ void pc_basic_device_init(struct PCMachineState *pcms,
i8257_dma_init(isa_bus, 0);
/* Super I/O */
- pc_superio_init(isa_bus, create_fdctrl, pcms->vmport != ON_OFF_AUTO_ON);
+ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled,
+ pcms->vmport != ON_OFF_AUTO_ON);
}
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
@@ -1512,6 +1516,20 @@ static void pc_machine_set_hpet(Object *obj, bool value, Error **errp)
pcms->hpet_enabled = value;
}
+static bool pc_machine_get_i8042(Object *obj, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ return pcms->i8042_enabled;
+}
+
+static void pc_machine_set_i8042(Object *obj, bool value, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ pcms->i8042_enabled = value;
+}
+
static bool pc_machine_get_default_bus_bypass_iommu(Object *obj, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
@@ -1641,6 +1659,7 @@ static void pc_machine_initfn(Object *obj)
pcms->smbus_enabled = true;
pcms->sata_enabled = true;
pcms->pit_enabled = true;
+ pcms->i8042_enabled = true;
pcms->max_fw_size = 8 * MiB;
#ifdef CONFIG_HPET
pcms->hpet_enabled = true;
@@ -1777,6 +1796,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "hpet",
"Enable/disable high precision event timer emulation");
+ object_class_property_add_bool(oc, PC_MACHINE_I8042,
+ pc_machine_get_i8042, pc_machine_set_i8042);
+
object_class_property_add_bool(oc, "default-bus-bypass-iommu",
pc_machine_get_default_bus_bypass_iommu,
pc_machine_set_default_bus_bypass_iommu);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 8d33cf689d..b72c03d0a6 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -757,6 +757,7 @@ static void pc_i440fx_1_7_machine_options(MachineClass *m)
m->hw_version = "1.7.0";
m->default_machine_opts = NULL;
m->option_rom_has_mr = true;
+ m->deprecation_reason = "old and unattended - use a newer version instead";
compat_props_add(m->compat_props, pc_compat_1_7, pc_compat_1_7_len);
pcmc->smbios_defaults = false;
pcmc->gigabyte_align = false;
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
index b84840a1bb..4cf107baea 100644
--- a/hw/i386/x86.c
+++ b/hw/i386/x86.c
@@ -83,24 +83,11 @@ inline void init_topo_info(X86CPUTopoInfo *topo_info,
uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms,
unsigned int cpu_index)
{
- X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms);
X86CPUTopoInfo topo_info;
- uint32_t correct_id;
- static bool warned;
init_topo_info(&topo_info, x86ms);
- correct_id = x86_apicid_from_cpu_idx(&topo_info, cpu_index);
- if (x86mc->compat_apic_id_mode) {
- if (cpu_index != correct_id && !warned && !qtest_enabled()) {
- error_report("APIC IDs set in compatibility mode, "
- "CPU topology won't match the configuration");
- warned = true;
- }
- return cpu_index;
- } else {
- return correct_id;
- }
+ return x86_apicid_from_cpu_idx(&topo_info, cpu_index);
}
@@ -1330,7 +1317,6 @@ static void x86_machine_class_init(ObjectClass *oc, void *data)
mc->cpu_index_to_instance_props = x86_cpu_index_to_props;
mc->get_default_cpu_node_id = x86_get_default_cpu_node_id;
mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids;
- x86mc->compat_apic_id_mode = false;
x86mc->save_tsc_khz = true;
x86mc->fwcfg_dma_enabled = true;
nc->nmi_monitor_handler = x86_nmi;
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
index 24bbde24c2..8bedbd13f1 100644
--- a/hw/ide/isa.c
+++ b/hw/ide/isa.c
@@ -75,7 +75,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp)
ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2);
ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2);
- isa_init_irq(isadev, &s->irq, s->isairq);
+ s->irq = isa_get_irq(isadev, s->isairq);
ide_init2(&s->bus, s->irq);
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s);
ide_register_restart_cb(&s->bus);
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index baba62f357..1773db0d25 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -26,6 +26,7 @@
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/timer.h"
+#include "qapi/error.h"
#include "hw/isa/isa.h"
#include "migration/vmstate.h"
#include "hw/acpi/aml-build.h"
@@ -671,6 +672,8 @@ struct ISAKBDState {
KBDState kbd;
bool kbd_throttle;
MemoryRegion io[2];
+ uint8_t kbd_irq;
+ uint8_t mouse_irq;
};
void i8042_isa_mouse_fake_event(ISAKBDState *isa)
@@ -734,8 +737,20 @@ static void i8042_realizefn(DeviceState *dev, Error **errp)
ISAKBDState *isa_s = I8042(dev);
KBDState *s = &isa_s->kbd;
- isa_init_irq(isadev, &s->irq_kbd, 1);
- isa_init_irq(isadev, &s->irq_mouse, 12);
+ if (isa_s->kbd_irq >= ISA_NUM_IRQS) {
+ error_setg(errp, "Maximum value for \"kbd-irq\" is: %u",
+ ISA_NUM_IRQS - 1);
+ return;
+ }
+
+ if (isa_s->mouse_irq >= ISA_NUM_IRQS) {
+ error_setg(errp, "Maximum value for \"mouse-irq\" is: %u",
+ ISA_NUM_IRQS - 1);
+ return;
+ }
+
+ s->irq_kbd = isa_get_irq(isadev, isa_s->kbd_irq);
+ s->irq_mouse = isa_get_irq(isadev, isa_s->mouse_irq);
isa_register_ioport(isadev, isa_s->io + 0, 0x60);
isa_register_ioport(isadev, isa_s->io + 1, 0x64);
@@ -754,6 +769,7 @@ static void i8042_realizefn(DeviceState *dev, Error **errp)
static void i8042_build_aml(ISADevice *isadev, Aml *scope)
{
+ ISAKBDState *isa_s = I8042(isadev);
Aml *kbd;
Aml *mou;
Aml *crs;
@@ -761,7 +777,7 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope)
crs = aml_resource_template();
aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01));
aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01));
- aml_append(crs, aml_irq_no_flags(1));
+ aml_append(crs, aml_irq_no_flags(isa_s->kbd_irq));
kbd = aml_device("KBD");
aml_append(kbd, aml_name_decl("_HID", aml_eisaid("PNP0303")));
@@ -769,7 +785,7 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope)
aml_append(kbd, aml_name_decl("_CRS", crs));
crs = aml_resource_template();
- aml_append(crs, aml_irq_no_flags(12));
+ aml_append(crs, aml_irq_no_flags(isa_s->mouse_irq));
mou = aml_device("MOU");
aml_append(mou, aml_name_decl("_HID", aml_eisaid("PNP0F13")));
@@ -783,6 +799,8 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope)
static Property i8042_properties[] = {
DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true),
DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false),
+ DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1),
+ DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
index 02625eb94e..88aa734e9e 100644
--- a/hw/ipmi/isa_ipmi_bt.c
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -92,7 +92,7 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
}
if (iib->isairq > 0) {
- isa_init_irq(isadev, &iib->irq, iib->isairq);
+ iib->irq = isa_get_irq(isadev, iib->isairq);
iib->bt.use_irq = 1;
iib->bt.raise_irq = isa_ipmi_bt_raise_irq;
iib->bt.lower_irq = isa_ipmi_bt_lower_irq;
diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c
index 3b23ad08b3..afabb95ebe 100644
--- a/hw/ipmi/isa_ipmi_kcs.c
+++ b/hw/ipmi/isa_ipmi_kcs.c
@@ -91,7 +91,7 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
}
if (iik->isairq > 0) {
- isa_init_irq(isadev, &iik->irq, iik->isairq);
+ iik->irq = isa_get_irq(isadev, iik->isairq);
iik->kcs.use_irq = 1;
iik->kcs.raise_irq = isa_ipmi_kcs_raise_irq;
iik->kcs.lower_irq = isa_ipmi_kcs_lower_irq;
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 6c31398dda..0ad1c5fd65 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -21,21 +21,18 @@
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qapi/error.h"
-#include "monitor/monitor.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
static ISABus *isabus;
-static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *isabus_get_fw_dev_path(DeviceState *dev);
static void isa_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
- k->print_dev = isabus_dev_print;
k->get_fw_dev_path = isabus_get_fw_dev_path;
}
@@ -88,19 +85,9 @@ qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq)
return isabus->irqs[isairq];
}
-void isa_init_irq(ISADevice *dev, qemu_irq *p, unsigned isairq)
-{
- assert(dev->nirqs < ARRAY_SIZE(dev->isairq));
- assert(isairq < ISA_NUM_IRQS);
- dev->isairq[dev->nirqs] = isairq;
- *p = isa_get_irq(dev, isairq);
- dev->nirqs++;
-}
-
void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq)
{
- qemu_irq irq;
- isa_init_irq(isadev, &irq, isairq);
+ qemu_irq irq = isa_get_irq(isadev, isairq);
qdev_connect_gpio_out(DEVICE(isadev), gpioirq, irq);
}
@@ -153,14 +140,6 @@ int isa_register_portio_list(ISADevice *dev,
return 0;
}
-static void isa_device_init(Object *obj)
-{
- ISADevice *dev = ISA_DEVICE(obj);
-
- dev->isairq[0] = -1;
- dev->isairq[1] = -1;
-}
-
ISADevice *isa_new(const char *name)
{
return ISA_DEVICE(qdev_new(name));
@@ -222,19 +201,6 @@ void isa_build_aml(ISABus *bus, Aml *scope)
}
}
-static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent)
-{
- ISADevice *d = ISA_DEVICE(dev);
-
- if (d->isairq[1] != -1) {
- monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "",
- d->isairq[0], d->isairq[1]);
- } else if (d->isairq[0] != -1) {
- monitor_printf(mon, "%*sisa irq %d\n", indent, "",
- d->isairq[0]);
- }
-}
-
static void isabus_bridge_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -260,7 +226,6 @@ static const TypeInfo isa_device_type_info = {
.name = TYPE_ISA_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(ISADevice),
- .instance_init = isa_device_init,
.abstract = true,
.class_size = sizeof(ISADeviceClass),
.class_init = isa_device_class_init,
diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c
index 0fe7b69bc4..8607e0ac36 100644
--- a/hw/isa/piix4.c
+++ b/hw/isa/piix4.c
@@ -39,8 +39,6 @@
#include "sysemu/runstate.h"
#include "qom/object.h"
-PCIDevice *piix4_dev;
-
struct PIIX4State {
PCIDevice dev;
qemu_irq cpu_intr;
@@ -54,6 +52,27 @@ struct PIIX4State {
OBJECT_DECLARE_SIMPLE_TYPE(PIIX4State, PIIX4_PCI_DEVICE)
+static void piix4_set_irq(void *opaque, int irq_num, int level)
+{
+ int i, pic_irq, pic_level;
+ PIIX4State *s = opaque;
+ PCIBus *bus = pci_get_bus(&s->dev);
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ /* XXX: optimize */
+ pic_irq = s->dev.config[PIIX_PIRQCA + irq_num];
+ if (pic_irq < ISA_NUM_IRQS) {
+ /* The pic level is the logical OR of all the PCI irqs mapped to it. */
+ pic_level = 0;
+ for (i = 0; i < PIIX_NUM_PIRQS; i++) {
+ if (pic_irq == s->dev.config[PIIX_PIRQCA + i]) {
+ pic_level |= pci_bus_get_irq_level(bus, i);
+ }
+ }
+ qemu_set_irq(s->isa[pic_irq], pic_level);
+ }
+}
+
static void piix4_isa_reset(DeviceState *dev)
{
PIIX4State *d = PIIX4_PCI_DEVICE(dev);
@@ -197,9 +216,7 @@ static void piix4_realize(PCIDevice *dev, Error **errp)
if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) {
return;
}
- isa_init_irq(ISA_DEVICE(&s->rtc), &s->rtc.irq, RTC_ISA_IRQ);
-
- piix4_dev = dev;
+ s->rtc.irq = isa_get_irq(ISA_DEVICE(&s->rtc), s->rtc.isairq);
}
static void piix4_init(Object *obj)
@@ -248,8 +265,34 @@ static void piix4_register_types(void)
type_init(piix4_register_types)
+static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot;
+
+ slot = PCI_SLOT(pci_dev->devfn);
+
+ switch (slot) {
+ /* PIIX4 USB */
+ case 10:
+ return 3;
+ /* AMD 79C973 Ethernet */
+ case 11:
+ return 1;
+ /* Crystal 4281 Sound */
+ case 12:
+ return 2;
+ /* PCI slot 1 to 4 */
+ case 18 ... 21:
+ return ((slot - 18) + irq_num) & 0x03;
+ /* Unknown device, don't do any translation */
+ default:
+ return irq_num;
+ }
+}
+
DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus)
{
+ PIIX4State *s;
PCIDevice *pci;
DeviceState *dev;
int devfn = PCI_DEVFN(10, 0);
@@ -257,6 +300,7 @@ DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus)
pci = pci_create_simple_multifunction(pci_bus, devfn, true,
TYPE_PIIX4_PCI_DEVICE);
dev = DEVICE(pci);
+ s = PIIX4_PCI_DEVICE(pci);
if (isa_bus) {
*isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
}
@@ -271,5 +315,7 @@ DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus)
NULL, 0, NULL);
}
+ pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS);
+
return dev;
}
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
index c7480bd019..e0ff1b5566 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -26,10 +26,8 @@
#include "qapi/error.h"
#include "qemu/units.h"
#include "qemu/log.h"
-#include "hw/mips/mips.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
-#include "hw/southbridge/piix.h"
#include "migration/vmstate.h"
#include "hw/intc/i8259.h"
#include "hw/irq.h"
@@ -981,56 +979,6 @@ static const MemoryRegionOps isd_mem_ops = {
},
};
-static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
-{
- int slot;
-
- slot = PCI_SLOT(pci_dev->devfn);
-
- switch (slot) {
- /* PIIX4 USB */
- case 10:
- return 3;
- /* AMD 79C973 Ethernet */
- case 11:
- return 1;
- /* Crystal 4281 Sound */
- case 12:
- return 2;
- /* PCI slot 1 to 4 */
- case 18 ... 21:
- return ((slot - 18) + irq_num) & 0x03;
- /* Unknown device, don't do any translation */
- default:
- return irq_num;
- }
-}
-
-static int pci_irq_levels[4];
-
-static void gt64120_pci_set_irq(void *opaque, int irq_num, int level)
-{
- int i, pic_irq, pic_level;
- qemu_irq *pic = opaque;
-
- pci_irq_levels[irq_num] = level;
-
- /* now we change the pic irq level according to the piix irq mappings */
- /* XXX: optimize */
- pic_irq = piix4_dev->config[PIIX_PIRQCA + irq_num];
- if (pic_irq < 16) {
- /* The pic level is the logical OR of all the PCI irqs mapped to it. */
- pic_level = 0;
- for (i = 0; i < 4; i++) {
- if (pic_irq == piix4_dev->config[PIIX_PIRQCA + i]) {
- pic_level |= pci_irq_levels[i];
- }
- }
- qemu_set_irq(pic[pic_irq], pic_level);
- }
-}
-
-
static void gt64120_reset(DeviceState *dev)
{
GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev);
@@ -1202,32 +1150,18 @@ static void gt64120_reset(DeviceState *dev)
static void gt64120_realize(DeviceState *dev, Error **errp)
{
GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev);
+ PCIHostState *phb = PCI_HOST_BRIDGE(dev);
memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s,
"gt64120-isd", 0x1000);
-}
-
-PCIBus *gt64120_register(qemu_irq *pic)
-{
- GT64120State *d;
- PCIHostState *phb;
- DeviceState *dev;
-
- dev = qdev_new(TYPE_GT64120_PCI_HOST_BRIDGE);
- d = GT64120_PCI_HOST_BRIDGE(dev);
- phb = PCI_HOST_BRIDGE(dev);
- memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB);
- address_space_init(&d->pci0_mem_as, &d->pci0_mem, "pci0-mem");
- phb->bus = pci_register_root_bus(dev, "pci",
- gt64120_pci_set_irq, gt64120_pci_map_irq,
- pic,
- &d->pci0_mem,
- get_system_io(),
- PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS);
- sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ memory_region_init(&s->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB);
+ address_space_init(&s->pci0_mem_as, &s->pci0_mem, "pci0-mem");
+ phb->bus = pci_root_bus_new(dev, "pci",
+ &s->pci0_mem,
+ get_system_io(),
+ PCI_DEVFN(18, 0), TYPE_PCI_BUS);
pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci");
- return phb->bus;
}
static void gt64120_pci_realize(PCIDevice *d, Error **errp)
diff --git a/hw/mips/malta.c b/hw/mips/malta.c
index b770b8d367..55037eb90f 100644
--- a/hw/mips/malta.c
+++ b/hw/mips/malta.c
@@ -97,7 +97,6 @@ struct MaltaState {
Clock *cpuclk;
MIPSCPSState cps;
- qemu_irq i8259[ISA_NUM_IRQS];
};
static struct _loaderparams {
@@ -1391,7 +1390,8 @@ void mips_malta_init(MachineState *machine)
stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420);
/* Northbridge */
- pci_bus = gt64120_register(s->i8259);
+ dev = sysbus_create_simple("gt64120", -1, NULL);
+ pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci"));
/*
* The whole address space decoded by the GT-64120A doesn't generate
* exception when accessing invalid memory. Create an empty slot to
@@ -1404,9 +1404,6 @@ void mips_malta_init(MachineState *machine)
/* Interrupt controller */
qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq);
- for (int i = 0; i < ISA_NUM_IRQS; i++) {
- s->i8259[i] = qdev_get_gpio_in_named(dev, "isa", i);
- }
/* generate SPD EEPROM data */
generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c
index a39fcdd1fc..b84d4d458d 100644
--- a/hw/misc/pvpanic-isa.c
+++ b/hw/misc/pvpanic-isa.c
@@ -21,6 +21,7 @@
#include "hw/misc/pvpanic.h"
#include "qom/object.h"
#include "hw/isa/isa.h"
+#include "standard-headers/linux/pvpanic.h"
OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE)
@@ -64,7 +65,8 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp)
static Property pvpanic_isa_properties[] = {
DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505),
- DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED),
+ DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events,
+ PVPANIC_PANICKED | PVPANIC_CRASH_LOADED),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c
index 62e1be68c1..99cf7e2041 100644
--- a/hw/misc/pvpanic-pci.c
+++ b/hw/misc/pvpanic-pci.c
@@ -21,6 +21,7 @@
#include "hw/misc/pvpanic.h"
#include "qom/object.h"
#include "hw/pci/pci.h"
+#include "standard-headers/linux/pvpanic.h"
OBJECT_DECLARE_SIMPLE_TYPE(PVPanicPCIState, PVPANIC_PCI_DEVICE)
@@ -53,7 +54,8 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp)
}
static Property pvpanic_pci_properties[] = {
- DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED),
+ DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events,
+ PVPANIC_PANICKED | PVPANIC_CRASH_LOADED),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c
index e2cb4a5d28..1540e9091a 100644
--- a/hw/misc/pvpanic.c
+++ b/hw/misc/pvpanic.c
@@ -21,12 +21,13 @@
#include "hw/qdev-properties.h"
#include "hw/misc/pvpanic.h"
#include "qom/object.h"
+#include "standard-headers/linux/pvpanic.h"
static void handle_event(int event)
{
static bool logged;
- if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) && !logged) {
+ if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) && !logged) {
qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event);
logged = true;
}
@@ -36,7 +37,7 @@ static void handle_event(int event)
return;
}
- if (event & PVPANIC_CRASHLOADED) {
+ if (event & PVPANIC_CRASH_LOADED) {
qemu_system_guest_crashloaded(NULL);
return;
}
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index dd6f6e34d3..6ced6775ff 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -68,7 +68,7 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp)
ne2000_setup_io(s, DEVICE(isadev), 0x20);
isa_register_ioport(isadev, &s->io, isa->iobase);
- isa_init_irq(isadev, &s->irq, isa->isairq);
+ s->irq = isa_get_irq(isadev, isa->isairq);
qemu_macaddr_default_if_unset(&s->c.macaddr);
ne2000_reset(s);
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index cf8ab0f8af..b02a0632df 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -628,17 +628,20 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n)
NetClientState *peer = n->nic_conf.peers.ncs[0];
/*
- * Backends other than vhost-user don't support max queue size.
+ * Backends other than vhost-user or vhost-vdpa don't support max queue
+ * size.
*/
if (!peer) {
return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
}
- if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
+ switch(peer->info->type) {
+ case NET_CLIENT_DRIVER_VHOST_USER:
+ case NET_CLIENT_DRIVER_VHOST_VDPA:
+ return VIRTQUEUE_MAX_SIZE;
+ default:
return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
- }
-
- return VIRTQUEUE_MAX_SIZE;
+ };
}
static int peer_attach(VirtIONet *n, int index)
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
index 10e6e7c2ab..de932286b5 100644
--- a/hw/pci-bridge/pci_expander_bridge.c
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -193,6 +193,12 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
PCIDevice *pxb = pci_get_bus(pci_dev)->parent_dev;
/*
+ * First carry out normal swizzle to handle
+ * multple root ports on a pxb instance.
+ */
+ pin = pci_swizzle_map_irq_fn(pci_dev, pin);
+
+ /*
* The bios does not index the pxb slot number when
* it computes the IRQ because it resides on bus 0
* and not on the current bus.
diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
index 04aae72cd6..05e2b06c0c 100644
--- a/hw/pci-bridge/xio3130_downstream.c
+++ b/hw/pci-bridge/xio3130_downstream.c
@@ -28,6 +28,7 @@
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/module.h"
+#include "hw/pci-bridge/xio3130_downstream.h"
#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
#define XIO3130_REVISION 0x1
@@ -84,7 +85,7 @@ static void xio3130_downstream_realize(PCIDevice *d, Error **errp)
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
errp);
if (rc < 0) {
- goto err_bridge;
+ goto err_msi;
}
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
@@ -173,7 +174,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo xio3130_downstream_info = {
- .name = "xio3130-downstream",
+ .name = TYPE_XIO3130_DOWNSTREAM,
.parent = TYPE_PCIE_SLOT,
.class_init = xio3130_downstream_class_init,
.interfaces = (InterfaceInfo[]) {
diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
index 5cd3af4fbc..5ff46ef050 100644
--- a/hw/pci-bridge/xio3130_upstream.c
+++ b/hw/pci-bridge/xio3130_upstream.c
@@ -75,7 +75,7 @@ static void xio3130_upstream_realize(PCIDevice *d, Error **errp)
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
errp);
if (rc < 0) {
- goto err_bridge;
+ goto err_msi;
}
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
diff --git a/hw/pci/meson.build b/hw/pci/meson.build
index 5c4bbac817..bcc9c75919 100644
--- a/hw/pci/meson.build
+++ b/hw/pci/meson.build
@@ -5,6 +5,7 @@ pci_ss.add(files(
'pci.c',
'pci_bridge.c',
'pci_host.c',
+ 'pcie_sriov.c',
'shpc.c',
'slotid_cap.c'
))
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 5d30f9ca60..5cb1232e27 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -239,6 +239,9 @@ int pci_bar(PCIDevice *d, int reg)
{
uint8_t type;
+ /* PCIe virtual functions do not have their own BARs */
+ assert(!pci_is_vf(d));
+
if (reg != PCI_ROM_SLOT)
return PCI_BASE_ADDRESS_0 + reg * 4;
@@ -304,10 +307,30 @@ void pci_device_deassert_intx(PCIDevice *dev)
}
}
-static void pci_do_device_reset(PCIDevice *dev)
+static void pci_reset_regions(PCIDevice *dev)
{
int r;
+ if (pci_is_vf(dev)) {
+ return;
+ }
+
+ for (r = 0; r < PCI_NUM_REGIONS; ++r) {
+ PCIIORegion *region = &dev->io_regions[r];
+ if (!region->size) {
+ continue;
+ }
+ if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) &&
+ region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_set_quad(dev->config + pci_bar(dev, r), region->type);
+ } else {
+ pci_set_long(dev->config + pci_bar(dev, r), region->type);
+ }
+ }
+}
+
+static void pci_do_device_reset(PCIDevice *dev)
+{
pci_device_deassert_intx(dev);
assert(dev->irq_state == 0);
@@ -323,19 +346,7 @@ static void pci_do_device_reset(PCIDevice *dev)
pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) |
pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE));
dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
- for (r = 0; r < PCI_NUM_REGIONS; ++r) {
- PCIIORegion *region = &dev->io_regions[r];
- if (!region->size) {
- continue;
- }
-
- if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) &&
- region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
- pci_set_quad(dev->config + pci_bar(dev, r), region->type);
- } else {
- pci_set_long(dev->config + pci_bar(dev, r), region->type);
- }
- }
+ pci_reset_regions(dev);
pci_update_mappings(dev);
msi_reset(dev);
@@ -885,6 +896,16 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp)
}
/*
+ * With SR/IOV and ARI, a device at function 0 need not be a multifunction
+ * device, as it may just be a VF that ended up with function 0 in
+ * the legacy PCI interpretation. Avoid failing in such cases:
+ */
+ if (pci_is_vf(dev) &&
+ dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+ return;
+ }
+
+ /*
* multifunction bit is interpreted in two ways as follows.
* - all functions must set the bit to 1.
* Example: Intel X53
@@ -1078,11 +1099,12 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev,
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",
+ " in use by %s,id=%s",
PCI_SLOT(devfn), PCI_FUNC(devfn), name,
- bus->devices[devfn]->name);
+ bus->devices[devfn]->name, bus->devices[devfn]->qdev.id);
return NULL;
} else if (dev->hotplugged &&
+ !pci_is_vf(pci_dev) &&
pci_get_function_0(pci_dev)) {
error_setg(errp, "PCI: slot %d function 0 already occupied by %s,"
" new func %s cannot be exposed to guest.",
@@ -1191,6 +1213,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
pcibus_t size = memory_region_size(memory);
uint8_t hdr_type;
+ assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */
assert(region_num >= 0);
assert(region_num < PCI_NUM_REGIONS);
assert(is_power_of_2(size));
@@ -1294,11 +1317,45 @@ pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num)
return pci_dev->io_regions[region_num].addr;
}
-static pcibus_t pci_bar_address(PCIDevice *d,
- int reg, uint8_t type, pcibus_t size)
+static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg,
+ uint8_t type, pcibus_t size)
+{
+ pcibus_t new_addr;
+ if (!pci_is_vf(d)) {
+ int bar = pci_bar(d, reg);
+ if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ new_addr = pci_get_quad(d->config + bar);
+ } else {
+ new_addr = pci_get_long(d->config + bar);
+ }
+ } else {
+ PCIDevice *pf = d->exp.sriov_vf.pf;
+ uint16_t sriov_cap = pf->exp.sriov_cap;
+ int bar = sriov_cap + PCI_SRIOV_BAR + reg * 4;
+ uint16_t vf_offset =
+ pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET);
+ uint16_t vf_stride =
+ pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE);
+ uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride;
+
+ if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ new_addr = pci_get_quad(pf->config + bar);
+ } else {
+ new_addr = pci_get_long(pf->config + bar);
+ }
+ new_addr += vf_num * size;
+ }
+ /* The ROM slot has a specific enable bit, keep it intact */
+ if (reg != PCI_ROM_SLOT) {
+ new_addr &= ~(size - 1);
+ }
+ return new_addr;
+}
+
+pcibus_t pci_bar_address(PCIDevice *d,
+ int reg, uint8_t type, pcibus_t size)
{
pcibus_t new_addr, last_addr;
- int bar = pci_bar(d, reg);
uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
Object *machine = qdev_get_machine();
ObjectClass *oc = object_get_class(machine);
@@ -1309,7 +1366,7 @@ static pcibus_t pci_bar_address(PCIDevice *d,
if (!(cmd & PCI_COMMAND_IO)) {
return PCI_BAR_UNMAPPED;
}
- new_addr = pci_get_long(d->config + bar) & ~(size - 1);
+ new_addr = pci_config_get_bar_addr(d, reg, type, size);
last_addr = new_addr + size - 1;
/* Check if 32 bit BAR wraps around explicitly.
* TODO: make priorities correct and remove this work around.
@@ -1324,11 +1381,7 @@ static pcibus_t pci_bar_address(PCIDevice *d,
if (!(cmd & PCI_COMMAND_MEMORY)) {
return PCI_BAR_UNMAPPED;
}
- if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
- new_addr = pci_get_quad(d->config + bar);
- } else {
- new_addr = pci_get_long(d->config + bar);
- }
+ new_addr = pci_config_get_bar_addr(d, reg, type, size);
/* the ROM slot has a specific enable bit */
if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) {
return PCI_BAR_UNMAPPED;
@@ -1473,6 +1526,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int
msi_write_config(d, addr, val_in, l);
msix_write_config(d, addr, val_in, l);
+ pcie_sriov_config_write(d, addr, val_in, l);
}
/***********************************************************/
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index d7d73a31e4..67a5d67372 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -366,6 +366,17 @@ static void hotplug_event_clear(PCIDevice *dev)
}
}
+void pcie_cap_slot_enable_power(PCIDevice *dev)
+{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ uint32_t sltcap = pci_get_long(exp_cap + PCI_EXP_SLTCAP);
+
+ if (sltcap & PCI_EXP_SLTCAP_PCP) {
+ pci_set_word_by_mask(exp_cap + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_ON);
+ }
+}
+
static void pcie_set_power_device(PCIBus *bus, PCIDevice *dev, void *opaque)
{
bool *power = opaque;
@@ -446,6 +457,11 @@ void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
PCIDevice *pci_dev = PCI_DEVICE(dev);
uint32_t lnkcap = pci_get_long(exp_cap + PCI_EXP_LNKCAP);
+ if (pci_is_vf(pci_dev)) {
+ /* Virtual function cannot be physically disconnected */
+ return;
+ }
+
/* Don't send event when device is enabled during qemu machine creation:
* it is present on boot, no hotplug event is necessary. We do send an
* event when the device is disabled later. */
diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
new file mode 100644
index 0000000000..87abad6ac8
--- /dev/null
+++ b/hw/pci/pcie_sriov.c
@@ -0,0 +1,302 @@
+/*
+ * pcie_sriov.c:
+ *
+ * Implementation of SR/IOV emulation support.
+ *
+ * Copyright (c) 2015-2017 Knut Omang <knut.omang@oracle.com>
+ *
+ * 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 "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/qdev-properties.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+static PCIDevice *register_vf(PCIDevice *pf, int devfn,
+ const char *name, uint16_t vf_num);
+static void unregister_vfs(PCIDevice *dev);
+
+void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
+ const char *vfname, uint16_t vf_dev_id,
+ uint16_t init_vfs, uint16_t total_vfs,
+ uint16_t vf_offset, uint16_t vf_stride)
+{
+ uint8_t *cfg = dev->config + offset;
+ uint8_t *wmask;
+
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1,
+ offset, PCI_EXT_CAP_SRIOV_SIZEOF);
+ dev->exp.sriov_cap = offset;
+ dev->exp.sriov_pf.num_vfs = 0;
+ dev->exp.sriov_pf.vfname = g_strdup(vfname);
+ dev->exp.sriov_pf.vf = NULL;
+
+ pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset);
+ pci_set_word(cfg + PCI_SRIOV_VF_STRIDE, vf_stride);
+
+ /*
+ * Mandatory page sizes to support.
+ * Device implementations can call pcie_sriov_pf_add_sup_pgsize()
+ * to set more bits:
+ */
+ pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, SRIOV_SUP_PGSIZE_MINREQ);
+
+ /*
+ * Default is to use 4K pages, software can modify it
+ * to any of the supported bits
+ */
+ pci_set_word(cfg + PCI_SRIOV_SYS_PGSIZE, 0x1);
+
+ /* Set up device ID and initial/total number of VFs available */
+ pci_set_word(cfg + PCI_SRIOV_VF_DID, vf_dev_id);
+ pci_set_word(cfg + PCI_SRIOV_INITIAL_VF, init_vfs);
+ pci_set_word(cfg + PCI_SRIOV_TOTAL_VF, total_vfs);
+ pci_set_word(cfg + PCI_SRIOV_NUM_VF, 0);
+
+ /* Write enable control bits */
+ wmask = dev->wmask + offset;
+ pci_set_word(wmask + PCI_SRIOV_CTRL,
+ PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI);
+ pci_set_word(wmask + PCI_SRIOV_NUM_VF, 0xffff);
+ pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553);
+
+ qdev_prop_set_bit(&dev->qdev, "multifunction", true);
+}
+
+void pcie_sriov_pf_exit(PCIDevice *dev)
+{
+ unregister_vfs(dev);
+ g_free((char *)dev->exp.sriov_pf.vfname);
+ dev->exp.sriov_pf.vfname = NULL;
+}
+
+void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
+ uint8_t type, dma_addr_t size)
+{
+ uint32_t addr;
+ uint64_t wmask;
+ uint16_t sriov_cap = dev->exp.sriov_cap;
+
+ assert(sriov_cap > 0);
+ assert(region_num >= 0);
+ assert(region_num < PCI_NUM_REGIONS);
+ assert(region_num != PCI_ROM_SLOT);
+
+ wmask = ~(size - 1);
+ addr = sriov_cap + PCI_SRIOV_BAR + region_num * 4;
+
+ pci_set_long(dev->config + addr, type);
+ if (!(type & PCI_BASE_ADDRESS_SPACE_IO) &&
+ type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_set_quad(dev->wmask + addr, wmask);
+ pci_set_quad(dev->cmask + addr, ~0ULL);
+ } else {
+ pci_set_long(dev->wmask + addr, wmask & 0xffffffff);
+ pci_set_long(dev->cmask + addr, 0xffffffff);
+ }
+ dev->exp.sriov_pf.vf_bar_type[region_num] = type;
+}
+
+void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
+ MemoryRegion *memory)
+{
+ PCIIORegion *r;
+ PCIBus *bus = pci_get_bus(dev);
+ uint8_t type;
+ pcibus_t size = memory_region_size(memory);
+
+ assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */
+ assert(region_num >= 0);
+ assert(region_num < PCI_NUM_REGIONS);
+ type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num];
+
+ if (!is_power_of_2(size)) {
+ error_report("%s: PCI region size must be a power"
+ " of two - type=0x%x, size=0x%"FMT_PCIBUS,
+ __func__, type, size);
+ exit(1);
+ }
+
+ r = &dev->io_regions[region_num];
+ r->memory = memory;
+ r->address_space =
+ type & PCI_BASE_ADDRESS_SPACE_IO
+ ? bus->address_space_io
+ : bus->address_space_mem;
+ r->size = size;
+ r->type = type;
+
+ r->addr = pci_bar_address(dev, region_num, r->type, r->size);
+ if (r->addr != PCI_BAR_UNMAPPED) {
+ memory_region_add_subregion_overlap(r->address_space,
+ r->addr, r->memory, 1);
+ }
+}
+
+static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name,
+ uint16_t vf_num)
+{
+ PCIDevice *dev = pci_new(devfn, name);
+ dev->exp.sriov_vf.pf = pf;
+ dev->exp.sriov_vf.vf_number = vf_num;
+ PCIBus *bus = pci_get_bus(pf);
+ Error *local_err = NULL;
+
+ qdev_realize(&dev->qdev, &bus->qbus, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return NULL;
+ }
+
+ /* set vid/did according to sr/iov spec - they are not used */
+ pci_config_set_vendor_id(dev->config, 0xffff);
+ pci_config_set_device_id(dev->config, 0xffff);
+
+ return dev;
+}
+
+static void register_vfs(PCIDevice *dev)
+{
+ uint16_t num_vfs;
+ uint16_t i;
+ uint16_t sriov_cap = dev->exp.sriov_cap;
+ uint16_t vf_offset =
+ pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET);
+ uint16_t vf_stride =
+ pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE);
+ int32_t devfn = dev->devfn + vf_offset;
+
+ assert(sriov_cap > 0);
+ num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
+
+ dev->exp.sriov_pf.vf = g_malloc(sizeof(PCIDevice *) * num_vfs);
+ assert(dev->exp.sriov_pf.vf);
+
+ trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), num_vfs);
+ for (i = 0; i < num_vfs; i++) {
+ dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn,
+ dev->exp.sriov_pf.vfname, i);
+ if (!dev->exp.sriov_pf.vf[i]) {
+ num_vfs = i;
+ break;
+ }
+ devfn += vf_stride;
+ }
+ dev->exp.sriov_pf.num_vfs = num_vfs;
+}
+
+static void unregister_vfs(PCIDevice *dev)
+{
+ Error *local_err = NULL;
+ uint16_t num_vfs = dev->exp.sriov_pf.num_vfs;
+ uint16_t i;
+
+ trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), num_vfs);
+ for (i = 0; i < num_vfs; i++) {
+ PCIDevice *vf = dev->exp.sriov_pf.vf[i];
+ object_property_set_bool(OBJECT(vf), "realized", false, &local_err);
+ if (local_err) {
+ fprintf(stderr, "Failed to unplug: %s\n",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ }
+ object_unparent(OBJECT(vf));
+ }
+ g_free(dev->exp.sriov_pf.vf);
+ dev->exp.sriov_pf.vf = NULL;
+ dev->exp.sriov_pf.num_vfs = 0;
+ pci_set_word(dev->config + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0);
+}
+
+void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
+ uint32_t val, int len)
+{
+ uint32_t off;
+ uint16_t sriov_cap = dev->exp.sriov_cap;
+
+ if (!sriov_cap || address < sriov_cap) {
+ return;
+ }
+ off = address - sriov_cap;
+ if (off >= PCI_EXT_CAP_SRIOV_SIZEOF) {
+ return;
+ }
+
+ trace_sriov_config_write(dev->name, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), off, val, len);
+
+ if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) {
+ if (dev->exp.sriov_pf.num_vfs) {
+ if (!(val & PCI_SRIOV_CTRL_VFE)) {
+ unregister_vfs(dev);
+ }
+ } else {
+ if (val & PCI_SRIOV_CTRL_VFE) {
+ register_vfs(dev);
+ }
+ }
+ }
+}
+
+
+/* Reset SR/IOV VF Enable bit to trigger an unregister of all VFs */
+void pcie_sriov_pf_disable_vfs(PCIDevice *dev)
+{
+ uint16_t sriov_cap = dev->exp.sriov_cap;
+ if (sriov_cap) {
+ uint32_t val = pci_get_byte(dev->config + sriov_cap + PCI_SRIOV_CTRL);
+ if (val & PCI_SRIOV_CTRL_VFE) {
+ val &= ~PCI_SRIOV_CTRL_VFE;
+ pcie_sriov_config_write(dev, sriov_cap + PCI_SRIOV_CTRL, val, 1);
+ }
+ }
+}
+
+/* Add optional supported page sizes to the mask of supported page sizes */
+void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize)
+{
+ uint8_t *cfg = dev->config + dev->exp.sriov_cap;
+ uint8_t *wmask = dev->wmask + dev->exp.sriov_cap;
+
+ uint16_t sup_pgsize = pci_get_word(cfg + PCI_SRIOV_SUP_PGSIZE);
+
+ sup_pgsize |= opt_sup_pgsize;
+
+ /*
+ * Make sure the new bits are set, and that system page size
+ * also can be set to any of the new values according to spec:
+ */
+ pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, sup_pgsize);
+ pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, sup_pgsize);
+}
+
+
+uint16_t pcie_sriov_vf_number(PCIDevice *dev)
+{
+ assert(pci_is_vf(dev));
+ return dev->exp.sriov_vf.vf_number;
+}
+
+PCIDevice *pcie_sriov_get_pf(PCIDevice *dev)
+{
+ return dev->exp.sriov_vf.pf;
+}
+
+PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n)
+{
+ assert(!pci_is_vf(dev));
+ if (n < dev->exp.sriov_pf.num_vfs) {
+ return dev->exp.sriov_pf.vf[n];
+ }
+ return NULL;
+}
diff --git a/hw/pci/trace-events b/hw/pci/trace-events
index 7570752c40..aaf46bc92d 100644
--- a/hw/pci/trace-events
+++ b/hw/pci/trace-events
@@ -10,3 +10,8 @@ pci_cfg_write(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, unsig
# msix.c
msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d masked %d"
+
+# hw/pci/pcie_sriov.c
+sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs"
+sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs"
+sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d"
diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c
index dc21fb10a5..e61f7ec370 100644
--- a/hw/rtc/m48t59-isa.c
+++ b/hw/rtc/m48t59-isa.c
@@ -42,6 +42,7 @@ struct M48txxISAState {
ISADevice parent_obj;
M48t59State state;
uint32_t io_base;
+ uint8_t isairq;
MemoryRegion io;
};
@@ -79,6 +80,7 @@ static void m48txx_isa_toggle_lock(Nvram *obj, int lock)
static Property m48t59_isa_properties[] = {
DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0),
DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74),
+ DEFINE_PROP_UINT8("irq", M48txxISAState, isairq, 8),
DEFINE_PROP_END_OF_LIST(),
};
@@ -97,9 +99,14 @@ static void m48t59_isa_realize(DeviceState *dev, Error **errp)
M48txxISAState *d = M48TXX_ISA(dev);
M48t59State *s = &d->state;
+ if (d->isairq >= ISA_NUM_IRQS) {
+ error_setg(errp, "Maximum value for \"irq\" is: %u", ISA_NUM_IRQS - 1);
+ return;
+ }
+
s->model = u->info.model;
s->size = u->info.size;
- isa_init_irq(isadev, &s->IRQ, 8);
+ s->IRQ = isa_get_irq(isadev, d->isairq);
m48t59_realize_common(s, errp);
memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4);
if (d->io_base != 0) {
diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
index ac9a60c90e..f235c2ddbe 100644
--- a/hw/rtc/mc146818rtc.c
+++ b/hw/rtc/mc146818rtc.c
@@ -912,6 +912,11 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
s->base_year = 0;
}
+ if (s->isairq >= ISA_NUM_IRQS) {
+ error_setg(errp, "Maximum value for \"irq\" is: %u", ISA_NUM_IRQS - 1);
+ return;
+ }
+
rtc_set_date_from_host(isadev);
switch (s->lost_tick_policy) {
@@ -957,15 +962,17 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
{
DeviceState *dev;
ISADevice *isadev;
+ RTCState *s;
isadev = isa_new(TYPE_MC146818_RTC);
dev = DEVICE(isadev);
+ s = MC146818_RTC(isadev);
qdev_prop_set_int32(dev, "base_year", base_year);
isa_realize_and_unref(isadev, bus, &error_fatal);
if (intercept_irq) {
qdev_connect_gpio_out(dev, 0, intercept_irq);
} else {
- isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ);
+ isa_connect_gpio_out(isadev, 0, s->isairq);
}
object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev),
@@ -976,6 +983,7 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
static Property mc146818rtc_properties[] = {
DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980),
+ DEFINE_PROP_UINT8("irq", RTCState, isairq, RTC_ISA_IRQ),
DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState,
lost_tick_policy, LOST_TICK_POLICY_DISCARD),
DEFINE_PROP_END_OF_LIST(),
@@ -1011,6 +1019,7 @@ static void rtc_reset_hold(Object *obj)
static void rtc_build_aml(ISADevice *isadev, Aml *scope)
{
+ RTCState *s = MC146818_RTC(isadev);
Aml *dev;
Aml *crs;
@@ -1021,7 +1030,7 @@ static void rtc_build_aml(ISADevice *isadev, Aml *scope)
crs = aml_resource_template();
aml_append(crs, aml_io(AML_DECODE16, RTC_ISA_BASE, RTC_ISA_BASE,
0x01, 0x08));
- aml_append(crs, aml_irq_no_flags(RTC_ISA_IRQ));
+ aml_append(crs, aml_irq_no_flags(s->isairq));
dev = aml_device("RTC");
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0B00")));
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 6013df1698..60349ee402 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -104,9 +104,11 @@ static struct {
const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part;
uint64_t max_speed;
uint64_t current_speed;
+ uint64_t processor_id;
} type4 = {
.max_speed = DEFAULT_CPU_SPEED,
- .current_speed = DEFAULT_CPU_SPEED
+ .current_speed = DEFAULT_CPU_SPEED,
+ .processor_id = 0,
};
static struct {
@@ -327,6 +329,10 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = {
.name = "part",
.type = QEMU_OPT_STRING,
.help = "part number",
+ }, {
+ .name = "processor-id",
+ .type = QEMU_OPT_NUMBER,
+ .help = "processor id",
},
{ /* end of list */ }
};
@@ -549,9 +555,23 @@ bool smbios_skip_table(uint8_t type, bool required_table)
return true;
}
+#define T0_BASE 0x000
+#define T1_BASE 0x100
+#define T2_BASE 0x200
+#define T3_BASE 0x300
+#define T4_BASE 0x400
+#define T11_BASE 0xe00
+
+#define T16_BASE 0x1000
+#define T17_BASE 0x1100
+#define T19_BASE 0x1300
+#define T32_BASE 0x2000
+#define T41_BASE 0x2900
+#define T127_BASE 0x7F00
+
static void smbios_build_type_0_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */
+ SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */
SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor);
SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version);
@@ -599,7 +619,7 @@ static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in)
static void smbios_build_type_1_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */
SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer);
SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product);
@@ -619,7 +639,7 @@ static void smbios_build_type_1_table(void)
static void smbios_build_type_2_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */
+ SMBIOS_BUILD_TABLE_PRE(2, T2_BASE, false); /* optional */
SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer);
SMBIOS_TABLE_SET_STR(2, product_str, type2.product);
@@ -637,7 +657,7 @@ static void smbios_build_type_2_table(void)
static void smbios_build_type_3_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */
SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer);
t->type = 0x01; /* Other */
@@ -662,15 +682,20 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
{
char sock_str[128];
- SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(4, T4_BASE + instance, true); /* required */
snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance);
SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str);
t->processor_type = 0x03; /* CPU */
t->processor_family = 0x01; /* Other */
SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer);
- t->processor_id[0] = cpu_to_le32(smbios_cpuid_version);
- t->processor_id[1] = cpu_to_le32(smbios_cpuid_features);
+ if (type4.processor_id == 0) {
+ t->processor_id[0] = cpu_to_le32(smbios_cpuid_version);
+ t->processor_id[1] = cpu_to_le32(smbios_cpuid_features);
+ } else {
+ t->processor_id[0] = cpu_to_le32((uint32_t)type4.processor_id);
+ t->processor_id[1] = cpu_to_le32(type4.processor_id >> 32);
+ }
SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version);
t->voltage = 0;
t->external_clock = cpu_to_le16(0); /* Unknown */
@@ -702,7 +727,7 @@ static void smbios_build_type_11_table(void)
return;
}
- SMBIOS_BUILD_TABLE_PRE(11, 0xe00, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(11, T11_BASE, true); /* required */
snprintf(count_str, sizeof(count_str), "%zu", type11.nvalues);
t->count = type11.nvalues;
@@ -722,7 +747,7 @@ static void smbios_build_type_16_table(unsigned dimm_cnt)
{
uint64_t size_kb;
- SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(16, T16_BASE, true); /* required */
t->location = 0x01; /* Other */
t->use = 0x03; /* System memory */
@@ -749,7 +774,7 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size)
char loc_str[128];
uint64_t size_mb;
- SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(17, T17_BASE + instance, true); /* required */
t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */
t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */
@@ -785,12 +810,13 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size)
SMBIOS_BUILD_TABLE_POST;
}
-static void smbios_build_type_19_table(unsigned instance,
+static void smbios_build_type_19_table(unsigned instance, unsigned offset,
uint64_t start, uint64_t size)
{
uint64_t end, start_kb, end_kb;
- SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(19, T19_BASE + offset + instance,
+ true); /* required */
end = start + size - 1;
assert(end > start);
@@ -814,7 +840,7 @@ static void smbios_build_type_19_table(unsigned instance,
static void smbios_build_type_32_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(32, T32_BASE, true); /* required */
memset(t->reserved, 0, 6);
t->boot_status = 0; /* No errors detected */
@@ -828,7 +854,7 @@ static void smbios_build_type_41_table(Error **errp)
struct type41_instance *t41;
QTAILQ_FOREACH(t41, &type41, next) {
- SMBIOS_BUILD_TABLE_PRE(41, 0x2900 + instance, true);
+ SMBIOS_BUILD_TABLE_PRE(41, T41_BASE + instance, true);
SMBIOS_TABLE_SET_STR(41, reference_designation_str, t41->designation);
t->device_type = t41->kind;
@@ -871,7 +897,7 @@ static void smbios_build_type_41_table(Error **errp)
static void smbios_build_type_127_table(void)
{
- SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */
+ SMBIOS_BUILD_TABLE_PRE(127, T127_BASE, true); /* required */
SMBIOS_BUILD_TABLE_POST;
}
@@ -982,7 +1008,7 @@ void smbios_get_tables(MachineState *ms,
uint8_t **anchor, size_t *anchor_len,
Error **errp)
{
- unsigned i, dimm_cnt;
+ unsigned i, dimm_cnt, offset;
if (smbios_legacy) {
*tables = *anchor = NULL;
@@ -1012,6 +1038,16 @@ void smbios_get_tables(MachineState *ms,
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ;
+ /*
+ * The offset determines if we need to keep additional space betweeen
+ * table 17 and table 19 header handle numbers so that they do
+ * not overlap. For example, for a VM with larger than 8 TB guest
+ * memory and DIMM like chunks of 16 GiB, the default space between
+ * the two tables (T19_BASE - T17_BASE = 512) is not enough.
+ */
+ offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
+ dimm_cnt - (T19_BASE - T17_BASE) : 0;
+
smbios_build_type_16_table(dimm_cnt);
for (i = 0; i < dimm_cnt; i++) {
@@ -1019,10 +1055,16 @@ void smbios_get_tables(MachineState *ms,
}
for (i = 0; i < mem_array_size; i++) {
- smbios_build_type_19_table(i, mem_array[i].address,
+ smbios_build_type_19_table(i, offset, mem_array[i].address,
mem_array[i].length);
}
+ /*
+ * make sure 16 bit handle numbers in the headers of tables 19
+ * and 32 do not overlap.
+ */
+ assert((mem_array_size + offset) < (T32_BASE - T19_BASE));
+
smbios_build_type_32_table();
smbios_build_type_38_table();
smbios_build_type_41_table(errp);
@@ -1292,6 +1334,8 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
save_opt(&type4.serial, opts, "serial");
save_opt(&type4.asset, opts, "asset");
save_opt(&type4.part, opts, "part");
+ /* If the value is 0, it will take the value from the CPU model. */
+ type4.processor_id = qemu_opt_get_number(opts, "processor-id", 0);
type4.max_speed = qemu_opt_get_number(opts, "max-speed",
DEFAULT_CPU_SPEED);
type4.current_speed = qemu_opt_get_number(opts, "current-speed",
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index d899be17fd..48305e1574 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -224,7 +224,7 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s,
AspeedSegments seg;
int i;
- for (i = 0; i < asc->max_peripherals; i++) {
+ for (i = 0; i < asc->cs_num_max; i++) {
if (i == cs) {
continue;
}
@@ -290,7 +290,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs,
*/
if ((asc->segments == aspeed_2500_spi1_segments ||
asc->segments == aspeed_2500_spi2_segments) &&
- cs == asc->max_peripherals &&
+ cs == asc->cs_num_max &&
seg.addr + seg.size != asc->segments[cs].addr +
asc->segments[cs].size) {
aspeed_smc_error("Tried to change CS%d end address to 0x%"
@@ -327,7 +327,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs,
static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr,
unsigned size)
{
- aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u" PRIx64, addr, size);
+ aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u", addr, size);
return 0;
}
@@ -693,13 +693,13 @@ static void aspeed_smc_reset(DeviceState *d)
}
/* Unselect all peripherals */
- for (i = 0; i < s->num_cs; ++i) {
+ for (i = 0; i < asc->cs_num_max; ++i) {
s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
qemu_set_irq(s->cs_lines[i], true);
}
/* setup the default segment register values and regions for all */
- for (i = 0; i < asc->max_peripherals; ++i) {
+ for (i = 0; i < asc->cs_num_max; ++i) {
aspeed_smc_flash_set_segment_region(s, i,
asc->segment_to_reg(s, &asc->segments[i]));
}
@@ -729,8 +729,8 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
(aspeed_smc_has_dma(asc) && addr == R_DMA_LEN) ||
(aspeed_smc_has_dma(asc) && addr == R_DMA_CHECKSUM) ||
(addr >= R_SEG_ADDR0 &&
- addr < R_SEG_ADDR0 + asc->max_peripherals) ||
- (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->max_peripherals)) {
+ addr < R_SEG_ADDR0 + asc->cs_num_max) ||
+ (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->cs_num_max)) {
trace_aspeed_smc_read(addr << 2, size, s->regs[addr]);
@@ -1042,11 +1042,11 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
addr < s->r_timings + asc->nregs_timings) ||
addr == s->r_ce_ctrl) {
s->regs[addr] = value;
- } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
+ } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->cs_num_max) {
int cs = addr - s->r_ctrl0;
aspeed_smc_flash_update_ctrl(&s->flashes[cs], value);
} else if (addr >= R_SEG_ADDR0 &&
- addr < R_SEG_ADDR0 + asc->max_peripherals) {
+ addr < R_SEG_ADDR0 + asc->cs_num_max) {
int cs = addr - R_SEG_ADDR0;
if (value != s->regs[R_SEG_ADDR0 + cs]) {
@@ -1090,7 +1090,7 @@ static void aspeed_smc_instance_init(Object *obj)
AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s);
int i;
- for (i = 0; i < asc->max_peripherals; i++) {
+ for (i = 0; i < asc->cs_num_max; i++) {
object_initialize_child(obj, "flash[*]", &s->flashes[i],
TYPE_ASPEED_SMC_FLASH);
}
@@ -1127,21 +1127,15 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
s->r_timings = asc->r_timings;
s->conf_enable_w0 = asc->conf_enable_w0;
- /* Enforce some real HW limits */
- if (s->num_cs > asc->max_peripherals) {
- aspeed_smc_error("num_cs cannot exceed: %d", asc->max_peripherals);
- s->num_cs = asc->max_peripherals;
- }
-
/* DMA irq. Keep it first for the initialization in the SoC */
sysbus_init_irq(sbd, &s->irq);
- s->spi = ssi_create_bus(dev, "spi");
+ s->spi = ssi_create_bus(dev, NULL);
/* Setup cs_lines for peripherals */
- s->cs_lines = g_new0(qemu_irq, s->num_cs);
+ s->cs_lines = g_new0(qemu_irq, asc->cs_num_max);
- for (i = 0; i < s->num_cs; ++i) {
+ for (i = 0; i < asc->cs_num_max; ++i) {
sysbus_init_irq(sbd, &s->cs_lines[i]);
}
@@ -1174,7 +1168,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
* module behind to handle the memory accesses. This depends on
* the board configuration.
*/
- for (i = 0; i < asc->max_peripherals; ++i) {
+ for (i = 0; i < asc->cs_num_max; ++i) {
AspeedSMCFlash *fl = &s->flashes[i];
if (!object_property_set_link(OBJECT(fl), "controller", OBJECT(s),
@@ -1211,7 +1205,6 @@ static const VMStateDescription vmstate_aspeed_smc = {
};
static Property aspeed_smc_properties[] = {
- DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false),
DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr,
TYPE_MEMORY_REGION, MemoryRegion *),
@@ -1321,7 +1314,7 @@ static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 1;
+ asc->cs_num_max = 1;
asc->segments = aspeed_2400_smc_segments;
asc->flash_window_base = 0x10000000;
asc->flash_window_size = 0x6000000;
@@ -1366,7 +1359,7 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 5;
+ asc->cs_num_max = 5;
asc->segments = aspeed_2400_fmc_segments;
asc->segment_addr_mask = 0xffff0000;
asc->resets = aspeed_2400_fmc_resets;
@@ -1408,7 +1401,7 @@ static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_SPI_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = SPI_CONF_ENABLE_W0;
- asc->max_peripherals = 1;
+ asc->cs_num_max = 1;
asc->segments = aspeed_2400_spi1_segments;
asc->flash_window_base = 0x30000000;
asc->flash_window_size = 0x10000000;
@@ -1449,7 +1442,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 3;
+ asc->cs_num_max = 3;
asc->segments = aspeed_2500_fmc_segments;
asc->segment_addr_mask = 0xffff0000;
asc->resets = aspeed_2500_fmc_resets;
@@ -1487,7 +1480,7 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 2;
+ asc->cs_num_max = 2;
asc->segments = aspeed_2500_spi1_segments;
asc->segment_addr_mask = 0xffff0000;
asc->flash_window_base = 0x30000000;
@@ -1522,7 +1515,7 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 2;
+ asc->cs_num_max = 2;
asc->segments = aspeed_2500_spi2_segments;
asc->segment_addr_mask = 0xffff0000;
asc->flash_window_base = 0x38000000;
@@ -1604,7 +1597,7 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 1;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 3;
+ asc->cs_num_max = 3;
asc->segments = aspeed_2600_fmc_segments;
asc->segment_addr_mask = 0x0ff00ff0;
asc->resets = aspeed_2600_fmc_resets;
@@ -1643,7 +1636,7 @@ static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 2;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 2;
+ asc->cs_num_max = 2;
asc->segments = aspeed_2600_spi1_segments;
asc->segment_addr_mask = 0x0ff00ff0;
asc->flash_window_base = 0x30000000;
@@ -1682,7 +1675,7 @@ static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data)
asc->r_timings = R_TIMINGS;
asc->nregs_timings = 3;
asc->conf_enable_w0 = CONF_ENABLE_W0;
- asc->max_peripherals = 3;
+ asc->cs_num_max = 3;
asc->segments = aspeed_2600_spi2_segments;
asc->segment_addr_mask = 0x0ff00ff0;
asc->flash_window_base = 0x50000000;
diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c
index 10d8a14f19..3477afd735 100644
--- a/hw/tpm/tpm_tis_isa.c
+++ b/hw/tpm/tpm_tis_isa.c
@@ -127,7 +127,7 @@ static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp)
return;
}
- isa_init_irq(ISA_DEVICE(dev), &s->irq, s->irq_num);
+ s->irq = isa_get_irq(ISA_DEVICE(dev), s->irq_num);
memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
TPM_TIS_ADDR_BASE, &s->mmio);
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index f7ad6be5fb..a5102eac9e 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -89,9 +89,11 @@ virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d"
# virtio-iommu.c
virtio_iommu_device_reset(void) "reset!"
+virtio_iommu_system_reset(void) "system reset!"
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
virtio_iommu_device_status(uint8_t status) "driver status = %d"
-virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x"
+virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size, uint8_t bypass) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x bypass=0x%x"
+virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x"
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c
index d172632bb0..42c7f6d9e5 100644
--- a/hw/virtio/vhost-user-i2c.c
+++ b/hw/virtio/vhost-user-i2c.c
@@ -19,6 +19,11 @@
#define VIRTIO_ID_I2C_ADAPTER 34
#endif
+static const int feature_bits[] = {
+ VIRTIO_I2C_F_ZERO_LENGTH_REQUEST,
+ VHOST_INVALID_FEATURE_BIT
+};
+
static void vu_i2c_start(VirtIODevice *vdev)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
@@ -113,8 +118,10 @@ static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status)
static uint64_t vu_i2c_get_features(VirtIODevice *vdev,
uint64_t requested_features, Error **errp)
{
- /* No feature bits used yet */
- return requested_features;
+ VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
+
+ virtio_add_feature(&requested_features, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST);
+ return vhost_get_features(&i2c->vhost_dev, feature_bits, requested_features);
}
static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 662853513e..6abbc9da32 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -25,6 +25,7 @@
#include "migration/migration.h"
#include "migration/postcopy-ram.h"
#include "trace.h"
+#include "exec/ramblock.h"
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -1162,37 +1163,32 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev,
return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
}
-static void vhost_user_host_notifier_restore(struct vhost_dev *dev,
- int queue_idx)
+static void vhost_user_host_notifier_free(VhostUserHostNotifier *n)
{
- struct vhost_user *u = dev->opaque;
- VhostUserHostNotifier *n = &u->user->notifier[queue_idx];
- VirtIODevice *vdev = dev->vdev;
-
- if (n->addr && !n->set) {
- virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true);
- n->set = true;
- }
+ assert(n && n->unmap_addr);
+ munmap(n->unmap_addr, qemu_real_host_page_size);
+ n->unmap_addr = NULL;
}
-static void vhost_user_host_notifier_remove(struct vhost_dev *dev,
- int queue_idx)
+static void vhost_user_host_notifier_remove(VhostUserState *user,
+ VirtIODevice *vdev, int queue_idx)
{
- struct vhost_user *u = dev->opaque;
- VhostUserHostNotifier *n = &u->user->notifier[queue_idx];
- VirtIODevice *vdev = dev->vdev;
+ VhostUserHostNotifier *n = &user->notifier[queue_idx];
- if (n->addr && n->set) {
- virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false);
- n->set = false;
+ if (n->addr) {
+ if (vdev) {
+ virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false);
+ }
+ assert(!n->unmap_addr);
+ n->unmap_addr = n->addr;
+ n->addr = NULL;
+ call_rcu(n, vhost_user_host_notifier_free, rcu);
}
}
static int vhost_user_set_vring_base(struct vhost_dev *dev,
struct vhost_vring_state *ring)
{
- vhost_user_host_notifier_restore(dev, ring->index);
-
return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
}
@@ -1235,8 +1231,9 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
.payload.state = *ring,
.hdr.size = sizeof(msg.payload.state),
};
+ struct vhost_user *u = dev->opaque;
- vhost_user_host_notifier_remove(dev, ring->index);
+ vhost_user_host_notifier_remove(u->user, dev->vdev, ring->index);
ret = vhost_user_write(dev, &msg, NULL, 0);
if (ret < 0) {
@@ -1522,12 +1519,7 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev,
n = &user->notifier[queue_idx];
- if (n->addr) {
- virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false);
- object_unparent(OBJECT(&n->mr));
- munmap(n->addr, page_size);
- n->addr = NULL;
- }
+ vhost_user_host_notifier_remove(user, vdev, queue_idx);
if (area->u64 & VHOST_USER_VRING_NOFD_MASK) {
return 0;
@@ -1546,9 +1538,12 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev,
name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]",
user, queue_idx);
- if (!n->mr.ram) /* Don't init again after suspend. */
+ if (!n->mr.ram) { /* Don't init again after suspend. */
memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name,
page_size, addr);
+ } else {
+ n->mr.ram_block->host = addr;
+ }
g_free(name);
if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) {
@@ -1558,7 +1553,6 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev,
}
n->addr = addr;
- n->set = true;
return 0;
}
@@ -2522,17 +2516,16 @@ bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
void vhost_user_cleanup(VhostUserState *user)
{
int i;
+ VhostUserHostNotifier *n;
if (!user->chr) {
return;
}
memory_region_transaction_begin();
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
- if (user->notifier[i].addr) {
- object_unparent(OBJECT(&user->notifier[i].mr));
- munmap(user->notifier[i].addr, qemu_real_host_page_size);
- user->notifier[i].addr = NULL;
- }
+ n = &user->notifier[i];
+ vhost_user_host_notifier_remove(user, NULL, i);
+ object_unparent(OBJECT(&n->mr));
}
memory_region_transaction_commit();
user->chr = NULL;
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index 04ea43704f..6c67d5f034 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -395,15 +395,6 @@ static void vhost_vdpa_host_notifier_uninit(struct vhost_dev *dev,
}
}
-static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n)
-{
- int i;
-
- for (i = 0; i < n; i++) {
- vhost_vdpa_host_notifier_uninit(dev, i);
- }
-}
-
static int vhost_vdpa_host_notifier_init(struct vhost_dev *dev, int queue_index)
{
size_t page_size = qemu_real_host_page_size;
@@ -431,6 +422,7 @@ static int vhost_vdpa_host_notifier_init(struct vhost_dev *dev, int queue_index)
g_free(name);
if (virtio_queue_set_host_notifier_mr(vdev, queue_index, &n->mr, true)) {
+ object_unparent(OBJECT(&n->mr));
munmap(addr, page_size);
goto err;
}
@@ -442,6 +434,15 @@ err:
return -1;
}
+static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n)
+{
+ int i;
+
+ for (i = dev->vq_index; i < dev->vq_index + n; i++) {
+ vhost_vdpa_host_notifier_uninit(dev, i);
+ }
+}
+
static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev)
{
int i;
@@ -455,7 +456,7 @@ static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev)
return;
err:
- vhost_vdpa_host_notifiers_uninit(dev, i);
+ vhost_vdpa_host_notifiers_uninit(dev, i - dev->vq_index);
return;
}
diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c
index 3f3771274e..ed706681ac 100644
--- a/hw/virtio/vhost-vsock-common.c
+++ b/hw/virtio/vhost-vsock-common.c
@@ -153,19 +153,23 @@ static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc)
if (elem->out_num) {
error_report("invalid vhost-vsock event virtqueue element with "
"out buffers");
- goto out;
+ goto err;
}
if (iov_from_buf(elem->in_sg, elem->in_num, 0,
&event, sizeof(event)) != sizeof(event)) {
error_report("vhost-vsock event virtqueue element is too short");
- goto out;
+ goto err;
}
virtqueue_push(vq, elem, sizeof(event));
virtio_notify(VIRTIO_DEVICE(vvc), vq);
-out:
+ g_free(elem);
+ return;
+
+err:
+ virtqueue_detach_element(vq, elem, 0);
g_free(elem);
}
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 7b03efccec..b643f42ea4 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1287,7 +1287,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
return r;
}
- file.fd = event_notifier_get_fd(&vq->masked_notifier);
+ file.fd = event_notifier_get_wfd(&vq->masked_notifier);
r = dev->vhost_ops->vhost_set_vring_call(dev, &file);
if (r) {
VHOST_OPS_DEBUG(r, "vhost_set_vring_call failed");
@@ -1542,9 +1542,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
if (mask) {
assert(vdev->use_guest_notifier_mask);
- file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier);
+ file.fd = event_notifier_get_wfd(&hdev->vqs[index].masked_notifier);
} else {
- file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
+ file.fd = event_notifier_get_wfd(virtio_queue_get_guest_notifier(vvq));
}
file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n);
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index d23db98c56..0f69d1c742 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -48,6 +48,7 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
+ bool vdev_has_iommu;
Error *local_err = NULL;
DPRINTF("%s: plug device.\n", qbus->name);
@@ -69,11 +70,6 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
return;
}
- if (has_iommu && !virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
- error_setg(errp, "iommu_platform=true is not supported by the device");
- return;
- }
-
if (klass->device_plugged != NULL) {
klass->device_plugged(qbus->parent, &local_err);
}
@@ -82,9 +78,15 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
return;
}
+ vdev_has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
if (klass->get_dma_as != NULL && has_iommu) {
virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM);
vdev->dma_as = klass->get_dma_as(qbus->parent);
+ if (!vdev_has_iommu && vdev->dma_as != &address_space_memory) {
+ error_setg(errp,
+ "iommu_platform=true is not supported by the device");
+ return;
+ }
} else {
vdev->dma_as = &address_space_memory;
}
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index aa9c16a17b..239fe97b12 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -24,6 +24,7 @@
#include "hw/qdev-properties.h"
#include "hw/virtio/virtio.h"
#include "sysemu/kvm.h"
+#include "sysemu/reset.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "trace.h"
@@ -42,6 +43,7 @@
typedef struct VirtIOIOMMUDomain {
uint32_t id;
+ bool bypass;
GTree *mappings;
QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list;
} VirtIOIOMMUDomain;
@@ -257,12 +259,16 @@ static void virtio_iommu_put_endpoint(gpointer data)
}
static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s,
- uint32_t domain_id)
+ uint32_t domain_id,
+ bool bypass)
{
VirtIOIOMMUDomain *domain;
domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
if (domain) {
+ if (domain->bypass != bypass) {
+ return NULL;
+ }
return domain;
}
domain = g_malloc0(sizeof(*domain));
@@ -270,6 +276,7 @@ static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s,
domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
NULL, (GDestroyNotify)g_free,
(GDestroyNotify)g_free);
+ domain->bypass = bypass;
g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
QLIST_INIT(&domain->endpoint_list);
trace_virtio_iommu_get_domain(domain_id);
@@ -333,11 +340,16 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
{
uint32_t domain_id = le32_to_cpu(req->domain);
uint32_t ep_id = le32_to_cpu(req->endpoint);
+ uint32_t flags = le32_to_cpu(req->flags);
VirtIOIOMMUDomain *domain;
VirtIOIOMMUEndpoint *ep;
trace_virtio_iommu_attach(domain_id, ep_id);
+ if (flags & ~VIRTIO_IOMMU_ATTACH_F_BYPASS) {
+ return VIRTIO_IOMMU_S_INVAL;
+ }
+
ep = virtio_iommu_get_endpoint(s, ep_id);
if (!ep) {
return VIRTIO_IOMMU_S_NOENT;
@@ -355,7 +367,12 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
}
}
- domain = virtio_iommu_get_domain(s, domain_id);
+ domain = virtio_iommu_get_domain(s, domain_id,
+ flags & VIRTIO_IOMMU_ATTACH_F_BYPASS);
+ if (!domain) {
+ /* Incompatible bypass flag */
+ return VIRTIO_IOMMU_S_INVAL;
+ }
QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next);
ep->domain = domain;
@@ -418,6 +435,10 @@ static int virtio_iommu_map(VirtIOIOMMU *s,
return VIRTIO_IOMMU_S_NOENT;
}
+ if (domain->bypass) {
+ return VIRTIO_IOMMU_S_INVAL;
+ }
+
interval = g_malloc0(sizeof(*interval));
interval->low = virt_start;
@@ -463,6 +484,11 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s,
if (!domain) {
return VIRTIO_IOMMU_S_NOENT;
}
+
+ if (domain->bypass) {
+ return VIRTIO_IOMMU_S_INVAL;
+ }
+
interval.low = virt_start;
interval.high = virt_end;
@@ -728,8 +754,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr,
.perm = IOMMU_NONE,
};
- bypass_allowed = virtio_vdev_has_feature(&s->parent_obj,
- VIRTIO_IOMMU_F_BYPASS);
+ bypass_allowed = s->config.bypass;
sid = virtio_iommu_get_bdf(sdev);
@@ -780,6 +805,9 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr,
entry.perm = flag;
}
goto unlock;
+ } else if (ep->domain->bypass) {
+ entry.perm = flag;
+ goto unlock;
}
found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval),
@@ -831,13 +859,37 @@ static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data)
out_config->domain_range.start = cpu_to_le32(dev_config->domain_range.start);
out_config->domain_range.end = cpu_to_le32(dev_config->domain_range.end);
out_config->probe_size = cpu_to_le32(dev_config->probe_size);
+ out_config->bypass = dev_config->bypass;
trace_virtio_iommu_get_config(dev_config->page_size_mask,
dev_config->input_range.start,
dev_config->input_range.end,
dev_config->domain_range.start,
dev_config->domain_range.end,
- dev_config->probe_size);
+ dev_config->probe_size,
+ dev_config->bypass);
+}
+
+static void virtio_iommu_set_config(VirtIODevice *vdev,
+ const uint8_t *config_data)
+{
+ VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev);
+ struct virtio_iommu_config *dev_config = &dev->config;
+ const struct virtio_iommu_config *in_config = (void *)config_data;
+
+ if (in_config->bypass != dev_config->bypass) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_IOMMU_F_BYPASS_CONFIG)) {
+ virtio_error(vdev, "cannot set config.bypass");
+ return;
+ } else if (in_config->bypass != 0 && in_config->bypass != 1) {
+ virtio_error(vdev, "invalid config.bypass value '%u'",
+ in_config->bypass);
+ return;
+ }
+ dev_config->bypass = in_config->bypass;
+ }
+
+ trace_virtio_iommu_set_config(in_config->bypass);
}
static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f,
@@ -963,6 +1015,19 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr,
return 0;
}
+static void virtio_iommu_system_reset(void *opaque)
+{
+ VirtIOIOMMU *s = opaque;
+
+ trace_virtio_iommu_system_reset();
+
+ /*
+ * config.bypass is sticky across device reset, but should be restored on
+ * system reset
+ */
+ s->config.bypass = s->boot_bypass;
+}
+
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -988,9 +1053,9 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE);
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE);
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
- virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO);
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE);
+ virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS_CONFIG);
qemu_mutex_init(&s->mutex);
@@ -1001,6 +1066,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
} else {
error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
}
+
+ qemu_register_reset(virtio_iommu_system_reset, s);
}
static void virtio_iommu_device_unrealize(DeviceState *dev)
@@ -1008,6 +1075,8 @@ static void virtio_iommu_device_unrealize(DeviceState *dev)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
+ qemu_unregister_reset(virtio_iommu_system_reset, s);
+
g_hash_table_destroy(s->as_by_busptr);
if (s->domains) {
g_tree_destroy(s->domains);
@@ -1098,8 +1167,8 @@ static const VMStateDescription vmstate_endpoint = {
static const VMStateDescription vmstate_domain = {
.name = "domain",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.pre_load = domain_preload,
.fields = (VMStateField[]) {
VMSTATE_UINT32(id, VirtIOIOMMUDomain),
@@ -1108,6 +1177,7 @@ static const VMStateDescription vmstate_domain = {
VirtIOIOMMUInterval, VirtIOIOMMUMapping),
VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1,
vmstate_endpoint, VirtIOIOMMUEndpoint, next),
+ VMSTATE_BOOL_V(bypass, VirtIOIOMMUDomain, 2),
VMSTATE_END_OF_LIST()
}
};
@@ -1141,21 +1211,22 @@ static int iommu_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_virtio_iommu_device = {
.name = "virtio-iommu-device",
- .minimum_version_id = 1,
- .version_id = 1,
+ .minimum_version_id = 2,
+ .version_id = 2,
.post_load = iommu_post_load,
.fields = (VMStateField[]) {
- VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1,
+ VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 2,
&vmstate_domain, VirtIOIOMMUDomain),
+ VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2),
VMSTATE_END_OF_LIST()
},
};
static const VMStateDescription vmstate_virtio_iommu = {
.name = "virtio-iommu",
- .minimum_version_id = 1,
+ .minimum_version_id = 2,
.priority = MIG_PRI_IOMMU,
- .version_id = 1,
+ .version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_VIRTIO_DEVICE,
VMSTATE_END_OF_LIST()
@@ -1164,6 +1235,7 @@ static const VMStateDescription vmstate_virtio_iommu = {
static Property virtio_iommu_properties[] = {
DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *),
+ DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1180,6 +1252,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data)
vdc->unrealize = virtio_iommu_device_unrealize;
vdc->reset = virtio_iommu_device_reset;
vdc->get_config = virtio_iommu_get_config;
+ vdc->set_config = virtio_iommu_set_config;
vdc->get_features = virtio_iommu_get_features;
vdc->set_status = virtio_iommu_set_status;
vdc->vmsd = &vmstate_virtio_iommu_device;