summary refs log tree commit diff stats
path: root/hw/pci/pci.c
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2013-07-08 08:00:23 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2013-07-08 08:00:23 -0500
commitdc11549ec213f85f6a024c7df68d349464cd1688 (patch)
tree92cd5c58d10412e722dd26b27c55a23065590605 /hw/pci/pci.c
parent945dad6d9d795f03fd839c6e36cc883b3839f5e5 (diff)
parent7588e2b0559ae72d3c2952c7807fc05c03099970 (diff)
downloadfocaccia-qemu-dc11549ec213f85f6a024c7df68d349464cd1688.tar.gz
focaccia-qemu-dc11549ec213f85f6a024c7df68d349464cd1688.zip
Merge remote-tracking branch 'mst/tags/for_anthony' into staging
pci,misc enhancements

This includes some pci enhancements:

Better support for systems with multiple PCI root buses
FW cfg interface for more robust pci programming in BIOS
Minor fixes/cleanups for fw cfg and cross-version migration -
    because of dependencies with other patches

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# gpg: Signature made Sun 07 Jul 2013 03:11:18 PM CDT using RSA key ID D28D5469
# gpg: Can't check signature: public key not found

# By David Gibson (10) and others
# Via Michael S. Tsirkin
* mst/tags/for_anthony:
  pci: Fold host_buses list into PCIHostState functionality
  pci: Remove domain from PCIHostBus
  pci: Simpler implementation of primary PCI bus
  pci: Add root bus parameter to pci_nic_init()
  pci: Add root bus argument to pci_get_bus_devfn()
  pci: Replace pci_find_domain() with more general pci_root_bus_path()
  pci: Use helper to find device's root bus in pci_find_domain()
  pci: Abolish pci_find_root_bus()
  pci: Move pci_read_devaddr to pci-hotplug-old.c
  pci: Cleanup configuration for pci-hotplug.c
  pvpanic: fix fwcfg for big endian hosts
  pvpanic: initialization cleanup
  MAINTAINERS: s/Marcelo/Paolo/
  e1000: cleanup process_tx_desc
  pc_piix: cleanup init compat handling
  pc: pass PCI hole ranges to Guests
  pci: store PCI hole ranges in guestinfo structure
  range: add Range structure

Message-id: 1373228271-31223-1-git-send-email-mst@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'hw/pci/pci.c')
-rw-r--r--hw/pci/pci.c137
1 files changed, 71 insertions, 66 deletions
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index ceb6a5d9cc..dcc85ef0af 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -25,6 +25,7 @@
 #include "hw/pci/pci.h"
 #include "hw/pci/pci_bridge.h"
 #include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_host.h"
 #include "monitor/monitor.h"
 #include "net/net.h"
 #include "sysemu/sysemu.h"
@@ -89,12 +90,7 @@ static void pci_del_option_rom(PCIDevice *pdev);
 static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
 static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
 
-struct PCIHostBus {
-    int domain;
-    struct PCIBus *bus;
-    QLIST_ENTRY(PCIHostBus) next;
-};
-static QLIST_HEAD(, PCIHostBus) host_buses;
+static QLIST_HEAD(, PCIHostState) pci_host_bridges;
 
 static const VMStateDescription vmstate_pcibus = {
     .name = "PCIBUS",
@@ -237,46 +233,54 @@ static int pcibus_reset(BusState *qbus)
     return 1;
 }
 
-static void pci_host_bus_register(int domain, PCIBus *bus)
+static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
 {
-    struct PCIHostBus *host;
-    host = g_malloc0(sizeof(*host));
-    host->domain = domain;
-    host->bus = bus;
-    QLIST_INSERT_HEAD(&host_buses, host, next);
+    PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent);
+
+    QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next);
 }
 
-PCIBus *pci_find_root_bus(int domain)
+PCIBus *pci_find_primary_bus(void)
 {
-    struct PCIHostBus *host;
+    PCIBus *primary_bus = NULL;
+    PCIHostState *host;
 
-    QLIST_FOREACH(host, &host_buses, next) {
-        if (host->domain == domain) {
-            return host->bus;
+    QLIST_FOREACH(host, &pci_host_bridges, next) {
+        if (primary_bus) {
+            /* We have multiple root buses, refuse to select a primary */
+            return NULL;
         }
+        primary_bus = host->bus;
     }
 
-    return NULL;
+    return primary_bus;
 }
 
-int pci_find_domain(const PCIBus *bus)
+PCIBus *pci_device_root_bus(const PCIDevice *d)
 {
-    PCIDevice *d;
-    struct PCIHostBus *host;
+    PCIBus *bus = d->bus;
 
-    /* obtain root bus */
     while ((d = bus->parent_dev) != NULL) {
         bus = d->bus;
     }
 
-    QLIST_FOREACH(host, &host_buses, next) {
-        if (host->bus == bus) {
-            return host->domain;
-        }
+    return bus;
+}
+
+const char *pci_root_bus_path(PCIDevice *dev)
+{
+    PCIBus *rootbus = pci_device_root_bus(dev);
+    PCIHostState *host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent);
+    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_GET_CLASS(host_bridge);
+
+    assert(!rootbus->parent_dev);
+    assert(host_bridge->bus == rootbus);
+
+    if (hc->root_bus_path) {
+        return (*hc->root_bus_path)(host_bridge, rootbus);
     }
 
-    abort();    /* should not be reached */
-    return -1;
+    return rootbus->qbus.name;
 }
 
 static void pci_bus_init(PCIBus *bus, DeviceState *parent,
@@ -292,7 +296,8 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent,
 
     /* host bridge */
     QLIST_INIT(&bus->child);
-    pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */
+
+    pci_host_bus_register(bus, parent);
 
     vmstate_register(NULL, -1, &vmstate_pcibus, bus);
 }
@@ -522,7 +527,7 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
  * Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
  *       [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
  */
-static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
                       unsigned int *slotp, unsigned int *funcp)
 {
     const char *p;
@@ -581,36 +586,34 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
     return 0;
 }
 
-int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
-                     unsigned *slotp)
-{
-    /* strip legacy tag */
-    if (!strncmp(addr, "pci_addr=", 9)) {
-        addr += 9;
-    }
-    if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
-        monitor_printf(mon, "Invalid pci address\n");
-        return -1;
-    }
-    return 0;
-}
-
-PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
+PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr)
 {
     int dom, bus;
     unsigned slot;
 
+    assert(!root->parent_dev);
+
+    if (!root) {
+        fprintf(stderr, "No primary PCI bus\n");
+        return NULL;
+    }
+
     if (!devaddr) {
         *devfnp = -1;
-        return pci_find_bus_nr(pci_find_root_bus(0), 0);
+        return pci_find_bus_nr(root, 0);
     }
 
     if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
         return NULL;
     }
 
