* [PATCH v2 01/19] Input: gameport - attach manual drivers through the driver core
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 02/19] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
` (17 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 [flat|nested] 22+ messages in thread* [PATCH v2 02/19] wifi: mac80211_hwsim: lock device for manual driver binding
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 01/19] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 03/19] USB: Lock registered interfaces during " Leon Romanovsky
` (16 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 [flat|nested] 22+ messages in thread* [PATCH v2 03/19] USB: Lock registered interfaces during manual driver binding
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 01/19] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 02/19] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 04/19] USB: Release interface claims refused during registration Leon Romanovsky
` (15 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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. The MediaTek btusb setup path instead holds the target
interface lock and would deadlock if the claim took it again.
Lock registered interfaces around binding. Change btusb setup to hold the
USB device lock with a PM reference and disconnect-aware acquisition,
because disconnect waits for this work while holding that lock. Publish
the claim state before releasing the lock.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/bluetooth/btusb_main.c | 42 ++++++++++++++++++++++++++++++------------
drivers/usb/core/driver.c | 5 ++++-
2 files changed, 34 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..ad5d9d5fc7598 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;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 04/19] USB: Release interface claims refused during registration
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (2 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 03/19] USB: Lock registered interfaces during " Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 05/19] PNP: Attach card devices through the driver core Leon Romanovsky
` (14 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 interface can be claimed by a sibling's probe before registration.
If its later binding fails, driver core clears the driver pointer while
USB retains the bound state and claimant data. Device registration still
succeeds, leaving the claimant unaware that its binding was refused.
Remember the claimant across device_add() and invoke USB unbinding when
driver core drops the claim. Hold the claimant's module while running its
disconnect callback, and clear the retained interface state.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/usb/core/driver.c | 18 ++++++++++++++++++
drivers/usb/core/message.c | 8 ++++++++
drivers/usb/core/usb.h | 2 ++
3 files changed, 28 insertions(+)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ad5d9d5fc7598..ee36eae34bdcc 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -652,6 +652,24 @@ 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);
+ 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] 22+ messages in thread* [PATCH v2 05/19] PNP: Attach card devices through the driver core
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (3 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 04/19] USB: Release interface claims refused during registration Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 06/19] cdx: Propagate DMA configuration errors Leon Romanovsky
` (13 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 [flat|nested] 22+ messages in thread* [PATCH v2 06/19] cdx: Propagate DMA configuration errors
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (4 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 05/19] PNP: Attach card devices through the driver core Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 07/19] driver core: Report deferred probes through sysfs drivers_probe Leon Romanovsky
` (12 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 [flat|nested] 22+ messages in thread* [PATCH v2 07/19] driver core: Report deferred probes through sysfs drivers_probe
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (5 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 06/19] cdx: Propagate DMA configuration errors Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 08/19] driver core: Add a compatibility API for manual driver attachment Leon Romanovsky
` (11 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 [flat|nested] 22+ messages in thread* [PATCH v2 08/19] driver core: Add a compatibility API for manual driver attachment
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (6 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 07/19] driver core: Report deferred probes through sysfs drivers_probe Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 09/19] Input: serio - use the compatibility driver attachment API Leon Romanovsky
` (10 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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>
Existing manual binding paths probe drivers under subsystem locks before
publishing their bindings. Normal attachment would add device locking to
these paths.
Add device_driver_attach_full() to preserve their serialization while
centralizing probe and binding cleanup. This compatibility API must not
be used in any new code: it prevents devices from using the device trust
authorization infrastructure. New code must use device_driver_attach().
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/dd.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-----
include/linux/device.h | 2 ++
2 files changed, 69 insertions(+), 7 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 52123251965de..0f56c3d825731 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -526,6 +526,21 @@ static void driver_sysfs_remove(struct device *dev)
}
}
+static int __device_bind_driver(struct device *dev)
+{
+ int ret;
+
+ ret = driver_sysfs_add(dev);
+ if (ret) {
+ bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
+ return ret;
+ }
+
+ device_links_force_bind(dev);
+ driver_bound(dev);
+ return 0;
+}
+
/**
* device_bind_driver - bind a driver to one device.
* @dev: device.
@@ -542,19 +557,64 @@ static void driver_sysfs_remove(struct device *dev)
* Callers should prefer to use device_driver_attach() instead.
*/
int device_bind_driver(struct device *dev)
+{
+ return __device_bind_driver(dev);
+}
+EXPORT_SYMBOL_GPL(device_bind_driver);
+
+/**
+ * device_driver_attach_full - attach a driver using subsystem serialization
+ * @drv: driver to attach
+ * @dev: device to attach it to
+ *
+ * This compatibility API must not be used in any new code. It prevents
+ * devices from using the device trust authorization infrastructure.
+ * New code must use device_driver_attach().
+ *
+ * Callers must serialize attachment and removal using their subsystem's
+ * existing locks. This function does not acquire the device or parent lock.
+ * It calls the bus or driver probe callback and publishes the binding without
+ * performing the normal driver-core DMA, pinctrl or power-domain setup.
+ *
+ * Return: 0 on success, or a negative error before binding.
+ */
+int device_driver_attach_full(const struct device_driver *drv, struct device *dev)
{
int ret;
- ret = driver_sysfs_add(dev);
- if (!ret) {
- device_links_force_bind(dev);
- driver_bound(dev);
- }
+ if (!device_is_registered(dev) || dev->p->dead)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ device_set_driver(dev, drv);
+ if (dev->bus->probe)
+ ret = dev->bus->probe(dev);
else
- bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
+ ret = drv->probe(dev);
+ if (ret)
+ goto clear_driver;
+
+ ret = __device_bind_driver(dev);
+ if (ret)
+ goto bus_remove;
+
+ return 0;
+
+bus_remove:
+ if (dev->bus->remove)
+ dev->bus->remove(dev);
+ else
+ drv->remove(dev);
+clear_driver:
+ devres_release_all(dev);
+ device_set_driver(dev, NULL);
+ dev_set_drvdata(dev, NULL);
+ if (ret == -EPROBE_DEFER)
+ return -EAGAIN;
return ret;
}
-EXPORT_SYMBOL_GPL(device_bind_driver);
+EXPORT_SYMBOL_GPL(device_driver_attach_full);
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
diff --git a/include/linux/device.h b/include/linux/device.h
index 429e39f647cf9..42e966ebaa2d3 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_attach_full(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 [flat|nested] 22+ messages in thread* [PATCH v2 09/19] Input: serio - use the compatibility driver attachment API
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (7 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 08/19] driver core: Add a compatibility API for manual driver attachment Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 10/19] net: phy: Use " Leon Romanovsky
` (9 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 invokes connect and publishes the binding under its
own subsystem serialization. Driver core needs an entry point before
connect to enforce device trust admission.
Use device_driver_attach_full() after the port ID check. Preserve the
existing locking and let the helper handle probe and binding cleanup.
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..b54072769ab68 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_full(&drv->driver, &serio->dev);
}
static void serio_find_driver(struct serio *serio)
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 10/19] net: phy: Use the compatibility driver attachment API
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (8 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 09/19] Input: serio - use the compatibility driver attachment API Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 13:45 ` Andrew Lunn
2026-11-09 7:33 ` [PATCH v2 11/19] driver core: Introduce a generic device trust level Leon Romanovsky
` (8 subsequent siblings)
18 siblings, 1 reply; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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.
Device trust admission needs to precede that probe without changing the
caller's serialization.
Use device_driver_attach_full() and leave driver ownership cleanup to the
helper. Mark generic drivers during probe so failure and removal callbacks
observe the right state.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/net/phy/phy_device.c | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d4508bcfbe99..5f0ca981644ec 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,10 @@ 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) {
+ err = device_driver_attach_full(drv, d);
+ if (err == -EBUSY)
+ irq = phydev->irq;
if (err)
goto error_module_put;
}
@@ -2071,8 +2072,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 +3812,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 +3950,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 +3979,7 @@ static int phy_remove(struct device *dev)
phy_device_reset(phydev, 1);
phydev->drv = NULL;
+ phydev->is_genphy_driven = 0;
return 0;
}
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 10/19] net: phy: Use the compatibility driver attachment API
2026-11-09 7:33 ` [PATCH v2 10/19] net: phy: Use " Leon Romanovsky
@ 2026-11-09 13:45 ` Andrew Lunn
0 siblings, 0 replies; 22+ messages in thread
From: Andrew Lunn @ 2026-11-09 13:45 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
> @@ -1956,11 +1958,10 @@ 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) {
> + err = device_driver_attach_full(drv, d);
> + if (err == -EBUSY)
> + irq = phydev->irq;
Just looking at this, it is not clear where the EBUSY can from? And
why set irq to phydev->irq?
Andrew
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 11/19] driver core: Introduce a generic device trust level
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (9 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 10/19] net: phy: Use " Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 12/19] driver core: Expose device trust authorization interfaces Leon Romanovsky
` (7 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 setup needs a common operating policy before driver probing or DMA
configuration. Userspace must be able to hold devices disabled until it
authorizes them.
Add generic trust admission with an immutable global boot policy. Restrict
the compatibility attachment API to explicit global full policy and full
operation, checking admission before probe. Keep the operating level
stable until teardown and retain normal locked admission for other callers.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/admin-guide/kernel-parameters.txt | 8 ++
drivers/base/Makefile | 2 +-
drivers/base/base.h | 16 +++
drivers/base/core.c | 3 +
drivers/base/dd.c | 38 ++++++-
drivers/base/trust.c | 132 ++++++++++++++++++++++++
include/linux/device.h | 8 ++
include/linux/device/bus.h | 10 ++
include/linux/device/trust.h | 44 ++++++++
9 files changed, 258 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0b3c140babd94..81a936dca9476 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1259,6 +1259,14 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ device_trust.default=
+ [KNL] Set the initial trust policy for all devices.
+ Format: { default | disabled | adversary | full }
+ If omitted or set to default, preserve bus policy.
+ Other values replace bus policy. The last valid value wins.
+ Full-only manual driver attachment requires full; other
+ global policies reject it before the probe callback.
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
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..ba8b5b3c51a65 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,7 @@ 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_state: policy 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 +130,7 @@ struct device_private {
const struct device_driver *async_driver;
char *deferred_probe_reason;
struct device *device;
+ enum device_trust_state trust_state;
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -166,6 +176,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);
+/* Full-only manual attachment uses the caller's subsystem serialization. */
+void device_trust_init_state(struct device *dev);
+int device_trust_prepare(struct device *dev, const struct device_driver *drv);
+int device_trust_prepare_full(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..6b94a163cda8d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3681,6 +3681,9 @@ 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__);
parent = get_device(dev->parent);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 0f56c3d825731..449986f741009 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -558,12 +558,24 @@ static int __device_bind_driver(struct device *dev)
*/
int device_bind_driver(struct device *dev)
{
- return __device_bind_driver(dev);
+ int ret;
+
+ if (dev->bus && dev->bus->trust_resolve)
+ return -EOPNOTSUPP;
+
+ ret = device_trust_prepare(dev, dev->driver);
+ if (ret)
+ return ret;
+
+ ret = __device_bind_driver(dev);
+ if (ret)
+ device_trust_clear(dev);
+ return ret;
}
EXPORT_SYMBOL_GPL(device_bind_driver);
/**
- * device_driver_attach_full - attach a driver using subsystem serialization
+ * device_driver_attach_full - attach a driver under the global full policy
* @drv: driver to attach
* @dev: device to attach it to
*
@@ -576,6 +588,12 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
* It calls the bus or driver probe callback and publishes the binding without
* performing the normal driver-core DMA, pinctrl or power-domain setup.
*
+ * Requires device_trust.default=full and a full operating level. Runtime
+ * policy writes are disabled in this mode; boot policy exceptions still
+ * apply. When driver boot rules exist, an explicit device boot rule must
+ * exclude them for @dev. Buses with a trust resolver must use
+ * device_driver_attach().
+ *
* Return: 0 on success, or a negative error before binding.
*/
int device_driver_attach_full(const struct device_driver *drv, struct device *dev)
@@ -587,6 +605,10 @@ int device_driver_attach_full(const struct device_driver *drv, struct device *de
if (dev->driver)
return -EBUSY;
+ ret = device_trust_prepare_full(dev, drv);
+ if (ret)
+ return ret;
+
device_set_driver(dev, drv);
if (dev->bus->probe)
ret = dev->bus->probe(dev);
@@ -610,6 +632,7 @@ int device_driver_attach_full(const struct device_driver *drv, struct device *de
devres_release_all(dev);
device_set_driver(dev, NULL);
dev_set_drvdata(dev, NULL);
+ WRITE_ONCE(dev->trust_level, DEVICE_TRUST_DISABLED);
if (ret == -EPROBE_DEFER)
return -EAGAIN;
return ret;
@@ -739,6 +762,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 */
@@ -805,6 +836,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;
}
@@ -831,6 +863,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;
}
@@ -1420,6 +1453,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..6a71abf10b3bb
--- /dev/null
+++ b/drivers/base/trust.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <kunit/visibility.h>
+
+#include "base.h"
+
+static enum device_trust_policy device_trust_default_policy __ro_after_init;
+
+void device_trust_init_state(struct device *dev)
+{
+ dev->p->trust_state = dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED ?
+ DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
+}
+
+static int __device_trust_prepare(struct device *dev,
+ const struct device_driver *drv, bool full_only)
+{
+ enum device_trust_policy policy;
+ enum device_trust_level level;
+ int ret;
+
+ policy = dev->trust_policy;
+
+ if (policy == DEVICE_TRUST_POLICY_DISABLED)
+ return -EPERM;
+ if (dev->bus && dev->bus->trust_resolve) {
+ ret = dev->bus->trust_resolve(dev, drv, policy, &level);
+ if (ret)
+ return ret;
+ } else {
+ level = policy == DEVICE_TRUST_POLICY_ADVERSARY ?
+ DEVICE_TRUST_ADVERSARY : DEVICE_TRUST_FULL;
+ }
+ if (level == DEVICE_TRUST_DISABLED)
+ return -EPERM;
+ if (full_only && level != DEVICE_TRUST_FULL)
+ return -EPERM;
+ if (dev->p->trust_state == DEVICE_TRUST_COMMITTED &&
+ dev->trust_committed_level != level)
+ return -EINVAL;
+
+ WRITE_ONCE(dev->trust_committed_level, level);
+ dev->p->trust_state = DEVICE_TRUST_COMMITTED;
+ WRITE_ONCE(dev->trust_level, level);
+ return 0;
+}
+
+int device_trust_prepare(struct device *dev, const struct device_driver *drv)
+{
+ device_lock_assert(dev);
+ return __device_trust_prepare(dev, drv, false);
+}
+
+int device_trust_prepare_full(struct device *dev, const struct device_driver *drv)
+{
+ if (device_trust_default_policy != DEVICE_TRUST_POLICY_FULL)
+ return -EPERM;
+
+ return __device_trust_prepare(dev, drv, true);
+}
+
+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);
+
+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 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);
+
+/**
+ * device_trust_init - Apply global boot policy before registration
+ * @dev: unregistered device with its initial policy set
+ */
+void device_trust_init(struct device *dev)
+{
+ if (device_trust_default_policy != DEVICE_TRUST_POLICY_DEFAULT)
+ dev->trust_policy = device_trust_default_policy;
+}
+EXPORT_SYMBOL_GPL(device_trust_init);
diff --git a/include/linux/device.h b/include/linux/device.h
index 42e966ebaa2d3..57d2e1b0d98ad 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,10 @@ 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().
+ * @trust_committed_level: Operating level retained across bind transactions.
* @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 +803,9 @@ struct device {
struct device_physical_location *physical_location;
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/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..b8f5514cacec3
--- /dev/null
+++ b/include/linux/device/trust.h
@@ -0,0 +1,44 @@
+/* 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,
+};
+
+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);
+
+#endif /* _DEVICE_TRUST_H_ */
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 12/19] driver core: Expose device trust authorization interfaces
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (10 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 11/19] driver core: Introduce a generic device trust level Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 13/19] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
` (6 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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. Buses with their own
authorization interface need to apply the same policy rules.
Expose requested policy and active trust through sysfs, and export
device_trust_set_policy() for bus authorization. Fix explicit authorization
or commitment for the device lifetime and serialize writes with probing.
Under the global full policy, reject changes and leave same-value writes
as no-ops so manual attachment needs no additional lock. Include the
attributes in device ownership changes.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 28 +++++++
Documentation/admin-guide/kernel-parameters.txt | 1 +
MAINTAINERS | 1 +
drivers/base/base.h | 2 +
drivers/base/core.c | 15 +++-
drivers/base/trust.c | 106 ++++++++++++++++++++++++
include/linux/device.h | 5 ++
include/linux/device/trust.h | 1 +
8 files changed, 158 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..d68113b0d6b08
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -0,0 +1,28 @@
+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 probe, and retains it through whole lifetime.
+
+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 driver from binding and informs the kernel
+ to provide containtment of the device. For example a PCIe device
+ should be unable to DMA to the memory. "adversary" informs the
+ kernel that the userspace wants to opt into any security
+ defences 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. "full" requests normal operation.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 81a936dca9476..2d046c79becd9 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1266,6 +1266,7 @@ Kernel parameters
Other values replace bus policy. The last valid value wins.
Full-only manual driver attachment requires full; other
global policies reject it before the probe callback.
+ With full, runtime trust policy changes are unavailable.
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
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 ba8b5b3c51a65..623444c7a3be8 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -176,6 +176,8 @@ 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;
+
/* Full-only manual attachment uses the caller's subsystem serialization. */
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 6b94a163cda8d..bd96d84c2ac2c 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)
@@ -4791,6 +4798,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 6a71abf10b3bb..86ce2751f30a3 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -5,6 +5,7 @@
#include <linux/export.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/sysfs.h>
#include <kunit/visibility.h>
#include "base.h"
@@ -105,6 +106,111 @@ static const char * const device_trust_policy_names[] = {
[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)]);
+}
+
+/**
+ * device_trust_set_policy - Request a trust policy for a device
+ * @dev: registered device
+ * @policy: requested policy
+ *
+ * Apply the same rules as a write to the device's trust_policy attribute.
+ * A bus can use this to translate its own authorization interface into
+ * device trust. Takes the device lock except when the global full policy
+ * makes runtime policy changes unavailable. In that mode, writing the current
+ * policy is a no-op.
+ *
+ * Return: 0 on success, -EBUSY if the policy can no longer change, -ENODEV
+ * if the device is being removed outside global full mode.
+ */
+int device_trust_set_policy(struct device *dev, enum device_trust_policy policy)
+{
+ int ret = 0;
+
+ if (device_trust_default_policy == DEVICE_TRUST_POLICY_FULL)
+ return policy == READ_ONCE(dev->trust_policy) ? 0 : -EBUSY;
+
+ 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_GPL(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,
+};
static int __init device_trust_default_setup(char *str)
{
int policy = match_string(device_trust_policy_names,
diff --git a/include/linux/device.h b/include/linux/device.h
index 57d2e1b0d98ad..e3e7457cd50f4 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -933,6 +933,11 @@ static inline bool device_iommu_mapped(struct device *dev)
return (dev->iommu_group != NULL);
}
+static inline bool device_policy_is_disabled(const struct device *dev)
+{
+ return READ_ONCE(dev->trust_policy) == DEVICE_TRUST_POLICY_DISABLED;
+}
+
/* Get the wakeup routines, which depend on struct device */
#include <linux/pm_wakeup.h>
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
index b8f5514cacec3..eb36970289e84 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -38,6 +38,7 @@ enum device_trust_policy {
};
void device_trust_init(struct device *dev);
+int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
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 [flat|nested] 22+ messages in thread* [PATCH v2 13/19] driver core: Accept device trust policy on the kernel command line
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (11 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 12/19] driver core: Expose device trust authorization interfaces Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 14/19] driver core: Accept device trust policy by driver name Leon Romanovsky
` (5 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 | 9 +++
drivers/base/base.h | 8 +++
drivers/base/trust.c | 93 +++++++++++++++++++++++--
drivers/pci/probe.c | 1 +
5 files changed, 109 insertions(+), 4 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index d68113b0d6b08..fd523bbe7bdc6 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -14,6 +14,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 2d046c79becd9..fbc0cd21bf09f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1259,6 +1259,15 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ device_trust= [KNL]
+ Set initial device trust policy before probing, including
+ for devices added after boot.
+ Format: <bus>:<device>=<policy>[,...]
+ Names match exactly as in /sys/bus/<bus>/devices/<device>.
+ Policies are default, disabled, adversary, and full.
+ Entries override bus policy and device_trust.default.
+ See Documentation/driver-api/driver-model/device-trust.rst.
+
device_trust.default=
[KNL] Set the initial trust policy for all devices.
Format: { default | disabled | adversary | full }
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 623444c7a3be8..59da537462a57 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -178,6 +178,14 @@ void device_release_driver_internal(struct device *dev, const struct device_driv
extern const struct attribute_group device_trust_attr_group;
+#if IS_ENABLED(CONFIG_KUNIT)
+int device_trust_parse_cmdline(const char *options, const struct device *dev,
+ enum device_trust_policy *policy);
+void device_trust_apply_boot_policy(struct device *dev,
+ enum device_trust_policy policy,
+ const char *options);
+#endif
+
/* Full-only manual attachment uses the caller's subsystem serialization. */
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/trust.c b/drivers/base/trust.c
index 86ce2751f30a3..f581857642493 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
@@ -211,6 +212,79 @@ 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)
+ 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);
+
static int __init device_trust_default_setup(char *str)
{
int policy = match_string(device_trust_policy_names,
@@ -226,13 +300,24 @@ static int __init device_trust_default_setup(char *str)
}
__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 (policy != DEVICE_TRUST_POLICY_DEFAULT)
+ dev->trust_policy = policy;
+ if (options)
+ device_trust_parse_cmdline(options, dev, &dev->trust_policy);
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_apply_boot_policy);
+
/**
- * device_trust_init - Apply global boot policy before registration
- * @dev: unregistered device with its initial policy set
+ * 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 (device_trust_default_policy != DEVICE_TRUST_POLICY_DEFAULT)
- dev->trust_policy = device_trust_default_policy;
+ device_trust_apply_boot_policy(dev, device_trust_default_policy,
+ device_trust_cmdline);
}
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;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 14/19] driver core: Accept device trust policy by driver name
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (12 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 13/19] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 15/19] driver core: Expose committed device trust level Leon Romanovsky
` (4 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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, while userspace arrives too
late to choose policy for built-in drivers.
Add driver-wide boot rules beneath explicit device policy and existing
authorization. Refuse full-only manual attachment when pending driver
rules could change policy outside subsystem locks; an explicit device
boot entry excludes those rules. Check unregistered USB claims against
policy without admitting the device before registration.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-devices-trust | 7 +-
Documentation/admin-guide/kernel-parameters.txt | 25 +++++--
drivers/base/base.h | 5 ++
drivers/base/trust.c | 99 +++++++++++++++++++++----
drivers/usb/core/driver.c | 4 +
drivers/usb/core/message.c | 2 +
include/linux/device/trust.h | 1 +
7 files changed, 119 insertions(+), 24 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index fd523bbe7bdc6..2d4bc96ed0d0f 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -14,8 +14,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.
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 fbc0cd21bf09f..6d8b427c69914 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1265,17 +1265,28 @@ Kernel parameters
Format: <bus>:<device>=<policy>[,...]
Names match exactly as in /sys/bus/<bus>/devices/<device>.
Policies are default, disabled, adversary, and full.
- Entries override bus policy and device_trust.default.
+ Entries override bus policy, device_trust.default, and
+ device_trust.driver.
See Documentation/driver-api/driver-model/device-trust.rst.
device_trust.default=
- [KNL] Set the initial trust policy for all devices.
+ [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 }
- If omitted or set to default, preserve bus policy.
- Other values replace bus policy. The last valid value wins.
- Full-only manual driver attachment requires full; other
- global policies reject it before the probe callback.
- With full, runtime trust policy changes are unavailable.
+ default preserves bus-initialized policy (the default).
+ Other values override bus-initialized policy before
+ probing, including for built-in drivers.
+ 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.
+ Rules remain available for hotplug and modules.
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 59da537462a57..f82403d0438f2 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -114,6 +114,7 @@ enum device_trust_state {
* @device: pointer back to the struct device that this structure is
* associated with.
* @trust_state: policy 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.
@@ -131,6 +132,7 @@ struct device_private {
char *deferred_probe_reason;
struct device *device;
enum device_trust_state trust_state;
+ u8 trust_policy_override:1;
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -181,6 +183,9 @@ extern const struct attribute_group device_trust_attr_group;
#if IS_ENABLED(CONFIG_KUNIT)
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);
void device_trust_apply_boot_policy(struct device *dev,
enum device_trust_policy policy,
const char *options);
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index f581857642493..2bec6825ef31e 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -13,6 +13,11 @@
static enum device_trust_policy device_trust_default_policy __ro_after_init;
+static bool device_trust_full_allowed(const struct device *dev);
+
+static void device_trust_init_driver_policy(struct device *dev,
+ const struct device_driver *drv);
+
void device_trust_init_state(struct device *dev)
{
dev->p->trust_state = dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED ?
@@ -26,8 +31,8 @@ static int __device_trust_prepare(struct device *dev,
enum device_trust_level level;
int ret;
+ 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) {
@@ -60,7 +65,7 @@ int device_trust_prepare(struct device *dev, const struct device_driver *drv)
int device_trust_prepare_full(struct device *dev, const struct device_driver *drv)
{
- if (device_trust_default_policy != DEVICE_TRUST_POLICY_FULL)
+ if (!device_trust_full_allowed(dev))
return -EPERM;
return __device_trust_prepare(dev, drv, true);
@@ -137,7 +142,7 @@ static ssize_t trust_policy_show(struct device *dev,
* A bus can use this to translate its own authorization interface into
* device trust. Takes the device lock except when the global full policy
* makes runtime policy changes unavailable. In that mode, writing the current
- * policy is a no-op.
+ * policy is a no-op and does not override a pending driver boot rule.
*
* Return: 0 on success, -EBUSY if the policy can no longer change, -ENODEV
* if the device is being removed outside global full mode.
@@ -155,7 +160,7 @@ int device_trust_set_policy(struct device *dev, enum device_trust_policy policy)
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)) {
@@ -180,7 +185,9 @@ int device_trust_set_policy(struct device *dev, enum device_trust_policy policy)
ret = -EBUSY;
goto unlock;
}
+set_policy:
WRITE_ONCE(dev->trust_policy, policy);
+ dev->p->trust_policy_override = true;
unlock:
device_unlock(dev);
return ret;
@@ -214,10 +221,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;
@@ -252,11 +260,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;
}
@@ -266,12 +274,59 @@ VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
entry = end + 1;
}
- if (matched)
+ if (matched && policy)
*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);
+static bool device_trust_full_allowed(const struct device *dev)
+{
+ if (device_trust_default_policy != DEVICE_TRUST_POLICY_FULL)
+ return false;
+
+ /* Driver rules can otherwise change policy outside subsystem locks. */
+ return !device_trust_driver_cmdline ||
+ (device_trust_cmdline &&
+ device_trust_parse_cmdline(device_trust_cmdline, dev, NULL) > 0);
+}
+
+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;
+
+ if (!options || 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->name,
+ 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);
+}
+
static int __init device_trust_setup(char *str)
{
if (device_trust_parse_cmdline(str, NULL, NULL) < 0) {
@@ -285,6 +340,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);
+
static int __init device_trust_default_setup(char *str)
{
int policy = match_string(device_trust_policy_names,
@@ -306,8 +373,10 @@ VISIBLE_IF_KUNIT void device_trust_apply_boot_policy(struct device *dev,
{
if (policy != DEVICE_TRUST_POLICY_DEFAULT)
dev->trust_policy = policy;
- if (options)
- device_trust_parse_cmdline(options, dev, &dev->trust_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);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ee36eae34bdcc..04b77691fbd7d 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -563,6 +563,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
+ if (!device_is_registered(dev) &&
+ dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED)
+ 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 eb36970289e84..9d6d387d01203 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
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 15/19] driver core: Expose committed device trust level
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (13 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 14/19] driver core: Accept device trust policy by driver name Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 16/19] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
` (3 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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
infrastructure needs access to the retained authorization.
Export device_get_committed_trust_level() to expose the existing lifetime
commitment independently of the active binding.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/trust.c | 13 +++++++++++++
include/linux/device/trust.h | 1 +
2 files changed, 14 insertions(+)
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 2bec6825ef31e..f79762e201178 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -93,6 +93,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/trust.h b/include/linux/device/trust.h
index 9d6d387d01203..decf5aaf2d46d 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -41,6 +41,7 @@ enum device_trust_policy {
void device_trust_init(struct device *dev);
int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
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 [flat|nested] 22+ messages in thread* [PATCH v2 16/19] driver core: Cover trust authorization and binding with KUnit
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (14 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 15/19] driver core: Expose committed device trust level Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 17/19] driver core: Document trust admission and initrd authorization Leon Romanovsky
` (2 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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. Failed setup must preserve commitment while clearing active
trust, and manual attachment must reject unsupported policy before probe.
Exercise authorization, deferred and asynchronous binding, supplier
cleanup, resolver errors and boot-rule precedence. Cover full-only manual
attachment under subsystem serialization, frozen runtime policy, boot
exceptions, failure cleanup and committed trust across retries.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/base/test/Makefile | 1 +
drivers/base/test/device-trust-test.c | 1720 +++++++++++++++++++++++++++++++++
drivers/base/trust.c | 1 +
3 files changed, 1722 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..b6ad3b0c1b536
--- /dev/null
+++ b/drivers/base/test/device-trust-test.c
@@ -0,0 +1,1720 @@
+// 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.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;
+ unsigned int release_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);
+ 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);
+ 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_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 ?: -EPERM);
+ 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;
+
+ 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]);
+ 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);
+ }
+ }
+ }
+ }
+}
+
+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);
+ 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_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_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_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_scan_continues_test),
+ {}
+};
+
+static bool device_trust_test_global_full(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);
+ return dev->trust_policy == DEVICE_TRUST_POLICY_FULL;
+}
+
+static struct device_trust_test_context *
+device_trust_full_test_register(struct kunit *test, const struct bus_type *bus,
+ struct device *parent)
+{
+ struct device_trust_test_context *ctx;
+ int ret;
+
+ ctx = device_trust_test_device_register_parent(test, bus,
+ DEVICE_TRUST_POLICY_DEFAULT,
+ DEVICE_TRUST_POLICY_DEFAULT, parent);
+ if (IS_ERR(ctx))
+ return ctx;
+ ctx->match_ret = 0;
+ ret = device_trust_test_driver_register(test, ctx);
+ if (ret)
+ return ERR_PTR(ret);
+ return ctx;
+}
+
+#if IS_BUILTIN(CONFIG_DM_KUNIT_TEST)
+static bool device_trust_full_test_driver_rules(struct kunit *test)
+{
+ char *args, *param, *value;
+ bool present = false;
+
+ args = kunit_kstrdup(test, saved_command_line, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, args);
+ while (*args) {
+ args = next_arg(args, ¶m, &value);
+ if (!strcmp(param, "device_trust.driver") && value &&
+ device_trust_parse_cmdline(value, NULL, NULL) >= 0)
+ present = true;
+ }
+ return present;
+}
+#endif
+
+static void device_trust_full_boot_policy_test(struct kunit *test)
+{
+#if IS_BUILTIN(CONFIG_DM_KUNIT_TEST)
+ struct device_trust_test_context *ctx;
+ enum device_trust_policy policy;
+ bool allowed;
+ int ret;
+
+ ctx = device_trust_full_test_register(test, &device_trust_test_plain_bus, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ policy = ctx->dev.trust_policy;
+ allowed = device_trust_test_global_full(test) &&
+ (policy == DEVICE_TRUST_POLICY_DEFAULT || policy == DEVICE_TRUST_POLICY_FULL) &&
+ (!device_trust_full_test_driver_rules(test) ||
+ ctx->dev.p->trust_policy_override);
+
+ ret = device_driver_attach_full(&ctx->drv, &ctx->dev);
+ KUNIT_ASSERT_EQ(test, ret, allowed ? 0 : -EPERM);
+ KUNIT_EXPECT_EQ(test, device_is_bound(&ctx->dev), allowed);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, allowed ? 1 : 0);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->dev.trust_policy, policy);
+ KUNIT_EXPECT_EQ(test, device_get_trust_level(&ctx->dev),
+ allowed ? DEVICE_TRUST_FULL : DEVICE_TRUST_DISABLED);
+ KUNIT_EXPECT_EQ(test, device_get_committed_trust_level(&ctx->dev),
+ allowed ? DEVICE_TRUST_FULL : DEVICE_TRUST_DISABLED);
+ if (!allowed)
+ KUNIT_EXPECT_PTR_EQ(test, ctx->dev.driver, NULL);
+#else
+ kunit_skip(test, "boot policy coverage requires built-in tests");
+#endif
+}
+
+static void device_trust_full_attach_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ struct device *parent;
+ int ret;
+
+ if (!device_trust_test_global_full(test))
+ kunit_skip(test, "requires device_trust.default=full");
+ 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_full_test_register(test, &device_trust_test_plain_bus, parent);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ device_lock(parent);
+ device_lock(&ctx->dev);
+ ret = device_driver_attach_full(&ctx->drv, &ctx->dev);
+ device_unlock(&ctx->dev);
+ device_unlock(parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_PTR_EQ(test, ctx->dev.driver, &ctx->drv);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, device_driver_attach_full(&ctx->drv, &ctx->dev), -EBUSY);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 1);
+
+ 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);
+ KUNIT_ASSERT_EQ(test, device_driver_attach_full(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 2);
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+}
+
+static void device_trust_full_test_release_resource(void *data)
+{
+ struct device_trust_test_context *ctx = data;
+
+ ctx->release_count++;
+}
+
+static int device_trust_full_test_probe(struct device *dev)
+{
+ struct device_trust_test_context *ctx = to_device_trust_test_context(dev);
+ int ret;
+
+ ret = devm_add_action(dev, device_trust_full_test_release_resource, ctx);
+ if (ret)
+ return ret;
+ dev_set_drvdata(dev, ctx);
+ return device_trust_test_probe(dev);
+}
+
+static void device_trust_full_probe_failure_test(struct kunit *test)
+{
+ static const int errors[] = { -EIO, -EPROBE_DEFER };
+ static const int expected[] = { -EIO, -EAGAIN };
+ struct device_trust_test_context *ctx;
+ int i;
+
+ if (!device_trust_test_global_full(test))
+ kunit_skip(test, "requires device_trust.default=full");
+ ctx = device_trust_full_test_register(test, &device_trust_test_plain_bus, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ ctx->drv.probe = device_trust_full_test_probe;
+
+ for (i = 0; i < ARRAY_SIZE(errors); i++) {
+ ctx->probe_ret = errors[i];
+ KUNIT_ASSERT_EQ(test, device_driver_attach_full(&ctx->drv, &ctx->dev),
+ expected[i]);
+ KUNIT_EXPECT_FALSE(test, device_is_bound(&ctx->dev));
+ KUNIT_EXPECT_PTR_EQ(test, ctx->dev.driver, NULL);
+ KUNIT_EXPECT_PTR_EQ(test, dev_get_drvdata(&ctx->dev), NULL);
+ KUNIT_EXPECT_EQ(test, ctx->probe_level, DEVICE_TRUST_FULL);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, i + 1);
+ KUNIT_EXPECT_EQ(test, ctx->release_count, i + 1);
+ 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);
+ }
+ KUNIT_EXPECT_EQ(test, ctx->configure_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->cleanup_count, 0);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 0);
+ ctx->probe_ret = 0;
+ KUNIT_ASSERT_EQ(test, device_driver_attach_full(&ctx->drv, &ctx->dev), 0);
+ KUNIT_EXPECT_PTR_EQ(test, dev_get_drvdata(&ctx->dev), ctx);
+ device_release_driver(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->release_count, ARRAY_SIZE(errors) + 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_count, 1);
+ KUNIT_EXPECT_EQ(test, ctx->remove_level, DEVICE_TRUST_FULL);
+}
+
+static void device_trust_full_policy_test(struct kunit *test)
+{
+ struct device_trust_test_context *ctx;
+ enum device_trust_state state;
+ bool override;
+
+ if (!device_trust_test_global_full(test))
+ kunit_skip(test, "requires device_trust.default=full");
+ ctx = device_trust_full_test_register(test, &device_trust_test_plain_bus, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ state = ctx->dev.p->trust_state;
+ override = ctx->dev.p->trust_policy_override;
+ device_lock(&ctx->dev);
+ device_trust_expect_policy_locked(test, &ctx->dev);
+ device_unlock(&ctx->dev);
+ KUNIT_EXPECT_EQ(test, ctx->dev.p->trust_state, state);
+ KUNIT_EXPECT_EQ(test, (bool)ctx->dev.p->trust_policy_override, override);
+ KUNIT_EXPECT_EQ(test, ctx->probe_count, 0);
+}
+
+static struct kunit_case device_trust_full_test_cases[] = {
+ KUNIT_CASE(device_trust_full_boot_policy_test),
+ KUNIT_CASE(device_trust_full_attach_test),
+ KUNIT_CASE(device_trust_full_probe_failure_test),
+ KUNIT_CASE(device_trust_full_policy_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,
+};
+
+static struct kunit_suite device_trust_full_test_suite = {
+ .name = "device-trust-full",
+ .suite_init = device_trust_test_suite_init,
+ .suite_exit = device_trust_test_suite_exit,
+ .test_cases = device_trust_full_test_cases,
+};
+
+kunit_test_suites(&device_trust_test_suite, &device_trust_full_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 f79762e201178..755e5a9fc5b00 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -23,6 +23,7 @@ void device_trust_init_state(struct device *dev)
dev->p->trust_state = dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED ?
DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_init_state);
static int __device_trust_prepare(struct device *dev,
const struct device_driver *drv, bool full_only)
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v2 17/19] driver core: Document trust admission and initrd authorization
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (15 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 16/19] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 18/19] PCI: Replace untrusted state with device trust Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 19/19] USB: Authorize devices and interfaces through " Leon Romanovsky
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 | 289 +++++++++++++++++++++
Documentation/driver-api/driver-model/index.rst | 1 +
2 files changed, 290 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..e23423b7153a9
--- /dev/null
+++ b/Documentation/driver-api/driver-model/device-trust.rst
@@ -0,0 +1,289 @@
+.. 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.
+
+With ``device_trust.default=full``, runtime policy changes are unavailable
+for every device, including devices with a boot policy exception. Writing
+a different policy returns ``-EBUSY``. Writing the current policy succeeds
+without changing state or suppressing a pending driver boot rule. Boot
+device and driver rules retain their precedence.
+
+In other global modes, runtime policy writes follow the internal states
+below; these 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. Except with ``device_trust.default=full``, 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.
+
+Manual attachment under global full policy
+==========================================
+
+This compatibility API **must not be used in any new code**. Its
+full-only restriction prevents devices from using the device trust
+authorization infrastructure. New code must use ``device_driver_attach()``.
+
+``device_driver_attach_full()`` supports manual attachment with the
+caller's existing subsystem serialization. It requires an explicit
+``device_trust.default=full`` boot option and a policy that resolves to
+full. It returns ``-EPERM`` in every other global mode, including when
+the option is absent or set to ``default``, before calling the driver.
+
+Per-device boot exceptions still apply: disabled and adversary reject
+attachment. If a ``device_trust.driver=`` list is configured, the device
+must also have an explicit ``device_trust=`` entry; otherwise this API
+returns ``-EPERM``. That entry prevents a concurrent driver rule from
+changing the policy.
+
+The caller serializes attachment and removal. The API takes neither the
+device nor parent lock, commits full before the probe callback, and
+publishes the binding after probe succeeds. It does not perform normal
+driver-core DMA, pinctrl, or power-domain setup. A failed probe or binding
+clears active trust but retains the full commitment.
+
+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 using a comma-separated
+list of ``<bus>:<device>=<policy>`` entries. 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 using a comma-separated
+list of ``<bus>:<driver>=<policy>`` entries. 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 or an existing
+authorization or commitment. A successful sysfs policy write also takes
+precedence, except for a no-op write with ``device_trust.default=full``.
+The rule 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. Userspace can authorize it unless the global
+mode is full. Before any rule applies, a driver with no matching rule may
+still bind through normal attachment. Use global or per-device disabled
+to block every driver.
+
+An initrd can hold devices with ``device_trust.default=disabled`` and use
+boot exceptions for devices and suppliers needed to reach the initrd and
+perform attestation. These exceptions allow operation before fresh initrd
+attestation. Devices without an exception 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. Driver rules apply later, when a candidate driver
+is known.
+
+All boot rules remain available for newly discovered devices. Initialize
+boot policy before device registration. These options are not runtime
+controls or proof of hardware identity.
+Disabled blocks early binding and, outside global full mode, 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.
+
+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. If a bus uses trust policy during DMA
+configuration, select the policy before that configuration takes place.
+A setup failure after commitment leaves the chosen mode fixed; it cannot
+fall back to full.
+
+Drivers that manage DMA themselves must provide their own containment.
+Drivers that do not use DMA need no domain.
+
+Initrd attestation sequence
+===========================
+
+For a device that uses normal driver attachment:
+
+1. Boot with ``device_trust.default=disabled`` to hold all devices, or use
+ a per-device rule to hold an individual device. Add exceptions needed
+ to reach the initrd and provide DMA containment while waiting. 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 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, encryption, verified
+identity, memory privacy, or DMA addressability. Keep these properties
+independent of the requested policy and active trust level.
+
+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 [flat|nested] 22+ messages in thread* [PATCH v2 18/19] PCI: Replace untrusted state with device trust
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (16 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 17/19] driver core: Document trust admission and initrd authorization Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
2026-11-09 13:53 ` Robin Murphy
2026-11-09 7:33 ` [PATCH v2 19/19] USB: Authorize devices and interfaces through " Leon Romanovsky
18 siblings, 1 reply; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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 | 22 +++++++++++-----------
drivers/pci/quirks.c | 7 ++++---
include/linux/device.h | 5 +++++
include/linux/pci.h | 30 ++++++++++++++++++++++--------
10 files changed, 67 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..607569c31b5b9 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1738,24 +1738,24 @@ 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 (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 +2082,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 e3e7457cd50f4..770c3040fd2b9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -933,6 +933,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;
+}
+
static inline bool device_policy_is_disabled(const struct device *dev)
{
return READ_ONCE(dev->trust_policy) == DEVICE_TRUST_POLICY_DISABLED;
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 [flat|nested] 22+ messages in thread* Re: [PATCH v2 18/19] PCI: Replace untrusted state with device trust
2026-11-09 7:33 ` [PATCH v2 18/19] PCI: Replace untrusted state with device trust Leon Romanovsky
@ 2026-11-09 13:53 ` Robin Murphy
0 siblings, 0 replies; 22+ messages in thread
From: Robin Murphy @ 2026-11-09 13:53 UTC (permalink / raw)
To: Leon Romanovsky, 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,
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
On 09/11/2026 7:33 am, Leon Romanovsky wrote:
[...]
> 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))) {
I think this should be the generic device_is_adversarial(gdev->dev) now
- in fact probably all the dev_is_pci()/to_pci_dev() stanzas aren't
conceptually PCI-specific, just an artefact of the current "untrusted"
flag being so.
> /*
> - * 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;
> }
> }
>
[...]
> @@ -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;
> +}
Unless I've wildly misunderstood, I'm not sure this is right - if the
user has set DEVICE_TRUST_POLICY_DISABLED to explicitly override the
bus-default behaviour, but that leads to the IOMMU layer special-casing
all PCI devices as adversarial and thus force-*enabling* all their
trust-model restrictions, isn't that pretty much the complete opposite
of what was asked for?
Thanks,
Robin.
> +
> +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)
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 19/19] USB: Authorize devices and interfaces through device trust
2026-11-09 7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
` (17 preceding siblings ...)
2026-11-09 7:33 ` [PATCH v2 18/19] PCI: Replace untrusted state with device trust Leon Romanovsky
@ 2026-11-09 7:33 ` Leon Romanovsky
18 siblings, 0 replies; 22+ messages in thread
From: Leon Romanovsky @ 2026-11-09 7:33 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>
USB authorization is a second gate next to device trust. An unauthorized
device still binds the generic driver, so trust admission never sees it,
and boot policy cannot authorize USB devices or interfaces.
Make the authorized_default and interface_authorized_default settings the
initial trust policy, so held devices and interfaces start disabled and do
not bind. Writing 1 to authorized requests full trust; for a device it
also binds the generic driver, which configures it. Writing 0 still stops
it and leaves the committed trust level in place.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Documentation/ABI/testing/sysfs-bus-usb | 11 +++++++++++
Documentation/usb/authorization.rst | 20 ++++++++++++++++++++
drivers/usb/core/driver.c | 10 +++-------
drivers/usb/core/generic.c | 2 +-
drivers/usb/core/hub.c | 17 ++++++++++++++++-
drivers/usb/core/message.c | 24 +++++++++++++++++++-----
drivers/usb/core/sysfs.c | 9 ++++++---
drivers/usb/core/trace.h | 2 +-
drivers/usb/core/usb.c | 5 ++++-
drivers/usb/core/usb.h | 2 +-
include/linux/usb.h | 29 ++++++++++++++++++++++-------
11 files changed, 104 insertions(+), 27 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 5213117fd957e..ca66b6a10c054 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -12,6 +12,11 @@ Description:
A deauthorized interface cannot be probed or claimed.
+ An interface that is not authorized by default starts with
+ the "disabled" trust policy (see sysfs-devices-trust).
+ Writing 1 then requests the "full" policy. Deauthorizing
+ does not change the committed trust level.
+
What: /sys/bus/usb/devices/usbX/interface_authorized_default
Date: August 2015
Description:
@@ -28,6 +33,12 @@ Description:
drivers, non-authorized one are not. By default, wired
USB devices are authorized.
+ A device that is not authorized by default starts with the
+ "disabled" trust policy (see sysfs-devices-trust). Writing 1
+ requests the "full" policy if the policy is disabled, then
+ binds and configures the device. Writing 0 deconfigures the
+ device without changing its committed trust level.
+
What: /sys/bus/usb/drivers/.../new_id
Date: October 2011
Contact: linux-usb@vger.kernel.org
diff --git a/Documentation/usb/authorization.rst b/Documentation/usb/authorization.rst
index 150a14970e951..2e07831876fed 100644
--- a/Documentation/usb/authorization.rst
+++ b/Documentation/usb/authorization.rst
@@ -92,6 +92,22 @@ can fake descriptors and device info. Don't trust that. You are
welcome.
+Device trust
+------------
+
+Authorization is expressed through device trust, see
+Documentation/driver-api/driver-model/device-trust.rst. A device that the
+authorized_default setting does not authorize starts with the "disabled"
+trust policy and reads 0 in "authorized". Boot options such as
+``device_trust=usb:1-2=full`` replace this initial policy.
+
+Writing 1 to "authorized" for such a device requests the "full" policy and
+binds the device, which configures it. To operate it as an adversary,
+write "adversary" to its "trust_policy" attribute before writing 1. The
+choice is fixed for the life of the device: writing 0 deconfigures it, and
+a later 1 configures it again at the same trust level.
+
+
Interface authorization
-----------------------
@@ -120,6 +136,10 @@ Deny interfaces per default::
Per default the interface_authorized_default bit is 1.
So all interfaces would authorized per default.
+As with devices, an interface that is not authorized by default starts
+with the "disabled" trust policy, and writing 1 to its "authorized"
+attribute requests the "full" policy.
+
Note:
If a deauthorized interface will be authorized so the driver probing must
be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 04b77691fbd7d..2ebd05d3855f4 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -334,10 +334,10 @@ static int usb_probe_interface(struct device *dev)
if (usb_device_is_owned(udev))
return error;
- if (udev->authorized == 0) {
+ if (!usb_device_authorized(udev)) {
dev_info(&intf->dev, "Device is not authorized for usage\n");
return error;
- } else if (intf->authorized == 0) {
+ } else if (!usb_interface_authorized(intf)) {
dev_info(&intf->dev, "Interface %d is not authorized for usage\n",
intf->altsetting->desc.bInterfaceNumber);
return error;
@@ -560,13 +560,9 @@ int usb_driver_claim_interface(struct usb_driver *driver,
return -EBUSY;
/* reject claim if interface is not authorized */
- if (!iface->authorized)
+ if (!usb_interface_authorized(iface))
return -ENODEV;
- if (!device_is_registered(dev) &&
- dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED)
- return -EPERM;
-
dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index a48994e11ef3d..bcfc01eefd09c 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -242,7 +242,7 @@ int usb_generic_driver_probe(struct usb_device *udev)
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
- if (udev->authorized == 0)
+ if (!usb_device_authorized(udev))
dev_info(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 199d3f535a891..086b8db36f51a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2770,7 +2770,7 @@ int usb_new_device(struct usb_device *udev)
int usb_deauthorize_device(struct usb_device *usb_dev)
{
usb_lock_device(usb_dev);
- if (usb_dev->authorized == 0)
+ if (!usb_device_authorized(usb_dev))
goto out_unauthorized;
usb_dev->authorized = 0;
@@ -2786,7 +2786,22 @@ int usb_authorize_device(struct usb_device *usb_dev)
{
int result = 0, c;
+ /* Authorizing a device that its trust policy holds back admits it */
+ if (device_policy_is_disabled(&usb_dev->dev)) {
+ result = device_trust_set_policy(&usb_dev->dev,
+ DEVICE_TRUST_POLICY_FULL);
+ if (result)
+ return result;
+ }
+
usb_lock_device(usb_dev);
+ if (!usb_dev->dev.driver) {
+ /* Binding the generic driver configures the device */
+ usb_dev->authorized = 1;
+ usb_unlock_device(usb_dev);
+ result = device_attach(&usb_dev->dev);
+ return result < 0 ? result : 0;
+ }
if (usb_dev->authorized == 1)
goto out_authorized;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 2d75b85b2d139..ddede900dace8 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1857,7 +1857,7 @@ void usb_deauthorize_interface(struct usb_interface *intf)
device_lock(dev->parent);
- if (intf->authorized) {
+ if (usb_interface_authorized(intf)) {
device_lock(dev);
intf->authorized = 0;
device_unlock(dev);
@@ -1872,16 +1872,27 @@ void usb_deauthorize_interface(struct usb_interface *intf)
* usb_authorize_interface - authorize an USB interface
*
* @intf: USB interface structure
+ *
+ * Return: 0 on success, or the error of the trust policy request.
*/
-void usb_authorize_interface(struct usb_interface *intf)
+int usb_authorize_interface(struct usb_interface *intf)
{
struct device *dev = &intf->dev;
+ int ret;
+
+ /* Authorizing an interface that its trust policy holds back admits it */
+ if (device_policy_is_disabled(dev)) {
+ ret = device_trust_set_policy(dev, DEVICE_TRUST_POLICY_FULL);
+ if (ret)
+ return ret;
+ }
if (!intf->authorized) {
device_lock(dev);
intf->authorized = 1; /* authorize interface */
device_unlock(dev);
}
+ return 0;
}
static int usb_if_uevent(const struct device *dev, struct kobj_uevent_env *env)
@@ -2043,7 +2054,7 @@ EXPORT_SYMBOL_GPL(usb_set_wireless_status);
* channels are available independently; and choosing between open
* standard device protocols (like CDC) or proprietary ones.
*
- * Note that a non-authorized device (dev->authorized == 0) will only
+ * Note that a non-authorized device (see usb_device_authorized()) will only
* be put in unconfigured mode.
*
* Note that USB has an additional level of device configurability,
@@ -2068,7 +2079,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int n, nintf;
- if (dev->authorized == 0 || configuration == -1)
+ if (!usb_device_authorized(dev) || configuration == -1)
configuration = 0;
else {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
@@ -2173,7 +2184,10 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
- intf->authorized = !!HCD_INTF_AUTHORIZED(hcd);
+ /* The authorization default is the interface's initial trust policy */
+ if (!HCD_INTF_AUTHORIZED(hcd))
+ intf->dev.trust_policy = DEVICE_TRUST_POLICY_DISABLED;
+ intf->authorized = 1;
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index d22dc78457d79..189fb917b81ee 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -728,7 +728,7 @@ static ssize_t authorized_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *usb_dev = to_usb_device(dev);
- return sysfs_emit(buf, "%u\n", usb_dev->authorized);
+ return sysfs_emit(buf, "%u\n", usb_device_authorized(usb_dev));
}
/*
@@ -1213,7 +1213,7 @@ static ssize_t interface_authorized_show(struct device *dev,
{
struct usb_interface *intf = to_usb_interface(dev);
- return sysfs_emit(buf, "%u\n", intf->authorized);
+ return sysfs_emit(buf, "%u\n", usb_interface_authorized(intf));
}
/*
@@ -1225,12 +1225,15 @@ static ssize_t interface_authorized_store(struct device *dev,
struct usb_interface *intf = to_usb_interface(dev);
bool val;
struct kernfs_node *kn;
+ int ret;
if (kstrtobool(buf, &val) != 0)
return -EINVAL;
if (val) {
- usb_authorize_interface(intf);
+ ret = usb_authorize_interface(intf);
+ if (ret)
+ return ret;
} else {
/*
* Prevent deadlock if another process is concurrently
diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h
index 903e57dc273af..42f9cfcd8b0f2 100644
--- a/drivers/usb/core/trace.h
+++ b/drivers/usb/core/trace.h
@@ -27,7 +27,7 @@ DECLARE_EVENT_CLASS(usb_core_log_usb_device,
__entry->speed = udev->speed;
__entry->state = udev->state;
__entry->bus_mA = udev->bus_mA;
- __entry->authorized = udev->authorized;
+ __entry->authorized = usb_device_authorized(udev);
),
TP_printk("usb %s speed %s state %s %dmA [%s]",
__get_str(name),
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2559a180fdeec..8b427209bcc02 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -747,7 +747,10 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->active_duration = -jiffies;
#endif
- dev->authorized = usb_dev_authorized(dev, usb_hcd);
+ /* The authorization default is the device's initial trust policy */
+ if (!usb_dev_authorized(dev, usb_hcd))
+ dev->dev.trust_policy = DEVICE_TRUST_POLICY_DISABLED;
+ dev->authorized = 1;
trace_usb_alloc_dev(dev);
return dev;
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 293340a48b6c5..00771270db523 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -34,7 +34,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *);
extern int usb_authorize_device(struct usb_device *);
extern void usb_deauthorize_interface(struct usb_interface *);
-extern void usb_authorize_interface(struct usb_interface *);
+int usb_authorize_interface(struct usb_interface *intf);
extern void usb_detect_quirks(struct usb_device *udev);
extern void usb_detect_interface_quirks(struct usb_device *udev);
extern void usb_release_quirk_list(void);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 825f03b290670..2ead3234b1301 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -204,8 +204,8 @@ enum usb_wireless_status {
* has been deferred.
* @needs_binding: flag set when the driver should be re-probed or unbound
* following a reset or suspend operation it doesn't support.
- * @authorized: This allows to (de)authorize individual interfaces instead
- * a whole device in contrast to the device authorization.
+ * @authorized: user space has not deauthorized the interface. Whether it may
+ * be used also depends on its trust policy, see usb_interface_authorized().
* @wireless_status: if the USB device uses a receiver/emitter combo, whether
* the emitter is connected.
* @wireless_status_work: Used for scheduling wireless status changes
@@ -276,6 +276,15 @@ struct usb_interface {
#define to_usb_interface(__dev) container_of_const(__dev, struct usb_interface, dev)
+/*
+ * An interface is authorized when its trust policy lets it bind and user space
+ * has not deauthorized it since.
+ */
+static inline bool usb_interface_authorized(const struct usb_interface *intf)
+{
+ return intf->authorized && !device_policy_is_disabled(&intf->dev);
+}
+
static inline void *usb_get_intfdata(struct usb_interface *intf)
{
return dev_get_drvdata(&intf->dev);
@@ -609,11 +618,8 @@ struct usb3_lpm_parameters {
* @persist_enabled: USB_PERSIST enabled for this device
* @reset_in_progress: the device is being reset
* @have_langid: whether string_langid is valid
- * @authorized: policy has said we can use it;
- * (user space) policy determines if we authorize this device to be
- * used or not. By default, wired USB devices are authorized.
- * WUSB devices are not, until we authorize them from user space.
- * FIXME -- complete doc
+ * @authorized: user space has not deauthorized the device. Whether the device
+ * may be used also depends on its trust policy, see usb_device_authorized().
* @authenticated: Crypto authentication passed
* @tunnel_mode: Connection native or tunneled over USB4
* @usb4_link: device link to the USB4 host interface
@@ -747,6 +753,15 @@ struct usb_device {
#define to_usb_device(__dev) container_of_const(__dev, struct usb_device, dev)
+/*
+ * A device is authorized when its trust policy lets it bind and user space has
+ * not deauthorized it since.
+ */
+static inline bool usb_device_authorized(const struct usb_device *udev)
+{
+ return udev->authorized && !device_policy_is_disabled(&udev->dev);
+}
+
static inline struct usb_device *__intf_to_usbdev(struct usb_interface *intf)
{
return to_usb_device(intf->dev.parent);
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread