mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
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:
commit
efcbd9d397
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user