arch: arc: Add the intial support of memory domain

Refering the ARM's implementation, the initial support of memory
domain in ARC is added:
* changes in MPU drivers
* changes in Kconfig
* codes to configure memory domain during thread swap
* changes in linker script template
* memory domain related macro definitions

the commited codes are simply tested through
samples/mpu/mem_domain_apis_test.

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
diff --git a/arch/arc/core/fast_irq.S b/arch/arc/core/fast_irq.S
index e65fb7d..7ce27fe 100644
--- a/arch/arc/core/fast_irq.S
+++ b/arch/arc/core/fast_irq.S
@@ -264,6 +264,14 @@
 	pop_s r2
 #endif
 
+#ifdef CONFIG_USERSPACE
+	push_s r2
+	mov r0, r2
+	bl configure_mpu_mem_domain
+	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/arc_core_mpu.c b/arch/arc/core/mpu/arc_core_mpu.c
index 2c4a196..163a995 100644
--- a/arch/arc/core/mpu/arc_core_mpu.c
+++ b/arch/arc/core/mpu/arc_core_mpu.c
@@ -9,6 +9,7 @@
 #include <kernel.h>
 #include <soc.h>
 #include <arch/arc/v2/mpu/arc_core_mpu.h>
+#include <logging/sys_log.h>
 
 #if defined(CONFIG_MPU_STACK_GUARD)
 /*
@@ -28,3 +29,61 @@
 	arc_core_mpu_enable();
 }
 #endif
+
+#if defined(CONFIG_USERSPACE)
+/*
+ * @brief Configure MPU memory domain
+ *
+ * This function configures per thread memory domain reprogramming the MPU.
+ * The functionality is meant to be used during context switch.
+ *
+ * @param thread thread info data structure.
+ */
+void configure_mpu_mem_domain(struct k_thread *thread)
+{
+	SYS_LOG_DBG("configure thread %p's domain", thread);
+	arc_core_mpu_disable();
+	arc_core_mpu_configure_mem_domain(thread->mem_domain_info.mem_domain);
+	arc_core_mpu_enable();
+}
+
+int _arch_mem_domain_max_partitions_get(void)
+{
+	return arc_core_mpu_get_max_domain_partition_regions();
+}
+
+/*
+ * Reset MPU region for a single memory partition
+ */
+void _arch_mem_domain_partition_remove(struct k_mem_domain *domain,
+				       u32_t  partition_id)
+{
+	ARG_UNUSED(domain);
+
+	arc_core_mpu_disable();
+	arc_core_mpu_mem_partition_remove(partition_id);
+	arc_core_mpu_enable();
+
+}
+
+/*
+ * Destroy MPU regions for the mem domain
+ */
+void _arch_mem_domain_destroy(struct k_mem_domain *domain)
+{
+	ARG_UNUSED(domain);
+
+	arc_core_mpu_disable();
+	arc_core_mpu_configure_mem_domain(NULL);
+	arc_core_mpu_enable();
+}
+
+/*
+ * Validate the given buffer is user accessible or not
+ */
+int _arch_buffer_validate(void *addr, size_t size, int write)
+{
+	return arc_core_mpu_buffer_validate(addr, size, write);
+}
+
+#endif
diff --git a/arch/arc/core/mpu/arc_mpu.c b/arch/arc/core/mpu/arc_mpu.c
index 83937b0..1f282d9 100644
--- a/arch/arc/core/mpu/arc_mpu.c
+++ b/arch/arc/core/mpu/arc_mpu.c
@@ -47,7 +47,7 @@
 
 
 /**
- * @brief Get the number of supported mpu regions
+ * @brief Get the number of supported MPU regions
  *
  */
 static inline u8_t _get_num_regions(void)
@@ -59,26 +59,18 @@
 	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)
