Patchwork [v11,4/5] pci: introduce a parser for fw device path to pci device

login
register
mail settings
Submitter Isaku Yamahata
Date Dec. 24, 2010, 3:14 a.m.
Message ID <8de626bdd26c44767f142fc6674e38cf998f26db.1293160345.git.yamahata@valinux.co.jp>
Download mbox | patch
Permalink /patch/76582/
State New
Headers show

Comments

Isaku Yamahata - Dec. 24, 2010, 3:14 a.m.
Introduce a function to parse fw device path to pci device.
the format is
/pci@{<ioport>, <mmio>}/[<fw_name>]@<slot>,<func>/.../[<fw_name>]@<slot>,<func>

<ioport> = "i"<ioport addr in hex>
<mmio> = <mmio addr in hex>
<slot> = slot number in hex
<func> = func number in hex

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/pci.c |  129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci.h |    2 +
 2 files changed, 131 insertions(+), 0 deletions(-)

Patch

diff --git a/hw/pci.c b/hw/pci.c
index 44bb3b9..752dde1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -2062,3 +2062,132 @@  int pci_qdev_find_device(const char *id, PCIDevice **pdev)
 
     return rc;
 }
+
+/*
+ * Parse format and get PCIDevice
+ * return 0 on success
+ *       <0 on error: format is invalid or device isn't found.
+ *
+ * Format:
+ * /pci@{<ioport>, <mmio>}/[<fw_name>]@<slot>,<func>/...
+ *                     .../[<fw_name>]@<slot>,<func>
+ *
+ * <ioport> = "i"<ioport addr in hex>
+ * <mmio> = <mmio addr in hex>
+ * <slot> = slot number in hex
+ * <func> = func number in hex
+ *
+ */
+int pci_parse_fw_dev_path(const char *path, PCIDevice **pdev)
+{
+    const char *p = path;
+    char *e;
+    size_t len;
+    PCIBus *bus;
+    struct PCIHostBus *host;
+
+    if (*p != '/') {
+        return -EINVAL;
+    }
+    e = strchr(p + 1, '/');
+    if (e == NULL) {
+        return -EINVAL;
+    }
+    len = e - p;
+    p = e + 1;
+
+    bus = NULL;
+    QLIST_FOREACH(host, &host_buses, next) {
+        DeviceState *qdev = host->bus->qbus.parent;
+        if (qdev) {
+            char *devpath = qdev_get_fw_dev_path(qdev);
+
+            if (len == strlen(devpath) && !strncmp(devpath, path, len)) {
+                bus = host->bus;
+                qemu_free(devpath);
+                break;
+            }
+            qemu_free(devpath);
+        } else {
+            /* This pci bus doesn't have host-to-pci bridge device.
+             * Check only if the path is pci ignoring other parameters. */
+#define PCI_FW_PATH     "/pci@"
+            if (strncmp(path, PCI_FW_PATH, strlen(PCI_FW_PATH))) {
+                return -EINVAL;
+            }
+            bus = host->bus;
+            break;
+        }
+    }
+
+    for (;;) {
+        char *at;
+        char *comma;
+        unsigned long slot;
+        unsigned long func;
+        PCIDevice *dev;
+        PCIBus *child_bus;
+
+        if (!bus) {
+            return -ENODEV;
+        }
+        if (*p == '\0') {
+            return -EINVAL;
+        }
+
+        at = strchr(p, '@');
+        if (at == NULL) {
+            return -EINVAL;
+        }
+        slot = strtoul(at + 1, &e, 16);
+        if (e == at + 1 || *e != ',') {
+            return -EINVAL;
+        }
+        if (slot >= PCI_SLOT_MAX) {
+            return -EINVAL;
+        }
+
+        comma = e;
+        func = strtoul(comma + 1, &e, 16);
+        if (e == comma + 1 || (*e != '/' && *e != '\0')) {
+            return -EINVAL;
+        }
+        if (func >= PCI_FUNC_MAX) {
+            return -EINVAL;
+        }
+
+        len = e - p;
+        dev = bus->devices[PCI_DEVFN(slot, func)];
+        if (!dev) {
+            return -ENODEV;
+        }
+        if (at != p) {
+            /* fw_name is specified. */
+            char *fw_dev_path = pcibus_get_fw_dev_path(&dev->qdev);
+            if (strncmp(p, fw_dev_path, len)) {
+                qemu_free(fw_dev_path);
+                return -EINVAL;
+            }
+            qemu_free(fw_dev_path);
+        }
+
+        if (*e == '\0') {
+            *pdev = dev;
+            return 0;
+        }
+
+        /*
+         * descending down pci-to-pci bridge.
+         * At the moment, there is no way to safely determine if the given
+         * pci device is really pci-to-pci device.
+         */
+        p = e;
+        QLIST_FOREACH(child_bus, &bus->child, sibling) {
+            if (child_bus->parent_dev == dev) {
+                bus = child_bus;
+                continue;
+            }
+        }
+        bus = NULL;
+    }
+}
diff --git a/hw/pci.h b/hw/pci.h
index 052960e..fc46ada 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -16,6 +16,7 @@ 
 #define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
 #define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
 #define PCI_FUNC(devfn)         ((devfn) & 0x07)
+#define PCI_SLOT_MAX            32
 #define PCI_FUNC_MAX            8
 
 /* Class, Vendor and Device IDs from Linux's pci_ids.h */
@@ -259,6 +260,7 @@  int pci_parse_devaddr(const char *addr, int *domp, int *busp,
                       unsigned int *slotp, unsigned int *funcp);
 int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
                      unsigned *slotp);
+int pci_parse_fw_dev_path(const char *path, PCIDevice **pdev);
 
 void do_pci_info_print(Monitor *mon, const QObject *data);
 void do_pci_info(Monitor *mon, QObject **ret_data);