[PATCH 01/20] ARM: add ARMv7R MPU support
Sascha Hauer
s.hauer at pengutronix.de
Fri Nov 29 03:44:16 PST 2024
This adds MPU (memory protection unit) support for ARMv7R cores.
Code is based on U-Boot-2025.01-rc1.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
arch/arm/cpu/Kconfig | 7 ++
arch/arm/cpu/Makefile | 3 +-
arch/arm/cpu/armv7r-mpu.c | 117 +++++++++++++++++++++++++++++++++
arch/arm/cpu/cpu.c | 3 +
arch/arm/cpu/uncompress.c | 2 +
arch/arm/include/asm/armv7r-mpu.h | 134 ++++++++++++++++++++++++++++++++++++++
6 files changed, 265 insertions(+), 1 deletion(-)
diff --git a/arch/arm/cpu/Kconfig b/arch/arm/cpu/Kconfig
index 6563394a7a..737586b826 100644
--- a/arch/arm/cpu/Kconfig
+++ b/arch/arm/cpu/Kconfig
@@ -156,3 +156,10 @@ config CACHE_L2X0
bool "Enable L2x0 PrimeCell"
depends on MMU && ARCH_HAS_L2X0
+config ARMV7R_MPU
+ bool
+ depends on CPU_V7
+ help
+ Some ARM systems without an MMU have instead a Memory Protection
+ Unit (MPU) that defines the type and permissions for regions of
+ memory.
diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile
index 999cc375da..1769249645 100644
--- a/arch/arm/cpu/Makefile
+++ b/arch/arm/cpu/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += cpu.o
+obj-pbl-y += cpu.o
obj-$(CONFIG_ARM_EXCEPTIONS) += exceptions_$(S64_32).o interrupts_$(S64_32).o
obj-$(CONFIG_MMU) += mmu-common.o
@@ -62,3 +62,4 @@ pbl-$(CONFIG_ARM_ATF) += atf.o
obj-pbl-y += common.o sections.o
KASAN_SANITIZE_common.o := n
+obj-pbl-$(CONFIG_ARMV7R_MPU) += armv7r-mpu.o
diff --git a/arch/arm/cpu/armv7r-mpu.c b/arch/arm/cpu/armv7r-mpu.c
new file mode 100644
index 0000000000..e28a7b3c5e
--- /dev/null
+++ b/arch/arm/cpu/armv7r-mpu.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cortex-R Memory Protection Unit specific code
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
+ * Lokesh Vutla <lokeshvutla at ti.com>
+ */
+
+#include <asm/armv7r-mpu.h>
+#include <asm/system.h>
+#include <cache.h>
+#include <asm/cache.h>
+
+/* MPU Type register definitions */
+#define MPUIR_S_SHIFT 0
+#define MPUIR_S_MASK BIT(MPUIR_S_SHIFT)
+#define MPUIR_DREGION_SHIFT 8
+#define MPUIR_DREGION_MASK (0xff << 8)
+
+/**
+ * Note:
+ * The Memory Protection Unit(MPU) allows to partition memory into regions
+ * and set individual protection attributes for each region. In absence
+ * of MPU a default map[1] will take effect. make sure to run this code
+ * from a region which has execution permissions by default.
+ * [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0460d/I1002400.html
+ */
+
+void armv7r_mpu_disable(void)
+{
+ u32 reg;
+
+ reg = get_cr();
+ reg &= ~CR_M;
+ dsb();
+ set_cr(reg);
+ isb();
+}
+
+void armv7r_mpu_enable(void)
+{
+ u32 reg;
+
+ reg = get_cr();
+ reg |= CR_M;
+ dsb();
+ set_cr(reg);
+ isb();
+}
+
+int armv7r_mpu_enabled(void)
+{
+ return get_cr() & CR_M;
+}
+
+void armv7r_mpu_config(struct mpu_region_config *rgn)
+{
+ u32 attr, val;
+
+ attr = get_attr_encoding(rgn->mr_attr);
+
+ /* MPU Region Number Register */
+ asm volatile ("mcr p15, 0, %0, c6, c2, 0" : : "r" (rgn->region_no));
+
+ /* MPU Region Base Address Register */
+ asm volatile ("mcr p15, 0, %0, c6, c1, 0" : : "r" (rgn->start_addr));
+
+ /* MPU Region Size and Enable Register */
+ if (rgn->reg_size)
+ val = (rgn->reg_size << REGION_SIZE_SHIFT) | ENABLE_REGION;
+ else
+ val = DISABLE_REGION;
+ asm volatile ("mcr p15, 0, %0, c6, c1, 2" : : "r" (val));
+
+ /* MPU Region Access Control Register */
+ val = rgn->xn << XN_SHIFT | rgn->ap << AP_SHIFT | attr;
+ asm volatile ("mcr p15, 0, %0, c6, c1, 4" : : "r" (val));
+}
+
+void armv7r_mpu_setup_regions(struct mpu_region_config *rgns, u32 num_rgns)
+{
+ u32 num, i;
+
+ asm volatile ("mrc p15, 0, %0, c0, c0, 4" : "=r" (num));
+ num = (num & MPUIR_DREGION_MASK) >> MPUIR_DREGION_SHIFT;
+ /* Regions to be configured cannot be greater than available regions */
+ if (num < num_rgns)
+ num_rgns = num;
+ /**
+ * Assuming dcache might not be enabled at this point, disabling
+ * and invalidating only icache.
+ */
+ icache_disable();
+ icache_invalidate();
+
+ armv7r_mpu_disable();
+
+ for (i = 0; i < num_rgns; i++)
+ armv7r_mpu_config(&rgns[i]);
+
+ armv7r_mpu_enable();
+
+ icache_enable();
+}
+
+#if 0
+void enable_caches(void)
+{
+ /*
+ * setup_mpu_regions() might have enabled Icache. So add a check
+ * before enabling Icache
+ */
+ if (!icache_status())
+ icache_enable();
+ dcache_enable();
+}
+#endif
diff --git a/arch/arm/cpu/cpu.c b/arch/arm/cpu/cpu.c
index b00e9e51e5..800d6b3cab 100644
--- a/arch/arm/cpu/cpu.c
+++ b/arch/arm/cpu/cpu.c
@@ -52,6 +52,8 @@ int icache_status(void)
return (get_cr () & CR_I) != 0;
}
+#ifndef __PBL__
+
/*
* SoC like the ux500 have the l2x0 always enable
* with or without MMU enable
@@ -108,3 +110,4 @@ static int arm_request_stack(void)
return 0;
}
coredevice_initcall(arm_request_stack);
+#endif
diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c
index ac1462b7b1..4657a4828e 100644
--- a/arch/arm/cpu/uncompress.c
+++ b/arch/arm/cpu/uncompress.c
@@ -65,6 +65,8 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
if (IS_ENABLED(CONFIG_MMU))
mmu_early_enable(membase, memsize);
+ else if (IS_ENABLED(CONFIG_ARMV7R_MPU))
+ set_cr(get_cr() | CR_C);
/* Add handoff data now, so arm_mem_barebox_image takes it into account */
if (boarddata)
diff --git a/arch/arm/include/asm/armv7r-mpu.h b/arch/arm/include/asm/armv7r-mpu.h
new file mode 100644
index 0000000000..1bbaeed66c
--- /dev/null
+++ b/arch/arm/include/asm/armv7r-mpu.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Vikas Manocha, <vikas.manocha at st.com> for STMicroelectronics.
+ */
+
+#ifndef _ASM_ARMV7_MPU_H
+#define _ASM_ARMV7_MPU_H
+
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+#ifdef CONFIG_CPU_V7M
+#define AP_SHIFT 24
+#define XN_SHIFT 28
+#define TEX_SHIFT 19
+#define S_SHIFT 18
+#define C_SHIFT 17
+#define B_SHIFT 16
+#else /* CONFIG_CPU_V7R */
+#define XN_SHIFT 12
+#define AP_SHIFT 8
+#define TEX_SHIFT 3
+#define S_SHIFT 2
+#define C_SHIFT 1
+#define B_SHIFT 0
+#endif /* CONFIG_CPU_V7R */
+
+#define CACHEABLE BIT(C_SHIFT)
+#define BUFFERABLE BIT(B_SHIFT)
+#define SHAREABLE BIT(S_SHIFT)
+#define REGION_SIZE_SHIFT 1
+#define ENABLE_REGION BIT(0)
+#define DISABLE_REGION 0
+
+enum region_number {
+ REGION_0 = 0,
+ REGION_1,
+ REGION_2,
+ REGION_3,
+ REGION_4,
+ REGION_5,
+ REGION_6,
+ REGION_7,
+};
+
+enum ap {
+ NO_ACCESS = 0,
+ PRIV_RW_USR_NO,
+ PRIV_RW_USR_RO,
+ PRIV_RW_USR_RW,
+ UNPREDICTABLE,
+ PRIV_RO_USR_NO,
+ PRIV_RO_USR_RO,
+};
+
+enum mr_attr {
+ STRONG_ORDER = 0,
+ SHARED_WRITE_BUFFERED,
+ O_I_WT_NO_WR_ALLOC,
+ O_I_WB_NO_WR_ALLOC,
+ O_I_NON_CACHEABLE,
+ O_I_WB_RD_WR_ALLOC,
+ DEVICE_NON_SHARED,
+};
+enum size {
+ REGION_8MB = 22,
+ REGION_16MB,
+ REGION_32MB,
+ REGION_64MB,
+ REGION_128MB,
+ REGION_256MB,
+ REGION_512MB,
+ REGION_1GB,
+ REGION_2GB,
+ REGION_4GB,
+};
+
+enum xn {
+ XN_DIS = 0,
+ XN_EN,
+};
+
+struct mpu_region_config {
+ uint32_t start_addr;
+ enum region_number region_no;
+ enum xn xn;
+ enum ap ap;
+ enum mr_attr mr_attr;
+ enum size reg_size;
+};
+
+void armv7r_mpu_disable(void);
+void armv7r_mpu_enable(void);
+int armv7r_mpu_enabled(void);
+void armv7r_mpu_config(struct mpu_region_config *rgn);
+void armv7r_mpu_setup_regions(struct mpu_region_config *rgns, u32 num_rgns);
+
+static inline u32 get_attr_encoding(u32 mr_attr)
+{
+ u32 attr;
+
+ switch (mr_attr) {
+ case STRONG_ORDER:
+ attr = SHAREABLE;
+ break;
+ case SHARED_WRITE_BUFFERED:
+ attr = BUFFERABLE;
+ break;
+ case O_I_WT_NO_WR_ALLOC:
+ attr = CACHEABLE;
+ break;
+ case O_I_WB_NO_WR_ALLOC:
+ attr = CACHEABLE | BUFFERABLE;
+ break;
+ case O_I_NON_CACHEABLE:
+ attr = 1 << TEX_SHIFT;
+ break;
+ case O_I_WB_RD_WR_ALLOC:
+ attr = (1 << TEX_SHIFT) | CACHEABLE | BUFFERABLE;
+ break;
+ case DEVICE_NON_SHARED:
+ attr = (2 << TEX_SHIFT) | BUFFERABLE;
+ break;
+ default:
+ attr = 0; /* strongly ordered */
+ break;
+ };
+
+ return attr;
+}
+
+#endif /* _ASM_ARMV7_MPU_H */
--
2.39.5
More information about the barebox
mailing list