Switches ignore the PASID when routing TLPs. This means the path from the PASID issuing end point to the IOMMU must be direct with no possibility for another device to claim the addresses. This is done using ACS flags and pci_enable_pasid() checks for this. The new ACS Enhanced bits clarify some undefined behaviors in the spec around what P2P Request Redirect means. Linux has long assumed that PCI_ACS_RR implies PCI_ACS_DSP_MT_RR | PCI_ACS_USP_MT_RR | PCI_ACS_UNCLAIMED_RR. If the device supports ACS Enhanced then use the information it reports to determine if PASID SVA is supported or not. PCI_ACS_DSP_MT_RR: Prevents Downstream Port BAR's from claiming upstream flowing transactions PCI_ACS_USP_MT_RR: Prevents Upstream Port BAR's from claiming upstream flowing transactions PCI_ACS_UNCLAIMED_RR: Prevents a hole in the USP bridge window compared to all the DSP bridge windows from generating a error. Each of these cases would poke a hole in the PASID address space which is not permitted. Enhance the comments around pci_acs_flags_enabled() to better explain the reasoning for its logic. Continue to take the approach of assuming the device is doing the "right ACS" if it does not explicitly declare otherwise. Fixes: 201007ef707a ("PCI: Enable PASID only when ACS RR & UF enabled on upstream path") Signed-off-by: Jason Gunthorpe --- drivers/pci/ats.c | 4 +++- drivers/pci/pci.c | 54 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index ec6c8dbdc5e9c9..00603c2c4ff0ea 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -416,7 +416,9 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) if (!pasid) return -EINVAL; - if (!pci_acs_path_enabled(pdev, NULL, PCI_ACS_RR | PCI_ACS_UF)) + if (!pci_acs_path_enabled(pdev, NULL, + PCI_ACS_RR | PCI_ACS_UF | PCI_ACS_USP_MT_RR | + PCI_ACS_DSP_MT_RR | PCI_ACS_UNCLAIMED_RR)) return -EINVAL; pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 983f71211f0055..620b7f79093854 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3606,6 +3606,52 @@ void pci_configure_ari(struct pci_dev *dev) } } + +/* + * The spec is not clear what it means if the capability bit is 0. One view is + * that the device acts as though the ctrl bit is zero, another view is the + * device behavior is undefined. + * + * Historically Linux has taken the position that the capability bit as 0 means + * the device supports the most favorable interpretation of the spec - ie that + * things like P2P RR are always on. As this is security sensitive we expect + * devices that do not follow this rule to be quirked. + * + * ACS Enhanced eliminated undefined areas of the spec around MMIO in root ports + * and switch ports. If those ports have no MMIO then it is not relavent. + * PCI_ACS_UNCLAIMED_RR eliminates the undefined area around an upstream switch + * window that is not fully decoded by the downstream windows. + * + * This takes the same approach with ACS Enhanced, if the device does not + * support it then we assume the ACS P2P RR has all the enhanced behaviors too. + * + * Due to ACS Enhanced bits being force set to 0 by older Linux kernels, and + * those values would break old kernels on the edge cases they cover, the only + * compatible thing for a new device to implement is ACS Enhanced supported with + * the control bits (except PCI_ACS_IORB) wired to follow ACS_RR. + */ +static u16 pci_acs_ctrl_mask(struct pci_dev *pdev, u16 hw_cap) +{ + /* + * Egress Control enables use of the Egress Control Vector which is not + * present without the cap. + */ + u16 mask = PCI_ACS_EC; + + mask = hw_cap & (PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | + PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT); + + /* + * If ACS Enhanced is supported the device reports what it is doing + * through these bits which may not be settable. + */ + if (hw_cap & PCI_ACS_ENHANCED) + mask |= PCI_ACS_IORB | PCI_ACS_DSP_MT_RB | PCI_ACS_DSP_MT_RR | + PCI_ACS_USP_MT_RB | PCI_ACS_USP_MT_RR | + PCI_ACS_UNCLAIMED_RR; + return mask; +} + static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { int pos; @@ -3615,15 +3661,9 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) if (!pos) return false; - /* - * Except for egress control, capabilities are either required - * or only required if controllable. Features missing from the - * capability field can therefore be assumed as hard-wired enabled. - */ pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap); - acs_flags &= (cap | PCI_ACS_EC); - pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + acs_flags &= pci_acs_ctrl_mask(pdev, cap); return (ctrl & acs_flags) == acs_flags; } -- 2.43.0