[PATCH 13/13] ARM: stm32mp: add RIFSC system bus driver for STM32MP25

Patrice Chotard patrice.chotard at foss.st.com
Tue Apr 1 15:14:13 CEST 2025


From: Patrick Delaunay <patrick.delaunay at foss.st.com>

This driver is checking the access rights of the different
peripherals connected to the RIFSC bus. If access is denied,
the associated device is not binded.

Signed-off-by: Gatien Chevallier <gatien.chevallier at foss.st.com>
Signed-off-by: Patrick Delaunay <patrick.delaunay at foss.st.com>
Signed-off-by: Patrice Chotard <patrice.chotard at foss.st.com>

---

 arch/arm/mach-stm32mp/include/mach/rif.h |  26 ++
 arch/arm/mach-stm32mp/stm32mp2/Makefile  |   1 +
 arch/arm/mach-stm32mp/stm32mp2/rifsc.c   | 364 +++++++++++++++++++++++
 3 files changed, 391 insertions(+)
 create mode 100644 arch/arm/mach-stm32mp/include/mach/rif.h
 create mode 100644 arch/arm/mach-stm32mp/stm32mp2/rifsc.c

diff --git a/arch/arm/mach-stm32mp/include/mach/rif.h b/arch/arm/mach-stm32mp/include/mach/rif.h
new file mode 100644
index 00000000000..10b22108120
--- /dev/null
+++ b/arch/arm/mach-stm32mp/include/mach/rif.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef MACH_RIF_H
+#define MACH_RIF_H
+
+#include <linux/types.h>
+
+/**
+ * stm32_rifsc_check_access - Check RIF accesses for given device node
+ *
+ * @device_node		Node of the device for which the accesses are checked
+ */
+int stm32_rifsc_check_access(ofnode device_node);
+
+/**
+ * stm32_rifsc_check_access - Check RIF accesses for given id
+ *
+ * @device_node		Node of the device to get a reference on RIFSC
+ * @id			ID of the resource to check
+ */
+int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id);
+
+#endif /* MACH_RIF_H*/
diff --git a/arch/arm/mach-stm32mp/stm32mp2/Makefile b/arch/arm/mach-stm32mp/stm32mp2/Makefile
index b579ce5a800..5dbf75daa76 100644
--- a/arch/arm/mach-stm32mp/stm32mp2/Makefile
+++ b/arch/arm/mach-stm32mp/stm32mp2/Makefile
@@ -5,5 +5,6 @@
 
 obj-y += cpu.o
 obj-y += arm64-mmu.o
+obj-y += rifsc.o
 obj-$(CONFIG_OF_SYSTEM_SETUP) += fdt.o
 obj-$(CONFIG_STM32MP25X) += stm32mp25x.o
