arch: arc: Add mpu support
* add arc mpu driver
* modify the corresponding kconfig and kbuild
* currently only em_starterkit 2.2's em7d configuration
has mpu feature (mpu version 2)
* as the minimum region size of arc mpu version 2 is 2048 bytes and
region size should be power of 2, the stack size of threads
(including main thread and idle thread) should be at least
2048 bytes and power of 2
* for mpu stack guard feature, a stack guard region of 2048 bytes
is generated. This brings more memory footprint
* For arc mpu version 3, the minimum region size is 32 bytes.
* the codes are tested by the mpu_stack_guard_test and stackprot
Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index d23344f..7ee9d27 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -186,6 +186,21 @@
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 32d9331..435210e 100644
--- a/arch/arc/core/Makefile
+++ b/arch/arc/core/Makefile
@@ -16,3 +16,5 @@
# 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 d864ab2..e65fb7d 100644
--- a/arch/arc/core/fast_irq.S
+++ b/arch/arc/core/fast_irq.S
@@ -257,6 +257,13 @@
*/
_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 0000000..bbbd9ff
--- /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 0000000..f789723
--- /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 0000000..e534cf1
--- /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 <device.h>
+#include <init.h>
+#include <kernel.h>
+#include <soc.h>
+#include <arch/arc/v2/mpu/arc_core_mpu.h>
+
+#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 0000000..4401552
--- /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 <device.h>
+#include <init.h>
+#include <kernel.h>
+#include <soc.h>
+#include <arch/arc/v2/aux_regs.h>
+#include <arch/arc/v2/mpu/arc_mpu.h>
+#include <arch/arc/v2/mpu/arc_core_mpu.h>
+#include <logging/sys_log.h>
+
+
+#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 dabab07..0c1919a 100644
--- a/arch/arc/core/regular_irq.S
+++ b/arch/arc/core/regular_irq.S
@@ -161,6 +161,13 @@
*/
_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 0274701..88954c3e 100644
--- a/arch/arc/core/swap.S
+++ b/arch/arc/core/swap.S
@@ -110,6 +110,13 @@
_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 9766939..f05c2a1 100644
--- a/arch/arc/soc/em7d/Kbuild
+++ b/arch/arc/soc/em7d/Kbuild
@@ -5,4 +5,6 @@
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 501c5d1..56038b4 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 0000000..1e854ab
--- /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 <soc.h>
+ #include <arch/arc/v2/mpu/arc_mpu.h>
+
+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 6516083..e2f1587 100644
--- a/boards/arc/em_starterkit/Kconfig
+++ b/boards/arc/em_starterkit/Kconfig
@@ -16,6 +16,7 @@
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 b51f6d7..454ff9f 100644
--- a/include/arch/arc/arch.h
+++ b/include/arch/arc/arch.h
@@ -38,7 +38,37 @@
#include <arch/arc/v2/addr_types.h>
#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 0000000..65feb39
--- /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 0000000..914eb41
--- /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_ */