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@xxxxxxxxxxxxxx> --- 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@xxxxxx> + */ + +#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@xxxxxx> 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