Skip to content

Commit 86eaf4a

Browse files
rmurphy-armjoergroedel
authored andcommitted
thunderbolt: Make iommu_dma_protection more accurate
Between me trying to get rid of iommu_present() and Mario wanting to support the AMD equivalent of DMAR_PLATFORM_OPT_IN, scrutiny has shown that the iommu_dma_protection attribute is being far too optimistic. Even if an IOMMU might be present for some PCI segment in the system, that doesn't necessarily mean it provides translation for the device(s) we care about. Furthermore, all that DMAR_PLATFORM_OPT_IN really does is tell us that memory was protected before the kernel was loaded, and prevent the user from disabling the intel-iommu driver entirely. While that lets us assume kernel integrity, what matters for actual runtime DMA protection is whether we trust individual devices, based on the "external facing" property that we expect firmware to describe for Thunderbolt ports. It's proven challenging to determine the appropriate ports accurately given the variety of possible topologies, so while still not getting a perfect answer, by putting enough faith in firmware we can at least get a good bit closer. If we can see that any device near a Thunderbolt NHI has all the requisites for Kernel DMA Protection, chances are that it *is* a relevant port, but moreover that implies that firmware is playing the game overall, so we'll use that to assume that all Thunderbolt ports should be correctly marked and thus will end up fully protected. CC: Mario Limonciello <mario.limonciello@amd.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Robin Murphy <robin.murphy@arm.com> Link: https://lore.kernel.org/r/b153f208bc9eafab5105bad0358b77366509d2d4.1650878781.git.robin.murphy@arm.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
1 parent d0be55f commit 86eaf4a

3 files changed

Lines changed: 49 additions & 9 deletions

File tree

drivers/thunderbolt/domain.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
*/
88

99
#include <linux/device.h>
10-
#include <linux/dmar.h>
1110
#include <linux/idr.h>
12-
#include <linux/iommu.h>
1311
#include <linux/module.h>
1412
#include <linux/pm_runtime.h>
1513
#include <linux/slab.h>
@@ -257,13 +255,9 @@ static ssize_t iommu_dma_protection_show(struct device *dev,
257255
struct device_attribute *attr,
258256
char *buf)
259257
{
260-
/*
261-
* Kernel DMA protection is a feature where Thunderbolt security is
262-
* handled natively using IOMMU. It is enabled when IOMMU is
263-
* enabled and ACPI DMAR table has DMAR_PLATFORM_OPT_IN set.
264-
*/
265-
return sprintf(buf, "%d\n",
266-
iommu_present(&pci_bus_type) && dmar_platform_optin());
258+
struct tb *tb = container_of(dev, struct tb, dev);
259+
260+
return sysfs_emit(buf, "%d\n", tb->nhi->iommu_dma_protection);
267261
}
268262
static DEVICE_ATTR_RO(iommu_dma_protection);
269263

drivers/thunderbolt/nhi.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
#include <linux/pci.h>
1616
#include <linux/dma-mapping.h>
1717
#include <linux/interrupt.h>
18+
#include <linux/iommu.h>
1819
#include <linux/module.h>
1920
#include <linux/delay.h>
2021
#include <linux/property.h>
22+
#include <linux/string_helpers.h>
2123

2224
#include "nhi.h"
2325
#include "nhi_regs.h"
@@ -1103,6 +1105,47 @@ static void nhi_check_quirks(struct tb_nhi *nhi)
11031105
nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
11041106
}
11051107

1108+
static int nhi_check_iommu_pdev(struct pci_dev *pdev, void *data)
1109+
{
1110+
if (!pdev->external_facing ||
1111+
!device_iommu_capable(&pdev->dev, IOMMU_CAP_PRE_BOOT_PROTECTION))
1112+
return 0;
1113+
*(bool *)data = true;
1114+
return 1; /* Stop walking */
1115+
}
1116+
1117+
static void nhi_check_iommu(struct tb_nhi *nhi)
1118+
{
1119+
struct pci_bus *bus = nhi->pdev->bus;
1120+
bool port_ok = false;
1121+
1122+
/*
1123+
* Ideally what we'd do here is grab every PCI device that
1124+
* represents a tunnelling adapter for this NHI and check their
1125+
* status directly, but unfortunately USB4 seems to make it
1126+
* obnoxiously difficult to reliably make any correlation.
1127+
*
1128+
* So for now we'll have to bodge it... Hoping that the system
1129+
* is at least sane enough that an adapter is in the same PCI
1130+
* segment as its NHI, if we can find *something* on that segment
1131+
* which meets the requirements for Kernel DMA Protection, we'll
1132+
* take that to imply that firmware is aware and has (hopefully)
1133+
* done the right thing in general. We need to know that the PCI
1134+
* layer has seen the ExternalFacingPort property which will then
1135+
* inform the IOMMU layer to enforce the complete "untrusted DMA"
1136+
* flow, but also that the IOMMU driver itself can be trusted not
1137+
* to have been subverted by a pre-boot DMA attack.
1138+
*/
1139+
while (bus->parent)
1140+
bus = bus->parent;
1141+
1142+
pci_walk_bus(bus, nhi_check_iommu_pdev, &port_ok);
1143+
1144+
nhi->iommu_dma_protection = port_ok;
1145+
dev_dbg(&nhi->pdev->dev, "IOMMU DMA protection is %s\n",
1146+
str_enabled_disabled(port_ok));
1147+
}
1148+
11061149
static int nhi_init_msi(struct tb_nhi *nhi)
11071150
{
11081151
struct pci_dev *pdev = nhi->pdev;
@@ -1220,6 +1263,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
12201263
return -ENOMEM;
12211264

12221265
nhi_check_quirks(nhi);
1266+
nhi_check_iommu(nhi);
12231267

12241268
res = nhi_init_msi(nhi);
12251269
if (res) {

include/linux/thunderbolt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
465465
* @msix_ida: Used to allocate MSI-X vectors for rings
466466
* @going_away: The host controller device is about to disappear so when
467467
* this flag is set, avoid touching the hardware anymore.
468+
* @iommu_dma_protection: An IOMMU will isolate external-facing ports.
468469
* @interrupt_work: Work scheduled to handle ring interrupt when no
469470
* MSI-X is used.
470471
* @hop_count: Number of rings (end point hops) supported by NHI.
@@ -479,6 +480,7 @@ struct tb_nhi {
479480
struct tb_ring **rx_rings;
480481
struct ida msix_ida;
481482
bool going_away;
483+
bool iommu_dma_protection;
482484
struct work_struct interrupt_work;
483485
u32 hop_count;
484486
unsigned long quirks;

0 commit comments

Comments
 (0)