mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-18 08:11:07 +00:00
Add one more sanity check: If the root port has more than one child, we won't enable PTM on the guest. This is not necessarily an error. We flag it as error just because we don't have this type of hw configuration at development time thus this configuration is not tested. Tracked-On: #5915 Signed-off-by: Rong Liu <rong.l.liu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
325 lines
6.7 KiB
C
325 lines
6.7 KiB
C
/*
|
|
* Copyright (C) 2021 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <sys/queue.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "pcireg.h"
|
|
#include "pciaccess.h"
|
|
#include "pci_core.h"
|
|
#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)
|
|
{
|
|
uint8_t cap_pos, cap_data;
|
|
uint16_t status = 0;
|
|
|
|
pci_device_cfg_read_u16(pdev, &status, PCIR_STATUS);
|
|
if (status & PCIM_STATUS_CAPPRESENT) {
|
|
pci_device_cfg_read_u8(pdev, &cap_pos, PCIR_CAP_PTR);
|
|
|
|
while (cap_pos != 0 && cap_pos != 0xff) {
|
|
pci_device_cfg_read_u8(pdev, &cap_data,
|
|
cap_pos + PCICAP_ID);
|
|
|
|
if (cap_data == cap_id)
|
|
return cap_pos;
|
|
|
|
pci_device_cfg_read_u8(pdev, &cap_pos,
|
|
cap_pos + PCICAP_NEXTPTR);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* find extend capability register position from cap_id */
|
|
int pci_find_ext_cap(struct pci_device *pdev, int cap_id)
|
|
{
|
|
int offset = 0;
|
|
uint32_t data = 0;
|
|
|
|
offset = PCIR_EXTCAP;
|
|
|
|
do {
|
|
/* PCI Express Extended Capability must have 4 bytes header */
|
|
pci_device_cfg_read_u32(pdev, &data, offset);
|
|
|
|
if (PCI_EXTCAP_ID(data) == cap_id)
|
|
break;
|
|
|
|
offset = PCI_EXTCAP_NEXTPTR(data);
|
|
} while (offset != 0);
|
|
|
|
return offset;
|
|
}
|
|
|
|
/* find pci-e device type */
|
|
int pci_get_pcie_type(struct pci_device *dev)
|
|
{
|
|
uint8_t data = 0;
|
|
int pcie_type;
|
|
int pos = 0;
|
|
|
|
if (dev == NULL)
|
|
return -EINVAL;
|
|
|
|
pos = pci_find_cap(dev, PCIY_EXPRESS);
|
|
if (!pos)
|
|
return -EINVAL;
|
|
|
|
pci_device_cfg_read_u8(dev, &data, pos + PCIER_FLAGS);
|
|
pcie_type = data & PCIEM_FLAGS_TYPE;
|
|
|
|
return pcie_type;
|
|
}
|
|
|
|
/* check whether pdev is a pci root port */
|
|
bool is_root_port(struct pci_device *pdev)
|
|
{
|
|
int pcie_type;
|
|
|
|
pcie_type = pci_get_pcie_type(pdev);
|
|
|
|
return (pcie_type == PCIEM_TYPE_ROOT_PORT);
|
|
}
|
|
|
|
/* check whether pdev is a bridge */
|
|
bool is_bridge(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);
|
|
}
|
|
|
|
/* 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
|