Skip to content

Commit 3b96b89

Browse files
Esther Shimanovichbjorn-helgaas
authored andcommitted
PCI: Detect and trust built-in Thunderbolt chips
Some computers with CPUs that lack Thunderbolt features use discrete Thunderbolt chips to add Thunderbolt functionality. These Thunderbolt chips are located within the chassis; between the Root Port labeled ExternalFacingPort and the USB-C port. These Thunderbolt PCIe devices should be labeled as fixed and trusted, as they are built into the computer. Otherwise, security policies that rely on those flags may have unintended results, such as preventing USB-C ports from enumerating. Detect the above scenario through the process of elimination. 1) Integrated Thunderbolt host controllers already have Thunderbolt implemented, so anything outside their external facing Root Port is removable and untrusted. Detect them using the following properties: - Most integrated host controllers have the "usb4-host-interface" ACPI property, as described here: https://learn.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#mapping-native-protocols-pcie-displayport-tunneled-through-usb4-to-usb4-host-routers - Integrated Thunderbolt PCIe Root Ports before Alder Lake do not have the "usb4-host-interface" ACPI property. Identify those by their PCI IDs instead. 2) If a Root Port does not have integrated Thunderbolt capabilities, but has the "ExternalFacingPort" ACPI property, that means the manufacturer has opted to use a discrete Thunderbolt host controller that is built into the computer. This host controller can be identified by virtue of being located directly below an external-facing Root Port that lacks integrated Thunderbolt. Label it as trusted and fixed. Everything downstream from it is untrusted and removable. The "ExternalFacingPort" ACPI property is described here: https://learn.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-externally-exposed-pcie-root-ports Link: https://lore.kernel.org/r/20240910-trust-tbt-fix-v5-1-7a7a42a5f496@chromium.org Suggested-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Esther Shimanovich <eshimanovich@chromium.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com> Tested-by: Mario Limonciello <mario.limonciello@amd.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
1 parent 9852d85 commit 3b96b89

File tree

3 files changed

+148
-7
lines changed

3 files changed

+148
-7
lines changed

arch/x86/pci/acpi.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,125 @@ void __init pci_acpi_crs_quirks(void)
250250
pr_info("Please notify linux-pci@vger.kernel.org so future kernels can do this automatically\n");
251251
}
252252

