@@ -769,7 +769,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
memory_region_add_subregion(addr_space, 0x801fc000000ULL,
&s->pchip.reg_io);
- b = pci_register_bus(dev, "pci",
+ b = pci_register_bus(dev, "pci", "pci",
typhoon_set_irq, sys_map_irq, s,
&s->pchip.reg_mem, addr_space_io, 0, 64);
phb->bus = b;
@@ -377,7 +377,7 @@ PCIBus *pci_apb_init(hwaddr special_base,
memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
memory_region_add_subregion(get_system_memory(), mem_base, &d->pci_mmio);
- d->bus = pci_register_bus(&d->busdev.qdev, "pci",
+ d->bus = pci_register_bus(&d->busdev.qdev, "pci", "pci",
pci_apb_set_irq, pci_pbm_map_irq, d,
&d->pci_mmio,
get_system_io(),
@@ -704,7 +704,7 @@ static int bonito_pcihost_initfn(SysBusDevice *dev)
{
PCIHostState *phb = PCI_HOST_BRIDGE(dev);
- phb->bus = pci_register_bus(DEVICE(dev), "pci",
+ phb->bus = pci_register_bus(DEVICE(dev), "pci", "pci",
pci_bonito_set_irq, pci_bonito_map_irq, dev,
get_system_memory(), get_system_io(),
0x28, 32);
@@ -82,7 +82,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- phb->bus = pci_register_bus(dev, "pci",
+ phb->bus = pci_register_bus(dev, "pci", "pci",
pci_grackle_set_irq,
pci_grackle_map_irq,
pic,
@@ -1102,7 +1102,7 @@ PCIBus *gt64120_register(qemu_irq *pic)
qdev_init_nofail(dev);
d = GT64120_PCI_HOST_BRIDGE(dev);
phb = PCI_HOST_BRIDGE(dev);
- phb->bus = pci_register_bus(dev, "pci",
+ phb->bus = pci_register_bus(dev, "pci", "pci",
gt64120_pci_set_irq, gt64120_pci_map_irq,
pic,
get_system_memory(),
@@ -113,18 +113,12 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo)
{
- int dom, pci_bus;
- unsigned slot;
PCIDevice *dev;
const char *pci_addr = qdict_get_str(qdict, "pci_addr");
switch (dinfo->type) {
case IF_SCSI:
- if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) {
- goto err;
- }
- dev = pci_find_device(pci_find_root_bus(dom), pci_bus,
- PCI_DEVFN(slot, 0));
+ dev = pci_read_and_find_devaddr(mon, pci_addr);
if (!dev) {
monitor_printf(mon, "no pci device with address %s\n", pci_addr);
goto err;
@@ -250,8 +244,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->bus),
+ monitor_printf(mon, "OK root bus %s, bus %d, slot %d, function %d\n",
+ pci_root_bus_id(dev->bus),
pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
} else
@@ -262,17 +256,11 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict)
static int pci_device_hot_remove(Monitor *mon, const char *pci_addr)
{
PCIDevice *d;
- int dom, bus;
- unsigned slot;
Error *local_err = NULL;
- if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) {
- return -1;
- }
-
- d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0));
+ d = pci_read_and_find_devaddr(mon, pci_addr);
if (!d) {
- monitor_printf(mon, "slot %d empty\n", slot);
+ monitor_printf(mon, "slot %s empty\n", pci_addr);
return -1;
}
@@ -85,7 +85,7 @@ 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;
+ const char *root_bus_id;
struct PCIBus *bus;
QLIST_ENTRY(PCIHostBus) next;
};
@@ -232,29 +232,29 @@ 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, const char *id)
{
struct PCIHostBus *host;
+
host = g_malloc0(sizeof(*host));
- host->domain = domain;
+ if (id) {
+ host->root_bus_id = g_strdup(id);
+ } else {
+ host->root_bus_id = "0000";
+ }
host->bus = bus;
- QLIST_INSERT_HEAD(&host_buses, host, next);
-}
-
-PCIBus *pci_find_root_bus(int domain)
-{
- struct PCIHostBus *host;
QLIST_FOREACH(host, &host_buses, next) {
- if (host->domain == domain) {
- return host->bus;
+ if (strcmp(id, host->root_bus_id) == 0) {
+ fprintf(stderr, "Duplicate id \"%s\" for PCI host bridges\n", id);
+ exit(1);
}
}
- return NULL;
+ QLIST_INSERT_HEAD(&host_buses, host, next);
}
-int pci_find_domain(const PCIBus *bus)
+const char *pci_root_bus_id(const PCIBus *bus)
{
PCIDevice *d;
struct PCIHostBus *host;
@@ -266,16 +266,48 @@ int pci_find_domain(const PCIBus *bus)
QLIST_FOREACH(host, &host_buses, next) {
if (host->bus == bus) {
- return host->domain;
+ return host->root_bus_id;
}
}
-
abort(); /* should not be reached */
- return -1;
+ return NULL;
+}
+
+#if 0
+static PCIBus *pci_find_root_bus_by_id(const char *id)
+{
+ struct PCIHostBus *host;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ if (strcmp(host->root_bus_id, id) == 0) {
+ return host->bus;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+static PCIBus *pci_find_root_bus_by_domain(int domain)
+{
+ struct PCIHostBus *host;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ int id_domain;
+ char *e;
+
+ id_domain = strtol(host->root_bus_id, &e, 16);
+ if ((*e == '\0') && (domain == id_domain)) {
+ return host->bus;
+ }
+ }
+
+ return NULL;
}
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
const char *name,
+ const char *root_bus_id,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min)
@@ -288,12 +320,13 @@ void pci_bus_new_inplace(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, root_bus_id);
vmstate_register(NULL, -1, &vmstate_pcibus, bus);
}
PCIBus *pci_bus_new(DeviceState *parent, const char *name,
+ const char *root_bus_id,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min)
@@ -301,8 +334,8 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name,
PCIBus *bus;
bus = g_malloc0(sizeof(*bus));
- pci_bus_new_inplace(bus, parent, name, address_space_mem,
- address_space_io, devfn_min);
+ pci_bus_new_inplace(bus, parent, name, root_bus_id,
+ address_space_mem, address_space_io, devfn_min);
OBJECT(bus)->free = g_free;
return bus;
}
@@ -325,6 +358,7 @@ void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev)
}
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
+ const char *root_bus_id,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque,
MemoryRegion *address_space_mem,
@@ -333,8 +367,8 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name,
{
PCIBus *bus;
- bus = pci_bus_new(parent, name, address_space_mem,
- address_space_io, devfn_min);
+ bus = pci_bus_new(parent, name, root_bus_id,
+ address_space_mem, address_space_io, devfn_min);
pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
return bus;
}
@@ -557,36 +591,44 @@ 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)
+PCIDevice *pci_read_and_find_devaddr(Monitor *mon, const char *addr)
{
+ int domnum, busnum;
+ unsigned slotnum;
+ PCIBus *root_bus;
+
/* strip legacy tag */
if (!strncmp(addr, "pci_addr=", 9)) {
addr += 9;
}
- if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
+ if (pci_parse_devaddr(addr, &domnum, &busnum, &slotnum, NULL)) {
monitor_printf(mon, "Invalid pci address\n");
- return -1;
+ return NULL;
}
- return 0;
+
+ root_bus = pci_find_root_bus_by_domain(domnum);
+ return pci_find_device(root_bus, busnum, PCI_DEVFN(slotnum, 0));
}
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
{
+ PCIBus *root_bus;
int dom, bus;
unsigned slot;
if (!devaddr) {
+ root_bus = pci_find_root_bus_by_domain(0);
*devfnp = -1;
- return pci_find_bus_nr(pci_find_root_bus(0), 0);
+ return pci_find_bus_nr(root_bus, 0);
}
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
return NULL;
}
+ root_bus = pci_find_root_bus_by_domain(dom);
*devfnp = PCI_DEVFN(slot, 0);
- return pci_find_bus_nr(pci_find_root_bus(dom), bus);
+ return pci_find_bus_nr(root_bus, bus);
}
static void pci_init_cmask(PCIDevice *dev)
@@ -1913,10 +1955,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_id(pdev->bus), pci_bus_num(pdev->bus),
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
cap_id, offset, overlapping_cap, i);
return -EINVAL;
@@ -2071,7 +2113,7 @@ static char *pcibus_get_dev_path(DeviceState *dev)
path[path_len] = '\0';
/* First field is the domain. */
- s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus));
+ s = snprintf(domain, sizeof domain, "%s:00", pci_root_bus_id(d->bus));
assert(s == domain_len);
memcpy(path, domain, domain_len);
@@ -308,10 +308,12 @@ typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev,
PCIHotplugState state);
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
const char *name,
+ const char *root_bus_id,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min);
PCIBus *pci_bus_new(DeviceState *parent, const char *name,
+ const char *root_bus_id,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min);
@@ -322,6 +324,7 @@ void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
/* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */
int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin);
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
+ const char *root_bus_id,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque,
MemoryRegion *address_space_mem,
@@ -347,14 +350,12 @@ int pci_bus_num(PCIBus *s);
void pci_for_each_device(PCIBus *bus, int bus_num,
void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque),
void *opaque);
-PCIBus *pci_find_root_bus(int domain);
-int pci_find_domain(const PCIBus *bus);
+const char *pci_root_bus_id(const PCIBus *bus);
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);
-int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
- unsigned *slotp);
+PCIDevice *pci_read_and_find_devaddr(Monitor *mon, const char *addr);
void pci_device_deassert_intx(PCIDevice *dev);
@@ -1022,8 +1022,8 @@ int do_pcie_aer_inject_error(Monitor *mon,
*ret_data = qobject_from_jsonf("{'id': %s, "
"'domain': %d, 'bus': %d, 'devfn': %d, "
"'ret': %d}",
- id,
- pci_find_domain(dev->bus),
+ /* FIXME: how do we encode host bridge ID */
+ id, 0,
pci_bus_num(dev->bus), dev->devfn,
ret);
assert(*ret_data);
@@ -232,7 +232,7 @@ static PCIBus *i440fx_common_init(const char *device_name,
dev = qdev_create(NULL, "i440FX-pcihost");
s = PCI_HOST_BRIDGE(dev);
s->address_space = address_space_mem;
- b = pci_bus_new(dev, NULL, pci_address_space,
+ b = pci_bus_new(dev, NULL, NULL, pci_address_space,
address_space_io, 0);
s->bus = b;
object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL);
@@ -335,6 +335,7 @@ static const VMStateDescription vmstate_ppc4xx_pci = {
/* XXX Interrupt acknowledge cycles not supported. */
static int ppc4xx_pcihost_initfn(SysBusDevice *dev)
{
+ char id[20];
PPC4xxPCIState *s;
PCIHostState *h;
PCIBus *b;
@@ -343,11 +344,13 @@ static int ppc4xx_pcihost_initfn(SysBusDevice *dev)
h = PCI_HOST_BRIDGE(dev);
s = PPC4xx_PCI_HOST_BRIDGE(dev);
+ sprintf(id, "pci@%" HWADDR_PRIx, dev->mmio[0].addr);
+
for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
sysbus_init_irq(dev, &s->irq[i]);
}
- b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq,
+ b = pci_register_bus(DEVICE(dev), NULL, id, ppc4xx_pci_set_irq,
ppc4xx_pci_map_irq, s->irq, get_system_memory(),
get_system_io(), 0, 4);
h->bus = b;
@@ -354,7 +354,7 @@ static int e500_pcihost_initfn(SysBusDevice *dev)
memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN);
- b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq,
+ b = pci_register_bus(DEVICE(dev), "pci", "pci", mpc85xx_pci_set_irq,
mpc85xx_pci_map_irq, s->irq, address_space_mem,
&s->pio, PCI_DEVFN(s->first_slot, 0), 4);
h->bus = b;
@@ -116,7 +116,7 @@ static int raven_pcihost_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq[i]);
}
- bus = pci_register_bus(DEVICE(dev), NULL,
+ bus = pci_register_bus(DEVICE(dev), "pci", "pci",
prep_set_irq, prep_map_irq, s->irq,
address_space_mem, address_space_io, 0, 4);
h->bus = bus;
@@ -53,7 +53,7 @@ static int q35_host_init(SysBusDevice *dev)
if (pcie_host_init(&s->host) < 0) {
return -1;
}
- b = pci_bus_new(&s->host.pci.busdev.qdev, "pcie.0",
+ b = pci_bus_new(&s->host.pci.busdev.qdev, "pcie.0", NULL,
s->mch.pci_address_space, s->mch.address_space_io, 0);
s->host.pci.bus = b;
qdev_set_parent_bus(DEVICE(&s->mch), BUS(b));
@@ -119,7 +119,7 @@ static int sh_pci_device_init(SysBusDevice *dev)
for (i = 0; i < 4; i++) {
sysbus_init_irq(dev, &s->irq[i]);
}
- s->bus = pci_register_bus(&s->busdev.qdev, "pci",
+ s->bus = pci_register_bus(&s->busdev.qdev, "pci", "pci",
sh_pci_set_irq, sh_pci_map_irq,
s->irq,
get_system_memory(),
@@ -621,7 +621,7 @@ static int spapr_phb_init(SysBusDevice *s)
&sphb->msiwindow);
}
- bus = pci_register_bus(DEVICE(s), sphb->busname,
+ bus = pci_register_bus(DEVICE(s), sphb->busname, sphb->dtbusname,
pci_spapr_set_irq, pci_spapr_map_irq, sphb,
&sphb->memspace, &sphb->iospace,
PCI_DEVFN(0, 0), PCI_NUM_PINS);
@@ -234,7 +234,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- h->bus = pci_register_bus(dev, "pci",
+ h->bus = pci_register_bus(dev, "pci", "pci",
pci_unin_set_irq, pci_unin_map_irq,
pic,
&d->pci_mmio,
@@ -300,7 +300,7 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- h->bus = pci_register_bus(dev, "pci",
+ h->bus = pci_register_bus(dev, "pci", "pci",
pci_unin_set_irq, pci_unin_map_irq,
pic,
&d->pci_mmio,
@@ -67,7 +67,7 @@ static int pci_vpb_init(SysBusDevice *dev)
for (i = 0; i < 4; i++) {
sysbus_init_irq(dev, &s->irq[i]);
}
- bus = pci_register_bus(&dev->qdev, "pci",
+ bus = pci_register_bus(&dev->qdev, "pci", NULL,
pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
get_system_memory(), get_system_io(),
PCI_DEVFN(11, 0), 4);
Currently the qemu core PCI code doesn't support a machine having multiple independent PCI host bridges (aka PCI domains). It does have some incomplete stubs for such support, but unfortunately what's there is based on a bogus premise. PCI domains are often referenced by a PCI domain number, and the current code stores a domain number for each root-level PCI bus (although it's always set to 0 so far). But by definition, the domain number never appears on the PCI bus itself, so domain numbers are assigned purely by convention. Some platforms have a well defined convention, for example on PC the domain numbers are assigned by ACPI. However other platforms have no strong convention, and so domain numbering is performed entirely by the guest and we have no reasonable way of knowing what it will pick. Fixing this is complicated by the fact that the domain numbers are exposed in migration streams via pcibus_get_dev_path(). We also can't easily switch to using the qbus name as our identifier, because libvirt and other tools often expect a bus named simply "pci" to exist - including on platforms that would need the identifier "0000" to match existing device paths. This patch replaces the domain number in the PCIHostBus structure with a string "root bus id". The idea is that platforms which do have a strong domain convention can derive this from the domain number, other platforms can use whatever technique is appropriate to generate a stable, clear id for each PCI host bridge. The functions for creating new PCI buses / host bridges now take an id. If this is NULL, it is treated as "0000" which makes the device paths backwards compatible with existing migration streams. Platforms are updated to supply root bus ids as follows: * Machines I believe support migration (x86, ARM), use NULL so that the device paths don't change. * Machines that appear only to support a single PCI host bridge, but which as far as I know don't (yet) support migration (i.e. most things), use "pci" as the root bus ID. * PPC4xx can in theory support multiple host bridges in real hardware, although our current models never construct that. In this case we use "pci@XXXX" for the root bus ID, where XXXX is the host bridge's base IO address (on the system bus). This changes device paths, but migration is so broken on PPC at present it doesn't matter. * pseries (aka sPAPR) has more extensive existing support for multiple host bridges, since multiple (indeed, numerous) PCI host bridges are routine on "real" hardware (pseries is emulating an existing defined para-virtualized environment). Here we use "pci@XXXX" as the root bus id, where XXXX is the BUID (firmware supplied ID for the host bridge, which is used in hypervisor calls). This patch leaves pci_parse_devaddr() (and therefore its callers) broken for machines that don't use the backwards compat "0000" id. I'm looking at fixing that in a later version of this patch. Cc: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: David Gibson <david@gibson.dropbear.id.au> --- hw/alpha_typhoon.c | 2 +- hw/apb_pci.c | 2 +- hw/bonito.c | 2 +- hw/grackle_pci.c | 2 +- hw/gt64xxx.c | 2 +- hw/pci-hotplug.c | 22 +++-------- hw/pci.c | 104 ++++++++++++++++++++++++++++++++++++---------------- hw/pci.h | 9 +++-- hw/pcie_aer.c | 4 +- hw/piix_pci.c | 2 +- hw/ppc4xx_pci.c | 5 ++- hw/ppce500_pci.c | 2 +- hw/prep_pci.c | 2 +- hw/q35.c | 2 +- hw/sh_pci.c | 2 +- hw/spapr_pci.c | 2 +- hw/unin_pci.c | 4 +- hw/versatile_pci.c | 2 +- 18 files changed, 103 insertions(+), 69 deletions(-) Michael, I know this is pretty ugly, but I haven't spotted any better way of doing things, given th constraint of maintaining migration stream compatibility. Does something along these lines seem reasonable, or is there another approach you'd prefer for fixing the domain problem.