diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index d23344f7d42..7ee9d273661 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -186,6 +186,21 @@ config FP_SHARING endmenu +menu "ARC MPU Options" +depends on CPU_HAS_MPU + +config ARC_MPU_ENABLE + bool "Enable MPU" + depends on CPU_HAS_MPU + select ARC_MPU + default n + help + Enable MPU + +source "arch/arc/core/mpu/Kconfig" + +endmenu + config ICCM_SIZE int "ICCM Size in kB" help diff --git a/arch/arc/core/Makefile b/arch/arc/core/Makefile index 32d93310962..435210ec381 100644 --- a/arch/arc/core/Makefile +++ b/arch/arc/core/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o # Some ARC cores like the EM4 lack the atomic LLOCK/SCOND and # can't use these. obj-$(CONFIG_ATOMIC_OPERATIONS_CUSTOM) += atomic.o + +obj-$(CONFIG_CPU_HAS_MPU) += mpu/ diff --git a/arch/arc/core/fast_irq.S b/arch/arc/core/fast_irq.S index d864ab246a4..e65fb7dd4ec 100644 --- a/arch/arc/core/fast_irq.S +++ b/arch/arc/core/fast_irq.S @@ -257,6 +257,13 @@ _firq_reschedule: */ _load_callee_saved_regs +#ifdef CONFIG_MPU_STACK_GUARD + push_s r2 + mov r0, r2 + bl configure_mpu_stack_guard + pop_s r2 +#endif + ld_s r3, [r2, _thread_offset_to_relinquish_cause] breq r3, _CAUSE_RIRQ, _firq_return_from_rirq diff --git a/arch/arc/core/mpu/Kconfig b/arch/arc/core/mpu/Kconfig new file mode 100644 index 00000000000..bbbd9ff48d1 --- /dev/null +++ b/arch/arc/core/mpu/Kconfig @@ -0,0 +1,38 @@ +# Kconfig - Memory Protection Unit (MPU) configuration options + +# +# Copyright (c) 2017 Synopsys +# +# SPDX-License-Identifier: Apache-2.0 +# +config ARC_MPU_VER + int + prompt "ARC MPU version" + range 2 4 + default 2 + help + ARC MPU has several versions. For MPU v2, the minimum region is 2048 bytes; + For MPU v3, the minimum region is 32 bytes + +config ARC_CORE_MPU + bool "ARC Core MPU functionalities" + depends on CPU_HAS_MPU + select THREAD_STACK_INFO + default n + help + ARC Core MPU functionalities + +config MPU_STACK_GUARD + bool "Thread Stack Guards" + depends on ARC_CORE_MPU + default n + help + Enable Thread Stack Guards via MPU + +config ARC_MPU + bool "ARC MPU Support" + depends on CPU_HAS_MPU + select ARC_CORE_MPU + default n + help + Target has ARC MPU (currently only works for EMSK 2.2 ARCEM7D) \ No newline at end of file diff --git a/arch/arc/core/mpu/Makefile b/arch/arc/core/mpu/Makefile new file mode 100644 index 00000000000..f789723e9a4 --- /dev/null +++ b/arch/arc/core/mpu/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ARC_CORE_MPU) += arc_core_mpu.o +obj-$(CONFIG_ARC_MPU) += arc_mpu.o diff --git a/arch/arc/core/mpu/arc_core_mpu.c b/arch/arc/core/mpu/arc_core_mpu.c new file mode 100644 index 00000000000..e534cf1c40c --- /dev/null +++ b/arch/arc/core/mpu/arc_core_mpu.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Synopsys. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_MPU_STACK_GUARD) +/* + * @brief Configure MPU stack guard + * + * This function configures per thread stack guards reprogramming the MPU. + * The functionality is meant to be used during context switch. + * + * @param thread thread info data structure. + */ +void configure_mpu_stack_guard(struct k_thread *thread) +{ + arc_core_mpu_disable(); + arc_core_mpu_configure(THREAD_STACK_GUARD_REGION, + thread->stack_info.start - STACK_ALIGN, + thread->stack_info.size); + arc_core_mpu_enable(); +} +#endif diff --git a/arch/arc/core/mpu/arc_mpu.c b/arch/arc/core/mpu/arc_mpu.c new file mode 100644 index 00000000000..44015529c1e --- /dev/null +++ b/arch/arc/core/mpu/arc_mpu.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017 Synopsys. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define AUX_MPU_RDB_VALID_MASK (0x1) +#define AUX_MPU_EN_ENABLE (0x40000000) +#define AUX_MPU_EN_DISABLE (0x0) + +#define AUX_MPU_RDP_REGION_SIZE(bits) \ + (((bits - 1) & 0x3) | (((bits - 1) & 0x1C) << 7)) + +#define _ARC_V2_MPU_EN (0x409) +#define _ARC_V2_MPU_RDB0 (0x422) +#define _ARC_V2_MPU_RDP0 (0x423) + +/* For MPU version 2, the minimum protection region size is 2048 bytes */ +/* FOr MPU version 3, the minimum protection region size is 32 bytes */ +#if CONFIG_ARC_MPU_VER == 2 +#define ARC_FEATURE_MPU_ALIGNMENT_BITS 11 +#elif CONFIG_ARC_MPU_VER == 3 +#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5 +#endif + +/** + * @brief Get the number of supported mpu regions + * + */ +static inline u8_t _get_num_regions(void) +{ + u32_t num = _arc_v2_aux_reg_read(_ARC_V2_MPU_BUILD); + + num = (num & 0xFF00) >> 8; + + return (u8_t)num; +} + + +/** + * This internal function is utilized by the MPU driver to parse the intent + * type (i.e. THREAD_STACK_REGION) and return the correct parameter set. + */ +static inline u32_t _get_region_attr_by_type(u32_t type, u32_t size) +{ + switch (type) { + case THREAD_STACK_REGION: + return 0; + case THREAD_STACK_GUARD_REGION: + /* no Read, Write and Execute to guard region */ + return AUX_MPU_RDP_REGION_SIZE( + ARC_FEATURE_MPU_ALIGNMENT_BITS); + default: + /* Size 0 region */ + return 0; + } +} + +static inline void _region_init(u32_t index, u32_t region_addr, + u32_t region_attr) +{ + + index = 2 * index; + + _arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr); + _arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr); +} + + +/* ARC Core MPU Driver API Implementation for ARC MPU */ + +/** + * @brief enable the MPU + */ +void arc_core_mpu_enable(void) +{ + /* Enable MPU */ + _arc_v2_aux_reg_write(_ARC_V2_MPU_EN, AUX_MPU_EN_ENABLE); +} + +/** + * @brief disable the MPU + */ +void arc_core_mpu_disable(void) +{ + /* Disable MPU */ + _arc_v2_aux_reg_write(_ARC_V2_MPU_EN, AUX_MPU_EN_DISABLE); +} + + +/** + * @brief configure the base address and size for an MPU region + * + * @param type MPU region type + * @param base base address in RAM + * @param size size of the region + */ +void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size) +{ + u32_t region_index; + u32_t region_attr; + + SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size); + /* + * The new MPU regions are allocated per type before + * the statically configured regions. + * + * For ARC MPU v2, MPU regions can be overlapped, smaller + * region index has higher priority. + */ + + region_index = _get_num_regions() - mpu_config.num_regions; + + if (type > region_index) { + return; + } + + region_index -= type; + region_attr = _get_region_attr_by_type(type, size); + + if (region_attr == 0) { + return; + } + + base |= AUX_MPU_RDB_VALID_MASK; + + _region_init(region_index, base, region_attr); +} + +/* ARC MPU Driver Initial Setup */ + +/* + * @brief MPU default configuration + * + * This function provides the default configuration mechanism for the Memory + * Protection Unit (MPU). + */ +static void _arc_mpu_config(void) +{ + u32_t r_index; + u32_t num_regions; + u32_t i; + + num_regions = _get_num_regions(); + + /* ARC MPU supports up to 16 Regions */ + if (mpu_config.num_regions > num_regions) { + return; + } + + /* + * the MPU regions are filled in the reverse order. + * According to ARCv2 ISA, the mpu region with smaller + * index has higher priority. The static background mpu + * regions in mpu_config will be in the bottom. Then + * the special type regions will be above. + * + */ + r_index = num_regions - mpu_config.num_regions; + /* Disable MPU */ + arc_core_mpu_disable(); + + /* clear the regions reserved for special type */ + for (i = 0; i < r_index; i++) { + _region_init(i, 0, 0); + } + + /* configure the static regions */ + for (r_index = 0; i < num_regions; i++) { + _region_init(i, + mpu_config.mpu_regions[r_index].base + | AUX_MPU_RDB_VALID_MASK, + mpu_config.mpu_regions[r_index].attr); + r_index++; + } + + /* Enable MPU */ + arc_core_mpu_enable(); +} + +static int arc_mpu_init(struct device *arg) +{ + ARG_UNUSED(arg); + + _arc_mpu_config(); + + return 0; +} + +SYS_INIT(arc_mpu_init, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/arch/arc/core/regular_irq.S b/arch/arc/core/regular_irq.S index dabab071441..0c1919a8fae 100644 --- a/arch/arc/core/regular_irq.S +++ b/arch/arc/core/regular_irq.S @@ -161,6 +161,13 @@ _rirq_common_interrupt_swap: */ _load_callee_saved_regs +#ifdef CONFIG_MPU_STACK_GUARD + push_s r2 + mov r0, r2 + bl configure_mpu_stack_guard + pop_s r2 +#endif + ld_s r3, [r2, _thread_offset_to_relinquish_cause] breq r3, _CAUSE_RIRQ, _rirq_return_from_rirq diff --git a/arch/arc/core/swap.S b/arch/arc/core/swap.S index 02747019c45..88954c3eb8b 100644 --- a/arch/arc/core/swap.S +++ b/arch/arc/core/swap.S @@ -110,6 +110,13 @@ SECTION_FUNC(TEXT, __swap) _load_callee_saved_regs +#ifdef CONFIG_MPU_STACK_GUARD + push_s r2 + mov r0, r2 + bl configure_mpu_stack_guard + pop_s r2 +#endif + ld_s r3, [r2, _thread_offset_to_relinquish_cause] breq r3, _CAUSE_RIRQ, _swap_return_from_rirq diff --git a/arch/arc/soc/em7d/Kbuild b/arch/arc/soc/em7d/Kbuild index 9766939ae54..f05c2a17530 100644 --- a/arch/arc/soc/em7d/Kbuild +++ b/arch/arc/soc/em7d/Kbuild @@ -5,4 +5,6 @@ ccflags-y +=-I$(srctree)/drivers asflags-y := ${ccflags-y} -obj-y = soc.o soc_config.o +obj-y += soc.o soc_config.o + +obj-$(CONFIG_ARC_MPU_ENABLE) += arc_mpu_regions.o diff --git a/arch/arc/soc/em7d/Kconfig.soc b/arch/arc/soc/em7d/Kconfig.soc index 501c5d19eff..56038b4c8d0 100644 --- a/arch/arc/soc/em7d/Kconfig.soc +++ b/arch/arc/soc/em7d/Kconfig.soc @@ -1,3 +1,4 @@ config SOC_EM7D bool "Synopsys ARC EM7D" + select CPU_HAS_MPU diff --git a/arch/arc/soc/em7d/arc_mpu_regions.c b/arch/arc/soc/em7d/arc_mpu_regions.c new file mode 100644 index 00000000000..1e854ab41d9 --- /dev/null +++ b/arch/arc/soc/em7d/arc_mpu_regions.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Synopsys + * + * SPDX-License-Identifier: Apache-2.0 + */ + + #include + #include + +static struct arc_mpu_region mpu_regions[] = { +#if CONFIG_ICCM_SIZE > 0 + /* Region ICCM */ + MPU_REGION_ENTRY("ICCM", + CONFIG_ICCM_BASE_ADDRESS, + REGION_FLASH_ATTR(REGION_256K)), +#endif +#if CONFIG_DCCM_SIZE > 0 + /* Region DCCM */ + MPU_REGION_ENTRY("DCCM", + CONFIG_DCCM_BASE_ADDRESS, + REGION_RAM_ATTR(REGION_128K)), +#endif +#if CONFIG_SRAM_SIZE > 0 + /* Region DDR RAM */ + MPU_REGION_ENTRY("DDR RAM", + CONFIG_SRAM_BASE_ADDRESS, + REGION_ALL_ATTR(REGION_128M)), +#endif + /* Region Peripheral */ + MPU_REGION_ENTRY("PERIPHERAL", + 0xF0000000, + REGION_IO_ATTR(REGION_64K)), +}; + +struct arc_mpu_config mpu_config = { + .num_regions = ARRAY_SIZE(mpu_regions), + .mpu_regions = mpu_regions, +}; diff --git a/boards/arc/em_starterkit/Kconfig b/boards/arc/em_starterkit/Kconfig index 651608377e5..e2f158725c8 100644 --- a/boards/arc/em_starterkit/Kconfig +++ b/boards/arc/em_starterkit/Kconfig @@ -16,6 +16,7 @@ choice bool "2.2" config BOARD_EM_STARTERKIT_R23 + depends on (SOC_EM9D || SOC_EM11D) bool "2.3" endchoice diff --git a/include/arch/arc/arch.h b/include/arch/arc/arch.h index b51f6d7346e..454ff9fd939 100644 --- a/include/arch/arc/arch.h +++ b/include/arch/arc/arch.h @@ -38,7 +38,37 @@ extern "C" { #include #endif +#if defined(CONFIG_MPU_STACK_GUARD) +#if defined(CONFIG_ARC_CORE_MPU) +#if CONFIG_ARC_MPU_VER == 2 +/* The minimum MPU region of MPU v2 is 2048 bytes */ +#define STACK_ALIGN 2048 +#elif CONFIG_ARC_MPU_VER == 3 +#define STACK_ALIGN 32 +#endif +#else /* CONFIG_ARC_CORE_MPU */ +#error "Unsupported STACK_ALIGN" +#endif +#else /* CONFIG_MPU_STACK_GUARD */ #define STACK_ALIGN 4 +#endif + +#define _ARCH_THREAD_STACK_DEFINE(sym, size) \ + struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) \ + sym[size+STACK_ALIGN] + +#define _ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ + struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) \ + sym[nmemb][size+STACK_ALIGN] + +#define _ARCH_THREAD_STACK_MEMBER(sym, size) \ + struct _k_thread_stack_element __aligned(STACK_ALIGN) \ + sym[size+STACK_ALIGN] + +#define _ARCH_THREAD_STACK_SIZEOF(sym) (sizeof(sym) - STACK_ALIGN) + +#define _ARCH_THREAD_STACK_BUFFER(sym) ((char *)(sym + STACK_ALIGN)) + #ifdef __cplusplus } diff --git a/include/arch/arc/v2/mpu/arc_core_mpu.h b/include/arch/arc/v2/mpu/arc_core_mpu.h new file mode 100644 index 00000000000..65feb393fca --- /dev/null +++ b/include/arch/arc/v2/mpu/arc_core_mpu.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Synopsys. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _ARC_CORE_MPU_H_ +#define _ARC_CORE_MPU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The defines below represent the region types. The MPU driver is responsible + * to allocate the region accordingly to the type and set the correct + * attributes. + * + * Each MPU is different and has a different set of attributes, hence instead + * of having the attributes at this level the arm_mpu_core defines the intent + * types. + * An intent type (i.e. THREAD_STACK_GUARD) can correspond to a different set + * of operations and attributes for each MPU and it is responsibility of the + * MPU driver to select the correct ones. + * + * The intent based configuration can't fail hence at this level no error + * is returned by the configuration functions. + * If one of the operations corresponding to an intent fails the error has to + * be managed inside the MPU driver and not escalated. + */ +/* Thread Stack Region Intent Type */ +#define THREAD_STACK_REGION 0x1 +#define THREAD_STACK_GUARD_REGION 0x2 + +#if defined(CONFIG_ARC_CORE_MPU) +/* ARC Core MPU Driver API */ + +/* + * This API has to be implemented by all the MPU drivers that have + * ARC_CORE_MPU support. + */ + +/** + * @brief enable the MPU + */ +void arc_core_mpu_enable(void); + +/** + * @brief disable the MPU + */ +void arc_core_mpu_disable(void); + +/** + * @brief configure the base address and size for an MPU region + * + * @param type MPU region type + * @param base base address in RAM + * @param size size of the region + */ +void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size); +#endif /* CONFIG_ARC_CORE_MPU */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ARC_CORE_MPU_H_ */ diff --git a/include/arch/arc/v2/mpu/arc_mpu.h b/include/arch/arc/v2/mpu/arc_mpu.h new file mode 100644 index 00000000000..914eb41c4e7 --- /dev/null +++ b/include/arch/arc/v2/mpu/arc_mpu.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Synopsys. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _ARC_MPU_H_ +#define _ARC_MPU_H_ + + + +#define AUX_MPU_RDP_UE 0x008 /* allow user execution */ +#define AUX_MPU_RDP_UW 0x010 /* allow user write */ +#define AUX_MPU_RDP_UR 0x020 /* allow user read */ +#define AUX_MPU_RDP_KE 0x040 /* only allow kernel execution */ +#define AUX_MPU_RDP_KW 0x080 /* only allow kernel write */ +#define AUX_MPU_RDP_KR 0x100 /* only allow kernel read */ + + + +/* Some helper defines for common regions */ +#define REGION_RAM_ATTR(size) \ + (AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \ + AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \ + size) + +#define REGION_FLASH_ATTR(size) \ + (AUX_MPU_RDP_UE | AUX_MPU_RDP_UR | \ + AUX_MPU_RDP_KE | AUX_MPU_RDP_KR | \ + size) + +#define REGION_IO_ATTR(size) \ + (AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \ + AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \ + size) + +#define REGION_ALL_ATTR(size) \ + (AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \ + AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \ + AUX_MPU_RDP_KE | AUX_MPU_RDP_UE | \ + size) + + +#define REGION_32B 0x200 +#define REGION_64B 0x201 +#define REGION_128B 0x202 +#define REGION_256B 0x203 +#define REGION_512B 0x400 +#define REGION_1K 0x401 +#define REGION_2K 0x402 +#define REGION_4K 0x403 +#define REGION_8K 0x600 +#define REGION_16K 0x601 +#define REGION_32K 0x602 +#define REGION_64K 0x603 +#define REGION_128K 0x800 +#define REGION_256K 0x801 +#define REGION_512K 0x802 +#define REGION_1M 0x803 +#define REGION_2M 0xA00 +#define REGION_4M 0xA01 +#define REGION_8M 0xA02 +#define REGION_16M 0xA03 +#define REGION_32M 0xC00 +#define REGION_64M 0xC01 +#define REGION_128M 0xC02 +#define REGION_256M 0xC03 +#define REGION_512M 0xE00 +#define REGION_1G 0xE01 +#define REGION_2G 0xE02 +#define REGION_4G 0xE03 + +/* Region definition data structure */ +struct arc_mpu_region { + /* Region Base Address */ + u32_t base; + /* Region Name */ + const char *name; + /* Region Attributes */ + u32_t attr; +}; + +#define MPU_REGION_ENTRY(_name, _base, _attr) \ + {\ + .name = _name, \ + .base = _base, \ + .attr = _attr, \ + } + +/* MPU configuration data structure */ +struct arc_mpu_config { + /* Number of regions */ + u32_t num_regions; + /* Regions */ + struct arc_mpu_region *mpu_regions; +}; + +/* Reference to the MPU configuration */ +extern struct arc_mpu_config mpu_config; + +#endif /* _ARC_CORE_MPU_H_ */