From af0669f0edbbcb8c17f7c2b919089485c8327f4f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 14 Dec 2010 14:24:53 +0200 Subject: pci: don't use bus number in migration, stub out Using bus numbers in migration is clearly wrong as they are guest assigned. Not really sure what the right thing to do is, for now stick 0 in there so things keep working for non-nested setups, add a TODO. We also probably have to mark nested bridges as non-migrateable until this is fixed? Signed-off-by: Michael S. Tsirkin Acked-by: Alex Williamson --- hw/pci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/pci.c b/hw/pci.c index ef00d20d5f..0cb411720d 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -2014,7 +2014,10 @@ static char *pcibus_get_dev_path(DeviceState *dev) char path[16]; snprintf(path, sizeof(path), "%04x:%02x:%02x.%x", - pci_find_domain(d->bus), d->config[PCI_SECONDARY_BUS], + pci_find_domain(d->bus), + 0 /* TODO: need a persistent path for nested buses. + * Note: pci_bus_num(d->bus) is not right as it's guest + * assigned. */, PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); return strdup(path); -- cgit v1.2.3 From 68694897e55ebd229898d4b6546877ccea500954 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 16 Dec 2010 19:33:22 +0100 Subject: qdev: sysbus_get_default must not return a NULL pointer (fix regression) Every system should have some sort of main system bus, so sysbus_get_default should always return a valid bus. Without this patch, at least mipssim and malta no longer start but raise a null pointer access exception (caused by commit ec990eb622ad46df5ddcb1e94c418c271894d416). Cc: Anthony Liguori Signed-off-by: Stefan Weil Signed-off-by: Michael S. Tsirkin --- hw/qdev.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index 10e28df7a1..6fc9b02a38 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -107,10 +107,7 @@ DeviceState *qdev_create(BusState *bus, const char *name) DeviceInfo *info; if (!bus) { - if (!main_system_bus) { - main_system_bus = qbus_create(&system_bus_info, NULL, "main-system-bus"); - } - bus = main_system_bus; + bus = sysbus_get_default(); } info = qdev_find_info(bus->info, name); @@ -311,6 +308,10 @@ static int qdev_reset_one(DeviceState *dev, void *opaque) BusState *sysbus_get_default(void) { + if (!main_system_bus) { + main_system_bus = qbus_create(&system_bus_info, NULL, + "main-system-bus"); + } return main_system_bus; } -- cgit v1.2.3 From 80376c3fc2c38fdd45354e4b0eb45031f35587ed Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Mon, 20 Dec 2010 14:33:35 +0900 Subject: qbus: register reset handler for qbus whose parent is NULL Stefan Weil reported the regression caused by ec990eb622ad46df5ddcb1e94c418c271894d416 as follows > The second regression also occurs with MIPS malta. > Networking no longer works with the default pcnet nic. > > This is caused because the reset function for pcnet is no > longer called during system boot. The result in an invalid > mac address (all zero) and a non-working nic. > > For this second regression I still have no simple solution. > Of course mips_malta.c should be converted to qdev which > would fix both problems (but only for malta system emulation). The issue is, it is assumed that all qbuses, qdeves are under main_system_bus. But there are qbuses whose parent is NULL. So it is necessary to trigger reset for those qbuses. (On the other hand, if NULL is passed to qdev_create(), its parent bus is main_system_bus.) Ideally those buses should be moved under bus controller device which is qdev. But it's not done yet. So register qbus reset handler for qbus whose parent is NULL. Reported-by: Stefan Weil Signed-off-by: "Michael S. Tsirkin" Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/qdev.c | 14 +++++++++++++- hw/qdev.h | 2 ++ vl.c | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index 6fc9b02a38..d93a6c4bf0 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -333,6 +333,12 @@ void qbus_reset_all(BusState *bus) qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); } +void qbus_reset_all_fn(void *opaque) +{ + BusState *bus = opaque; + qbus_reset_all(bus); +} + /* can be used as ->unplug() callback for the simple cases */ int qdev_simple_unplug_cb(DeviceState *dev) { @@ -754,8 +760,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 +787,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..b239bb480c 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -199,6 +199,8 @@ 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/vl.c b/vl.c index c4d3fc0d3d..8d6bab4032 100644 --- a/vl.c +++ b/vl.c @@ -3088,7 +3088,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(); -- cgit v1.2.3 From f530cce3152b55a535e9e87610110fefa4e9c853 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Dec 2010 15:17:10 +0200 Subject: qdev: remove an unused function qbus_reset_all is unused, remove it Signed-off-by: Michael S. Tsirkin --- hw/qdev.c | 7 +------ hw/qdev.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index d93a6c4bf0..4747c6768c 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -328,15 +328,10 @@ void qdev_reset_all(DeviceState *dev) qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL); } -void qbus_reset_all(BusState *bus) -{ - qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); -} - void qbus_reset_all_fn(void *opaque) { BusState *bus = opaque; - qbus_reset_all(bus); + qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); } /* can be used as ->unplug() callback for the simple cases */ diff --git a/hw/qdev.h b/hw/qdev.h index b239bb480c..5f5a319da4 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -198,7 +198,6 @@ 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); -- cgit v1.2.3 From 362dd48c16728a656c1ef75f8160838127fd76d5 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 22 Dec 2010 15:13:43 +0900 Subject: pc/piix: fix mismerge of b1aeb92666d2fde413c34578b3b42bbfe5f2a506 The change set of b1aeb92666d2fde413c34578b3b42bbfe5f2a506 in pci branch was mismerged. The compatibility should be kept for 0.13, not for 0.14. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/pc_piix.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 */ } }, -- cgit v1.2.3 From 0ead87c8debaf12bf4e8cf5130e4e7fb83dbf126 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 22 Dec 2010 15:14:35 +0900 Subject: pcie: add flr support Support flr: trigger device reset on flr config write. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 6 +++++- hw/pci.h | 1 + hw/pcie.c | 11 +++++------ hw/pcie.h | 2 -- hw/xio3130_downstream.c | 2 +- hw/xio3130_upstream.c | 3 --- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index 0cb411720d..eb21848b0f 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 diff --git a/hw/pci.h b/hw/pci.h index 17744dc859..6e80b08e0b 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); 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/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) { -- cgit v1.2.3 From b3a29fd5604841dac8bfee1bac35f150cab976fb Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 22 Dec 2010 19:54:48 +0900 Subject: build, pci: remove QMP dependency on core PCI code by introducing pci-stub.c, eliminate QMP dependency on core PCI code rquired by query-pci command. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- Makefile.objs | 4 +--- Makefile.target | 2 ++ hw/pci-stub.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 hw/pci-stub.c 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 d08f5dd4ef..38582d4bde 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/hw/pci-stub.c b/hw/pci-stub.c new file mode 100644 index 0000000000..674591d913 --- /dev/null +++ b/hw/pci-stub.c @@ -0,0 +1,37 @@ +/* + * PCI stubs for plathome that doesn't support pci bus. + * + * Copyright (c) 2010 Isaku Yamahata + * 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 . + */ + +#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); +} -- cgit v1.2.3 From a2ee6b4fcb3e2f2f5d60211ce0f734c61f8e0e30 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 24 Dec 2010 12:14:12 +0900 Subject: qdev: export qdev_find_recursive() for later use This patch exports qdev_find_recursive() for later use. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/qdev.c | 2 +- hw/qdev.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/qdev.c b/hw/qdev.c index 4747c6768c..31eb464f23 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -548,7 +548,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; diff --git a/hw/qdev.h b/hw/qdev.h index 5f5a319da4..2be775f9e8 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -183,6 +183,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); -- cgit v1.2.3 From f3006dd1e60af3765c501a082d621f947d6e5974 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 24 Dec 2010 12:14:13 +0900 Subject: pci: introduce a helper function to convert qdev id to PCIDevice This patch introduce a helper function to get PCIDevice from qdev id. This function will be used later. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 35 +++++++++++++++++++++++++++++++++++ hw/pci.h | 1 + 2 files changed, 36 insertions(+) diff --git a/hw/pci.c b/hw/pci.c index eb21848b0f..44bb3b9a91 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -2027,3 +2027,38 @@ static char *pcibus_get_dev_path(DeviceState *dev) return strdup(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 6e80b08e0b..052960e3ea 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -252,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, -- cgit v1.2.3 From 2ae63bda50ec864a3e61d375b53c8e453ad50140 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 24 Dec 2010 12:14:14 +0900 Subject: pcie/aer: glue aer error injection into qemu monitor introduce pcie_aer_inject_error command. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hmp-commands.hx | 25 +++++++ hw/pci-stub.c | 13 ++++ hw/pcie_aer.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sysemu.h | 5 ++ 4 files changed, 266 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index dd3db36108..8d84ddc62a 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -870,6 +870,31 @@ STEXI @item pci_del @findex pci_del 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 " + " [ []]", + .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" + " = qdev device id\n\t\t\t" + " = error string or 32bit\n\t\t\t" + " = 32bit x 4\n\t\t\t" + " = 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 { diff --git a/hw/pci-stub.c b/hw/pci-stub.c index 674591d913..c5a0aa8979 100644 --- a/hw/pci-stub.c +++ b/hw/pci-stub.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ +#include "sysemu.h" #include "monitor.h" #include "pci.h" @@ -35,3 +36,15 @@ 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/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/sysemu.h b/sysemu.h index 38a20a3b04..4182aac3c1 100644 --- a/sysemu.h +++ b/sysemu.h @@ -157,6 +157,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 -- cgit v1.2.3 From a6a7005d14b3c32d4864a718fb1cb19c789f58a5 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 27 Dec 2010 11:21:38 +0200 Subject: pci: fix migration path for devices behind bridges The device path used for migration is currently broken for for all devices behind a nested bridge. Replace this by a hierarchical list of slot/function numbers, walking the path from root down to device. Add :00 after the domain number so that if there are no nested bridges, this is compatible with what we have now. Note: as pointed out by Gleb, using openfirmware paths might be cleaner, doing this would break compatibility though, and the IDs used are not guest or user visible at all, so breaking the compatibility is probably not worth it. Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index 44bb3b9a91..d0b51b80bd 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -2014,17 +2014,43 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev) static char *pcibus_get_dev_path(DeviceState *dev) { - PCIDevice *d = (PCIDevice *)dev; - char path[16]; - - snprintf(path, sizeof(path), "%04x:%02x:%02x.%x", - pci_find_domain(d->bus), - 0 /* TODO: need a persistent path for nested buses. - * Note: pci_bus_num(d->bus) is not right as it's guest - * assigned. */, - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); - - return strdup(path); + 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; + + /* Calculate # of slots on path between device and root. */; + slot_depth = 0; + for (t = d; t; t = t->bus->parent_dev) { + ++slot_depth; + } + + 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, -- cgit v1.2.3