This refactoring moves all the code needed (recursively) to register TYPE_PCI_BUS type to a new file hw/pci/pci_bus.c . This allows to properly add new functionality to the pci bus class.
Signed-off-by: Marcel Apfelbaum marcel@redhat.com --- arch_init.c | 1 + hw/alpha/typhoon.c | 1 + hw/mips/gt64xxx_pci.c | 1 + hw/pci-host/bonito.c | 1 + hw/pci-host/grackle.c | 1 + hw/pci-host/piix.c | 1 + hw/pci-host/ppce500.c | 1 + hw/pci-host/q35.c | 1 + hw/pci-host/uninorth.c | 1 + hw/pci/Makefile.objs | 2 +- hw/pci/pci.c | 472 +-------------------------------------------- hw/pci/pci_bus.c | 491 +++++++++++++++++++++++++++++++++++++++++++++++ hw/ppc/ppc4xx_pci.c | 1 + hw/sh4/r2d.c | 1 + hw/sh4/sh_pci.c | 1 + include/hw/pci/pci.h | 3 +- include/hw/pci/pci_bus.h | 8 + 17 files changed, 514 insertions(+), 474 deletions(-) create mode 100644 hw/pci/pci_bus.c
diff --git a/arch_init.c b/arch_init.c index 691b5e2..cbebf6d 100644 --- a/arch_init.c +++ b/arch_init.c @@ -37,6 +37,7 @@ #include "audio/audio.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/audio/audio.h" #include "sysemu/kvm.h" #include "migration/migration.h" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 62af946..d3fad5d 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -8,6 +8,7 @@
#include "cpu.h" #include "hw/hw.h" +#include "hw/pci/pci_bus.h" #include "hw/devices.h" #include "sysemu/sysemu.h" #include "alpha_sys.h" diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index 10fcca3..c4ca6f3 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -25,6 +25,7 @@ #include "hw/hw.h" #include "hw/mips/mips.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/i386/pc.h" #include "exec/address-spaces.h" diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 8bdd569..49e1122 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -41,6 +41,7 @@
#include "hw/hw.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/i386/pc.h" #include "hw/mips/mips.h" #include "hw/pci/pci_host.h" diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index bfe707a..89745cd 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -26,6 +26,7 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h"
/* debug Grackle */ //#define DEBUG_GRACKLE diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 723836f..292b6e9 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -25,6 +25,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 613ba73..4363951 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -17,6 +17,7 @@ #include "hw/hw.h" #include "hw/ppc/e500-ccsr.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "qemu/bswap.h" #include "hw/pci-host/ppce500.h" diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index df60e61..a42d0fb 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -28,6 +28,7 @@ * THE SOFTWARE. */ #include "hw/hw.h" +#include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" #include "qapi/visitor.h"
diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 53f2b59..1406b42 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -24,6 +24,7 @@ #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h"
/* debug UniNorth */ diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index 9f905e6..a05cca0 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o +common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bus.o common-obj-$(CONFIG_PCI) += msix.o msi.o common-obj-$(CONFIG_PCI) += shpc.o common-obj-$(CONFIG_PCI) += slotid_cap.o diff --git a/hw/pci/pci.c b/hw/pci/pci.c index cc5d946..442d209 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -45,11 +45,6 @@ # define PCI_DPRINTF(format, ...) do { } while (0) #endif
-static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); -static char *pcibus_get_dev_path(DeviceState *dev); -static char *pcibus_get_fw_dev_path(DeviceState *dev); -static void pcibus_reset(BusState *qbus); - static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), @@ -61,58 +56,11 @@ static Property pci_props[] = { DEFINE_PROP_END_OF_LIST() };
-static const VMStateDescription vmstate_pcibus = { - .name = "PCIBUS", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(nirq, PCIBus), - VMSTATE_VARRAY_INT32(irq_count, PCIBus, - nirq, 0, vmstate_info_int32, - int32_t), - VMSTATE_END_OF_LIST() - } -}; - -static void pci_bus_realize(BusState *qbus, Error **errp) -{ - PCIBus *bus = PCI_BUS(qbus); - - vmstate_register(NULL, -1, &vmstate_pcibus, bus); -} - -static void pci_bus_unrealize(BusState *qbus, Error **errp) -{ - PCIBus *bus = PCI_BUS(qbus); - - vmstate_unregister(NULL, &vmstate_pcibus, bus); -} - -static void pci_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->print_dev = pcibus_dev_print; - k->get_dev_path = pcibus_get_dev_path; - k->get_fw_dev_path = pcibus_get_fw_dev_path; - k->realize = pci_bus_realize; - k->unrealize = pci_bus_unrealize; - k->reset = pcibus_reset; -} - -static const TypeInfo pci_bus_info = { - .name = TYPE_PCI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(PCIBus), - .class_init = pci_bus_class_init, -}; - static const TypeInfo pcie_bus_info = { .name = TYPE_PCIE_BUS, .parent = TYPE_PCI_BUS, };
-static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); static void pci_update_mappings(PCIDevice *d); static void pci_irq_handler(void *opaque, int irq_num, int level); static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, Error **); @@ -185,7 +133,7 @@ void pci_device_deassert_intx(PCIDevice *dev) } }
-static void pci_do_device_reset(PCIDevice *dev) +void pci_do_device_reset(PCIDevice *dev) { int r;
@@ -230,27 +178,6 @@ void pci_device_reset(PCIDevice *dev) pci_do_device_reset(dev); }
-/* - * Trigger pci bus reset under a given bus. - * Called via qbus_reset_all on RST# assert, after the devices - * have been reset qdev_reset_all-ed already. - */ -static void pcibus_reset(BusState *qbus) -{ - PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus); - int i; - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - if (bus->devices[i]) { - pci_do_device_reset(bus->devices[i]); - } - } - - for (i = 0; i < bus->nirq; i++) { - assert(bus->irq_count[i] == 0); - } -} - static void pci_host_bus_register(PCIBus *bus, DeviceState *parent) { PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent); @@ -1301,74 +1228,6 @@ int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) return (pin + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS; }
-/***********************************************************/ -/* monitor info on PCI */ - -typedef struct { - uint16_t class; - const char *desc; - const char *fw_name; - uint16_t fw_ign_bits; -} pci_class_desc; - -static const pci_class_desc pci_class_descriptions[] = -{ - { 0x0001, "VGA controller", "display"}, - { 0x0100, "SCSI controller", "scsi"}, - { 0x0101, "IDE controller", "ide"}, - { 0x0102, "Floppy controller", "fdc"}, - { 0x0103, "IPI controller", "ipi"}, - { 0x0104, "RAID controller", "raid"}, - { 0x0106, "SATA controller"}, - { 0x0107, "SAS controller"}, - { 0x0180, "Storage controller"}, - { 0x0200, "Ethernet controller", "ethernet"}, - { 0x0201, "Token Ring controller", "token-ring"}, - { 0x0202, "FDDI controller", "fddi"}, - { 0x0203, "ATM controller", "atm"}, - { 0x0280, "Network controller"}, - { 0x0300, "VGA controller", "display", 0x00ff}, - { 0x0301, "XGA controller"}, - { 0x0302, "3D controller"}, - { 0x0380, "Display controller"}, - { 0x0400, "Video controller", "video"}, - { 0x0401, "Audio controller", "sound"}, - { 0x0402, "Phone"}, - { 0x0403, "Audio controller", "sound"}, - { 0x0480, "Multimedia controller"}, - { 0x0500, "RAM controller", "memory"}, - { 0x0501, "Flash controller", "flash"}, - { 0x0580, "Memory controller"}, - { 0x0600, "Host bridge", "host"}, - { 0x0601, "ISA bridge", "isa"}, - { 0x0602, "EISA bridge", "eisa"}, - { 0x0603, "MC bridge", "mca"}, - { 0x0604, "PCI bridge", "pci-bridge"}, - { 0x0605, "PCMCIA bridge", "pcmcia"}, - { 0x0606, "NUBUS bridge", "nubus"}, - { 0x0607, "CARDBUS bridge", "cardbus"}, - { 0x0608, "RACEWAY bridge"}, - { 0x0680, "Bridge"}, - { 0x0700, "Serial port", "serial"}, - { 0x0701, "Parallel port", "parallel"}, - { 0x0800, "Interrupt controller", "interrupt-controller"}, - { 0x0801, "DMA controller", "dma-controller"}, - { 0x0802, "Timer", "timer"}, - { 0x0803, "RTC", "rtc"}, - { 0x0900, "Keyboard", "keyboard"}, - { 0x0901, "Pen", "pen"}, - { 0x0902, "Mouse", "mouse"}, - { 0x0A00, "Dock station", "dock", 0x00ff}, - { 0x0B00, "i386 cpu", "cpu", 0x00ff}, - { 0x0c00, "Fireware contorller", "fireware"}, - { 0x0c01, "Access bus controller", "access-bus"}, - { 0x0c02, "SSA controller", "ssa"}, - { 0x0c03, "USB controller", "usb"}, - { 0x0c04, "Fibre channel controller", "fibre-channel"}, - { 0x0c05, "SMBus"}, - { 0, NULL} -}; - static void pci_for_each_device_under_bus(PCIBus *bus, void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), @@ -1396,161 +1255,6 @@ void pci_for_each_device(PCIBus *bus, int bus_num, } }
-static const pci_class_desc *get_class_desc(int class) -{ - const pci_class_desc *desc; - - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) { - desc++; - } - - return desc; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - -static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) -{ - PciMemoryRegionList *head = NULL, *cur_item = NULL; - int i; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - const PCIIORegion *r = &dev->io_regions[i]; - PciMemoryRegionList *region; - - if (!r->size) { - continue; - } - - region = g_malloc0(sizeof(*region)); - region->value = g_malloc0(sizeof(*region->value)); - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - region->value->type = g_strdup("io"); - } else { - region->value->type = g_strdup("memory"); - region->value->has_prefetch = true; - region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); - region->value->has_mem_type_64 = true; - region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); - } - - region->value->bar = i; - region->value->address = r->addr; - region->value->size = r->size; - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = region; - } else { - cur_item->next = region; - cur_item = region; - } - } - - return head; -} - -static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - PciBridgeInfo *info; - - info = g_malloc0(sizeof(*info)); - - info->bus.number = dev->config[PCI_PRIMARY_BUS]; - info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; - info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; - - info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); - info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); - info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range)); - info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); - if (child_bus) { - info->has_devices = true; - info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); - } - } - - return info; -} - -static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - const pci_class_desc *desc; - PciDeviceInfo *info; - uint8_t type; - int class; - - info = g_malloc0(sizeof(*info)); - info->bus = bus_num; - info->slot = PCI_SLOT(dev->devfn); - info->function = PCI_FUNC(dev->devfn); - - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info.q_class = class; - desc = get_class_desc(class); - if (desc->desc) { - info->class_info.has_desc = true; - info->class_info.desc = g_strdup(desc->desc); - } - - info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); - info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); - info->regions = qmp_query_pci_regions(dev); - info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); - - if (dev->config[PCI_INTERRUPT_PIN] != 0) { - info->has_irq = true; - info->irq = dev->config[PCI_INTERRUPT_LINE]; - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - info->has_pci_bridge = true; - info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); - } - - return info; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) -{ - PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; - PCIDevice *dev; - int devfn; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - dev = bus->devices[devfn]; - if (dev) { - info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_device(dev, bus, bus_num); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = info; - } else { - cur_item->next = info; - cur_item = info; - } - } - } - - return head; -} - static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) { PciInfo *info = NULL; @@ -1674,50 +1378,6 @@ PCIDevice *pci_vga_init(PCIBus *bus) } }
-/* Whether a given bus number is in range of the secondary - * bus of the given bridge device. */ -static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) -{ - return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & - PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && - dev->config[PCI_SECONDARY_BUS] < bus_num && - bus_num <= dev->config[PCI_SUBORDINATE_BUS]; -} - -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) -{ - PCIBus *sec; - - if (!bus) { - return NULL; - } - - if (pci_bus_num(bus) == bus_num) { - return bus; - } - - /* Consider all bus numbers in range for the host pci bridge. */ - if (!pci_bus_is_root(bus) && - !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { - return NULL; - } - - /* try child bus */ - for (; bus; bus = sec) { - QLIST_FOREACH(sec, &bus->child, sibling) { - assert(!pci_bus_is_root(sec)); - if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { - return sec; - } - if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { - break; - } - } - } - - return NULL; -} - void pci_for_each_bus_depth_first(PCIBus *bus, void *(*begin)(PCIBus *bus, void *parent_state), void (*end)(PCIBus *bus, void *state), @@ -2130,135 +1790,6 @@ uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) return pci_find_capability_list(pdev, cap_id, NULL); }
-static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - PCIDevice *d = (PCIDevice *)dev; - const pci_class_desc *desc; - char ctxt[64]; - PCIIORegion *r; - int i, class; - - class = pci_get_word(d->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; - if (desc->desc) { - snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); - } else { - snprintf(ctxt, sizeof(ctxt), "Class %04x", class); - } - - monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " - "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, pci_bus_num(d->bus), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_ID)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (!r->size) - continue; - monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS - " [0x%"FMT_PCIBUS"]\n", - indent, "", - i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", - r->addr, r->addr + r->size - 1); - } -} - -static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) -{ - PCIDevice *d = (PCIDevice *)dev; - const char *name = NULL; - const pci_class_desc *desc = pci_class_descriptions; - int class = pci_get_word(d->config + PCI_CLASS_DEVICE); - - while (desc->desc && - (class & ~desc->fw_ign_bits) != - (desc->class & ~desc->fw_ign_bits)) { - desc++; - } - - if (desc->desc) { - name = desc->fw_name; - } - - if (name) { - pstrcpy(buf, len, name); - } else { - snprintf(buf, len, "pci%04x,%04x", - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID)); - } - - return buf; -} - -static char *pcibus_get_fw_dev_path(DeviceState *dev) -{ - PCIDevice *d = (PCIDevice *)dev; - char path[50], name[33]; - int off; - - off = snprintf(path, sizeof(path), "%s@%x", - pci_dev_fw_name(dev, name, sizeof name), - PCI_SLOT(d->devfn)); - if (PCI_FUNC(d->devfn)) - snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); - return g_strdup(path); -} - -static char *pcibus_get_dev_path(DeviceState *dev) -{ - 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. */ - const char *root_bus_path; - int root_bus_len; - char slot[] = ":SS.F"; - 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 = 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'; - - 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. */ - p = path + path_len; - for (t = d; t; t = t->bus->parent_dev) { - p -= slot_len; - s = snprintf(slot, sizeof slot, ":%02x.%x", - PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); - assert(s == slot_len); - memcpy(p, slot, slot_len); - } - - return path; -} - static int pci_qdev_find_recursive(PCIBus *bus, const char *id, PCIDevice **pdev) { @@ -2404,7 +1935,6 @@ static const TypeInfo pci_device_type_info = {
static void pci_register_types(void) { - type_register_static(&pci_bus_info); type_register_static(&pcie_bus_info); type_register_static(&pci_device_type_info); } diff --git a/hw/pci/pci_bus.c b/hw/pci/pci_bus.c new file mode 100644 index 0000000..d156194 --- /dev/null +++ b/hw/pci/pci_bus.c @@ -0,0 +1,491 @@ +/* + * PCI Bus + * + * Copyright (C) 2014 Red Hat Inc + * + * Authors: + * Marcel Apfelbaum marcel.a@redhat.com (split out from pci.c) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" +#include "monitor/monitor.h" + +typedef struct { + uint16_t class; + const char *desc; + const char *fw_name; + uint16_t fw_ign_bits; +} pci_class_desc; + +static const pci_class_desc pci_class_descriptions[] = { + { 0x0001, "VGA controller", "display"}, + { 0x0100, "SCSI controller", "scsi"}, + { 0x0101, "IDE controller", "ide"}, + { 0x0102, "Floppy controller", "fdc"}, + { 0x0103, "IPI controller", "ipi"}, + { 0x0104, "RAID controller", "raid"}, + { 0x0106, "SATA controller"}, + { 0x0107, "SAS controller"}, + { 0x0180, "Storage controller"}, + { 0x0200, "Ethernet controller", "ethernet"}, + { 0x0201, "Token Ring controller", "token-ring"}, + { 0x0202, "FDDI controller", "fddi"}, + { 0x0203, "ATM controller", "atm"}, + { 0x0280, "Network controller"}, + { 0x0300, "VGA controller", "display", 0x00ff}, + { 0x0301, "XGA controller"}, + { 0x0302, "3D controller"}, + { 0x0380, "Display controller"}, + { 0x0400, "Video controller", "video"}, + { 0x0401, "Audio controller", "sound"}, + { 0x0402, "Phone"}, + { 0x0403, "Audio controller", "sound"}, + { 0x0480, "Multimedia controller"}, + { 0x0500, "RAM controller", "memory"}, + { 0x0501, "Flash controller", "flash"}, + { 0x0580, "Memory controller"}, + { 0x0600, "Host bridge", "host"}, + { 0x0601, "ISA bridge", "isa"}, + { 0x0602, "EISA bridge", "eisa"}, + { 0x0603, "MC bridge", "mca"}, + { 0x0604, "PCI bridge", "pci-bridge"}, + { 0x0605, "PCMCIA bridge", "pcmcia"}, + { 0x0606, "NUBUS bridge", "nubus"}, + { 0x0607, "CARDBUS bridge", "cardbus"}, + { 0x0608, "RACEWAY bridge"}, + { 0x0680, "Bridge"}, + { 0x0700, "Serial port", "serial"}, + { 0x0701, "Parallel port", "parallel"}, + { 0x0800, "Interrupt controller", "interrupt-controller"}, + { 0x0801, "DMA controller", "dma-controller"}, + { 0x0802, "Timer", "timer"}, + { 0x0803, "RTC", "rtc"}, + { 0x0900, "Keyboard", "keyboard"}, + { 0x0901, "Pen", "pen"}, + { 0x0902, "Mouse", "mouse"}, + { 0x0A00, "Dock station", "dock", 0x00ff}, + { 0x0B00, "i386 cpu", "cpu", 0x00ff}, + { 0x0c00, "Fireware contorller", "fireware"}, + { 0x0c01, "Access bus controller", "access-bus"}, + { 0x0c02, "SSA controller", "ssa"}, + { 0x0c03, "USB controller", "usb"}, + { 0x0c04, "Fibre channel controller", "fibre-channel"}, + { 0x0c05, "SMBus"}, + { 0, NULL} +}; + +/* Whether a given bus number is in range of the secondary + * bus of the given bridge device. */ +static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) +{ + return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & + PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && + dev->config[PCI_SECONDARY_BUS] < bus_num && + bus_num <= dev->config[PCI_SUBORDINATE_BUS]; +} + +PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) +{ + PCIBus *sec; + + if (!bus) { + return NULL; + } + + if (pci_bus_num(bus) == bus_num) { + return bus; + } + + /* Consider all bus numbers in range for the host pci bridge. */ + if (!pci_bus_is_root(bus) && + !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { + return NULL; + } + + /* try child bus */ + for (; bus; bus = sec) { + QLIST_FOREACH(sec, &bus->child, sibling) { + assert(!pci_bus_is_root(sec)); + if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { + return sec; + } + if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { + break; + } + } + } + + return NULL; +} + +static const pci_class_desc *get_class_desc(int class) +{ + const pci_class_desc *desc; + + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) { + desc++; + } + + return desc; +} + +static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +{ + PciMemoryRegionList *head = NULL, *cur_item = NULL; + int i; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &dev->io_regions[i]; + PciMemoryRegionList *region; + + if (!r->size) { + continue; + } + + region = g_malloc0(sizeof(*region)); + region->value = g_malloc0(sizeof(*region->value)); + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + region->value->type = g_strdup("io"); + } else { + region->value->type = g_strdup("memory"); + region->value->has_prefetch = true; + region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); + region->value->has_mem_type_64 = true; + region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); + } + + region->value->bar = i; + region->value->address = r->addr; + region->value->size = r->size; + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = region; + } else { + cur_item->next = region; + cur_item = region; + } + } + + return head; +} + +static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + PciBridgeInfo *info; + + info = g_malloc0(sizeof(*info)); + + info->bus.number = dev->config[PCI_PRIMARY_BUS]; + info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; + info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; + + info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); + info->bus.io_range->base = pci_bridge_get_base(dev, + PCI_BASE_ADDRESS_SPACE_IO); + info->bus.io_range->limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); + + info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); + info->bus.memory_range->base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + info->bus.memory_range->limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + info->bus.prefetchable_range = + g_malloc0(sizeof(*info->bus.prefetchable_range)); + info->bus.prefetchable_range->base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + info->bus.prefetchable_range->limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); + if (child_bus) { + info->has_devices = true; + info->devices = qmp_query_pci_devices(child_bus, + dev->config[PCI_SECONDARY_BUS]); + } + } + + return info; +} + +static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + const pci_class_desc *desc; + PciDeviceInfo *info; + uint8_t type; + int class; + + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->slot = PCI_SLOT(dev->devfn); + info->function = PCI_FUNC(dev->devfn); + + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + info->class_info.q_class = class; + desc = get_class_desc(class); + if (desc->desc) { + info->class_info.has_desc = true; + info->class_info.desc = g_strdup(desc->desc); + } + + info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); + info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); + info->regions = qmp_query_pci_regions(dev); + info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); + + if (dev->config[PCI_INTERRUPT_PIN] != 0) { + info->has_irq = true; + info->irq = dev->config[PCI_INTERRUPT_LINE]; + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + info->has_pci_bridge = true; + info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); + } + + return info; +} + +PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) +{ + PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; + PCIDevice *dev; + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + dev = bus->devices[devfn]; + if (dev) { + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_device(dev, bus, bus_num); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + } + + return head; +} + + +static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) +{ + PCIDevice *d = (PCIDevice *)dev; + const pci_class_desc *desc; + char ctxt[64]; + PCIIORegion *r; + int i, class; + + class = pci_get_word(d->config + PCI_CLASS_DEVICE); + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) { + desc++; + } + if (desc->desc) { + snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); + } else { + snprintf(ctxt, sizeof(ctxt), "Class %04x", class); + } + + monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " + "pci id %04x:%04x (sub %04x:%04x)\n", + indent, "", ctxt, pci_bus_num(d->bus), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_ID)); + for (i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (!r->size) { + continue; + } + monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS + " [0x%"FMT_PCIBUS"]\n", + indent, "", + i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", + r->addr, r->addr + r->size - 1); + } +} + +static char *pcibus_get_dev_path(DeviceState *dev) +{ + 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. */ + const char *root_bus_path; + int root_bus_len; + char slot[] = ":SS.F"; + 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 = 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'; + + 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. */ + p = path + path_len; + for (t = d; t; t = t->bus->parent_dev) { + p -= slot_len; + s = snprintf(slot, sizeof slot, ":%02x.%x", + PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); + assert(s == slot_len); + memcpy(p, slot, slot_len); + } + + return path; +} + +static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) +{ + PCIDevice *d = (PCIDevice *)dev; + const char *name = NULL; + const pci_class_desc *desc = pci_class_descriptions; + int class = pci_get_word(d->config + PCI_CLASS_DEVICE); + + while (desc->desc && + (class & ~desc->fw_ign_bits) != + (desc->class & ~desc->fw_ign_bits)) { + desc++; + } + + if (desc->desc) { + name = desc->fw_name; + } + + if (name) { + pstrcpy(buf, len, name); + } else { + snprintf(buf, len, "pci%04x,%04x", + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID)); + } + + return buf; +} + +static char *pcibus_get_fw_dev_path(DeviceState *dev) +{ + PCIDevice *d = (PCIDevice *)dev; + char path[50], name[33]; + int off; + + off = snprintf(path, sizeof(path), "%s@%x", + pci_dev_fw_name(dev, name, sizeof name), + PCI_SLOT(d->devfn)); + if (PCI_FUNC(d->devfn)) { + snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); + } + return g_strdup(path); +} + +static const VMStateDescription vmstate_pcibus = { + .name = "PCIBUS", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32_EQUAL(nirq, PCIBus), + VMSTATE_VARRAY_INT32(irq_count, PCIBus, + nirq, 0, vmstate_info_int32, + int32_t), + VMSTATE_END_OF_LIST() + } +}; + +static void pci_bus_realize(BusState *qbus, Error **errp) +{ + PCIBus *bus = PCI_BUS(qbus); + + vmstate_register(NULL, -1, &vmstate_pcibus, bus); +} + +static void pci_bus_unrealize(BusState *qbus, Error **errp) +{ + PCIBus *bus = PCI_BUS(qbus); + + vmstate_unregister(NULL, &vmstate_pcibus, bus); +} + +/* + * Trigger pci bus reset under a given bus. + * Called via qbus_reset_all on RST# assert, after the devices + * have been reset qdev_reset_all-ed already. + */ +static void pcibus_reset(BusState *qbus) +{ + PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus); + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + if (bus->devices[i]) { + pci_do_device_reset(bus->devices[i]); + } + } + + for (i = 0; i < bus->nirq; i++) { + assert(bus->irq_count[i] == 0); + } +} + +static void pci_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->print_dev = pcibus_dev_print; + k->get_dev_path = pcibus_get_dev_path; + k->get_fw_dev_path = pcibus_get_fw_dev_path; + k->realize = pci_bus_realize; + k->unrealize = pci_bus_unrealize; + k->reset = pcibus_reset; +} + +static const TypeInfo pci_bus_info = { + .name = TYPE_PCI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(PCIBus), + .class_init = pci_bus_class_init, +}; + +static void pci_bus_register_types(void) +{ + type_register_static(&pci_bus_info); +} + +type_init(pci_bus_register_types) diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 0bb3cdb..f5847bc 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -23,6 +23,7 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "exec/address-spaces.h"
diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index d1d0847..8dd2ce3 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -30,6 +30,7 @@ #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "net/net.h" #include "sh7750_regs.h" #include "hw/ide.h" diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index a2f6d9e..f02e998 100644 --- a/hw/sh4/sh_pci.c +++ b/hw/sh4/sh_pci.c @@ -24,6 +24,7 @@ #include "hw/sysbus.h" #include "hw/sh4/sh.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "qemu/bswap.h" #include "exec/address-spaces.h" diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index be2d9b8..47077cd 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -337,8 +337,6 @@ typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin);
-#define TYPE_PCI_BUS "PCI" -#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) #define TYPE_PCIE_BUS "PCIE"
bool pci_bus_is_express(PCIBus *bus); @@ -370,6 +368,7 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus); void pci_device_set_intx_routing_notifier(PCIDevice *dev, PCIINTxRoutingNotifier notifier); void pci_device_reset(PCIDevice *dev); +void pci_do_device_reset(PCIDevice *dev);
PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_model, diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index fabaeee..ea427a3 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -1,6 +1,8 @@ #ifndef QEMU_PCI_BUS_H #define QEMU_PCI_BUS_H
+#include "hw/pci/pci.h" + /* * PCI Bus and Bridge datastructures. * @@ -8,6 +10,12 @@ * use accessor functions in pci.h, pci_bridge.h */
+PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); +PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); + +#define TYPE_PCI_BUS "PCI" +#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) + struct PCIBus { BusState qbus; PCIIOMMUFunc iommu_fn;