+    if (dom != 0) {
+        fprintf(stderr, "No support for non-zero PCI domains\n");
+        return NULL;
+    }
+
     *devfnp = PCI_DEVFN(slot, 0);
-    return pci_find_bus_nr(pci_find_root_bus(dom), bus);
+    return pci_find_bus_nr(root, bus);
 }
 
 static void pci_init_cmask(PCIDevice *dev)
@@ -1526,11 +1529,11 @@ static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num)
 PciInfoList *qmp_query_pci(Error **errp)
 {
     PciInfoList *info, *head = NULL, *cur_item = NULL;
-    struct PCIHostBus *host;
+    PCIHostState *host_bridge;
 
-    QLIST_FOREACH(host, &host_buses, next) {
+    QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
         info = g_malloc0(sizeof(*info));
-        info->value = qmp_query_pci_bus(host->bus, 0);
+        info->value = qmp_query_pci_bus(host_bridge->bus, 0);
 
         /* XXX: waiting for the qapi to support GSList */
         if (!cur_item) {
@@ -1570,7 +1573,8 @@ static const char * const pci_nic_names[] = {
 
 /* Initialize a PCI NIC.  */
 /* FIXME callers should check for failure, but don't */
-PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
+                        const char *default_model,
                         const char *default_devaddr)
 {
     const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
@@ -1584,7 +1588,7 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
     if (i < 0)
         return NULL;
 
-    bus = pci_get_bus_devfn(&devfn, devaddr);
+    bus = pci_get_bus_devfn(&devfn, rootbus, devaddr);
     if (!bus) {
         error_report("Invalid PCI device address %s for device %s",
                      devaddr, pci_nic_names[i]);
@@ -1599,7 +1603,8 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
     return pci_dev;
 }
 
-PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
+                               const char *default_model,
                                const char *default_devaddr)
 {
     PCIDevice *res;
@@ -1607,7 +1612,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
     if (qemu_show_nic_models(nd->model, pci_nic_models))
         exit(0);
 
-    res = pci_nic_init(nd, default_model, default_devaddr);
+    res = pci_nic_init(nd, rootbus, default_model, default_devaddr);
     if (!res)
         exit(1);
     return res;
@@ -1998,10 +2003,10 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
         for (i = offset; i < offset + size; i++) {
             overlapping_cap = pci_find_capability_at_offset(pdev, i);
             if (overlapping_cap) {
-                fprintf(stderr, "ERROR: %04x:%02x:%02x.%x "
+                fprintf(stderr, "ERROR: %s:%02x:%02x.%x "
                         "Attempt to add PCI capability %x at offset "
                         "%x overlaps existing capability %x at offset %x\n",
-                        pci_find_domain(pdev->bus), pci_bus_num(pdev->bus),
+                        pci_root_bus_path(pdev), pci_bus_num(pdev->bus),
                         PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
                         cap_id, offset, overlapping_cap, i);
                 return -EINVAL;
@@ -2135,30 +2140,30 @@ static char *pcibus_get_dev_path(DeviceState *dev)
      * 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. */
-    char domain[] = "DDDD:00";
+    const char *root_bus_path;
+    int root_bus_len;
     char slot[] = ":SS.F";
-    int domain_len = sizeof domain - 1 /* For '\0' */;
     int slot_len = sizeof slot - 1 /* For '\0' */;
     int path_len;
     char *path, *p;
     int s;
 
+    root_bus_path = pci_root_bus_path(d);
+    root_bus_len = strlen(root_bus_path);
+
     /* 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;
+    path_len = root_bus_len + slot_len * slot_depth;
 
     /* Allocate memory, fill in the terminating null byte. */
     path = g_malloc(path_len + 1 /* For '\0' */);
     path[path_len] = '\0';
 
-    /* First field is the domain. */
-    s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus));
-    assert(s == domain_len);
-    memcpy(path, domain, domain_len);
+    memcpy(path, root_bus_path, root_bus_len);
 
     /* Fill in slot numbers. We walk up from device to root, so need to print
      * them in the reverse order, last to first. */
@@ -2192,11 +2197,11 @@ static int pci_qdev_find_recursive(PCIBus *bus,
 
 int pci_qdev_find_device(const char *id, PCIDevice **pdev)
 {
-    struct PCIHostBus *host;
+    PCIHostState *host_bridge;
     int rc = -ENODEV;
 
-    QLIST_FOREACH(host, &host_buses, next) {
-        int tmp = pci_qdev_find_recursive(host->bus, id, pdev);
+    QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
+        int tmp = pci_qdev_find_recursive(host_bridge->bus, id, pdev);
         if (!tmp) {
             rc = 0;
             break;