253+
/*
254+
* Check if pdev is part of a PCIe switch that is directly below the
255+
* specified bridge.
256+
*/
257+
static bool pcie_switch_directly_under(struct pci_dev *bridge,
258+
struct pci_dev *pdev)
259+
{
260+
struct pci_dev *parent = pci_upstream_bridge(pdev);
261+
262+
/* If the device doesn't have a parent, it's not under anything */
263+
if (!parent)
264+
return false;
265+
266+
/*
267+
* If the device has a PCIe type, check if it is below the
268+
* corresponding PCIe switch components (if applicable). Then check
269+
* if its upstream port is directly beneath the specified bridge.
270+
*/
271+
switch (pci_pcie_type(pdev)) {
272+
case PCI_EXP_TYPE_UPSTREAM:
273+
return parent == bridge;
274+
275+
case PCI_EXP_TYPE_DOWNSTREAM:
276+
if (pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM)
277+
return false;
278+
parent = pci_upstream_bridge(parent);
279+
return parent == bridge;
280+
281+
case PCI_EXP_TYPE_ENDPOINT:
282+
if (pci_pcie_type(parent) != PCI_EXP_TYPE_DOWNSTREAM)
283+
return false;
284+
parent = pci_upstream_bridge(parent);
285+
if (!parent || pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM)
286+
return false;
287+
parent = pci_upstream_bridge(parent);
288+
return parent == bridge;
289+
}
290+
291+
return false;
292+
}
293+
294+
static bool pcie_has_usb4_host_interface(struct pci_dev *pdev)
295+
{
296+
struct fwnode_handle *fwnode;
297+
298+
/*
299+
* For USB4, the tunneled PCIe Root or Downstream Ports are marked
300+
* with the "usb4-host-interface" ACPI property, so we look for
301+
* that first. This should cover most cases.
302+
*/
303+
fwnode = fwnode_find_reference(dev_fwnode(&pdev->dev),
304+
"usb4-host-interface", 0);
305+
if (!IS_ERR(fwnode)) {
306+
fwnode_handle_put(fwnode);
307+
return true;
308+
}
309+
310+
/*
311+
* Any integrated Thunderbolt 3/4 PCIe Root Ports from Intel
312+
* before Alder Lake do not have the "usb4-host-interface"
313+
* property so we use their PCI IDs instead. All these are
314+
* tunneled. This list is not expected to grow.
315+
*/
316+
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
317+
switch (pdev->device) {
318+
/* Ice Lake Thunderbolt 3 PCIe Root Ports */
319+
case 0x8a1d:
320+
case 0x8a1f:
321+
case 0x8a21:
322+
case 0x8a23:
323+
/* Tiger Lake-LP Thunderbolt 4 PCIe Root Ports */
324+
case 0x9a23:
325+
case 0x9a25:
326+
case 0x9a27:
327+
case 0x9a29:
328+
/* Tiger Lake-H Thunderbolt 4 PCIe Root Ports */
329+
case 0x9a2b:
330+
case 0x9a2d:
331+
case 0x9a2f:
332+
case 0x9a31:
333+
return true;
334+
}
335+
}
336+
337+
return false;
338+
}
339+
340+
bool arch_pci_dev_is_removable(struct pci_dev *pdev)
341+
{
342+
struct pci_dev *parent, *root;
343+
344+
/* pdev without a parent or Root Port is never tunneled */
345+
parent = pci_upstream_bridge(pdev);
346+
if (!parent)
347+
return false;
348+
root = pcie_find_root_port(pdev);
349+
if (!root)
350+
return false;
351+
352+
/* Internal PCIe devices are not tunneled */
353+
if (!root->external_facing)
354+
return false;
355+
356+
/* Anything directly behind a "usb4-host-interface" is tunneled */
357+
if (pcie_has_usb4_host_interface(parent))
358+
return true;
359+
360+
/*
361+
* Check if this is a discrete Thunderbolt/USB4 controller that is
362+
* directly behind the non-USB4 PCIe Root Port marked as
363+
* "ExternalFacingPort". Those are not behind a PCIe tunnel.
364+
*/
365+
if (pcie_switch_directly_under(root, pdev))
366+
return false;
367+
368+
/* PCIe devices after the discrete chip are tunneled */
369+
return true;
370+
}
371+
253372
#ifdef CONFIG_PCI_MMCONFIG
254373
static int check_segment(u16 seg, struct device *dev, char *estr)
255374
{

drivers/pci/probe.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,23 +1633,33 @@ static void set_pcie_thunderbolt(struct pci_dev *dev)
16331633

16341634
static void set_pcie_untrusted(struct pci_dev *dev)
16351635
{
1636-
struct pci_dev *parent;
1636+
struct pci_dev *parent = pci_upstream_bridge(dev);
16371637

1638+
if (!parent)
1639+
return;
16381640
/*
1639-
* If the upstream bridge is untrusted we treat this device
1641+
* If the upstream bridge is untrusted we treat this device as
16401642
* untrusted as well.
16411643
*/
1642-
parent = pci_upstream_bridge(dev);
1643-
if (parent && (parent->untrusted || parent->external_facing))
1644+
if (parent->untrusted) {
1645+
dev->untrusted = true;
1646+
return;
1647+
}
1648+
1649+
if (arch_pci_dev_is_removable(dev)) {
1650+
pci_dbg(dev, "marking as untrusted\n");
16441651
dev->untrusted = true;
1652+
}
16451653
}
16461654

16471655
static void pci_set_removable(struct pci_dev *dev)
16481656
{
16491657
struct pci_dev *parent = pci_upstream_bridge(dev);
16501658

1659+
if (!parent)
1660+
return;
16511661
/*
1652-
* We (only) consider everything downstream from an external_facing
1662+
* We (only) consider everything tunneled below an external_facing
16531663
* device to be removable by the user. We're mainly concerned with
16541664
* consumer platforms with user accessible thunderbolt ports that are
16551665
* vulnerable to DMA attacks, and we expect those ports to be marked by
@@ -1659,9 +1669,15 @@ static void pci_set_removable(struct pci_dev *dev)
16591669
* accessible to user / may not be removed by end user, and thus not
16601670
* exposed as "removable" to userspace.
16611671
*/
1662-
if (parent &&
1663-
(parent->external_facing || dev_is_removable(&parent->dev)))
1672+
if (dev_is_removable(&parent->dev)) {
1673+
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
1674+
return;
1675+
}
1676+
1677+
if (arch_pci_dev_is_removable(dev)) {
1678+
pci_dbg(dev, "marking as removable\n");
16641679
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
1680+
}
16651681
}
16661682

16671683
/**

include/linux/pci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,6 +2609,12 @@ pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; }
26092609
static inline bool pci_pr3_present(struct pci_dev *pdev) { return false; }
26102610
#endif
26112611

2612+
#if defined(CONFIG_X86) && defined(CONFIG_ACPI)
2613+
bool arch_pci_dev_is_removable(struct pci_dev *pdev);
2614+
#else
2615+
static inline bool arch_pci_dev_is_removable(struct pci_dev *pdev) { return false; }
2616+
#endif
2617+
26122618
#ifdef CONFIG_EEH
26132619
static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
26142620
{

0 commit comments

Comments
 (0)