pci: Replace pci_find_domain() with more general pci_root_bus_path()
pci_find_domain() is used in a number of places where we want an id for a whole PCI domain (i.e. the subtree under a PCI root bus). The trouble is that many platforms may support multiple independent host bridges with no hardware supplied notion of domain number. This patch, therefore, replaces calls to pci_find_domain() with calls to a new pci_root_bus_path() returning a string. The new call is implemented in terms of a new callback in the host bridge class, so it can be defined in some way that's well defined for the platform. When no callback is available we fall back on the qbus name. Most current uses of pci_find_domain() are for error or informational messages, so the change in identifiers should be harmless. The exception is pci_get_dev_path(), whose results form part of migration streams. To maintain compatibility with old migration streams, the PIIX PCI host is altered to always supply "0000" for this path, which matches the old domain number (since the code didn't actually support domains other than 0). For the pseries (spapr) PCI bridge we use a different platform-unique identifier (pseries machines can routinely have dozens of PCI host bridges). Theoretically that breaks migration streams, but given that we don't yet have migration support for pseries, it doesn't matter. Any other machines that have working migration support including PCI devices will need to be updated to maintain migration stream compatibility. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
c473d18da1
commit
568f0690fd
@ -629,11 +629,20 @@ static const TypeInfo i440fx_info = {
|
||||
.class_init = i440fx_class_init,
|
||||
};
|
||||
|
||||
static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
/* For backwards compat with old device paths */
|
||||
return "0000";
|
||||
}
|
||||
|
||||
static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = i440fx_pcihost_root_bus_path;
|
||||
k->init = i440fx_pcihost_initfn;
|
||||
dc->fw_name = "pci";
|
||||
dc->no_user = 1;
|
||||
|
@ -63,6 +63,13 @@ static int q35_host_init(SysBusDevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
/* For backwards compat with old device paths */
|
||||
return "0000";
|
||||
}
|
||||
|
||||
static Property mch_props[] = {
|
||||
DEFINE_PROP_UINT64("MCFG", Q35PCIHost, host.base_addr,
|
||||
MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
|
||||
@ -73,7 +80,9 @@ static void q35_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = q35_host_root_bus_path;
|
||||
k->init = q35_host_init;
|
||||
dc->props = mch_props;
|
||||
dc->fw_name = "pci";
|
||||
|
@ -275,8 +275,8 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n",
|
||||
pci_find_domain(dev),
|
||||
monitor_printf(mon, "OK root bus %s, bus %d, slot %d, function %d\n",
|
||||
pci_root_bus_path(dev),
|
||||
pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
|
||||
PCI_FUNC(dev->devfn));
|
||||
} else
|
||||
|
38
hw/pci/pci.c
38
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"
|
||||
@ -270,19 +271,20 @@ PCIBus *pci_device_root_bus(const PCIDevice *d)
|
||||
return bus;
|
||||
}
|
||||
|
||||
int pci_find_domain(const PCIDevice *dev)
|
||||
const char *pci_root_bus_path(PCIDevice *dev)
|
||||
{
|
||||
const PCIBus *rootbus = pci_device_root_bus(dev);
|
||||
struct PCIHostBus *host;
|
||||
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);
|
||||
|
||||
QLIST_FOREACH(host, &host_buses, next) {
|
||||
if (host->bus == rootbus) {
|
||||
return host->domain;
|
||||
}
|
||||
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,
|
||||
@ -2000,10 +2002,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), 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;
|
||||
@ -2137,30 +2139,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));
|
||||
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. */
|
||||
|
@ -169,6 +169,7 @@ static const TypeInfo pci_host_type_info = {
|
||||
.name = TYPE_PCI_HOST_BRIDGE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(PCIHostBridgeClass),
|
||||
.instance_size = sizeof(PCIHostState),
|
||||
};
|
||||
|
||||
|
@ -817,9 +817,9 @@ void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
|
||||
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",
|
||||
monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
|
||||
qdict_get_str(qdict, "id"),
|
||||
(int) qdict_get_int(qdict, "domain"),
|
||||
qdict_get_str(qdict, "root_bus"),
|
||||
(int) qdict_get_int(qdict, "bus"),
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
}
|
||||
@ -1020,9 +1020,9 @@ int do_pcie_aer_inject_error(Monitor *mon,
|
||||
|
||||
ret = pcie_aer_inject_error(dev, &err);
|
||||
*ret_data = qobject_from_jsonf("{'id': %s, "
|
||||
"'domain': %d, 'bus': %d, 'devfn': %d, "
|
||||
"'root_bus': %s, 'bus': %d, 'devfn': %d, "
|
||||
"'ret': %d}",
|
||||
id, pci_find_domain(dev),
|
||||
id, pci_root_bus_path(dev),
|
||||
pci_bus_num(dev->bus), dev->devfn,
|
||||
ret);
|
||||
assert(*ret_data);
|
||||
|
@ -696,11 +696,21 @@ static Property spapr_phb_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(host_bridge);
|
||||
|
||||
return sphb->dtbusname;
|
||||
}
|
||||
|
||||
static void spapr_phb_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = spapr_phb_root_bus_path;
|
||||
sdc->init = spapr_phb_init;
|
||||
dc->props = spapr_phb_properties;
|
||||
dc->reset = spapr_phb_reset;
|
||||
|
@ -391,7 +391,7 @@ void pci_for_each_device(PCIBus *bus, int bus_num,
|
||||
void *opaque);
|
||||
PCIBus *pci_find_primary_bus(void);
|
||||
PCIBus *pci_device_root_bus(const PCIDevice *d);
|
||||
int pci_find_domain(const PCIDevice *dev);
|
||||
const char *pci_root_bus_path(PCIDevice *dev);
|
||||
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn);
|
||||
int pci_qdev_find_device(const char *id, PCIDevice **pdev);
|
||||
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
|
||||
|
@ -33,6 +33,10 @@
|
||||
#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge"
|
||||
#define PCI_HOST_BRIDGE(obj) \
|
||||
OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE)
|
||||
#define PCI_HOST_BRIDGE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PCIHostBridgeClass, (klass), TYPE_PCI_HOST_BRIDGE)
|
||||
#define PCI_HOST_BRIDGE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(PCIHostBridgeClass, (obj), TYPE_PCI_HOST_BRIDGE)
|
||||
|
||||
struct PCIHostState {
|
||||
SysBusDevice busdev;
|
||||
@ -44,6 +48,12 @@ struct PCIHostState {
|
||||
PCIBus *bus;
|
||||
};
|
||||
|
||||
typedef struct PCIHostBridgeClass {
|
||||
SysBusDeviceClass parent_class;
|
||||
|
||||
const char *(*root_bus_path)(PCIHostState *, PCIBus *);
|
||||
} PCIHostBridgeClass;
|
||||
|
||||
/* common internal helpers for PCI/PCIe hosts, cut off overflows */
|
||||
void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
|
||||
uint32_t limit, uint32_t val, uint32_t len);
|
||||
|
Loading…
Reference in New Issue
Block a user