mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-12 02:18:40 +00:00
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 <yu1.wang@intel.com> Reviewed-by: Fei Li <fei1.li@intel.com> Signed-off-by: Minggui Cao <minggui.cao@intel.com>
This commit is contained in:
parent
5d54b0d110
commit
483b7b1280
@ -1127,6 +1127,5 @@ fail:
|
||||
create_fail:
|
||||
uninit_hugetlb();
|
||||
deinit_loggers();
|
||||
clean_pci_cache();
|
||||
exit(ret);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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__);
|
||||
|
@ -11,32 +11,10 @@
|
||||
#include <stdbool.h>
|
||||
#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
|
||||
|
Loading…
Reference in New Issue
Block a user