diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index 6963a0271..a850b06d6 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -66,6 +66,7 @@ #include "virtio.h" #include "pm_vuart.h" #include "log.h" +#include "pci_util.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -1122,5 +1123,6 @@ fail: create_fail: uninit_hugetlb(); deinit_loggers(); + clean_pci_cache(); exit(ret); } diff --git a/devicemodel/hw/pci/pci_util.c b/devicemodel/hw/pci/pci_util.c index 4dd588a0f..9055cb271 100644 --- a/devicemodel/hw/pci/pci_util.c +++ b/devicemodel/hw/pci/pci_util.c @@ -18,22 +18,6 @@ #define MAX_LEN (PCI_BUSMAX + 1) -struct pci_device_info { - bool is_bridge; - int primary_bus; - int secondary_bus; - int subordinate_bus; - uint16_t bdf; - struct pci_device_info *parent; /* pointer to its parent bridge */ - struct pci_device_info *clist; /* children list */ - - /* cache of all pci devices - * FIXME: remove PCI_DEVICE_Q. To cleanup pci device cache: - * remove children, then remove parents - */ - TAILQ_ENTRY(pci_device_info) PCI_DEVICE_Q; -}; - TAILQ_HEAD(ALL_PCI_DEVICES, pci_device_info); static struct ALL_PCI_DEVICES pci_device_q; @@ -196,6 +180,9 @@ int scan_pci(void) struct pci_device *dev; int i; + if (pci_scanned) + return 0; + TAILQ_INIT(&pci_device_q); pci_scanned = 1; @@ -243,7 +230,11 @@ int scan_pci(void) done: if (error < 0) + { + pr_err("%s: failed to build pci hierarchy.\n", __func__); clean_pci_cache(); + pci_scanned = false; + } pci_iterator_destroy(iter); diff --git a/devicemodel/hw/pci/ptm.c b/devicemodel/hw/pci/ptm.c index d03ebad67..5f481d0cf 100644 --- a/devicemodel/hw/pci/ptm.c +++ b/devicemodel/hw/pci/ptm.c @@ -106,10 +106,10 @@ add_vroot_port(struct vmctx *ctx, struct passthru_dev *ptdev, struct pci_device int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) { int error = 0; - int pos, pcie_type, cap, rootport_ptm_offset, device_ptm_offset; + int pos, pcie_type, cap, rp_ptm_offset, device_ptm_offset; struct pci_device *phys_dev = ptdev->phys_dev; - //struct pci_bridge_info bridge_info = {}; - struct pci_device *root_port; + struct pci_device *rp; + struct pci_device_info rp_info = {}; *vrp_sec_bus = 0; @@ -146,23 +146,37 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) return -EINVAL; } - root_port = pci_find_root_port(phys_dev); - if (root_port == NULL) { + rp = pci_find_root_port(phys_dev); + if (rp == NULL) { pr_err("%s Error: Cannot find root port of %x:%x.%x.\n", __func__, phys_dev->bus, phys_dev->dev, phys_dev->func); return -ENODEV; } - cap = get_ptm_cap(root_port, &rootport_ptm_offset); + cap = get_ptm_cap(rp, &rp_ptm_offset); if (!(cap & PCIM_PTM_CAP_ROOT)) { pr_err("%s Error: root port %x:%x.%x of %x:%x.%x is not PTM root.\n", - __func__, root_port->bus, root_port->dev, - root_port->func, phys_dev->bus, phys_dev->dev, phys_dev->func); + __func__, rp->bus, rp->dev, + rp->func, phys_dev->bus, phys_dev->dev, phys_dev->func); + return -EINVAL; + } + + /* check whether more than one devices are connected to the root port. + * if more than one devices are connected to the root port, we flag + * this as error and won't enable PTM. We do this just because we + * don't have this hw configuration and won't be able totest this case. + */ + error = pci_device_get_bridge_buses(rp, &(rp_info.primary_bus), + &(rp_info.secondary_bus), &(rp_info.subordinate_bus)); + + if (error || (get_device_count_on_bridge(&rp_info) != 1)) { + pr_err("%s Error: Failed to enable PTM on root port [%x:%x.%x] that has multiple children.\n", + __func__, rp->bus, rp->dev, rp->func); return -EINVAL; } // add virtual root port - *vrp_sec_bus = add_vroot_port(ctx, ptdev, root_port, rootport_ptm_offset); + *vrp_sec_bus = add_vroot_port(ctx, ptdev, rp, rp_ptm_offset); } else if (pcie_type == PCIEM_TYPE_ROOT_INT_EP) { // No need to emulate root port if ptm requestor is RCIE diff --git a/devicemodel/include/pci_util.h b/devicemodel/include/pci_util.h index 9be322e45..524459b97 100644 --- a/devicemodel/include/pci_util.h +++ b/devicemodel/include/pci_util.h @@ -11,7 +11,21 @@ #include #include "pciaccess.h" -struct pci_device_info; +struct pci_device_info { + bool is_bridge; + int primary_bus; + int secondary_bus; + int subordinate_bus; + uint16_t bdf; + struct pci_device_info *parent; /* pointer to its parent bridge */ + struct pci_device_info *clist; /* children list */ + + /* cache of all pci devices + * FIXME: remove PCI_DEVICE_Q. To cleanup pci device cache: + * remove children, then remove parents + */ + TAILQ_ENTRY(pci_device_info) PCI_DEVICE_Q; +}; int pci_find_cap(struct pci_device *pdev, const int cap_id); int pci_find_ext_cap(struct pci_device *pdev, int cap_id);