[PATCH v2 2/9] bus: add RISAB dump debug driver

Gatien Chevallier gatien.chevallier at foss.st.com
Thu Feb 12 02:10:31 PST 2026


The Resource ISolation peripheral unit for Address space protection
(Block-based)(RISAB) is a memory firewall peripheral protecting
internal RAMs and is part of the STM32 Resource Isolation Framework
(RIF).

RIF configuration on stm32mp2x platforms can be quite complex.
Especially for memory firewalls. The RISAB configuration is done by
the Trusted Domain that is running in secure privileged mode.
However, its configuration can be read in any mode.

In order to facilitate the memory firewall configuration (check if
correctly applied, what rights for which range, etc...), add a RISAB
configuration dump driver that creates a debugfs entry as an
interactive way of looking at RISAB configuration.

This debugfs entry is a heavy printing function when read so be aware
that reading it will involve significant time in the driver.

Introduce CONFIG_STM32_FIREWALL_DEBUG configuration switch to
enable STM32 firewall drivers configuration dump.

Co-developed-by: Theo GOUREAU <theo.goureau-ext at st.com>
Signed-off-by: Theo GOUREAU <theo.goureau-ext at st.com>
Signed-off-by: Gatien Chevallier <gatien.chevallier at foss.st.com>
---
 drivers/bus/Kconfig       |   9 ++
 drivers/bus/Makefile      |   1 +
 drivers/bus/stm32_risab.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 346 insertions(+)

diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index fe7600283e70..1b12de2854ab 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -179,6 +179,15 @@ config STM32_FIREWALL
 	  hardware resources linked to a firewall controller can be requested
 	  through this STM32 framework.
 
+config STM32_FIREWALL_DEBUG
+	tristate "STM32 Firewall drivers debug features"
+	depends on STM32_FIREWALL && DEBUG_FS
+	help
+	  Say y to enable STM32 firewall configuration debug features. This
+	  will cause the generation of a "stm32_firewall" directory in the
+	  debugfs with entries for each firewall driver capable of dumping
+	  its configuration.
+
 config SUN50I_DE2_BUS
 	bool "Allwinner A64 DE2 Bus Driver"
 	  default ARM64
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 8e693fe8a03a..a0447682c314 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_OMAP_OCP2SCP)	+= omap-ocp2scp.o
 obj-$(CONFIG_QCOM_EBI2)		+= qcom-ebi2.o
 obj-$(CONFIG_QCOM_SSC_BLOCK_BUS)	+= qcom-ssc-block-bus.o
 obj-$(CONFIG_STM32_FIREWALL)	+= stm32_firewall.o stm32_rifsc.o stm32_etzpc.o
+obj-$(CONFIG_STM32_FIREWALL_DEBUG)	+= stm32_risab.o
 obj-$(CONFIG_SUN50I_DE2_BUS)	+= sun50i-de2.o
 obj-$(CONFIG_SUNXI_RSB)		+= sunxi-rsb.o
 obj-$(CONFIG_OF)		+= simple-pm-bus.o