+static inline u32_t _get_region_attr_by_type(u32_t type)
 {
 	switch (type) {
 	case THREAD_STACK_REGION:
 		return 0;
 	case THREAD_STACK_GUARD_REGION:
 	/* no Write and Execute to guard region */
-#if CONFIG_ARC_MPU_VER == 2
-		u8_t bits = find_msb_set(size) + 1;
-
-		return  AUX_MPU_RDP_REGION_SIZE(bits) |
-			AUX_MPU_RDP_UR | AUX_MPU_RDP_KR;
-#elif CONFIG_ARC_MPU_VER == 3
 		return AUX_MPU_RDP_UR | AUX_MPU_RDP_KR;
-#endif
 	default:
 		/* Size 0 region */
 		return 0;
@@ -90,15 +82,23 @@
 {
 /* ARC MPU version 2 and version 3 have different aux reg interface */
 #if CONFIG_ARC_MPU_VER == 2
-	u8_t bits = find_msb_set(size) + 1;
+	u8_t bits = find_msb_set(size) - 1;
 	index = 2 * index;
 
 	if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) {
 		bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
 	}
 
-	region_addr |= (AUX_MPU_RDP_REGION_SIZE(bits) |
-			 AUX_MPU_RDB_VALID_MASK);
+	if ((1 << bits) < size) {
+		bits++;
+	}
+
+	if (size > 0) {
+		region_attr |= AUX_MPU_RDP_REGION_SIZE(bits);
+		region_addr |= AUX_MPU_RDB_VALID_MASK;
+	} else {
+		region_addr = 0;
+	}
 
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
@@ -109,11 +109,12 @@
 		size = (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS);
 	}
 
-/* all mpu regions SID are the same: 1, the default SID */
+/* all MPU regions SID are the same: 1, the default SID */
 	if (region_attr) {
 		region_attr |=  (AUX_MPU_RDB_VALID_MASK | AUX_MPU_RDP_S |
 				 AUX_MPU_RPER_SID1);
 	}
+
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, region_addr);
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_REND,
@@ -122,7 +123,6 @@
 #endif
 }
 
-
 #if CONFIG_ARC_MPU_VER == 3
 static inline s32_t _mpu_probe(u32_t addr)
 {
@@ -140,6 +140,136 @@
 }
 #endif
 
+/**
+ * This internal function is utilized by the MPU driver to parse the intent
+ * type (i.e. THREAD_STACK_REGION) and return the correct region index.
+ */
+static inline u32_t _get_region_index_by_type(u32_t type)
+{
+	/*
+	 * The new MPU regions are allocated per type after the statically
+	 * configured regions. The type is one-indexed rather than
+	 * zero-indexed.
+	 *
+	 * For ARC MPU v2, the smaller index has higher priority, so the
+	 * index is allocated in reverse order. Static regions start from
+	 * the biggest index, then thread related regions.
+	 *
+	 * For ARC MPU v3, each index has the same priority, so the index is
+	 * allocated from small to big. Static regions start from 0, then
+	 * thread related regions.
+	 */
+	switch (type) {
+#if CONFIG_ARC_MPU_VER  == 2
+	case THREAD_STACK_REGION:
+		return _get_num_regions() - mpu_config.num_regions - type;
+	case THREAD_STACK_GUARD_REGION:
+		return _get_num_regions() - mpu_config.num_regions - type;
+	case THREAD_DOMAIN_PARTITION_REGION:
+#if defined(CONFIG_MPU_STACK_GUARD)
+		return _get_num_regions() - mpu_config.num_regions - type;
+#else
+		/*
+		 * Start domain partition region from stack guard region
+		 * since stack guard is not enabled.
+		 */
+		return _get_num_regions() - mpu_config.num_regions - type + 1;
+#endif
+#elif CONFIG_ARC_MPU_VER == 3
+	case THREAD_STACK_REGION:
+		return mpu_config.num_regions + type - 1;
+	case THREAD_STACK_GUARD_REGION:
+		return mpu_config.num_regions + type - 1;
+	case THREAD_DOMAIN_PARTITION_REGION:
+#if defined(CONFIG_MPU_STACK_GUARD)
+		return mpu_config.num_regions + type - 1;
+#else
+		/*
+		 * Start domain partition region from stack guard region
+		 * since stack guard is not enabled.
+		 */
+		return mpu_config.num_regions + type - 2;
+#endif
+#endif
+	default:
+		__ASSERT(0, "Unsupported type");
+		return 0;
+	}
+}
+
+/**
+ * This internal function checks if region is enabled or not
+ */
+static inline int _is_enabled_region(u32_t r_index)
+{
+#if CONFIG_ARC_MPU_VER == 2
+	return ((_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + 2 * r_index)
+		& AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
+#elif CONFIG_ARC_MPU_VER == 3
+	_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index);
+	return ((_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER) &
+		 AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
+#endif
+}
+
+/**
+ * This internal function check if the given buffer in in the region
+ */
+static inline int _is_in_region(u32_t r_index, u32_t start, u32_t size)
+{
+#if CONFIG_ARC_MPU_VER == 2
+	u32_t r_addr_start;
+	u32_t r_addr_end;
+	u32_t r_size_lshift;
+
+	r_addr_start = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + 2 * r_index)
+			& (~AUX_MPU_RDB_VALID_MASK);
+	r_size_lshift = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + 2 * r_index)
+			& AUX_MPU_RDP_ATTR_MASK;
+	r_size_lshift = (r_size_lshift & 0x3) | ((r_size_lshift >> 7) & 0x1C);
+	r_addr_end = r_addr_start  + (1 << (r_size_lshift + 1));
+
+	if (start >= r_addr_start && (start + size) < r_addr_end) {
+		return 1;
+	}
+
+#elif CONFIG_ARC_MPU_VER == 3
+
+	if ((r_index == _mpu_probe(start)) &&
+		(r_index == _mpu_probe(start + size))) {
+		return 1;
+	}
+#endif
+
+
+
+	return 0;
+}
+
+/**
+ * This internal function check if the region is user accessible or not
+ */
+static inline int _is_user_accessible_region(u32_t r_index, int write)
+{
+	u32_t r_ap;
+
+#if CONFIG_ARC_MPU_VER == 2
+	r_ap = _arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + 2 * r_index);
+#elif CONFIG_ARC_MPU_VER == 3
+	_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index);
+	r_ap = _arc_v2_aux_reg_read(_ARC_V2_MPU_RPER);
+#endif
+	r_ap &= AUX_MPU_RDP_ATTR_MASK;
+
+	if (write) {
+		return ((r_ap & (AUX_MPU_RDP_UW | AUX_MPU_RDP_KW)) ==
+			(AUX_MPU_RDP_UW | AUX_MPU_RDP_KW));
+	}
+
+	return ((r_ap & (AUX_MPU_RDP_UR | AUX_MPU_RDP_KR)) ==
+			(AUX_MPU_RDP_UR | AUX_MPU_RDP_KR));
+}
+
 /* ARC Core MPU Driver API Implementation for ARC MPU */
 
 /**
@@ -153,7 +283,7 @@
 		_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE);
 
 	/* MPU is always enabled, use default region to
-	 * simulate mpu enable
+	 * simulate MPU enable
 	 */
 #elif CONFIG_ARC_MPU_VER == 3
 	arc_core_mpu_default(0);
