From 483b7b128018d30d45e11b30e8ab96a01eccd12a Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Sat, 15 Jan 2022 10:57:17 +0800 Subject: [PATCH] dm: fix: TSN in multi-hostbridge could crash fix bug: if PTM-CAP device, like TSN in multi-hostbridge could cause acrn-dm crash. original PTM PCI code has not handled multi-hostbridge case, and just handled hostbridge (00:00.0) case. this patch calls PCI access API to handle PTM-CAP device/bridge (root port) structure, to avoid scan PCI hierarchical. Tracked-On: #7045 Acked-by: Wang, Yu1 Reviewed-by: Fei Li Signed-off-by: Minggui Cao --- devicemodel/core/main.c | 1 - devicemodel/hw/pci/pci_util.c | 226 +-------------------------------- devicemodel/hw/pci/ptm.c | 28 ++-- devicemodel/include/pci_util.h | 24 +--- 4 files changed, 12 insertions(+), 267 deletions(-) diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index 78800e4bb..84d432948 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -1127,6 +1127,5 @@ 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 9055cb271..3c606be8a 100644 --- a/devicemodel/hw/pci/pci_util.c +++ b/devicemodel/hw/pci/pci_util.c @@ -16,18 +16,6 @@ #include "pci_util.h" #include "log.h" -#define MAX_LEN (PCI_BUSMAX + 1) - -TAILQ_HEAD(ALL_PCI_DEVICES, pci_device_info); -static struct ALL_PCI_DEVICES pci_device_q; - -static int pci_scanned; - -/* map bus to bridge - * assume only one pci domain in the system so the - * bus # in the pci hierarchy is unique. - */ -static struct pci_device_info *bus_map[MAX_LEN]; /* find position of specified pci capability register*/ int pci_find_cap(struct pci_device *pdev, const int cap_id) @@ -105,220 +93,12 @@ bool is_root_port(struct pci_device *pdev) return (pcie_type == PCIEM_TYPE_ROOT_PORT); } -/* check whether pdev is a bridge */ -bool is_bridge(struct pci_device *pdev) +/* check whether pdev is multi-function device */ +bool is_mfdev(struct pci_device *pdev) { uint8_t hdr_type; pci_device_cfg_read_u8(pdev, &hdr_type, PCIR_HDRTYPE); - return ((hdr_type & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE); + return ((hdr_type & PCIM_MFDEV) == PCIM_MFDEV); } - -/* add new_child to parent's child list */ -static inline void -add_child(struct pci_device_info *new_child, - struct pci_device_info *parent) -{ - new_child->clist = parent->clist; - parent->clist = new_child; - - // it is important to reset the new_child's clist to null - new_child->clist = NULL; -} - -/* get number of children on bus */ -int get_device_count_on_bus(int bus) -{ - int count = 0; - struct pci_device_info *bridge = bus_map[bus]; - struct pci_device_info *next; - - if (bridge == NULL) - return 0; - - next = bridge->clist; - - while (next != NULL) { - if (next->is_bridge) - count += get_device_count_on_bridge(bridge); - else - count++; - - next = next->clist; - } - - return count; -} - -/* get number of children on bridge */ -int get_device_count_on_bridge(const struct pci_device_info *bridge_info) -{ - int count = 0; - int i, ret; - - if (bridge_info == NULL) - return 0; - - for (i = bridge_info->secondary_bus; i <= bridge_info->subordinate_bus; i++) { - ret = get_device_count_on_bus(i); - pr_info("%s: device_count on bus[%x]=%d.\n", __func__, i, ret); - count += ret; - } - - return count; -} - -/* build and cache pci hierarchy in one pci domain - * @pre: only one pci domain in the system. Need revisit - * if there is more than one domain in the system. - */ -int scan_pci(void) -{ - int error = 0; - struct pci_device_iterator *iter; - struct pci_device *dev; - int i; - - if (pci_scanned) - return 0; - - TAILQ_INIT(&pci_device_q); - - pci_scanned = 1; - - iter = pci_slot_match_iterator_create(NULL); - while ((dev = pci_device_next(iter)) != NULL) { - struct pci_device_info *pci_info = calloc(1, sizeof(struct pci_device_info)); - - if (pci_info == NULL) { - error = -ENOMEM; - pr_err("%s, %s: calloc", __func__, __LINE__); - goto done; - } - - pci_info->parent = NULL; - pci_info->clist = NULL; - - pci_info->bdf = PCI_BDF(dev->bus, dev->dev, dev->func); - - TAILQ_INSERT_TAIL(&pci_device_q, pci_info, PCI_DEVICE_Q); - - if (pci_info->bdf == 0) { /* host bridge */ - bus_map[0] = pci_info; - continue; - } - - if (is_bridge(dev)) { - pci_info->is_bridge = true; - - pci_device_get_bridge_buses(dev, &(pci_info->primary_bus), - &(pci_info->secondary_bus), &(pci_info->subordinate_bus)); - - // put bus and bridge into bus_map - for (i = pci_info->secondary_bus; i <= pci_info->subordinate_bus; i++) - bus_map[i] = pci_info; - } else - pci_info->is_bridge = false; - - // set its parent bridge - pci_info->parent = bus_map[dev->bus]; - - // add itself to its parent bridge's child list - add_child(pci_info, pci_info->parent); - } - -done: - if (error < 0) - { - pr_err("%s: failed to build pci hierarchy.\n", __func__); - clean_pci_cache(); - pci_scanned = false; - } - - pci_iterator_destroy(iter); - - return error; -} - -/* find bdf of pci device pdev's root port */ -struct pci_device * pci_find_root_port(const struct pci_device *pdev) -{ - struct pci_device *bdev; - int bus, dev, func; - - struct pci_device_info *brdg = bus_map[pdev->bus]; - - for (;;) { - if (brdg == NULL) - return NULL; - - bus = (brdg->bdf >> 8) & 0xFF; - dev = (brdg->bdf >> 3) & 0x1F; - func = brdg->bdf & 0x7; - - bdev = pci_device_find_by_slot(0, bus, dev, func); - if (is_root_port(bdev)) - return bdev; - - brdg = brdg->parent; - } - - return NULL; -} - -/* clean cache of pci device: free all allocated memory */ -void clean_pci_cache(void) -{ - if (!pci_scanned) - return; - - while (!TAILQ_EMPTY(&pci_device_q)) { - struct pci_device_info *p = TAILQ_FIRST(&pci_device_q); - - TAILQ_REMOVE(&pci_device_q, p, PCI_DEVICE_Q); - } - - pci_scanned = 0; -} - -#ifdef SCAN_PCI_TEST -static void -scan_pci_test(void) -{ - struct pci_device_info *device_info; - struct pci_device *pdev; - int bdf, parent_bdf; - - printf("%s enter.\n", __func__); - if (!pci_scanned) { - printf("%s: no pci hierarchy to test.\n", __func__); - return; - } - - TAILQ_FOREACH(device_info, &pci_device_q, PCI_DEVICE_Q) { - bdf = device_info->bdf; - - if (bdf == 0) { - printf("%s: host bridge.\n", __func__); - continue; - } - - parent_bdf = device_info->parent->bdf; - - printf("%s: [%x:%x.%x]: [%x:%x.%x]\n", __func__, (bdf >> 8) & 0xFF, (bdf >> 3) & 0x1F, bdf & 0x7, - (parent_bdf >> 8) & 0xFF, (parent_bdf >> 3) & 0x1F, parent_bdf & 0x7); - - pdev = pci_device_find_by_slot(0, (bdf >> 8) & 0xFF, (bdf >> 3) & 0x1F, bdf & 0x7); - - if (is_bridge(pdev)) { - struct pci_device_info *child; - child = device_info->clist; - while(child != NULL) { - printf("\t\t: child: [%x:%x.%x]\n", (child->bdf >> 8) & 0xFF, (child->bdf >> 3) & 0x1F, child->bdf & 0x7); - child = child->clist; - } - } - } -} -#endif diff --git a/devicemodel/hw/pci/ptm.c b/devicemodel/hw/pci/ptm.c index d628bd9fe..d0f65cbe8 100644 --- a/devicemodel/hw/pci/ptm.c +++ b/devicemodel/hw/pci/ptm.c @@ -116,19 +116,12 @@ add_vroot_port(struct vmctx *ctx, struct passthru_dev *ptdev, struct pci_device /* Probe whether device and its root port support PTM */ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) { - int error = 0; int pos, pcie_type, cap, rp_ptm_offset; struct pci_device *phys_dev = ptdev->phys_dev; struct pci_device *rp; - struct pci_device_info rp_info = {}; *vrp_sec_bus = 0; - /* build pci hierarch */ - error = scan_pci(); - if (error) - return error; - if (!ptdev->pcie_cap) { pr_err("%s Error: %x:%x.%x is not a pci-e device.\n", __func__, phys_dev->bus, phys_dev->dev, phys_dev->func); @@ -161,8 +154,9 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) return -EINVAL; } - rp = pci_find_root_port(phys_dev); - if (rp == NULL) { + /* Do not support switch */ + rp = pci_device_get_parent_bridge(phys_dev); + if ((rp == NULL) || !is_root_port(rp)) { 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; @@ -177,16 +171,11 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) 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 to test this case. + /* TODO: Support multiple PFs device later as it needs to consider to prevent P2P + * attack through ACS or passthrough all PFs together. */ - 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", + if (is_mfdev(phys_dev)) { + pr_err("%s: Failed to enable PTM on root port [%x:%x.%x], multi-func dev is not supported.\n", __func__, rp->bus, rp->dev, rp->func); return -EINVAL; } @@ -211,8 +200,7 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus) *vrp_sec_bus = add_vroot_port(ctx, ptdev, rp, rp_ptm_offset); } else if (pcie_type == PCIEM_TYPE_ROOT_INT_EP) { /* Do NOT emulate root port if ptm requestor is RCIE */ - pr_notice("%s: ptm requestor is root complex integrated device.\n", - __func__); + pr_notice("%s: ptm requestor is root complex integrated device.\n", __func__); } else { pr_err("%s Error: PTM can only be enabled on pci root complex integrated device or endpoint device.\n", __func__); diff --git a/devicemodel/include/pci_util.h b/devicemodel/include/pci_util.h index 524459b97..db2688500 100644 --- a/devicemodel/include/pci_util.h +++ b/devicemodel/include/pci_util.h @@ -11,32 +11,10 @@ #include #include "pciaccess.h" -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); int pci_get_pcie_type(struct pci_device *dev); bool is_root_port(struct pci_device *pdev); -bool is_bridge(struct pci_device *pdev); - -int get_device_count_on_bus(int bus); -int get_device_count_on_bridge(const struct pci_device_info *bridge_info); -int scan_pci(void); -struct pci_device * pci_find_root_port(const struct pci_device *pdev); -void clean_pci_cache(void); +bool is_mfdev(struct pci_device *pdev); #endif