diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 57f8a3762b..bb98fb2e46 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -23,6 +23,9 @@ #define TYPE_PXB_BUS "pxb-bus" #define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS) +#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" +#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS) + typedef struct PXBBus { /*< private >*/ PCIBus parent_obj; @@ -34,6 +37,9 @@ typedef struct PXBBus { #define TYPE_PXB_DEVICE "pxb" #define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE) +#define TYPE_PXB_PCIE_DEVICE "pxb-pcie" +#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE) + typedef struct PXBDev { /*< private >*/ PCIDevice parent_obj; @@ -43,13 +49,18 @@ typedef struct PXBDev { uint16_t numa_node; } PXBDev; +static PXBDev *convert_to_pxb(PCIDevice *dev) +{ + return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev); +} + static GList *pxb_dev_list; #define TYPE_PXB_HOST "pxb-host" static int pxb_bus_num(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->bus_nr; } @@ -61,7 +72,7 @@ static bool pxb_is_root(PCIBus *bus) static uint16_t pxb_bus_numa_node(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->numa_node; } @@ -82,10 +93,18 @@ static const TypeInfo pxb_bus_info = { .class_init = pxb_bus_class_init, }; +static const TypeInfo pxb_pcie_bus_info = { + .name = TYPE_PXB_PCIE_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(PXBBus), + .class_init = pxb_bus_class_init, +}; + static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - PXBBus *bus = PXB_BUS(rootbus); + PXBBus *bus = pci_bus_is_express(rootbus) ? + PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus); snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus)); return bus->bus_path; @@ -103,7 +122,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) pxb_host = PCI_HOST_BRIDGE(dev); pxb_bus = pxb_host->bus; - pxb_dev = PXB_DEV(pxb_bus->parent_dev); + pxb_dev = convert_to_pxb(pxb_bus->parent_dev); position = g_list_index(pxb_dev_list, pxb_dev); assert(position >= 0); @@ -193,10 +212,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) 0; } -static int pxb_dev_initfn(PCIDevice *dev) +static int pxb_dev_init_common(PCIDevice *dev, bool pcie) { - PXBDev *pxb = PXB_DEV(dev); - DeviceState *ds, *bds; + PXBDev *pxb = convert_to_pxb(dev); + DeviceState *ds, *bds = NULL; PCIBus *bus; const char *dev_name = NULL; @@ -211,18 +230,21 @@ static int pxb_dev_initfn(PCIDevice *dev) } ds = qdev_create(NULL, TYPE_PXB_HOST); - bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + if (pcie) { + bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS); + } else { + bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + bds = qdev_create(BUS(bus), "pci-bridge"); + bds->id = dev_name; + qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); + qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); + } bus->parent_dev = dev; bus->address_space_mem = dev->bus->address_space_mem; bus->address_space_io = dev->bus->address_space_io; bus->map_irq = pxb_map_irq_fn; - bds = qdev_create(BUS(bus), "pci-bridge"); - bds->id = dev_name; - qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); - qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); - PCI_HOST_BRIDGE(ds)->bus = bus; if (pxb_register_bus(dev, bus)) { @@ -230,7 +252,9 @@ static int pxb_dev_initfn(PCIDevice *dev) } qdev_init_nofail(ds); - qdev_init_nofail(bds); + if (bds) { + qdev_init_nofail(bds); + } pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); @@ -240,9 +264,19 @@ static int pxb_dev_initfn(PCIDevice *dev) return 0; } +static int pxb_dev_initfn(PCIDevice *dev) +{ + if (pci_bus_is_express(dev->bus)) { + error_report("pxb devices cannot reside on a PCIe bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, false); +} + static void pxb_dev_exitfn(PCIDevice *pci_dev) { - PXBDev *pxb = PXB_DEV(pci_dev); + PXBDev *pxb = convert_to_pxb(pci_dev); pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } @@ -276,11 +310,45 @@ static const TypeInfo pxb_dev_info = { .class_init = pxb_dev_class_init, }; +static int pxb_pcie_dev_initfn(PCIDevice *dev) +{ + if (!pci_bus_is_express(dev->bus)) { + error_report("pxb-pcie devices cannot reside on a PCI bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, true); +} + +static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pxb_pcie_dev_initfn; + k->exit = pxb_dev_exitfn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE; + k->class_id = PCI_CLASS_BRIDGE_HOST; + + dc->desc = "PCI Express Expander Bridge"; + dc->props = pxb_dev_properties; +} + +static const TypeInfo pxb_pcie_dev_info = { + .name = TYPE_PXB_PCIE_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PXBDev), + .class_init = pxb_pcie_dev_class_init, +}; + static void pxb_register_types(void) { type_register_static(&pxb_bus_info); + type_register_static(&pxb_pcie_bus_info); type_register_static(&pxb_host_info); type_register_static(&pxb_dev_info); + type_register_static(&pxb_pcie_dev_info); } type_init(pxb_register_types) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 379b6e1a45..dedf277627 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -93,6 +93,7 @@ #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 #define PCI_DEVICE_ID_REDHAT_PXB 0x0009 #define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a +#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64