summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/pc_piix.c12
-rw-r--r--hw/pci-stub.c50
-rw-r--r--hw/pci.c82
-rw-r--r--hw/pci.h2
-rw-r--r--hw/pcie.c11
-rw-r--r--hw/pcie.h2
-rw-r--r--hw/pcie_aer.c223
-rw-r--r--hw/qdev.c13
-rw-r--r--hw/qdev.h5
-rw-r--r--hw/xio3130_downstream.c2
-rw-r--r--hw/xio3130_upstream.c3
11 files changed, 374 insertions, 31 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 */ }
     },
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..d0b51b80bd 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
@@ -2010,13 +2014,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..052960e3ea 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,
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/qdev.c b/hw/qdev.c
index 6fc9b02a38..31eb464f23 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -328,8 +328,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);
 }
 
@@ -547,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;
@@ -754,8 +755,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 +782,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..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);
@@ -198,7 +200,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/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) {