dm: check if PTM root is enabled

hv is responsible to ensure PTM is always enabled on hw root port if
that root port is PTM root-capable.  If PTM root is not enabled already in physical
root port before guest launch, guest OS can only enable it in root port's virtual
config space and PTM may not function as desired so we would rather not
to allow user to enable PTM on pass-thru device in this case.
Also revisit a few comments to add assumption that acrn only support a
simple PTM hierarch emulation i.e., EP (ptm requestor) is directly connected
to root port (ptm root), or ptm requestor is rcie).

V4:
 - Change behavior from V3: When users tries to enable PTM while PTM root is not enabled on
physical root port, allow user to launch VM and pass thru the device
with no PTM support.
V3:
 - When users tries to enable PTM while PTM root is not enabled on
physical root port, allow user to launch VM and enable PTM in virtual
root port.  This is not going to enable PTM on physical root port.

Tracked-On: #5915
Signed-off-by: Rong Liu <rong.l.liu@intel.com>
Acked-by: Jason Chen <jason.cj.chen@intel.com>
This commit is contained in:
Rong Liu 2021-05-24 00:50:44 +00:00 committed by wenlingz
parent 43acbdd345
commit 8f6c17f1f1

View File

@ -42,25 +42,23 @@ static int ptm_root_port_secondary_bus;
/* get ptm capability register value */
static int
get_ptm_cap(struct pci_device *pdev, int *offset)
get_ptm_reg_value(struct pci_device *pdev, int reg)
{
int pos;
uint32_t cap;
uint32_t reg_val;
pos = pci_find_ext_cap(pdev, PCIZ_PTM);
if (!pos) {
*offset = 0;
return 0;
}
*offset = pos;
pci_device_cfg_read_u32(pdev, &cap, pos + PCIR_PTM_CAP);
pci_device_cfg_read_u32(pdev, &reg_val, pos + reg);
pr_notice("<PTM>-%s: device [%x:%x.%x]: ptm-cap=0x%x, offset=0x%x.\n",
__func__, pdev->bus, pdev->dev, pdev->func, cap, *offset);
pr_notice("<PTM>-%s: device [%x:%x.%x]: ptm pos=0x%x, ptm reg val=0x%x.\n",
__func__, pdev->bus, pdev->dev, pdev->func, pos, reg_val);
return cap;
return reg_val;
}
/* add virtual root port to hv */
@ -106,7 +104,7 @@ 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, rp_ptm_offset, device_ptm_offset;
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 = {};
@ -133,13 +131,17 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus)
pcie_type = pci_get_pcie_type(phys_dev);
/* Assume that PTM requestor can be enabled on pci ep or rcie.
* If PTM is enabled on an ep, PTM hierarchy requires it has an upstream
* PTM root. Based on available HW platforms, currently PTM root
* capability is implemented in pci root port.
/* The following sanity checks are based on these assumptions:
* 1. PTM requestor can be enabled on pci ep or rcie.
* 2. HW implements this simple PTM hierarchy: PTM requestor (EP) is
* directly connected to PTM root (root port), or requestor itself is RCIE.
* 3. There is no intermediate nodes (such as switch) in between the PTM
* root and PTM requestor.
* 4. HW only implements one PCI domain in the system and only one PTM
* domain implemented in this PCI domain
*/
if (pcie_type == PCIEM_TYPE_ENDPOINT) {
cap = get_ptm_cap(phys_dev, &device_ptm_offset);
cap = get_ptm_reg_value(phys_dev, PCIR_PTM_CAP);
if (!(cap & PCIM_PTM_CAP_REQ)) {
pr_err("%s Error: %x:%x.%x must be PTM requestor.\n", __func__,
phys_dev->bus, phys_dev->dev, phys_dev->func);
@ -153,11 +155,12 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus)
return -ENODEV;
}
cap = get_ptm_cap(rp, &rp_ptm_offset);
/* check whether root port is PTM root-capable */
cap = get_ptm_reg_value(rp, PCIR_PTM_CAP);
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__, rp->bus, rp->dev,
rp->func, phys_dev->bus, phys_dev->dev, phys_dev->func);
pr_err("%s Error: root port %x:%x.%x of %x:%x.%x is not PTM root capable.\n",
__func__, rp->bus, rp->dev, rp->func,
phys_dev->bus, phys_dev->dev, phys_dev->func);
return -EINVAL;
}
@ -175,11 +178,26 @@ int ptm_probe(struct vmctx *ctx, struct passthru_dev *ptdev, int *vrp_sec_bus)
return -EINVAL;
}
// add virtual root port
*vrp_sec_bus = add_vroot_port(ctx, ptdev, rp, rp_ptm_offset);
/* hv is responsible to ensure that PTM is enabled on hw root port if
* root port is PTM root-capable. If PTM root is not enabled already in physical
* root port before guest launch, guest OS can only enable it in root port's virtual
* config space and PTM may not function as desired so we are not going to allow user
* to enable PTM on pass-thru device.
*/
cap = get_ptm_reg_value(rp, PCIR_PTM_CTRL);
if (!(cap & PCIM_PTM_CTRL_ENABLE) || !(cap & PCIM_PTM_CTRL_ROOT_SELECT)) {
pr_err("%s Warning: guest is not allowed to enable PTM on root port %x:%x.%x.\n",
__func__, rp->bus, rp->dev, rp->func);
return -EINVAL;
}
rp_ptm_offset = pci_find_ext_cap(rp, PCIZ_PTM);
/* add virtual root port */
*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
/* Do NOT emulate root port if ptm requestor is RCIE */
pr_notice("%s: ptm requestor is root complex integrated device.\n",
__func__);
} else {