diff options
-rw-r--r-- | Makefile.objs | 4 | ||||
-rw-r--r-- | Makefile.target | 2 | ||||
-rw-r--r-- | hmp-commands.hx | 25 | ||||
-rw-r--r-- | hw/acpi_piix4.c | 2 | ||||
-rw-r--r-- | hw/cirrus_vga.c | 1 | ||||
-rw-r--r-- | hw/ide/piix.c | 2 | ||||
-rw-r--r-- | hw/pc_piix.c | 12 | ||||
-rw-r--r-- | hw/pci-stub.c | 50 | ||||
-rw-r--r-- | hw/pci.c | 92 | ||||
-rw-r--r-- | hw/pci.h | 5 | ||||
-rw-r--r-- | hw/pcie.c | 11 | ||||
-rw-r--r-- | hw/pcie.h | 2 | ||||
-rw-r--r-- | hw/pcie_aer.c | 223 | ||||
-rw-r--r-- | hw/piix4.c | 1 | ||||
-rw-r--r-- | hw/piix_pci.c | 2 | ||||
-rw-r--r-- | hw/qdev.c | 23 | ||||
-rw-r--r-- | hw/qdev.h | 6 | ||||
-rw-r--r-- | hw/rtl8139.c | 28 | ||||
-rw-r--r-- | hw/vga-pci.c | 1 | ||||
-rw-r--r-- | hw/vmware_vga.c | 1 | ||||
-rw-r--r-- | hw/xio3130_downstream.c | 2 | ||||
-rw-r--r-- | hw/xio3130_upstream.c | 3 | ||||
-rw-r--r-- | qerror.c | 4 | ||||
-rw-r--r-- | qerror.h | 3 | ||||
-rw-r--r-- | sysemu.h | 5 | ||||
-rw-r--r-- | vl.c | 4 |
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); +} @@ -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; +} @@ -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; @@ -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); } } @@ -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 */ @@ -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) { @@ -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) { @@ -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)", }, @@ -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 } }" @@ -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 @@ -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(); |