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_ */