aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs4
-rw-r--r--Makefile.target2
-rw-r--r--hmp-commands.hx25
-rw-r--r--hw/acpi_piix4.c2
-rw-r--r--hw/cirrus_vga.c1
-rw-r--r--hw/ide/piix.c2
-rw-r--r--hw/pc_piix.c12
-rw-r--r--hw/pci-stub.c50
-rw-r--r--hw/pci.c92
-rw-r--r--hw/pci.h5
-rw-r--r--hw/pcie.c11
-rw-r--r--hw/pcie.h2
-rw-r--r--hw/pcie_aer.c223
-rw-r--r--hw/piix4.c1
-rw-r--r--hw/piix_pci.c2
-rw-r--r--hw/qdev.c23
-rw-r--r--hw/qdev.h6
-rw-r--r--hw/rtl8139.c28
-rw-r--r--hw/vga-pci.c1
-rw-r--r--hw/vmware_vga.c1
-rw-r--r--hw/xio3130_downstream.c2
-rw-r--r--hw/xio3130_upstream.c3
-rw-r--r--qerror.c4
-rw-r--r--qerror.h3
-rw-r--r--sysemu.h5
-rw-r--r--vl.c4
26 files changed, 478 insertions, 36 deletions
diff --git a/Makefile.objs b/Makefile.objs
index d6b3d60703..c3e52c5674 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -169,9 +169,7 @@ hw-obj-y =
hw-obj-y += vl.o loader.o
hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
hw-obj-y += fw_cfg.o
-# FIXME: Core PCI code and its direct dependencies are required by the
-# QMP query-pci command.
-hw-obj-y += pci.o pci_bridge.o
+hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
hw-obj-$(CONFIG_PCI) += msix.o msi.o
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
diff --git a/Makefile.target b/Makefile.target
index a5e217edc3..e15b1c4fb6 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -1,6 +1,7 @@
# -*- Mode: makefile -*-
GENERATED_HEADERS = config-target.h
+CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
include ../config-host.mak
@@ -188,6 +189,7 @@ ifdef CONFIG_SOFTMMU
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
+obj-$(CONFIG_NO_PCI) += pci-stub.o
obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
obj-y += vhost_net.o
diff --git a/hmp-commands.hx b/hmp-commands.hx
index df134f8f56..1cea572b15 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -873,6 +873,31 @@ Hot remove PCI device.
ETEXI
{
+ .name = "pcie_aer_inject_error",
+ .args_type = "advisory_non_fatal:-a,correctable:-c,"
+ "id:s,error_status:s,"
+ "header0:i?,header1:i?,header2:i?,header3:i?,"
+ "prefix0:i?,prefix1:i?,prefix2:i?,prefix3:i?",
+ .params = "[-a] [-c] id "
+ "<error_status> [<tlp header> [<tlp header prefix>]]",
+ .help = "inject pcie aer error\n\t\t\t"
+ " -a for advisory non fatal error\n\t\t\t"
+ " -c for correctable error\n\t\t\t"
+ "<id> = qdev device id\n\t\t\t"
+ "<error_status> = error string or 32bit\n\t\t\t"
+ "<tlb header> = 32bit x 4\n\t\t\t"
+ "<tlb header prefix> = 32bit x 4",
+ .user_print = pcie_aer_inject_error_print,
+ .mhandler.cmd_new = do_pcie_aer_inejct_error,
+ },
+
+STEXI
+@item pcie_aer_inject_error
+@findex pcie_aer_inject_error
+Inject PCIe AER error
+ETEXI
+
+ {
.name = "host_net_add",
.args_type = "device:s,opts:s?",
.params = "tap|user|socket|vde|dump [options]",
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 173d78148d..273097d480 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -428,6 +428,8 @@ static PCIDeviceInfo piix4_pm_info = {
.qdev.desc = "PM",
.qdev.size = sizeof(PIIX4PMState),
.qdev.vmsd = &vmstate_acpi,
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
.init = piix4_pm_initfn,
.config_write = pm_write_config,
.qdev.props = (Property[]) {
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 75d1cc6f57..5f45b5dee7 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -3140,6 +3140,7 @@ static PCIDeviceInfo cirrus_vga_info = {
.qdev.desc = "Cirrus CLGD 54xx VGA",
.qdev.size = sizeof(PCICirrusVGAState),
.qdev.vmsd = &vmstate_pci_cirrus_vga,
+ .no_hotplug = 1,
.init = pci_cirrus_vga_initfn,
.romfile = VGABIOS_CIRRUS_FILENAME,
.config_write = pci_cirrus_write_config,
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 1cad9066a0..d4289af9c4 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -194,11 +194,13 @@ static PCIDeviceInfo piix_ide_info[] = {
.qdev.name = "piix3-ide",
.qdev.size = sizeof(PCIIDEState),
.qdev.no_user = 1,
+ .no_hotplug = 1,
.init = pci_piix3_ide_initfn,
},{
.qdev.name = "piix4-ide",
.qdev.size = sizeof(PCIIDEState),
.qdev.no_user = 1,
+ .no_hotplug = 1,
.init = pci_piix4_ide_initfn,
},{
/* end of list */
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index a2fb554aa2..f82508d92f 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -217,14 +217,6 @@ static QEMUMachine pc_machine = {
.desc = "Standard PC",
.init = pc_init_pci,
.max_cpus = 255,
- .compat_props = (GlobalProperty[]) {
- {
- .driver = "PCI",
- .property = "command_serr_enable",
- .value = "off",
- },
- { /* end of list */ }
- },
.is_default = 1,
};
@@ -246,6 +238,10 @@ static QEMUMachine pc_machine_v0_13 = {
.driver = "vmware-svga",
.property = "rombar",
.value = stringify(0),
+ },{
+ .driver = "PCI",
+ .property = "command_serr_enable",
+ .value = "off",
},
{ /* end of list */ }
},
diff --git a/hw/pci-stub.c b/hw/pci-stub.c
new file mode 100644
index 0000000000..c5a0aa8979
--- /dev/null
+++ b/hw/pci-stub.c
@@ -0,0 +1,50 @@
+/*
+ * PCI stubs for plathome that doesn't support pci bus.
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysemu.h"
+#include "monitor.h"
+#include "pci.h"
+
+static void pci_error_message(Monitor *mon)
+{
+ monitor_printf(mon, "PCI devices not supported\n");
+}
+
+void do_pci_info(Monitor *mon, QObject **ret_data)
+{
+ pci_error_message(mon);
+}
+
+void do_pci_info_print(Monitor *mon, const QObject *data)
+{
+ pci_error_message(mon);
+}
+
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
+{
+ pci_error_message(mon);
+ return -ENOSYS;
+}
+
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
+{
+ pci_error_message(mon);
+}
diff --git a/hw/pci.c b/hw/pci.c
index ef00d20d5f..8d0e3df2e5 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -137,7 +137,11 @@ static void pci_update_irq_status(PCIDevice *dev)
}
}
-static void pci_device_reset(PCIDevice *dev)
+/*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
{
int r;
/* TODO: call the below unconditionally once all pci devices
@@ -1620,6 +1624,11 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
info->is_bridge);
if (pci_dev == NULL)
return -1;
+ if (qdev->hotplugged && info->no_hotplug) {
+ qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
+ do_pci_unregister_device(pci_dev);
+ return -1;
+ }
rc = info->init(pci_dev);
if (rc != 0) {
do_pci_unregister_device(pci_dev);
@@ -1652,7 +1661,12 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
static int pci_unplug_device(DeviceState *qdev)
{
PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+ PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+ if (info->no_hotplug) {
+ qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
+ return -1;
+ }
return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
PCI_HOTPLUG_DISABLED);
}
@@ -2010,13 +2024,77 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev)
static char *pcibus_get_dev_path(DeviceState *dev)
{
- PCIDevice *d = (PCIDevice *)dev;
- char path[16];
+ PCIDevice *d = container_of(dev, PCIDevice, qdev);
+ PCIDevice *t;
+ int slot_depth;
+ /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function.
+ * 00 is added here to make this format compatible with
+ * domain:Bus:Slot.Func for systems without nested PCI bridges.
+ * Slot.Function list specifies the slot and function numbers for all
+ * devices on the path from root to the specific device. */
+ int domain_len = strlen("DDDD:00");
+ int slot_len = strlen(":SS.F");
+ int path_len;
+ char *path, *p;
- snprintf(path, sizeof(path), "%04x:%02x:%02x.%x",
- pci_find_domain(d->bus), d->config[PCI_SECONDARY_BUS],
- PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+ /* Calculate # of slots on path between device and root. */;
+ slot_depth = 0;
+ for (t = d; t; t = t->bus->parent_dev) {
+ ++slot_depth;
+ }
- return strdup(path);
+ path_len = domain_len + slot_len * slot_depth;
+
+ /* Allocate memory, fill in the terminating null byte. */
+ path = malloc(path_len + 1 /* For '\0' */);
+ path[path_len] = '\0';
+
+ /* First field is the domain. */
+ snprintf(path, domain_len, "%04x:00", pci_find_domain(d->bus));
+
+ /* Fill in slot numbers. We walk up from device to root, so need to print
+ * them in the reverse order, last to first. */
+ p = path + path_len;
+ for (t = d; t; t = t->bus->parent_dev) {
+ p -= slot_len;
+ snprintf(p, slot_len, ":%02x.%x", PCI_SLOT(t->devfn), PCI_FUNC(d->devfn));
+ }
+
+ return path;
+}
+
+static int pci_qdev_find_recursive(PCIBus *bus,
+ const char *id, PCIDevice **pdev)
+{
+ DeviceState *qdev = qdev_find_recursive(&bus->qbus, id);
+ if (!qdev) {
+ return -ENODEV;
+ }
+
+ /* roughly check if given qdev is pci device */
+ if (qdev->info->init == &pci_qdev_init &&
+ qdev->parent_bus->info == &pci_bus_info) {
+ *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
+ return 0;
+ }
+ return -EINVAL;
}
+int pci_qdev_find_device(const char *id, PCIDevice **pdev)
+{
+ struct PCIHostBus *host;
+ int rc = -ENODEV;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ int tmp = pci_qdev_find_recursive(host->bus, id, pdev);
+ if (!tmp) {
+ rc = 0;
+ break;
+ }
+ if (tmp != -ENODEV) {
+ rc = tmp;
+ }
+ }
+
+ return rc;
+}
diff --git a/hw/pci.h b/hw/pci.h
index 17744dc859..bc8d5bb3c7 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -237,6 +237,7 @@ void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque, int devfn_min, int nirq);
+void pci_device_reset(PCIDevice *dev);
void pci_bus_reset(PCIBus *bus);
void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
@@ -251,6 +252,7 @@ PCIBus *pci_find_root_bus(int domain);
int pci_find_domain(const PCIBus *bus);
PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
+int pci_qdev_find_device(const char *id, PCIDevice **pdev);
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
@@ -434,6 +436,9 @@ typedef struct {
/* pcie stuff */
int is_express; /* is this device pci express? */
+ /* device isn't hot-pluggable */
+ int no_hotplug;
+
/* rom bar */
const char *romfile;
} PCIDeviceInfo;
diff --git a/hw/pcie.c b/hw/pcie.c
index d1f0086559..6a113a9327 100644
--- a/hw/pcie.c
+++ b/hw/pcie.c
@@ -380,10 +380,6 @@ void pcie_cap_root_reset(PCIDevice *dev)
pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
}
-/*
- * TODO: implement FLR:
- * Right now sets the bit which indicates FLR is supported.
- */
/* function level reset(FLR) */
void pcie_cap_flr_init(PCIDevice *dev)
{
@@ -403,8 +399,11 @@ void pcie_cap_flr_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
- if (pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR)) {
- /* TODO: implement FLR */
+ if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) {
+ /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler
+ so the handler can detect FLR by looking at this bit. */
+ pci_device_reset(dev);
+ pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR);
}
}
diff --git a/hw/pcie.h b/hw/pcie.h
index 7baa813509..bc909e2793 100644
--- a/hw/pcie.h
+++ b/hw/pcie.h
@@ -63,8 +63,6 @@ struct PCIExpressDevice {
/* Offset of express capability in config space */
uint8_t exp_cap;
- /* TODO FLR */
-
/* SLOT */
unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#)
* default is 0 = INTA#
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
index cb97a95d61..6e653ddb92 100644
--- a/hw/pcie_aer.c
+++ b/hw/pcie_aer.c
@@ -19,6 +19,8 @@
*/
#include "sysemu.h"
+#include "qemu-objects.h"
+#include "monitor.h"
#include "pci_bridge.h"
#include "pcie.h"
#include "msix.h"
@@ -806,3 +808,224 @@ const VMStateDescription vmstate_pcie_aer_log = {
VMSTATE_END_OF_LIST()
}
};
+
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+ int devfn;
+ assert(qobject_type(data) == QTYPE_QDICT);
+ qdict = qobject_to_qdict(data);
+
+ devfn = (int)qdict_get_int(qdict, "devfn");
+ monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n",
+ qdict_get_str(qdict, "id"),
+ (int) qdict_get_int(qdict, "domain"),
+ (int) qdict_get_int(qdict, "bus"),
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
+
+typedef struct PCIEAERErrorName {
+ const char *name;
+ uint32_t val;
+ bool correctable;
+} PCIEAERErrorName;
+
+/*
+ * AER error name -> value convertion table
+ * This naming scheme is same to linux aer-injection tool.
+ */
+static const struct PCIEAERErrorName pcie_aer_error_list[] = {
+ {
+ .name = "TRAIN",
+ .val = PCI_ERR_UNC_TRAIN,
+ .correctable = false,
+ }, {
+ .name = "DLP",
+ .val = PCI_ERR_UNC_DLP,
+ .correctable = false,
+ }, {
+ .name = "SDN",
+ .val = PCI_ERR_UNC_SDN,
+ .correctable = false,
+ }, {
+ .name = "POISON_TLP",
+ .val = PCI_ERR_UNC_POISON_TLP,
+ .correctable = false,
+ }, {
+ .name = "FCP",
+ .val = PCI_ERR_UNC_FCP,
+ .correctable = false,
+ }, {
+ .name = "COMP_TIME",
+ .val = PCI_ERR_UNC_COMP_TIME,
+ .correctable = false,
+ }, {
+ .name = "COMP_ABORT",
+ .val = PCI_ERR_UNC_COMP_ABORT,
+ .correctable = false,
+ }, {
+ .name = "UNX_COMP",
+ .val = PCI_ERR_UNC_UNX_COMP,
+ .correctable = false,
+ }, {
+ .name = "RX_OVER",
+ .val = PCI_ERR_UNC_RX_OVER,
+ .correctable = false,
+ }, {
+ .name = "MALF_TLP",
+ .val = PCI_ERR_UNC_MALF_TLP,
+ .correctable = false,
+ }, {
+ .name = "ECRC",
+ .val = PCI_ERR_UNC_ECRC,
+ .correctable = false,
+ }, {
+ .name = "UNSUP",
+ .val = PCI_ERR_UNC_UNSUP,
+ .correctable = false,
+ }, {
+ .name = "ACSV",
+ .val = PCI_ERR_UNC_ACSV,
+ .correctable = false,
+ }, {
+ .name = "INTN",
+ .val = PCI_ERR_UNC_INTN,
+ .correctable = false,
+ }, {
+ .name = "MCBTLP",
+ .val = PCI_ERR_UNC_MCBTLP,
+ .correctable = false,
+ }, {
+ .name = "ATOP_EBLOCKED",
+ .val = PCI_ERR_UNC_ATOP_EBLOCKED,
+ .correctable = false,
+ }, {
+ .name = "TLP_PRF_BLOCKED",
+ .val = PCI_ERR_UNC_TLP_PRF_BLOCKED,
+ .correctable = false,
+ }, {
+ .name = "RCVR",
+ .val = PCI_ERR_COR_RCVR,
+ .correctable = true,
+ }, {
+ .name = "BAD_TLP",
+ .val = PCI_ERR_COR_BAD_TLP,
+ .correctable = true,
+ }, {
+ .name = "BAD_DLLP",
+ .val = PCI_ERR_COR_BAD_DLLP,
+ .correctable = true,
+ }, {
+ .name = "REP_ROLL",
+ .val = PCI_ERR_COR_REP_ROLL,
+ .correctable = true,
+ }, {
+ .name = "REP_TIMER",
+ .val = PCI_ERR_COR_REP_TIMER,
+ .correctable = true,
+ }, {
+ .name = "ADV_NONFATAL",
+ .val = PCI_ERR_COR_ADV_NONFATAL,
+ .correctable = true,
+ }, {
+ .name = "INTERNAL",
+ .val = PCI_ERR_COR_INTERNAL,
+ .correctable = true,
+ }, {
+ .name = "HL_OVERFLOW",
+ .val = PCI_ERR_COR_HL_OVERFLOW,
+ .correctable = true,
+ },
+};
+
+static int pcie_aer_parse_error_string(const char *error_name,
+ uint32_t *status, bool *correctable)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) {
+ const PCIEAERErrorName *e = &pcie_aer_error_list[i];
+ if (strcmp(error_name, e->name)) {
+ continue;
+ }
+
+ *status = e->val;
+ *correctable = e->correctable;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ const char *error_name;
+ uint32_t error_status;
+ bool correctable;
+ PCIDevice *dev;
+ PCIEAERErr err;
+ int ret;
+
+ ret = pci_qdev_find_device(id, &dev);
+ if (ret < 0) {
+ monitor_printf(mon,
+ "id or pci device path is invalid or device not "
+ "found. %s\n", id);
+ return ret;
+ }
+ if (!pci_is_express(dev)) {
+ monitor_printf(mon, "the device doesn't support pci express. %s\n",
+ id);
+ return -ENOSYS;
+ }
+
+ error_name = qdict_get_str(qdict, "error_status");
+ if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
+ char *e = NULL;
+ error_status = strtoul(error_name, &e, 0);
+ correctable = !!qdict_get_int(qdict, "correctable");
+ if (!e || *e != '\0') {
+ monitor_printf(mon, "invalid error status value. \"%s\"",
+ error_name);
+ return -EINVAL;
+ }
+ }
+ err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+
+ err.flags = 0;
+ if (correctable) {
+ err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
+ }
+ if (qdict_get_int(qdict, "advisory_non_fatal")) {
+ err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
+ }
+ if (qdict_haskey(qdict, "header0")) {
+ err.flags |= PCIE_AER_ERR_HEADER_VALID;
+ }
+ if (qdict_haskey(qdict, "prefix0")) {
+ err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT;
+ }
+
+ err.header[0] = qdict_get_try_int(qdict, "header0", 0);
+ err.header[1] = qdict_get_try_int(qdict, "header1", 0);
+ err.header[2] = qdict_get_try_int(qdict, "header2", 0);
+ err.header[3] = qdict_get_try_int(qdict, "header3", 0);
+
+ err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0);
+ err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0);
+ err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0);
+ err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0);
+
+ ret = pcie_aer_inject_error(dev, &err);
+ *ret_data = qobject_from_jsonf("{'id': %s, "
+ "'domain': %d, 'bus': %d, 'devfn': %d, "
+ "'ret': %d}",
+ id,
+ pci_find_domain(dev->bus),
+ pci_bus_num(dev->bus), dev->devfn,
+ ret);
+ assert(*ret_data);
+
+ return 0;
+}
diff --git a/hw/piix4.c b/hw/piix4.c
index 5489386d68..72073cd0a0 100644
--- a/hw/piix4.c
+++ b/hw/piix4.c
@@ -113,6 +113,7 @@ static PCIDeviceInfo piix4_info[] = {
.qdev.desc = "ISA bridge",
.qdev.size = sizeof(PCIDevice),
.qdev.no_user = 1,
+ .no_hotplug = 1,
.init = piix4_initfn,
},{
/* end of list */
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 38f9d9eea4..358da58a80 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -348,6 +348,7 @@ static PCIDeviceInfo i440fx_info[] = {
.qdev.size = sizeof(PCII440FXState),
.qdev.vmsd = &vmstate_i440fx,
.qdev.no_user = 1,
+ .no_hotplug = 1,
.init = i440fx_initfn,
.config_write = i440fx_write_config,
},{
@@ -356,6 +357,7 @@ static PCIDeviceInfo i440fx_info[] = {
.qdev.size = sizeof(PIIX3State),
.qdev.vmsd = &vmstate_piix3,
.qdev.no_user = 1,
+ .no_hotplug = 1,
.init = piix3_initfn,
},{
/* end of list */
diff --git a/hw/qdev.c b/hw/qdev.c
index 6fc9b02a38..5b8d3742ec 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -32,6 +32,8 @@
#include "blockdev.h"
static int qdev_hotplug = 0;
+static bool qdev_hot_added = false;
+static bool qdev_hot_removed = false;
/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
static BusState *main_system_bus;
@@ -93,6 +95,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
if (qdev_hotplug) {
assert(bus->allow_hotplug);
dev->hotplugged = 1;
+ qdev_hot_added = true;
}
dev->instance_id_alias = -1;
dev->state = DEV_STATE_CREATED;
@@ -294,6 +297,8 @@ int qdev_unplug(DeviceState *dev)
}
assert(dev->info->unplug != NULL);
+ qdev_hot_removed = true;
+
return dev->info->unplug(dev);
}
@@ -328,8 +333,9 @@ void qdev_reset_all(DeviceState *dev)
qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
}
-void qbus_reset_all(BusState *bus)
+void qbus_reset_all_fn(void *opaque)
{
+ BusState *bus = opaque;
qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
}
@@ -394,6 +400,11 @@ void qdev_machine_creation_done(void)
qdev_hotplug = 1;
}
+bool qdev_machine_modified(void)
+{
+ return qdev_hot_added || qdev_hot_removed;
+}
+
/* Get a character (serial) device interface. */
CharDriverState *qdev_init_chardev(DeviceState *dev)
{
@@ -547,7 +558,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name,
return NULL;
}
-static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
{
DeviceState *dev, *ret;
BusState *child;
@@ -754,8 +765,11 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
if (parent) {
QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
parent->num_child_bus++;
+ } else if (bus != main_system_bus) {
+ /* TODO: once all bus devices are qdevified,
+ only reset handler for main_system_bus should be registered here. */
+ qemu_register_reset(qbus_reset_all_fn, bus);
}
-
}
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
@@ -778,6 +792,9 @@ void qbus_free(BusState *bus)
if (bus->parent) {
QLIST_REMOVE(bus, sibling);
bus->parent->num_child_bus--;
+ } else {
+ assert(bus != main_system_bus); /* main_system_bus is never freed */
+ qemu_unregister_reset(qbus_reset_all_fn, bus);
}
qemu_free((void*)bus->name);
if (bus->qdev_allocated) {
diff --git a/hw/qdev.h b/hw/qdev.h
index aaaf55ab26..e520aaa786 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -132,6 +132,7 @@ int qdev_unplug(DeviceState *dev);
void qdev_free(DeviceState *dev);
int qdev_simple_unplug_cb(DeviceState *dev);
void qdev_machine_creation_done(void);
+bool qdev_machine_modified(void);
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
@@ -183,6 +184,8 @@ BusState *qdev_get_parent_bus(DeviceState *dev);
/*** BUS API. ***/
+DeviceState *qdev_find_recursive(BusState *bus, const char *id);
+
/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */
typedef int (qbus_walkerfn)(BusState *bus, void *opaque);
typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque);
@@ -198,7 +201,8 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
qbus_walkerfn *busfn, void *opaque);
void qdev_reset_all(DeviceState *dev);
-void qbus_reset_all(BusState *bus);
+void qbus_reset_all_fn(void *opaque);
+
void qbus_free(BusState *bus);
#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index a8aed89074..a22530cf89 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -495,6 +495,8 @@ typedef struct RTL8139State {
QEMUTimer *timer;
int64_t TimerExpire;
+ /* Support migration to/from old versions */
+ int rtl8139_mmio_io_addr_dummy;
} RTL8139State;
static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time);
@@ -3162,6 +3164,21 @@ static int rtl8139_post_load(void *opaque, int version_id)
return 0;
}
+static bool rtl8139_hotplug_ready_needed(void *opaque)
+{
+ return qdev_machine_modified();
+}
+
+static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
+ .name = "rtl8139/hotplug_ready",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void rtl8139_pre_save(void *opaque)
{
RTL8139State* s = opaque;
@@ -3171,6 +3188,7 @@ static void rtl8139_pre_save(void *opaque)
rtl8139_set_next_tctr_time(s, current_time);
s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
get_ticks_per_sec());
+ s->rtl8139_mmio_io_addr_dummy = s->rtl8139_mmio_io_addr;
}
static const VMStateDescription vmstate_rtl8139 = {
@@ -3223,7 +3241,7 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UNUSED(4),
VMSTATE_MACADDR(conf.macaddr, RTL8139State),
- VMSTATE_INT32(rtl8139_mmio_io_addr, RTL8139State),
+ VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State),
VMSTATE_UINT32(currTxDesc, RTL8139State),
VMSTATE_UINT32(currCPlusRxDesc, RTL8139State),
@@ -3252,6 +3270,14 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_rtl8139_hotplug_ready,
+ .needed = rtl8139_hotplug_ready_needed,
+ }, {
+ /* empty */
+ }
}
};
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
index 791ca22763..ce9ec45777 100644
--- a/hw/vga-pci.c
+++ b/hw/vga-pci.c
@@ -110,6 +110,7 @@ static PCIDeviceInfo vga_info = {
.qdev.name = "VGA",
.qdev.size = sizeof(PCIVGAState),
.qdev.vmsd = &vmstate_vga_pci,
+ .no_hotplug = 1,
.init = pci_vga_initfn,
.config_write = pci_vga_write_config,
.romfile = "vgabios-stdvga.bin",
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index d9dd52fc60..6c59053308 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1318,6 +1318,7 @@ static PCIDeviceInfo vmsvga_info = {
.qdev.name = "vmware-svga",
.qdev.size = sizeof(struct pci_vmsvga_state_s),
.qdev.vmsd = &vmstate_vmware_vga,
+ .no_hotplug = 1,
.init = pci_vmsvga_initfn,
.romfile = "vgabios-vmware.bin",
};
diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c
index 1a2d258bd2..5aa6a6b149 100644
--- a/hw/xio3130_downstream.c
+++ b/hw/xio3130_downstream.c
@@ -89,7 +89,7 @@ static int xio3130_downstream_initfn(PCIDevice *d)
if (rc < 0) {
goto err_msi;
}
- pcie_cap_flr_init(d); /* TODO: implement FLR */
+ pcie_cap_flr_init(d);
pcie_cap_deverr_init(d);
pcie_cap_slot_init(d, s->slot);
pcie_chassis_create(s->chassis);
diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c
index 387bf6c77e..a7640f518a 100644
--- a/hw/xio3130_upstream.c
+++ b/hw/xio3130_upstream.c
@@ -85,10 +85,7 @@ static int xio3130_upstream_initfn(PCIDevice *d)
if (rc < 0) {
goto err_msi;
}
-
- /* TODO: implement FLR */
pcie_cap_flr_init(d);
-
pcie_cap_deverr_init(d);
rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
if (rc < 0) {
diff --git a/qerror.c b/qerror.c
index ac2cdafa65..9d0cdeb45c 100644
--- a/qerror.c
+++ b/qerror.c
@@ -101,6 +101,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Device '%(device)' has no child bus",
},
{
+ .error_fmt = QERR_DEVICE_NO_HOTPLUG,
+ .desc = "Device '%(device)' does not support hotplugging",
+ },
+ {
.error_fmt = QERR_DUPLICATE_ID,
.desc = "Duplicate ID '%(id)' for %(object)",
},
diff --git a/qerror.h b/qerror.h
index 943a24b4e5..b0f69dabe5 100644
--- a/qerror.h
+++ b/qerror.h
@@ -90,6 +90,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_DEVICE_NO_BUS \
"{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }"
+#define QERR_DEVICE_NO_HOTPLUG \
+ "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }"
+
#define QERR_DUPLICATE_ID \
"{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }"
diff --git a/sysemu.h b/sysemu.h
index e728ea175b..d8fceec806 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -158,6 +158,11 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict);
void drive_hot_add(Monitor *mon, const QDict *qdict);
void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
+/* pcie aer error injection */
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data);
+
/* serial ports */
#define MAX_SERIAL_PORTS 4
diff --git a/vl.c b/vl.c
index 78fcef1fee..0292184273 100644
--- a/vl.c
+++ b/vl.c
@@ -3092,7 +3092,9 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- qemu_register_reset((void *)qbus_reset_all, sysbus_get_default());
+ /* TODO: once all bus devices are qdevified, this should be done
+ * when bus is created by qdev.c */
+ qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
qemu_run_machine_init_done_notifiers();
qemu_system_reset();