@@ -171,13 +301,12 @@
 		_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE);
 #elif CONFIG_ARC_MPU_VER == 3
 	/* MPU is always enabled, use default region to
-	 * simulate mpu disable
+	 * simulate MPU disable
 	 */
 	arc_core_mpu_default(REGION_ALL_ATTR);
 #endif
 }
 
-
 /**
  * @brief configure the base address and size for an MPU region
  *
@@ -187,10 +316,14 @@
  */
 void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size)
 {
-	u32_t region_index;
-	u32_t region_attr;
+	u32_t region_index =  _get_region_index_by_type(type);
+	u32_t region_attr = _get_region_attr_by_type(type);
 
 	SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size);
+
+	if (region_attr == 0) {
+		return;
+	}
 	/*
 	 * The new MPU regions are allocated per type before
 	 * the statically configured regions.
@@ -200,38 +333,12 @@
 	 * 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;
-	}
-
 	_region_init(region_index, base, size, region_attr);
 #elif CONFIG_ARC_MPU_VER == 3
 	static s32_t last_index;
 	s32_t index;
 	u32_t last_region = _get_num_regions() - 1;
 
-	region_index = mpu_config.num_regions + type - 1;
-
-	if (region_index > last_region) {
-		return;
-	}
-
-	region_attr = _get_region_attr_by_type(type, size);
-
-	if (region_attr == 0) {
-		return;
-	}
 
 	/* use hardware probe to find the region maybe split.
 	 * another way is to look up the mpu_config.mpu_regions
@@ -290,9 +397,8 @@
 	_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr | val);
 }
 
-
 /**
- * @brief configure the mpu region
+ * @brief configure the MPU region
  *
  * @param   index   MPU region index
  * @param   base    base address
@@ -310,6 +416,161 @@
 	_region_init(index, base, size, region_attr);
 }
 
+#if defined(CONFIG_USERSPACE)
+/**
+ * @brief configure MPU regions for the memory partitions of the memory domain
+ *
+ * @param   mem_domain    memory domain that thread belongs to
+ */
+void arc_core_mpu_configure_mem_domain(struct k_mem_domain *mem_domain)
+{
+	s32_t region_index =
+		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+	u32_t num_partitions;
+	struct k_mem_partition *pparts;
+
+	if (mem_domain) {
+		SYS_LOG_DBG("configure domain: %p", mem_domain);
+		num_partitions = mem_domain->num_partitions;
+		pparts = mem_domain->partitions;
+	} else {
+		SYS_LOG_DBG("disable domain partition regions");
+		num_partitions = 0;
+		pparts = NULL;
+	}
+#if CONFIG_ARC_MPU_VER == 2
+	for (; region_index >= 0; region_index--) {
+#elif CONFIG_ARC_MPU_VER == 3
+/*
+ * Note: For ARC MPU v3, overlapping is not allowed, so the following
+ * partitions/region may be overlapped with each other or regions in
+ * mpu_config. This will cause EV_MachineCheck exception (ECR = 0x030600).
+ * Although split mechanism is used for stack guard region to avoid this,
+ * it doesn't work for memory domain, because the dynamic region numbers.
+ * So be careful to avoid the overlap situation.
+ */
+	for (; region_index <  _get_num_regions() - 1; region_index++) {
+#endif
+		if (num_partitions && pparts->size) {
+			SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
+				    region_index, pparts->start, pparts->size);
+			_region_init(region_index, pparts->start, pparts->size,
+					pparts->attr);
+			num_partitions--;
+		} else {
+			SYS_LOG_DBG("disable region 0x%x", region_index);
+			/* Disable region */
+			_region_init(region_index, 0, 0, 0);
+		}
+		pparts++;
+	}
+}
+
+/**
+ * @brief configure MPU region for a single memory partition
+ *
+ * @param   part_index  memory partition index
+ * @param   part        memory partition info
+ */
+void arc_core_mpu_configure_mem_partition(u32_t part_index,
+					  struct k_mem_partition *part)
+{
+	u32_t region_index =
+		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+
+	SYS_LOG_DBG("configure partition index: %u", part_index);
+
+	if (part) {
+		SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
+			    region_index + part_index, part->start, part->size);
+		_region_init(region_index, part->start, part->size,
+				part->attr);
+	} else {
+		SYS_LOG_DBG("disable region 0x%x", region_index + part_index);
+		/* Disable region */
+		_region_init(region_index + part_index, 0, 0, 0);
+	}
+}
+
+/**
+ * @brief Reset MPU region for a single memory partition
+ *
+ * @param   part_index  memory partition index
+ */
+void arc_core_mpu_mem_partition_remove(u32_t part_index)
+{
+	u32_t region_index =
+		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+
+	SYS_LOG_DBG("disable region 0x%x", region_index + part_index);
+	/* Disable region */
+	_region_init(region_index + part_index, 0, 0, 0);
+}
+
+/**
+ * @brief get the maximum number of free regions for memory domain partitions
+ */
+int arc_core_mpu_get_max_domain_partition_regions(void)
+{
+#if CONFIG_ARC_MPU_VER == 2
+	return _get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1;
+#elif CONFIG_ARC_MPU_VER == 3
+	/*
+	 * Subtract the start of domain partition regions and 1 reserved region
+	 * from total regions will get the maximum number of free regions for
+	 * memory domain partitions.
+	 */
+	return _get_num_regions() -
+		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1;
+#endif
+}
+
+/**
+ * @brief validate the given buffer is user accessible or not
+ */
+int arc_core_mpu_buffer_validate(void *addr, size_t size, int write)
+{
+	s32_t r_index;
+
+
+	/*
+	 * For ARC MPU v2, smaller region number takes priority.
+	 * we can stop the iteration immediately once we find the
+	 * matched region that grants permission or denies access.
+	 *
+	 * For ARC MPU v3, overlapping is not supported.
+	 * we can stop the iteration immediately once we find the
+	 * matched region that grants permission or denies access.
+	 */
+#if CONFIG_ARC_MPU_VER == 2
+	for (r_index = 0; r_index < _get_num_regions(); r_index++) {
+		if (!_is_enabled_region(r_index) ||
+		    !_is_in_region(r_index, (u32_t)addr, size)) {
+			continue;
+		}
+
+		if (_is_user_accessible_region(r_index, write)) {
+			return 0;
+		} else {
+			return -EPERM;
+		}
+	}
+#elif CONFIG_ARC_MPU_VER == 3
+	r_index = _mpu_probe((u32_t)addr);
+	/*  match and the area is in one region */
+	if (r_index >= 0 && r_index == _mpu_probe((u32_t)addr + size)) {
+		if (_is_user_accessible_region(r_index, write)) {
+			return 0;
+		} else {
+			return -EPERM;
+		}
+	}
+#endif
+
+	return -EPERM;
+}
+#endif /* CONFIG_USERSPACE */
+
 /* ARC MPU Driver Initial Setup */
 
 /*
@@ -337,8 +598,8 @@
 	u32_t r_index;
 	/*
 	 * 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
+	 * 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.
 	 *
@@ -346,16 +607,16 @@
 	r_index = num_regions - mpu_config.num_regions;
 
 	/* clear all the regions first */
