Merge branch 'pci/thunderbolt'

- Detect some Thunderbolt chips that are built-in and hence 'trustworthy'
  by a heuristic since the 'ExternalFacingPort' and 'usb4-host-interface'
  ACPI properties are not quite enough (Esther Shimanovich)

* pci/thunderbolt:
  PCI: Detect and trust built-in Thunderbolt chips
This commit is contained in:
Bjorn Helgaas 2024-11-25 13:40:55 -06:00
commit efcbd9d397
3 changed files with 148 additions and 7 deletions

View File

@ -250,6 +250,125 @@ void __init pci_acpi_crs_quirks(void)
pr_info("Please notify linux-pci@vger.kernel.org so future kernels can do this automatically\n");
}
/*
* Check if pdev is part of a PCIe switch that is directly below the
* specified bridge.
*/
static bool pcie_switch_directly_under(struct pci_dev *bridge,
struct pci_dev *pdev)
{
struct pci_dev *parent = pci_upstream_bridge(pdev);
/* If the device doesn't have a parent, it's not under anything */
if (!parent)
return false;
/*
* If the device has a PCIe type, check if it is below the
* corresponding PCIe switch components (if applicable). Then check
* if its upstream port is directly beneath the specified bridge.
*/
switch (pci_pcie_type(pdev)) {
case PCI_EXP_TYPE_UPSTREAM:
return parent == bridge;
case PCI_EXP_TYPE_DOWNSTREAM:
if (pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM)
return false;
parent = pci_upstream_bridge(parent);
return parent == bridge;
case PCI_EXP_TYPE_ENDPOINT:
if (pci_pcie_type(parent) != PCI_EXP_TYPE_DOWNSTREAM)
return false;
parent = pci_upstream_bridge(parent);
if (!parent || pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM)
return false;
parent = pci_upstream_bridge(parent);
return parent == bridge;
}
return false;
}
static bool pcie_has_usb4_host_interface(struct pci_dev *pdev)
{
struct fwnode_handle *fwnode;
/*
* For USB4, the tunneled PCIe Root or Downstream Ports are marked
* with the "usb4-host-interface" ACPI property, so we look for
* that first. This should cover most cases.
*/
fwnode = fwnode_find_reference(dev_fwnode(&pdev->dev),
"usb4-host-interface", 0);
if (!IS_ERR(fwnode)) {
fwnode_handle_put(fwnode);
return true;
}
/*
* Any integrated Thunderbolt 3/4 PCIe Root Ports from Intel
* before Alder Lake do not have the "usb4-host-interface"
* property so we use their PCI IDs instead. All these are
* tunneled. This list is not expected to grow.
*/
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
switch (pdev->device) {
/* Ice Lake Thunderbolt 3 PCIe Root Ports */
case 0x8a1d:
case 0x8a1f:
case 0x8a21:
case 0x8a23:
/* Tiger Lake-LP Thunderbolt 4 PCIe Root Ports */
case 0x9a23:
case 0x9a25:
case 0x9a27:
case 0x9a29:
/* Tiger Lake-H Thunderbolt 4 PCIe Root Ports */
case 0x9a2b:
case 0x9a2d:
case 0x9a2f:
case 0x9a31:
return true;
}
}
return false;
}
bool arch_pci_dev_is_removable(struct pci_dev *pdev)
{
struct pci_dev *parent, *root;
/* pdev without a parent or Root Port is never tunneled */
parent = pci_upstream_bridge(pdev);
if (!parent)
return false;
root = pcie_find_root_port(pdev);
if (!root)
return false;
/* Internal PCIe devices are not tunneled */
if (!root->external_facing)
return false;
/* Anything directly behind a "usb4-host-interface" is tunneled */
if (pcie_has_usb4_host_interface(parent))
return true;
/*
* Check if this is a discrete Thunderbolt/USB4 controller that is
* directly behind the non-USB4 PCIe Root Port marked as
* "ExternalFacingPort". Those are not behind a PCIe tunnel.
*/
if (pcie_switch_directly_under(root, pdev))
return false;
/* PCIe devices after the discrete chip are tunneled */
return true;
}
#ifdef CONFIG_PCI_MMCONFIG
static int check_segment(u16 seg, struct device *dev, char *estr)
{

View File

@ -1635,23 +1635,33 @@ static void set_pcie_thunderbolt(struct pci_dev *dev)
static void set_pcie_untrusted(struct pci_dev *dev)
{
struct pci_dev *parent;
struct pci_dev *parent = pci_upstream_bridge(dev);
if (!parent)
return;
/*
* If the upstream bridge is untrusted we treat this device
* If the upstream bridge is untrusted we treat this device as
* untrusted as well.
*/
parent = pci_upstream_bridge(dev);
if (parent && (parent->untrusted || parent->external_facing))
if (parent->untrusted) {
dev->untrusted = true;
return;
}
if (arch_pci_dev_is_removable(dev)) {
pci_dbg(dev, "marking as untrusted\n");
dev->untrusted = true;
}
}
static void pci_set_removable(struct pci_dev *dev)
{
struct pci_dev *parent = pci_upstream_bridge(dev);
if (!parent)
return;
/*
* We (only) consider everything downstream from an external_facing
* We (only) consider everything tunneled below an external_facing
* device to be removable by the user. We're mainly concerned with
* consumer platforms with user accessible thunderbolt ports that are
* vulnerable to DMA attacks, and we expect those ports to be marked by
@ -1661,9 +1671,15 @@ static void pci_set_removable(struct pci_dev *dev)
* accessible to user / may not be removed by end user, and thus not
* exposed as "removable" to userspace.
*/
if (parent &&
(parent->external_facing || dev_is_removable(&parent->dev)))
if (dev_is_removable(&parent->dev)) {
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
return;
}
if (arch_pci_dev_is_removable(dev)) {
pci_dbg(dev, "marking as removable\n");
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
}
}
/**

View File

@ -2606,6 +2606,12 @@ pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; }
static inline bool pci_pr3_present(struct pci_dev *pdev) { return false; }
#endif
#if defined(CONFIG_X86) && defined(CONFIG_ACPI)
bool arch_pci_dev_is_removable(struct pci_dev *pdev);
#else
static inline bool arch_pci_dev_is_removable(struct pci_dev *pdev) { return false; }
#endif
#ifdef CONFIG_EEH
static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
{