[PATCH 12/17] driver core: Accept device trust policy by driver name
From: Leon Romanovsky
Date: Thu Nov 05 2026 - 07:00:10 EST
From: Leon Romanovsky <leonro@xxxxxxxxxx>
Device addresses can change between boots and cannot conveniently name
every device handled by a driver, including devices discovered later.
Userspace arrives too late to choose the initial policy for built-in
drivers.
Administrators need a driver-wide boot default without weakening an
explicit device policy, userspace authorization, or a device's lifetime
trust commitment.
A driver may claim a USB interface before it is registered, which skips
admission. Let USB refuse such a claim early when the interface's own
policy is disabled, and leave driver rules to admission at registration.
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
---
Documentation/ABI/testing/sysfs-devices-trust | 5 +
Documentation/admin-guide/kernel-parameters.txt | 22 +++++
drivers/base/base.h | 9 ++
drivers/base/trust.c | 124 +++++++++++++++++++++---
drivers/usb/core/driver.c | 5 +
drivers/usb/core/message.c | 2 +
include/linux/device/trust.h | 3 +
7 files changed, 157 insertions(+), 13 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-trust b/Documentation/ABI/testing/sysfs-devices-trust
index 87d3410f6bd90..c0ce905b625d0 100644
--- a/Documentation/ABI/testing/sysfs-devices-trust
+++ b/Documentation/ABI/testing/sysfs-devices-trust
@@ -19,6 +19,11 @@ Description:
to an active trust level before the device is probed.
A matching device_trust= kernel command-line entry overrides
the initial requested policy before registration.
+ Otherwise, device_trust.driver= can initialize policy on the
+ first binding attempt with a matching driver rule. A successful
+ policy write, including the existing value, suppresses this
+ deferred initialization. Driver rules do not change an existing
+ authorization or commitment and apply at most once per device.
The values are "default", "disabled", "adversary", and
"full". "default" lets the bus select the active trust level.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index ed307427a5ca4..b1d6999a5befa 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1269,6 +1269,7 @@ Kernel parameters
Policies are default, disabled, adversary, and full.
Entries override bus-initialized requested policy;
unlisted devices retain their existing policy.
+ Per-device entries take precedence over device_trust.driver.
The last entry for a device wins. If specified more than
once, the last valid list replaces all earlier lists.
Malformed lists are warned about and ignored in full.
@@ -1284,6 +1285,27 @@ Kernel parameters
device_trust=pci:0000:01:00.0=adversary,pci:0000:02:00.0=disabled
See Documentation/driver-api/driver-model/device-trust.rst.
+ device_trust.driver=
+ [KNL] Set initial device trust policy by driver name,
+ including for drivers built into the kernel.
+ Format: <bus>:<driver>=<policy>[,...]
+ Names match exactly as in /sys/bus/<bus>/drivers/<driver>.
+ Policies are default, disabled, adversary, and full.
+ The name restrictions and repeated-list rules are the same
+ as device_trust=. Module aliases and wildcards are not
+ expanded. Rules remain available for hotplug and modules.
+ The first binding attempt with a matching rule applies it
+ once, before trust resolution and driver or DMA setup.
+ A per-device device_trust= entry or any successful write
+ to trust_policy, even of its current value, takes precedence.
+ Rules never replace an existing authorization or commitment.
+ A rejected disabled attempt leaves the device disabled until
+ userspace authorizes it, including across driver changes.
+ Driver rules apply after bus discovery and do not reconfigure
+ IOMMU domains. Adversary operation needs suitable existing
+ DMA configuration; see the device trust documentation.
+ Example: device_trust.driver=pci:e1000=disabled,pci:nvme=full
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a286f48a69747..b4441242496f7 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -116,6 +116,7 @@ enum device_trust_state {
* @trust_committed_level: operating level retained across bind transactions
* @trust_state: policy admission state
* @trust_initialized: bus and boot policy have initialized admission state
+ * @trust_policy_override: explicit policy prevents deferred driver boot policy
* @dead: This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
@@ -135,6 +136,7 @@ struct device_private {
enum device_trust_level trust_committed_level;
enum device_trust_state trust_state;
u8 trust_initialized:1;
+ u8 trust_policy_override:1;
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -186,6 +188,13 @@ extern const struct attribute_group device_trust_attr_group;
int device_trust_set_policy(struct device *dev, enum device_trust_policy policy);
int device_trust_parse_cmdline(const char *options, const struct device *dev,
enum device_trust_policy *policy);
+void device_trust_apply_driver_policy(struct device *dev,
+ const struct device_driver *drv,
+ const char *options);
+bool device_trust_claim_allowed(const struct device *dev,
+ const struct device_driver *drv,
+ const char *device_options,
+ const char *driver_options);
#endif
/* All active trust level transitions are serialized by the device lock. */
diff --git a/drivers/base/trust.c b/drivers/base/trust.c
index 246d2516fb3af..b38f69e440eae 100644
--- a/drivers/base/trust.c
+++ b/drivers/base/trust.c
@@ -11,6 +11,9 @@
#include "base.h"
+static void device_trust_init_driver_policy(struct device *dev,
+ const struct device_driver *drv);
+
void device_trust_init_state(struct device *dev)
{
if (dev->p->trust_initialized)
@@ -64,10 +67,11 @@ int device_trust_prepare(struct device *dev, const struct device_driver *drv)
int ret;
device_lock_assert(dev);
- policy = dev->trust_policy;
if (WARN_ON_ONCE(READ_ONCE(dev->trust_level) != DEVICE_TRUST_DISABLED))
return -EBUSY;
+ device_trust_init_driver_policy(dev, drv);
+ policy = dev->trust_policy;
if (policy == DEVICE_TRUST_POLICY_DISABLED)
return -EPERM;
if (dev->bus && dev->bus->trust_resolve)
@@ -170,7 +174,7 @@ VISIBLE_IF_KUNIT int device_trust_set_policy(struct device *dev,
goto unlock;
}
if (policy == dev->trust_policy)
- goto unlock;
+ goto set_policy;
/* A driver that claimed the device before registration was not admitted. */
if (READ_ONCE(dev->driver)) {
@@ -195,7 +199,9 @@ VISIBLE_IF_KUNIT int device_trust_set_policy(struct device *dev,
ret = -EBUSY;
goto unlock;
}
+set_policy:
WRITE_ONCE(dev->trust_policy, policy);
+ dev->p->trust_policy_override = true;
unlock:
device_unlock(dev);
return ret;
@@ -229,10 +235,11 @@ const struct attribute_group device_trust_attr_group = {
};
static const char *device_trust_cmdline __ro_after_init;
+static const char *device_trust_driver_cmdline __ro_after_init;
-VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
- const struct device *dev,
- enum device_trust_policy *policy)
+static int device_trust_parse_policy(const char *options, const char *target_bus,
+ const char *target_name,
+ enum device_trust_policy *policy)
{
enum device_trust_policy selected = DEVICE_TRUST_POLICY_DEFAULT;
const char *entry = options;
@@ -267,11 +274,11 @@ VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
if (i == ARRAY_SIZE(device_trust_policy_names))
return -EINVAL;
- if (dev && dev->bus && dev_name(dev) &&
- strlen(dev->bus->name) == bus_end - entry &&
- !strncmp(dev->bus->name, entry, bus_end - entry) &&
- strlen(dev_name(dev)) == level - name &&
- !strncmp(dev_name(dev), name, level - name)) {
+ if (target_bus && target_name &&
+ strlen(target_bus) == bus_end - entry &&
+ !strncmp(target_bus, entry, bus_end - entry) &&
+ strlen(target_name) == level - name &&
+ !strncmp(target_name, name, level - name)) {
selected = i;
matched = true;
}
@@ -285,8 +292,86 @@ VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
*policy = selected;
return matched;
}
+
+VISIBLE_IF_KUNIT int device_trust_parse_cmdline(const char *options,
+ const struct device *dev,
+ enum device_trust_policy *policy)
+{
+ return device_trust_parse_policy(options, dev && dev->bus ? dev->bus->name : NULL,
+ dev ? dev_name(dev) : NULL, policy);
+}
EXPORT_SYMBOL_IF_KUNIT(device_trust_parse_cmdline);
+VISIBLE_IF_KUNIT void device_trust_apply_driver_policy(struct device *dev,
+ const struct device_driver *drv,
+ const char *options)
+{
+ enum device_trust_policy policy;
+
+ device_lock_assert(dev);
+ if (!options || !drv || dev->p->trust_policy_override ||
+ (dev->p->trust_state != DEVICE_TRUST_SELECTABLE &&
+ dev->p->trust_state != DEVICE_TRUST_WAITING))
+ return;
+
+ if (device_trust_parse_policy(options, drv->bus ? drv->bus->name : NULL,
+ drv->name, &policy) <= 0)
+ return;
+
+ WRITE_ONCE(dev->trust_policy, policy);
+ dev->p->trust_state = policy == DEVICE_TRUST_POLICY_DISABLED ?
+ DEVICE_TRUST_WAITING : DEVICE_TRUST_SELECTABLE;
+ dev->p->trust_policy_override = true;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_apply_driver_policy);
+
+static void device_trust_init_driver_policy(struct device *dev,
+ const struct device_driver *drv)
+{
+ device_trust_apply_driver_policy(dev, drv, device_trust_driver_cmdline);
+}
+
+/*
+ * A matching driver rule is applied, together with its admission state, when
+ * the device is registered, so only the device's own policy rejects a claim.
+ */
+VISIBLE_IF_KUNIT bool device_trust_claim_allowed(const struct device *dev,
+ const struct device_driver *drv,
+ const char *device_options,
+ const char *driver_options)
+{
+ enum device_trust_policy policy = READ_ONCE(dev->trust_policy);
+ bool device_rule = device_options &&
+ device_trust_parse_cmdline(device_options, dev, &policy) > 0;
+
+ if (!device_rule && driver_options && drv &&
+ device_trust_parse_policy(driver_options,
+ drv->bus ? drv->bus->name : NULL,
+ drv->name, &policy) > 0)
+ return true;
+
+ return policy != DEVICE_TRUST_POLICY_DISABLED;
+}
+EXPORT_SYMBOL_IF_KUNIT(device_trust_claim_allowed);
+
+/**
+ * device_trust_may_claim - Test whether a driver may claim an unregistered device
+ * @dev: device with its bus, name, and boot policy set
+ * @drv: driver that claims the device
+ *
+ * A driver can claim a device before it is registered, which skips the bind
+ * attempt that admits it. Reject a claim that the device's own policy forbids.
+ *
+ * Return: False if the policy of @dev rejects the claim.
+ */
+bool device_trust_may_claim(const struct device *dev,
+ const struct device_driver *drv)
+{
+ return device_trust_claim_allowed(dev, drv, device_trust_cmdline,
+ device_trust_driver_cmdline);
+}
+EXPORT_SYMBOL_GPL(device_trust_may_claim);
+
static int __init device_trust_setup(char *str)
{
if (device_trust_parse_cmdline(str, NULL, NULL) < 0) {
@@ -300,6 +385,18 @@ static int __init device_trust_setup(char *str)
}
__setup("device_trust=", device_trust_setup);
+static int __init device_trust_driver_setup(char *str)
+{
+ if (device_trust_parse_policy(str, NULL, NULL, NULL) < 0) {
+ pr_warn("device_trust: ignoring invalid driver policy list '%s'\n", str);
+ return 1;
+ }
+
+ device_trust_driver_cmdline = str;
+ return 1;
+}
+__setup("device_trust.driver=", device_trust_driver_setup);
+
/**
* device_trust_init - Apply boot policy before registration or bus policy use
* @dev: unregistered device with its bus, name, and default policy set
@@ -309,8 +406,9 @@ void device_trust_init(struct device *dev)
if (dev->p && dev->p->trust_initialized)
return;
- if (device_trust_cmdline)
- device_trust_parse_cmdline(device_trust_cmdline, dev,
- &dev->trust_policy);
+ if (device_trust_cmdline &&
+ device_trust_parse_cmdline(device_trust_cmdline, dev,
+ &dev->trust_policy) > 0 && dev->p)
+ dev->p->trust_policy_override = true;
}
EXPORT_SYMBOL_GPL(device_trust_init);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 262502642ca98..4620667a11832 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -563,6 +563,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
+ /* an unregistered interface is admitted later, so refuse what would fail */
+ if (!device_is_registered(dev) &&
+ !device_trust_may_claim(dev, &driver->driver))
+ return -EPERM;
+
dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index d35a7710c8ef4..2d75b85b2d139 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2209,6 +2209,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
pm_runtime_no_callbacks(&intf->dev);
dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum,
dev->devpath, configuration, ifnum);
+ /* A sibling may claim the interface before it is registered */
+ device_trust_init(&intf->dev);
/*
* Make sure the interface does not outlive its parent (e.g.
* as needed for usb_queue_reset_device() and
diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
index b8f5514cacec3..552af3e73d4fc 100644
--- a/include/linux/device/trust.h
+++ b/include/linux/device/trust.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
struct device;
+struct device_driver;
/**
* enum device_trust_level - active trust level for a device
@@ -38,6 +39,8 @@ enum device_trust_policy {
};
void device_trust_init(struct device *dev);
+bool device_trust_may_claim(const struct device *dev,
+ const struct device_driver *drv);
enum device_trust_level device_get_trust_level(const struct device *dev);
bool device_is_adversarial(const struct device *dev);
--
2.56.0