-	for (i = 0; i < num_regions; i++) {
+	for (i = 0; i < r_index; i++) {
 		_region_init(i, 0, 0, 0);
 	}
 
 	/* configure the static regions */
-	for (r_index = 0; i < num_regions; i++) {
-		_region_init(i,
-			mpu_config.mpu_regions[r_index].base,
-			mpu_config.mpu_regions[r_index].size,
-			mpu_config.mpu_regions[r_index].attr);
+	for (i = 0; i < mpu_config.num_regions; i++) {
+		_region_init(r_index,
+			mpu_config.mpu_regions[i].base,
+			mpu_config.mpu_regions[i].size,
+			mpu_config.mpu_regions[i].attr);
 		r_index++;
 	}
 
diff --git a/arch/arc/core/regular_irq.S b/arch/arc/core/regular_irq.S
index ad7dc70..643f34a 100644
--- a/arch/arc/core/regular_irq.S
+++ b/arch/arc/core/regular_irq.S
@@ -168,6 +168,13 @@
 	pop_s r2
 #endif
 
+#ifdef CONFIG_USERSPACE
+	push_s r2
+	mov r0, r2
+	bl configure_mpu_mem_domain
+	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 6096dd1..2f9ac8e 100644
--- a/arch/arc/core/swap.S
+++ b/arch/arc/core/swap.S
@@ -117,6 +117,13 @@
 	pop_s r2
 #endif
 
+#ifdef CONFIG_USERSPACE
+	push_s r2
+	mov r0, r2
+	bl configure_mpu_mem_domain
+	pop_s r2
+#endif
+
 	ld_s r3, [r2, _thread_offset_to_relinquish_cause]
 
 	breq r3, _CAUSE_RIRQ, _swap_return_from_rirq
diff --git a/boards/arc/em_starterkit/em_starterkit_em7d_defconfig b/boards/arc/em_starterkit/em_starterkit_em7d_defconfig
index e1825db..13ff2b4 100644
--- a/boards/arc/em_starterkit/em_starterkit_em7d_defconfig
+++ b/boards/arc/em_starterkit/em_starterkit_em7d_defconfig
@@ -18,3 +18,4 @@
 CONFIG_UART_NS16550_PORT_0=n
 CONFIG_UART_INTERRUPT_DRIVEN=y
 CONFIG_GPIO=y
+CONFIG_ARC_MPU_ENABLE=y
diff --git a/include/arch/arc/arch.h b/include/arch/arc/arch.h
index 9d505d8..c799aa7 100644
--- a/include/arch/arc/arch.h
+++ b/include/arch/arc/arch.h
@@ -78,7 +78,128 @@
 
 #define _ARCH_THREAD_STACK_BUFFER(sym) ((char *)(sym + STACK_GUARD_SIZE))
 
+#ifdef CONFIG_USERSPACE
+#ifdef CONFIG_ARC_MPU
+#ifndef _ASMLANGUAGE
+#include <arch/arc/v2/mpu/arc_mpu.h>
 
+#define K_MEM_PARTITION_P_NA_U_NA	AUX_MPU_RDP_N
+#define K_MEM_PARTITION_P_RW_U_RW	(AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \
+					 AUX_MPU_RDP_KW | AUX_MPU_RDP_KR)
+#define K_MEM_PARTITION_P_RW_U_RO	(AUX_MPU_RDP_UR | \
+					 AUX_MPU_RDP_KW | AUX_MPU_RDP_KR)
+#define K_MEM_PARTITION_P_RW_U_NA	(AUX_MPU_RDP_KW | AUX_MPU_RDP_KR)
+#define K_MEM_PARTITION_P_RO_U_RO	(AUX_MPU_RDP_UR | AUX_MPU_RDP_KR)
+#define K_MEM_PARTITION_P_RO_U_NA	(AUX_MPU_RDP_KR)
+
+/* Execution-allowed attributes */
+#define K_MEM_PARTITION_P_RWX_U_RWX	(AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \
+					 AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \
+					 AUX_MPU_RDP_KE | AUX_MPU_RDP_UE)
+#define K_MEM_PARTITION_P_RWX_U_RX	(AUX_MPU_RDP_UR | \
+					 AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \
+					 AUX_MPU_RDP_KE | AUX_MPU_RDP_UE)
+#define K_MEM_PARTITION_P_RX_U_RX	(AUX_MPU_RDP_UR | \
+					 AUX_MPU_RDP_KR | \
+					 AUX_MPU_RDP_KE | AUX_MPU_RDP_UE)
+
+#define K_MEM_PARTITION_IS_WRITABLE(attr) \
+	({ \
+		int __is_writable__; \
+		attr &= (AUX_MPU_RDP_UW | AUX_MPU_RDP_KW); \
+		switch (attr) { \
+		case (AUX_MPU_RDP_UW | AUX_MPU_RDP_KW): \
+		case AUX_MPU_RDP_UW: \
+		case AUX_MPU_RDP_KW: \
+			__is_writable__ = 1; \
+			break; \
+		default: \
+			__is_writable__ = 0; \
+			break; \
+		} \
+		__is_writable__; \
+	})
+#define K_MEM_PARTITION_IS_EXECUTABLE(attr) \
+	((attr) & (AUX_MPU_RDP_KE | AUX_MPU_RDP_UE))
+
+#endif /* _ASMLANGUAGE */
+
+#if CONFIG_ARC_MPU_VER == 2
+#define _ARCH_MEM_PARTITION_ALIGN_CHECK(start, size) \
+	BUILD_ASSERT_MSG(!(((size) & ((size) - 1))) && (size) >= STACK_ALIGN \
+		 && !((u32_t)(start) & ((size) - 1)), \
+		"the size of the partition must be power of 2" \
+		" and greater than or equal to the mpu adddress alignment." \
+		"start address of the partition must align with size.")
+#elif CONFIG_ARC_MPU_VER == 3
+#define _ARCH_MEM_PARTITION_ALIGN_CHECK(start, size) \
+	BUILD_ASSERT_MSG((size) % STACK_ALIGN == 0 && (size) >= STACK_ALIGN \
+		 && (u32_t)(start) % STACK_ALIGN == 0, \
+		"the size of the partition must align with 32" \
+		" and greater than or equal to 32." \
+		"start address of the partition must align with 32.")
+#endif
+#endif /* CONFIG_ARC_MPU*/
+#endif /* CONFIG_USERSPACE */
+
+#ifndef _ASMLANGUAGE
+/* Typedef for the k_mem_partition attribute*/
+typedef u32_t k_mem_partition_attr_t;
+#endif /* _ASMLANGUAGE */
+
+#ifdef CONFIG_USERSPACE
+#ifndef _ASMLANGUAGE
+/* Syscall invocation macros. arc-specific machine constraints used to ensure
+ * args land in the proper registers. Currently, they are all stub functions
+ * just for enabling CONFIG_USERSPACE on arc w/o errors.
+ */
+
+static inline u32_t _arch_syscall_invoke6(u32_t arg1, u32_t arg2, u32_t arg3,
+					  u32_t arg4, u32_t arg5, u32_t arg6,
+					  u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke5(u32_t arg1, u32_t arg2, u32_t arg3,
+					  u32_t arg4, u32_t arg5, u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke4(u32_t arg1, u32_t arg2, u32_t arg3,
+					  u32_t arg4, u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke3(u32_t arg1, u32_t arg2, u32_t arg3,
+					  u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke2(u32_t arg1, u32_t arg2, u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke1(u32_t arg1, u32_t call_id)
+{
+	return 0;
+}
+
+static inline u32_t _arch_syscall_invoke0(u32_t call_id)
+{
+	return 0;
+}
+
+static inline int _arch_is_user_context(void)
+{
+	return 0;
+}
+#endif /* _ASMLANGUAGE */
+#endif /* CONFIG_USERSPACE */
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/arch/arc/v2/linker.ld b/include/arch/arc/v2/linker.ld
index 778d2ab..56ab036 100644
--- a/include/arch/arc/v2/linker.ld
+++ b/include/arch/arc/v2/linker.ld
@@ -21,6 +21,8 @@
 #include <linker/linker-defs.h>
 #include <linker/linker-tool.h>
 
+#define KOBJECT_TEXT_AREA 256
+
 /* physical address of RAM */
 #ifdef CONFIG_HARVARD
 	#define ROMABLE_REGION ICCM
@@ -81,9 +83,12 @@
 		*(".text.*")
 		*(.gnu.linkonce.t.*)
 
-		_image_text_end = .;
+#include <linker/kobject-text.ld>
 	} GROUP_LINK_IN(ROMABLE_REGION)
 
+	_image_text_end = .;
+	_image_rodata_start = .;
+
 #include <linker/common-rom.ld>
 
 	SECTION_PROLOGUE(_RODATA_SECTION_NAME,,) {
@@ -98,34 +103,49 @@
 #include <custom-rodata.ld>
 #endif
 
+#include <linker/kobject-rom.ld>
+
 	} GROUP_LINK_IN(ROMABLE_REGION)
 
+	_image_rodata_end = .;
 	_image_rom_end = .;
-	__data_rom_start = ALIGN(4);	/* XIP imaged DATA ROM start addr */
 
 	GROUP_END(ROMABLE_REGION)
 
 	GROUP_START(RAMABLE_REGION)
 
-	SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,) {
-
-/* when XIP, .text is in ROM, but vector table must be at start of .data */
-
+#ifdef CONFIG_APPLICATION_MEMORY
+	SECTION_DATA_PROLOGUE(_APP_DATA_SECTION_NAME, (OPTIONAL),)
+	{
+		__app_ram_start = .;
+		__app_data_ram_start = .;
 		_image_ram_start = .;
-		__data_ram_start = .;
-		*(.data)
-		*(".data.*")
-
-#ifdef CONFIG_CUSTOM_RWDATA_LD
-/* Located in project source directory */
-#include <custom-rwdata.ld>
-#endif
-
+		APP_INPUT_SECTION(.data)
+		APP_INPUT_SECTION(".data.*")
+		__app_data_ram_end = .;
 	} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
 
-#include <linker/common-ram.ld>
+	__app_data_rom_start = LOADADDR(_APP_DATA_SECTION_NAME);
 
-	__data_ram_end = .;
+	SECTION_PROLOGUE(_APP_BSS_SECTION_NAME, (NOLOAD OPTIONAL),)
+	{
+		__app_bss_start = .;
+		APP_INPUT_SECTION(.bss)
+		APP_INPUT_SECTION(".bss.*")
+		APP_INPUT_SECTION(COMMON)
+		__app_bss_end = .;
+	} GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION)
+
+	__app_bss_num_words = (__app_bss_end - __app_bss_start) >> 2;
+
+	SECTION_PROLOGUE(_APP_NOINIT_SECTION_NAME, (NOLOAD OPTIONAL),)
+	{
+		APP_INPUT_SECTION(.noinit)
+		APP_INPUT_SECTION(".noinit.*")
+	} GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION)
+
+	__app_ram_end = .;
+#endif /* CONFIG_APPLICATION_MEMORY */
 
 	SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),) {
 		/*
@@ -134,9 +154,16 @@
 		 */
 		. = ALIGN(4);
 		__bss_start = .;
-		*(.bss)
-		*(".bss.*")
-		COMMON_SYMBOLS
+
+#ifndef CONFIG_APPLICATION_MEMORY
+		_image_ram_start = .;
+#endif
+		__kernel_ram_start = .;
+		KERNEL_INPUT_SECTION(.bss)
+		KERNEL_INPUT_SECTION(".bss.*")
+		KERNEL_INPUT_SECTION(COMMON)
+		*(".kernel_bss.*")
+
 		/*
 		 * BSP clears this memory in words only and doesn't clear any
 		 * potential left over bytes.
@@ -149,27 +176,43 @@
 		 * This section is used for non-initialized objects that
 		 * will not be cleared during the boot process.
 		 */
-		*(.noinit)
-		*(".noinit.*")
+		 KERNEL_INPUT_SECTION(.noinit)
+		 KERNEL_INPUT_SECTION(".noinit.*")
+		 *(".kernel_noinit.*")
 
 	} GROUP_LINK_IN(RAMABLE_REGION)
 
