From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83969360EC0 for ; Sun, 5 Jul 2026 22:08:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783289317; cv=none; b=FNp8TWYffW5Lh6ffHppgOQGOaPV17Hbvc6H/PgQ1h1NKmDPeh7FzEH8IcUk9pdPqTvUf3CrrPSH7uV7+2QkYZuyxIM+dJIAr1ml8MCt/W89iBB8PV7TMrOUHPQOzMOZm/ZsTb1ngHkdTzyjsFJxUibnKtYmGaThh8PEJotidyH4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783289317; c=relaxed/simple; bh=PhWfIV32DkkR/+SviOVcSMEpYGZliI89IVWX5QDxTks=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nn623A1xVzKrJD0692ckijBKC7EOU15WbWvUYaZlzk4tpqbYflBS7LQCWsay2MhyuQcyQ28AveFKobE+94yIt1HMkTJNM4XbjdN+aQLkzHdiYRhsZ81UHFaPXzsUt83g0e90omXNKeIJYd42Xesgwj1nZvADY8qrimogjdpFUXU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Yr6W5xwA; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Yr6W5xwA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0C3991F00ACA; Sun, 5 Jul 2026 22:08:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1783289315; bh=l7PRaL6tsrr7HOFGf6yJxdhPNLJYfWpA/s26s2btWV0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Yr6W5xwAMDns723Y74tgxZ7DgotLhL76P5G/dj3T9G+9CuPFZU4YP5LC6noeVvTuU +Zn0LAdKDPQTOrtmG3ABN93DsoBa7qnUYXZPEPkSHoqRbvkNOvs1IwYVHLuo4WKJz4 IL/b6K3WC9a5yIYNCgZIobcOh/CQwQVT2S1soQDa1wvTj0AZAyzuVzy/i2rVNpX/L3 8E65SoLGHYuS7WmQkhDSvZfM90bBFJ5CmHD8lwjCci29A5hcvgIQYf0gZu0qo6Dw8P c5hCst3vBCTvKA3zjdMDv37DA2mluVTuYZoStq2F1N06A6+Q2NEm3cuSV1rJQhYyeV /124GZvEVLNZg== Received: from phl-compute-10.internal (phl-compute-10.internal [10.202.2.50]) by mailfauth.phl.internal (Postfix) with ESMTP id 441BFF40069; Sun, 5 Jul 2026 18:08:34 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-10.internal (MEProxy); Sun, 05 Jul 2026 18:08:34 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTFFhnrK7Q4+cShYTMBOkizHeZCgSaG/tGWY9LdyYKFH25UNRWJVO6yYXESHxB/SCv DQRbccyPRK+tJDc6XZtmn2JiHHk6Ag+j12fVpZXgWVLE0DndUEnt8lOvTPfZe+QaHsRvyh MDrApxX0I7y0rzWnyotpdNIcdMuu6SVrxnhTW1SZ6cd0/lCBtiynAE4vbSFTcrDPQCWzgR sK9QQss3PTR+mOmilWKYs7C8Q7uQwrbIqXydvTrAEAaDRFS6sU0A8JaHSkNr52k8JCJnOz +DUqbdwZjIplBxLaKC/mxZNc+6u0fanv8CdAf2hVNBGDD3/aQsaFO0eQZ8N3P6JVu+bouI tM23KZurzluFKgXuvJ6SY8N5q4rNsmwYpPsziZClpqYACIfn/STLlp2gAef54FC+Wn2/E5 uZpTzzjNyB8hp5sAANj4Trab809bl8NKvBm2VODDpwwnSv9SCv0b4yQvUkLC3XMrAcMJPn 9TxsD2VaZ1HxAHeGxM7R3FSSMDSfwEPXQ4+hTVotgyjG6hU/ofvNNa/Qbazy4LUav698Ee g4JtMYAo3MdbueWPgNKraULp4uWp6bmXGOtg7krR+0ccMugSIJ8oKjXXkIuB2oBps8Z4y7 tOYgz0zm3enBpKZHbS4l95x9sr0YyijZmdbpObwQXBkNiCT81SwwkfILW5Bw X-ME-Proxy: Feedback-ID: i67ae4b3e:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sun, 5 Jul 2026 18:08:33 -0400 (EDT) From: Dan Williams To: linux-coco@lists.linux.dev Cc: linux-pci@vger.kernel.org, driver-core@lists.linux.dev, ankita@nvidia.com, Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Will Deacon , Robin Murphy , Jason Gunthorpe , Bjorn Helgaas , Alexey Kardashevskiy , Xu Yilun , "Aneesh Kumar K.V" , Dexuan Cui Subject: [PATCH 09/15] PCI, device core: Move "untrusted" concept to DEVICE_TRUST_ADVERSARY Date: Sun, 5 Jul 2026 15:08:13 -0700 Message-ID: <20260705220819.2472765-10-djbw@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260705220819.2472765-1-djbw@kernel.org> References: <20260705220819.2472765-1-djbw@kernel.org> Precedence: bulk X-Mailing-List: linux-coco@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit An "adversary" device is one that the kernel is allowed to operate, but with extra safety / paranoia added. It aims to keep the device bounded in both address space and time (no batched invalidation) for DMA accesses. The concept of adversarial devices already exists in the PCI core. The categorization is applied to externally attached PCI devices by default (like Thunderbolt attached devices). Unlike DEVICE_TRUST_NONE that says "do not allow driver bind", DEVICE_TRUST_ADVERSARY says "allow driver bind as long as the driver and/or IOMMU are taking precautions." Uplevel this trust mechanism from a PCI device "untrusted" boolean flag to the core device trust level. The rationale for dev->bus_trust separate from dev->p->trust is to give buses the ability to establish a trust level before device_add(). Then, after device_add(), the dependency on 'struct device_private' mandates that the device-core own and coordinate changes to the device's trust level. There are implications of the bus expressing less than full trust in a device and a module later expressing higher levels of trust. For PCI, the "adversary" / pci_untrusted() determination is made prior to initial iommu_probe_device(). The default IOMMU domain determination is latched from bus's specified trust value. Later changes to the trust level, for example by "modprobe $module trust=auto" skip that IOMMU probing. The result is that moving a device to DEVICE_TRUST_AUTO may lift translation blocking (PCI_ACS_TB), but not change the default IOMMU domain from IOMMU_DOMAIN_DMA. The default domain can be validated / changed via iommu_group sysfs. The longer term goal is to dynamically adjust iommu domain at the next $bus_dma_configure() event, but that needs more surgery and consideration for multi-device iommu groups. Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Danilo Krummrich Cc: Will Deacon Cc: Robin Murphy Cc: Jason Gunthorpe Cc: Bjorn Helgaas Cc: Alexey Kardashevskiy Cc: Xu Yilun Cc: "Aneesh Kumar K.V" Cc: Dexuan Cui Signed-off-by: Dan Williams --- drivers/base/Kconfig | 13 +++++++++++++ drivers/pci/Kconfig | 1 + include/linux/device.h | 3 +++ include/linux/device/trust.h | 20 +++++++++++++++----- include/linux/pci.h | 13 +++++-------- drivers/base/trust.c | 20 +++++++++++++++++++- drivers/iommu/amd/iommu.c | 2 +- drivers/iommu/dma-iommu.c | 13 ++++--------- drivers/iommu/intel/iommu.c | 2 +- drivers/iommu/iommu.c | 2 +- drivers/pci/ats.c | 2 +- drivers/pci/pci.c | 2 +- drivers/pci/probe.c | 10 +++++++--- drivers/pci/quirks.c | 4 ++-- 14 files changed, 74 insertions(+), 33 deletions(-) diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index a08523d348d8..a4233bdf9804 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -291,6 +291,14 @@ config DEVICE_TRUST_NONE possible, the device is blocked by an IOMMU from accessing assets. +config DEVICE_TRUST_ADVERSARY + bool "Adversary" + help + Device is allowed to bind. Bus, IOMMU, and driver layers may + react to this trust level by disabling access validation + bypass mechanisms like PCI ATS. When device is unbound from a + driver the device is blocked by an IOMMU where possible. + config DEVICE_TRUST_AUTO bool "Auto" help @@ -317,6 +325,11 @@ config BUILTIN_DEVICE_TRUST_AUTO a driver and deploy all available mechanisms to allow performant direct memory access This trust level does not grant TCB privileges. +config BUILTIN_DEVICE_TRUST_ADVERSARY + bool "Adversary" + help + Deploy mitigations in the IOMMU layer and driver to limit access. + endchoice endmenu diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index ed17b5d2d5ae..185b42cefe20 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -22,6 +22,7 @@ menuconfig PCI bool "PCI support" depends on HAVE_PCI depends on MMU + select DEVICE_TRUST help This option enables support for the PCI local bus, including support for PCI-X and the foundations for PCI Express support. diff --git a/include/linux/device.h b/include/linux/device.h index 7b2baffdd2f5..3e203d573a58 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -688,6 +688,8 @@ enum struct_device_flags { * @removable: Whether the device can be removed from the system. This * should be set by the subsystem / bus driver that discovered * the device. + * @bus_trust: Device's initial / prior to device_add() trust level. + * * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -791,6 +793,7 @@ struct device { struct device_physical_location *physical_location; enum device_removable removable; + enum device_trust bus_trust; DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h index 3377d26dc485..283d3196e5e6 100644 --- a/include/linux/device/trust.h +++ b/include/linux/device/trust.h @@ -11,30 +11,40 @@ * * @DEVICE_TRUST_UNSET: Unregistered device object with no current bus * @DEVICE_TRUST_NONE: Blocked when idle, cannot bind + * @DEVICE_TRUST_ADVERSARY: Blocked when idle, constrained when active. * @DEVICE_TRUST_AUTO: All typical privileges granted + * + * Devices flagged as adversarial are the ones that can potentially + * execute DMA attacks and similar. They are typically connected through + * external ports such as Thunderbolt but not limited to that. When an + * IOMMU is enabled they should be getting full mappings to make sure + * they cannot access arbitrary memory. */ enum device_trust { DEVICE_TRUST_UNSET, DEVICE_TRUST_NONE, + DEVICE_TRUST_ADVERSARY, DEVICE_TRUST_AUTO, }; -#define DEVICE_DEFAULT_TRUST \ - (IS_ENABLED(CONFIG_DEVICE_TRUST_NONE) ? DEVICE_TRUST_NONE : \ - DEVICE_TRUST_AUTO) +#define DEVICE_DEFAULT_TRUST \ + (IS_ENABLED(CONFIG_DEVICE_TRUST_NONE) ? DEVICE_TRUST_NONE : \ + IS_ENABLED(CONFIG_DEVICE_TRUST_ADVERSARY) ? DEVICE_TRUST_ADVERSARY : \ + DEVICE_TRUST_AUTO) struct device; struct device_driver; #ifdef CONFIG_DEVICE_TRUST +bool device_untrusted(struct device *dev); void module_driver_trust(struct module *mod, const char *val); -void module_driver_trust_init(struct module *mod, bool distrust); +void module_driver_trust_init(struct module *mod, bool require_trust); #else static inline void module_driver_trust(struct module *mod, const char *val) { pr_warn("module: %s: trust= support disabled\n", mod->name); } -static inline void module_driver_trust_init(struct module *mod, bool distrust) +static inline void module_driver_trust_init(struct module *mod, bool require_trust) { } #endif diff --git a/include/linux/pci.h b/include/linux/pci.h index ebb5b9d76360..095ea37fc6d0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -485,14 +485,6 @@ struct pci_dev { unsigned int shpc_managed:1; /* SHPC owned by shpchp */ unsigned int is_thunderbolt:1; /* Thunderbolt controller */ unsigned int is_cxl:1; /* Compute Express Link (CXL) */ - /* - * Devices marked being untrusted are the ones that can potentially - * execute DMA attacks and similar. They are typically connected - * through external ports such as Thunderbolt but not limited to - * that. When an IOMMU is enabled they should be getting full - * mappings to make sure they cannot access arbitrary memory. - */ - unsigned int untrusted:1; /* * Info from the platform, e.g., ACPI or device tree, may mark a * device as "external-facing". An external-facing device is @@ -812,6 +804,11 @@ static inline bool pcie_is_cxl(struct pci_dev *pci_dev) return pci_dev->is_cxl; } +static inline bool pci_untrusted(struct pci_dev *pdev) +{ + return device_untrusted(&pdev->dev); +} + #define for_each_pci_bridge(dev, bus) \ list_for_each_entry(dev, &bus->devices, bus_list) \ if (!pci_is_bridge(dev)) {} else diff --git a/drivers/base/trust.c b/drivers/base/trust.c index 0fd494e1557d..8efbe5c51250 100644 --- a/drivers/base/trust.c +++ b/drivers/base/trust.c @@ -6,15 +6,28 @@ #include #include "base.h" +/* If the bus did not initialize trust, set a default */ void device_initialize_trust(struct device *dev) { + dev->p->trust = dev->bus_trust; if (dev->p->trust == DEVICE_TRUST_UNSET) dev->p->trust = DEVICE_DEFAULT_TRUST; } +/* + * ->bus_trust is evaluated / manipulated prior to device_add() and + * synced with dev->p->trust post device_add() under device_lock(). + */ +bool device_untrusted(struct device *dev) +{ + return dev->bus_trust && dev->bus_trust <= DEVICE_TRUST_ADVERSARY; +} + /* Driver trust policy requires modules, builtin drivers always attach */ static enum device_trust builtin_driver_trust(void) { + if (IS_ENABLED(CONFIG_BUILTIN_DEVICE_TRUST_ADVERSARY)) + return DEVICE_TRUST_ADVERSARY; return DEVICE_TRUST_AUTO; } @@ -30,18 +43,23 @@ static enum device_trust driver_trust(struct module *mod) * policy on trusting devices it attaches, update the device's trust * level from that policy. Trust privileges beyond driver bind are * realized in a bus's ->dma_configure(). + * + * Reflect the operational trust level back to the public indicator. */ bool device_trust_bind(const struct device_driver *drv, struct device *dev) { enum device_trust drv_trust = driver_trust(drv->owner); - if (drv_trust != DEVICE_TRUST_UNSET) + if (drv_trust != DEVICE_TRUST_UNSET) { dev->p->trust = drv_trust; + dev->bus_trust = drv_trust; + } return dev->p->trust > DEVICE_TRUST_NONE; } static const char * const device_trust_names[] = { [DEVICE_TRUST_NONE] = "none", + [DEVICE_TRUST_ADVERSARY] = "adversary", [DEVICE_TRUST_AUTO] = "auto", }; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 563f9c2672d5..ef663e3efc70 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -3121,7 +3121,7 @@ static int amd_iommu_def_domain_type(struct device *dev) return 0; /* Always use DMA domain for untrusted device */ - if (dev_is_pci(dev) && to_pci_dev(dev)->untrusted) + if (device_untrusted(dev)) return IOMMU_DOMAIN_DMA; /* diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 9abaec0703ef..957ef77911a9 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -588,16 +588,11 @@ static int iova_reserve_iommu_regions(struct device *dev, return ret; } -static bool dev_is_untrusted(struct device *dev) -{ - return dev_is_pci(dev) && to_pci_dev(dev)->untrusted; -} - static bool dev_use_swiotlb(struct device *dev, size_t size, enum dma_data_direction dir) { return IS_ENABLED(CONFIG_SWIOTLB) && - (dev_is_untrusted(dev) || + (device_untrusted(dev) || dma_kmalloc_needs_bounce(dev, size, dir)); } @@ -610,7 +605,7 @@ static bool dev_use_sg_swiotlb(struct device *dev, struct scatterlist *sg, if (!IS_ENABLED(CONFIG_SWIOTLB)) return false; - if (dev_is_untrusted(dev)) + if (device_untrusted(dev)) return true; /* @@ -1188,7 +1183,7 @@ static phys_addr_t iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys, * swiotlb_tbl_map_single() has initialized the bounce buffer proper to * the contents of the original memory buffer. */ - if (phys != (phys_addr_t)DMA_MAPPING_ERROR && dev_is_untrusted(dev)) { + if (phys != (phys_addr_t)DMA_MAPPING_ERROR && device_untrusted(dev)) { size_t start, virt = (size_t)phys_to_virt(phys); /* Pre-padding */ @@ -1761,7 +1756,7 @@ size_t iommu_dma_opt_mapping_size(void) size_t iommu_dma_max_mapping_size(struct device *dev) { - if (dev_is_untrusted(dev)) + if (device_untrusted(dev)) return swiotlb_max_mapping_size(dev); return SIZE_MAX; diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 849d06dfe1ae..82d9abe87d99 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -3501,7 +3501,7 @@ static bool intel_iommu_is_attach_deferred(struct device *dev) */ static bool risky_device(struct pci_dev *pdev) { - if (pdev->untrusted) { + if (pci_untrusted(pdev)) { pci_info(pdev, "Skipping IOMMU quirk for dev [%04X:%04X] on untrusted PCI link\n", pdev->vendor, pdev->device); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index e8f13dcebbde..4769d2b548d6 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1901,7 +1901,7 @@ static int iommu_get_default_domain_type(struct iommu_group *group, driver_type = iommu_get_def_domain_type(group, gdev->dev, driver_type); - if (dev_is_pci(gdev->dev) && to_pci_dev(gdev->dev)->untrusted) { + if (device_untrusted(gdev->dev)) { /* * No ARM32 using systems will set untrusted, it cannot * work. diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 96efa00d9743..7296f0ce7530 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -43,7 +43,7 @@ bool pci_ats_supported(struct pci_dev *dev) if (!dev->ats_cap) return false; - return (dev->untrusted == 0); + return !pci_untrusted(dev); } EXPORT_SYMBOL_GPL(pci_ats_supported); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77b17b13ee61..ff1ca74f30b1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -994,7 +994,7 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps) caps->ctrl |= (dev->acs_capabilities & PCI_ACS_UF); /* Enable Translation Blocking for external devices and noats */ - if (pci_ats_disabled() || dev->external_facing || dev->untrusted) + if (pci_ats_disabled() || dev->external_facing || pci_untrusted(dev)) caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB); } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dd0abbc63e18..91a7bbcfdecb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1737,20 +1738,23 @@ static void set_pcie_untrusted(struct pci_dev *dev) { struct pci_dev *parent = pci_upstream_bridge(dev); + dev->dev.bus_trust = DEVICE_DEFAULT_TRUST; if (!parent) return; /* * If the upstream bridge is untrusted we treat this device as * untrusted as well. */ - if (parent->untrusted) { - dev->untrusted = true; + if (pci_untrusted(parent)) { + dev->dev.bus_trust = + min(dev->dev.bus_trust, parent->dev.bus_trust); return; } if (arch_pci_dev_is_removable(dev)) { pci_dbg(dev, "marking as untrusted\n"); - dev->untrusted = true; + dev->dev.bus_trust = + min(dev->dev.bus_trust, DEVICE_TRUST_ADVERSARY); } } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b09f27f7846f..73e473856999 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5370,7 +5370,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) * PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF * * TODO: This quirk also needs to do equivalent of PCI_ACS_TB, - * if dev->external_facing || dev->untrusted + * if dev->external_facing || pci_untrusted(dev) */ static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) { @@ -5411,7 +5411,7 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev) ctrl |= (cap & PCI_ACS_CR); ctrl |= (cap & PCI_ACS_UF); - if (pci_ats_disabled() || dev->external_facing || dev->untrusted) + if (pci_ats_disabled() || dev->external_facing || pci_untrusted(dev)) ctrl |= (cap & PCI_ACS_TB); pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl); -- 2.54.0