From f2ef204312480bfba7700f47c8ce9fb975c26557 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 26 Apr 2018 18:21:30 +0530 Subject: [PATCH] arm: v7R: Add support for MPU 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. Add support for configuring MPU on Cortex-R, by reusing the existing support for Cortex-M processor. [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0460d/I1002400.html Tested-by: Michal Simek Signed-off-by: Lokesh Vutla Reviewed-by: Tom Rini --- arch/arm/Kconfig | 12 ++++ arch/arm/cpu/armv7/Makefile | 2 + arch/arm/cpu/armv7/mpu_v7r.c | 108 +++++++++++++++++++++++++++++++ arch/arm/cpu/armv7m/Makefile | 3 +- arch/arm/cpu/armv7m/mpu.c | 43 +------------ arch/arm/include/asm/armv7_mpu.h | 130 ++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/armv7m_mpu.h | 66 ------------------- arch/arm/mach-stm32/soc.c | 2 +- 8 files changed, 257 insertions(+), 109 deletions(-) create mode 100644 arch/arm/cpu/armv7/mpu_v7r.c create mode 100644 arch/arm/include/asm/armv7_mpu.h delete mode 100644 arch/arm/include/asm/armv7m_mpu.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index f056e03..2bbb86c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -87,6 +87,15 @@ config SYS_ARM_MMU Select if you want MMU-based virtualised addressing space support by paged memory management. +config SYS_ARM_MPU + bool 'Use the ARM v7 PMSA Compliant MPU' + help + Some ARM systems without an MMU have instead a Memory Protection + Unit (MPU) that defines the type and permissions for regions of + memory. + If your CPU has an MPU then you should choose 'y' here unless you + know that you do not want to use the MPU. + # If set, the workarounds for these ARM errata are applied early during U-Boot # startup. Note that in general these options force the workarounds to be # applied; no CPU-type/version detection exists, unlike the similar options in @@ -211,11 +220,14 @@ config CPU_V7M select HAS_THUMB2 select THUMB2_KERNEL select SYS_CACHE_SHIFT_5 + select SYS_ARM_MPU config CPU_V7R bool select HAS_THUMB2 select SYS_CACHE_SHIFT_6 + select SYS_ARM_MPU + select SYS_ARM_CACHE_CP15 config CPU_PXA bool diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 97065c3..2605664 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -10,6 +10,8 @@ obj-y += cache_v7.o cache_v7_asm.o obj-y += cpu.o cp15.o obj-y += syslib.o +obj-$(CONFIG_SYS_ARM_MPU) += mpu_v7r.o + ifneq ($(CONFIG_SKIP_LOWLEVEL_INIT),y) obj-y += lowlevel_init.o endif diff --git a/arch/arm/cpu/armv7/mpu_v7r.c b/arch/arm/cpu/armv7/mpu_v7r.c new file mode 100644 index 0000000..567d913 --- /dev/null +++ b/arch/arm/cpu/armv7/mpu_v7r.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cortex-R Memory Protection Unit specific code + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* 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 disable_mpu(void) +{ + u32 reg; + + reg = get_cr(); + reg &= ~CR_M; + dsb(); + set_cr(reg); + isb(); +} + +void enable_mpu(void) +{ + u32 reg; + + reg = get_cr(); + reg |= CR_M; + dsb(); + set_cr(reg); + isb(); +} + +int mpu_enabled(void) +{ + return get_cr() & CR_M; +} + +void 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 setup_mpu_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(); + invalidate_icache_all(); + + disable_mpu(); + + for (i = 0; i < num_rgns; i++) + mpu_config(&rgns[i]); + + enable_mpu(); + + icache_enable(); +} diff --git a/arch/arm/cpu/armv7m/Makefile b/arch/arm/cpu/armv7m/Makefile index 6c78d29..baeac93 100644 --- a/arch/arm/cpu/armv7m/Makefile +++ b/arch/arm/cpu/armv7m/Makefile @@ -4,5 +4,6 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. extra-y := start.o -obj-y += cpu.o cache.o mpu.o +obj-y += cpu.o cache.o +obj-$(CONFIG_SYS_ARM_MPU) += mpu.o obj-$(CONFIG_SYS_ARCH_TIMER) += systick-timer.o diff --git a/arch/arm/cpu/armv7m/mpu.c b/arch/arm/cpu/armv7m/mpu.c index d89d9f2..81e7492 100644 --- a/arch/arm/cpu/armv7m/mpu.c +++ b/arch/arm/cpu/armv7m/mpu.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include #define V7M_MPU_CTRL_ENABLE BIT(0) @@ -15,20 +15,6 @@ #define V7M_MPU_CTRL_PRIVDEFENA BIT(2) #define VALID_REGION BIT(4) -#define ENABLE_REGION BIT(0) - -#define AP_SHIFT 24 -#define XN_SHIFT 28 -#define TEX_SHIFT 19 -#define S_SHIFT 18 -#define C_SHIFT 17 -#define B_SHIFT 16 -#define REGION_SIZE_SHIFT 1 - -#define CACHEABLE (1 << C_SHIFT) -#define BUFFERABLE (1 << B_SHIFT) -#define SHAREABLE (1 << S_SHIFT) - void disable_mpu(void) { writel(0, &V7M_MPU->ctrl); @@ -47,32 +33,7 @@ void mpu_config(struct mpu_region_config *reg_config) { uint32_t attr; - switch (reg_config->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; - }; + attr = get_attr_encoding(reg_config->mr_attr); writel(reg_config->start_addr | VALID_REGION | reg_config->region_no, &V7M_MPU->rbar); diff --git a/arch/arm/include/asm/armv7_mpu.h b/arch/arm/include/asm/armv7_mpu.h new file mode 100644 index 0000000..8f77ec4 --- /dev/null +++ b/arch/arm/include/asm/armv7_mpu.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Vikas Manocha, for STMicroelectronics. + */ + +#ifndef _ASM_ARMV7_MPU_H +#define _ASM_ARMV7_MPU_H + +#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 disable_mpu(void); +void enable_mpu(void); +int mpu_enabled(void); +void mpu_config(struct mpu_region_config *reg_config); +void setup_mpu_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 */ diff --git a/arch/arm/include/asm/armv7m_mpu.h b/arch/arm/include/asm/armv7m_mpu.h deleted file mode 100644 index 51d482a..0000000 --- a/arch/arm/include/asm/armv7m_mpu.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2017, STMicroelectronics - All Rights Reserved - * Author(s): Vikas Manocha, for STMicroelectronics. - */ - -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 disable_mpu(void); -void enable_mpu(void); -void mpu_config(struct mpu_region_config *reg_config); diff --git a/arch/arm/mach-stm32/soc.c b/arch/arm/mach-stm32/soc.c index 0259268..2b52854 100644 --- a/arch/arm/mach-stm32/soc.c +++ b/arch/arm/mach-stm32/soc.c @@ -6,7 +6,7 @@ #include #include -#include +#include int arch_cpu_init(void) { -- 2.7.4