+	SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,) {
+
+/* when XIP, .text is in ROM, but vector table must be at start of .data */
+		__data_ram_start = .;
+		KERNEL_INPUT_SECTION(.data)
+		KERNEL_INPUT_SECTION(".data.*")
+		*(".kernel.*")
+
+#ifdef CONFIG_CUSTOM_RWDATA_LD
+/* Located in project source directory */
+#include <custom-rwdata.ld>
+#endif
+
+	} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
+
+	__data_rom_start = LOADADDR(_DATA_SECTION_NAME);
+
+#include <linker/common-ram.ld>
+#include <linker/kobject.ld>
+
+	__data_ram_end = .;
+
 	/* Define linker symbols */
 	_image_ram_end = .;
 	_end = .; /* end of image */
 
+	__kernel_ram_end = .;
+	__kernel_ram_size = __kernel_ram_end - __kernel_ram_start;
+
 	GROUP_END(RAMABLE_REGION)
 
-	/* Data Closely Coupled Memory (DCCM) */
-	GROUP_START(DCCM)
-	GROUP_END(DCCM)
-
-	SECTION_PROLOGUE(initlevel_error, (OPTIONAL),)
-	{
-		DEVICE_INIT_UNDEFINED_SECTION()
-	}
-	ASSERT(SIZEOF(initlevel_error) == 0, "Undefined initialization levels used.")
-
 #ifdef CONFIG_CUSTOM_SECTIONS_LD
 /* Located in project source directory */
 #include <custom-sections.ld>