diff --git a/drivers/bus/stm32_risab.c b/drivers/bus/stm32_risab.c
new file mode 100644
index 000000000000..794c2bf40a2d
--- /dev/null
+++ b/drivers/bus/stm32_risab.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+/*
+ * RISAB constants
+ */
+#define RISAB_NUMBER_OF_PAGES_MAX	32
+#define RISAB_BLOCKS_PER_PAGES		8
+#define RISAB_NUMBER_OF_CID		7
+#define RISAB_PAGE_SIZE			0x1000
+
+/*
+ * RISAB configuration register
+ */
+#define RISAB_CR	0x000
+#define RISAB_CR_SRWIAD	BIT(31)
+#define RISAB_CR_GLOCK	BIT(0)
+
+/*
+ * RISAB configuration lock register
+ */
+#define RISAB_RCFGLOCKR	0x010
+
+/*
+ * RISAB page y=0..31 security and privileged configuration registers
+ */
+#define RISAB_PG_SECCFGR(y)	(0x100 + 0x4 * (y))
+#define RISAB_PG_PRIVCFGR(y)	(0x200 + 0x4 * (y))
+#define RISAB_PG_C2PRIVCFGR(y)	(0x600 + 0x4 * (y))
+
+/*
+ * RISAB compartment x=0..6 privilege, read and write configuration registers
+ */
+#define RISAB_CIDPRIVCFGR(x)	(0x800 + 0x20 * (x))
+#define RISAB_CIDRDCFGR(x)	(0x808 + 0x20 * (x))
+#define RISAB_CIDWRCFGR(x)	(0x810 + 0x20 * (x))
+
+/*
+ * RISAB page y=0..31 CID configuration registers
+ */
+#define RISAB_PG_CIDCFGR(y)		(0xA00 + 0x4 * (y))
+#define RISAB_PG_CIDCFGR_DCCID_MASK	GENMASK(6, 4)
+#define RISAB_PG_CIDCFGR_DCEN		BIT(2)
+#define RISAB_PG_CIDCFGR_CFEN		BIT(0)
+
+/*
+ * RISAB hardware configuration registers
+ */
+#define RISAB_HWCFGR1				0xFF0
+#define RISAB_HWCFGR1_LOG_NUM_PAGE_MASK		GENMASK(27, 24)
+#define RISAB_HWCFGR1_LOG_NUM_PAGE_SHIFT	24
+
+struct risab_debug_data {
+	bool sec[RISAB_BLOCKS_PER_PAGES];
+	bool priv[RISAB_BLOCKS_PER_PAGES];
+	bool c2priv[RISAB_BLOCKS_PER_PAGES];
+	u8 dccid;
+	bool dcen;
+	bool cfen;
+};
+
+struct risab_generic_debug_data {
+	bool srwiad;
+	bool glock;
+	bool rlock[RISAB_NUMBER_OF_PAGES_MAX];
+	bool ppriv[RISAB_NUMBER_OF_CID][RISAB_NUMBER_OF_PAGES_MAX];
+	bool prden[RISAB_NUMBER_OF_CID][RISAB_NUMBER_OF_PAGES_MAX];
+	bool pwren[RISAB_NUMBER_OF_CID][RISAB_NUMBER_OF_PAGES_MAX];
+};
+
+struct risab_pdata {
+	void __iomem *base;
+	struct clk *clk;
+	struct device *dev;
+	struct dentry *dbg_entry;
+	u32 phys_base;
+	u32 risab_map_base;
+	u32 nb_pages;
+};
+
+static void stm32_risab_fill_dev_dbg_entry(struct risab_pdata *pdata,
+					   struct risab_debug_data *dbg_entry, int page)
+{
+	u32 risab_pg_c2privcfgr = readl_relaxed(pdata->base + RISAB_PG_C2PRIVCFGR(page));
+	u32 risab_pg_privcfgr = readl_relaxed(pdata->base + RISAB_PG_PRIVCFGR(page));
+	u32 risab_pg_seccfgr = readl_relaxed(pdata->base + RISAB_PG_SECCFGR(page));
+	u32 risab_pg_cidcfgr = readl_relaxed(pdata->base + RISAB_PG_CIDCFGR(page));
+	int block;
+
+	for (block = 0; block < RISAB_BLOCKS_PER_PAGES; block++) {
+		dbg_entry->sec[block] = risab_pg_seccfgr & BIT(block);
+		dbg_entry->priv[block] = risab_pg_privcfgr & BIT(block);
+		dbg_entry->c2priv[block] = risab_pg_c2privcfgr & BIT(block);
+	}
+
+	dbg_entry->dccid = FIELD_GET(RISAB_PG_CIDCFGR_DCCID_MASK, risab_pg_cidcfgr);
+	dbg_entry->dcen = risab_pg_cidcfgr & RISAB_PG_CIDCFGR_DCEN;
+	dbg_entry->cfen = risab_pg_cidcfgr & RISAB_PG_CIDCFGR_CFEN;
+}
+
+static void stm32_risab_fill_dev_generic_dbg_entry(struct risab_pdata *pdata,
+						   struct risab_generic_debug_data *dbg_entry)
+{
+	u32 risab_rcfglockr = readl_relaxed(pdata->base + RISAB_RCFGLOCKR);
+	u32 risab_cr = readl_relaxed(pdata->base + RISAB_CR);
+	int page, compartment;
+
+	dbg_entry->srwiad = risab_cr & RISAB_CR_SRWIAD;
+	dbg_entry->glock = risab_cr & RISAB_CR_GLOCK;
+
+	for (page = 0; page < pdata->nb_pages; page++)
+		dbg_entry->rlock[page] = risab_rcfglockr & BIT(page);
+
+	for (compartment = 0; compartment < RISAB_NUMBER_OF_CID; compartment++) {
+		u32 risab_cidprivcfgr = readl_relaxed(pdata->base + RISAB_CIDPRIVCFGR(compartment));
+		u32 risab_cidrdcfgr = readl_relaxed(pdata->base + RISAB_CIDRDCFGR(compartment));
+		u32 risab_cidwrcfgr = readl_relaxed(pdata->base + RISAB_CIDWRCFGR(compartment));
+
+		for (page = 0; page < pdata->nb_pages; page++) {
+			dbg_entry->ppriv[compartment][page] = risab_cidprivcfgr & BIT(page);
+			dbg_entry->prden[compartment][page] = risab_cidrdcfgr & BIT(page);
+			dbg_entry->pwren[compartment][page] = risab_cidwrcfgr & BIT(page);
+		}
+	}
+}
+
+static int stm32_risab_conf_dump_show(struct seq_file *s, void *data)
+{
+	struct risab_pdata *pdata = (struct risab_pdata *)s->private;
+	struct risab_generic_debug_data generic_dbg_entry;
+	struct risab_debug_data dbg_entry;
+	int ret, page, compartment, block;
+
+	ret = clk_prepare_enable(pdata->clk);
+	if (ret) {
+		dev_err(pdata->dev, "Couldn't enable RISAB clock");
+		return ret;
+	}
+
+	stm32_risab_fill_dev_generic_dbg_entry(pdata, &generic_dbg_entry);
+
+	seq_puts(s, "=============================================\n");
+	seq_printf(s, "        RISAB dump (%s)\n", pdata->dev->of_node->full_name);
+	seq_puts(s, "=============================================\n");
+
+	seq_printf(s, "Secure read/write illegal access disable (SRWIAD): %d.\n",
+		   generic_dbg_entry.srwiad);
+	seq_printf(s, "Global lock (GLOCK): %d.\n", generic_dbg_entry.glock);
+
+	seq_puts(s, "| Page                       |");
+	seq_puts(s, "| Res. |");
+	seq_puts(s, "|     priv. (p) read (r) write (w) per compartment      |");
+	seq_puts(s, "| Delegated |");
+	seq_puts(s, "|    CID    |");
+	seq_puts(s, "| secure (s) default priv. (p) compartment2 priv. (P) per block |\n");
+
+	seq_puts(s, "|     start add.  end add.   |");
+	seq_puts(s, "| lock |");
+	seq_puts(s, "| CID0  | CID1  | CID2  | CID3  | CID4  | CID5  | CID6  |");
+	seq_puts(s, "| conf. CID |");
+	seq_puts(s, "| filtering |");
+	seq_puts(s, "| blck0 | blck1 | blck2 | blck3 | blck4 | blck5 | blck6 | blck7 |\n");
+
+	for (page = 0; page < pdata->nb_pages; page++) {
+		stm32_risab_fill_dev_dbg_entry(pdata, &dbg_entry, page);
+		seq_printf(s, "| %2d  0x%08x  0x%08x |",
+			   page,
+			   pdata->risab_map_base + page * RISAB_PAGE_SIZE,
+			   pdata->risab_map_base + (page + 1) * RISAB_PAGE_SIZE - 1
+		);
+		seq_printf(s, "| %3s  |", generic_dbg_entry.rlock[page] ? "Yes" : "No");
+		for (compartment = 0; compartment < RISAB_NUMBER_OF_CID; compartment++) {
+			seq_printf(s, "| %1s %1s %1s ",
+				   generic_dbg_entry.ppriv[compartment][page] ? "p" : " ",
+				   generic_dbg_entry.prden[compartment][page] ? "r" : " ",
+				   generic_dbg_entry.pwren[compartment][page] ? "w" : " "
+			);
+		}
+
+		if (dbg_entry.dcen)
+			seq_printf(s, "||  0x%04x   |", dbg_entry.dccid);
+		else
+			seq_puts(s, "|| disabled  |");
+
+		seq_printf(s, "| %-9s |", dbg_entry.cfen ? "enabled" : "disabled");
+		for (block = 0; block < RISAB_BLOCKS_PER_PAGES; block++) {
+			seq_printf(s, "| %1s %1s %1s ",
+				   dbg_entry.sec[block] ? "s" : " ",
+				   dbg_entry.priv[block] && !dbg_entry.cfen ? "p" : " ",
+				   dbg_entry.c2priv[block] ? "P" : " "
+			);
+		}
+		seq_puts(s, "|\n");
+	}
+
+	clk_disable_unprepare(pdata->clk);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(stm32_risab_conf_dump);
+
+static int stm32_risab_register_debugfs(struct risab_pdata *pdata)
+{
+	struct dentry *root = NULL;
+	char risab_name[15];
+
+	root = debugfs_lookup("stm32_firewall", NULL);
+	if (!root)
+		root = debugfs_create_dir("stm32_firewall", NULL);
+
+	if (IS_ERR(root))
+		return PTR_ERR(root);
+
+	scnprintf(risab_name, sizeof(risab_name), "risab@%x", pdata->phys_base);
+
+	pdata->dbg_entry = debugfs_create_file(risab_name, 0444, root, pdata,
+					       &stm32_risab_conf_dump_fops);
+
+	return 0;
+}
+
+static int stm32_risab_get_nb_pages(struct risab_pdata *pdata)
+{
+	u32 risab_hwcfgr1, nb_pages_shift;
+	int ret, nb_page;
+
+	ret = clk_prepare_enable(pdata->clk);
+	if (ret) {
+		dev_err(pdata->dev, "Failed to enable clk: %d\n", ret);
+		return ret;
+	}
+
+	risab_hwcfgr1 = readl_relaxed(pdata->base + RISAB_HWCFGR1);
+	nb_pages_shift = FIELD_GET(RISAB_HWCFGR1_LOG_NUM_PAGE_MASK,
+				   risab_hwcfgr1);
+	nb_page = BIT(nb_pages_shift);
+
+	if (nb_page > RISAB_NUMBER_OF_PAGES_MAX) {
+		dev_err(pdata->dev, "RISAB number of pages is greater than %d",
+			RISAB_NUMBER_OF_PAGES_MAX);
+		ret = -EINVAL;
+		goto err_clk_disable;
+	}
+
+	ret = nb_page;
+
+err_clk_disable:
+	clk_disable_unprepare(pdata->clk);
+
+	return ret;
+}
+
+static int stm32_risab_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct risab_pdata *pdata;
+	struct resource *res;
+	void __iomem *mmio;
+	int err, nb_pages;
+	struct clk *clk;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	mmio =  devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+	if (IS_ERR(mmio))
+		return PTR_ERR(mmio);
+
+	pdata->phys_base = res->start;
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Failed to get clk\n");
+
+	pdata->base = mmio;
+	pdata->clk = clk;
+	pdata->dev = &pdev->dev;
+
+	err = of_property_read_u32(np, "st,mem-map", &pdata->risab_map_base);
+	if (err) {
+		return dev_err_probe(&pdev->dev, err,
+				     "Couldn't read the memory range covered by the RISAB\n");
+	}
+
+	nb_pages = stm32_risab_get_nb_pages(pdata);
+	if (nb_pages < 0)
+		return dev_err_probe(&pdev->dev, nb_pages, "Couldn't read RISAB number of pages\n");
+
+	pdata->nb_pages = nb_pages;
+
+	platform_set_drvdata(pdev, pdata);
+
+	return stm32_risab_register_debugfs(pdata);
+}
+
+static void stm32_risab_remove(struct platform_device *pdev)
+{
+	struct risab_pdata *pdata = platform_get_drvdata(pdev);
+
+	debugfs_remove(pdata->dbg_entry);
+}
+
+static const struct of_device_id stm32_risab_match[] = {
+	{ .compatible = "st,stm32mp25-risab", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_risab_match);
+
+static struct platform_driver stm32_risab_driver = {
+	.probe  = stm32_risab_probe,
+	.remove = stm32_risab_remove,
+	.driver = {
+		.name = "stm32-risab",
+		.of_match_table = stm32_risab_match,
+	},
+};
+module_platform_driver(stm32_risab_driver);
+
+MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier at foss.st.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0




More information about the linux-arm-kernel mailing list