From a35fe226558ac85436ea01af8977f1834927f53f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:47 +0300 Subject: hw/pci: introduce pcie-pci-bridge device Introduce a new PCIExpress-to-PCI Bridge device, which is a hot-pluggable PCI Express device and supports devices hot-plug with SHPC. This device is intended to replace the DMI-to-PCI Bridge. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/pci/pci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/hw/pci') diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 8bb6449dd7..aa7ef9cf69 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -100,6 +100,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b #define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c #define PCI_DEVICE_ID_REDHAT_XHCI 0x000d +#define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 -- cgit 1.4.1 From 70e1ee59bb9490d9ac529e96820a03b346086ca1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:48 +0300 Subject: hw/pci: introduce bridge-only vendor-specific capability to provide some hints to firmware On PCI init PCI bridges may need some extra info about bus number, IO, memory and prefetchable memory to reserve. QEMU can provide this with a special vendor-specific PCI capability. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci_bridge.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/pci/pci_bridge.h | 25 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include/hw/pci') diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 720119b21a..17feae5ed8 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -408,6 +408,52 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, br->bus_name = bus_name; } + +int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, + uint32_t bus_reserve, uint64_t io_reserve, + uint32_t mem_non_pref_reserve, + uint32_t mem_pref_32_reserve, + uint64_t mem_pref_64_reserve, + Error **errp) +{ + if (mem_pref_32_reserve != (uint32_t)-1 && + mem_pref_64_reserve != (uint64_t)-1) { + error_setg(errp, + "PCI resource reserve cap: PREF32 and PREF64 conflict"); + return -EINVAL; + } + + if (bus_reserve == (uint32_t)-1 && + io_reserve == (uint64_t)-1 && + mem_non_pref_reserve == (uint32_t)-1 && + mem_pref_32_reserve == (uint32_t)-1 && + mem_pref_64_reserve == (uint64_t)-1) { + return 0; + } + + size_t cap_len = sizeof(PCIBridgeQemuCap); + PCIBridgeQemuCap cap = { + .len = cap_len, + .type = REDHAT_PCI_CAP_RESOURCE_RESERVE, + .bus_res = bus_reserve, + .io = io_reserve, + .mem = mem_non_pref_reserve, + .mem_pref_32 = mem_pref_32_reserve, + .mem_pref_64 = mem_pref_64_reserve + }; + + int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, + cap_offset, cap_len, errp); + if (offset < 0) { + return offset; + } + + memcpy(dev->config + offset + PCI_CAP_FLAGS, + (char *)&cap + PCI_CAP_FLAGS, + cap_len - PCI_CAP_FLAGS); + return 0; +} + static const TypeInfo pci_bridge_type_info = { .name = TYPE_PCI_BRIDGE, .parent = TYPE_PCI_DEVICE, diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index ff7cbaa227..1acadc2c15 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -67,4 +67,29 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, #define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ #define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ +typedef struct PCIBridgeQemuCap { + uint8_t id; /* Standard PCI capability header field */ + uint8_t next; /* Standard PCI capability header field */ + uint8_t len; /* Standard PCI vendor-specific capability header field */ + uint8_t type; /* Red Hat vendor-specific capability type. + Types are defined with REDHAT_PCI_CAP_ prefix */ + + uint32_t bus_res; /* Minimum number of buses to reserve */ + uint64_t io; /* IO space to reserve */ + uint32_t mem; /* Non-prefetchable memory to reserve */ + /* At most one of the following two fields may be set to a value + * different from -1 */ + uint32_t mem_pref_32; /* Prefetchable memory to reserve (32-bit MMIO) */ + uint64_t mem_pref_64; /* Prefetchable memory to reserve (64-bit MMIO) */ +} PCIBridgeQemuCap; + +#define REDHAT_PCI_CAP_RESOURCE_RESERVE 1 + +int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, + uint32_t bus_reserve, uint64_t io_reserve, + uint32_t mem_non_pref_reserve, + uint32_t mem_pref_32_reserve, + uint64_t mem_pref_64_reserve, + Error **errp); + #endif /* QEMU_PCI_BRIDGE_H */ -- cgit 1.4.1 From 226263fb5cdaa4a4a95f1680fabbc9dd2123fd67 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Fri, 18 Aug 2017 02:36:49 +0300 Subject: hw/pci: add QEMU-specific PCI capability to the Generic PCI Express Root Port To enable hotplugging of a newly created pcie-pci-bridge, we need to tell firmware (e.g. SeaBIOS) to reserve additional buses or IO/MEM/PREF space for pcie-root-port. Additional bus reservation allows us to hotplug pcie-pci-bridge into this root port. The number of buses and IO/MEM/PREF space to reserve are provided to the device via a corresponding property, and to the firmware via new PCI capability. The properties' default values are -1 to keep default behavior unchanged. Signed-off-by: Aleksandr Bezzubikov Reviewed-by: Marcel Apfelbaum Tested-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/gen_pcie_root_port.c | 36 ++++++++++++++++++++++++++++++++++++ include/hw/pci/pcie_port.h | 1 + 2 files changed, 37 insertions(+) (limited to 'include/hw/pci') diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index cb694d6da5..ed03ffc764 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -16,6 +16,8 @@ #include "hw/pci/pcie_port.h" #define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port" +#define GEN_PCIE_ROOT_PORT(obj) \ + OBJECT_CHECK(GenPCIERootPort, (obj), TYPE_GEN_PCIE_ROOT_PORT) #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 @@ -26,6 +28,13 @@ typedef struct GenPCIERootPort { /*< public >*/ bool migrate_msix; + + /* additional resources to reserve on firmware init */ + uint32_t bus_reserve; + uint64_t io_reserve; + uint64_t mem_reserve; + uint64_t pref32_reserve; + uint64_t pref64_reserve; } GenPCIERootPort; static uint8_t gen_rp_aer_vector(const PCIDevice *d) @@ -60,6 +69,24 @@ static bool gen_rp_test_migrate_msix(void *opaque, int version_id) return rp->migrate_msix; } +static void gen_rp_realize(DeviceState *dev, Error **errp) +{ + PCIDevice *d = PCI_DEVICE(dev); + GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); + + rpc->parent_realize(dev, errp); + + int rc = pci_bridge_qemu_reserve_cap_init(d, 0, grp->bus_reserve, + grp->io_reserve, grp->mem_reserve, grp->pref32_reserve, + grp->pref64_reserve, errp); + + if (rc < 0) { + rpc->parent_class.exit(d); + return; + } +} + static const VMStateDescription vmstate_rp_dev = { .name = "pcie-root-port", .version_id = 1, @@ -78,6 +105,11 @@ static const VMStateDescription vmstate_rp_dev = { static Property gen_rp_props[] = { DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), + DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, bus_reserve, -1), + DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, io_reserve, -1), + DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, mem_reserve, -1), + DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, pref32_reserve, -1), + DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, pref64_reserve, -1), DEFINE_PROP_END_OF_LIST() }; @@ -92,6 +124,10 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Express Root Port"; dc->vmsd = &vmstate_rp_dev; dc->props = gen_rp_props; + + rpc->parent_realize = dc->realize; + dc->realize = gen_rp_realize; + rpc->aer_vector = gen_rp_aer_vector; rpc->interrupts_init = gen_rp_interrupts_init; rpc->interrupts_uninit = gen_rp_interrupts_uninit; diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 13332668e8..0736014bfd 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -65,6 +65,7 @@ void pcie_chassis_del_slot(PCIESlot *s); typedef struct PCIERootPortClass { PCIDeviceClass parent_class; + DeviceRealize parent_realize; uint8_t (*aer_vector)(const PCIDevice *dev); int (*interrupts_init)(PCIDevice *dev, Error **errp); -- cgit 1.4.1 From 8b8849844fd6a31956e934885f2a7ae9ac1a95d8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 16 Jul 2017 21:27:34 +0100 Subject: pci: add reserved slot check to do_pci_register_device() Add a new slot_reserved_mask bitmask to PCIBus indicating whether or not each PCI slot on the bus is reserved. Ensure that it is initialised to zero to maintain the existing behaviour that all slots are available by default, and add the additional check with appropriate error reporting to do_pci_register_device(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 18 +++++++++++++++--- include/hw/pci/pci_bus.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include/hw/pci') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 002e66920c..21e203b056 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -373,6 +373,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, { assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; + bus->slot_reserved_mask = 0x0; bus->address_space_mem = address_space_mem; bus->address_space_io = address_space_io; @@ -958,6 +959,11 @@ static bool pci_bus_devfn_available(PCIBus *bus, int devfn) return !(bus->devices[devfn]); } +static bool pci_bus_devfn_reserved(PCIBus *bus, int devfn) +{ + return bus->slot_reserved_mask & (1UL << PCI_SLOT(devfn)); +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn, @@ -981,14 +987,20 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); devfn += PCI_FUNC_MAX) { - if (pci_bus_devfn_available(bus, devfn)) { + if (pci_bus_devfn_available(bus, devfn) && + !pci_bus_devfn_reserved(bus, devfn)) { goto found; } } - error_setg(errp, "PCI: no slot/function available for %s, all in use", - name); + error_setg(errp, "PCI: no slot/function available for %s, all in use " + "or reserved", name); return NULL; found: ; + } else if (pci_bus_devfn_reserved(bus, devfn)) { + error_setg(errp, "PCI: slot %d function %d not available for %s," + " reserved", + PCI_SLOT(devfn), PCI_FUNC(devfn), name); + return NULL; } else if (!pci_bus_devfn_available(bus, devfn)) { error_setg(errp, "PCI: slot %d function %d not available for %s," " in use by %s", diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 5484a9b5c5..bc34fd0017 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -23,6 +23,7 @@ struct PCIBus { PCIIOMMUFunc iommu_fn; void *iommu_opaque; uint8_t devfn_min; + uint32_t slot_reserved_mask; pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; pci_route_irq_fn route_intx_to_irq; -- cgit 1.4.1