Allocate new segment for pxb-pcie host bridges in MCFG table, and reserve corresponding MCFG space for them. This allows user-defined pxb-pcie host bridges to be placed in different pci domain than q35 host.
The pci_host_bridges list is changed to be tail list to ensure the q35 host is always the first element when traversing the list, because q35 host is inserted beofre pxb-pcie hosts A few new callbacks are added to PCIBusClass to get domain number and max_bus property of a given PCIBus. Only pxb-pcie with a non-zero domain number will have an item in MCFG table, others will still reside in pci domain 0 under q35 host Signed-off-by: Zihan Yang <whois.zihan.y...@gmail.com> --- hw/i386/acpi-build.c | 109 +++++++++++++++++++++------- hw/pci-bridge/pci_expander_bridge.c | 51 +++++++++---- hw/pci/pci.c | 30 +++++++- include/hw/pci-bridge/pci_expander_bridge.h | 16 ++++ include/hw/pci/pci.h | 2 + include/hw/pci/pci_bus.h | 2 + include/hw/pci/pci_host.h | 2 +- 7 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 include/hw/pci-bridge/pci_expander_bridge.h diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e1ee8ae..9b49b0e 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -55,6 +55,7 @@ #include "hw/i386/ich9.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" +#include "hw/pci-bridge/pci_expander_bridge.h" #include "hw/i386/x86-iommu.h" #include "hw/acpi/aml-build.h" @@ -89,6 +90,10 @@ typedef struct AcpiMcfgInfo { uint64_t mcfg_base; uint32_t mcfg_size; + uint32_t domain_nr; + uint8_t start_bus; + uint8_t end_bus; + QTAILQ_ENTRY(AcpiMcfgInfo) next; } AcpiMcfgInfo; typedef struct AcpiPmInfo { @@ -119,6 +124,9 @@ typedef struct AcpiBuildPciBusHotplugState { bool pcihp_bridge_en; } AcpiBuildPciBusHotplugState; +static QTAILQ_HEAD(, AcpiMcfgInfo) mcfg = + QTAILQ_HEAD_INITIALIZER(mcfg); + static void init_common_fadt_data(Object *o, AcpiFadtData *data) { uint32_t io = object_property_get_uint(o, ACPI_PM_PROP_PM_IO_BASE, NULL); @@ -2427,18 +2435,28 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } static void -build_mcfg_q35(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info) +build_mcfg_q35(GArray *table_data, BIOSLinker *linker) { - AcpiTableMcfg *mcfg; + AcpiTableMcfg *mcfg_tbl; const char *sig; - int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); - - mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base); - /* Only a single allocation so no need to play with segments */ - mcfg->allocation[0].pci_segment = cpu_to_le16(0); - mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1); + int len, count = 0; + AcpiMcfgInfo *info; + + QTAILQ_FOREACH(info, &mcfg, next) { + count++; + } + + len = sizeof(*mcfg_tbl) + count * sizeof(mcfg_tbl->allocation[0]); + + mcfg_tbl = acpi_data_push(table_data, len); + + count = 0; + QTAILQ_FOREACH(info, &mcfg, next) { + mcfg_tbl->allocation[count].address = cpu_to_le64(info->mcfg_base); + mcfg_tbl->allocation[count].pci_segment = cpu_to_le16(info->domain_nr); + mcfg_tbl->allocation[count].start_bus_number = info->start_bus; + mcfg_tbl->allocation[count++].end_bus_number = info->end_bus; + } /* MCFG is used for ECAM which can be enabled or disabled by guest. * To avoid table size changes (which create migration issues), @@ -2446,13 +2464,13 @@ build_mcfg_q35(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info) * but set the signature to a reserved value in this case. * ACPI spec requires OSPMs to ignore such tables. */ - if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) { + if (QTAILQ_FIRST(&mcfg)->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) { /* Reserved signature: ignored by OSPM */ sig = "QEMU"; } else { sig = "MCFG"; } - build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL); + build_header(linker, table_data, (void *)mcfg_tbl, sig, len, 1, NULL, NULL); } /* @@ -2606,25 +2624,66 @@ struct AcpiBuildState { MemoryRegion *linker_mr; } AcpiBuildState; -static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) +static inline void cleanup_mcfg(void) +{ + AcpiMcfgInfo *cfg, *tmp; + + QTAILQ_FOREACH_SAFE (cfg, &mcfg, next, tmp) { + QTAILQ_REMOVE(&mcfg, cfg, next); + free(cfg); + } +} + +static bool acpi_get_mcfg(void) { Object *pci_host; QObject *o; + AcpiMcfgInfo *info; + uint8_t bus_nr = 0, end_bus = 255; + uint32_t domain_nr = 0, mcfg_size = MCH_HOST_BRIDGE_PCIEXBAR_MAX; + uint64_t mcfg_base = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; + PCIBus *bus; pci_host = acpi_get_i386_pci_host(); g_assert(pci_host); - o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); - if (!o) { - return false; + while (pci_host) { + if (object_dynamic_cast(pci_host, TYPE_PXB_PCIE_HOST)) { + /* we are in pxb-pcie, overwrite default value */ + bus = PCI_HOST_BRIDGE(pci_host)->bus; + domain_nr = pci_bus_domain_num(bus); + if (domain_nr == 0) { + pci_host = OBJECT(QTAILQ_NEXT(PCI_HOST_BRIDGE(pci_host), next)); + continue; + } + + o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); + assert(o); + mcfg_base = qnum_get_uint(qobject_to(QNum, o)); + qobject_unref(o); + + o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); + assert(o); + mcfg_size = qnum_get_uint(qobject_to(QNum, o)); + qobject_unref(o); + + bus_nr = pci_bus_num(bus); + domain_nr = pci_bus_domain_num(bus); + end_bus = pci_bus_max_bus(bus); + } + + info = g_new0(AcpiMcfgInfo, 1); + g_assert(info); + info->domain_nr = domain_nr; + info->start_bus = bus_nr; + info->end_bus = end_bus; + info->mcfg_base = mcfg_base; + info->mcfg_size = mcfg_size; + + QTAILQ_INSERT_TAIL(&mcfg, info, next); + pci_host = OBJECT(QTAILQ_NEXT(PCI_HOST_BRIDGE(pci_host), next)); } - mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o)); - qobject_unref(o); - o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); - assert(o); - mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o)); - qobject_unref(o); return true; } @@ -2637,7 +2696,6 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) unsigned facs, dsdt, rsdt, fadt; AcpiPmInfo pm; AcpiMiscInfo misc; - AcpiMcfgInfo mcfg; Range pci_hole, pci_hole64; uint8_t *u; size_t aml_len = 0; @@ -2718,10 +2776,11 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_slit(tables_blob, tables->linker); } } - if (acpi_get_mcfg(&mcfg)) { + if (acpi_get_mcfg()) { acpi_add_table(table_offsets, tables_blob); - build_mcfg_q35(tables_blob, tables->linker, &mcfg); + build_mcfg_q35(tables_blob, tables->linker); } + cleanup_mcfg(); if (x86_iommu_get_default()) { IommuType IOMMUType = x86_iommu_get_type(); if (IOMMUType == TYPE_AMD) { diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 20fec50..1e1999d 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -17,6 +17,7 @@ #include "hw/pci/pci_host.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci_bridge.h" +#include "hw/pci-bridge/pci_expander_bridge.h" #include "qemu/range.h" #include "qemu/error-report.h" #include "sysemu/numa.h" @@ -58,16 +59,6 @@ typedef struct PXBDev { uint8_t max_bus; /* max bus number to use(including this one) */ } PXBDev; -#define TYPE_PXB_PCIE_HOST "pxb-pcie-host" -#define PXB_PCIE_HOST_DEVICE(obj) \ - OBJECT_CHECK(PXBPCIEHost, (obj), TYPE_PXB_PCIE_HOST) - -typedef struct PXBPCIEHost { - /*< private >*/ - PCIExpressHost parent_obj; - /*< public >*/ -} PXBPCIEHost; - static PXBDev *convert_to_pxb(PCIDevice *dev) { return pci_bus_is_express(pci_get_bus(dev)) @@ -85,11 +76,17 @@ static int pxb_bus_num(PCIBus *bus) return pxb->bus_nr; } -static int pxb_domain_num(PCIBus *bus) +static int pxb_max_bus(PCIBus *bus) +{ + PXBDev *pxb = convert_to_pxb(bus->parent_dev); + + return pxb->max_bus; +} + +static uint32_t pxb_domain_num(PCIBus *bus) { PXBDev *pxb = convert_to_pxb(bus->parent_dev); - /* for pxb, this should always be zero */ return pxb->domain_nr; } @@ -110,8 +107,10 @@ static void pxb_bus_class_init(ObjectClass *class, void *data) PCIBusClass *pbc = PCI_BUS_CLASS(class); pbc->bus_num = pxb_bus_num; + pbc->max_bus = pxb_max_bus; pbc->is_root = pxb_is_root; pbc->numa_node = pxb_bus_numa_node; + pbc->domain_num = pxb_domain_num; } static const TypeInfo pxb_bus_info = { @@ -174,7 +173,17 @@ static void pxb_pcie_host_get_mmcfg_base(Object *obj, Visitor *v, const char *na { PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - visit_type_uint64(v, name, &e->size, errp); + visit_type_uint64(v, name, &e->base_addr, errp); +} + +static void pxb_pcie_host_set_mmcfg_base(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PXBPCIEHost *host = PXB_PCIE_HOST_DEVICE(obj); + uint64_t value; + + visit_type_uint64(v, name, &value, errp); + host->parent_obj.base_addr = value; } static void pxb_pcie_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, @@ -185,6 +194,16 @@ static void pxb_pcie_host_get_mmcfg_size(Object *obj, Visitor *v, const char *na visit_type_uint64(v, name, &e->size, errp); } +static void pxb_pcie_host_set_mmcfg_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PXBPCIEHost *host = PXB_PCIE_HOST_DEVICE(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + host->parent_obj.size = value; +} + static void pxb_pcie_host_initfn(Object *obj) { PCIHostState *phb = PCI_HOST_BRIDGE(obj); @@ -196,11 +215,13 @@ static void pxb_pcie_host_initfn(Object *obj) object_property_add(obj, PCIE_HOST_MCFG_BASE, "uint64", pxb_pcie_host_get_mmcfg_base, - NULL, NULL, NULL, NULL); + pxb_pcie_host_set_mmcfg_base, + NULL, NULL, NULL); object_property_add(obj, PCIE_HOST_MCFG_SIZE, "uint64", pxb_pcie_host_get_mmcfg_size, - NULL, NULL, NULL, NULL); + pxb_pcie_host_set_mmcfg_size, + NULL, NULL, NULL); } static void pxb_host_class_init(ObjectClass *class, void *data) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 80bc459..fe69672 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -142,6 +142,15 @@ static int pcibus_num(PCIBus *bus) return bus->parent_dev->config[PCI_SECONDARY_BUS]; } +/* return 0 unless user overwrite this callback */ +static uint32_t pcibus_domain_num(PCIBus *bus) { + return 0; +} + +static int pcibus_max_bus(PCIBus *bus) { + return 255; +} + static uint16_t pcibus_numa_node(PCIBus *bus) { return NUMA_NODE_UNASSIGNED; @@ -162,6 +171,8 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) pbc->is_root = pcibus_is_root; pbc->bus_num = pcibus_num; pbc->numa_node = pcibus_numa_node; + pbc->domain_num = pcibus_domain_num; + pbc->max_bus = pcibus_max_bus; } static const TypeInfo pci_bus_info = { @@ -196,7 +207,8 @@ 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; -static QLIST_HEAD(, PCIHostState) pci_host_bridges; +static QTAILQ_HEAD(, PCIHostState) pci_host_bridges = + QTAILQ_HEAD_INITIALIZER(pci_host_bridges); int pci_bar(PCIDevice *d, int reg) { @@ -330,7 +342,7 @@ static void pci_host_bus_register(DeviceState *host) { PCIHostState *host_bridge = PCI_HOST_BRIDGE(host); - QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next); + QTAILQ_INSERT_TAIL(&pci_host_bridges, host_bridge, next); } PCIBus *pci_device_root_bus(const PCIDevice *d) @@ -444,6 +456,16 @@ int pci_bus_num(PCIBus *s) return PCI_BUS_GET_CLASS(s)->bus_num(s); } +uint32_t pci_bus_domain_num(PCIBus *s) +{ + return PCI_BUS_GET_CLASS(s)->domain_num(s); +} + +int pci_bus_max_bus(PCIBus *s) +{ + return PCI_BUS_GET_CLASS(s)->max_bus(s); +} + int pci_bus_numa_node(PCIBus *bus) { return PCI_BUS_GET_CLASS(bus)->numa_node(bus); @@ -1798,7 +1820,7 @@ PciInfoList *qmp_query_pci(Error **errp) PciInfoList *info, *head = NULL, *cur_item = NULL; PCIHostState *host_bridge; - QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { + QTAILQ_FOREACH(host_bridge, &pci_host_bridges, next) { info = g_malloc0(sizeof(*info)); info->value = qmp_query_pci_bus(host_bridge->bus, pci_bus_num(host_bridge->bus)); @@ -2493,7 +2515,7 @@ int pci_qdev_find_device(const char *id, PCIDevice **pdev) PCIHostState *host_bridge; int rc = -ENODEV; - QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { + QTAILQ_FOREACH(host_bridge, &pci_host_bridges, next) { int tmp = pci_qdev_find_recursive(host_bridge->bus, id, pdev); if (!tmp) { rc = 0; diff --git a/include/hw/pci-bridge/pci_expander_bridge.h b/include/hw/pci-bridge/pci_expander_bridge.h new file mode 100644 index 0000000..e017c62 --- /dev/null +++ b/include/hw/pci-bridge/pci_expander_bridge.h @@ -0,0 +1,16 @@ +#ifndef HW_PCI_EXPANDER_H +#define HW_PCI_EXPANDER_H + +#include "hw/pci/pcie_host.h" + +#define TYPE_PXB_PCIE_HOST "pxb-pcie-host" +#define PXB_PCIE_HOST_DEVICE(obj) \ + OBJECT_CHECK(PXBPCIEHost, (obj), TYPE_PXB_PCIE_HOST) + +typedef struct PXBPCIEHost { + /*< private >*/ + PCIExpressHost parent_obj; + /*< public >*/ +} PXBPCIEHost; + +#endif diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 990d6fc..b127fcf 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -436,6 +436,8 @@ static inline PCIBus *pci_get_bus(const PCIDevice *dev) return PCI_BUS(qdev_get_parent_bus(DEVICE(dev))); } int pci_bus_num(PCIBus *s); +uint32_t pci_bus_domain_num(PCIBus *s); +int pci_bus_max_bus(PCIBus *s); static inline int pci_dev_bus_num(const PCIDevice *dev) { return pci_bus_num(pci_get_bus(dev)); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index b7da8f5..faad155 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -15,7 +15,9 @@ typedef struct PCIBusClass { bool (*is_root)(PCIBus *bus); int (*bus_num)(PCIBus *bus); + int (*max_bus)(PCIBus *bus); uint16_t (*numa_node)(PCIBus *bus); + uint32_t (*domain_num)(PCIBus *bus); } PCIBusClass; struct PCIBus { diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index ba31595..a5617cf 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -47,7 +47,7 @@ struct PCIHostState { uint32_t config_reg; PCIBus *bus; - QLIST_ENTRY(PCIHostState) next; + QTAILQ_ENTRY(PCIHostState) next; }; typedef struct PCIHostBridgeClass { -- 2.7.4