* [PATCH 00/17] driver core: Authorize device trust before driver binding
@ 2026-11-05 11:42 Leon Romanovsky
2026-11-05 11:42 ` [PATCH 01/17] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
` (16 more replies)
0 siblings, 17 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
Device trust was a concept introduced by Dan in his series here:
https://lore.kernel.org/all/20260705220819.2472765-1-djbw@kernel.org
This series takes just the Device Trust idea and works to introduce
it aside from the other components.
Device Trust is a general concept that already exists in PCI via the
'untrusted' flag. This series generalizes that into a consistent
cross-bus concept that allow the user to inject their own opinion into
how the kernel should operate the device. Three levels are defined:
- DISABLED: Device is not operated by the kernel and is kept
quarantined. Confidential computing kernels will start all
devices in that mode.
- ADVERSARY: Device may be malicious and the kernel should defend
itself, eg by forcing use of a strong iommu, not exposing random
kernel memory to DMA and maybe even protections in the driver from
malicious data in DMAs.
- FULL: The normal kernel operating mode.
Aside from this basic idea of defining the three levels, this series
also introduces a mechanism for userspace to make a policy decision
which level the device should operate in.
This is important for Confidential Computing systems that need to
evaluate a device, do a cryptographic authentication, enable PCIe link
encryption, and other steps designed to protect the system. These
operations need to be done in userspace before any DMA access to the
device is allowed.
Using this series the user can boot the kernel with a default trust
policy of DISABLED, allowing only a small number of devices like
interrupt controllers, timers and iommus to start. Then the kernel can
get into the initrd where a userspace policy agent and implement
whatever decisions are required.
For Confidential Computing it is important that the policy logic be
robustly inclusive. If the kernel autoprobes any sort of real HW
device without going through the policy phase it could become an
attack vector.
Finally the policy agent can set the trust level and then cause a
driver to bind in the normal way. User control of driver binding was
already setup in 2007 via commit b8c5cec23d5c ("Driver core: udev
triggered device-<>driver binding") which relied on the
drivers_autoprobe to work.
At least for Confidential Computing a high security policy in
userspace would leave unexpected devices as DISABLED, authenticated
PCI devices as FULL and everything else ADVERSARIAL.
A draft starting point for a policy agent has been prepared:
https://github.com/rleon/systemd/commits/accept-d-trust-level
As a proposed systemd service that manages the process.
Thanks
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Leon Romanovsky (17):
Input: gameport - attach manual drivers through the driver core
Input: serio - attach manual drivers through the driver core
net: phy: Attach generic PHY drivers through the driver core
wifi: mac80211_hwsim: lock device for manual driver binding
USB: Lock registered interfaces during manual driver binding
PNP: Attach card devices through the driver core
cdx: Propagate DMA configuration errors
driver core: Allow userspace to detect EPROBE_DEFER when using sysfs drivers_probe
driver core: Introduce a generic device trust level
driver core: Expose device trust authorization through sysfs
driver core: Accept device trust policy on the kernel command line
driver core: Accept device trust policy by driver name
driver core: Preserve trust commitment beyond driver binding
driver core: Add a global initial device trust policy
driver core: Cover trust authorization and binding with KUnit
driver core: Document trust admission and initrd authorization
PCI: Replace untrusted state with device trust
Documentation/ABI/testing/sysfs-bus-drivers-probe | 13 +
Documentation/ABI/testing/sysfs-devices-trust | 61 +
Documentation/admin-guide/kernel-parameters.txt | 64 +
.../driver-api/driver-model/device-trust.rst | 294 ++++
Documentation/driver-api/driver-model/index.rst | 1 +
MAINTAINERS | 2 +
drivers/base/Makefile | 2 +-
drivers/base/base.h | 37 +
drivers/base/bus.c | 9 +-
drivers/base/core.c | 18 +-
drivers/base/dd.c | 88 +-
drivers/base/test/Makefile | 1 +
drivers/base/test/device-trust-test.c | 1607 ++++++++++++++++++++
drivers/base/trust.c | 455 ++++++
drivers/bluetooth/btusb_main.c | 42 +-
drivers/cdx/cdx.c | 2 +-
drivers/input/gameport/gameport.c | 21 +-
drivers/input/serio/serio.c | 24 +-
drivers/iommu/amd/iommu.c | 3 +-
drivers/iommu/dma-iommu.c | 14 +-
drivers/iommu/intel/iommu.c | 17 +-
drivers/iommu/iommu.c | 17 +-
drivers/net/phy/phy_device.c | 39 +-
drivers/net/wireless/virtual/mac80211_hwsim_main.c | 2 +
drivers/pci/ats.c | 4 +-
drivers/pci/pci.c | 5 +-
drivers/pci/probe.c | 26 +-
drivers/pci/quirks.c | 7 +-
drivers/pnp/card.c | 12 +-
drivers/usb/core/driver.c | 30 +-
drivers/usb/core/message.c | 10 +
drivers/usb/core/usb.h | 2 +
include/linux/device.h | 16 +
include/linux/device/bus.h | 10 +
include/linux/device/trust.h | 48 +
include/linux/pci.h | 30 +-
36 files changed, 2886 insertions(+), 147 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20260915-device-trust-core-12934e618489
Best regards,
--
Leon Romanovsky <leonro@nvidia.com>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 01/17] Input: gameport - attach manual drivers through the driver core
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 02/17] Input: serio " Leon Romanovsky
` (15 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Manual gameport binding calls connect before publishing the binding, so
driver-core admission cannot reject a device before functional setup.
Use device_driver_attach() to run the existing bus probe and removal
callbacks under the device lock. This gives manual attachment the same
admission, setup and failure cleanup as automatic probing.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/input/gameport/gameport.c | 21 +--------------------
1 file changed, 1 insertion(+), 20 deletions(-)
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 9707b155bc947..c9efec2dc55da 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -214,26 +214,7 @@ static void gameport_run_poll_handler(struct timer_list *t)
static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
{
- int error;
-
- gameport->dev.driver = &drv->driver;
- if (drv->connect(gameport, drv)) {
- gameport->dev.driver = NULL;
- return -ENODEV;
- }
-
- error = device_bind_driver(&gameport->dev);
- if (error) {
- dev_warn(&gameport->dev,
- "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
- gameport->phys, gameport->name,
- drv->description, error);
- drv->disconnect(gameport);
- gameport->dev.driver = NULL;
- return error;
- }
-
- return 0;
+ return device_driver_attach(&drv->driver, &gameport->dev);
}
static void gameport_find_driver(struct gameport *gameport)
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 02/17] Input: serio - attach manual drivers through the driver core
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-05 11:42 ` [PATCH 01/17] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 17:05 ` Dmitry Torokhov
2026-11-05 11:42 ` [PATCH 03/17] net: phy: Attach generic PHY " Leon Romanovsky
` (14 subsequent siblings)
16 siblings, 1 reply; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Manual serio binding connects a driver before driver core can admit the
device. A rejected binding therefore arrives after functional setup.
Keep the port ID check and use device_driver_attach() for the existing bus
probe path. Driver core then serializes admission and handles probe failure
before publishing the binding.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/input/serio/serio.c | 24 +++---------------------
1 file changed, 3 insertions(+), 21 deletions(-)
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 54dd26249b027..604440244c1ca 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -81,28 +81,10 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser
static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
{
- int error;
-
- if (serio_match_port(drv->id_table, serio)) {
-
- serio->dev.driver = &drv->driver;
- if (serio_connect_driver(serio, drv)) {
- serio->dev.driver = NULL;
- return -ENODEV;
- }
+ if (!serio_match_port(drv->id_table, serio))
+ return 0;
- error = device_bind_driver(&serio->dev);
- if (error) {
- dev_warn(&serio->dev,
- "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
- serio->phys, serio->name,
- drv->description, error);
- serio_disconnect_driver(serio);
- serio->dev.driver = NULL;
- return error;
- }
- }
- return 0;
+ return device_driver_attach(&drv->driver, &serio->dev);
}
static void serio_find_driver(struct serio *serio)
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 03/17] net: phy: Attach generic PHY drivers through the driver core
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-05 11:42 ` [PATCH 01/17] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
2026-11-05 11:42 ` [PATCH 02/17] Input: serio " Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 14:50 ` Andrew Lunn
2026-11-05 15:33 ` Andrew Lunn
2026-11-05 11:42 ` [PATCH 04/17] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
` (13 subsequent siblings)
16 siblings, 2 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
The generic PHY fallback probes directly before publishing its binding,
bypassing driver-core admission and supplier checks.
Attach through the driver core so admission precedes functional setup.
Callers hold RTNL, which a concurrent bind of a specific driver may wait
for with the device lock held, so for this flow let's add
device_driver_try_attach() so the attachment never waits for that lock.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/dd.c | 65 +++++++++++++++++++++++++++++++++++++-------
drivers/net/phy/phy_device.c | 39 +++++++++++++++++---------
include/linux/device.h | 2 ++
3 files changed, 83 insertions(+), 23 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 52123251965de..01026e02de92e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1046,6 +1046,25 @@ static void __device_driver_lock(struct device *dev, struct device *parent)
device_lock(dev);
}
+/*
+ * __device_driver_trylock - __device_driver_lock() without sleeping
+ *
+ * Returns true if the locks were taken.
+ */
+static bool __device_driver_trylock(struct device *dev, struct device *parent)
+{
+ if (dev->type && dev->type->need_parent_lock && parent) {
+ if (!device_trylock(parent))
+ return false;
+ if (!device_trylock(dev)) {
+ device_unlock(parent);
+ return false;
+ }
+ return true;
+ }
+ return device_trylock(dev);
+}
+
/*
* __device_driver_unlock - release locks needed to manipulate dev->drv
* @dev: Device we will update driver info for
@@ -1195,19 +1214,17 @@ void device_initial_probe(struct device *dev)
subsys_put(sp);
}
-/**
- * device_driver_attach - attach a specific driver to a specific device
- * @drv: Driver to attach
- * @dev: Device to attach it to
- *
- * Manually attach driver to a device. Will acquire both @dev lock and
- * @dev->parent lock if needed. Returns 0 on success, -ERR on failure.
- */
-int device_driver_attach(const struct device_driver *drv, struct device *dev)
+static int __device_driver_attach(const struct device_driver *drv,
+ struct device *dev, bool trylock)
{
int ret;
- __device_driver_lock(dev, dev->parent);
+ if (trylock) {
+ if (!__device_driver_trylock(dev, dev->parent))
+ return -EBUSY;
+ } else {
+ __device_driver_lock(dev, dev->parent);
+ }
ret = __driver_probe_device(drv, dev);
__device_driver_unlock(dev, dev->parent);
@@ -1218,8 +1235,36 @@ int device_driver_attach(const struct device_driver *drv, struct device *dev)
return -EAGAIN;
return ret;
}
+
+/**
+ * device_driver_attach - attach a specific driver to a specific device
+ * @drv: Driver to attach
+ * @dev: Device to attach it to
+ *
+ * Manually attach driver to a device. Will acquire both @dev lock and
+ * @dev->parent lock if needed. Returns 0 on success, -ERR on failure.
+ */
+int device_driver_attach(const struct device_driver *drv, struct device *dev)
+{
+ return __device_driver_attach(drv, dev, false);
+}
EXPORT_SYMBOL_GPL(device_driver_attach);
+/**
+ * device_driver_try_attach - attach a specific driver without waiting for locks
+ * @drv: Driver to attach
+ * @dev: Device to attach it to
+ *
+ * Like device_driver_attach(), for callers that hold a lock which the holder
+ * of the @dev or @dev->parent lock may wait for. Returns -EBUSY, before any
+ * probing, if one of those locks is contended.
+ */
+int device_driver_try_attach(const struct device_driver *drv, struct device *dev)
+{
+ return __device_driver_attach(drv, dev, true);
+}
+EXPORT_SYMBOL_GPL(device_driver_try_attach);
+
static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
{
struct device *dev = _dev;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d4508bcfbe99..8cc0de755bd08 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1912,7 +1912,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
struct device *d = &phydev->mdio.dev;
struct module *ndev_owner = NULL;
struct module *drv_owner;
+ struct device_driver *drv;
int irq = phydev->irq;
+ bool bind_genphy;
int err;
/* For Ethernet device drivers that register their own MDIO bus, we
@@ -1939,16 +1941,16 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
/* Assume that if there is no driver, that it doesn't
* exist, and we should use the genphy driver.
*/
- if (!d->driver) {
+ drv = d->driver;
+ bind_genphy = !drv;
+ if (bind_genphy) {
if (phydev->is_c45)
- d->driver = &genphy_c45_driver.mdiodrv.driver;
+ drv = &genphy_c45_driver.mdiodrv.driver;
else
- d->driver = &genphy_driver.mdiodrv.driver;
-
- phydev->is_genphy_driven = 1;
+ drv = &genphy_driver.mdiodrv.driver;
}
- drv_owner = d->driver->owner;
+ drv_owner = drv->owner;
if (!try_module_get(drv_owner)) {
phydev_err(phydev, "failed to get the device driver module\n");
err = -EIO;
@@ -1956,11 +1958,20 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
}
phydev->drv_owner = drv_owner;
- if (phydev->is_genphy_driven) {
- err = d->driver->probe(d);
- if (err >= 0)
- err = device_bind_driver(d);
-
+ if (bind_genphy) {
+ /*
+ * RTNL is held, and a concurrent bind of a specific driver may
+ * wait for it with the device lock held. Never wait for that
+ * lock, and let the caller retry.
+ */
+ err = device_driver_try_attach(drv, d);
+ if (err == -EBUSY) {
+ /* Another bind owns the PHY, so nothing was probed here. */
+ irq = phydev->irq;
+ err = -EPROBE_DEFER;
+ } else if (err == -EAGAIN) {
+ err = -EPROBE_DEFER;
+ }
if (err)
goto error_module_put;
}
@@ -2071,8 +2082,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
module_put(phydev->drv_owner);
phydev->drv_owner = NULL;
phydev->irq = irq;
- phydev->is_genphy_driven = 0;
- d->driver = NULL;
error_put_device:
put_device(d);
if (ndev_owner != bus->owner)
@@ -3813,6 +3822,8 @@ static int phy_probe(struct device *dev)
int err = 0;
phydev->drv = phydrv;
+ phydev->is_genphy_driven = phydrv == &genphy_driver ||
+ phydrv == &genphy_c45_driver;
/* Disable the interrupt if the PHY doesn't support it
* but the interrupt is still a valid one
@@ -3949,6 +3960,7 @@ static int phy_probe(struct device *dev)
out_reset:
/* Re-assert the reset signal on error */
phy_device_reset(phydev, 1);
+ phydev->is_genphy_driven = 0;
return err;
}
@@ -3977,6 +3989,7 @@ static int phy_remove(struct device *dev)
phy_device_reset(phydev, 1);
phydev->drv = NULL;
+ phydev->is_genphy_driven = 0;
return 0;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index 429e39f647cf9..4cf62d69eac4f 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1314,6 +1314,8 @@ static inline void *dev_get_platdata(const struct device *dev)
*/
int __must_check device_driver_attach(const struct device_driver *drv,
struct device *dev);
+int __must_check device_driver_try_attach(const struct device_driver *drv,
+ struct device *dev);
int __must_check device_bind_driver(struct device *dev);
void device_release_driver(struct device *dev);
int __must_check device_attach(struct device *dev);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 04/17] wifi: mac80211_hwsim: lock device for manual driver binding
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (2 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 03/17] net: phy: Attach generic PHY " Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 05/17] USB: Lock registered interfaces during " Leon Romanovsky
` (12 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
The documented API contract for device_bind_driver() requires the device
lock. mac80211_hwsim uses it for a newly created class device with no bus,
so no automatic driver-core path can bind the device concurrently and the
missing lock was safe.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/net/wireless/virtual/mac80211_hwsim_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index e53714b81ec2c..b796da3dd54b3 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -5769,7 +5769,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver.driver;
+ device_lock(data->dev);
err = device_bind_driver(data->dev);
+ device_unlock(data->dev);
if (err != 0) {
pr_debug("mac80211_hwsim: device_bind_driver failed (%d)\n",
err);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 05/17] USB: Lock registered interfaces during manual driver binding
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (3 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 04/17] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 17:24 ` Alan Stern
2026-11-05 11:42 ` [PATCH 06/17] PNP: Attach card devices through the driver core Leon Romanovsky
` (11 subsequent siblings)
16 siblings, 1 reply; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Interface claims hold the USB device lock, but device_bind_driver()
requires the target interface lock. Without it, trust-policy writes can
race admission.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/bluetooth/btusb_main.c | 42 ++++++++++++++++++++++++++++++------------
drivers/usb/core/driver.c | 25 ++++++++++++++++++++++++-
drivers/usb/core/message.c | 8 ++++++++
drivers/usb/core/usb.h | 2 ++
4 files changed, 64 insertions(+), 13 deletions(-)
diff --git a/drivers/bluetooth/btusb_main.c b/drivers/bluetooth/btusb_main.c
index 92a78a7e60396..8fe6bd1bff906 100644
--- a/drivers/bluetooth/btusb_main.c
+++ b/drivers/bluetooth/btusb_main.c
@@ -2993,10 +2993,13 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
+/* usb_lock_device_for_reset() gives up after one second of contention */
+#define BTUSB_MTK_ISO_LOCK_TRIES 5
+
static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
{
struct btmtk_data *btmtk_data;
- int err;
+ int err, i;
if (!data->hdev)
return;
@@ -3011,22 +3014,37 @@ static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
}
/*
- * The function usb_driver_claim_interface() is documented to need
- * locks held if it's not called from a probe routine. The code here
- * is called from the hci_power_on workqueue, so grab the lock.
+ * Disconnect holds the device lock while draining this setup work, so
+ * the lock must be taken with disconnect-aware acquisition. Hold a PM
+ * reference so the device cannot suspend first. A slow interface
+ * registration can keep the lock past the one-second limit of a single
+ * attempt, so retry a few times, but not without bound: a teardown
+ * that holds the lock may be waiting for this work.
*/
- device_lock(&btmtk_data->isopkt_intf->dev);
- err = usb_driver_claim_interface(&btusb_driver,
- btmtk_data->isopkt_intf, data);
- device_unlock(&btmtk_data->isopkt_intf->dev);
+ err = usb_autopm_get_interface(data->intf);
+ if (!err) {
+ for (i = 0; i < BTUSB_MTK_ISO_LOCK_TRIES; i++) {
+ err = usb_lock_device_for_reset(data->udev, data->intf);
+ if (err != -EBUSY)
+ break;
+ }
+ if (!err) {
+ err = usb_driver_claim_interface(&btusb_driver,
+ btmtk_data->isopkt_intf,
+ data);
+ if (!err) {
+ init_usb_anchor(&btmtk_data->isopkt_anchor);
+ set_bit(BTMTK_ISOPKT_OVER_INTR,
+ &btmtk_data->flags);
+ }
+ usb_unlock_device(data->udev);
+ }
+ usb_autopm_put_interface(data->intf);
+ }
if (err < 0) {
btmtk_data->isopkt_intf = NULL;
bt_dev_err(data->hdev, "Failed to claim iso interface: %d", err);
- return;
}
-
- set_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
- init_usb_anchor(&btmtk_data->isopkt_anchor);
}
static void btusb_mtk_release_iso_intf(struct hci_dev *hdev)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d3a61ebc6afb0..262502642ca98 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -584,8 +584,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
*/
- if (device_is_registered(dev))
+ if (device_is_registered(dev)) {
+ device_lock(dev);
retval = device_bind_driver(dev);
+ device_unlock(dev);
+ }
if (retval) {
dev->driver = NULL;
@@ -649,6 +652,26 @@ void usb_driver_release_interface(struct usb_driver *driver,
}
EXPORT_SYMBOL_GPL(usb_driver_release_interface);
+/*
+ * The driver core refused to bind an interface that a sibling claimed before
+ * it was registered, and dropped the claim. Let the claimant release the
+ * interface instead of operating it with a bound interface but no driver.
+ */
+void usb_driver_release_refused_claim(struct usb_interface *iface,
+ struct device_driver *claimant)
+{
+ struct device *dev = &iface->dev;
+
+ device_lock(dev);
+ if (!dev->driver && iface->condition == USB_INTERFACE_BOUND) {
+ dev->driver = claimant;
+ usb_unbind_interface(dev);
+ dev_set_drvdata(dev, NULL);
+ dev->driver = NULL;
+ }
+ device_unlock(dev);
+}
+
/* returns 0 if no match, 1 if match */
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index a738d03c23eac..d35a7710c8ef4 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2264,6 +2264,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
*/
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
+ struct device_driver *claimant;
if (intf->dev.of_node &&
!of_device_is_available(intf->dev.of_node)) {
@@ -2277,7 +2278,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
+ claimant = intf->dev.driver;
+ if (claimant && !try_module_get(claimant->owner))
+ claimant = NULL;
ret = device_add(&intf->dev);
+ if (!ret && claimant && !intf->dev.driver)
+ usb_driver_release_refused_claim(intf, claimant);
+ if (claimant)
+ module_put(claimant->owner);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
dev_name(&intf->dev), ret);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index a9b37aeb515be..293340a48b6c5 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -49,6 +49,8 @@ extern int usb_set_isoch_delay(struct usb_device *dev);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
+void usb_driver_release_refused_claim(struct usb_interface *iface,
+ struct device_driver *claimant);
extern int usb_choose_configuration(struct usb_device *udev);
extern int usb_generic_driver_probe(struct usb_device *udev);
extern void usb_generic_driver_disconnect(struct usb_device *udev);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 06/17] PNP: Attach card devices through the driver core
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (4 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 05/17] USB: Lock registered interfaces during " Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 07/17] cdx: Propagate DMA configuration errors Leon Romanovsky
` (10 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Card device requests probe the selected link driver before driver core can
perform admission and setup.
Use device_driver_attach() for the normal binding transaction. Keep the
card link owned by the enclosing card probe until the device is bound, so
probe cleanup and debug removal cannot free a link still in use by that
transaction.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/pnp/card.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c
index df5e3d3cbf6c0..9aaaa03d5ffa9 100644
--- a/drivers/pnp/card.c
+++ b/drivers/pnp/card.c
@@ -52,13 +52,17 @@ static const struct pnp_card_device_id *match_card(struct pnp_card_driver *drv,
static void card_remove(struct pnp_dev *dev)
{
- dev->card_link = NULL;
+ if (device_is_bound(&dev->dev))
+ dev->card_link = NULL;
}
static void card_remove_first(struct pnp_dev *dev)
{
struct pnp_card_driver *drv = to_pnp_card_driver(dev->driver);
+ /* The card probe still owns its link until this device is bound. */
+ if (!device_is_bound(&dev->dev))
+ return;
if (!dev->card || !drv)
return;
if (drv->remove)
@@ -331,16 +335,12 @@ struct pnp_dev *pnp_request_card_device(struct pnp_card_link *clink,
found:
dev->card_link = clink;
- dev->dev.driver = &drv->link.driver;
- if (pnp_bus_type.probe(&dev->dev))
- goto err_out;
- if (device_bind_driver(&dev->dev))
+ if (device_driver_attach(&drv->link.driver, &dev->dev))
goto err_out;
return dev;
err_out:
- dev->dev.driver = NULL;
dev->card_link = NULL;
return NULL;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 07/17] cdx: Propagate DMA configuration errors
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (5 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 06/17] PNP: Attach card devices through the driver core Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 08/17] driver core: Allow userspace to detect EPROBE_DEFER when using sysfs drivers_probe Leon Romanovsky
` (9 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
CDX reports successful DMA configuration when firmware setup defers or
default-domain acquisition fails. Driver probing can then proceed without
the required DMA setup or IOMMU ownership.
Return the configuration result so driver core stops or defers probing
instead of allowing a driver to use a rejected domain.
Fixes: 2959ab247061 ("cdx: add the cdx bus driver")
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/cdx/cdx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c
index a118a89a7779d..0a030bf522690 100644
--- a/drivers/cdx/cdx.c
+++ b/drivers/cdx/cdx.c
@@ -367,7 +367,7 @@ static int cdx_dma_configure(struct device *dev)
arch_teardown_dma_ops(dev);
}
- return 0;
+ return ret;
}
static void cdx_dma_cleanup(struct device *dev)
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 08/17] driver core: Allow userspace to detect EPROBE_DEFER when using sysfs drivers_probe
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (6 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 07/17] cdx: Propagate DMA configuration errors Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 09/17] driver core: Introduce a generic device trust level Leon Romanovsky
` (8 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
commit b8c5cec23d5c ("Driver core: udev triggered device-<>driver
binding") introduced the concept of "Systems who want to control the
driver binding with udev" can use the drivers_probe sysfs to trigger a
driver bind based on the kernel's match list.
If attaching the driver is deferred before its probe callback runs,
userspace sees EINVAL and cannot tell it should try again, maybe after
loading other drivers.
Map that deferral to EAGAIN and add missed documentation for the
drivers_probe sysfs. A deferral requested by the probe callback itself
is still not reported.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-bus-drivers-probe | 13 +++++++++++++
MAINTAINERS | 1 +
drivers/base/bus.c | 9 +++++++--
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-drivers-probe b/Documentation/ABI/testing/sysfs-bus-drivers-probe
new file mode 100644
index 0000000000000..851c65b9331ae
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-drivers-probe
@@ -0,0 +1,13 @@
+What: /sys/bus/.../drivers_probe
+Date: February 2007
+Contact: driver-core@lists.linux.dev
+Description:
+ (WO) Writing a device name requests driver attachment on this
+ bus, even when drivers_autoprobe is disabled. A successful
+ write does not guarantee that a driver bound. The device's
+ driver link reports the result.
+
+ EAGAIN means attachment was deferred before the driver's probe
+ callback, for example while a supplier or the device itself
+ is not ready. ENODEV means the device was not found. Other
+ attachment errors return EINVAL.
diff --git a/MAINTAINERS b/MAINTAINERS
index 93abbb315065d..b726344362b41 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8149,6 +8149,7 @@ M: Johan Hovold <johan@kernel.org>
L: driver-core@lists.linux.dev
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
+F: Documentation/ABI/testing/sysfs-bus-drivers-probe
F: Documentation/core-api/kobject.rst
F: Documentation/driver-api/driver-model/
F: drivers/base/
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 396097fdb3162..ecff32c3704b5 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -310,13 +310,18 @@ static ssize_t drivers_probe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
- int err = -EINVAL;
+ int err;
dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
- if (bus_rescan_devices_helper(dev, NULL) == 0)
+ err = bus_rescan_devices_helper(dev, NULL);
+ if (!err)
err = count;
+ else if (err == -EPROBE_DEFER)
+ err = -EAGAIN;
+ else
+ err = -EINVAL;
put_device(dev);
return err;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 09/17] driver core: Introduce a generic device trust level
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (7 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 08/17] driver core: Allow userspace to detect EPROBE_DEFER when using sysfs drivers_probe Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 10/17] driver core: Expose device trust authorization through sysfs Leon Romanovsky
` (7 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
For a long time now there has been a PCI concept of 'untrusted' that
is set for things like Thunderbolt devices. The way the kernel uses
'untrusted' means the device should be assumed 'adversarial' (credit
to DJW for the name), that it could attack the kernel through its
operations.
This makes sense in a Thunderbolt context where you could just plug in
some PCIe device and get up to trouble. Currently the kernel responds
to this by forcing the iommu to strict mode, but we can also imagine
further defenses.
Take this general idea and promote it to a first class citizen in the
driver core so all busses can use it consistently, including the uAPI
that comes along with it.
A device has an active trust level DISABLED, ADVERSARY, and FULL. FULL
represents what Linux usually does today. ADVERSARY is close to PCI
untrusted, while DISABLED prevents a driver from binding.
A user configurable policy level sets the target trust level when the
device gets bound to a device, including DEFAULT meaning the bus gets
to choose the level. The active trust level is static while a driver
is bound, so userspace can only change the policy before binding the
driver.
Typically the kernel will boot with all devices set to a DEFAULT
policy. This will usually select a FULL trust level, except for PCI
Thunderbolt/etc which chooses ADVERSARY.
Aside from allowing userspace to arbitrarily select the ADVERSARY
mode, this is also the basic mechanism to allow userspace to inject
policy into the driver probing path. Later patches will allow the
kernel to enter the initrd with the policy set to DISABLED. In this
case a flow similar to commit b8c5cec23d5c ("Driver core: udev
triggered device-<>driver binding") allows userspace to inspect the
device, apply a policy, set the trust level, set other options like
PCIe link encryption, and then progress to allow the driver to bind.
Report an admission failure like a probe error, so that a driver
scan moves on to the next candidate. Export the getters to modules,
as drivers are expected to call them.
Keep the active level in struct device, so lockless readers do not
depend on the lifetime of device_private.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/Makefile | 2 +-
drivers/base/base.h | 19 +++++++
drivers/base/core.c | 2 +
drivers/base/dd.c | 23 +++++++-
drivers/base/trust.c | 124 +++++++++++++++++++++++++++++++++++++++++++
include/linux/device.h | 6 +++
include/linux/device/bus.h | 10 ++++
include/linux/device/trust.h | 43 +++++++++++++++
8 files changed, 226 insertions(+), 3 deletions(-)
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 435710f643a5c..cb3bd5fa2287a 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -3,7 +3,7 @@
obj-y := component.o core.o bus.o dd.o syscore.o \
driver.o class.o platform.o \
- cpu.o firmware.o init.o map.o devres.o \
+ cpu.o firmware.o init.o map.o devres.o trust.o \
attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o \
swnode.o faux.o
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 866b2beb6baa2..4683b9c2a8d83 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -10,6 +10,7 @@
* shared outside of the drivers/base/ directory.
*
*/
+#include <linux/device/trust.h>
#include <linux/notifier.h>
/**
@@ -88,6 +89,13 @@ struct driver_private {
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+enum device_trust_state {
+ DEVICE_TRUST_SELECTABLE,
+ DEVICE_TRUST_WAITING,
+ DEVICE_TRUST_AUTHORIZED,
+ DEVICE_TRUST_COMMITTED,
+};
+
/**
* struct device_private - structure to hold the private to the driver core
* portions of the device structure.
@@ -105,6 +113,9 @@ struct driver_private {
* dev_err_probe() for later retrieval via debugfs
* @device: pointer back to the struct device that this structure is
* associated with.
+ * @trust_committed_level: operating level retained across bind transactions
+ * @trust_state: policy admission state
+ * @trust_initialized: bus and boot policy have initialized admission state
* @dead: This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
@@ -121,6 +132,9 @@ struct device_private {
const struct device_driver *async_driver;
char *deferred_probe_reason;
struct device *device;
+ enum device_trust_level trust_committed_level;
+ enum device_trust_state trust_state;
+ u8 trust_initialized:1;
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -166,6 +180,11 @@ void bus_remove_driver(struct device_driver *drv);
void device_release_driver_internal(struct device *dev, const struct device_driver *drv,
struct device *parent);
+/* All active trust level transitions are serialized by the device lock. */
+void device_trust_init_state(struct device *dev);
+int device_trust_prepare(struct device *dev, const struct device_driver *drv);
+void device_trust_clear(struct device *dev);
+
void driver_detach(const struct device_driver *drv);
void driver_deferred_probe_del(struct device *dev);
void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index cbe05197d4a0b..a0ffe74993677 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3681,6 +3681,8 @@ int device_add(struct device *dev)
if (error)
goto name_error;
+ device_trust_init_state(dev);
+
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 01026e02de92e..0d83f62195f69 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -545,13 +545,21 @@ int device_bind_driver(struct device *dev)
{
int ret;
+ if (dev->bus && dev->bus->trust_resolve)
+ return -EOPNOTSUPP;
+
+ ret = device_trust_prepare(dev, dev->driver);
+ if (ret)
+ return ret;
+
ret = driver_sysfs_add(dev);
if (!ret) {
device_links_force_bind(dev);
driver_bound(dev);
- }
- else
+ } else {
bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
+ device_trust_clear(dev);
+ }
return ret;
}
EXPORT_SYMBOL_GPL(device_bind_driver);
@@ -679,6 +687,14 @@ static int really_probe(struct device *dev, const struct device_driver *drv)
}
re_probe:
+ ret = device_trust_prepare(dev, drv);
+ if (ret) {
+ device_links_no_driver(dev);
+ /* Report it like a probe error so a scan tries the next driver. */
+ ret = -ret;
+ goto done;
+ }
+
device_set_driver(dev, drv);
/* If using pinctrl, bind pins now before probing */
@@ -745,6 +761,7 @@ static int really_probe(struct device *dev, const struct device_driver *drv)
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
device_unbind_cleanup(dev);
+ device_trust_clear(dev);
goto re_probe;
}
@@ -771,6 +788,7 @@ static int really_probe(struct device *dev, const struct device_driver *drv)
pinctrl_bind_failed:
device_links_no_driver(dev);
device_unbind_cleanup(dev);
+ device_trust_clear(dev);
done:
return ret;
}
@@ -1405,6 +1423,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
dev->bus->dma_cleanup(dev);
device_unbind_cleanup(dev);
+ device_trust_clear(dev);
device_links_driver_cleanup(dev);
klist_remove(&dev->p->knode_driver);
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
new file mode 100644
index 0000000000000..10a135b8bf399
--- /dev/null
+++ b/drivers/base/trust.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <kunit/visibility.h>
+
+#include "base.h"
+
+void device_trust_init_state(struct device *dev)
+{
+ if (dev->p->trust_initialized)
+ return;
+
+ dev->p->trust_state = dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED ?
+ DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
+ dev->p->trust_initialized = true;
+}
+
+static bool device_trust_policy_matches(enum device_trust_policy policy,
+ enum device_trust_level level)
+{
+ switch (policy) {
+ case DEVICE_TRUST_POLICY_DEFAULT:
+ return true;
+ case DEVICE_TRUST_POLICY_DISABLED:
+ return level == DEVICE_TRUST_DISABLED;
+ case DEVICE_TRUST_POLICY_ADVERSARY:
+ return level == DEVICE_TRUST_ADVERSARY;
+ case DEVICE_TRUST_POLICY_FULL:
+ return level == DEVICE_TRUST_FULL;
+ default:
+ return false;
+ }
+}
+
+static int device_trust_resolve_default(enum device_trust_policy policy,
+ enum device_trust_level *level)
+{
+ switch (policy) {
+ case DEVICE_TRUST_POLICY_DEFAULT:
+ case DEVICE_TRUST_POLICY_FULL:
+ *level = DEVICE_TRUST_FULL;
+ return 0;
+ case DEVICE_TRUST_POLICY_DISABLED:
+ *level = DEVICE_TRUST_DISABLED;
+ return 0;
+ case DEVICE_TRUST_POLICY_ADVERSARY:
+ *level = DEVICE_TRUST_ADVERSARY;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int device_trust_prepare(struct device *dev, const struct device_driver *drv)
+{
+ enum device_trust_policy policy;
+ enum device_trust_level level = DEVICE_TRUST_DISABLED;
+ int ret;
+
+ device_lock_assert(dev);
+ policy = dev->trust_policy;
+
+ if (WARN_ON_ONCE(READ_ONCE(dev->trust_level) != DEVICE_TRUST_DISABLED))
+ return -EBUSY;
+ if (policy == DEVICE_TRUST_POLICY_DISABLED)
+ return -EPERM;
+ if (dev->bus && dev->bus->trust_resolve)
+ ret = dev->bus->trust_resolve(dev, drv, policy, &level);
+ else
+ ret = device_trust_resolve_default(policy, &level);
+ if (ret)
+ return ret;
+
+ if (!device_trust_policy_matches(policy, level))
+ return -EINVAL;
+ if (level == DEVICE_TRUST_DISABLED)
+ return -EPERM;
+ if (level != DEVICE_TRUST_ADVERSARY && level != DEVICE_TRUST_FULL)
+ return -EINVAL;
+ if (dev->p->trust_state == DEVICE_TRUST_COMMITTED &&
+ dev->p->trust_committed_level != level)
+ return -EINVAL;
+
+ dev->p->trust_committed_level = level;
+ dev->p->trust_state = DEVICE_TRUST_COMMITTED;
+ WRITE_ONCE(dev->trust_level, level);
+ return 0;
+}
+
+void device_trust_clear(struct device *dev)
+{
+ device_lock_assert(dev);
+ WRITE_ONCE(dev->trust_level, DEVICE_TRUST_DISABLED);
+}
+
+/**
+ * device_get_trust_level - Return a device's active trust level
+ * @dev: device to inspect
+ *
+ * The value is stable from the start of DMA configuration through the end of
+ * DMA cleanup. An unbound device has %DEVICE_TRUST_DISABLED as its active
+ * trust level.
+ *
+ * Return: The active trust level for the current bind transaction.
+ */
+enum device_trust_level device_get_trust_level(const struct device *dev)
+{
+ return READ_ONCE(dev->trust_level);
+}
+EXPORT_SYMBOL_GPL(device_get_trust_level);
+
+/**
+ * device_is_adversarial - Test whether a device is operated as an adversary
+ * @dev: device to inspect
+ *
+ * Return: True when the active trust level is %DEVICE_TRUST_ADVERSARY.
+ */
+bool device_is_adversarial(const struct device *dev)
+{
+ return device_get_trust_level(dev) == DEVICE_TRUST_ADVERSARY;
+}
+EXPORT_SYMBOL_GPL(device_is_adversarial);
diff --git a/include/linux/device.h b/include/linux/device.h
index 4cf62d69eac4f..5bd0cee874731 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -30,6 +30,7 @@
#include <linux/device/class.h>
#include <linux/device/devres.h>
#include <linux/device/driver.h>
+#include <linux/device/trust.h>
#include <linux/cleanup.h>
#include <asm/device.h>
@@ -695,6 +696,9 @@ 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.
+ * @trust_policy: Requested operating policy, resolved before driver probe.
+ * @trust_level: Active trust level while a driver is bound, see
+ * device_get_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
@@ -798,6 +802,8 @@ struct device {
struct device_physical_location *physical_location;
enum device_removable removable;
+ enum device_trust_policy trust_policy;
+ enum device_trust_level trust_level;
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h
index 9ef7538a9a7f2..ba2b5743a47cd 100644
--- a/include/linux/device/bus.h
+++ b/include/linux/device/bus.h
@@ -17,6 +17,7 @@
#include <linux/kobject.h>
#include <linux/klist.h>
#include <linux/pm.h>
+#include <linux/device/trust.h>
struct device_driver;
struct fwnode_handle;
@@ -59,6 +60,11 @@ struct fwnode_handle;
* @resume: Called to bring a device on this bus out of sleep mode.
* @num_vf: Called to find out how many virtual functions a device on this
* bus supports.
+ * @trust_resolve: Resolve a requested trust policy to an active trust level
+ * immediately before driver probe. The device lock is held and
+ * @drv is the candidate driver, which dev->driver does not name
+ * yet. Return 0 on success or a negative errno. This callback
+ * must not enable the device.
* @dma_configure: Called to setup DMA configuration on a device on
* this bus.
* @dma_cleanup: Called to cleanup DMA configuration on a device on
@@ -102,6 +108,10 @@ struct bus_type {
int (*num_vf)(struct device *dev);
+ int (*trust_resolve)(struct device *dev,
+ const struct device_driver *drv,
+ enum device_trust_policy policy,
+ enum device_trust_level *level);
int (*dma_configure)(struct device *dev);
void (*dma_cleanup)(struct device *dev);
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
new file mode 100644
index 0000000000000..dccc47c3b4960
--- /dev/null
+++ b/include/linux/device/trust.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _DEVICE_TRUST_H_
+#define _DEVICE_TRUST_H_
+
+#include <linux/types.h>
+
+struct device;
+
+/**
+ * enum device_trust_level - active trust level for a device
+ * @DEVICE_TRUST_DISABLED: The device cannot be operated by a driver
+ * @DEVICE_TRUST_ADVERSARY: Operate the device with adversarial restrictions
+ * @DEVICE_TRUST_FULL: Operate the device with the usual kernel privileges
+ *
+ * Active trust levels are discrete states, not an ordered privilege scale.
+ */
+enum device_trust_level {
+ DEVICE_TRUST_DISABLED,
+ DEVICE_TRUST_ADVERSARY,
+ DEVICE_TRUST_FULL,
+};
+
+/**
+ * enum device_trust_policy - requested device trust policy
+ * @DEVICE_TRUST_POLICY_DEFAULT: Let the bus select the active trust level
+ * @DEVICE_TRUST_POLICY_DISABLED: Request that the device remain disabled
+ * @DEVICE_TRUST_POLICY_ADVERSARY: Request adversarial operation
+ * @DEVICE_TRUST_POLICY_FULL: Request normal operation
+ *
+ * A requested policy is resolved to an active trust level before a driver is
+ * probed.
+ */
+enum device_trust_policy {
+ DEVICE_TRUST_POLICY_DEFAULT,
+ DEVICE_TRUST_POLICY_DISABLED,
+ DEVICE_TRUST_POLICY_ADVERSARY,
+ DEVICE_TRUST_POLICY_FULL,
+};
+
+enum device_trust_level device_get_trust_level(const struct device *dev);
+bool device_is_adversarial(const struct device *dev);
+
+#endif /* _DEVICE_TRUST_H_ */
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (8 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 09/17] driver core: Introduce a generic device trust level Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 13:10 ` Greg Kroah-Hartman
2026-11-05 17:02 ` Jason Gunthorpe
2026-11-05 11:42 ` [PATCH 11/17] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
` (6 subsequent siblings)
16 siblings, 2 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
An initrd must authorize a disabled device after attestation even if a
built-in driver already attempted to probe it.
Expose requested trust_policy and read-only active trust. Allow explicit
disabled policy to advance once to adversary or full, fixing that decision
immediately. Reject later policy changes, including for a device claimed
before registration, while allowing same-value writes on live devices.
Keep initial unrestricted selection separate from explicit authorization
and bind-time commitment. Serialize writes with probing under the device
lock. Include the attributes in device ownership changes.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
MAINTAINERS | 1 +
drivers/base/base.h | 6 ++
drivers/base/core.c | 15 +++-
drivers/base/trust.c | 102 ++++++++++++++++++++++++++
5 files changed, 174 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
new file mode 100644
index 0000000000000..97e07a174769c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -0,0 +1,51 @@
+What: /sys/devices/.../trust
+Date: September 2026
+Contact: driver-core@lists.linux.dev
+Description:
+ (RO) The active trust level for the device. The values are
+ "disabled", "adversary", and "full". Driver core resolves
+ the level before DMA configuration and probe, and retains it
+ through removal and DMA cleanup. Outside a bind transaction
+ the level is "disabled". This does not imply that policy is
+ writable or that the device has never operated.
+
+What: /sys/devices/.../trust_policy
+Date: September 2026
+Contact: driver-core@lists.linux.dev
+Description:
+ (RW) The requested trust policy for the device. The initial
+ value may be selected by the bus before the device is
+ registered; otherwise it is "default". The policy is resolved
+ to an active trust level before the device is probed.
+
+ The values are "default", "disabled", "adversary", and
+ "full". "default" lets the bus select the active trust level.
+ "disabled" prevents a functional driver from binding.
+ "adversary" requests operation with the restrictions that the
+ bus and DMA layers can enforce for a potentially malicious
+ device; it does not isolate DMA by itself. "full" requests
+ normal operation.
+
+ Before an operating level is committed, policy may change
+ freely until "disabled" is selected. Explicit "disabled"
+ blocks binding while allowing a later write of "adversary" or
+ "full". Repeated rejected probes preserve this opportunity.
+ Authorization immediately fixes the requested policy.
+ Returning to "default" or "disabled", or selecting another
+ mode, fails.
+
+ Successful resolution to an allowed level commits policy
+ and operating level before driver or DMA setup. Both remain
+ fixed for the device object's lifetime, including through
+ failed or deferred probes, unbind, reset, and driver reload.
+ Later binds must resolve to the same level, including when
+ the policy is "default". A resolver error or disabled result
+ before commitment leaves the policy's writability unchanged.
+
+ For a live device, writing the existing value succeeds
+ without changing state. Other forbidden writes fail with
+ EBUSY; invalid strings fail with EINVAL. Removed devices
+ reject writes with ENODEV. Writes do not trigger probing or
+ attestation.
+ Queued probes may proceed immediately after authorization,
+ so userspace must complete verification before granting it.
diff --git a/MAINTAINERS b/MAINTAINERS
index b726344362b41..a145aaaca2ef3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8150,6 +8150,7 @@ L: driver-core@lists.linux.dev
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
F: Documentation/ABI/testing/sysfs-bus-drivers-probe
+F: Documentation/ABI/testing/sysfs-devices-trust
F: Documentation/core-api/kobject.rst
F: Documentation/driver-api/driver-model/
F: drivers/base/
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 4683b9c2a8d83..c88b3302969f2 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -180,6 +180,12 @@ void bus_remove_driver(struct device_driver *drv);
void device_release_driver_internal(struct device *dev, const struct device_driver *drv,
struct device *parent);
+extern const struct attribute_group device_trust_attr_group;
+
+#if IS_ENABLED(CONFIG_KUNIT)
+int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
+#endif
+
/* All active trust level transitions are serialized by the device lock. */
void device_trust_init_state(struct device *dev);
int device_trust_prepare(struct device *dev, const struct device_driver *drv);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index a0ffe74993677..dca0179714c82 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2989,10 +2989,14 @@ static int device_add_attrs(struct device *dev)
if (error)
goto err_remove_type_groups;
+ error = device_add_group(dev, &device_trust_attr_group);
+ if (error)
+ goto err_remove_dev_groups;
+
if (device_supports_offline(dev) && !dev_offline_disabled(dev)) {
error = device_create_file(dev, &dev_attr_online);
if (error)
- goto err_remove_dev_groups;
+ goto err_remove_dev_trust_group;
}
if (fw_devlink_flags && !fw_devlink_is_permissive() && dev->fwnode) {
@@ -3022,6 +3026,8 @@ static int device_add_attrs(struct device *dev)
device_remove_file(dev, &dev_attr_waiting_for_supplier);
err_remove_dev_online:
device_remove_file(dev, &dev_attr_online);
+ err_remove_dev_trust_group:
+ device_remove_group(dev, &device_trust_attr_group);
err_remove_dev_groups:
device_remove_groups(dev, dev->groups);
err_remove_type_groups:
@@ -3047,6 +3053,7 @@ static void device_remove_attrs(struct device *dev)
device_remove_file(dev, &dev_attr_removable);
device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_remove_file(dev, &dev_attr_online);
+ device_remove_group(dev, &device_trust_attr_group);
device_remove_groups(dev, dev->groups);
if (type)
@@ -4790,6 +4797,12 @@ static int device_attrs_change_owner(struct device *dev, kuid_t kuid,
if (error)
return error;
+ /* Change the trust attributes of @dev to @kuid/@kgid. */
+ error = sysfs_group_change_owner(kobj, &device_trust_attr_group, kuid,
+ kgid);
+ if (error)
+ return error;
+
if (device_supports_offline(dev) && !dev_offline_disabled(dev)) {
/* Change online device attributes of @dev to @kuid/@kgid. */
error = sysfs_file_change_owner(kobj, dev_attr_online.attr.name,
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 10a135b8bf399..884e7c2dfbbb6 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -3,6 +3,7 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
+#include <linux/sysfs.h>
#include <kunit/visibility.h>
#include "base.h"
@@ -122,3 +123,104 @@ bool device_is_adversarial(const struct device *dev)
return device_get_trust_level(dev) == DEVICE_TRUST_ADVERSARY;
}
EXPORT_SYMBOL_GPL(device_is_adversarial);
+
+static const char * const device_trust_policy_names[] = {
+ [DEVICE_TRUST_POLICY_DEFAULT] = "default",
+ [DEVICE_TRUST_POLICY_DISABLED] = "disabled",
+ [DEVICE_TRUST_POLICY_ADVERSARY] = "adversary",
+ [DEVICE_TRUST_POLICY_FULL] = "full",
+};
+
+static const char * const device_trust_level_names[] = {
+ [DEVICE_TRUST_DISABLED] = "disabled",
+ [DEVICE_TRUST_ADVERSARY] = "adversary",
+ [DEVICE_TRUST_FULL] = "full",
+};
+
+static ssize_t trust_show(struct device *dev,
+ const struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%s\n",
+ device_trust_level_names[device_get_trust_level(dev)]);
+}
+static DEVICE_ATTR_RO(trust);
+
+static ssize_t trust_policy_show(struct device *dev,
+ const struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%s\n",
+ device_trust_policy_names[READ_ONCE(dev->trust_policy)]);
+}
+
+VISIBLE_IF_KUNIT int device_trust_set_policy(struct device *dev,
+ enum device_trust_policy policy)
+{
+ int ret = 0;
+
+ if (policy < DEVICE_TRUST_POLICY_DEFAULT ||
+ policy > DEVICE_TRUST_POLICY_FULL)
+ return -EINVAL;
+
+ device_lock(dev);
+ if (dev->p->dead) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+ if (policy == dev->trust_policy)
+ goto unlock;
+
+ /* A driver that claimed the device before registration was not admitted. */
+ if (READ_ONCE(dev->driver)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ switch (dev->p->trust_state) {
+ case DEVICE_TRUST_SELECTABLE:
+ if (policy == DEVICE_TRUST_POLICY_DISABLED)
+ dev->p->trust_state = DEVICE_TRUST_WAITING;
+ break;
+ case DEVICE_TRUST_WAITING:
+ if (policy != DEVICE_TRUST_POLICY_ADVERSARY &&
+ policy != DEVICE_TRUST_POLICY_FULL) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ dev->p->trust_state = DEVICE_TRUST_AUTHORIZED;
+ break;
+ default:
+ ret = -EBUSY;
+ goto unlock;
+ }
+ WRITE_ONCE(dev->trust_policy, policy);
+unlock:
+ device_unlock(dev);
+ return ret;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_set_policy);
+
+static ssize_t trust_policy_store(struct device *dev,
+ const struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int policy, ret;
+
+ policy = sysfs_match_string(device_trust_policy_names, buf);
+ if (policy < 0)
+ return policy;
+
+ ret = device_trust_set_policy(dev, policy);
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(trust_policy);
+
+static struct attribute *device_trust_attrs[] = {
+ &dev_attr_trust.attr,
+ &dev_attr_trust_policy.attr,
+ NULL,
+};
+
+const struct attribute_group device_trust_attr_group = {
+ .attrs = device_trust_attrs,
+};
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 11/17] driver core: Accept device trust policy on the kernel command line
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (9 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 10/17] driver core: Expose device trust authorization through sysfs Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 12/17] driver core: Accept device trust policy by driver name Leon Romanovsky
` (5 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Built-in drivers can probe before initrd policy agents start. Fresh
attestation therefore requires disabled policy before those attempts.
Add device_trust=<bus>:<device>=<policy>[,...] to initialize requested
policy before registration. Apply PCI overrides during discovery so IOMMU
and ACS decisions use the same initial policy as binding. Retain rules for
hotplug, preserve admission history when a device object is reused, and
leave unlisted devices unchanged.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 2 +
Documentation/admin-guide/kernel-parameters.txt | 25 +++++++
drivers/base/base.h | 2 +
drivers/base/core.c | 1 +
drivers/base/trust.c | 90 +++++++++++++++++++++++++
drivers/pci/probe.c | 1 +
include/linux/device/trust.h | 1 +
7 files changed, 122 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index 97e07a174769c..87d3410f6bd90 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -17,6 +17,8 @@ Description:
value may be selected by the bus before the device is
registered; otherwise it is "default". The policy is resolved
to an active trust level before the device is probed.
+ A matching device_trust= kernel command-line entry overrides
+ the initial requested policy before registration.
The values are "default", "disabled", "adversary", and
"full". "default" lets the bus select the active trust level.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0b3c140babd94..ed307427a5ca4 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1259,6 +1259,31 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ device_trust= [KNL]
+ Set requested device trust policy before driver probing,
+ including for drivers built into the kernel.
+ Format: <bus>:<device>=<policy>[,...]
+ The bus and device names must match exactly, as in
+ /sys/bus/<bus>/devices/<device>. The first colon separates
+ the bus name; further colons belong to the device name.
+ Policies are default, disabled, adversary, and full.
+ Entries override bus-initialized requested policy;
+ unlisted devices retain their existing policy.
+ The last entry for a device wins. If specified more than
+ once, the last valid list replaces all earlier lists.
+ Malformed lists are warned about and ignored in full.
+ Names containing whitespace, control characters, commas,
+ equals signs, or double quotes cannot be specified.
+ Rules also apply to devices added after boot.
+ Explicit disabled blocks early binding but permits later
+ authorization by writing adversary or full to trust_policy.
+ Rejected disabled probes leave this opportunity intact.
+ Authorization fixes the policy before probing; adversary
+ or full at boot permits early probes without attestation.
+ Example:
+ device_trust=pci:0000:01:00.0=adversary,pci:0000:02:00.0=disabled
+ See Documentation/driver-api/driver-model/device-trust.rst.
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/drivers/base/base.h b/drivers/base/base.h
index c88b3302969f2..a286f48a69747 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -184,6 +184,8 @@ extern const struct attribute_group device_trust_attr_group;
#if IS_ENABLED(CONFIG_KUNIT)
int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
+int device_trust_parse_cmdline(const char *options, const struct device *dev,
+ enum device_trust_policy *policy);
#endif
/* All active trust level transitions are serialized by the device lock. */
diff --git a/drivers/base/core.c b/drivers/base/core.c
index dca0179714c82..bd96d84c2ac2c 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3688,6 +3688,7 @@ int device_add(struct device *dev)
if (error)
goto name_error;
+ device_trust_init(dev);
device_trust_init_state(dev);
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 884e7c2dfbbb6..246d2516fb3af 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/string.h>
#include <linux/sysfs.h>
#include <kunit/visibility.h>
@@ -224,3 +227,90 @@ static struct attribute *device_trust_attrs[] = {
const struct attribute_group device_trust_attr_group = {
.attrs = device_trust_attrs,
};
+
+static const char *device_trust_cmdline __ro_after_init;
+
+VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
+ const struct device *dev,
+ enum device_trust_policy *policy)
+{
+ enum device_trust_policy selected = DEVICE_TRUST_POLICY_DEFAULT;
+ const char *entry = options;
+ bool matched = false;
+
+ if (!entry || !*entry)
+ return -EINVAL;
+
+ for (;;) {
+ const char *end = entry + strcspn(entry, ",");
+ const char *bus_end, *name, *level, *p;
+ unsigned int i;
+
+ bus_end = memchr(entry, ':', end - entry);
+ if (!bus_end || bus_end == entry ||
+ memchr(entry, '=', bus_end - entry))
+ return -EINVAL;
+ name = bus_end + 1;
+ level = memchr(name, '=', end - name);
+ if (!level || level == name || level + 1 == end)
+ return -EINVAL;
+
+ for (p = entry; p < end; p++)
+ if (isspace(*p) || iscntrl(*p) || *p == '"')
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(device_trust_policy_names); i++)
+ if (strlen(device_trust_policy_names[i]) == end - level - 1 &&
+ !strncmp(level + 1, device_trust_policy_names[i],
+ end - level - 1))
+ break;
+ if (i == ARRAY_SIZE(device_trust_policy_names))
+ return -EINVAL;
+
+ if (dev && dev->bus && dev_name(dev) &&
+ strlen(dev->bus->name) == bus_end - entry &&
+ !strncmp(dev->bus->name, entry, bus_end - entry) &&
+ strlen(dev_name(dev)) == level - name &&
+ !strncmp(dev_name(dev), name, level - name)) {
+ selected = i;
+ matched = true;
+ }
+
+ if (!*end)
+ break;
+ entry = end + 1;
+ }
+
+ if (matched)
+ *policy = selected;
+ return matched;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_parse_cmdline);
+
+static int __init device_trust_setup(char *str)
+{
+ if (device_trust_parse_cmdline(str, NULL, NULL) < 0) {
+ pr_warn("device_trust: ignoring invalid policy list '%s'\n", str);
+ return 1;
+ }
+
+ /* The command-line buffer survives init. */
+ device_trust_cmdline = str;
+ return 1;
+}
+__setup("device_trust=", device_trust_setup);
+
+/**
+ * device_trust_init - Apply boot policy before registration or bus policy use
+ * @dev: unregistered device with its bus, name, and default policy set
+ */
+void device_trust_init(struct device *dev)
+{
+ if (dev->p && dev->p->trust_initialized)
+ return;
+
+ if (device_trust_cmdline)
+ device_trust_parse_cmdline(device_trust_cmdline, dev,
+ &dev->trust_policy);
+}
+EXPORT_SYMBOL_GPL(device_trust_init);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9a178e0e5f7f4..5b6960bbb35bc 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2083,6 +2083,7 @@ int pci_setup_device(struct pci_dev *dev)
set_pcie_cxl(dev);
set_pcie_untrusted(dev);
+ device_trust_init(&dev->dev);
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
index dccc47c3b4960..b8f5514cacec3 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -37,6 +37,7 @@ enum device_trust_policy {
DEVICE_TRUST_POLICY_FULL,
};
+void device_trust_init(struct device *dev);
enum device_trust_level device_get_trust_level(const struct device *dev);
bool device_is_adversarial(const struct device *dev);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 12/17] driver core: Accept device trust policy by driver name
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (10 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 11/17] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 13/17] driver core: Preserve trust commitment beyond driver binding Leon Romanovsky
` (4 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Device addresses can change between boots and cannot conveniently name
every device handled by a driver, including devices discovered later.
Userspace arrives too late to choose the initial policy for built-in
drivers.
Administrators need a driver-wide boot default without weakening an
explicit device policy, userspace authorization, or a device's lifetime
trust commitment.
A driver may claim a USB interface before it is registered, which skips
admission. Let USB refuse such a claim early when the interface's own
policy is disabled, and leave driver rules to admission at registration.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 5 +
Documentation/admin-guide/kernel-parameters.txt | 22 +++++
drivers/base/base.h | 9 ++
drivers/base/trust.c | 124 +++++++++++++++++++++---
drivers/usb/core/driver.c | 5 +
drivers/usb/core/message.c | 2 +
include/linux/device/trust.h | 3 +
7 files changed, 157 insertions(+), 13 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index 87d3410f6bd90..c0ce905b625d0 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -19,6 +19,11 @@ Description:
to an active trust level before the device is probed.
A matching device_trust= kernel command-line entry overrides
the initial requested policy before registration.
+ Otherwise, device_trust.driver= can initialize policy on the
+ first binding attempt with a matching driver rule. A successful
+ policy write, including the existing value, suppresses this
+ deferred initialization. Driver rules do not change an existing
+ authorization or commitment and apply at most once per device.
The values are "default", "disabled", "adversary", and
"full". "default" lets the bus select the active trust level.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index ed307427a5ca4..b1d6999a5befa 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1269,6 +1269,7 @@ Kernel parameters
Policies are default, disabled, adversary, and full.
Entries override bus-initialized requested policy;
unlisted devices retain their existing policy.
+ Per-device entries take precedence over device_trust.driver.
The last entry for a device wins. If specified more than
once, the last valid list replaces all earlier lists.
Malformed lists are warned about and ignored in full.
@@ -1284,6 +1285,27 @@ Kernel parameters
device_trust=pci:0000:01:00.0=adversary,pci:0000:02:00.0=disabled
See Documentation/driver-api/driver-model/device-trust.rst.
+ device_trust.driver=
+ [KNL] Set initial device trust policy by driver name,
+ including for drivers built into the kernel.
+ Format: <bus>:<driver>=<policy>[,...]
+ Names match exactly as in /sys/bus/<bus>/drivers/<driver>.
+ Policies are default, disabled, adversary, and full.
+ The name restrictions and repeated-list rules are the same
+ as device_trust=. Module aliases and wildcards are not
+ expanded. Rules remain available for hotplug and modules.
+ The first binding attempt with a matching rule applies it
+ once, before trust resolution and driver or DMA setup.
+ A per-device device_trust= entry or any successful write
+ to trust_policy, even of its current value, takes precedence.
+ Rules never replace an existing authorization or commitment.
+ A rejected disabled attempt leaves the device disabled until
+ userspace authorizes it, including across driver changes.
+ Driver rules apply after bus discovery and do not reconfigure
+ IOMMU domains. Adversary operation needs suitable existing
+ DMA configuration; see the device trust documentation.
+ Example: device_trust.driver=pci:e1000=disabled,pci:nvme=full
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a286f48a69747..b4441242496f7 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -116,6 +116,7 @@ enum device_trust_state {
* @trust_committed_level: operating level retained across bind transactions
* @trust_state: policy admission state
* @trust_initialized: bus and boot policy have initialized admission state
+ * @trust_policy_override: explicit policy prevents deferred driver boot policy
* @dead: This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
@@ -135,6 +136,7 @@ struct device_private {
enum device_trust_level trust_committed_level;
enum device_trust_state trust_state;
u8 trust_initialized:1;
+ u8 trust_policy_override:1;
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -186,6 +188,13 @@ extern const struct attribute_group device_trust_attr_group;
int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
int device_trust_parse_cmdline(const char *options, const struct device *dev,
enum device_trust_policy *policy);
+void device_trust_apply_driver_policy(struct device *dev,
+ const struct device_driver *drv,
+ const char *options);
+bool device_trust_claim_allowed(const struct device *dev,
+ const struct device_driver *drv,
+ const char *device_options,
+ const char *driver_options);
#endif
/* All active trust level transitions are serialized by the device lock. */
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 246d2516fb3af..b38f69e440eae 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -11,6 +11,9 @@
#include "base.h"
+static void device_trust_init_driver_policy(struct device *dev,
+ const struct device_driver *drv);
+
void device_trust_init_state(struct device *dev)
{
if (dev->p->trust_initialized)
@@ -64,10 +67,11 @@ int device_trust_prepare(struct device *dev, const struct device_driver *drv)
int ret;
device_lock_assert(dev);
- policy = dev->trust_policy;
if (WARN_ON_ONCE(READ_ONCE(dev->trust_level) != DEVICE_TRUST_DISABLED))
return -EBUSY;
+ device_trust_init_driver_policy(dev, drv);
+ policy = dev->trust_policy;
if (policy == DEVICE_TRUST_POLICY_DISABLED)
return -EPERM;
if (dev->bus && dev->bus->trust_resolve)
@@ -170,7 +174,7 @@ VISIBLE_IF_KUNIT int device_trust_set_policy(struct device *dev,
goto unlock;
}
if (policy == dev->trust_policy)
- goto unlock;
+ goto set_policy;
/* A driver that claimed the device before registration was not admitted. */
if (READ_ONCE(dev->driver)) {
@@ -195,7 +199,9 @@ VISIBLE_IF_KUNIT int device_trust_set_policy(struct device *dev,
ret = -EBUSY;
goto unlock;
}
+set_policy:
WRITE_ONCE(dev->trust_policy, policy);
+ dev->p->trust_policy_override = true;
unlock:
device_unlock(dev);
return ret;
@@ -229,10 +235,11 @@ const struct attribute_group device_trust_attr_group = {
};
static const char *device_trust_cmdline __ro_after_init;
+static const char *device_trust_driver_cmdline __ro_after_init;
-VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
- const struct device *dev,
- enum device_trust_policy *policy)
+static int device_trust_parse_policy(const char *options, const char *target_bus,
+ const char *target_name,
+ enum device_trust_policy *policy)
{
enum device_trust_policy selected = DEVICE_TRUST_POLICY_DEFAULT;
const char *entry = options;
@@ -267,11 +274,11 @@ VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
if (i == ARRAY_SIZE(device_trust_policy_names))
return -EINVAL;
- if (dev && dev->bus && dev_name(dev) &&
- strlen(dev->bus->name) == bus_end - entry &&
- !strncmp(dev->bus->name, entry, bus_end - entry) &&
- strlen(dev_name(dev)) == level - name &&
- !strncmp(dev_name(dev), name, level - name)) {
+ if (target_bus && target_name &&
+ strlen(target_bus) == bus_end - entry &&
+ !strncmp(target_bus, entry, bus_end - entry) &&
+ strlen(target_name) == level - name &&
+ !strncmp(target_name, name, level - name)) {
selected = i;
matched = true;
}
@@ -285,8 +292,86 @@ VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
*policy = selected;
return matched;
}
+
+VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
+ const struct device *dev,
+ enum device_trust_policy *policy)
+{
+ return device_trust_parse_policy(options, dev && dev->bus ? dev->bus->name : NULL,
+ dev ? dev_name(dev) : NULL, policy);
+}
EXPORT_SYMBOL_IF_KUNIT(device_trust_parse_cmdline);
+VISIBLE_IF_KUNIT void device_trust_apply_driver_policy(struct device *dev,
+ const struct device_driver *drv,
+ const char *options)
+{
+ enum device_trust_policy policy;
+
+ device_lock_assert(dev);
+ if (!options || !drv || dev->p->trust_policy_override ||
+ (dev->p->trust_state != DEVICE_TRUST_SELECTABLE &&
+ dev->p->trust_state != DEVICE_TRUST_WAITING))
+ return;
+
+ if (device_trust_parse_policy(options, drv->bus ? drv->bus->name : NULL,
+ drv->name, &policy) <= 0)
+ return;
+
+ WRITE_ONCE(dev->trust_policy, policy);
+ dev->p->trust_state = policy == DEVICE_TRUST_POLICY_DISABLED ?
+ DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
+ dev->p->trust_policy_override = true;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_apply_driver_policy);
+
+static void device_trust_init_driver_policy(struct device *dev,
+ const struct device_driver *drv)
+{
+ device_trust_apply_driver_policy(dev, drv, device_trust_driver_cmdline);
+}
+
+/*
+ * A matching driver rule is applied, together with its admission state, when
+ * the device is registered, so only the device's own policy rejects a claim.
+ */
+VISIBLE_IF_KUNIT bool device_trust_claim_allowed(const struct device *dev,
+ const struct device_driver *drv,
+ const char *device_options,
+ const char *driver_options)
+{
+ enum device_trust_policy policy = READ_ONCE(dev->trust_policy);
+ bool device_rule = device_options &&
+ device_trust_parse_cmdline(device_options, dev, &policy) > 0;
+
+ if (!device_rule && driver_options && drv &&
+ device_trust_parse_policy(driver_options,
+ drv->bus ? drv->bus->name : NULL,
+ drv->name, &policy) > 0)
+ return true;
+
+ return policy != DEVICE_TRUST_POLICY_DISABLED;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_claim_allowed);
+
+/**
+ * device_trust_may_claim - Test whether a driver may claim an unregistered device
+ * @dev: device with its bus, name, and boot policy set
+ * @drv: driver that claims the device
+ *
+ * A driver can claim a device before it is registered, which skips the bind
+ * attempt that admits it. Reject a claim that the device's own policy forbids.
+ *
+ * Return: False if the policy of @dev rejects the claim.
+ */
+bool device_trust_may_claim(const struct device *dev,
+ const struct device_driver *drv)
+{
+ return device_trust_claim_allowed(dev, drv, device_trust_cmdline,
+ device_trust_driver_cmdline);
+}
+EXPORT_SYMBOL_GPL(device_trust_may_claim);
+
static int __init device_trust_setup(char *str)
{
if (device_trust_parse_cmdline(str, NULL, NULL) < 0) {
@@ -300,6 +385,18 @@ static int __init device_trust_setup(char *str)
}
__setup("device_trust=", device_trust_setup);
+static int __init device_trust_driver_setup(char *str)
+{
+ if (device_trust_parse_policy(str, NULL, NULL, NULL) < 0) {
+ pr_warn("device_trust: ignoring invalid driver policy list '%s'\n", str);
+ return 1;
+ }
+
+ device_trust_driver_cmdline = str;
+ return 1;
+}
+__setup("device_trust.driver=", device_trust_driver_setup);
+
/**
* device_trust_init - Apply boot policy before registration or bus policy use
* @dev: unregistered device with its bus, name, and default policy set
@@ -309,8 +406,9 @@ void device_trust_init(struct device *dev)
if (dev->p && dev->p->trust_initialized)
return;
- if (device_trust_cmdline)
- device_trust_parse_cmdline(device_trust_cmdline, dev,
- &dev->trust_policy);
+ if (device_trust_cmdline &&
+ device_trust_parse_cmdline(device_trust_cmdline, dev,
+ &dev->trust_policy) > 0 && dev->p)
+ dev->p->trust_policy_override = true;
}
EXPORT_SYMBOL_GPL(device_trust_init);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 262502642ca98..4620667a11832 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -563,6 +563,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
+ /* an unregistered interface is admitted later, so refuse what would fail */
+ if (!device_is_registered(dev) &&
+ !device_trust_may_claim(dev, &driver->driver))
+ return -EPERM;
+
dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index d35a7710c8ef4..2d75b85b2d139 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2209,6 +2209,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
pm_runtime_no_callbacks(&intf->dev);
dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum,
dev->devpath, configuration, ifnum);
+ /* A sibling may claim the interface before it is registered */
+ device_trust_init(&intf->dev);
/*
* Make sure the interface does not outlive its parent (e.g.
* as needed for usb_queue_reset_device() and
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
index b8f5514cacec3..552af3e73d4fc 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
struct device;
+struct device_driver;
/**
* enum device_trust_level - active trust level for a device
@@ -38,6 +39,8 @@ enum device_trust_policy {
};
void device_trust_init(struct device *dev);
+bool device_trust_may_claim(const struct device *dev,
+ const struct device_driver *drv);
enum device_trust_level device_get_trust_level(const struct device *dev);
bool device_is_adversarial(const struct device *dev);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 13/17] driver core: Preserve trust commitment beyond driver binding
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (11 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 12/17] driver core: Accept device trust policy by driver name Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 14/17] driver core: Add a global initial device trust policy Leon Romanovsky
` (3 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
IOMMU state can outlive a functional driver, including after failed setup
and unbind. The active trust level becomes disabled in those states, so
it cannot describe the authorization governing persistent translation
state. Infrastructure needs the immutable commitment to avoid treating
an unbound, previously authorized device as newly untrusted.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/base.h | 2 --
drivers/base/trust.c | 17 +++++++++++++++--
include/linux/device.h | 3 +++
include/linux/device/trust.h | 1 +
4 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b4441242496f7..dd78857abf74f 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -113,7 +113,6 @@ enum device_trust_state {
* dev_err_probe() for later retrieval via debugfs
* @device: pointer back to the struct device that this structure is
* associated with.
- * @trust_committed_level: operating level retained across bind transactions
* @trust_state: policy admission state
* @trust_initialized: bus and boot policy have initialized admission state
* @trust_policy_override: explicit policy prevents deferred driver boot policy
@@ -133,7 +132,6 @@ struct device_private {
const struct device_driver *async_driver;
char *deferred_probe_reason;
struct device *device;
- enum device_trust_level trust_committed_level;
enum device_trust_state trust_state;
u8 trust_initialized:1;
u8 trust_policy_override:1;
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index b38f69e440eae..4bcda888591a5 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -88,10 +88,10 @@ int device_trust_prepare(struct device *dev, const struct device_driver *drv)
if (level != DEVICE_TRUST_ADVERSARY && level != DEVICE_TRUST_FULL)
return -EINVAL;
if (dev->p->trust_state == DEVICE_TRUST_COMMITTED &&
- dev->p->trust_committed_level != level)
+ dev->trust_committed_level != level)
return -EINVAL;
- dev->p->trust_committed_level = level;
+ WRITE_ONCE(dev->trust_committed_level, level);
dev->p->trust_state = DEVICE_TRUST_COMMITTED;
WRITE_ONCE(dev->trust_level, level);
return 0;
@@ -119,6 +119,19 @@ enum device_trust_level device_get_trust_level(const struct device *dev)
}
EXPORT_SYMBOL_GPL(device_get_trust_level);
+/**
+ * device_get_committed_trust_level - Return the lifetime trust commitment
+ * @dev: device to inspect
+ *
+ * Return: %DEVICE_TRUST_DISABLED before admission, otherwise the level fixed
+ * before driver setup. The commitment survives setup failure and unbind.
+ */
+enum device_trust_level device_get_committed_trust_level(const struct device *dev)
+{
+ return READ_ONCE(dev->trust_committed_level);
+}
+EXPORT_SYMBOL_GPL(device_get_committed_trust_level);
+
/**
* device_is_adversarial - Test whether a device is operated as an adversary
* @dev: device to inspect
diff --git a/include/linux/device.h b/include/linux/device.h
index 5bd0cee874731..65ea07c9ac35b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -699,6 +699,8 @@ enum struct_device_flags {
* @trust_policy: Requested operating policy, resolved before driver probe.
* @trust_level: Active trust level while a driver is bound, see
* device_get_trust_level().
+ * @trust_committed_level: Operating level retained across bind transactions,
+ * see device_get_committed_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
@@ -804,6 +806,7 @@ struct device {
enum device_removable removable;
enum device_trust_policy trust_policy;
enum device_trust_level trust_level;
+ enum device_trust_level trust_committed_level;
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
index 552af3e73d4fc..19052e3301729 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -42,6 +42,7 @@ void device_trust_init(struct device *dev);
bool device_trust_may_claim(const struct device *dev,
const struct device_driver *drv);
enum device_trust_level device_get_trust_level(const struct device *dev);
+enum device_trust_level device_get_committed_trust_level(const struct device *dev);
bool device_is_adversarial(const struct device *dev);
#endif /* _DEVICE_TRUST_H_ */
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 14/17] driver core: Add a global initial device trust policy
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (12 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 13/17] driver core: Preserve trust commitment beyond driver binding Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 15/17] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
` (2 subsequent siblings)
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Confidential guests may require fresh userspace attestation before
permitting any functional driver to probe. Listing individual devices on
the command line leaves unlisted hardware free to probe, and initrd policy
writes arrive too late for built-in drivers.
A system-wide initial policy is needed to keep unlisted devices waiting
for authorization while allowing explicit exceptions for devices needed
to reach the initrd. Existing boot behavior must remain available when
no global policy is selected.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 7 +++--
Documentation/admin-guide/kernel-parameters.txt | 23 ++++++++++++--
drivers/base/base.h | 3 ++
drivers/base/trust.c | 41 ++++++++++++++++++++-----
4 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index c0ce905b625d0..6f52102a1dfbf 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -17,8 +17,11 @@ Description:
value may be selected by the bus before the device is
registered; otherwise it is "default". The policy is resolved
to an active trust level before the device is probed.
- A matching device_trust= kernel command-line entry overrides
- the initial requested policy before registration.
+ A non-default device_trust.default= kernel command-line value
+ replaces the bus policy before registration. A matching
+ device_trust= entry takes precedence over both, regardless
+ of command-line order. Boot policy also applies to new devices
+ discovered after boot.
Otherwise, device_trust.driver= can initialize policy on the
first binding attempt with a matching driver rule. A successful
policy write, including the existing value, suppresses this
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b1d6999a5befa..458f59a8af854 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1267,9 +1267,8 @@ Kernel parameters
/sys/bus/<bus>/devices/<device>. The first colon separates
the bus name; further colons belong to the device name.
Policies are default, disabled, adversary, and full.
- Entries override bus-initialized requested policy;
- unlisted devices retain their existing policy.
- Per-device entries take precedence over device_trust.driver.
+ Entries override device_trust.default, device_trust.driver,
+ and bus-initialized policy, regardless of option order.
The last entry for a device wins. If specified more than
once, the last valid list replaces all earlier lists.
Malformed lists are warned about and ignored in full.
@@ -1285,6 +1284,24 @@ Kernel parameters
device_trust=pci:0000:01:00.0=adversary,pci:0000:02:00.0=disabled
See Documentation/driver-api/driver-model/device-trust.rst.
+ device_trust.default=
+ [KNL] Set initial trust policy for devices without a
+ matching device_trust= entry, including hotplugged devices.
+ A matching device_trust.driver= rule can replace this
+ initial policy at the first binding attempt.
+ Format: { default | disabled | adversary | full }
+ default preserves bus-initialized policy (the default).
+ Other values override bus-initialized policy before
+ probing, including for built-in drivers.
+ The last valid value wins; invalid values are warned
+ about and ignored.
+ Without a matching driver rule, disabled prevents binding
+ until userspace authorizes each device by writing adversary
+ or full to trust_policy. Boot-critical devices need explicit
+ device or driver rules to operate before userspace. DMA
+ containment remains a bus and IOMMU responsibility.
+ See Documentation/driver-api/driver-model/device-trust.rst.
+
device_trust.driver=
[KNL] Set initial device trust policy by driver name,
including for drivers built into the kernel.
diff --git a/drivers/base/base.h b/drivers/base/base.h
index dd78857abf74f..5ff3217eb916e 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -193,6 +193,9 @@ bool device_trust_claim_allowed(const struct device *dev,
const struct device_driver *drv,
const char *device_options,
const char *driver_options);
+void device_trust_apply_boot_policy(struct device *dev,
+ enum device_trust_policy policy,
+ const char *options);
#endif
/* All active trust level transitions are serialized by the device lock. */
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 4bcda888591a5..c2463b51c3a03 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -249,6 +249,7 @@ const struct attribute_group device_trust_attr_group = {
static const char *device_trust_cmdline __ro_after_init;
static const char *device_trust_driver_cmdline __ro_after_init;
+static enum device_trust_policy device_trust_default_policy __ro_after_init;
static int device_trust_parse_policy(const char *options, const char *target_bus,
const char *target_name,
@@ -410,18 +411,44 @@ static int __init device_trust_driver_setup(char *str)
}
__setup("device_trust.driver=", device_trust_driver_setup);
+static int __init device_trust_default_setup(char *str)
+{
+ int policy = match_string(device_trust_policy_names,
+ ARRAY_SIZE(device_trust_policy_names), str);
+
+ if (policy < 0) {
+ pr_warn("device_trust: ignoring invalid default policy '%s'\n", str);
+ return 1;
+ }
+
+ device_trust_default_policy = policy;
+ return 1;
+}
+__setup("device_trust.default=", device_trust_default_setup);
+
+VISIBLE_IF_KUNIT void device_trust_apply_boot_policy(struct device *dev,
+ enum device_trust_policy policy,
+ const char *options)
+{
+ if (dev->p && dev->p->trust_initialized)
+ return;
+
+ if (policy != DEVICE_TRUST_POLICY_DEFAULT)
+ dev->trust_policy = policy;
+ if (options &&
+ device_trust_parse_cmdline(options, dev, &dev->trust_policy) > 0 &&
+ dev->p)
+ dev->p->trust_policy_override = true;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_apply_boot_policy);
+
/**
* device_trust_init - Apply boot policy before registration or bus policy use
* @dev: unregistered device with its bus, name, and default policy set
*/
void device_trust_init(struct device *dev)
{
- if (dev->p && dev->p->trust_initialized)
- return;
-
- if (device_trust_cmdline &&
- device_trust_parse_cmdline(device_trust_cmdline, dev,
- &dev->trust_policy) > 0 && dev->p)
- dev->p->trust_policy_override = true;
+ device_trust_apply_boot_policy(dev, device_trust_default_policy,
+ device_trust_cmdline);
}
EXPORT_SYMBOL_GPL(device_trust_init);
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 15/17] driver core: Cover trust authorization and binding with KUnit
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (13 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 14/17] driver core: Add a global initial device trust policy Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 16/17] driver core: Document trust admission and initrd authorization Leon Romanovsky
2026-11-05 11:42 ` [PATCH 17/17] PCI: Replace untrusted state with device trust Leon Romanovsky
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Requested policy, admission history and active trust have different
lifetimes. Rejected or queued probes must not consume authorization or
leave supplier state inconsistent.
Exercise disabled authorization, asynchronous and deferred binding,
supplier cleanup, resolver failures, pinned defaults across drivers, and
irreversible commitment after setup failures or unbind. Cover driver
scans past a rejected candidate, non-blocking attachment, exact boot
policy matching and malformed command lines. Free fixtures only on the
final device release.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/test/Makefile | 1 +
drivers/base/test/device-trust-test.c | 1607 +++++++++++++++++++++++++++++++++
drivers/base/trust.c | 1 +
3 files changed, 1609 insertions(+)
diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile
index 9ced7bbd569fc..dfff5e1d83f7c 100644
--- a/drivers/base/test/Makefile
+++ b/drivers/base/test/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
obj-$(CONFIG_DM_KUNIT_TEST) += root-device-test.o
obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
+obj-$(CONFIG_DM_KUNIT_TEST) += device-trust-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/base/test/device-trust-test.c b/drivers/base/test/device-trust-test.c
new file mode 100644
index 0000000000000..590676648e485
--- /dev/null
+++ b/drivers/base/test/device-trust-test.c
@@ -0,0 +1,1607 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+#include <kunit/test.h>
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "../base.h"
+
+struct device_trust_test_context {
+ struct device dev;
+ struct device_driver drv;
+
+ enum device_trust_level resolve_level;
+ enum device_trust_policy resolve_policy;
+ enum device_trust_level configure_level;
+ enum device_trust_level probe_level;
+ enum device_trust_level remove_level;
+ enum device_trust_level cleanup_level;
+ const struct device_driver *resolve_driver;
+ const struct device_driver *reject_driver;
+ struct completion *released;
+
+ int match_ret;
+ int resolve_ret;
+ int configure_ret;
+ int probe_ret;
+ unsigned int resolve_count;
+ unsigned int configure_count;
+ unsigned int probe_count;
+ unsigned int remove_count;
+ unsigned int cleanup_count;
+ bool probe_adversarial;
+ bool device_initialized;
+ bool device_registered;
+ bool driver_registered;
+};
+
+static struct device_trust_test_context *
+to_device_trust_test_context(struct device *dev)
+{
+ return container_of(dev, struct device_trust_test_context, dev);
+}
+
+static int device_trust_test_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ return READ_ONCE(ctx->match_ret);
+}
+
+static int device_trust_test_resolve(struct device *dev,
+ const struct device_driver *drv,
+ enum device_trust_policy policy,
+ enum device_trust_level *level)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ ctx->resolve_count++;
+ ctx->resolve_policy = policy;
+ ctx->resolve_driver = drv;
+ if (ctx->reject_driver && drv == ctx->reject_driver)
+ return -EPERM;
+ if (ctx->resolve_ret)
+ return ctx->resolve_ret;
+
+ *level = ctx->resolve_level;
+ return 0;
+}
+
+static int device_trust_test_dma_configure(struct device *dev)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ ctx->configure_count++;
+ ctx->configure_level = device_get_trust_level(dev);
+ return ctx->configure_ret;
+}
+
+static void device_trust_test_dma_cleanup(struct device *dev)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ ctx->cleanup_count++;
+ ctx->cleanup_level = device_get_trust_level(dev);
+}
+
+static int device_trust_test_probe(struct device *dev)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ ctx->probe_count++;
+ ctx->probe_level = device_get_trust_level(dev);
+ ctx->probe_adversarial = device_is_adversarial(dev);
+ return ctx->probe_ret;
+}
+
+static int device_trust_test_remove(struct device *dev)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+
+ ctx->remove_count++;
+ ctx->remove_level = device_get_trust_level(dev);
+ return 0;
+}
+
+static const struct bus_type device_trust_test_plain_bus = {
+ .name = "device-trust-test-plain",
+ .match = device_trust_test_match,
+ .dma_configure = device_trust_test_dma_configure,
+ .dma_cleanup = device_trust_test_dma_cleanup,
+};
+
+static const struct bus_type device_trust_test_resolving_bus = {
+ .name = "device-trust-test-resolving",
+ .match = device_trust_test_match,
+ .trust_resolve = device_trust_test_resolve,
+ .dma_configure = device_trust_test_dma_configure,
+ .dma_cleanup = device_trust_test_dma_cleanup,
+};
+
+static const struct device_type device_trust_test_child_type = {
+ .need_parent_lock = true,
+};
+
+static bool device_trust_test_plain_bus_registered;
+static bool device_trust_test_resolving_bus_registered;
+
+static void device_trust_test_release(struct device *dev)
+{
+ struct device_trust_test_context *ctx =
+ to_device_trust_test_context(dev);
+ struct completion *released = ctx->released;
+
+ kfree(ctx);
+ complete(released);
+}
+
+/*
+ * The fixture is freed when the last reference to its device is dropped,
+ * which deferred probing, device links and delayed kobject release may do
+ * after the test. Wait for that so the release callback never outlives it.
+ */
+static void device_trust_test_device_unregister(void *data)
+{
+ struct device_trust_test_context *ctx = data;
+ struct completion *released = ctx->released;
+
+ if (!ctx->device_initialized)
+ return;
+
+ ctx->device_initialized = false;
+ if (ctx->device_registered) {
+ ctx->device_registered = false;
+ device_unregister(&ctx->dev);
+ } else {
+ put_device(&ctx->dev);
+ }
+ if (!wait_for_completion_timeout(released, 10 * HZ)) {
+ WARN(1, "device-trust test fixture is still referenced\n");
+ wait_for_completion(released);
+ }
+}
+
+static void device_trust_test_driver_unregister(void *data)
+{
+ struct device_trust_test_context *ctx = data;
+
+ if (!ctx->driver_registered)
+ return;
+
+ ctx->driver_registered = false;
+ driver_unregister(&ctx->drv);
+}
+
+static struct device_trust_test_context *
+device_trust_test_device_register_parent(struct kunit *test,
+ const struct bus_type *bus,
+ enum device_trust_policy policy,
+ enum device_trust_policy boot_policy,
+ struct device *parent)
+{
+ struct device_trust_test_context *ctx;
+ struct completion *released;
+ int ret;
+
+ released = kunit_kzalloc(test, sizeof(*released), GFP_KERNEL);
+ if (!released)
+ return ERR_PTR(-ENOMEM);
+ init_completion(released);
+
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->released = released;
+ ctx->match_ret = 1;
+ ctx->resolve_level = DEVICE_TRUST_FULL;
+ ctx->drv.probe_type = PROBE_FORCE_SYNCHRONOUS;
+ ctx->drv.suppress_bind_attrs = true;
+ device_initialize(&ctx->dev);
+ ctx->dev.bus = bus;
+ ctx->dev.parent = parent;
+ if (parent && bus == &device_trust_test_plain_bus)
+ ctx->dev.type = &device_trust_test_child_type;
+ ctx->dev.release = device_trust_test_release;
+ ctx->dev.trust_policy = policy;
+ ctx->device_initialized = true;
+
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_device_unregister, ctx);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = dev_set_name(&ctx->dev, "%s-%s",
+ bus ? bus->name : "device-trust-test-busless",
+ test->name);
+ if (ret)
+ return ERR_PTR(ret);
+
+ device_trust_apply_boot_policy(&ctx->dev, boot_policy, NULL);
+ ret = device_add(&ctx->dev);
+ if (ret)
+ return ERR_PTR(ret);
+ ctx->device_registered = true;
+
+ return ctx;
+}
+
+static struct device_trust_test_context *
+device_trust_test_device_register(struct kunit *test,
+ const struct bus_type *bus,
+ enum device_trust_policy policy)
+{
+ return device_trust_test_device_register_parent(test, bus, policy,
+ DEVICE_TRUST_POLICY_DEFAULT, NULL);
+}
+
+static int device_trust_test_driver_register(struct kunit *test,
+ struct device_trust_test_context *ctx)
+{
+ int ret;
+
+ ctx->drv.name = test->name;
+ ctx->drv.bus = ctx->dev.bus ?: &device_trust_test_plain_bus;
+ ctx->drv.probe = device_trust_test_probe;
+ ctx->drv.remove = device_trust_test_remove;
+
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_driver_unregister, ctx);
+ if (ret)
+ return ret;
+
+ ret = driver_register(&ctx->drv);
+ if (ret)
+ return ret;
+ ctx->driver_registered = true;
+
+ return 0;
+}
+
+static void device_trust_expect_policy_locked(struct kunit *test,
+ struct device *dev)
+{
+ enum device_trust_policy original = dev->trust_policy;
+ enum device_trust_level active = device_get_trust_level(dev);
+ enum device_trust_policy policy;
+
+ for (policy = DEVICE_TRUST_POLICY_DEFAULT;
+ policy <= DEVICE_TRUST_POLICY_FULL; policy++) {
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(dev, policy),
+ policy == original ? 0 : -EBUSY);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, original);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(dev), active);
+ }
+}
+
+static void device_trust_default_bind_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_plain_bus, DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_FALSE(test, ctx->probe_adversarial);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_FULL);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ DEVICE_TRUST_FULL);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_bus_adversary_bind_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->resolve_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->resolve_policy,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->resolve_driver, &ctx->drv);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_TRUE(test, ctx->probe_adversarial);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+}
+
+static void device_trust_test_other_driver_unregister(void *data)
+{
+ driver_unregister(data);
+}
+
+static void device_trust_default_pinned_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ struct device_driver *other;
+ int ret;
+
+ ctx = device_trust_test_device_register(test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ device_release_driver(&ctx->dev);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, other);
+ other->name = "device-trust-pinned-other";
+ other->bus = ctx->dev.bus;
+ other->probe = device_trust_test_probe;
+ other->remove = device_trust_test_remove;
+ other->probe_type = PROBE_FORCE_SYNCHRONOUS;
+ other->suppress_bind_attrs = true;
+ ctx->resolve_level = DEVICE_TRUST_FULL;
+ KUNIT_ASSERT_EQ(test, driver_register(other), 0);
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_other_driver_unregister, other);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, device_driver_attach(other, &ctx->dev), -EINVAL);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->resolve_driver, other);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_DEFAULT);
+
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ KUNIT_ASSERT_EQ(test, device_driver_attach(other, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_PTR_EQ(test, ctx->dev.driver, other);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_authorize_test(struct kunit *test,
+ enum device_trust_policy initial_policy,
+ enum device_trust_policy boot_policy,
+ enum device_trust_policy policy,
+ enum device_trust_level level)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register_parent(
+ test, &device_trust_test_plain_bus, initial_policy, boot_policy, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ KUNIT_ASSERT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_DISABLED);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = device_driver_attach(&ctx->drv, &ctx->dev);
+ KUNIT_EXPECT_EQ(test, ret, -EPERM);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_TRUE(test, dev_can_match(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DISABLED), 0);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DEFAULT), -EBUSY);
+
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev, policy), 0);
+ device_trust_apply_boot_policy(&ctx->dev, DEVICE_TRUST_POLICY_DISABLED, NULL);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, policy);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, level);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, level);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, level);
+ device_trust_apply_boot_policy(&ctx->dev, DEVICE_TRUST_POLICY_DISABLED, NULL);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, policy);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ level);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_disabled_bind_test(struct kunit *test)
+{
+ device_trust_authorize_test(test, DEVICE_TRUST_POLICY_DISABLED,
+ DEVICE_TRUST_POLICY_DEFAULT, DEVICE_TRUST_POLICY_ADVERSARY,
+ DEVICE_TRUST_ADVERSARY);
+}
+
+static void device_trust_disabled_full_bind_test(struct kunit *test)
+{
+ device_trust_authorize_test(test, DEVICE_TRUST_POLICY_DISABLED,
+ DEVICE_TRUST_POLICY_DEFAULT, DEVICE_TRUST_POLICY_FULL,
+ DEVICE_TRUST_FULL);
+}
+
+static void device_trust_global_disabled_adversary_test(struct kunit *test)
+{
+ device_trust_authorize_test(test, DEVICE_TRUST_POLICY_ADVERSARY,
+ DEVICE_TRUST_POLICY_DISABLED, DEVICE_TRUST_POLICY_ADVERSARY,
+ DEVICE_TRUST_ADVERSARY);
+}
+
+static void device_trust_global_disabled_full_test(struct kunit *test)
+{
+ device_trust_authorize_test(test, DEVICE_TRUST_POLICY_FULL,
+ DEVICE_TRUST_POLICY_DISABLED, DEVICE_TRUST_POLICY_FULL,
+ DEVICE_TRUST_FULL);
+}
+
+static void device_trust_test_link_wait(void *data)
+{
+ device_link_wait_removal();
+}
+
+static void device_trust_supplier_retry_test(struct kunit *test)
+{
+ struct device_trust_test_context *supplier, *consumer;
+ struct device_link *link;
+ int ret;
+
+ supplier = device_trust_test_device_register(test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, supplier), 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&supplier->dev));
+ /* Runs after the consumer is gone and before the supplier goes. */
+ ret = kunit_add_action_or_reset(test, device_trust_test_link_wait, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ consumer = device_trust_test_device_register(test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer);
+ consumer->resolve_level = DEVICE_TRUST_ADVERSARY;
+ link = device_link_add(&consumer->dev, &supplier->dev, 0);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, consumer), 0);
+ KUNIT_ASSERT_EQ(test, link->status, DL_STATE_AVAILABLE);
+ KUNIT_EXPECT_EQ(test, consumer->dev.links.status, DL_DEV_NO_DRIVER);
+ KUNIT_EXPECT_EQ(test, consumer->configure_count, 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&consumer->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ consumer->resolve_ret = -EAGAIN;
+ KUNIT_EXPECT_EQ(test, device_driver_attach(&consumer->drv, &consumer->dev), -EAGAIN);
+ KUNIT_ASSERT_EQ(test, link->status, DL_STATE_AVAILABLE);
+ KUNIT_EXPECT_EQ(test, consumer->dev.links.status, DL_DEV_NO_DRIVER);
+
+ consumer->resolve_ret = 0;
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&consumer->drv, &consumer->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&consumer->dev));
+ KUNIT_EXPECT_EQ(test, link->status, DL_STATE_ACTIVE);
+ device_release_driver(&consumer->dev);
+ KUNIT_ASSERT_EQ(test, link->status, DL_STATE_AVAILABLE);
+ device_release_driver(&supplier->dev);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&supplier->dev));
+}
+
+static void device_trust_match_defer_authorize_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+
+ ctx = device_trust_test_device_register(test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->match_ret = -EPROBE_DEFER;
+
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+ KUNIT_EXPECT_TRUE(test, dev_can_match(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ WRITE_ONCE(ctx->match_ret, 1);
+ KUNIT_ASSERT_EQ(test, device_attach(&ctx->dev), 1);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+}
+
+static void device_trust_test_parent_unregister(void *data)
+{
+ root_device_unregister(data);
+}
+
+static void device_trust_async_policy_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ struct device *parent;
+ int ret;
+
+ parent = root_device_register(test->name);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_parent_unregister, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ctx = device_trust_test_device_register_parent(test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DEFAULT,
+ DEVICE_TRUST_POLICY_DEFAULT, parent);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->drv.probe_type = PROBE_PREFER_ASYNCHRONOUS;
+
+ /* The queued probe cannot acquire the parent until policy is disabled. */
+ device_lock(parent);
+ ret = device_trust_test_driver_register(test, ctx);
+ if (!ret)
+ ret = device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DISABLED);
+ device_unlock(parent);
+ wait_for_device_probe();
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_TRUE(test, dev_can_match(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DEFAULT), -EBUSY);
+
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+}
+
+static void device_trust_generic_adversary_bind_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_ADVERSARY);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_TRUE(test, ctx->probe_adversarial);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+}
+
+static int device_trust_platform_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static void device_trust_no_iommu_bind_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct platform_driver *drv;
+
+ drv = kunit_kzalloc(test, sizeof(*drv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, drv);
+ drv->driver.name = test->name;
+ drv->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
+ drv->driver.suppress_bind_attrs = true;
+ drv->probe = device_trust_platform_probe;
+ pdev = kunit_platform_device_alloc(test, test->name, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ pdev->dev.trust_policy = DEVICE_TRUST_POLICY_DISABLED;
+ KUNIT_ASSERT_EQ(test, kunit_platform_device_add(test, pdev), 0);
+ KUNIT_ASSERT_PTR_EQ(test, pdev->dev.iommu_group, NULL);
+ KUNIT_ASSERT_EQ(test, kunit_platform_driver_register(test, drv), 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&pdev->dev));
+
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&pdev->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&drv->driver, &pdev->dev), 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&pdev->dev));
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&pdev->dev), DEVICE_TRUST_ADVERSARY);
+
+ device_release_driver(&pdev->dev);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&pdev->dev), DEVICE_TRUST_DISABLED);
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&drv->driver, &pdev->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&pdev->dev));
+}
+
+static void device_trust_explicit_policy_mismatch_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_ADVERSARY);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_FULL;
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = device_driver_attach(&ctx->drv, &ctx->dev);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+ KUNIT_EXPECT_GT(test, ctx->resolve_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->resolve_policy,
+ DEVICE_TRUST_POLICY_ADVERSARY);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_FULL), 0);
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_FULL);
+}
+
+static void device_trust_resolver_retry_test(struct kunit *test, int error,
+ enum device_trust_level level)
+{
+ struct device_trust_test_context *ctx;
+
+ ctx = device_trust_test_device_register(test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_ret = error;
+ ctx->resolve_level = level;
+
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_GT(test, ctx->resolve_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_FULL), 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DEFAULT), 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DISABLED), 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ KUNIT_EXPECT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), error ?: -EINVAL);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ ctx->resolve_ret = 0;
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_resolver_failure_test(struct kunit *test)
+{
+ device_trust_resolver_retry_test(test, -EAGAIN, DEVICE_TRUST_FULL);
+}
+
+static void device_trust_resolver_disabled_test(struct kunit *test)
+{
+ device_trust_resolver_retry_test(test, 0, DEVICE_TRUST_DISABLED);
+}
+
+static void device_trust_probe_failure_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_ADVERSARY);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ ctx->probe_ret = -EINVAL;
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_probe_defer_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ ctx->probe_ret = -EPROBE_DEFER;
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->resolve_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ ctx->probe_ret = 0;
+ ret = device_driver_attach(&ctx->drv, &ctx->dev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->resolve_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 2);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_ADVERSARY);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_dma_failure_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ ctx->configure_ret = -EIO;
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->configure_level, DEVICE_TRUST_ADVERSARY);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 0);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ DEVICE_TRUST_ADVERSARY);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+
+ ctx->configure_ret = 0;
+ ctx->resolve_level = DEVICE_TRUST_FULL;
+ KUNIT_EXPECT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), -EINVAL);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ ctx->resolve_level = DEVICE_TRUST_ADVERSARY;
+ KUNIT_ASSERT_EQ(test, device_driver_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+}
+
+static void device_trust_force_bind_guard_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ unsigned int resolve_count;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ resolve_count = ctx->resolve_count;
+
+ device_lock(&ctx->dev);
+ ctx->dev.driver = &ctx->drv;
+ ret = device_bind_driver(&ctx->dev);
+ ctx->dev.driver = NULL;
+ device_unlock(&ctx->dev);
+
+ KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP);
+ KUNIT_EXPECT_EQ(test, ctx->resolve_count, resolve_count);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+}
+
+static void device_trust_legacy_force_bind_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_FULL), 0);
+
+ device_lock(&ctx->dev);
+ ctx->dev.driver = &ctx->drv;
+ ret = device_bind_driver(&ctx->dev);
+ if (ret)
+ ctx->dev.driver = NULL;
+ device_unlock(&ctx->dev);
+
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_FULL);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+}
+
+static void device_trust_busless_force_bind_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, NULL, DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ret = device_trust_test_driver_register(test, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ device_lock(&ctx->dev);
+ ctx->dev.driver = &ctx->drv;
+ ret = device_bind_driver(&ctx->dev);
+ if (ret)
+ ctx->dev.driver = NULL;
+ device_unlock(&ctx->dev);
+
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_FULL);
+
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 0);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ DEVICE_TRUST_DISABLED);
+}
+
+static const struct bus_type device_trust_test_pci_bus = { .name = "pci" };
+static const struct bus_type device_trust_test_usb_bus = { .name = "usb" };
+static const struct bus_type device_trust_test_platform_bus = { .name = "platform" };
+
+struct device_trust_test_stub {
+ struct device_driver drv;
+ struct device_private private;
+ struct device dev;
+};
+
+/* Stack instances of these objects overflow the frame limit under KASAN. */
+static struct device_trust_test_stub *
+device_trust_test_stub_alloc(struct kunit *test)
+{
+ struct device_trust_test_stub *stub;
+
+ stub = kunit_kzalloc(test, sizeof(*stub), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stub);
+
+ return stub;
+}
+
+static struct device *
+device_trust_test_stub_reset(struct device_trust_test_stub *stub,
+ const struct bus_type *bus, const char *name,
+ enum device_trust_policy policy)
+{
+ memset(stub, 0, sizeof(*stub));
+ stub->drv.bus = bus;
+ stub->drv.name = "test";
+ stub->dev.bus = bus;
+ stub->dev.init_name = name;
+ stub->dev.trust_policy = policy;
+ mutex_init(&stub->dev.mutex);
+
+ return &stub->dev;
+}
+
+static struct device *
+device_trust_test_pci_stub(struct device_trust_test_stub *stub, const char *name,
+ enum device_trust_policy policy)
+{
+ return device_trust_test_stub_reset(stub, &device_trust_test_pci_bus, name,
+ policy);
+}
+
+static void device_trust_cmdline_policies_test(struct kunit *test)
+{
+ static const char options[] =
+ "pci:0000:01:00.0=adversary,usb:1-1=full,usb:1-2=disabled,platform:i8042=default";
+ static const struct {
+ const struct bus_type *bus;
+ const char *name;
+ enum device_trust_policy policy;
+ } cases[] = {
+ { &device_trust_test_pci_bus, "0000:01:00.0", DEVICE_TRUST_POLICY_ADVERSARY },
+ { &device_trust_test_usb_bus, "1-1", DEVICE_TRUST_POLICY_FULL },
+ { &device_trust_test_usb_bus, "1-2", DEVICE_TRUST_POLICY_DISABLED },
+ { &device_trust_test_platform_bus, "i8042", DEVICE_TRUST_POLICY_DEFAULT },
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cases); i++) {
+ enum device_trust_policy policy = DEVICE_TRUST_POLICY_DISABLED;
+ struct device *dev = device_trust_test_stub_reset(stub, cases[i].bus,
+ cases[i].name,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ int ret;
+
+ ret = device_trust_parse_cmdline(options, dev, &policy);
+ KUNIT_EXPECT_EQ_MSG(test, ret, 1, "case %u", i);
+ KUNIT_EXPECT_EQ_MSG(test, policy, cases[i].policy, "case %u", i);
+ }
+}
+
+static void device_trust_cmdline_exact_match_test(struct kunit *test)
+{
+ static const char * const options[] = {
+ "pcie:0000:01:00.0=full",
+ "pc:0000:01:00.0=full",
+ "usb:0000:01:00.0=full",
+ "pci:0000:01:00.01=full",
+ "pci:0000:01:00=full",
+ "pci:0000:01:00.1=full",
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, "0000:01:00.0",
+ DEVICE_TRUST_POLICY_DEFAULT);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ enum device_trust_policy policy = DEVICE_TRUST_POLICY_ADVERSARY;
+ int ret;
+
+ ret = device_trust_parse_cmdline(options[i], dev, &policy);
+ KUNIT_EXPECT_EQ_MSG(test, ret, 0, "%s", options[i]);
+ KUNIT_EXPECT_EQ_MSG(test, policy, DEVICE_TRUST_POLICY_ADVERSARY,
+ "%s", options[i]);
+ }
+}
+
+static void device_trust_cmdline_last_match_test(struct kunit *test)
+{
+ static const char options[] =
+ "pci:0000:01:00.0=disabled,usb:1-1=full,pci:0000:01:00.0=full,usb:1-2=disabled";
+ static const char defaults[] =
+ "pci:0000:01:00.0=disabled,pci:0000:01:00.0=default";
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, "0000:01:00.0",
+ DEVICE_TRUST_POLICY_DEFAULT);
+ enum device_trust_policy policy = DEVICE_TRUST_POLICY_ADVERSARY;
+ int ret;
+
+ ret = device_trust_parse_cmdline(options, dev, &policy);
+ KUNIT_ASSERT_EQ(test, ret, 1);
+ KUNIT_EXPECT_EQ(test, policy, DEVICE_TRUST_POLICY_FULL);
+
+ ret = device_trust_parse_cmdline(defaults, dev, &policy);
+ KUNIT_ASSERT_EQ(test, ret, 1);
+ KUNIT_EXPECT_EQ(test, policy, DEVICE_TRUST_POLICY_DEFAULT);
+}
+
+static void device_trust_cmdline_nameless_test(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, NULL,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ enum device_trust_policy policy = DEVICE_TRUST_POLICY_ADVERSARY;
+ int ret;
+
+ ret = device_trust_parse_cmdline("pci:0000:01:00.0=full", dev, &policy);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, policy, DEVICE_TRUST_POLICY_ADVERSARY);
+}
+
+static void device_trust_cmdline_validate_test(struct kunit *test)
+{
+ static const char valid[] = "pci:0000:01:00.0=adversary,usb:1-2=disabled";
+ static const char invalid[] = "pci:0000:01:00.0=adversary,usb:1-2=invalid";
+
+ KUNIT_EXPECT_EQ(test, device_trust_parse_cmdline(valid, NULL, NULL), 0);
+ KUNIT_EXPECT_EQ(test, device_trust_parse_cmdline(invalid, NULL, NULL), -EINVAL);
+}
+
+static void device_trust_cmdline_invalid_test(struct kunit *test)
+{
+ static const char * const options[] = {
+ "",
+ ",pci:0000:01:00.0=full",
+ "pci:0000:01:00.0=full,",
+ "pci:0000:01:00.0=full,,usb:1-2=disabled",
+ ":0000:01:00.0=full",
+ "pci:=full",
+ "pci:0000:01:00.0=",
+ "pci=full",
+ "pci=bad:0000:01:00.0=full",
+ "pci:0000:01:00.0",
+ "pci:0000:01:00.0=unknown",
+ "pci:0000:01:00.0=full=disabled",
+ "pc i:0000:01:00.0=full",
+ "pci:0000:01:00.0 =full",
+ "pci:0000:01:00.0= full",
+ "pci:0000:01:00.0=full\n",
+ "pci:0000:01:00.0\t=full",
+ "pci:0000:01:00.0\001=full",
+ "p\"ci:0000:01:00.0=full",
+ "pci:0000:01:00.0=\"full\"",
+ "pci:0000:01:00.0=full,usb:1-2=unknown",
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, "0000:01:00.0",
+ DEVICE_TRUST_POLICY_DEFAULT);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ enum device_trust_policy policy = DEVICE_TRUST_POLICY_ADVERSARY;
+ int ret;
+
+ ret = device_trust_parse_cmdline(options[i], dev, &policy);
+ KUNIT_EXPECT_EQ_MSG(test, ret, -EINVAL, "case %u", i);
+ KUNIT_EXPECT_EQ_MSG(test, policy, DEVICE_TRUST_POLICY_ADVERSARY,
+ "case %u", i);
+ }
+}
+
+static void device_trust_boot_precedence_test(struct kunit *test)
+{
+ static const char * const overrides[] = {
+ [DEVICE_TRUST_POLICY_DEFAULT] = "pci:0000:01:00.0=default",
+ [DEVICE_TRUST_POLICY_DISABLED] = "pci:0000:01:00.0=disabled",
+ [DEVICE_TRUST_POLICY_ADVERSARY] = "pci:0000:01:00.0=adversary",
+ [DEVICE_TRUST_POLICY_FULL] = "pci:0000:01:00.0=full",
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ unsigned int initial, global, override;
+
+ for (initial = DEVICE_TRUST_POLICY_DEFAULT;
+ initial <= DEVICE_TRUST_POLICY_FULL; initial++) {
+ for (global = DEVICE_TRUST_POLICY_DEFAULT;
+ global <= DEVICE_TRUST_POLICY_FULL; global++) {
+ struct device *dev = device_trust_test_pci_stub(stub, "0000:01:00.0",
+ initial);
+ enum device_trust_policy expected = global ?: initial;
+
+ device_trust_apply_boot_policy(dev, global, NULL);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, expected);
+ device_trust_apply_boot_policy(dev, global,
+ "pci:0000:02:00.0=full");
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, expected);
+ for (override = 0; override < ARRAY_SIZE(overrides); override++) {
+ dev->trust_policy = initial;
+ device_trust_apply_boot_policy(dev, global,
+ overrides[override]);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, override);
+ device_trust_apply_boot_policy(dev, global,
+ overrides[override]);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, override);
+ }
+ }
+ }
+}
+
+static void device_trust_global_busless_test(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_stub_reset(stub, NULL, NULL,
+ DEVICE_TRUST_POLICY_FULL);
+
+ device_trust_apply_boot_policy(dev, DEVICE_TRUST_POLICY_DISABLED,
+ "pci:0000:01:00.0=full");
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, DEVICE_TRUST_POLICY_DISABLED);
+}
+
+static void device_trust_driver_precedence_test(struct kunit *test)
+{
+ static const char * const driver_rules[] = {
+ "pci:test=default", "pci:test=disabled",
+ "pci:test=adversary", "pci:test=full",
+ };
+ static const char * const device_rules[] = {
+ NULL, "pci:0000:01:00.0=default", "pci:0000:01:00.0=disabled",
+ "pci:0000:01:00.0=adversary", "pci:0000:01:00.0=full",
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ unsigned int initial, global, driver, device;
+ struct device *dev;
+ bool allowed, ok;
+
+ for (initial = DEVICE_TRUST_POLICY_DEFAULT;
+ initial <= DEVICE_TRUST_POLICY_FULL; initial++) {
+ for (global = DEVICE_TRUST_POLICY_DEFAULT;
+ global <= DEVICE_TRUST_POLICY_FULL; global++) {
+ for (driver = 0; driver < ARRAY_SIZE(driver_rules); driver++) {
+ for (device = 0; device < ARRAY_SIZE(device_rules); device++) {
+ dev = device_trust_test_pci_stub(stub, "0000:01:00.0",
+ initial);
+
+ device_trust_apply_boot_policy(dev, global,
+ device_rules[device]);
+ /* The verdict for a claim before registration */
+ allowed = device_trust_claim_allowed(dev, &stub->drv,
+ device_rules[device],
+ driver_rules[driver]);
+ dev->p = &stub->private;
+ device_trust_apply_boot_policy(dev, global,
+ device_rules[device]);
+ device_trust_init_state(dev);
+ device_lock(dev);
+ device_trust_apply_driver_policy(dev, &stub->drv,
+ driver_rules[driver]);
+ device_unlock(dev);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy,
+ device ? device - 1 : driver);
+ /* Driver rules are left to admission */
+ ok = !device || device - 1 != DEVICE_TRUST_POLICY_DISABLED;
+ KUNIT_EXPECT_EQ(test, allowed, ok);
+ }
+ }
+ }
+ }
+}
+
+static void device_trust_claim_allowed_test(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev;
+
+ dev = device_trust_test_pci_stub(stub, "0000:01:00.0", DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_EXPECT_FALSE(test, device_trust_claim_allowed(dev, &stub->drv, NULL, NULL));
+ KUNIT_EXPECT_FALSE(test, device_trust_claim_allowed(dev, &stub->drv, NULL,
+ "pci:other=full"));
+ KUNIT_EXPECT_TRUE(test, device_trust_claim_allowed(dev, &stub->drv,
+ "pci:0000:01:00.0=full", NULL));
+ KUNIT_EXPECT_FALSE(test, device_trust_claim_allowed(dev, &stub->drv,
+ "pci:0000:01:00.0=disabled",
+ "pci:test=full"));
+ KUNIT_EXPECT_TRUE(test, device_trust_claim_allowed(dev, &stub->drv, NULL,
+ "pci:test=full"));
+ KUNIT_EXPECT_TRUE(test, device_trust_claim_allowed(dev, &stub->drv, NULL,
+ "pci:test=disabled"));
+
+ dev = device_trust_test_pci_stub(stub, "0000:01:00.0", DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_EXPECT_TRUE(test, device_trust_claim_allowed(dev, &stub->drv, NULL, NULL));
+}
+
+static void device_trust_driver_matching_test(struct kunit *test)
+{
+ static const char * const unmatched[] = {
+ NULL, "usb:test=disabled", "pc:test=disabled", "pcie:test=disabled",
+ "pci:tes=disabled", "pci:test1=disabled", "pci:te*=disabled",
+ "pci:test=disabled,pci:other=invalid", "pci:test=disabled,",
+ };
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, NULL,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ unsigned int i;
+
+ dev->p = &stub->private;
+ device_trust_init_state(dev);
+ for (i = 0; i < ARRAY_SIZE(unmatched); i++) {
+ device_lock(dev);
+ device_trust_apply_driver_policy(dev, &stub->drv, unmatched[i]);
+ device_unlock(dev);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, DEVICE_TRUST_POLICY_DEFAULT);
+ }
+ device_lock(dev);
+ device_trust_apply_driver_policy(dev, &stub->drv,
+ "pci:test=disabled,pci:test=adversary");
+ device_unlock(dev);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, DEVICE_TRUST_POLICY_ADVERSARY);
+}
+
+static void device_trust_driver_user_precedence_test(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ enum device_trust_policy policy;
+
+ for (policy = DEVICE_TRUST_POLICY_DEFAULT;
+ policy <= DEVICE_TRUST_POLICY_FULL; policy++) {
+ struct device *dev = device_trust_test_pci_stub(stub, NULL,
+ policy);
+
+ dev->p = &stub->private;
+ device_trust_init_state(dev);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(dev, policy), 0);
+ device_lock(dev);
+ device_trust_apply_driver_policy(dev, &stub->drv, "pci:test=disabled");
+ device_trust_apply_driver_policy(dev, &stub->drv, "pci:test=full");
+ device_unlock(dev);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, policy);
+ }
+}
+
+static void device_trust_driver_rejected_write_test(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, NULL,
+ DEVICE_TRUST_POLICY_DISABLED);
+
+ dev->p = &stub->private;
+ device_trust_init_state(dev);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(dev, DEVICE_TRUST_POLICY_DEFAULT),
+ -EBUSY);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(dev, DEVICE_TRUST_POLICY_FULL + 1),
+ -EINVAL);
+ device_lock(dev);
+ device_trust_apply_driver_policy(dev, &stub->drv, "pci:test=full");
+ device_unlock(dev);
+ KUNIT_EXPECT_EQ(test, dev->trust_policy, DEVICE_TRUST_POLICY_FULL);
+}
+
+static void device_trust_driver_authorize_test(struct kunit *test)
+{
+ const struct device_driver drv = {
+ .bus = &device_trust_test_plain_bus,
+ .name = "test",
+ };
+ struct device_trust_test_context *ctx;
+ const struct device_driver other = {
+ .bus = &device_trust_test_plain_bus,
+ .name = "other",
+ };
+ int ret;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_plain_bus, DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &drv,
+ "device-trust-test-plain:test=disabled");
+ device_unlock(&ctx->dev);
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &other,
+ "device-trust-test-plain:other=full");
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &drv,
+ "device-trust-test-plain:test=full");
+ device_unlock(&ctx->dev);
+ ret = device_attach(&ctx->dev);
+ KUNIT_ASSERT_EQ(test, ret, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_ADVERSARY);
+
+ device_release_driver(&ctx->dev);
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &drv,
+ "device-trust-test-plain:test=full");
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_ADVERSARY);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+}
+
+static void device_trust_driver_resolver_failure_test(struct kunit *test)
+{
+ const struct device_driver drv = {
+ .bus = &device_trust_test_resolving_bus,
+ .name = "test",
+ };
+ struct device_trust_test_context *ctx;
+
+ ctx = device_trust_test_device_register(
+ test, &device_trust_test_resolving_bus, DEVICE_TRUST_POLICY_DISABLED);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &drv,
+ "device-trust-test-resolving:test=full");
+ device_unlock(&ctx->dev);
+ ctx->resolve_ret = -EIO;
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->resolve_policy, DEVICE_TRUST_POLICY_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+
+ device_lock(&ctx->dev);
+ device_trust_apply_driver_policy(&ctx->dev, &drv,
+ "device-trust-test-resolving:test=disabled");
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_FULL);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_ADVERSARY), 0);
+}
+
+static void device_trust_claimed_policy_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+
+ ctx = device_trust_test_device_register(test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ /* A driver that claimed the device before registration skipped admission. */
+ device_lock(&ctx->dev);
+ ctx->dev.driver = &ctx->drv;
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DISABLED), -EBUSY);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DEFAULT), 0);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, DEVICE_TRUST_POLICY_DEFAULT);
+
+ device_lock(&ctx->dev);
+ ctx->dev.driver = NULL;
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, device_trust_set_policy(&ctx->dev,
+ DEVICE_TRUST_POLICY_DISABLED), 0);
+}
+
+static void device_trust_try_attach_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ struct device *parent;
+ bool parent_free;
+ int ret;
+
+ parent = root_device_register(test->name);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_parent_unregister, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ctx = device_trust_test_device_register_parent(test, &device_trust_test_plain_bus,
+ DEVICE_TRUST_POLICY_DEFAULT,
+ DEVICE_TRUST_POLICY_DEFAULT, parent);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->match_ret = 0;
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+
+ device_lock(parent);
+ KUNIT_EXPECT_EQ(test, device_driver_try_attach(&ctx->drv, &ctx->dev), -EBUSY);
+ device_unlock(parent);
+
+ device_lock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, device_driver_try_attach(&ctx->drv, &ctx->dev), -EBUSY);
+ device_unlock(&ctx->dev);
+
+ parent_free = device_trylock(parent);
+ if (parent_free)
+ device_unlock(parent);
+ KUNIT_EXPECT_TRUE(test, parent_free);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+
+ KUNIT_ASSERT_EQ(test, device_driver_try_attach(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+}
+
+static void device_trust_scan_continues_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ struct device_driver *other;
+ int ret;
+
+ ctx = device_trust_test_device_register(test, &device_trust_test_resolving_bus,
+ DEVICE_TRUST_POLICY_DEFAULT);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->match_ret = 0;
+ KUNIT_ASSERT_EQ(test, device_trust_test_driver_register(test, ctx), 0);
+
+ other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, other);
+ other->name = "device-trust-scan-other";
+ other->bus = ctx->dev.bus;
+ other->probe = device_trust_test_probe;
+ other->remove = device_trust_test_remove;
+ other->probe_type = PROBE_FORCE_SYNCHRONOUS;
+ other->suppress_bind_attrs = true;
+ KUNIT_ASSERT_EQ(test, driver_register(other), 0);
+ ret = kunit_add_action_or_reset(test,
+ device_trust_test_other_driver_unregister, other);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /* The first candidate is rejected; the scan must reach the second. */
+ ctx->reject_driver = &ctx->drv;
+ WRITE_ONCE(ctx->match_ret, 1);
+ KUNIT_EXPECT_EQ(test, device_attach(&ctx->dev), 1);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->dev.driver, other);
+ KUNIT_EXPECT_EQ(test, ctx->resolve_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+}
+
+static struct kunit_case device_trust_test_cases[] = {
+ KUNIT_CASE(device_trust_default_bind_test),
+ KUNIT_CASE(device_trust_bus_adversary_bind_test),
+ KUNIT_CASE(device_trust_default_pinned_test),
+ KUNIT_CASE(device_trust_disabled_bind_test),
+ KUNIT_CASE(device_trust_disabled_full_bind_test),
+ KUNIT_CASE(device_trust_global_disabled_adversary_test),
+ KUNIT_CASE(device_trust_global_disabled_full_test),
+ KUNIT_CASE(device_trust_supplier_retry_test),
+ KUNIT_CASE(device_trust_match_defer_authorize_test),
+ KUNIT_CASE(device_trust_async_policy_test),
+ KUNIT_CASE(device_trust_generic_adversary_bind_test),
+ KUNIT_CASE(device_trust_no_iommu_bind_test),
+ KUNIT_CASE(device_trust_explicit_policy_mismatch_test),
+ KUNIT_CASE(device_trust_resolver_failure_test),
+ KUNIT_CASE(device_trust_resolver_disabled_test),
+ KUNIT_CASE(device_trust_probe_failure_test),
+ KUNIT_CASE(device_trust_probe_defer_test),
+ KUNIT_CASE(device_trust_dma_failure_test),
+ KUNIT_CASE(device_trust_force_bind_guard_test),
+ KUNIT_CASE(device_trust_legacy_force_bind_test),
+ KUNIT_CASE(device_trust_busless_force_bind_test),
+ KUNIT_CASE(device_trust_cmdline_policies_test),
+ KUNIT_CASE(device_trust_cmdline_exact_match_test),
+ KUNIT_CASE(device_trust_cmdline_last_match_test),
+ KUNIT_CASE(device_trust_cmdline_nameless_test),
+ KUNIT_CASE(device_trust_cmdline_validate_test),
+ KUNIT_CASE(device_trust_cmdline_invalid_test),
+ KUNIT_CASE(device_trust_boot_precedence_test),
+ KUNIT_CASE(device_trust_global_busless_test),
+ KUNIT_CASE(device_trust_driver_precedence_test),
+ KUNIT_CASE(device_trust_claim_allowed_test),
+ KUNIT_CASE(device_trust_driver_matching_test),
+ KUNIT_CASE(device_trust_driver_user_precedence_test),
+ KUNIT_CASE(device_trust_driver_rejected_write_test),
+ KUNIT_CASE(device_trust_driver_authorize_test),
+ KUNIT_CASE(device_trust_driver_resolver_failure_test),
+ KUNIT_CASE(device_trust_claimed_policy_test),
+ KUNIT_CASE(device_trust_try_attach_test),
+ KUNIT_CASE(device_trust_scan_continues_test),
+ {}
+};
+
+/* The fixtures set their own policy, which a live boot default would replace. */
+static int device_trust_test_init(struct kunit *test)
+{
+ struct device_trust_test_stub *stub = device_trust_test_stub_alloc(test);
+ struct device *dev = device_trust_test_pci_stub(stub, "device-trust-test",
+ DEVICE_TRUST_POLICY_DEFAULT);
+
+ device_trust_init(dev);
+ if (dev->trust_policy != DEVICE_TRUST_POLICY_DEFAULT)
+ kunit_skip(test, "device_trust.default= replaces the fixture policy");
+
+ return 0;
+}
+
+static int device_trust_test_suite_init(struct kunit_suite *suite)
+{
+ int ret;
+
+ ret = bus_register(&device_trust_test_plain_bus);
+ if (ret)
+ return ret;
+ device_trust_test_plain_bus_registered = true;
+
+ ret = bus_register(&device_trust_test_resolving_bus);
+ if (ret) {
+ device_trust_test_plain_bus_registered = false;
+ bus_unregister(&device_trust_test_plain_bus);
+ return ret;
+ }
+ device_trust_test_resolving_bus_registered = true;
+
+ return 0;
+}
+
+static void device_trust_test_suite_exit(struct kunit_suite *suite)
+{
+ if (device_trust_test_resolving_bus_registered) {
+ device_trust_test_resolving_bus_registered = false;
+ bus_unregister(&device_trust_test_resolving_bus);
+ }
+ if (device_trust_test_plain_bus_registered) {
+ device_trust_test_plain_bus_registered = false;
+ bus_unregister(&device_trust_test_plain_bus);
+ }
+}
+
+static struct kunit_suite device_trust_test_suite = {
+ .name = "device-trust",
+ .init = device_trust_test_init,
+ .suite_init = device_trust_test_suite_init,
+ .suite_exit = device_trust_test_suite_exit,
+ .test_cases = device_trust_test_cases,
+};
+
+kunit_test_suite(device_trust_test_suite);
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_DESCRIPTION("KUnit tests for device trust");
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index c2463b51c3a03..b68644d197b6f 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -23,6 +23,7 @@ void device_trust_init_state(struct device *dev)
DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
dev->p->trust_initialized = true;
}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_init_state);
static bool device_trust_policy_matches(enum device_trust_policy policy,
enum device_trust_level level)
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 16/17] driver core: Document trust admission and initrd authorization
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (14 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 15/17] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 11:42 ` [PATCH 17/17] PCI: Replace untrusted state with device trust Leon Romanovsky
16 siblings, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Policy agents and bus drivers need a shared definition of authorization,
commitment and active trust across boot and device lifetime.
Document the admission state machine and disabled-to-authorized initrd
sequence, including built-in probes and pending work. Explain DMA domain
preparation, binding restrictions, device replacement and current userspace
integration requirements. Distinguish attestation and containment from the
permission to bind a functional driver.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
.../driver-api/driver-model/device-trust.rst | 294 +++++++++++++++++++++
Documentation/driver-api/driver-model/index.rst | 1 +
2 files changed, 295 insertions(+)
diff --git a/Documentation/driver-api/driver-model/device-trust.rst b/Documentation/driver-api/driver-model/device-trust.rst
new file mode 100644
index 0000000000000..1013784daeaca
--- /dev/null
+++ b/Documentation/driver-api/driver-model/device-trust.rst
@@ -0,0 +1,294 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Device trust
+============
+
+Device trust controls how the kernel uses a device. It does not verify the
+device's identity or firmware. Driver core tracks the requested policy,
+permission to bind, and active trust level separately.
+
+Policy and active trust
+=======================
+
+The writable ``trust_policy`` attribute accepts ``default``, ``disabled``,
+``adversary``, or ``full``. The bus may set the initial policy; otherwise it
+starts as ``default``. Boot options can override it for all devices, a
+named driver, or an individual device. See `Boot policy`_ for precedence.
+
+The read-only ``trust`` attribute reports the active trust level:
+
+``DEVICE_TRUST_DISABLED`` (``disabled``)
+ There is no active driver setup or binding. The requested policy decides
+ whether a later bind is allowed.
+
+``DEVICE_TRUST_ADVERSARY`` (``adversary``)
+ The kernel treats the device as potentially malicious. The bus and DMA
+ layers must enforce the restrictions needed to operate it.
+
+``DEVICE_TRUST_FULL`` (``full``)
+ The device uses the normal privileges allowed by the bus and DMA policy.
+
+Policy resolution
+=================
+
+A bus can implement ``bus_type::trust_resolve`` to choose the active level
+from the device, candidate driver, and requested policy. For ``default``,
+it may use topology or firmware information. Other policies must resolve
+to the matching level or fail.
+
+The callback must not enable the device or grant DMA access. Those steps
+belong in setup callbacks, after resolution and before probe.
+
+Without this callback, ``default`` and ``full`` resolve to
+``DEVICE_TRUST_FULL``, and ``adversary`` to ``DEVICE_TRUST_ADVERSARY``.
+Explicit ``disabled`` blocks binding. The bus remains responsible for
+enforcing the chosen level's restrictions.
+
+Admission and policy writes
+===========================
+
+Authorization is userspace's choice of adversary or full for a disabled
+device. Commitment happens when the kernel accepts a policy and operating
+level, before starting driver setup. Both limit later policy changes.
+
+The states below are internal; they are not additional sysfs values.
+``P`` is the requested policy and ``L`` is the committed operating level.
+
+.. list-table:: Admission states
+ :header-rows: 1
+ :widths: 25 40 35
+
+ * - State
+ - Meaning
+ - Policy writes
+ * - SELECTABLE
+ - The device has not been authorized or committed.
+ - Any policy is allowed. Selecting disabled enters WAITING.
+ * - WAITING
+ - Policy is disabled - no operating level is committed.
+ - Choose adversary or full to enter AUTHORIZED(P).
+ * - AUTHORIZED(P)
+ - Userspace has chosen adversary or full from WAITING.
+ - The policy cannot change, even before setup begins.
+ * - COMMITTED(P, L)
+ - The kernel has accepted P and L for setup.
+ - Neither the policy nor the operating level can change.
+
+Bus defaults and boot options set the initial state: disabled starts in
+WAITING; other policies start in SELECTABLE. Driver boot rules apply later,
+when a matching driver attempts to bind. They may still replace this
+initial state, including WAITING, unless a per-device boot entry or a
+successful policy write takes precedence.
+
+Userspace cannot move WAITING back to SELECTABLE by writing ``default``.
+Choosing adversary or full from WAITING locks that policy immediately.
+
+After boot initialization, the state transitions are::
+
+ SELECTABLE ----select disabled----> WAITING
+ | |
+ | authorize
+ | adversary/full
+ | |
+ | v
+ | AUTHORIZED(P)
+ | |
+ +------ permitted resolution -----+
+ |
+ v
+ COMMITTED(P, L)
+
+Binding and device lifetime
+============================
+
+A manual bind that reaches WAITING returns ``-EPERM`` before driver or DMA
+setup. Automatic matching skips the rejected driver and tries the next
+candidate. Repeated rejected attempts leave the policy writable for later
+authorization.
+
+Active trust stays at L through DMA setup, probe, operation, removal, and
+``bus_type::dma_cleanup``. It returns to disabled only after cleanup ends.
+
+The state lasts for the device object's lifetime, including the switch
+from initrd to the root filesystem. Unbinding and rebinding a registered
+device does not reset it. A removed device cannot be registered again; a
+new object starts with bus and boot policy.
+
+A driver can claim a USB interface before it is registered. The claim fails
+if the interface's own policy is disabled. A driver rule and other claims
+are admitted when the interface is registered, and a refusal then releases
+the claim. The policy of a claimed interface cannot be changed, and
+registration does not admit it while ``drivers_autoprobe`` is disabled.
+
+Boot policy
+===========
+
+Built-in drivers can probe before userspace starts. Use boot options to
+set their initial policy. All three options accept default, disabled,
+adversary, and full.
+
+``device_trust.default=`` sets policy for all devices. If omitted or set
+to default, it preserves bus policy. Other values replace bus policy,
+including topology and removability defaults. The last valid value wins;
+invalid values are reported and ignored.
+
+``device_trust=`` sets policy for individual devices::
+
+ device_trust=pci:0000:01:00.0=adversary,pci:0000:02:00.0=disabled
+
+Each entry is ``<bus>:<device>=<policy>``. Names must match
+``/sys/bus/<bus>/devices/<device>`` exactly. The first colon separates the
+bus name; later colons are part of the device name. Names cannot contain
+whitespace, control characters, commas, equals signs, or double quotes.
+Wildcards are not supported.
+
+A device entry overrides driver rules, the global setting, and bus policy,
+regardless of option order. Explicit default asks the bus or generic
+resolver to choose a level, even if the global setting is disabled. The
+last entry for a device wins. If the option is repeated, the last valid
+list replaces all earlier lists. A malformed list is ignored in full,
+with a warning.
+
+``device_trust.driver=`` sets policy by driver name::
+
+ device_trust.driver=pci:e1000=disabled,pci:nvme=full
+
+Each entry is ``<bus>:<driver>=<policy>``. Use exact names from
+``/sys/bus/<bus>/drivers/<driver>``, not module names or aliases. The name
+restrictions and list handling are the same as for device entries. The
+two lists are independent. Driver rules cover built-in drivers, modules,
+and hotplug.
+
+The first bind attempt with a matching driver rule applies it before
+resolution and setup. It overrides bus and global defaults, including
+global disabled. It cannot override a per-device entry, any successful
+sysfs policy write, or an existing authorization or commitment. It applies
+only once per device object, even if resolution or setup fails.
+
+After a disabled driver rule applies, rejected probes and driver changes
+leave the device in WAITING until userspace authorizes it. Before any rule
+applies, a driver with no matching rule may still bind. Use global or
+per-device disabled to block every driver.
+
+For example, an initrd can hold all devices except one needed at boot::
+
+ device_trust.default=disabled device_trust=pci:0000:00:01.0=full
+
+Include exceptions for every device and supplier needed to reach the
+initrd and perform attestation. These exceptions allow operation before
+fresh initrd attestation. Unlisted devices stay disabled even if their
+bus would normally choose adversary or full.
+
+Global and per-device rules apply before device registration. Buses that
+need policy earlier call ``device_trust_init()`` after setting the bus,
+name, and initial policy. PCI does this during discovery, before header
+fixups, ACS setup, and IOMMU configuration. PCI applies topology and
+removability defaults only to default policy. A child's explicit boot
+entry overrides inherited policy. Driver rules wait until a candidate
+driver is known and cannot change these earlier discovery choices.
+
+All boot rules remain available for newly discovered devices. Reapplying
+initialization to an already registered device does not reset its state.
+These options are not runtime controls or proof of hardware identity.
+Disabled blocks early binding and allows later authorization. Adversary
+and full permit early binding without fresh initrd attestation.
+
+DMA containment
+===============
+
+Disabled blocks driver binding, not DMA that hardware starts on its own.
+The bus and IOMMU must provide containment while a device waits for
+authorization.
+
+For PCI devices whose policy is disabled or adversary at discovery, the
+IOMMU core selects a strict translated default domain for the whole group.
+AMD domain selection and Intel bypass checks apply the same restriction.
+This prepares a disabled device for later adversary authorization without
+granting permission to bind. Later full authorization may keep these
+restrictions. PCI does not record the ATS capability of such a device, even
+after a later full authorization, and sets ACS Translation Blocking for it
+where the path supports it.
+
+Adversary does not require an IOMMU. Without an IOMMU group, or with IOMMU
+support compiled out, binding uses the platform's normal DMA configuration.
+Adversary policy alone does not provide DMA isolation.
+
+Binding does not check the IOMMU domain. Neither a driver rule nor a sysfs
+policy write reconfigures it, so a policy selected after discovery may
+bind with an identity or deferred-invalidation domain. Select the policy
+with ``device_trust=`` or ``device_trust.default=`` for it to apply at
+discovery. Outside PCI, discovery does not select a domain. Changing a
+group's domain type through sysfs is not checked against trust. A setup
+failure after commitment leaves the chosen mode fixed; it cannot fall back
+to full.
+
+Adversarial IOMMU mappings that are not aligned to IOMMU pages need an
+active SWIOTLB bounce-buffer pool. Low-memory x86 systems may need
+``swiotlb=force`` to keep this pool available. Without it, mappings can fail
+even after the driver binds.
+
+Drivers marked ``driver_managed_dma`` bypass the kernel DMA API's default
+domain handling and must provide their own DMA containment. Drivers that
+do not use DMA need no domain.
+
+Initrd attestation sequence
+===========================
+
+For a device that uses normal driver attachment, such as a PCI function:
+
+1. Boot with ``device_trust.default=disabled`` to hold all devices, or
+ ``device_trust=pci:0000:01:00.0=disabled`` to hold one. Add exceptions
+ needed to reach the initrd and provide DMA containment while waiting.
+ ``device_trust.driver=pci:e1000=disabled`` can instead hold a known
+ driver; global disabled also covers other drivers. Disabled attempts
+ remain in WAITING, including attempts by built-in drivers.
+2. In the initrd, control probing and attest the current device through a
+ transport that works without its functional driver. Keep policy disabled
+ during verification and preparation. Leave it disabled if verification
+ fails or is unavailable. Removal or replacement invalidates the result;
+ a reused bus address is not proof of identity. Repeat attestation if a
+ reset or preparation step invalidates the evidence.
+3. Once all checks and preparation are complete, write adversary or full
+ to ``trust_policy``. This locks the choice and allows binding.
+4. Request binding through ``drivers_probe`` or another attach path. The
+ kernel commits the operating level before setup. Retries keep the same
+ policy and level. An attachment deferred before the driver's probe
+ callback returns ``-EAGAIN``; retry later. A successful write does not
+ guarantee that a driver bound, so check the device's ``driver`` link.
+
+Queued or concurrent probes may start as soon as step 3 completes, before
+step 4. Evidence must still be valid when authorizing the device.
+Boot exceptions that permit adversary or full allow early operation
+without fresh initrd attestation.
+
+The kernel interface provides neither an attestation transport nor a verifier.
+
+Driver use and independent properties
+=====================================
+
+Drivers read active trust with ``device_get_trust_level()`` or
+``device_is_adversarial()``. They may cache it while bound, but must not
+change requested policy or treat trust as proof of other security
+properties. Use the adversary check for features such as peer-to-peer DMA,
+device memory, PASID/SVA, or paths that bypass the DMA API. General DMA
+containment belongs in bus and IOMMU code.
+
+Discovery code uses ``device_policy_is_adversarial()`` to check requested
+policy before binding or after unbind. This helper does not resolve
+default policy or report active trust.
+
+Code that needs the level fixed for the device's lifetime can use
+``device_get_committed_trust_level()``. It returns disabled before
+commitment and the fixed level afterwards, including after failed setup
+or unbind. It does not permit operation while unbound.
+
+Trust does not describe transport protection, TDISP T=1/RUN, link
+encryption, private MMIO, verified evidence, or access to private versus
+shared memory. T=1 can use either adversary or full. In particular,
+``force_dma_unencrypted()`` describes DMA addressability and must not be
+derived from trust.
+
+Policy writes cannot revoke authorization or switch an operating device
+between adversary and full. Stopping it requires a separate procedure to
+quiesce the driver, tear down DMA, and contain the device on its bus.
diff --git a/Documentation/driver-api/driver-model/index.rst b/Documentation/driver-api/driver-model/index.rst
index abeb4b36636b6..af547cd13b2c4 100644
--- a/Documentation/driver-api/driver-model/index.rst
+++ b/Documentation/driver-api/driver-model/index.rst
@@ -9,6 +9,7 @@ Driver Model
bus
design-patterns
device
+ device-trust
devres
driver
overview
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
` (15 preceding siblings ...)
2026-11-05 11:42 ` [PATCH 16/17] driver core: Document trust admission and initrd authorization Leon Romanovsky
@ 2026-11-05 11:42 ` Leon Romanovsky
2026-11-05 13:12 ` Greg Kroah-Hartman
16 siblings, 1 reply; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 11:42 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren
Cc: linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
From: Leon Romanovsky <leonro@nvidia.com>
Convert the PCI specific untrusted to use the common device trust
level. Adversarial means the same thing untrusted does.
PCI discovery keeps the logic that set untrusted: it looks for removable
PCI devices with arch_pci_dev_is_removable() and defaults them to an
adversarial policy. Treat a disabled policy the same way when choosing
the IOMMU domain. Do not record the ATS capability of such a device, as
pci_ats_supported() rejected untrusted ones. Let the Intel write-buffer
quirk apply, as it only adds flushing.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/iommu/amd/iommu.c | 3 +--
drivers/iommu/dma-iommu.c | 14 +++++---------
drivers/iommu/intel/iommu.c | 17 +++++------------
drivers/iommu/iommu.c | 17 +++++++++--------
drivers/pci/ats.c | 4 ++--
drivers/pci/pci.c | 5 +++--
drivers/pci/probe.c | 25 ++++++++++++++-----------
drivers/pci/quirks.c | 7 ++++---
include/linux/device.h | 5 +++++
include/linux/pci.h | 30 ++++++++++++++++++++++--------
10 files changed, 70 insertions(+), 57 deletions(-)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 960e1dc05ec69..e358bbb1f2baa 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3401,8 +3401,7 @@ static int amd_iommu_def_domain_type(struct device *dev)
if (!dev_is_pci(dev))
return 0;
- /* Always use DMA domain for untrusted device */
- if (to_pci_dev(dev)->untrusted)
+ if (pci_dev_default_is_restricted(to_pci_dev(dev)))
return IOMMU_DOMAIN_DMA;
/* Apply device specific quirks */
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 78742aeba932d..a48fb511bfe6f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -589,16 +589,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_is_adversarial(dev) ||
dma_kmalloc_needs_bounce(dev, size, dir));
}
@@ -611,7 +606,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_is_adversarial(dev))
return true;
/*
@@ -1189,7 +1184,8 @@ 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_is_adversarial(dev)) {
size_t start, virt = (size_t)phys_to_virt(phys);
/* Pre-padding */
@@ -1790,7 +1786,7 @@ size_t iommu_dma_max_opt_mapping_size(void)
size_t iommu_dma_max_mapping_size(struct device *dev)
{
- if (dev_is_untrusted(dev))
+ if (device_is_adversarial(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 5a2e705a2f2e0..66942a91b992f 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2624,8 +2624,8 @@ static void __init platform_optin_force_iommu(void)
return;
/*
- * If Intel-IOMMU is disabled by default, we will apply identity
- * map for all devices except those marked as being untrusted.
+ * If Intel-IOMMU is disabled by default, an external device could
+ * otherwise receive an identity map despite its adversarial policy.
*/
if (dmar_policy_off()) {
pr_info("Intel-IOMMU force enabled due to platform opt in\n");
@@ -3629,16 +3629,12 @@ static bool intel_iommu_is_attach_deferred(struct device *dev)
return translation_pre_enabled(info->iommu) && !info->domain;
}
-/*
- * Check that the device does not live on an external facing PCI port that is
- * marked as untrusted. Such devices should not be able to apply quirks and
- * thus not be able to bypass the IOMMU restrictions.
- */
+/* Bypass quirks must not prevent later adversarial operation. */
static bool risky_device(struct pci_dev *pdev)
{
- if (pdev->untrusted) {
+ if (pci_dev_default_is_restricted(pdev)) {
pci_info(pdev,
- "Skipping IOMMU quirk for dev [%04X:%04X] on untrusted PCI link\n",
+ "Skipping IOMMU quirk due to trust policy [%04X:%04X]\n",
pdev->vendor, pdev->device);
pci_info(pdev, "Please check with your BIOS/Platform vendor about this\n");
return true;
@@ -4124,9 +4120,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
static void quirk_iommu_rwbf(struct pci_dev *dev)
{
- if (risky_device(dev))
- return;
-
/*
* Mobile 4 Series Chipset neglects to set RWBF capability,
* but needs it. Same seems to hold for the desktop versions.
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b486b8bbd1fcd..8be2a246d4903 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1879,7 +1879,7 @@ static int iommu_get_def_domain_type(struct iommu_group *group,
static int iommu_get_default_domain_type(struct iommu_group *group,
int target_type)
{
- struct device *untrusted = NULL;
+ struct device *restricted = NULL;
struct group_device *gdev;
int driver_type = 0;
@@ -1901,14 +1901,15 @@ 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 (dev_is_pci(gdev->dev) &&
+ pci_dev_default_is_restricted(to_pci_dev(gdev->dev))) {
/*
- * No ARM32 using systems will set untrusted, it cannot
- * work.
+ * No ARM32 using systems select a restricted trust
+ * policy; it cannot work.
*/
if (WARN_ON(IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)))
return -1;
- untrusted = gdev->dev;
+ restricted = gdev->dev;
}
}
@@ -1924,11 +1925,11 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
driver_type = IOMMU_DOMAIN_IDENTITY;
}
- if (untrusted) {
+ if (restricted) {
if (driver_type && driver_type != IOMMU_DOMAIN_DMA) {
dev_err_ratelimited(
- untrusted,
- "Device is not trusted, but driver is overriding group %u to %s, refusing to probe.\n",
+ restricted,
+ "Device trust policy restricts DMA, but driver is overriding group %u to %s, refusing to probe.\n",
group->id, iommu_domain_type_str(driver_type));
return -1;
}
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index 9cb23780093dd..c8c96acc26343 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -21,7 +21,7 @@ void pci_ats_init(struct pci_dev *dev)
{
int pos;
- if (pci_ats_disabled())
+ if (pci_ats_disabled() || pci_dev_default_is_restricted(dev))
return;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
@@ -40,7 +40,7 @@ void pci_ats_init(struct pci_dev *dev)
*/
bool pci_ats_supported(struct pci_dev *dev)
{
- if (!dev->ats_cap || dev->untrusted)
+ if (!dev->ats_cap)
return false;
if (dev->is_virtfn)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7c6f2aba81443..b08adba03f96b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1016,8 +1016,9 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
/* Upstream Forwarding */
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)
+ /* Enable Translation Blocking when ATS is unavailable or restricted. */
+ if (pci_ats_disabled() || dev->external_facing ||
+ !pci_dev_ats_permitted(dev))
caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 5b6960bbb35bc..139e6f7b4e022 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1738,24 +1738,27 @@ static void set_pcie_cxl(struct pci_dev *dev)
}
-static void set_pcie_untrusted(struct pci_dev *dev)
+static void pci_set_default_trust_policy(struct pci_dev *dev)
{
- struct pci_dev *parent = pci_upstream_bridge(dev);
+ struct pci_dev *parent = pci_physfn(dev);
+
+ if (dev->dev.trust_policy != DEVICE_TRUST_POLICY_DEFAULT)
+ return;
+
+ if (parent == dev)
+ parent = pci_upstream_bridge(dev);
if (!parent)
return;
- /*
- * If the upstream bridge is untrusted we treat this device as
- * untrusted as well.
- */
- if (parent->untrusted) {
- dev->untrusted = true;
+
+ if (device_policy_is_adversarial(&parent->dev)) {
+ dev->dev.trust_policy = DEVICE_TRUST_POLICY_ADVERSARY;
return;
}
if (arch_pci_dev_is_removable(dev)) {
- pci_dbg(dev, "marking as untrusted\n");
- dev->untrusted = true;
+ pci_dbg(dev, "setting adversarial trust policy\n");
+ dev->dev.trust_policy = DEVICE_TRUST_POLICY_ADVERSARY;
}
}
@@ -2082,7 +2085,7 @@ int pci_setup_device(struct pci_dev *dev)
set_pcie_cxl(dev);
- set_pcie_untrusted(dev);
+ pci_set_default_trust_policy(dev);
device_trust_init(&dev->dev);
/* "Unknown power state" */
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 7f1f47ea2ee41..9ed3e7660bab2 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -5525,8 +5525,8 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
* Currently this quirk does the equivalent of
* 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
+ * TODO: This quirk also needs to do the equivalent of PCI_ACS_TB when the
+ * device is external-facing or ATS is restricted by trust policy.
*/
static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
{
@@ -5567,7 +5567,8 @@ 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_dev_ats_permitted(dev))
ctrl |= (cap & PCI_ACS_TB);
pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl);
diff --git a/include/linux/device.h b/include/linux/device.h
index 65ea07c9ac35b..870ce14924905 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -934,6 +934,11 @@ static inline bool device_iommu_mapped(struct device *dev)
return (dev->iommu_group != NULL);
}
+static inline bool device_policy_is_adversarial(const struct device *dev)
+{
+ return READ_ONCE(dev->trust_policy) == DEVICE_TRUST_POLICY_ADVERSARY;
+}
+
/* Get the wakeup routines, which depend on struct device */
#include <linux/pm_wakeup.h>
diff --git a/include/linux/pci.h b/include/linux/pci.h
index aa3765a20a96c..087c63b177c85 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -490,14 +490,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
@@ -616,6 +608,28 @@ static inline struct pci_dev *to_pci_dev(const struct device *dev)
{
return container_of(dev, struct pci_dev, dev);
}
+
+static inline bool
+pci_dev_default_is_restricted(const struct pci_dev *pdev)
+{
+ enum device_trust_policy policy = READ_ONCE(pdev->dev.trust_policy);
+
+ return policy == DEVICE_TRUST_POLICY_DISABLED ||
+ policy == DEVICE_TRUST_POLICY_ADVERSARY;
+}
+
+static inline bool pci_dev_ats_permitted(const struct pci_dev *pdev)
+{
+ enum device_trust_level level = device_get_trust_level(&pdev->dev);
+ enum device_trust_policy policy = READ_ONCE(pdev->dev.trust_policy);
+
+ if (level == DEVICE_TRUST_DISABLED)
+ return policy != DEVICE_TRUST_POLICY_ADVERSARY &&
+ policy != DEVICE_TRUST_POLICY_DISABLED;
+
+ return level == DEVICE_TRUST_FULL;
+}
+
#define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
#define for_each_pci_dev_reverse(d) \
while ((d = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
--
2.56.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 11:42 ` [PATCH 10/17] driver core: Expose device trust authorization through sysfs Leon Romanovsky
@ 2026-11-05 13:10 ` Greg Kroah-Hartman
2026-11-05 13:27 ` Leon Romanovsky
2026-11-05 17:02 ` Jason Gunthorpe
1 sibling, 1 reply; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 13:10 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> An initrd must authorize a disabled device after attestation even if a
> built-in driver already attempted to probe it.
>
> Expose requested trust_policy and read-only active trust. Allow explicit
> disabled policy to advance once to adversary or full, fixing that decision
> immediately. Reject later policy changes, including for a device claimed
> before registration, while allowing same-value writes on live devices.
> Keep initial unrestricted selection separate from explicit authorization
> and bind-time commitment. Serialize writes with probing under the device
> lock. Include the attributes in device ownership changes.
>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> ---
> Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> MAINTAINERS | 1 +
> drivers/base/base.h | 6 ++
> drivers/base/core.c | 15 +++-
> drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> 5 files changed, 174 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> new file mode 100644
> index 0000000000000..97e07a174769c
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-devices-trust
> @@ -0,0 +1,51 @@
> +What: /sys/devices/.../trust
> +Date: September 2026
> +Contact: driver-core@lists.linux.dev
> +Description:
> + (RO) The active trust level for the device. The values are
> + "disabled", "adversary", and "full". Driver core resolves
> + the level before DMA configuration and probe, and retains it
> + through removal and DMA cleanup. Outside a bind transaction
> + the level is "disabled". This does not imply that policy is
> + writable or that the device has never operated.
> +
> +What: /sys/devices/.../trust_policy
> +Date: September 2026
> +Contact: driver-core@lists.linux.dev
> +Description:
> + (RW) The requested trust policy for the device. The initial
> + value may be selected by the bus before the device is
> + registered; otherwise it is "default". The policy is resolved
> + to an active trust level before the device is probed.
> +
> + The values are "default", "disabled", "adversary", and
> + "full". "default" lets the bus select the active trust level.
> + "disabled" prevents a functional driver from binding.
> + "adversary" requests operation with the restrictions that the
> + bus and DMA layers can enforce for a potentially malicious
> + device; it does not isolate DMA by itself. "full" requests
> + normal operation.
> +
> + Before an operating level is committed, policy may change
> + freely until "disabled" is selected. Explicit "disabled"
> + blocks binding while allowing a later write of "adversary" or
> + "full". Repeated rejected probes preserve this opportunity.
> + Authorization immediately fixes the requested policy.
> + Returning to "default" or "disabled", or selecting another
> + mode, fails.
> +
> + Successful resolution to an allowed level commits policy
> + and operating level before driver or DMA setup. Both remain
> + fixed for the device object's lifetime, including through
> + failed or deferred probes, unbind, reset, and driver reload.
> + Later binds must resolve to the same level, including when
> + the policy is "default". A resolver error or disabled result
> + before commitment leaves the policy's writability unchanged.
I understand you are aproaching this from a PCI point-of-view, but many
busses do not care or have DMA at all, and need to actually talk to the
device before being able to even "add" it to the kernel. So you are
making a driver core-specific policy based on mentioning DMA here, when
really that should only be a bus-specific thing, right? Some busses
might require DMA to happen (i.e. USB needs their host controllers to
use DMA to even talk to the device at all).
So can this be written to be more vague and generic and then put the
specifics in the bus-specific portion?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 11:42 ` [PATCH 17/17] PCI: Replace untrusted state with device trust Leon Romanovsky
@ 2026-11-05 13:12 ` Greg Kroah-Hartman
2026-11-05 13:18 ` Leon Romanovsky
0 siblings, 1 reply; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 13:12 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:42:30PM +0200, Leon Romanovsky wrote:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> Convert the PCI specific untrusted to use the common device trust
> level. Adversarial means the same thing untrusted does.
What about for USB? Will that "move" to use this common trust level?
If not, I don't think it's very "common" :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 13:12 ` Greg Kroah-Hartman
@ 2026-11-05 13:18 ` Leon Romanovsky
2026-11-05 13:26 ` Greg Kroah-Hartman
2026-11-05 14:03 ` Jason Gunthorpe
0 siblings, 2 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 13:18 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 08:12:51AM -0500, Greg Kroah-Hartman wrote:
> On Thu, Nov 05, 2026 at 01:42:30PM +0200, Leon Romanovsky wrote:
> > From: Leon Romanovsky <leonro@nvidia.com>
> >
> > Convert the PCI specific untrusted to use the common device trust
> > level. Adversarial means the same thing untrusted does.
>
> What about for USB? Will that "move" to use this common trust level?
> If not, I don't think it's very "common" :)
It supposed to work for all buses out-of-the box, at least this is the
intention.
Thanks
>
> thanks,
>
> greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 13:18 ` Leon Romanovsky
@ 2026-11-05 13:26 ` Greg Kroah-Hartman
2026-11-05 14:03 ` Jason Gunthorpe
1 sibling, 0 replies; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 13:26 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 03:18:56PM +0200, Leon Romanovsky wrote:
> On Thu, Nov 05, 2026 at 08:12:51AM -0500, Greg Kroah-Hartman wrote:
> > On Thu, Nov 05, 2026 at 01:42:30PM +0200, Leon Romanovsky wrote:
> > > From: Leon Romanovsky <leonro@nvidia.com>
> > >
> > > Convert the PCI specific untrusted to use the common device trust
> > > level. Adversarial means the same thing untrusted does.
> >
> > What about for USB? Will that "move" to use this common trust level?
> > If not, I don't think it's very "common" :)
>
> It supposed to work for all buses out-of-the box, at least this is the
> intention.
Great, did I miss that patch in this series? USB already has a "trust
model" so how is that being handled here?
Note, I just skimmed this, sorry, am at a conference right now, so odds
are I missed something...
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:10 ` Greg Kroah-Hartman
@ 2026-11-05 13:27 ` Leon Romanovsky
2026-11-05 13:40 ` Greg Kroah-Hartman
2026-11-05 13:42 ` Robin Murphy
0 siblings, 2 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 13:27 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
> On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> > From: Leon Romanovsky <leonro@nvidia.com>
> >
> > An initrd must authorize a disabled device after attestation even if a
> > built-in driver already attempted to probe it.
> >
> > Expose requested trust_policy and read-only active trust. Allow explicit
> > disabled policy to advance once to adversary or full, fixing that decision
> > immediately. Reject later policy changes, including for a device claimed
> > before registration, while allowing same-value writes on live devices.
> > Keep initial unrestricted selection separate from explicit authorization
> > and bind-time commitment. Serialize writes with probing under the device
> > lock. Include the attributes in device ownership changes.
> >
> > Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> > ---
> > Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> > MAINTAINERS | 1 +
> > drivers/base/base.h | 6 ++
> > drivers/base/core.c | 15 +++-
> > drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> > 5 files changed, 174 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> > new file mode 100644
> > index 0000000000000..97e07a174769c
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-devices-trust
> > @@ -0,0 +1,51 @@
> > +What: /sys/devices/.../trust
> > +Date: September 2026
> > +Contact: driver-core@lists.linux.dev
> > +Description:
> > + (RO) The active trust level for the device. The values are
> > + "disabled", "adversary", and "full". Driver core resolves
> > + the level before DMA configuration and probe, and retains it
> > + through removal and DMA cleanup. Outside a bind transaction
> > + the level is "disabled". This does not imply that policy is
> > + writable or that the device has never operated.
> > +
> > +What: /sys/devices/.../trust_policy
> > +Date: September 2026
> > +Contact: driver-core@lists.linux.dev
> > +Description:
> > + (RW) The requested trust policy for the device. The initial
> > + value may be selected by the bus before the device is
> > + registered; otherwise it is "default". The policy is resolved
> > + to an active trust level before the device is probed.
> > +
> > + The values are "default", "disabled", "adversary", and
> > + "full". "default" lets the bus select the active trust level.
> > + "disabled" prevents a functional driver from binding.
> > + "adversary" requests operation with the restrictions that the
> > + bus and DMA layers can enforce for a potentially malicious
> > + device; it does not isolate DMA by itself. "full" requests
> > + normal operation.
> > +
> > + Before an operating level is committed, policy may change
> > + freely until "disabled" is selected. Explicit "disabled"
> > + blocks binding while allowing a later write of "adversary" or
> > + "full". Repeated rejected probes preserve this opportunity.
> > + Authorization immediately fixes the requested policy.
> > + Returning to "default" or "disabled", or selecting another
> > + mode, fails.
> > +
> > + Successful resolution to an allowed level commits policy
> > + and operating level before driver or DMA setup. Both remain
> > + fixed for the device object's lifetime, including through
> > + failed or deferred probes, unbind, reset, and driver reload.
> > + Later binds must resolve to the same level, including when
> > + the policy is "default". A resolver error or disabled result
> > + before commitment leaves the policy's writability unchanged.
>
> I understand you are aproaching this from a PCI point-of-view, but many
> busses do not care or have DMA at all, and need to actually talk to the
> device before being able to even "add" it to the kernel. So you are
> making a driver core-specific policy based on mentioning DMA here, when
> really that should only be a bus-specific thing, right? Some busses
> might require DMA to happen (i.e. USB needs their host controllers to
> use DMA to even talk to the device at all).
>
> So can this be written to be more vague and generic and then put the
> specifics in the bus-specific portion?
Yes, of course. The main point is that drivers must not communicate with
or configure hardware before attestation is approved. DMA is the PCI
way to communicate with hardware.
Thanks
>
> thanks,
>
> greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:27 ` Leon Romanovsky
@ 2026-11-05 13:40 ` Greg Kroah-Hartman
2026-11-05 13:59 ` Robin Murphy
2026-11-05 13:42 ` Robin Murphy
1 sibling, 1 reply; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 13:40 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
> On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
> > On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> > > From: Leon Romanovsky <leonro@nvidia.com>
> > >
> > > An initrd must authorize a disabled device after attestation even if a
> > > built-in driver already attempted to probe it.
> > >
> > > Expose requested trust_policy and read-only active trust. Allow explicit
> > > disabled policy to advance once to adversary or full, fixing that decision
> > > immediately. Reject later policy changes, including for a device claimed
> > > before registration, while allowing same-value writes on live devices.
> > > Keep initial unrestricted selection separate from explicit authorization
> > > and bind-time commitment. Serialize writes with probing under the device
> > > lock. Include the attributes in device ownership changes.
> > >
> > > Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> > > ---
> > > Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> > > MAINTAINERS | 1 +
> > > drivers/base/base.h | 6 ++
> > > drivers/base/core.c | 15 +++-
> > > drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> > > 5 files changed, 174 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> > > new file mode 100644
> > > index 0000000000000..97e07a174769c
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-devices-trust
> > > @@ -0,0 +1,51 @@
> > > +What: /sys/devices/.../trust
> > > +Date: September 2026
> > > +Contact: driver-core@lists.linux.dev
> > > +Description:
> > > + (RO) The active trust level for the device. The values are
> > > + "disabled", "adversary", and "full". Driver core resolves
> > > + the level before DMA configuration and probe, and retains it
> > > + through removal and DMA cleanup. Outside a bind transaction
> > > + the level is "disabled". This does not imply that policy is
> > > + writable or that the device has never operated.
> > > +
> > > +What: /sys/devices/.../trust_policy
> > > +Date: September 2026
> > > +Contact: driver-core@lists.linux.dev
> > > +Description:
> > > + (RW) The requested trust policy for the device. The initial
> > > + value may be selected by the bus before the device is
> > > + registered; otherwise it is "default". The policy is resolved
> > > + to an active trust level before the device is probed.
> > > +
> > > + The values are "default", "disabled", "adversary", and
> > > + "full". "default" lets the bus select the active trust level.
> > > + "disabled" prevents a functional driver from binding.
> > > + "adversary" requests operation with the restrictions that the
> > > + bus and DMA layers can enforce for a potentially malicious
> > > + device; it does not isolate DMA by itself. "full" requests
> > > + normal operation.
> > > +
> > > + Before an operating level is committed, policy may change
> > > + freely until "disabled" is selected. Explicit "disabled"
> > > + blocks binding while allowing a later write of "adversary" or
> > > + "full". Repeated rejected probes preserve this opportunity.
> > > + Authorization immediately fixes the requested policy.
> > > + Returning to "default" or "disabled", or selecting another
> > > + mode, fails.
> > > +
> > > + Successful resolution to an allowed level commits policy
> > > + and operating level before driver or DMA setup. Both remain
> > > + fixed for the device object's lifetime, including through
> > > + failed or deferred probes, unbind, reset, and driver reload.
> > > + Later binds must resolve to the same level, including when
> > > + the policy is "default". A resolver error or disabled result
> > > + before commitment leaves the policy's writability unchanged.
> >
> > I understand you are aproaching this from a PCI point-of-view, but many
> > busses do not care or have DMA at all, and need to actually talk to the
> > device before being able to even "add" it to the kernel. So you are
> > making a driver core-specific policy based on mentioning DMA here, when
> > really that should only be a bus-specific thing, right? Some busses
> > might require DMA to happen (i.e. USB needs their host controllers to
> > use DMA to even talk to the device at all).
> >
> > So can this be written to be more vague and generic and then put the
> > specifics in the bus-specific portion?
>
> Yes, of course. The main point is that drivers must not communicate with
> or configure hardware before attestation is approved. DMA is the PCI
> way to communicate with hardware.
That's a very vague statement of "must not communicate with". Busses
HAVE to talk to the device to determine what the device even is before
it can add it to its internal bus list. That's just how the hardware
works, you can't say "no talking at all" otherwise that means that you
never even know if a device is there (and for some busses, you'll miss
the device entirely as they can only be found when the "host controller"
tells the kernel that a device showed up, not later by manually
scanning.)
Think about reading PCI device ids, or USB configuration information,
that's a bus-core thing. Are you really thinking that non of this can
be read from a device under this "policy"?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:27 ` Leon Romanovsky
2026-11-05 13:40 ` Greg Kroah-Hartman
@ 2026-11-05 13:42 ` Robin Murphy
1 sibling, 0 replies; 42+ messages in thread
From: Robin Murphy @ 2026-11-05 13:42 UTC (permalink / raw)
To: Leon Romanovsky, Greg Kroah-Hartman
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, David Woodhouse,
Lu Baolu, Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu, Jason Gunthorpe
On 2026-11-05 1:27 pm, Leon Romanovsky wrote:
> On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
>> On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
>>> From: Leon Romanovsky <leonro@nvidia.com>
>>>
>>> An initrd must authorize a disabled device after attestation even if a
>>> built-in driver already attempted to probe it.
>>>
>>> Expose requested trust_policy and read-only active trust. Allow explicit
>>> disabled policy to advance once to adversary or full, fixing that decision
>>> immediately. Reject later policy changes, including for a device claimed
>>> before registration, while allowing same-value writes on live devices.
>>> Keep initial unrestricted selection separate from explicit authorization
>>> and bind-time commitment. Serialize writes with probing under the device
>>> lock. Include the attributes in device ownership changes.
>>>
>>> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
>>> ---
>>> Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
>>> MAINTAINERS | 1 +
>>> drivers/base/base.h | 6 ++
>>> drivers/base/core.c | 15 +++-
>>> drivers/base/trust.c | 102 ++++++++++++++++++++++++++
>>> 5 files changed, 174 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
>>> new file mode 100644
>>> index 0000000000000..97e07a174769c
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-devices-trust
>>> @@ -0,0 +1,51 @@
>>> +What: /sys/devices/.../trust
>>> +Date: September 2026
>>> +Contact: driver-core@lists.linux.dev
>>> +Description:
>>> + (RO) The active trust level for the device. The values are
>>> + "disabled", "adversary", and "full". Driver core resolves
>>> + the level before DMA configuration and probe, and retains it
>>> + through removal and DMA cleanup. Outside a bind transaction
>>> + the level is "disabled". This does not imply that policy is
>>> + writable or that the device has never operated.
>>> +
>>> +What: /sys/devices/.../trust_policy
>>> +Date: September 2026
>>> +Contact: driver-core@lists.linux.dev
>>> +Description:
>>> + (RW) The requested trust policy for the device. The initial
>>> + value may be selected by the bus before the device is
>>> + registered; otherwise it is "default". The policy is resolved
>>> + to an active trust level before the device is probed.
>>> +
>>> + The values are "default", "disabled", "adversary", and
>>> + "full". "default" lets the bus select the active trust level.
>>> + "disabled" prevents a functional driver from binding.
>>> + "adversary" requests operation with the restrictions that the
>>> + bus and DMA layers can enforce for a potentially malicious
>>> + device; it does not isolate DMA by itself. "full" requests
>>> + normal operation.
>>> +
>>> + Before an operating level is committed, policy may change
>>> + freely until "disabled" is selected. Explicit "disabled"
>>> + blocks binding while allowing a later write of "adversary" or
>>> + "full". Repeated rejected probes preserve this opportunity.
>>> + Authorization immediately fixes the requested policy.
>>> + Returning to "default" or "disabled", or selecting another
>>> + mode, fails.
>>> +
>>> + Successful resolution to an allowed level commits policy
>>> + and operating level before driver or DMA setup. Both remain
>>> + fixed for the device object's lifetime, including through
>>> + failed or deferred probes, unbind, reset, and driver reload.
>>> + Later binds must resolve to the same level, including when
>>> + the policy is "default". A resolver error or disabled result
>>> + before commitment leaves the policy's writability unchanged.
>>
>> I understand you are aproaching this from a PCI point-of-view, but many
>> busses do not care or have DMA at all, and need to actually talk to the
>> device before being able to even "add" it to the kernel. So you are
>> making a driver core-specific policy based on mentioning DMA here, when
>> really that should only be a bus-specific thing, right? Some busses
>> might require DMA to happen (i.e. USB needs their host controllers to
>> use DMA to even talk to the device at all).
>>
>> So can this be written to be more vague and generic and then put the
>> specifics in the bus-specific portion?
>
> Yes, of course. The main point is that drivers must not communicate with
> or configure hardware before attestation is approved. DMA is the PCI
> way to communicate with hardware.
Indeed the principle of "I trust that this device is what it claims to
be" should be universal, and that's really the thing we should represent
here, it's just that most existing buses (especially simpler lower-level
ones like I2C/SPI/etc.) are unlikely to ever have any kind of mechanism
to be able to usefully attest that.
DMA is simply the biggest and most prominent attack surface for
malicious devices, as it gives them the means to attempt to access
arbitrary addresses; however even a non-DMA-capable device is still
potentially capable of maliciously doing the "wrong" thing with data
that a kernel driver explicitly gives it - imagine something pretending
to be a USB network adapter that's actually clever enough to MITM your
data at the packet level...
Thanks,
Robin.
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:40 ` Greg Kroah-Hartman
@ 2026-11-05 13:59 ` Robin Murphy
2026-11-05 14:03 ` Greg Kroah-Hartman
` (2 more replies)
0 siblings, 3 replies; 42+ messages in thread
From: Robin Murphy @ 2026-11-05 13:59 UTC (permalink / raw)
To: Greg Kroah-Hartman, Leon Romanovsky
Cc: Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, David Woodhouse,
Lu Baolu, Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu, Jason Gunthorpe
On 2026-11-05 1:40 pm, Greg Kroah-Hartman wrote:
> On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
>> On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
>>> On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
>>>> From: Leon Romanovsky <leonro@nvidia.com>
>>>>
>>>> An initrd must authorize a disabled device after attestation even if a
>>>> built-in driver already attempted to probe it.
>>>>
>>>> Expose requested trust_policy and read-only active trust. Allow explicit
>>>> disabled policy to advance once to adversary or full, fixing that decision
>>>> immediately. Reject later policy changes, including for a device claimed
>>>> before registration, while allowing same-value writes on live devices.
>>>> Keep initial unrestricted selection separate from explicit authorization
>>>> and bind-time commitment. Serialize writes with probing under the device
>>>> lock. Include the attributes in device ownership changes.
>>>>
>>>> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
>>>> ---
>>>> Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
>>>> MAINTAINERS | 1 +
>>>> drivers/base/base.h | 6 ++
>>>> drivers/base/core.c | 15 +++-
>>>> drivers/base/trust.c | 102 ++++++++++++++++++++++++++
>>>> 5 files changed, 174 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
>>>> new file mode 100644
>>>> index 0000000000000..97e07a174769c
>>>> --- /dev/null
>>>> +++ b/Documentation/ABI/testing/sysfs-devices-trust
>>>> @@ -0,0 +1,51 @@
>>>> +What: /sys/devices/.../trust
>>>> +Date: September 2026
>>>> +Contact: driver-core@lists.linux.dev
>>>> +Description:
>>>> + (RO) The active trust level for the device. The values are
>>>> + "disabled", "adversary", and "full". Driver core resolves
>>>> + the level before DMA configuration and probe, and retains it
>>>> + through removal and DMA cleanup. Outside a bind transaction
>>>> + the level is "disabled". This does not imply that policy is
>>>> + writable or that the device has never operated.
>>>> +
>>>> +What: /sys/devices/.../trust_policy
>>>> +Date: September 2026
>>>> +Contact: driver-core@lists.linux.dev
>>>> +Description:
>>>> + (RW) The requested trust policy for the device. The initial
>>>> + value may be selected by the bus before the device is
>>>> + registered; otherwise it is "default". The policy is resolved
>>>> + to an active trust level before the device is probed.
>>>> +
>>>> + The values are "default", "disabled", "adversary", and
>>>> + "full". "default" lets the bus select the active trust level.
>>>> + "disabled" prevents a functional driver from binding.
>>>> + "adversary" requests operation with the restrictions that the
>>>> + bus and DMA layers can enforce for a potentially malicious
>>>> + device; it does not isolate DMA by itself. "full" requests
>>>> + normal operation.
>>>> +
>>>> + Before an operating level is committed, policy may change
>>>> + freely until "disabled" is selected. Explicit "disabled"
>>>> + blocks binding while allowing a later write of "adversary" or
>>>> + "full". Repeated rejected probes preserve this opportunity.
>>>> + Authorization immediately fixes the requested policy.
>>>> + Returning to "default" or "disabled", or selecting another
>>>> + mode, fails.
>>>> +
>>>> + Successful resolution to an allowed level commits policy
>>>> + and operating level before driver or DMA setup. Both remain
>>>> + fixed for the device object's lifetime, including through
>>>> + failed or deferred probes, unbind, reset, and driver reload.
>>>> + Later binds must resolve to the same level, including when
>>>> + the policy is "default". A resolver error or disabled result
>>>> + before commitment leaves the policy's writability unchanged.
>>>
>>> I understand you are aproaching this from a PCI point-of-view, but many
>>> busses do not care or have DMA at all, and need to actually talk to the
>>> device before being able to even "add" it to the kernel. So you are
>>> making a driver core-specific policy based on mentioning DMA here, when
>>> really that should only be a bus-specific thing, right? Some busses
>>> might require DMA to happen (i.e. USB needs their host controllers to
>>> use DMA to even talk to the device at all).
>>>
>>> So can this be written to be more vague and generic and then put the
>>> specifics in the bus-specific portion?
>>
>> Yes, of course. The main point is that drivers must not communicate with
>> or configure hardware before attestation is approved. DMA is the PCI
>> way to communicate with hardware.
>
> That's a very vague statement of "must not communicate with". Busses
> HAVE to talk to the device to determine what the device even is before
> it can add it to its internal bus list. That's just how the hardware
> works, you can't say "no talking at all" otherwise that means that you
> never even know if a device is there (and for some busses, you'll miss
> the device entirely as they can only be found when the "host controller"
> tells the kernel that a device showed up, not later by manually
> scanning.)
>
> Think about reading PCI device ids, or USB configuration information,
> that's a bus-core thing. Are you really thinking that non of this can
> be read from a device under this "policy"?
Yup, and that's true even in the PCI TDISP case - there's a major amount
of "communication" and "configuration" involved in that. The principle
which should (hopefully) generalise is that a bus/host controller driver
can always interrogate its child device to perform whatever attestation
and general configuration is appropriate, but we won't hand off any
_control_ of that device to a specific device driver (nor enable
potentially-impactful bus-level features like DMA, interrupts, etc. if
relevant) without the requisite agreement between the bus code and the user.
Thanks,
Robin.
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:59 ` Robin Murphy
@ 2026-11-05 14:03 ` Greg Kroah-Hartman
2026-11-05 14:17 ` Leon Romanovsky
2026-11-05 14:26 ` Robin Murphy
2026-11-05 14:06 ` Jason Gunthorpe
2026-11-05 14:43 ` Andrew Lunn
2 siblings, 2 replies; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 14:03 UTC (permalink / raw)
To: Robin Murphy
Cc: Leon Romanovsky, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:59:20PM +0000, Robin Murphy wrote:
> On 2026-11-05 1:40 pm, Greg Kroah-Hartman wrote:
> > On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
> > > On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
> > > > On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> > > > > From: Leon Romanovsky <leonro@nvidia.com>
> > > > >
> > > > > An initrd must authorize a disabled device after attestation even if a
> > > > > built-in driver already attempted to probe it.
> > > > >
> > > > > Expose requested trust_policy and read-only active trust. Allow explicit
> > > > > disabled policy to advance once to adversary or full, fixing that decision
> > > > > immediately. Reject later policy changes, including for a device claimed
> > > > > before registration, while allowing same-value writes on live devices.
> > > > > Keep initial unrestricted selection separate from explicit authorization
> > > > > and bind-time commitment. Serialize writes with probing under the device
> > > > > lock. Include the attributes in device ownership changes.
> > > > >
> > > > > Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> > > > > ---
> > > > > Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> > > > > MAINTAINERS | 1 +
> > > > > drivers/base/base.h | 6 ++
> > > > > drivers/base/core.c | 15 +++-
> > > > > drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> > > > > 5 files changed, 174 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > new file mode 100644
> > > > > index 0000000000000..97e07a174769c
> > > > > --- /dev/null
> > > > > +++ b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > @@ -0,0 +1,51 @@
> > > > > +What: /sys/devices/.../trust
> > > > > +Date: September 2026
> > > > > +Contact: driver-core@lists.linux.dev
> > > > > +Description:
> > > > > + (RO) The active trust level for the device. The values are
> > > > > + "disabled", "adversary", and "full". Driver core resolves
> > > > > + the level before DMA configuration and probe, and retains it
> > > > > + through removal and DMA cleanup. Outside a bind transaction
> > > > > + the level is "disabled". This does not imply that policy is
> > > > > + writable or that the device has never operated.
> > > > > +
> > > > > +What: /sys/devices/.../trust_policy
> > > > > +Date: September 2026
> > > > > +Contact: driver-core@lists.linux.dev
> > > > > +Description:
> > > > > + (RW) The requested trust policy for the device. The initial
> > > > > + value may be selected by the bus before the device is
> > > > > + registered; otherwise it is "default". The policy is resolved
> > > > > + to an active trust level before the device is probed.
> > > > > +
> > > > > + The values are "default", "disabled", "adversary", and
> > > > > + "full". "default" lets the bus select the active trust level.
> > > > > + "disabled" prevents a functional driver from binding.
> > > > > + "adversary" requests operation with the restrictions that the
> > > > > + bus and DMA layers can enforce for a potentially malicious
> > > > > + device; it does not isolate DMA by itself. "full" requests
> > > > > + normal operation.
> > > > > +
> > > > > + Before an operating level is committed, policy may change
> > > > > + freely until "disabled" is selected. Explicit "disabled"
> > > > > + blocks binding while allowing a later write of "adversary" or
> > > > > + "full". Repeated rejected probes preserve this opportunity.
> > > > > + Authorization immediately fixes the requested policy.
> > > > > + Returning to "default" or "disabled", or selecting another
> > > > > + mode, fails.
> > > > > +
> > > > > + Successful resolution to an allowed level commits policy
> > > > > + and operating level before driver or DMA setup. Both remain
> > > > > + fixed for the device object's lifetime, including through
> > > > > + failed or deferred probes, unbind, reset, and driver reload.
> > > > > + Later binds must resolve to the same level, including when
> > > > > + the policy is "default". A resolver error or disabled result
> > > > > + before commitment leaves the policy's writability unchanged.
> > > >
> > > > I understand you are aproaching this from a PCI point-of-view, but many
> > > > busses do not care or have DMA at all, and need to actually talk to the
> > > > device before being able to even "add" it to the kernel. So you are
> > > > making a driver core-specific policy based on mentioning DMA here, when
> > > > really that should only be a bus-specific thing, right? Some busses
> > > > might require DMA to happen (i.e. USB needs their host controllers to
> > > > use DMA to even talk to the device at all).
> > > >
> > > > So can this be written to be more vague and generic and then put the
> > > > specifics in the bus-specific portion?
> > >
> > > Yes, of course. The main point is that drivers must not communicate with
> > > or configure hardware before attestation is approved. DMA is the PCI
> > > way to communicate with hardware.
> >
> > That's a very vague statement of "must not communicate with". Busses
> > HAVE to talk to the device to determine what the device even is before
> > it can add it to its internal bus list. That's just how the hardware
> > works, you can't say "no talking at all" otherwise that means that you
> > never even know if a device is there (and for some busses, you'll miss
> > the device entirely as they can only be found when the "host controller"
> > tells the kernel that a device showed up, not later by manually
> > scanning.)
> >
> > Think about reading PCI device ids, or USB configuration information,
> > that's a bus-core thing. Are you really thinking that non of this can
> > be read from a device under this "policy"?
>
> Yup, and that's true even in the PCI TDISP case - there's a major amount of
> "communication" and "configuration" involved in that. The principle which
> should (hopefully) generalise is that a bus/host controller driver can
> always interrogate its child device to perform whatever attestation and
> general configuration is appropriate, but we won't hand off any _control_ of
> that device to a specific device driver (nor enable potentially-impactful
> bus-level features like DMA, interrupts, etc. if relevant) without the
> requisite agreement between the bus code and the user.
I agree, that's fine, the bus core should always be able to talk to the
device, the "barrier" is where the device is handed to a driver for that
bus. That's what I'm trying to say here, DMA is often needed before
that can ever happen, so no claims of "no dma is happening" should be
here.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 13:18 ` Leon Romanovsky
2026-11-05 13:26 ` Greg Kroah-Hartman
@ 2026-11-05 14:03 ` Jason Gunthorpe
2026-11-05 15:43 ` Greg Kroah-Hartman
1 sibling, 1 reply; 42+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 14:03 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 03:18:56PM +0200, Leon Romanovsky wrote:
> On Thu, Nov 05, 2026 at 08:12:51AM -0500, Greg Kroah-Hartman wrote:
> > On Thu, Nov 05, 2026 at 01:42:30PM +0200, Leon Romanovsky wrote:
> > > From: Leon Romanovsky <leonro@nvidia.com>
> > >
> > > Convert the PCI specific untrusted to use the common device trust
> > > level. Adversarial means the same thing untrusted does.
> >
> > What about for USB? Will that "move" to use this common trust level?
> > If not, I don't think it's very "common" :)
>
> It supposed to work for all buses out-of-the box, at least this is the
> intention.
You mean DISABLED/FULL should work with this patch series on USB
right? Meaning the core code will not bind a USB driver when DISABLED?
I was wondering if some of the broader things we are doing on PCIe
also apply to USB, like device authentication. I don't know alot about
USB, but I got AI to help me find some relavent spec information.
https://usb.org/authentication
This looks like the parallel to PCIe SPDM device authentication. I
would expect it to work broadly the same way as PCIe if anyone wanted
to implement it:
1) USB discovery discovers authentication support and creates an
attestation subsystem device for it
2) The struct device starts up with trust = DISABLED
3) Userspace runs the crypto flow through attestation subsystem. This
is extremely similar looking to PCIe SPDM:
1. GET_DIGESTS -> device returns a hash for each certificate chain
slot it has.
2. GET_CERTIFICATE -> device returns its certificate chain, read
in pieces.
3. CHALLENGE (nonce) -> device returns CHALLENGE_AUTH: the chain
hash, a salt, a hash of its descriptors, and a signature over the
challenge and the response.
4) Userspace inspects the above and approves the device
5) trust = FULL, driver is probed.
But I supposed Greg is asking about /sys/bus/usb/devices/DEVICE/authorized ..
Jason
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:59 ` Robin Murphy
2026-11-05 14:03 ` Greg Kroah-Hartman
@ 2026-11-05 14:06 ` Jason Gunthorpe
2026-11-05 14:43 ` Andrew Lunn
2 siblings, 0 replies; 42+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 14:06 UTC (permalink / raw)
To: Robin Murphy
Cc: Greg Kroah-Hartman, Leon Romanovsky, Rafael J. Wysocki,
Danilo Krummrich, Dmitry Torokhov, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Jakub Kicinski, Paolo Abeni,
Johannes Berg, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Bjorn Helgaas, Joerg Roedel (AMD), Suravee Suthikulpanit,
Vasant Hegde, Will Deacon, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 01:59:20PM +0000, Robin Murphy wrote:
> Yup, and that's true even in the PCI TDISP case - there's a major amount of
> "communication" and "configuration" involved in that. The principle which
> should (hopefully) generalise is that a bus/host controller driver can
> always interrogate its child device to perform whatever attestation and
> general configuration is appropriate, but we won't hand off any _control_ of
> that device to a specific device driver (nor enable potentially-impactful
> bus-level features like DMA, interrupts, etc. if relevant) without the
> requisite agreement between the bus code and the user.
+1, I think that captures the idea very well.
Jason
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 14:03 ` Greg Kroah-Hartman
@ 2026-11-05 14:17 ` Leon Romanovsky
2026-11-05 14:26 ` Robin Murphy
1 sibling, 0 replies; 42+ messages in thread
From: Leon Romanovsky @ 2026-11-05 14:17 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Robin Murphy, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
On Thu, Nov 05, 2026 at 09:03:11AM -0500, Greg Kroah-Hartman wrote:
> On Thu, Nov 05, 2026 at 01:59:20PM +0000, Robin Murphy wrote:
> > On 2026-11-05 1:40 pm, Greg Kroah-Hartman wrote:
> > > On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
> > > > On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
> > > > > On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> > > > > > From: Leon Romanovsky <leonro@nvidia.com>
> > > > > >
> > > > > > An initrd must authorize a disabled device after attestation even if a
> > > > > > built-in driver already attempted to probe it.
> > > > > >
> > > > > > Expose requested trust_policy and read-only active trust. Allow explicit
> > > > > > disabled policy to advance once to adversary or full, fixing that decision
> > > > > > immediately. Reject later policy changes, including for a device claimed
> > > > > > before registration, while allowing same-value writes on live devices.
> > > > > > Keep initial unrestricted selection separate from explicit authorization
> > > > > > and bind-time commitment. Serialize writes with probing under the device
> > > > > > lock. Include the attributes in device ownership changes.
> > > > > >
> > > > > > Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> > > > > > ---
> > > > > > Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> > > > > > MAINTAINERS | 1 +
> > > > > > drivers/base/base.h | 6 ++
> > > > > > drivers/base/core.c | 15 +++-
> > > > > > drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> > > > > > 5 files changed, 174 insertions(+), 1 deletion(-)
> > > > > >
> > > > > > diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > > new file mode 100644
> > > > > > index 0000000000000..97e07a174769c
> > > > > > --- /dev/null
> > > > > > +++ b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > > @@ -0,0 +1,51 @@
> > > > > > +What: /sys/devices/.../trust
> > > > > > +Date: September 2026
> > > > > > +Contact: driver-core@lists.linux.dev
> > > > > > +Description:
> > > > > > + (RO) The active trust level for the device. The values are
> > > > > > + "disabled", "adversary", and "full". Driver core resolves
> > > > > > + the level before DMA configuration and probe, and retains it
> > > > > > + through removal and DMA cleanup. Outside a bind transaction
> > > > > > + the level is "disabled". This does not imply that policy is
> > > > > > + writable or that the device has never operated.
> > > > > > +
> > > > > > +What: /sys/devices/.../trust_policy
> > > > > > +Date: September 2026
> > > > > > +Contact: driver-core@lists.linux.dev
> > > > > > +Description:
> > > > > > + (RW) The requested trust policy for the device. The initial
> > > > > > + value may be selected by the bus before the device is
> > > > > > + registered; otherwise it is "default". The policy is resolved
> > > > > > + to an active trust level before the device is probed.
> > > > > > +
> > > > > > + The values are "default", "disabled", "adversary", and
> > > > > > + "full". "default" lets the bus select the active trust level.
> > > > > > + "disabled" prevents a functional driver from binding.
> > > > > > + "adversary" requests operation with the restrictions that the
> > > > > > + bus and DMA layers can enforce for a potentially malicious
> > > > > > + device; it does not isolate DMA by itself. "full" requests
> > > > > > + normal operation.
> > > > > > +
> > > > > > + Before an operating level is committed, policy may change
> > > > > > + freely until "disabled" is selected. Explicit "disabled"
> > > > > > + blocks binding while allowing a later write of "adversary" or
> > > > > > + "full". Repeated rejected probes preserve this opportunity.
> > > > > > + Authorization immediately fixes the requested policy.
> > > > > > + Returning to "default" or "disabled", or selecting another
> > > > > > + mode, fails.
> > > > > > +
> > > > > > + Successful resolution to an allowed level commits policy
> > > > > > + and operating level before driver or DMA setup. Both remain
> > > > > > + fixed for the device object's lifetime, including through
> > > > > > + failed or deferred probes, unbind, reset, and driver reload.
> > > > > > + Later binds must resolve to the same level, including when
> > > > > > + the policy is "default". A resolver error or disabled result
> > > > > > + before commitment leaves the policy's writability unchanged.
> > > > >
> > > > > I understand you are aproaching this from a PCI point-of-view, but many
> > > > > busses do not care or have DMA at all, and need to actually talk to the
> > > > > device before being able to even "add" it to the kernel. So you are
> > > > > making a driver core-specific policy based on mentioning DMA here, when
> > > > > really that should only be a bus-specific thing, right? Some busses
> > > > > might require DMA to happen (i.e. USB needs their host controllers to
> > > > > use DMA to even talk to the device at all).
> > > > >
> > > > > So can this be written to be more vague and generic and then put the
> > > > > specifics in the bus-specific portion?
> > > >
> > > > Yes, of course. The main point is that drivers must not communicate with
> > > > or configure hardware before attestation is approved. DMA is the PCI
> > > > way to communicate with hardware.
> > >
> > > That's a very vague statement of "must not communicate with". Busses
> > > HAVE to talk to the device to determine what the device even is before
> > > it can add it to its internal bus list. That's just how the hardware
> > > works, you can't say "no talking at all" otherwise that means that you
> > > never even know if a device is there (and for some busses, you'll miss
> > > the device entirely as they can only be found when the "host controller"
> > > tells the kernel that a device showed up, not later by manually
> > > scanning.)
> > >
> > > Think about reading PCI device ids, or USB configuration information,
> > > that's a bus-core thing. Are you really thinking that non of this can
> > > be read from a device under this "policy"?
> >
> > Yup, and that's true even in the PCI TDISP case - there's a major amount of
> > "communication" and "configuration" involved in that. The principle which
> > should (hopefully) generalise is that a bus/host controller driver can
> > always interrogate its child device to perform whatever attestation and
> > general configuration is appropriate, but we won't hand off any _control_ of
> > that device to a specific device driver (nor enable potentially-impactful
> > bus-level features like DMA, interrupts, etc. if relevant) without the
> > requisite agreement between the bus code and the user.
>
> I agree, that's fine, the bus core should always be able to talk to the
> device, the "barrier" is where the device is handed to a driver for that
> bus. That's what I'm trying to say here, DMA is often needed before
> that can ever happen, so no claims of "no dma is happening" should be
> here.
You wanted vague terminology, I was vague :)
Anyway, Robin perfectly described the trust model we are trying to implement here.
Thanks
>
> thanks,
>
> greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 14:03 ` Greg Kroah-Hartman
2026-11-05 14:17 ` Leon Romanovsky
@ 2026-11-05 14:26 ` Robin Murphy
2026-11-05 15:45 ` Greg Kroah-Hartman
1 sibling, 1 reply; 42+ messages in thread
From: Robin Murphy @ 2026-11-05 14:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Leon Romanovsky, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
On 2026-11-05 2:03 pm, Greg Kroah-Hartman wrote:
> On Thu, Nov 05, 2026 at 01:59:20PM +0000, Robin Murphy wrote:
>> On 2026-11-05 1:40 pm, Greg Kroah-Hartman wrote:
>>> On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
>>>> On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
>>>>> On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
>>>>>> From: Leon Romanovsky <leonro@nvidia.com>
>>>>>>
>>>>>> An initrd must authorize a disabled device after attestation even if a
>>>>>> built-in driver already attempted to probe it.
>>>>>>
>>>>>> Expose requested trust_policy and read-only active trust. Allow explicit
>>>>>> disabled policy to advance once to adversary or full, fixing that decision
>>>>>> immediately. Reject later policy changes, including for a device claimed
>>>>>> before registration, while allowing same-value writes on live devices.
>>>>>> Keep initial unrestricted selection separate from explicit authorization
>>>>>> and bind-time commitment. Serialize writes with probing under the device
>>>>>> lock. Include the attributes in device ownership changes.
>>>>>>
>>>>>> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
>>>>>> ---
>>>>>> Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
>>>>>> MAINTAINERS | 1 +
>>>>>> drivers/base/base.h | 6 ++
>>>>>> drivers/base/core.c | 15 +++-
>>>>>> drivers/base/trust.c | 102 ++++++++++++++++++++++++++
>>>>>> 5 files changed, 174 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
>>>>>> new file mode 100644
>>>>>> index 0000000000000..97e07a174769c
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/ABI/testing/sysfs-devices-trust
>>>>>> @@ -0,0 +1,51 @@
>>>>>> +What: /sys/devices/.../trust
>>>>>> +Date: September 2026
>>>>>> +Contact: driver-core@lists.linux.dev
>>>>>> +Description:
>>>>>> + (RO) The active trust level for the device. The values are
>>>>>> + "disabled", "adversary", and "full". Driver core resolves
>>>>>> + the level before DMA configuration and probe, and retains it
>>>>>> + through removal and DMA cleanup. Outside a bind transaction
>>>>>> + the level is "disabled". This does not imply that policy is
>>>>>> + writable or that the device has never operated.
>>>>>> +
>>>>>> +What: /sys/devices/.../trust_policy
>>>>>> +Date: September 2026
>>>>>> +Contact: driver-core@lists.linux.dev
>>>>>> +Description:
>>>>>> + (RW) The requested trust policy for the device. The initial
>>>>>> + value may be selected by the bus before the device is
>>>>>> + registered; otherwise it is "default". The policy is resolved
>>>>>> + to an active trust level before the device is probed.
>>>>>> +
>>>>>> + The values are "default", "disabled", "adversary", and
>>>>>> + "full". "default" lets the bus select the active trust level.
>>>>>> + "disabled" prevents a functional driver from binding.
>>>>>> + "adversary" requests operation with the restrictions that the
>>>>>> + bus and DMA layers can enforce for a potentially malicious
>>>>>> + device; it does not isolate DMA by itself. "full" requests
>>>>>> + normal operation.
>>>>>> +
>>>>>> + Before an operating level is committed, policy may change
>>>>>> + freely until "disabled" is selected. Explicit "disabled"
>>>>>> + blocks binding while allowing a later write of "adversary" or
>>>>>> + "full". Repeated rejected probes preserve this opportunity.
>>>>>> + Authorization immediately fixes the requested policy.
>>>>>> + Returning to "default" or "disabled", or selecting another
>>>>>> + mode, fails.
>>>>>> +
>>>>>> + Successful resolution to an allowed level commits policy
>>>>>> + and operating level before driver or DMA setup. Both remain
>>>>>> + fixed for the device object's lifetime, including through
>>>>>> + failed or deferred probes, unbind, reset, and driver reload.
>>>>>> + Later binds must resolve to the same level, including when
>>>>>> + the policy is "default". A resolver error or disabled result
>>>>>> + before commitment leaves the policy's writability unchanged.
>>>>>
>>>>> I understand you are aproaching this from a PCI point-of-view, but many
>>>>> busses do not care or have DMA at all, and need to actually talk to the
>>>>> device before being able to even "add" it to the kernel. So you are
>>>>> making a driver core-specific policy based on mentioning DMA here, when
>>>>> really that should only be a bus-specific thing, right? Some busses
>>>>> might require DMA to happen (i.e. USB needs their host controllers to
>>>>> use DMA to even talk to the device at all).
>>>>>
>>>>> So can this be written to be more vague and generic and then put the
>>>>> specifics in the bus-specific portion?
>>>>
>>>> Yes, of course. The main point is that drivers must not communicate with
>>>> or configure hardware before attestation is approved. DMA is the PCI
>>>> way to communicate with hardware.
>>>
>>> That's a very vague statement of "must not communicate with". Busses
>>> HAVE to talk to the device to determine what the device even is before
>>> it can add it to its internal bus list. That's just how the hardware
>>> works, you can't say "no talking at all" otherwise that means that you
>>> never even know if a device is there (and for some busses, you'll miss
>>> the device entirely as they can only be found when the "host controller"
>>> tells the kernel that a device showed up, not later by manually
>>> scanning.)
>>>
>>> Think about reading PCI device ids, or USB configuration information,
>>> that's a bus-core thing. Are you really thinking that non of this can
>>> be read from a device under this "policy"?
>>
>> Yup, and that's true even in the PCI TDISP case - there's a major amount of
>> "communication" and "configuration" involved in that. The principle which
>> should (hopefully) generalise is that a bus/host controller driver can
>> always interrogate its child device to perform whatever attestation and
>> general configuration is appropriate, but we won't hand off any _control_ of
>> that device to a specific device driver (nor enable potentially-impactful
>> bus-level features like DMA, interrupts, etc. if relevant) without the
>> requisite agreement between the bus code and the user.
>
> I agree, that's fine, the bus core should always be able to talk to the
> device, the "barrier" is where the device is handed to a driver for that
> bus. That's what I'm trying to say here, DMA is often needed before
> that can ever happen, so no claims of "no dma is happening" should be
> here.
For the case of USB and co. it's also still fine - just perhaps wants
some clearer wording - since the endpoint itself is not performing DMA
operations. It might send some message which causes the host controller
device to DMA to/from a buffer owned by the host controller driver, but
that's perfectly OK since the host controller itself is not on its own
bus, and must have already been accepted by the PCI/platform/whatever
code to get to that point.
Now if we had some bonkers kind of bus where the only way to identify an
endpoint was to give it DMA addresses to have it read a command
descriptor and write out its own data directly, then indeed there would
be little hope of being able to enforce much of a trust model for that...
Thanks,
Robin.
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 13:59 ` Robin Murphy
2026-11-05 14:03 ` Greg Kroah-Hartman
2026-11-05 14:06 ` Jason Gunthorpe
@ 2026-11-05 14:43 ` Andrew Lunn
2026-11-05 14:59 ` Jason Gunthorpe
2026-11-05 15:09 ` Robin Murphy
2 siblings, 2 replies; 42+ messages in thread
From: Andrew Lunn @ 2026-11-05 14:43 UTC (permalink / raw)
To: Robin Murphy
Cc: Greg Kroah-Hartman, Leon Romanovsky, Rafael J. Wysocki,
Danilo Krummrich, Dmitry Torokhov, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
> Yup, and that's true even in the PCI TDISP case - there's a major amount of
> "communication" and "configuration" involved in that. The principle which
> should (hopefully) generalise is that a bus/host controller driver can
> always interrogate its child device to perform whatever attestation and
> general configuration is appropriate, but we won't hand off any _control_ of
> that device to a specific device driver (nor enable potentially-impactful
> bus-level features like DMA, interrupts, etc. if relevant) without the
> requisite agreement between the bus code and the user.
Maybe it would be good to list subsystems which the bus driver can
use, and cannot use.
You list interrupts as potentially being dangerous. What about reset
controllers, GPIOs, regulators? At least in the embedded world, the
bus driver might need to enable power, set a clock ticking, and take
the device out of reset via a GPIO, before the device pops into
existence and will enumerate on the bus.
I don't see why interrupts are dangerous, which makes it hard for me
to determine if GPIOs and clocks, and regulators are also dangerous?
Andrew
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 03/17] net: phy: Attach generic PHY drivers through the driver core
2026-11-05 11:42 ` [PATCH 03/17] net: phy: Attach generic PHY " Leon Romanovsky
@ 2026-11-05 14:50 ` Andrew Lunn
2026-11-05 15:33 ` Andrew Lunn
1 sibling, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2026-11-05 14:50 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Heiner Kallweit, Russell King, David S. Miller,
Jakub Kicinski, Paolo Abeni, Johannes Berg, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:42:16PM +0200, Leon Romanovsky wrote:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> The generic PHY fallback probes directly before publishing its binding,
> bypassing driver-core admission and supplier checks.
>
> Attach through the driver core so admission precedes functional setup.
> Callers hold RTNL, which a concurrent bind of a specific driver may wait
> for with the device lock held, so for this flow let's add
> device_driver_try_attach() so the attachment never waits for that lock.
FYI: This code is fragile, and causes issues. I will need to stare
long and hard at it before accepting it. So i'm unlikely to do that
until the basic idea is accepted.
I also wounder how a PHY is a threat. What is your threat model for an
Ethernet PHY?
Andrew
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 14:43 ` Andrew Lunn
@ 2026-11-05 14:59 ` Jason Gunthorpe
2026-11-05 15:09 ` Robin Murphy
1 sibling, 0 replies; 42+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 14:59 UTC (permalink / raw)
To: Andrew Lunn
Cc: Robin Murphy, Greg Kroah-Hartman, Leon Romanovsky,
Rafael J. Wysocki, Danilo Krummrich, Dmitry Torokhov,
Heiner Kallweit, Russell King, David S. Miller, Jakub Kicinski,
Paolo Abeni, Johannes Berg, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, David Woodhouse,
Lu Baolu, Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 03:43:53PM +0100, Andrew Lunn wrote:
> > Yup, and that's true even in the PCI TDISP case - there's a major amount of
> > "communication" and "configuration" involved in that. The principle which
> > should (hopefully) generalise is that a bus/host controller driver can
> > always interrogate its child device to perform whatever attestation and
> > general configuration is appropriate, but we won't hand off any _control_ of
> > that device to a specific device driver (nor enable potentially-impactful
> > bus-level features like DMA, interrupts, etc. if relevant) without the
> > requisite agreement between the bus code and the user.
>
> Maybe it would be good to list subsystems which the bus driver can
> use, and cannot use.
>
> You list interrupts as potentially being dangerous. What about reset
> controllers, GPIOs, regulators? At least in the embedded world, the
> bus driver might need to enable power, set a clock ticking, and take
> the device out of reset via a GPIO, before the device pops into
> existence and will enumerate on the bus.
>
> I don't see why interrupts are dangerous, which makes it hard for me
> to determine if GPIOs and clocks, and regulators are also dangerous?
IMHO we shouldn't try to over think this.
The overall point is to give the userspace a place to inject policy.
Bus drivers should try as hard as they can to contain their child
devices. Like PCIE should activate the iommu, disable ATS. USB should
limit the messages it exchanges, and so on.
When userspace accepts the bus driver it also accepts its containment
capability. So if it accepts one that uses gpio, etc then that's fine.
We are not the point of trying to describe what threat models various
kernel drivers can support. Userspace has to figure that out on their
own.
Jason
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 14:43 ` Andrew Lunn
2026-11-05 14:59 ` Jason Gunthorpe
@ 2026-11-05 15:09 ` Robin Murphy
1 sibling, 0 replies; 42+ messages in thread
From: Robin Murphy @ 2026-11-05 15:09 UTC (permalink / raw)
To: Andrew Lunn
Cc: Greg Kroah-Hartman, Leon Romanovsky, Rafael J. Wysocki,
Danilo Krummrich, Dmitry Torokhov, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
On 2026-11-05 2:43 pm, Andrew Lunn wrote:
>> Yup, and that's true even in the PCI TDISP case - there's a major amount of
>> "communication" and "configuration" involved in that. The principle which
>> should (hopefully) generalise is that a bus/host controller driver can
>> always interrogate its child device to perform whatever attestation and
>> general configuration is appropriate, but we won't hand off any _control_ of
>> that device to a specific device driver (nor enable potentially-impactful
>> bus-level features like DMA, interrupts, etc. if relevant) without the
>> requisite agreement between the bus code and the user.
>
> Maybe it would be good to list subsystems which the bus driver can
> use, and cannot use.
>
> You list interrupts as potentially being dangerous.
Nope, "impactful" - as in "things the hardware may do by itself that
could impact the rest of the system". If it has wired interrupts, it may
assert them at will as an attempt at some mild DoS or to try to hit some
kind of known timing condition; if it has MSIs then it can effectively
attempt to write at least one arbitrary word of data to an arbitrary
address.
If the bus does happen to provide some way to physically prevent any
such device-initiated behaviour from propagating until we OK it, then
that's a nice thing to be able to include in our trust model.
> What about reset
> controllers, GPIOs, regulators? At least in the embedded world, the
> bus driver might need to enable power, set a clock ticking, and take
> the device out of reset via a GPIO, before the device pops into
> existence and will enumerate on the bus.
External resources that a device only _consumes_ should not need to
factor into the trust of that device itself. If you want to worry from
the PoV of the device whether _it_ trusts those external providers, that
would be a whole other can of worms that I for one will run screaming
from as fast as possible :)
Thanks,
Robin.
> I don't see why interrupts are dangerous, which makes it hard for me
> to determine if GPIOs and clocks, and regulators are also dangerous?
>
> Andrew
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 03/17] net: phy: Attach generic PHY drivers through the driver core
2026-11-05 11:42 ` [PATCH 03/17] net: phy: Attach generic PHY " Leon Romanovsky
2026-11-05 14:50 ` Andrew Lunn
@ 2026-11-05 15:33 ` Andrew Lunn
1 sibling, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2026-11-05 15:33 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Heiner Kallweit, Russell King, David S. Miller,
Jakub Kicinski, Paolo Abeni, Johannes Berg, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
> + if (err == -EBUSY) {
> + /* Another bind owns the PHY, so nothing was probed here. */
> + irq = phydev->irq;
> + err = -EPROBE_DEFER;
> + } else if (err == -EAGAIN) {
> + err = -EPROBE_DEFER;
You cannot return -EPROBE_DEFER here. You are not guaranteed to be in
the context of a probe. Very likely, you are in the middle of the
ethernet drives open() call. Both probes happened a long time ago.
Andrew
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 14:03 ` Jason Gunthorpe
@ 2026-11-05 15:43 ` Greg Kroah-Hartman
2026-11-05 16:26 ` Jason Gunthorpe
0 siblings, 1 reply; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 15:43 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Leon Romanovsky, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 10:03:52AM -0400, Jason Gunthorpe wrote:
> On Thu, Nov 05, 2026 at 03:18:56PM +0200, Leon Romanovsky wrote:
> > On Thu, Nov 05, 2026 at 08:12:51AM -0500, Greg Kroah-Hartman wrote:
> > > On Thu, Nov 05, 2026 at 01:42:30PM +0200, Leon Romanovsky wrote:
> > > > From: Leon Romanovsky <leonro@nvidia.com>
> > > >
> > > > Convert the PCI specific untrusted to use the common device trust
> > > > level. Adversarial means the same thing untrusted does.
> > >
> > > What about for USB? Will that "move" to use this common trust level?
> > > If not, I don't think it's very "common" :)
> >
> > It supposed to work for all buses out-of-the box, at least this is the
> > intention.
>
> You mean DISABLED/FULL should work with this patch series on USB
> right? Meaning the core code will not bind a USB driver when DISABLED?
>
> I was wondering if some of the broader things we are doing on PCIe
> also apply to USB, like device authentication. I don't know alot about
> USB, but I got AI to help me find some relavent spec information.
>
> https://usb.org/authentication
>
> This looks like the parallel to PCIe SPDM device authentication. I
> would expect it to work broadly the same way as PCIe if anyone wanted
> to implement it:
No real need to as no one actually made a USB device with that spec :)
There was some patches on the linux-usb list 6 months or so ago to try
to implement it, but it stalled due to there not even be any devices to
test it for.
> 1) USB discovery discovers authentication support and creates an
> attestation subsystem device for it
> 2) The struct device starts up with trust = DISABLED
> 3) Userspace runs the crypto flow through attestation subsystem. This
> is extremely similar looking to PCIe SPDM:
> 1. GET_DIGESTS -> device returns a hash for each certificate chain
> slot it has.
> 2. GET_CERTIFICATE -> device returns its certificate chain, read
> in pieces.
> 3. CHALLENGE (nonce) -> device returns CHALLENGE_AUTH: the chain
> hash, a salt, a hash of its descriptors, and a signature over the
> challenge and the response.
> 4) Userspace inspects the above and approves the device
> 5) trust = FULL, driver is probed.
>
> But I supposed Greg is asking about /sys/bus/usb/devices/DEVICE/authorized ..
Yes, that is exactly what I am asking about.
And I know there are other types of devices that will want to use this
type of thing (i.e. SPDM seems to be spreading to other new bus types),
so it's good to have and get right.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 14:26 ` Robin Murphy
@ 2026-11-05 15:45 ` Greg Kroah-Hartman
0 siblings, 0 replies; 42+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-05 15:45 UTC (permalink / raw)
To: Robin Murphy
Cc: Leon Romanovsky, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, David Woodhouse, Lu Baolu, Johan Hovold,
Eric Dumazet, Marcel Holtmann, Luiz Augusto von Dentz,
Nipun Gupta, Nikhil Agarwal, Pieter Jansen van Vuuren,
linux-kernel, driver-core, linux-input, netdev, linux-wireless,
linux-usb, linux-bluetooth, linux-acpi, linux-doc, linux-pci,
iommu, Jason Gunthorpe
On Thu, Nov 05, 2026 at 02:26:39PM +0000, Robin Murphy wrote:
> On 2026-11-05 2:03 pm, Greg Kroah-Hartman wrote:
> > On Thu, Nov 05, 2026 at 01:59:20PM +0000, Robin Murphy wrote:
> > > On 2026-11-05 1:40 pm, Greg Kroah-Hartman wrote:
> > > > On Thu, Nov 05, 2026 at 03:27:29PM +0200, Leon Romanovsky wrote:
> > > > > On Thu, Nov 05, 2026 at 08:10:14AM -0500, Greg Kroah-Hartman wrote:
> > > > > > On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> > > > > > > From: Leon Romanovsky <leonro@nvidia.com>
> > > > > > >
> > > > > > > An initrd must authorize a disabled device after attestation even if a
> > > > > > > built-in driver already attempted to probe it.
> > > > > > >
> > > > > > > Expose requested trust_policy and read-only active trust. Allow explicit
> > > > > > > disabled policy to advance once to adversary or full, fixing that decision
> > > > > > > immediately. Reject later policy changes, including for a device claimed
> > > > > > > before registration, while allowing same-value writes on live devices.
> > > > > > > Keep initial unrestricted selection separate from explicit authorization
> > > > > > > and bind-time commitment. Serialize writes with probing under the device
> > > > > > > lock. Include the attributes in device ownership changes.
> > > > > > >
> > > > > > > Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> > > > > > > ---
> > > > > > > Documentation/ABI/testing/sysfs-devices-trust | 51 +++++++++++++
> > > > > > > MAINTAINERS | 1 +
> > > > > > > drivers/base/base.h | 6 ++
> > > > > > > drivers/base/core.c | 15 +++-
> > > > > > > drivers/base/trust.c | 102 ++++++++++++++++++++++++++
> > > > > > > 5 files changed, 174 insertions(+), 1 deletion(-)
> > > > > > >
> > > > > > > diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > > > new file mode 100644
> > > > > > > index 0000000000000..97e07a174769c
> > > > > > > --- /dev/null
> > > > > > > +++ b/Documentation/ABI/testing/sysfs-devices-trust
> > > > > > > @@ -0,0 +1,51 @@
> > > > > > > +What: /sys/devices/.../trust
> > > > > > > +Date: September 2026
> > > > > > > +Contact: driver-core@lists.linux.dev
> > > > > > > +Description:
> > > > > > > + (RO) The active trust level for the device. The values are
> > > > > > > + "disabled", "adversary", and "full". Driver core resolves
> > > > > > > + the level before DMA configuration and probe, and retains it
> > > > > > > + through removal and DMA cleanup. Outside a bind transaction
> > > > > > > + the level is "disabled". This does not imply that policy is
> > > > > > > + writable or that the device has never operated.
> > > > > > > +
> > > > > > > +What: /sys/devices/.../trust_policy
> > > > > > > +Date: September 2026
> > > > > > > +Contact: driver-core@lists.linux.dev
> > > > > > > +Description:
> > > > > > > + (RW) The requested trust policy for the device. The initial
> > > > > > > + value may be selected by the bus before the device is
> > > > > > > + registered; otherwise it is "default". The policy is resolved
> > > > > > > + to an active trust level before the device is probed.
> > > > > > > +
> > > > > > > + The values are "default", "disabled", "adversary", and
> > > > > > > + "full". "default" lets the bus select the active trust level.
> > > > > > > + "disabled" prevents a functional driver from binding.
> > > > > > > + "adversary" requests operation with the restrictions that the
> > > > > > > + bus and DMA layers can enforce for a potentially malicious
> > > > > > > + device; it does not isolate DMA by itself. "full" requests
> > > > > > > + normal operation.
> > > > > > > +
> > > > > > > + Before an operating level is committed, policy may change
> > > > > > > + freely until "disabled" is selected. Explicit "disabled"
> > > > > > > + blocks binding while allowing a later write of "adversary" or
> > > > > > > + "full". Repeated rejected probes preserve this opportunity.
> > > > > > > + Authorization immediately fixes the requested policy.
> > > > > > > + Returning to "default" or "disabled", or selecting another
> > > > > > > + mode, fails.
> > > > > > > +
> > > > > > > + Successful resolution to an allowed level commits policy
> > > > > > > + and operating level before driver or DMA setup. Both remain
> > > > > > > + fixed for the device object's lifetime, including through
> > > > > > > + failed or deferred probes, unbind, reset, and driver reload.
> > > > > > > + Later binds must resolve to the same level, including when
> > > > > > > + the policy is "default". A resolver error or disabled result
> > > > > > > + before commitment leaves the policy's writability unchanged.
> > > > > >
> > > > > > I understand you are aproaching this from a PCI point-of-view, but many
> > > > > > busses do not care or have DMA at all, and need to actually talk to the
> > > > > > device before being able to even "add" it to the kernel. So you are
> > > > > > making a driver core-specific policy based on mentioning DMA here, when
> > > > > > really that should only be a bus-specific thing, right? Some busses
> > > > > > might require DMA to happen (i.e. USB needs their host controllers to
> > > > > > use DMA to even talk to the device at all).
> > > > > >
> > > > > > So can this be written to be more vague and generic and then put the
> > > > > > specifics in the bus-specific portion?
> > > > >
> > > > > Yes, of course. The main point is that drivers must not communicate with
> > > > > or configure hardware before attestation is approved. DMA is the PCI
> > > > > way to communicate with hardware.
> > > >
> > > > That's a very vague statement of "must not communicate with". Busses
> > > > HAVE to talk to the device to determine what the device even is before
> > > > it can add it to its internal bus list. That's just how the hardware
> > > > works, you can't say "no talking at all" otherwise that means that you
> > > > never even know if a device is there (and for some busses, you'll miss
> > > > the device entirely as they can only be found when the "host controller"
> > > > tells the kernel that a device showed up, not later by manually
> > > > scanning.)
> > > >
> > > > Think about reading PCI device ids, or USB configuration information,
> > > > that's a bus-core thing. Are you really thinking that non of this can
> > > > be read from a device under this "policy"?
> > >
> > > Yup, and that's true even in the PCI TDISP case - there's a major amount of
> > > "communication" and "configuration" involved in that. The principle which
> > > should (hopefully) generalise is that a bus/host controller driver can
> > > always interrogate its child device to perform whatever attestation and
> > > general configuration is appropriate, but we won't hand off any _control_ of
> > > that device to a specific device driver (nor enable potentially-impactful
> > > bus-level features like DMA, interrupts, etc. if relevant) without the
> > > requisite agreement between the bus code and the user.
> >
> > I agree, that's fine, the bus core should always be able to talk to the
> > device, the "barrier" is where the device is handed to a driver for that
> > bus. That's what I'm trying to say here, DMA is often needed before
> > that can ever happen, so no claims of "no dma is happening" should be
> > here.
>
> For the case of USB and co. it's also still fine - just perhaps wants some
> clearer wording - since the endpoint itself is not performing DMA
> operations. It might send some message which causes the host controller
> device to DMA to/from a buffer owned by the host controller driver, but
> that's perfectly OK since the host controller itself is not on its own bus,
> and must have already been accepted by the PCI/platform/whatever code to get
> to that point.
>
> Now if we had some bonkers kind of bus where the only way to identify an
> endpoint was to give it DMA addresses to have it read a command descriptor
> and write out its own data directly, then indeed there would be little hope
> of being able to enforce much of a trust model for that...
Look at how USB typec code handles many parts of the device discovery
process. Lots of data is read and written for loads of different things
that the device provides and the kernel has to "trust" to be correct.
It's very very complex and DMA and other buffers and resources are quite
involved here. We have tried to "harden" most of these paths now, but
odds are we haven't found them all...
thanks,
greg k-h
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 17/17] PCI: Replace untrusted state with device trust
2026-11-05 15:43 ` Greg Kroah-Hartman
@ 2026-11-05 16:26 ` Jason Gunthorpe
0 siblings, 0 replies; 42+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 16:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Leon Romanovsky, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 10:43:00AM -0500, Greg Kroah-Hartman wrote:
> > But I supposed Greg is asking about /sys/bus/usb/devices/DEVICE/authorized ..
>
> Yes, that is exactly what I am asking about.
It looks like that does the same basic idea of delaying driver binding
that this series does. Hopefully Leon can harmonize it to use the
shared trust level flag instead of a usb specific flag and retain all
the uABI around it.
But, even with this v1 the user can choose to use the generic trust
level and ignore the USB specific authorized and get the same
functional outcome.
> And I know there are other types of devices that will want to use this
> type of thing (i.e. SPDM seems to be spreading to other new bus types),
> so it's good to have and get right.
Yes, we are investing alot of time right now in trying to get SPDM
uAPI right when we bring the general framework into the
kernel. Everyone needing SPDM should be able to re-use it.
Jason
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 10/17] driver core: Expose device trust authorization through sysfs
2026-11-05 11:42 ` [PATCH 10/17] driver core: Expose device trust authorization through sysfs Leon Romanovsky
2026-11-05 13:10 ` Greg Kroah-Hartman
@ 2026-11-05 17:02 ` Jason Gunthorpe
1 sibling, 0 replies; 42+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 17:02 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu
On Thu, Nov 05, 2026 at 01:42:23PM +0200, Leon Romanovsky wrote:
> + (RW) The requested trust policy for the device. The initial
> + value may be selected by the bus before the device is
> + registered; otherwise it is "default". The policy is resolved
> + to an active trust level before the device is probed.
> +
> + The values are "default", "disabled", "adversary", and
> + "full". "default" lets the bus select the active trust level.
> + "disabled" prevents a functional driver from binding.
For making this more general:
"disabled" prevents a driver from binding and informs the kernel to
provide containment of the device. For example a PCIe device should be
unable to DMA to memory.
> + "adversary" requests operation with the restrictions that the
> + bus and DMA layers can enforce for a potentially malicious
> + device; it does not isolate DMA by itself. "full" requests
> + normal operation.
"adversary" informs the kernel that the userspace wants to opt into
any security defenses the kernel may have to restrict and further
validate interactions with the device. This may have a performance or
functional cost compared to "full". As an example the DMA API and
IOMMU layers will avoid exposing unrelated nearby memory to the device
during DMA.
> + Successful resolution to an allowed level commits policy
> + and operating level before driver or DMA setup. Both remain
"before driver startup"
> + fixed for the device object's lifetime, including through
> + failed or deferred probes, unbind, reset, and driver reload.
> + Later binds must resolve to the same level, including when
> + the policy is "default". A resolver error or disabled result
> + before commitment leaves the policy's writability unchanged.
Not sure what "resolver error" is.
Jason
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 02/17] Input: serio - attach manual drivers through the driver core
2026-11-05 11:42 ` [PATCH 02/17] Input: serio " Leon Romanovsky
@ 2026-11-05 17:05 ` Dmitry Torokhov
0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2026-11-05 17:05 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Jakub Kicinski, Paolo Abeni, Johannes Berg, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Bjorn Helgaas, Joerg Roedel (AMD),
Suravee Suthikulpanit, Vasant Hegde, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Johan Hovold, Eric Dumazet,
Marcel Holtmann, Luiz Augusto von Dentz, Nipun Gupta,
Nikhil Agarwal, Pieter Jansen van Vuuren, linux-kernel,
driver-core, linux-input, netdev, linux-wireless, linux-usb,
linux-bluetooth, linux-acpi, linux-doc, linux-pci, iommu,
Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:42:15PM +0200, Leon Romanovsky wrote:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> Manual serio binding connects a driver before driver core can admit the
> device. A rejected binding therefore arrives after functional setup.
>
> Keep the port ID check and use device_driver_attach() for the existing bus
> probe path. Driver core then serializes admission and handles probe failure
> before publishing the binding.
>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
> ---
> drivers/input/serio/serio.c | 24 +++---------------------
> 1 file changed, 3 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
> index 54dd26249b027..604440244c1ca 100644
> --- a/drivers/input/serio/serio.c
> +++ b/drivers/input/serio/serio.c
> @@ -81,28 +81,10 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser
>
> static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
> {
> - int error;
> -
> - if (serio_match_port(drv->id_table, serio)) {
> -
> - serio->dev.driver = &drv->driver;
> - if (serio_connect_driver(serio, drv)) {
> - serio->dev.driver = NULL;
> - return -ENODEV;
> - }
> + if (!serio_match_port(drv->id_table, serio))
> + return 0;
>
> - error = device_bind_driver(&serio->dev);
> - if (error) {
> - dev_warn(&serio->dev,
> - "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
> - serio->phys, serio->name,
> - drv->description, error);
> - serio_disconnect_driver(serio);
> - serio->dev.driver = NULL;
> - return error;
> - }
> - }
> - return 0;
> + return device_driver_attach(&drv->driver, &serio->dev);
I am not sure how safe it is, serio (and gameport) have pretty elaborate
structure and locking rules because they can recurse into themselves (to
support case of Synaptics touch-pads with pass-through PS/2 ports).
But this call is for user-initiated probing anyways, I am not sure if it
even needs "trust" handling.
... And I even thinking if we really need to keep these custom
attributes or if I can rip them out in the future ...
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 05/17] USB: Lock registered interfaces during manual driver binding
2026-11-05 11:42 ` [PATCH 05/17] USB: Lock registered interfaces during " Leon Romanovsky
@ 2026-11-05 17:24 ` Alan Stern
0 siblings, 0 replies; 42+ messages in thread
From: Alan Stern @ 2026-11-05 17:24 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Jakub Kicinski, Paolo Abeni, Johannes Berg,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Bjorn Helgaas,
Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
Will Deacon, Robin Murphy, David Woodhouse, Lu Baolu,
Johan Hovold, Eric Dumazet, Marcel Holtmann,
Luiz Augusto von Dentz, Nipun Gupta, Nikhil Agarwal,
Pieter Jansen van Vuuren, linux-kernel, driver-core, linux-input,
netdev, linux-wireless, linux-usb, linux-bluetooth, linux-acpi,
linux-doc, linux-pci, iommu, Jason Gunthorpe
On Thu, Nov 05, 2026 at 01:42:18PM +0200, Leon Romanovsky wrote:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> Interface claims hold the USB device lock, but device_bind_driver()
> requires the target interface lock. Without it, trust-policy writes can
> race admission.
This says nothing about usb_driver_release_refused_claim() and the code
related to it. It is an incomplete description of the patch.
Note that nothing prevents drivers from using an interface they claim
before that interface is registered. For your scheme to work properly,
you should make sure the claim is authorized when
usb_driver_claim_interface() is called, not when the interface is
registered.
Alan Stern
> +/*
> + * The driver core refused to bind an interface that a sibling claimed before
> + * it was registered, and dropped the claim. Let the claimant release the
> + * interface instead of operating it with a bound interface but no driver.
> + */
> +void usb_driver_release_refused_claim(struct usb_interface *iface,
> + struct device_driver *claimant)
> +{
> + struct device *dev = &iface->dev;
> +
> + device_lock(dev);
> + if (!dev->driver && iface->condition == USB_INTERFACE_BOUND) {
> + dev->driver = claimant;
> + usb_unbind_interface(dev);
> + dev_set_drvdata(dev, NULL);
> + dev->driver = NULL;
> + }
> + device_unlock(dev);
> +}
> +
> /* returns 0 if no match, 1 if match */
> int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
> {
> diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
> index a738d03c23eac..d35a7710c8ef4 100644
> --- a/drivers/usb/core/message.c
> +++ b/drivers/usb/core/message.c
> @@ -2264,6 +2264,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
> */
> for (i = 0; i < nintf; ++i) {
> struct usb_interface *intf = cp->interface[i];
> + struct device_driver *claimant;
>
> if (intf->dev.of_node &&
> !of_device_is_available(intf->dev.of_node)) {
> @@ -2277,7 +2278,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
> dev_name(&intf->dev), configuration,
> intf->cur_altsetting->desc.bInterfaceNumber);
> device_enable_async_suspend(&intf->dev);
> + claimant = intf->dev.driver;
> + if (claimant && !try_module_get(claimant->owner))
> + claimant = NULL;
> ret = device_add(&intf->dev);
> + if (!ret && claimant && !intf->dev.driver)
> + usb_driver_release_refused_claim(intf, claimant);
> + if (claimant)
> + module_put(claimant->owner);
> if (ret != 0) {
> dev_err(&dev->dev, "device_add(%s) --> %d\n",
> dev_name(&intf->dev), ret);
> diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
> index a9b37aeb515be..293340a48b6c5 100644
> --- a/drivers/usb/core/usb.h
> +++ b/drivers/usb/core/usb.h
> @@ -49,6 +49,8 @@ extern int usb_set_isoch_delay(struct usb_device *dev);
> extern int usb_get_bos_descriptor(struct usb_device *dev);
> extern void usb_release_bos_descriptor(struct usb_device *dev);
> extern int usb_set_configuration(struct usb_device *dev, int configuration);
> +void usb_driver_release_refused_claim(struct usb_interface *iface,
> + struct device_driver *claimant);
> extern int usb_choose_configuration(struct usb_device *udev);
> extern int usb_generic_driver_probe(struct usb_device *udev);
> extern void usb_generic_driver_disconnect(struct usb_device *udev);
>
> --
> 2.56.0
^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2026-11-05 17:24 UTC | newest]
Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 11:42 [PATCH 00/17] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-05 11:42 ` [PATCH 01/17] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
2026-11-05 11:42 ` [PATCH 02/17] Input: serio " Leon Romanovsky
2026-11-05 17:05 ` Dmitry Torokhov
2026-11-05 11:42 ` [PATCH 03/17] net: phy: Attach generic PHY " Leon Romanovsky
2026-11-05 14:50 ` Andrew Lunn
2026-11-05 15:33 ` Andrew Lunn
2026-11-05 11:42 ` [PATCH 04/17] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
2026-11-05 11:42 ` [PATCH 05/17] USB: Lock registered interfaces during " Leon Romanovsky
2026-11-05 17:24 ` Alan Stern
2026-11-05 11:42 ` [PATCH 06/17] PNP: Attach card devices through the driver core Leon Romanovsky
2026-11-05 11:42 ` [PATCH 07/17] cdx: Propagate DMA configuration errors Leon Romanovsky
2026-11-05 11:42 ` [PATCH 08/17] driver core: Allow userspace to detect EPROBE_DEFER when using sysfs drivers_probe Leon Romanovsky
2026-11-05 11:42 ` [PATCH 09/17] driver core: Introduce a generic device trust level Leon Romanovsky
2026-11-05 11:42 ` [PATCH 10/17] driver core: Expose device trust authorization through sysfs Leon Romanovsky
2026-11-05 13:10 ` Greg Kroah-Hartman
2026-11-05 13:27 ` Leon Romanovsky
2026-11-05 13:40 ` Greg Kroah-Hartman
2026-11-05 13:59 ` Robin Murphy
2026-11-05 14:03 ` Greg Kroah-Hartman
2026-11-05 14:17 ` Leon Romanovsky
2026-11-05 14:26 ` Robin Murphy
2026-11-05 15:45 ` Greg Kroah-Hartman
2026-11-05 14:06 ` Jason Gunthorpe
2026-11-05 14:43 ` Andrew Lunn
2026-11-05 14:59 ` Jason Gunthorpe
2026-11-05 15:09 ` Robin Murphy
2026-11-05 13:42 ` Robin Murphy
2026-11-05 17:02 ` Jason Gunthorpe
2026-11-05 11:42 ` [PATCH 11/17] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
2026-11-05 11:42 ` [PATCH 12/17] driver core: Accept device trust policy by driver name Leon Romanovsky
2026-11-05 11:42 ` [PATCH 13/17] driver core: Preserve trust commitment beyond driver binding Leon Romanovsky
2026-11-05 11:42 ` [PATCH 14/17] driver core: Add a global initial device trust policy Leon Romanovsky
2026-11-05 11:42 ` [PATCH 15/17] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
2026-11-05 11:42 ` [PATCH 16/17] driver core: Document trust admission and initrd authorization Leon Romanovsky
2026-11-05 11:42 ` [PATCH 17/17] PCI: Replace untrusted state with device trust Leon Romanovsky
2026-11-05 13:12 ` Greg Kroah-Hartman
2026-11-05 13:18 ` Leon Romanovsky
2026-11-05 13:26 ` Greg Kroah-Hartman
2026-11-05 14:03 ` Jason Gunthorpe
2026-11-05 15:43 ` Greg Kroah-Hartman
2026-11-05 16:26 ` Jason Gunthorpe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox