Skip to content

Commit efcbd9d

Browse files
committed
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
2 parents c03d361 + 3b96b89 commit efcbd9d

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
@@ -1635,23 +1635,33 @@ static void set_pcie_thunderbolt(struct pci_dev *dev)
16351635

16361636
static void set_pcie_untrusted(struct pci_dev *dev)
16371637
{
1638-
struct pci_dev *parent;
1638+
struct pci_dev *parent = pci_upstream_bridge(dev);
16391639

1640+
if (!parent)
1641+
return;
16401642
/*
1641-
* If the upstream bridge is untrusted we treat this device
1643+
* If the upstream bridge is untrusted we treat this device as
16421644
* untrusted as well.
16431645
*/
1644-
parent = pci_upstream_bridge(dev);
1645-
if (parent && (parent->untrusted || parent->external_facing))
1646+
if (parent->untrusted) {
1647+
dev->untrusted = true;
1648+
return;
1649+
}
1650+
1651+
if (arch_pci_dev_is_removable(dev)) {
1652+
pci_dbg(dev, "marking as untrusted\n");
16461653
dev->untrusted = true;
1654+
}
16471655
}
16481656

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

1661+
if (!parent)
1662+
return;
16531663
/*
1654-
* We (only) consider everything downstream from an external_facing
1664+
* We (only) consider everything tunneled below an external_facing
16551665
* device to be removable by the user. We're mainly concerned with
16561666
* consumer platforms with user accessible thunderbolt ports that are
16571667
* 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)
16611671
* accessible to user / may not be removed by end user, and thus not
16621672
* exposed as "removable" to userspace.
16631673
*/
1664-
if (parent &&
1665-
(parent->external_facing || dev_is_removable(&parent->dev)))
1674+
if (dev_is_removable(&parent->dev)) {
1675+
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
1676+
return;
1677+
}
1678+
1679+
if (arch_pci_dev_is_removable(dev)) {
1680+
pci_dbg(dev, "marking as removable\n");
16661681
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
1682+
}
16671683
}
16681684

16691685
/**

include/linux/pci.h

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

2609+
#if defined(CONFIG_X86) && defined(CONFIG_ACPI)
2610+
bool arch_pci_dev_is_removable(struct pci_dev *pdev);
2611+
#else
2612+
static inline bool arch_pci_dev_is_removable(struct pci_dev *pdev) { return false; }
2613+
#endif
2614+
26092615
#ifdef CONFIG_EEH
26102616
static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
26112617
{

0 commit comments

Comments
 (0)