diff --git a/arch/arm/mach-stm32mp/stm32mp2/rifsc.c b/arch/arm/mach-stm32mp/stm32mp2/rifsc.c
new file mode 100644
index 00000000000..48f65365376
--- /dev/null
+++ b/arch/arm/mach-stm32mp/stm32mp2/rifsc.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#define LOG_CATEGORY UCLASS_SIMPLE_BUS
+
+#include <dm.h>
+#include <asm/io.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <linux/bitfield.h>
+#include <mach/rif.h>
+
+/* RIFSC offset register */
+#define RIFSC_RISC_SECCFGR0(id)		(0x10 + 0x4 * (id))
+#define RIFSC_RISC_PER0_CIDCFGR(id)	(0x100 + 0x8 * (id))
+#define RIFSC_RISC_PER0_SEMCR(id)	(0x104 + 0x8 * (id))
+
+/*
+ * SEMCR register
+ */
+#define SEMCR_MUTEX			BIT(0)
+
+/* RIFSC miscellaneous */
+#define RIFSC_RISC_SCID_MASK		GENMASK(6, 4)
+#define RIFSC_RISC_SEMWL_MASK		GENMASK(23, 16)
+
+#define IDS_PER_RISC_SEC_PRIV_REGS	32
+
+/*
+ * CIDCFGR register fields
+ */
+#define CIDCFGR_CFEN			BIT(0)
+#define CIDCFGR_SEMEN			BIT(1)
+
+#define SEMWL_SHIFT			16
+
+#define STM32MP25_RIFSC_ENTRIES		178
+
+/* Compartiment IDs */
+#define RIF_CID0			0x0
+#define RIF_CID1			0x1
+
+/*
+ * struct stm32_rifsc_plat: Information about RIFSC device
+ *
+ * @base: Base address of RIFSC
+ */
+struct stm32_rifsc_plat {
+	void *base;
+};
+
+/*
+ * struct stm32_rifsc_child_plat: Information about each child
+ *
+ * @domain_id: Domain id
+ */
+struct stm32_rifsc_child_plat {
+	u32 domain_id;
+};
+
+static bool stm32_rif_is_semaphore_available(void *base, u32 id)
+{
+	void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+	return !(readl(addr) & SEMCR_MUTEX);
+}
+
+static int stm32_rif_acquire_semaphore(void *base, u32 id)
+{
+	void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+	/* Check that the semaphore is available */
+	if (!stm32_rif_is_semaphore_available(base, id))
+		return -EACCES;
+
+	setbits_le32(addr, SEMCR_MUTEX);
+
+	/* Check that CID1 has the semaphore */
+	if (stm32_rif_is_semaphore_available(base, id) ||
+	    FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
+		return -EACCES;
+
+	return 0;
+}
+
+static int stm32_rif_release_semaphore(void *base, u32 id)
+{
+	void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+	if (stm32_rif_is_semaphore_available(base, id))
+		return 0;
+
+	clrbits_le32(addr, SEMCR_MUTEX);
+
+	/* Ok if another compartment takes the semaphore before the check */
+	if (!stm32_rif_is_semaphore_available(base, id) &&
+	    FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) == RIF_CID1))
+		return -EACCES;
+
+	return 0;
+}
+
+static int rifsc_parse_access_controller(ofnode node, struct ofnode_phandle_args *args)
+{
+	int ret;
+
+	ret = ofnode_parse_phandle_with_args(node, "access-controllers",
+					     "#access-controller-cells", 0,
+					     0, args);
+	if (ret) {
+		log_debug("failed to parse access-controller (%d)\n", ret);
+		return ret;
+	}
+
+	if (args->args_count != 1) {
+		log_debug("invalid domain args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	if (args->args[0] >= STM32MP25_RIFSC_ENTRIES) {
+		log_err("Invalid sys bus ID for %s\n", ofnode_get_name(node));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rifsc_check_access(void *base, u32 id)
+{
+	u32 reg_offset, reg_id, sec_reg_value, cid_reg_value, sem_reg_value;
+
+	/*
+	 * RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle configuration access for
+	 * 32 peripherals. On the other hand, there is one _RIFSC_RISC_PERx_CIDCFGR register
+	 * per peripheral
+	 */
+	reg_id = id / IDS_PER_RISC_SEC_PRIV_REGS;
+	reg_offset = id % IDS_PER_RISC_SEC_PRIV_REGS;
+	sec_reg_value = readl(base + RIFSC_RISC_SECCFGR0(reg_id));
+	cid_reg_value = readl(base + RIFSC_RISC_PER0_CIDCFGR(id));
+	sem_reg_value = readl(base + RIFSC_RISC_PER0_SEMCR(id));
+
+	/*
+	 * First check conditions for semaphore mode, which doesn't take into
+	 * account static CID.
+	 */
+	if (cid_reg_value & CIDCFGR_SEMEN)
+		goto skip_cid_check;
+
+	/*
+	 * Skip cid check if CID filtering isn't enabled or filtering is enabled on CID0, which
+	 * corresponds to whatever CID.
+	 */
+	if (!(cid_reg_value & CIDCFGR_CFEN) ||
+	    FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0)
+		goto skip_cid_check;
+
+	/* Coherency check with the CID configuration */
+	if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) {
+		log_debug("Invalid CID configuration for peripheral %d\n", id);
+		return -EACCES;
+	}
+
+	/* Check semaphore accesses */
+	if (cid_reg_value & CIDCFGR_SEMEN) {
+		if (!(FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
+			log_debug("Not in semaphore whitelist for peripheral %d\n", id);
+			return -EACCES;
+		}
+		if (!stm32_rif_is_semaphore_available(base, id) &&
+		    !(FIELD_GET(RIFSC_RISC_SCID_MASK, sem_reg_value) & BIT(RIF_CID1))) {
+			log_debug("Semaphore unavailable for peripheral %d\n", id);
+			return -EACCES;
+		}
+	}
+
+skip_cid_check:
+	/* Check security configuration */
+	if (sec_reg_value & BIT(reg_offset)) {
+		log_debug("Invalid security configuration for peripheral %d\n", id);
+		return -EACCES;
+	}
+
+	return 0;
+}
+
+int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id)
+{
+	struct ofnode_phandle_args args;
+	int err;
+
+	if (id >= STM32MP25_RIFSC_ENTRIES)
+		return -EINVAL;
+
+	err = rifsc_parse_access_controller(device_node, &args);
+	if (err)
+		return err;
+
+	return rifsc_check_access((void *)ofnode_get_addr(args.node), id);
+}
+
+int stm32_rifsc_check_access(ofnode device_node)
+{
+	struct ofnode_phandle_args args;
+	int err;
+
+	err = rifsc_parse_access_controller(device_node, &args);
+	if (err)
+		return err;
+
+	return rifsc_check_access((void *)ofnode_get_addr(args.node), args.args[0]);
+}
+
+static int stm32_rifsc_child_pre_probe(struct udevice *dev)
+{
+	struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
+	struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+	u32 cid_reg_value;
+	int err;
+	u32 id = child_plat->domain_id;
+
+	cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
+
+	/*
+	 * If the peripheral is in semaphore mode, take the semaphore so that
+	 * the CID1 has the ownership.
+	 */
+	if (cid_reg_value & CIDCFGR_SEMEN &&
+	    (FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
+		err = stm32_rif_acquire_semaphore(plat->base, id);
+		if (err) {
+			dev_err(dev, "Couldn't acquire RIF semaphore for peripheral %d (%d)\n",
+				id, err);
+			return err;
+		}
+		dev_dbg(dev, "Acquiring semaphore for peripheral %d\n", id);
+	}
+
+	return 0;
+}
+
+static int stm32_rifsc_child_post_remove(struct udevice *dev)
+{
+	struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
+	struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+	u32 cid_reg_value;
+	int err;
+	u32 id = child_plat->domain_id;
+
+	cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
+
+	/*
+	 * If the peripheral is in semaphore mode, release the semaphore so that
+	 * there's no ownership.
+	 */
+	if (cid_reg_value & CIDCFGR_SEMEN &&
+	    (FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
+		err = stm32_rif_release_semaphore(plat->base, id);
+		if (err)
+			dev_err(dev, "Couldn't release rif semaphore for peripheral %d (%d)\n",
+				id, err);
+	}
+
+	return 0;
+}
+
+static int stm32_rifsc_child_post_bind(struct udevice *dev)
+{
+	struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+	struct ofnode_phandle_args args;
+	int ret;
+
+	if (!dev_has_ofnode(dev))
+		return -EPERM;
+
+	ret = rifsc_parse_access_controller(dev_ofnode(dev), &args);
+	if (ret)
+		return ret;
+
+	child_plat->domain_id = args.args[0];
+
+	return 0;
+}
+
+static int stm32_rifsc_bind(struct udevice *dev)
+{
+	struct stm32_rifsc_plat *plat = dev_get_plat(dev);
+	struct ofnode_phandle_args args;
+	int ret = 0, err = 0;
+	ofnode node;
+
+	plat->base = dev_read_addr_ptr(dev);
+	if (!plat->base) {
+		dev_err(dev, "can't get registers base address\n");
+		return -ENOENT;
+	}
+
+	for (node = ofnode_first_subnode(dev_ofnode(dev));
+	     ofnode_valid(node);
+	     node = ofnode_next_subnode(node)) {
+		const char *node_name = ofnode_get_name(node);
+
+		if (!ofnode_is_enabled(node))
+			continue;
+
+		err = rifsc_parse_access_controller(node, &args);
+		if (err) {
+			dev_dbg(dev, "%s failed to parse child on bus (%d)\n", node_name, err);
+			continue;
+		}
+
+		err = rifsc_check_access(plat->base, args.args[0]);
+		if (err) {
+			dev_info(dev, "%s not allowed on bus (%d)\n", node_name, err);
+			continue;
+		}
+
+		err = lists_bind_fdt(dev, node, NULL, NULL,
+				     gd->flags & GD_FLG_RELOC ? false : true);
+		if (err && !ret) {
+			ret = err;
+			dev_err(dev, "%s failed to bind on bus (%d)\n", node_name, ret);
+		}
+	}
+
+	if (ret)
+		dev_err(dev, "Some child failed to bind (%d)\n", ret);
+
+	return ret;
+}
+
+static int stm32_rifsc_remove(struct udevice *bus)
+{
+	struct udevice *dev;
+
+	/* Deactivate all child devices not yet removed */
+	for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev))
+		if (device_active(dev))
+			stm32_rifsc_child_post_remove(dev);
+
+	return 0;
+}
+
+static const struct udevice_id stm32_rifsc_ids[] = {
+	{ .compatible = "st,stm32mp25-rifsc" },
+	{},
+};
+
+U_BOOT_DRIVER(stm32_rifsc) = {
+	.name = "stm32_rifsc",
+	.id = UCLASS_NOP,
+	.of_match = stm32_rifsc_ids,
+	.bind = stm32_rifsc_bind,
+	.remove = stm32_rifsc_remove,
+	.child_post_bind = stm32_rifsc_child_post_bind,
+	.child_pre_probe = stm32_rifsc_child_pre_probe,
+	.child_post_remove = stm32_rifsc_child_post_remove,
+	.plat_auto = sizeof(struct stm32_rifsc_plat),
+	.per_child_plat_auto = sizeof(struct stm32_rifsc_child_plat),
+	.flags = DM_FLAG_OS_PREPARE,
+};
-- 
2.25.1



More information about the U-Boot mailing list