This driver creates an entry in the debugfs to dump RISAB memory
regions configurations.
Signed-off-by: Theo GOUREAU <theo.goureau-ext@st.com>
Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
---
MAINTAINERS | 1 +
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/st/Kconfig | 11 ++
drivers/soc/st/Makefile | 1 +
drivers/soc/st/stm32_risab.c | 329 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 344 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b9a1276e94a9..64a5b894c6b8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25096,6 +25096,7 @@ STM32 SoC FIREWALL DRIVERS
M: Gatien Chevallier <gatien.chevallier@foss.st.com>
S: Maintained
F: Documentation/devicetree/bindings/soc/st/*risa*
+F: drivers/soc/st/*risa*
STM32 TIMER/LPTIMER DRIVERS
M: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index a2d65adffb80..dfe5cec59262 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -24,6 +24,7 @@ source "drivers/soc/renesas/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/samsung/Kconfig"
source "drivers/soc/sophgo/Kconfig"
+source "drivers/soc/st/Kconfig"
source "drivers/soc/sunxi/Kconfig"
source "drivers/soc/tegra/Kconfig"
source "drivers/soc/ti/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index c9e689080ceb..894bd6ebaaa4 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -30,6 +30,7 @@ obj-y += renesas/
obj-y += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-y += sophgo/
+obj-y += st/
obj-y += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += ti/
diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig
new file mode 100644
index 000000000000..6c293af58f2a
--- /dev/null
+++ b/drivers/soc/st/Kconfig
@@ -0,0 +1,11 @@
+if ARCH_STM32 || COMPILE_TEST
+
+config STM32_RISAB
+ tristate "STM32 RISAB"
+ depends on (ARM64 || COMPILE_TEST) && DEBUG_FS
+ default y
+ help
+ Say y to enable RISAB dump debug helper driver. This creates an entry
+ in the debugfs to dump RISAB pages and blocks RIF configurations.
+
+endif # ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile
new file mode 100644
index 000000000000..6b53f3eda801
--- /dev/null
+++ b/drivers/soc/st/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STM32_RISAB) += stm32_risab.o
diff --git a/drivers/soc/st/stm32_risab.c b/drivers/soc/st/stm32_risab.c
new file mode 100644
index 000000000000..f1f0300817e2
--- /dev/null
+++ b/drivers/soc/st/stm32_risab.c
@@ -0,0 +1,329 @@
+// 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 device *dev;
+ struct dentry *dbg_entry;
+ u32 risab_map_base;
+ struct clk *clk;
+ 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;
+
+ root = debugfs_lookup("stm32_firewall", NULL);
+ if (!root)
+ root = debugfs_create_dir("stm32_firewall", NULL);
+
+ if (IS_ERR(root))
+ return PTR_ERR(root);
+
+ pdata->dbg_entry = debugfs_create_file(dev_name(pdata->dev), 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;
+ 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_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ 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@foss.st.com>");
+MODULE_LICENSE("GPL");
--
2.43.0
On 09/02/2026 15:59, Gatien Chevallier wrote:
> +
> +static int stm32_risab_register_debugfs(struct risab_pdata *pdata)
> +{
> + struct dentry *root = NULL;
> +
> + root = debugfs_lookup("stm32_firewall", NULL);
> + if (!root)
> + root = debugfs_create_dir("stm32_firewall", NULL);
> +
> + if (IS_ERR(root))
> + return PTR_ERR(root);
> +
> + pdata->dbg_entry = debugfs_create_file(dev_name(pdata->dev), 0444,
> + root, pdata, &stm32_risab_conf_dump_fops);
soc drivers should not have any user-space interfaces. This was
requested by Arnd many times in the past, nothing new, including about
debugfs. Otherwise it opens cans of worms, because soc platform
maintainer can dump here whatever the subsystem rejected.
Find suitable subsystem and get its approval for such debugfs entry.
Your commit msg is so vague it is another reason this is not mergeable.
Explains nothing about the feature, nothing about the risk or impact on
the system when user does (while (1); read stm32_risab_conf_dump_fops).
Not mentioning that it does not explain WHY do we want this driver in
the first place...
Best regards,
Krzysztof
On 2/10/26 08:50, Krzysztof Kozlowski wrote:
> On 09/02/2026 15:59, Gatien Chevallier wrote:
>> +
>> +static int stm32_risab_register_debugfs(struct risab_pdata *pdata)
>> +{
>> + struct dentry *root = NULL;
>> +
>> + root = debugfs_lookup("stm32_firewall", NULL);
>> + if (!root)
>> + root = debugfs_create_dir("stm32_firewall", NULL);
>> +
>> + if (IS_ERR(root))
>> + return PTR_ERR(root);
>> +
>> + pdata->dbg_entry = debugfs_create_file(dev_name(pdata->dev), 0444,
>> + root, pdata, &stm32_risab_conf_dump_fops);
>
> soc drivers should not have any user-space interfaces. This was
> requested by Arnd many times in the past, nothing new, including about
> debugfs. Otherwise it opens cans of worms, because soc platform
> maintainer can dump here whatever the subsystem rejected.
>
> Find suitable subsystem and get its approval for such debugfs entry.
>
> Your commit msg is so vague it is another reason this is not mergeable.
> Explains nothing about the feature, nothing about the risk or impact on
> the system when user does (while (1); read stm32_risab_conf_dump_fops).
>
> Not mentioning that it does not explain WHY do we want this driver in
> the first place...
>
> Best regards,
> Krzysztof
My bad, I did not know that and only checked in the directory to see
that there were other debugfs entries in it.
You're right, it's too vague, I will develop the commit message in V2.
Best regards,
Gatien
© 2016 - 2026 Red Hat, Inc.