Introduce STM32 Firewall framework for STM32MP1x and STM32MP2x
platforms. STM32MP1x(ETZPC) and STM32MP2x(RIFSC) Firewall controllers
register to the framework to offer firewall services such as access
granting.
This series of patches is a new approach on the previous STM32 system
bus, history is available here:
https://lore.kernel.org/lkml/20230127164040.1047583/
The need for such framework arises from the fact that there are now
multiple hardware firewalls implemented across multiple products.
Drivers are shared between different products, using the same code.
When it comes to firewalls, the purpose mostly stays the same: Protect
hardware resources. But the implementation differs, and there are
multiple types of firewalls: peripheral, memory, ...
Some hardware firewall controllers such as the RIFSC implemented on
STM32MP2x platforms may require to take ownership of a resource before
being able to use it, hence the requirement for firewall services to
take/release the ownership of such resources.
On the other hand, hardware firewall configurations are becoming
more and more complex. These mecanisms prevent platform crashes
or other firewall-related incoveniences by denying access to some
resources.
The stm32 firewall framework offers an API that is defined in
firewall controllers drivers to best fit the specificity of each
firewall.
For every peripherals protected by either the ETZPC or the RIFSC, the
firewall framework checks the firewall controlelr registers to see if
the peripheral's access is granted to the Linux kernel. If not, the
peripheral is configured as secure, the node is marked populated,
so that the driver is not probed for that device.
The firewall framework relies on the access-controller device tree
binding. It is used by peripherals to reference a domain access
controller. In this case a firewall controller. The bus uses the ID
referenced by the access-controller property to know where to look
in the firewall to get the security configuration for the peripheral.
This allows a device tree description rather than a hardcoded peripheral
table in the bus driver.
The STM32 ETZPC device is responsible for filtering accesses based on
security level, or co-processor isolation for any resource connected
to it.
The RIFSC is responsible for filtering accesses based on Compartment
ID / security level / privilege level for any resource connected to
it.
STM32MP13/15/25 SoC device tree files are updated in this series to
implement this mecanism.
Changes in V8:
- Add missing "simple-bus" compatible in STM32MP13/25 SoC
device tree files for the ETZPC/RIFSC nodes
- Add missing dependency on OF for OF_DYNAMIC that is selected
by STM32_FIREWALL
Changes in V7:
- Separate indentation changes from access-controllers changes
in the device tree file commits
- Select OF_DYNAMIC when STM32_FIREWALL is set in order to use
of_detach_node() in the firewall framework
- Handle previously non-present RNG and HASH nodes in the
STM32MP13 device tree file
Changes in V6:
- Rename access-controller to access-controllers
- Remove access-controller-provider
- Update device trees and other bindings accordingly
- Rework ETZPC/RIFSC bindings to define what access-controllers
cells contain inside #access-controller-cells
- Some other minor fixes
Changes in V5:
- Integrate and rework the "feature-domains" binding patch in
this patchset. The binding is renamed to "access-controller"
- Rename every feature-domain* reference to access-control*
ones
- Correct loop bug and missing select STM32_FIREWALL in 32-bit
platform Kconfig
Changes in V4:
- Fix typo in commit message and YAML check errors in
"dt-bindings: Document common device controller bindings"
Note: This patch should be ignored as stated in the cover
letter. I've done this to avoid errors on this series of
patch
- Correct code syntax/style issues reported by Simon Horman
- Added Jonathan's tag for IIO on the treewide patch
Changes in V3:
Change incorrect ordering for bindings commits leading
to an error while running
"make DT_CHECKER_FLAGS=-m dt_binding_check"
Changes in V2:
generic:
- Add fw_devlink dependency for "feature-domains"
property.
bindings:
- Corrected YAMLS errors highlighted by Rob's robot
- Firewall controllers YAMLs no longer define the
maxItems for the "feature-domains" property
- Renamed st,stm32-rifsc.yaml to
st,stm32mp25-rifsc.yaml
- Fix examples in YAML files
- Change feature-domains maxItems to 2 in firewall
consumer files as there should not be more than
2 entries for now
- Declare "feature-domain-names" as an optional
property for firewall controllers child nodes.
- Add missing "feature-domains" property declaration
in bosch,m_can.yaml and st,stm32-cryp.yaml files
firewall framework:
- Support multiple entries for "feature-domains"
property
- Better handle the device-tree parsing using
phandle+args APIs
- Remove "resource firewall" type
- Add a field for the name of the firewall entry
- Fix licenses
RIFSC:
- Add controller name
- Driver is now a module_platform_driver
- Fix license
ETZPC:
- Add controller name
- Driver is now a module_platform_driver
- Fix license
Device trees:
- Fix rifsc node name
- Move the "ranges" property under the
"feature-domains" one
Gatien Chevallier (12):
dt-bindings: treewide: add access-controllers description
dt-bindings: bus: document RIFSC
dt-bindings: bus: document ETZPC
firewall: introduce stm32_firewall framework
of: property: fw_devlink: Add support for "access-controller"
bus: rifsc: introduce RIFSC firewall controller driver
arm64: dts: st: add RIFSC as an access controller for STM32MP25x
boards
bus: etzpc: introduce ETZPC firewall controller driver
ARM: dts: stm32: add ETZPC as a system bus for STM32MP15x boards
ARM: dts: stm32: put ETZPC as an access controller for STM32MP15x
boards
ARM: dts: stm32: add ETZPC as a system bus for STM32MP13x boards
ARM: dts: stm32: put ETZPC as an access controller for STM32MP13x
boards
Oleksii Moisieiev (1):
dt-bindings: document generic access controllers
.../access-controllers.yaml | 84 +
.../bindings/bus/st,stm32-etzpc.yaml | 87 +
.../bindings/bus/st,stm32mp25-rifsc.yaml | 96 +
.../bindings/crypto/st,stm32-cryp.yaml | 4 +
.../bindings/crypto/st,stm32-hash.yaml | 4 +
.../devicetree/bindings/dma/st,stm32-dma.yaml | 4 +
.../bindings/dma/st,stm32-dmamux.yaml | 4 +
.../devicetree/bindings/i2c/st,stm32-i2c.yaml | 4 +
.../bindings/iio/adc/st,stm32-adc.yaml | 4 +
.../bindings/iio/adc/st,stm32-dfsdm-adc.yaml | 4 +
.../bindings/iio/dac/st,stm32-dac.yaml | 4 +
.../bindings/media/cec/st,stm32-cec.yaml | 4 +
.../bindings/media/st,stm32-dcmi.yaml | 4 +
.../memory-controllers/st,stm32-fmc2-ebi.yaml | 4 +
.../bindings/mfd/st,stm32-lptimer.yaml | 4 +
.../bindings/mfd/st,stm32-timers.yaml | 4 +
.../devicetree/bindings/mmc/arm,pl18x.yaml | 4 +
.../bindings/net/can/bosch,m_can.yaml | 4 +
.../devicetree/bindings/net/stm32-dwmac.yaml | 4 +
.../bindings/phy/phy-stm32-usbphyc.yaml | 4 +
.../bindings/regulator/st,stm32-vrefbuf.yaml | 4 +
.../devicetree/bindings/rng/st,stm32-rng.yaml | 4 +
.../bindings/serial/st,stm32-uart.yaml | 4 +
.../bindings/sound/st,stm32-i2s.yaml | 4 +
.../bindings/sound/st,stm32-sai.yaml | 4 +
.../bindings/sound/st,stm32-spdifrx.yaml | 4 +
.../bindings/spi/st,stm32-qspi.yaml | 4 +
.../devicetree/bindings/spi/st,stm32-spi.yaml | 4 +
.../devicetree/bindings/usb/dwc2.yaml | 4 +
MAINTAINERS | 7 +
arch/arm/boot/dts/st/stm32mp131.dtsi | 1063 ++++---
arch/arm/boot/dts/st/stm32mp133.dtsi | 51 +-
arch/arm/boot/dts/st/stm32mp13xc.dtsi | 19 +-
arch/arm/boot/dts/st/stm32mp13xf.dtsi | 19 +-
arch/arm/boot/dts/st/stm32mp151.dtsi | 2756 +++++++++--------
arch/arm/boot/dts/st/stm32mp153.dtsi | 52 +-
arch/arm/boot/dts/st/stm32mp15xc.dtsi | 19 +-
arch/arm/mach-stm32/Kconfig | 1 +
arch/arm64/Kconfig.platforms | 1 +
arch/arm64/boot/dts/st/stm32mp251.dtsi | 7 +-
drivers/bus/Kconfig | 10 +
drivers/bus/Makefile | 1 +
drivers/bus/stm32_etzpc.c | 141 +
drivers/bus/stm32_firewall.c | 294 ++
drivers/bus/stm32_firewall.h | 83 +
drivers/bus/stm32_rifsc.c | 252 ++
drivers/of/property.c | 2 +
include/linux/bus/stm32_firewall_device.h | 141 +
48 files changed, 3352 insertions(+), 1938 deletions(-)
create mode 100644 Documentation/devicetree/bindings/access-controllers/access-controllers.yaml
create mode 100644 Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
create mode 100644 Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
create mode 100644 drivers/bus/stm32_etzpc.c
create mode 100644 drivers/bus/stm32_firewall.c
create mode 100644 drivers/bus/stm32_firewall.h
create mode 100644 drivers/bus/stm32_rifsc.c
create mode 100644 include/linux/bus/stm32_firewall_device.h
--
2.25.1
Allows tracking dependencies between devices and their access
controller.
Signed-off-by: Gatien Chevallier <[email protected]>
---
Changes in V6:
- Renamed access-controller to access-controllers
Changes in V5:
- Rename feature-domain* to access-control*
Patch not present in V1
drivers/of/property.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/of/property.c b/drivers/of/property.c
index afdaefbd03f6..7f737eac91b2 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1268,6 +1268,7 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL)
DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
DEFINE_SIMPLE_PROP(panel, "panel", NULL)
DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells")
+DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells")
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
@@ -1363,6 +1364,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_regulators, },
{ .parse_prop = parse_gpio, },
{ .parse_prop = parse_gpios, },
+ { .parse_prop = parse_access_controllers, },
{}
};
--
2.25.1
RIFSC is a peripheral firewall controller that filter accesses based on
Arm TrustZone secure state, Arm CPU privilege execution level and
Compartment IDentification of the STM32 SoC subsystems.
Signed-off-by: Gatien Chevallier <[email protected]>
---
Changes in V4:
- Fix incorrect use of __set_bit() in
- stm32_rif_acquire_semaphore()
Changes in V2:
- Add controller name
- Driver is now a module_platform_driver
- Use error code returned by stm32_firewall_populate_bus()
- Fix license
MAINTAINERS | 1 +
drivers/bus/Makefile | 2 +-
drivers/bus/stm32_rifsc.c | 252 ++++++++++++++++++++++++++++++++++++++
3 files changed, 254 insertions(+), 1 deletion(-)
create mode 100644 drivers/bus/stm32_rifsc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 774c7e1601d8..a1152438b52d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20434,6 +20434,7 @@ ST STM32 FIREWALL
M: Gatien Chevallier <[email protected]>
S: Maintained
F: drivers/bus/stm32_firewall.c
+F: drivers/bus/stm32_rifsc.c
ST STM32 I2C/SMBUS DRIVER
M: Pierre-Yves MORDRET <[email protected]>
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index fc0511450ec2..e50d18e1d141 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -26,7 +26,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
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
+obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.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_rifsc.c b/drivers/bus/stm32_rifsc.c
new file mode 100644
index 000000000000..4cf1b60014b7
--- /dev/null
+++ b/drivers/bus/stm32_rifsc.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.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>
+#include <linux/types.h>
+
+#include "stm32_firewall.h"
+
+/*
+ * RIFSC offset register
+ */
+#define RIFSC_RISC_SECCFGR0 0x10
+#define RIFSC_RISC_PRIVCFGR0 0x30
+#define RIFSC_RISC_PER0_CIDCFGR 0x100
+#define RIFSC_RISC_PER0_SEMCR 0x104
+#define RIFSC_RISC_HWCFGR2 0xFEC
+
+/*
+ * SEMCR register
+ */
+#define SEMCR_MUTEX BIT(0)
+
+/*
+ * HWCFGR2 register
+ */
+#define HWCFGR2_CONF1_MASK GENMASK(15, 0)
+#define HWCFGR2_CONF2_MASK GENMASK(23, 16)
+#define HWCFGR2_CONF3_MASK GENMASK(31, 24)
+
+/*
+ * RIFSC miscellaneous
+ */
+#define RIFSC_RISC_CFEN_MASK BIT(0)
+#define RIFSC_RISC_SEM_EN_MASK BIT(1)
+#define RIFSC_RISC_SCID_MASK GENMASK(6, 4)
+#define RIFSC_RISC_SEML_SHIFT 16
+#define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16)
+#define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24)
+
+#define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \
+ RIFSC_RISC_SEM_EN_MASK | \
+ RIFSC_RISC_SCID_MASK | \
+ RIFSC_RISC_SEMWL_MASK)
+
+#define IDS_PER_RISC_SEC_PRIV_REGS 32
+
+/* RIF miscellaneous */
+/*
+ * CIDCFGR register fields
+ */
+#define CIDCFGR_CFEN BIT(0)
+#define CIDCFGR_SEMEN BIT(1)
+#define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x))
+
+#define SEMWL_SHIFT 16
+
+/* Compartiment IDs */
+#define RIF_CID0 0x0
+#define RIF_CID1 0x1
+
+static bool stm32_rifsc_is_semaphore_available(void __iomem *addr)
+{
+ return !(readl(addr) & SEMCR_MUTEX);
+}
+
+static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
+ int id)
+{
+ void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
+
+ writel(SEMCR_MUTEX, addr);
+
+ /* Check that CID1 has the semaphore */
+ if (stm32_rifsc_is_semaphore_available(addr) ||
+ FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1)
+ return -EACCES;
+
+ return 0;
+}
+
+static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
+ int id)
+{
+ void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
+
+ if (stm32_rifsc_is_semaphore_available(addr))
+ return;
+
+ writel(SEMCR_MUTEX, addr);
+
+ /* Ok if another compartment takes the semaphore before the check */
+ WARN_ON(!stm32_rifsc_is_semaphore_available(addr) &&
+ FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1);
+}
+
+static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
+{
+ struct stm32_firewall_controller *rifsc_controller = ctrl;
+ u32 reg_offset, reg_id, sec_reg_value, cid_reg_value;
+ int rc;
+
+ if (firewall_id >= rifsc_controller->max_entries) {
+ dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id);
+ return -EINVAL;
+ }
+
+ /*
+ * 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 = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS;
+ reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS;
+ sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id);
+ cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id);
+
+ /* First check conditions for semaphore mode, which doesn't take into account static CID. */
+ if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
+ if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) {
+ /* Static CID is irrelevant if semaphore mode */
+ goto skip_cid_check;
+ } else {
+ dev_dbg(rifsc_controller->dev,
+ "Invalid bus semaphore configuration: index %d\n", firewall_id);
+ return -EACCES;
+ }
+ }
+
+ /*
+ * 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) {
+ dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n",
+ firewall_id);
+ return -EACCES;
+ }
+
+skip_cid_check:
+ /* Check security configuration */
+ if (sec_reg_value & BIT(reg_offset)) {
+ dev_dbg(rifsc_controller->dev,
+ "Invalid security configuration for peripheral: %d\n", firewall_id);
+ return -EACCES;
+ }
+
+ /*
+ * If the peripheral is in semaphore mode, take the semaphore so that
+ * the CID1 has the ownership.
+ */
+ if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
+ rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id);
+ if (rc) {
+ dev_err(rifsc_controller->dev,
+ "Couldn't acquire semaphore for peripheral: %d\n", firewall_id);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
+{
+ stm32_rif_release_semaphore(ctrl, firewall_id);
+}
+
+static int stm32_rifsc_probe(struct platform_device *pdev)
+{
+ struct stm32_firewall_controller *rifsc_controller;
+ struct device_node *np = pdev->dev.of_node;
+ u32 nb_risup, nb_rimu, nb_risal;
+ struct resource *res;
+ void __iomem *mmio;
+ int rc;
+
+ rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL);
+ if (!rifsc_controller)
+ return -ENOMEM;
+
+ mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ rifsc_controller->dev = &pdev->dev;
+ rifsc_controller->mmio = mmio;
+ rifsc_controller->name = dev_driver_string(rifsc_controller->dev);
+ rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
+ rifsc_controller->grant_access = stm32_rifsc_grant_access;
+ rifsc_controller->release_access = stm32_rifsc_release_access;
+
+ /* Get number of RIFSC entries*/
+ nb_risup = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF1_MASK;
+ nb_rimu = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF2_MASK;
+ nb_risal = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF3_MASK;
+ rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal;
+
+ platform_set_drvdata(pdev, rifsc_controller);
+
+ rc = stm32_firewall_controller_register(rifsc_controller);
+ if (rc) {
+ dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d",
+ rc);
+ return rc;
+ }
+
+ rc = stm32_firewall_populate_bus(rifsc_controller);
+ if (rc) {
+ dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d",
+ rc);
+ return rc;
+ }
+
+ /* Populate all allowed nodes */
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id stm32_rifsc_of_match[] = {
+ { .compatible = "st,stm32mp25-rifsc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match);
+
+static struct platform_driver stm32_rifsc_driver = {
+ .probe = stm32_rifsc_probe,
+ .driver = {
+ .name = "stm32-rifsc",
+ .of_match_table = stm32_rifsc_of_match,
+ },
+};
+module_platform_driver(stm32_rifsc_driver);
+
+MODULE_AUTHOR("Gatien Chevallier <[email protected]>");
+MODULE_DESCRIPTION("STMicroelectronics RIFSC driver");
+MODULE_LICENSE("GPL");
--
2.25.1
On Tue, 12 Dec 2023 16:23:49 +0100, Gatien Chevallier wrote:
> Allows tracking dependencies between devices and their access
> controller.
>
> Signed-off-by: Gatien Chevallier <[email protected]>
> ---
>
> Changes in V6:
> - Renamed access-controller to access-controllers
>
> Changes in V5:
> - Rename feature-domain* to access-control*
>
> Patch not present in V1
>
> drivers/of/property.c | 2 ++
> 1 file changed, 2 insertions(+)
>
Acked-by: Rob Herring <[email protected]>