diff --git a/include/arch/arc/v2/mpu/arc_core_mpu.h b/include/arch/arc/v2/mpu/arc_core_mpu.h
index 62f9ec5..9c67541 100644
--- a/include/arch/arc/v2/mpu/arc_core_mpu.h
+++ b/include/arch/arc/v2/mpu/arc_core_mpu.h
@@ -16,7 +16,7 @@
  * 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
+ * of having the attributes at this level the arc_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
@@ -27,9 +27,10 @@
  * 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 */
+/* Thread Region Intent Type */
 #define THREAD_STACK_REGION 0x1
 #define THREAD_STACK_GUARD_REGION 0x2
+#define THREAD_DOMAIN_PARTITION_REGION 0x3
 
 #if defined(CONFIG_ARC_CORE_MPU)
 /* ARC Core MPU Driver API */
@@ -50,7 +51,7 @@
 void arc_core_mpu_disable(void);
 
 /*
- * Before configure the MPU regions, mpu should be disabled
+ * Before configure the MPU regions, MPU should be disabled
  */
 /**
  * @brief configure the default region
@@ -60,7 +61,7 @@
 void arc_core_mpu_default(u32_t region_attr);
 
 /**
- * @brief configure the mpu region
+ * @brief configure the MPU region
  *
  * @param   index   MPU region index
  * @param   base    base address
@@ -80,6 +81,40 @@
 void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size);
 #endif /* CONFIG_ARC_CORE_MPU */
 
+
+#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);
+#endif
+
+#if defined(CONFIG_USERSPACE)
+
+void arc_core_mpu_configure_mem_domain(struct k_mem_domain *mem_domain);
+void arc_core_mpu_mem_partition_remove(u32_t part_index);
+void arc_core_mpu_configure_mem_partition(u32_t part_index,
+					  struct k_mem_partition *part);
+int arc_core_mpu_get_max_domain_partition_regions(void);
+int arc_core_mpu_buffer_validate(void *addr, size_t size, int write);
+
+/*
+ * @brief Configure MPU memory domain
+ *
+ * This function configures per thread memory domain reprogramming the MPU.
+ * The functionality is meant to be used during context switch.
+ *
+ * @param thread thread info data structure.
+ */
+void configure_mpu_mem_domain(struct k_thread *thread);
+#endif
+
+
 #ifdef __cplusplus
 }
 #endif