Scan the PCI devices to find bridge and set PCI_SECONDARY_BUS and
PCI_SUBORDINATE_BUS (algorithm from seabios)
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---
include/hw/pci/pci_bridge.h | 8 +++
tests/qtest/libqos/pci.c | 118 ++++++++++++++++++++++++++++++++++++
tests/qtest/libqos/pci.h | 1 +
3 files changed, 127 insertions(+)
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
index a94d350034bf..30691a6e5728 100644
--- a/include/hw/pci/pci_bridge.h
+++ b/include/hw/pci/pci_bridge.h
@@ -138,6 +138,7 @@ typedef struct PCIBridgeQemuCap {
uint64_t mem_pref_64; /* Prefetchable memory to reserve (64-bit MMIO) */
} PCIBridgeQemuCap;
+#define REDHAT_PCI_CAP_TYPE_OFFSET 3
#define REDHAT_PCI_CAP_RESOURCE_RESERVE 1
/*
@@ -152,6 +153,13 @@ typedef struct PCIResReserve {
uint64_t mem_pref_64;
} PCIResReserve;
+#define REDHAT_PCI_CAP_RES_RESERVE_BUS_RES 4
+#define REDHAT_PCI_CAP_RES_RESERVE_IO 8
+#define REDHAT_PCI_CAP_RES_RESERVE_MEM 16
+#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_32 20
+#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_64 24
+#define REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE 32
+
int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
PCIResReserve res_reserve, Error **errp);
diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c
index e1e96189c821..3f0b18f4750b 100644
--- a/tests/qtest/libqos/pci.c
+++ b/tests/qtest/libqos/pci.c
@@ -13,6 +13,8 @@
#include "qemu/osdep.h"
#include "pci.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_regs.h"
#include "qemu/host-utils.h"
#include "qgraph.h"
@@ -99,6 +101,122 @@ void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
g_assert(!addr->device_id || device_id == addr->device_id);
}
+static uint8_t qpci_find_resource_reserve_capability(QPCIDevice *dev)
+{
+ uint16_t device_id;
+ uint8_t cap = 0;
+
+ if (qpci_config_readw(dev, PCI_VENDOR_ID) != PCI_VENDOR_ID_REDHAT) {
+ return 0;
+ }
+
+ device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
+
+ if (device_id != PCI_DEVICE_ID_REDHAT_PCIE_RP &&
+ device_id != PCI_DEVICE_ID_REDHAT_BRIDGE) {
+ return 0;
+ }
+
+ do {
+ cap = qpci_find_capability(dev, PCI_CAP_ID_VNDR, cap);
+ } while (cap &&
+ qpci_config_readb(dev, cap + REDHAT_PCI_CAP_TYPE_OFFSET) !=
+ REDHAT_PCI_CAP_RESOURCE_RESERVE);
+ if (cap) {
+ uint8_t cap_len = qpci_config_readb(dev, cap + PCI_CAP_FLAGS);
+ if (cap_len < REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE) {
+ return 0;
+ }
+ }
+ return cap;
+}
+
+static void qpci_secondary_buses_rec(QPCIBus *qbus, int bus, int *pci_bus)
+{
+ QPCIDevice *dev;
+ uint16_t class;
+ uint8_t pribus, secbus, subbus;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ dev = qpci_device_find(qbus, QPCI_DEVFN(bus + i, 0));
+ if (dev == NULL) {
+ continue;
+ }
+ class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
+ if (class == PCI_CLASS_BRIDGE_PCI) {
+ qpci_config_writeb(dev, PCI_SECONDARY_BUS, 255);
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 0);
+ }
+ g_free(dev);
+ }
+
+ for (i = 0; i < 32; i++) {
+ dev = qpci_device_find(qbus, QPCI_DEVFN(bus + i, 0));
+ if (dev == NULL) {
+ continue;
+ }
+ class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
+ if (class != PCI_CLASS_BRIDGE_PCI) {
+ continue;
+ }
+
+ pribus = qpci_config_readb(dev, PCI_PRIMARY_BUS);
+ if (pribus != bus) {
+ qpci_config_writeb(dev, PCI_PRIMARY_BUS, bus);
+ }
+
+ secbus = qpci_config_readb(dev, PCI_SECONDARY_BUS);
+ (*pci_bus)++;
+ if (*pci_bus != secbus) {
+ secbus = *pci_bus;
+ qpci_config_writeb(dev, PCI_SECONDARY_BUS, secbus);
+ }
+
+ subbus = qpci_config_readb(dev, PCI_SUBORDINATE_BUS);
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 255);
+
+ qpci_secondary_buses_rec(qbus, secbus << 5, pci_bus);
+
+ if (subbus != *pci_bus) {
+ uint8_t res_bus = *pci_bus;
+ uint8_t cap = qpci_find_resource_reserve_capability(dev);
+
+ if (cap) {
+ uint32_t tmp_res_bus;
+
+ tmp_res_bus = qpci_config_readl(dev, cap +
+ REDHAT_PCI_CAP_RES_RESERVE_BUS_RES);
+ if (tmp_res_bus != (uint32_t)-1) {
+ res_bus = tmp_res_bus & 0xFF;
+ if ((uint8_t)(res_bus + secbus) < secbus ||
+ (uint8_t)(res_bus + secbus) < res_bus) {
+ res_bus = 0;
+ }
+ if (secbus + res_bus > *pci_bus) {
+ res_bus = secbus + res_bus;
+ }
+ }
+ }
+ subbus = res_bus;
+ *pci_bus = res_bus;
+ }
+
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, subbus);
+ g_free(dev);
+ }
+}
+
+int qpci_secondary_buses_init(QPCIBus *bus)
+{
+ int last_bus = 0;
+
+ qpci_secondary_buses_rec(bus, 0, &last_bus);
+
+ return last_bus;
+}
+
+
void qpci_device_enable(QPCIDevice *dev)
{
uint16_t cmd;
diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h
index ee64fdecbda8..becb800f9e6a 100644
--- a/tests/qtest/libqos/pci.h
+++ b/tests/qtest/libqos/pci.h
@@ -81,6 +81,7 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
void *data);
QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
+int qpci_secondary_buses_init(QPCIBus *bus);
bool qpci_has_buggy_msi(QPCIDevice *dev);
bool qpci_check_buggy_msi(QPCIDevice *dev);
--
2.33.1
On 06/12/2021 23.20, Laurent Vivier wrote:
> Scan the PCI devices to find bridge and set PCI_SECONDARY_BUS and
> PCI_SUBORDINATE_BUS (algorithm from seabios)
>
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> ---
> include/hw/pci/pci_bridge.h | 8 +++
> tests/qtest/libqos/pci.c | 118 ++++++++++++++++++++++++++++++++++++
> tests/qtest/libqos/pci.h | 1 +
> 3 files changed, 127 insertions(+)
>
> diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
> index a94d350034bf..30691a6e5728 100644
> --- a/include/hw/pci/pci_bridge.h
> +++ b/include/hw/pci/pci_bridge.h
> @@ -138,6 +138,7 @@ typedef struct PCIBridgeQemuCap {
> uint64_t mem_pref_64; /* Prefetchable memory to reserve (64-bit MMIO) */
> } PCIBridgeQemuCap;
>
> +#define REDHAT_PCI_CAP_TYPE_OFFSET 3
> #define REDHAT_PCI_CAP_RESOURCE_RESERVE 1
>
> /*
> @@ -152,6 +153,13 @@ typedef struct PCIResReserve {
> uint64_t mem_pref_64;
> } PCIResReserve;
>
> +#define REDHAT_PCI_CAP_RES_RESERVE_BUS_RES 4
> +#define REDHAT_PCI_CAP_RES_RESERVE_IO 8
> +#define REDHAT_PCI_CAP_RES_RESERVE_MEM 16
> +#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_32 20
> +#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_64 24
> +#define REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE 32
> +
> int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
> PCIResReserve res_reserve, Error **errp);
>
> diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c
> index e1e96189c821..3f0b18f4750b 100644
> --- a/tests/qtest/libqos/pci.c
> +++ b/tests/qtest/libqos/pci.c
> @@ -13,6 +13,8 @@
> #include "qemu/osdep.h"
> #include "pci.h"
>
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_bridge.h"
> #include "hw/pci/pci_regs.h"
> #include "qemu/host-utils.h"
> #include "qgraph.h"
> @@ -99,6 +101,122 @@ void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
> g_assert(!addr->device_id || device_id == addr->device_id);
> }
>
> +static uint8_t qpci_find_resource_reserve_capability(QPCIDevice *dev)
> +{
> + uint16_t device_id;
> + uint8_t cap = 0;
> +
> + if (qpci_config_readw(dev, PCI_VENDOR_ID) != PCI_VENDOR_ID_REDHAT) {
> + return 0;
> + }
> +
> + device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
> +
> + if (device_id != PCI_DEVICE_ID_REDHAT_PCIE_RP &&
> + device_id != PCI_DEVICE_ID_REDHAT_BRIDGE) {
> + return 0;
> + }
> +
> + do {
> + cap = qpci_find_capability(dev, PCI_CAP_ID_VNDR, cap);
> + } while (cap &&
> + qpci_config_readb(dev, cap + REDHAT_PCI_CAP_TYPE_OFFSET) !=
> + REDHAT_PCI_CAP_RESOURCE_RESERVE);
> + if (cap) {
> + uint8_t cap_len = qpci_config_readb(dev, cap + PCI_CAP_FLAGS);
> + if (cap_len < REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE) {
> + return 0;
> + }
> + }
> + return cap;
> +}
> +
> +static void qpci_secondary_buses_rec(QPCIBus *qbus, int bus, int *pci_bus)
> +{
> + QPCIDevice *dev;
> + uint16_t class;
> + uint8_t pribus, secbus, subbus;
> + int i;
<nit>I'd maybe use a better name instead of "i" here.</nit>
> + for (i = 0; i < 32; i++) {
> + dev = qpci_device_find(qbus, QPCI_DEVFN(bus + i, 0));
> + if (dev == NULL) {
> + continue;
> + }
> + class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
> + if (class == PCI_CLASS_BRIDGE_PCI) {
> + qpci_config_writeb(dev, PCI_SECONDARY_BUS, 255);
> + qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 0);
> + }
> + g_free(dev);
> + }
> +
> + for (i = 0; i < 32; i++) {
> + dev = qpci_device_find(qbus, QPCI_DEVFN(bus + i, 0));
> + if (dev == NULL) {
> + continue;
> + }
> + class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
> + if (class != PCI_CLASS_BRIDGE_PCI) {
> + continue;
> + }
> +
> + pribus = qpci_config_readb(dev, PCI_PRIMARY_BUS);
> + if (pribus != bus) {
> + qpci_config_writeb(dev, PCI_PRIMARY_BUS, bus);
> + }
> +
> + secbus = qpci_config_readb(dev, PCI_SECONDARY_BUS);
> + (*pci_bus)++;
> + if (*pci_bus != secbus) {
> + secbus = *pci_bus;
> + qpci_config_writeb(dev, PCI_SECONDARY_BUS, secbus);
> + }
> +
> + subbus = qpci_config_readb(dev, PCI_SUBORDINATE_BUS);
> + qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 255);
> +
> + qpci_secondary_buses_rec(qbus, secbus << 5, pci_bus);
> +
> + if (subbus != *pci_bus) {
> + uint8_t res_bus = *pci_bus;
> + uint8_t cap = qpci_find_resource_reserve_capability(dev);
> +
> + if (cap) {
> + uint32_t tmp_res_bus;
> +
> + tmp_res_bus = qpci_config_readl(dev, cap +
> + REDHAT_PCI_CAP_RES_RESERVE_BUS_RES);
> + if (tmp_res_bus != (uint32_t)-1) {
> + res_bus = tmp_res_bus & 0xFF;
> + if ((uint8_t)(res_bus + secbus) < secbus ||
> + (uint8_t)(res_bus + secbus) < res_bus) {
> + res_bus = 0;
> + }
> + if (secbus + res_bus > *pci_bus) {
> + res_bus = secbus + res_bus;
> + }
> + }
> + }
> + subbus = res_bus;
> + *pci_bus = res_bus;
> + }
> +
> + qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, subbus);
> + g_free(dev);
> + }
> +}
> +
> +int qpci_secondary_buses_init(QPCIBus *bus)
> +{
> + int last_bus = 0;
> +
> + qpci_secondary_buses_rec(bus, 0, &last_bus);
> +
> + return last_bus;
> +}
> +
> +
> void qpci_device_enable(QPCIDevice *dev)
> {
> uint16_t cmd;
> diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h
> index ee64fdecbda8..becb800f9e6a 100644
> --- a/tests/qtest/libqos/pci.h
> +++ b/tests/qtest/libqos/pci.h
> @@ -81,6 +81,7 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
> void *data);
> QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
> void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
> +int qpci_secondary_buses_init(QPCIBus *bus);
>
> bool qpci_has_buggy_msi(QPCIDevice *dev);
> bool qpci_check_buggy_msi(QPCIDevice *dev);
>
Acked-by: Thomas Huth <thuth@redhat.com>
© 2016 - 2026 Red Hat, Inc.