[PATCH v8 6/7] drivers: pinctrl: configure pinctrl based on policy name

Fabian Pflug
2026-04-21T10:30:10Z

Security policies can be used to have different functionality on the
system. If systems have different functionality, they might have
different pinctrl settings.

One example could be the console input:
When using security policies to disable console input on the default
console, it might be more advantageous to also disable the RX pin hard
in pinctrl, so that if there is a software error with the security
policy implementation input does not reach to system and cannot be
exploited.

An example devicetree could look like this:
/ {
chosen {
stdout-path = &uart3;
};
};

&uart3 {
pinctrl-names = "default", "barebox,policy-devel-default";
pinctrl-0 = <&pinctrl_uart3_tx_only>;
pinctrl-1 = <&pinctrl_uart3_interactive>;
status = "okay";
};

&iomuxc {
pinctrl_uart3_interactive: uart3ingrp {
fsl,pins = <MX8MP_IOMUXC_SD1_DATA6__UART3_DCE_TX 0x140>,
<MX8MP_IOMUXC_SD1_DATA7__UART3_DCE_RX 0x140>;
};

pinctrl_uart3_tx_only: uart3txgrp {
fsl,pins = <MX8MP_IOMUXC_SD1_DATA6__UART3_DCE_TX 0x140>,
<MX8MP_IOMUXC_SD1_DATA7__GPIO2_IO09 0x140>;
};
};

This would apply the devel pinmux on selecting the devel config and the
default on every other security policy configuration.

A Kconfig option to enable this feature has been chosen, because parsing
pinctrl and mapping the names is a lot of string operations, that could
increase boottime for a feature, that is maybe not needed for everyone.

Signed-off-by: Fabian Pflug <f.pflug@pengutronix.de>
---
drivers/pinctrl/pinctrl.c | 73 +++++++++++++++++++++++++++++++++++++++++++++--
security/Kconfig.policy | 12 ++++++++
2 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c
index 01567aa5ae..eb4103c5fd 100644
--- a/drivers/pinctrl/pinctrl.c
+++ b/drivers/pinctrl/pinctrl.c
@@ -10,11 +10,22 @@
#include <linux/overflow.h>
#include <errno.h>
#include <of.h>
+#include <security/config.h>
+#include <security/policy.h>

struct pinctrl {
struct device_node consumer_np;
};

+LIST_HEAD(pinctrl_devices_name_list);
+
+struct pinctrl_devices_name_info {
+ struct device_node *node;
+ const char *name;
+ struct list_head list;
+};
+
+
static LIST_HEAD(pinctrl_consumer_list);

struct pinctrl_state {
@@ -218,15 +229,48 @@ int pinctrl_select_state(struct pinctrl *pinctrl, struct pinctrl_state *state)
return ret;
}

+static void of_pinctrl_policy_update_or_add(struct device_node *np, const char *name)
+{
+ struct pinctrl_devices_name_info *dev;
+
+ list_for_each_entry(dev, &pinctrl_devices_name_list, list) {
+ if (dev->node == np) {
+ free_const(dev->name);
+ dev->name = xstrdup_const(name);
+ return;
+ }
+ }
+ dev = xzalloc(sizeof(*dev));
+ dev->node = np;
+ dev->name = xstrdup_const(name);
+ list_add(&dev->list, &pinctrl_devices_name_list);
+}
+
int of_pinctrl_select_state(struct device_node *np, const char *name)
{
struct pinctrl *pinctrl = of_pinctrl_get(np);
- struct pinctrl_state *state;
+ struct pinctrl_state *state = NULL;

if (!of_find_property(np, "pinctrl-0", NULL))
return 0;

- state = pinctrl_lookup_state(pinctrl, name);
+ if (IS_ENABLED(CONFIG_SECURITY_POLICY_PINCTRL)) {
+ const struct security_policy *active_policy = security_policy_get_active();
+
+ of_pinctrl_policy_update_or_add(np, name);
+
+ if (active_policy && active_policy->name && name) {
+ char *policy_pinctrl;
+
+ policy_pinctrl = basprintf("barebox,policy-%s-%s",
+ active_policy->name,
+ name);
+ state = pinctrl_lookup_state(pinctrl, policy_pinctrl);
+ free(policy_pinctrl);
+ }
+ }
+ if (IS_ERR_OR_NULL(state))
+ state = pinctrl_lookup_state(pinctrl, name);
if (IS_ERR(state))
return PTR_ERR(state);

@@ -331,3 +375,28 @@ void of_pinctrl_unregister_consumer(struct device *dev)
}
}
#endif
+
+static int pinctrl_change_policy(struct notifier_block *nb,
+ unsigned long _ignored,
+ void *_data)
+{
+ struct pinctrl_devices_name_info *dev;
+
+ list_for_each_entry(dev, &pinctrl_devices_name_list, list) {
+ of_pinctrl_select_state(dev->node, dev->name);
+ }
+ return 0;
+}
+
+static struct notifier_block pinctrl_policy_notifier = {
+ .notifier_call = pinctrl_change_policy
+};
+
+static int pinctrl_policy_init(void)
+{
+ if (!IS_ENABLED(CONFIG_SECURITY_POLICY_PINCTRL))
+ return 0;
+
+ return notifier_chain_register(&sconfig_name_notifier, &pinctrl_policy_notifier);
+}
+pure_initcall(pinctrl_policy_init);
diff --git a/security/Kconfig.policy b/security/Kconfig.policy
index e778327b0d..2c8e8399a0 100644
--- a/security/Kconfig.policy
+++ b/security/Kconfig.policy
@@ -69,6 +69,18 @@ config SECURITY_POLICY_DEFAULT_PERMISSIVE
A security policy should always be selected, either early on by
board code or via CONFIG_SECURITY_POLICY_INIT.

+config SECURITY_POLICY_PINCTRL
+ bool "Update pinctrl based on policy-name"
+ help
+ When selecting a pinctrl with the name <pinctrlname>, if there exists
+ a pinctrl with the name barebox,policy-<policyname>-<pinctrlname>,
+ then this pinctrl will be used instead.
+ This could be used for example to disable the RX (and TX) Pin in
+ lockdown mode for the console or disable the usage of SPI.
+
+ Enabling this option does increase the boottime.
+ If unsure, say N.
+
config SECURITY_POLICY_PATH
string
depends on SECURITY_POLICY

--
2.47.3