arch: arm: clean up MPU code for ARM and NXP

* We are now *much* better at not reserving unnecessary
system MPU regions based on configuration. The #defines
for intent are now an enumerated type. As a bonus, the
implementation of _get_region_index_by_type() is much
simpler. Previously we were wasting regions for stack guard
and application memory if they were not configured.

* NXP MPU doesn't reserve the last region if HW stack
protection isn't enabled.

* Certain parts of the MPU code are now properly ifdef'd
based on configuration.

* THREAD_STACK_REGION and THREAD_STACK_USER_REGION was a
confusing construction and has now been replaced with
just THREAD_STACK_REGION, which represents the MPU region
for a user mode thread stack. Supervisor mode stacks
do not require an MPU region.

* The bounds of CONFIG_APPLICATION_MEMORY never changes
and we just do it once during initialization instead of
every context switch.

* Assertions have been added to catch out-of-bounds cases.

Fixes: #7384

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
diff --git a/arch/arm/core/cortex_m/mpu/arm_mpu.c b/arch/arm/core/cortex_m/mpu/arm_mpu.c
index e5ae942..59a8f6c 100644
--- a/arch/arm/core/cortex_m/mpu/arm_mpu.c
+++ b/arch/arm/core/cortex_m/mpu/arm_mpu.c
@@ -60,21 +60,27 @@
  */
 static inline u32_t _get_region_attr_by_type(u32_t type, u32_t size)
 {
+#if defined(CONFIG_USERSPACE) || defined(CONFIG_MPU_STACK_GUARD) || \
+	defined(CONFIG_APPLICATION_MEMORY)
 	int region_size = _size_to_mpu_rasr_size(size);
+#endif
 
 	switch (type) {
-	case THREAD_STACK_USER_REGION:
-		return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
-					1, 0, region_size);
+#ifdef CONFIG_USERSPACE
 	case THREAD_STACK_REGION:
 		return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
 					1, 0, region_size);
+#endif
+#ifdef CONFIG_MPU_STACK_GUARD
 	case THREAD_STACK_GUARD_REGION:
 		return _get_region_attr(1, P_RO_U_NA, 0, 1, 0,
 					1, 0, region_size);
+#endif
+#ifdef CONFIG_APPLICATION_MEMORY
 	case THREAD_APP_DATA_REGION:
 		return _get_region_attr(1, P_RW_U_RW, 0, 1, 0,
 					1, 0, region_size);
+#endif
 	default:
 		/* Size 0 region */
 		return 0;
@@ -122,35 +128,18 @@
  */
 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, therefore we need to subtract by one to get the region
-	 * index.
-	 */
-	switch (type) {
-	case THREAD_STACK_USER_REGION:
-		return mpu_config.num_regions + THREAD_STACK_REGION - 1;
-	case THREAD_STACK_REGION:
-	case THREAD_STACK_GUARD_REGION:
-	case THREAD_APP_DATA_REGION:
-		return mpu_config.num_regions + type - 1;
-	case THREAD_DOMAIN_PARTITION_REGION:
-#if defined(CONFIG_USERSPACE)
-		return mpu_config.num_regions + type - 1;
-#elif defined(CONFIG_MPU_STACK_GUARD)
-		return mpu_config.num_regions + type - 2;
-#else
-		/*
-		 * Start domain partition region from stack guard region
-		 * since stack guard is not enabled.
-		 */
-		return mpu_config.num_regions + type - 3;
-#endif
-	default:
-		__ASSERT(0, "Unsupported type");
-		return 0;
-	}
+	u32_t region_index;
+
+	__ASSERT(type < THREAD_MPU_REGION_LAST,
+		 "unsupported region type");
+
+	region_index = mpu_config.num_regions + type;
+
+	__ASSERT(region_index < _get_num_regions(),
+		 "out of MPU regions, requested %u max is %u",
+		 region_index, _get_num_regions() - 1);
+
+	return region_index;
 }
 
 /**
@@ -212,32 +201,6 @@
 	return 0;
 }
 
-/**
- * This internal function checks if the region is user accessible or not.
- *
- * Note:
- *   The caller must provide a valid region number.
- */
-static inline int _is_user_accessible_region(u32_t r_index, int write)
-{
-	u32_t r_ap;
-
-	ARM_MPU_DEV->rnr = r_index;
-	r_ap = ARM_MPU_DEV->rasr & MPU_RASR_AP_Msk;
-
-	/* always return true if this is the thread stack region */
-	if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
-		return 1;
-	}
-
-	if (write) {
-		return r_ap == P_RW_U_RW;
-	}
-
-	/* For all user accessible permissions, their AP[1] bit is l */
-	return r_ap & (0x2 << MPU_RASR_AP_Pos);
-}
-
 /* ARM Core MPU Driver API Implementation for ARM MPU */
 
 /**
@@ -285,8 +248,8 @@
 {
 	u32_t base = (u32_t)thread->stack_obj;
 	u32_t size = thread->stack_info.size;
-	u32_t index = _get_region_index_by_type(THREAD_STACK_USER_REGION);
-	u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_USER_REGION,
+	u32_t index = _get_region_index_by_type(THREAD_STACK_REGION);
+	u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_REGION,
 						     size);
 
 	if (!thread->arch.priv_stack_start) {
@@ -298,19 +261,6 @@
 	}
 	/* configure stack */
 	_region_init(index, base, region_attr);
-
-#if defined(CONFIG_APPLICATION_MEMORY)
-	/* configure app data portion */
-	index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
-	if (index < _get_num_regions()) {
-		size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;
-		region_attr =
-			_get_region_attr_by_type(THREAD_APP_DATA_REGION, size);
-		if (size > 0) {
-			_region_init(index, (u32_t)&__app_ram_start, region_attr);
-		}
-	}
-#endif /* CONFIG_APPLICATION_MEMORY */
 }
 
 /**
@@ -406,7 +356,35 @@
 }
 
 /**
+ * This internal function checks if the region is user accessible or not.
+ *
+ * Note:
+ *   The caller must provide a valid region number.
+ */
+static inline int _is_user_accessible_region(u32_t r_index, int write)
+{
+	u32_t r_ap;
+
+	ARM_MPU_DEV->rnr = r_index;
+	r_ap = ARM_MPU_DEV->rasr & MPU_RASR_AP_Msk;
+
+	/* always return true if this is the thread stack region */
+	if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
+		return 1;
+	}
+
+	if (write) {
+		return r_ap == P_RW_U_RW;
+	}
+
+	/* For all user accessible permissions, their AP[1] bit is l */
+	return r_ap & (0x2 << MPU_RASR_AP_Pos);
+}
+
+/**
  * @brief validate the given buffer is user accessible or not
+ *
+ * Presumes the background mapping is NOT user accessible.
  */
 int arm_core_mpu_buffer_validate(void *addr, size_t size, int write)
 {
@@ -443,7 +421,7 @@
  * This function provides the default configuration mechanism for the Memory
  * Protection Unit (MPU).
  */
-static void _arm_mpu_config(void)
+static int arm_mpu_init(struct device *arg)
 {
 	u32_t r_index;
 
@@ -459,9 +437,11 @@
 			mpu_config.num_regions,
 			_get_num_regions()
 		);
-		return;
+		return -1;
 	}
 
+	SYS_LOG_DBG("total region count: %d", _get_num_regions());
+
 	/* Disable MPU */
 	ARM_MPU_DEV->ctrl = 0;
 
@@ -477,16 +457,20 @@
 	 */
 	ARM_MPU_DEV->ctrl = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;
 
+#if defined(CONFIG_APPLICATION_MEMORY)
+	u32_t index, size, region_attr;
+
+	/* configure app data portion */
+	index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
+	size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;
+	region_attr = _get_region_attr_by_type(THREAD_APP_DATA_REGION, size);
+	if (size > 0) {
+		_region_init(index, (u32_t)&__app_ram_start, region_attr);
+	}
+#endif
 	/* Make sure that all the registers are set before proceeding */
 	__DSB();
 	__ISB();
-}
-
-static int arm_mpu_init(struct device *arg)
-{
-	ARG_UNUSED(arg);
-
-	_arm_mpu_config();
 
 	/* Sanity check for number of regions in Cortex-M0+, M3, and M4. */
 #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \
diff --git a/arch/arm/core/cortex_m/mpu/nxp_mpu.c b/arch/arm/core/cortex_m/mpu/nxp_mpu.c
index 0e2dfb9..133efea 100644
--- a/arch/arm/core/cortex_m/mpu/nxp_mpu.c
+++ b/arch/arm/core/cortex_m/mpu/nxp_mpu.c
@@ -24,15 +24,19 @@
 static inline u32_t _get_region_attr_by_type(u32_t type)
 {
 	switch (type) {
-	case THREAD_STACK_USER_REGION:
-		return REGION_USER_MODE_ATTR;
+#ifdef CONFIG_USERSPACE
 	case THREAD_STACK_REGION:
-		return REGION_RAM_ATTR;
+		return REGION_USER_MODE_ATTR;
+#endif
+#ifdef CONFIG_MPU_STACK_GUARD
 	case THREAD_STACK_GUARD_REGION:
 		/* The stack guard region has to be not accessible */
 		return REGION_RO_ATTR;
+#endif
+#ifdef CONFIG_APPLICATION_MEMORY
 	case THREAD_APP_DATA_REGION:
 		return REGION_USER_MODE_ATTR;
+#endif
 	default:
 		/* Size 0 region */
 		return 0;
@@ -44,6 +48,19 @@
 	return FSL_FEATURE_SYSMPU_DESCRIPTOR_COUNT;
 }
 
+static inline u8_t _get_num_usable_regions(void)
+{
+	u8_t max = _get_num_regions();
+#ifdef CONFIG_MPU_STACK_GUARD
+	/* Last region reserved for stack guard.
+	 * See comments in nxp_mpu_setup_sram_region()
+	 */
+	return max - 1;
+#else
+	return max;
+#endif
+}
+
 static void _region_init(u32_t index, u32_t region_base,
 			 u32_t region_end, u32_t region_attr)
 {
@@ -88,35 +105,19 @@
  */
 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, therefore we need to subtract by one to get the region
-	 * index.
-	 */
-	switch (type) {
-	case THREAD_STACK_USER_REGION:
-		return mpu_config.num_regions + THREAD_STACK_REGION - 1;
-	case THREAD_STACK_REGION:
-	case THREAD_STACK_GUARD_REGION:
-	case THREAD_APP_DATA_REGION:
-		return mpu_config.num_regions + type - 1;
-	case THREAD_DOMAIN_PARTITION_REGION:
-#if defined(CONFIG_USERSPACE)
-		return mpu_config.num_regions + type - 1;
-#elif defined(CONFIG_MPU_STACK_GUARD)
-		return mpu_config.num_regions + type - 2;
-#else
-		/*
-		 * Start domain partition region from stack guard region
-		 * since stack guard is not enabled.
-		 */
-		return mpu_config.num_regions + type - 3;
-#endif
-	default:
-		__ASSERT(0, "Unsupported type");
-		return 0;
-	}
+	u32_t region_index;
+
+	__ASSERT(type < THREAD_MPU_REGION_LAST,
+		 "unsupported region type");
+
+
+	region_index = mpu_config.num_regions + type;
+
+	__ASSERT(region_index < _get_num_usable_regions(),
+		 "out of MPU regions, requested %u max is %u",
+		 region_index, _get_num_usable_regions() - 1);
+
+	return region_index;
 }
 
 /**
@@ -145,25 +146,7 @@
 	return 0;
 }
 
-/**
- * This internal function checks 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 = SYSMPU->WORD[r_index][2];
-
-	/* always return true if this is the thread stack region */
-	if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
-		return 1;
-	}
-
-	if (write) {
-		return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE;
-	}
-
-	return (r_ap & MPU_REGION_READ) == MPU_REGION_READ;
-}
-
+#ifdef CONFIG_MPU_STACK_GUARD
 static void nxp_mpu_setup_sram_region(u32_t base, u32_t size)
 {
 	u32_t last_region = _get_num_regions() - 1;
@@ -201,6 +184,7 @@
 			mpu_config.mpu_regions[mpu_config.sram_region].attr);
 
 }
+#endif /* CONFIG_MPU_STACK_GUARD */
 
 /* ARM Core MPU Driver API Implementation for NXP MPU */
 
@@ -245,17 +229,14 @@
 	u32_t region_index = _get_region_index_by_type(type);
 	u32_t region_attr = _get_region_attr_by_type(type);
 
-	/* NXP MPU supports up to 16 Regions */
-	if (region_index > _get_num_regions() - 2) {
-		return;
-	}
-
 	_region_init(region_index, base,
 		     ENDADDR_ROUND(base + size),
 		     region_attr);
+#ifdef CONFIG_MPU_STACK_GUARD
 	if (type == THREAD_STACK_GUARD_REGION) {
 		nxp_mpu_setup_sram_region(base, size);
 	}
+#endif
 }
 
 #if defined(CONFIG_USERSPACE)
@@ -263,27 +244,11 @@
 {
 	u32_t base = (u32_t)thread->stack_info.start;
 	u32_t size = thread->stack_info.size;
-	u32_t index = _get_region_index_by_type(THREAD_STACK_USER_REGION);
-	u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_USER_REGION);
+	u32_t index = _get_region_index_by_type(THREAD_STACK_REGION);
+	u32_t region_attr = _get_region_attr_by_type(THREAD_STACK_REGION);
 
 	/* configure stack */
 	_region_init(index, base, ENDADDR_ROUND(base + size), region_attr);
-
-#if defined(CONFIG_APPLICATION_MEMORY)
-	/* configure app data portion */
-	index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
-	region_attr = _get_region_attr_by_type(THREAD_APP_DATA_REGION);
-	base = (u32_t)&__app_ram_start;
-	size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;
-
-	/* set up app data region if exists, otherwise disable */
-	if (size > 0) {
-		_region_init(index, base, ENDADDR_ROUND(base + size),
-			     region_attr);
-	} else {
-		SYSMPU->WORD[index][3] = 0;
-	}
-#endif
 }
 
 /**
@@ -313,7 +278,7 @@
 	 * Don't touch the last region, it is reserved for SRAM_1 region.
 	 * See comments in arm_core_mpu_configure().
 	 */
-	for (; region_index < _get_num_regions() - 1; region_index++) {
+	for (; region_index < _get_num_usable_regions(); region_index++) {
 		if (num_partitions && pparts->size) {
 			SYS_LOG_DBG("set region 0x%x 0x%x 0x%x",
 				    region_index, pparts->start, pparts->size);
@@ -392,12 +357,29 @@
 	/*
 	 * Subtract the start of domain partition regions from total regions
 	 * should get the maximum number of free regions for memory domain
-	 * partitions. But we need to consume an extra 1 region to make
-	 * stack/stack guard protection work properly.
-	 * See the comments in arm_core_mpu_configure().
+	 * partitions
 	 */
-	return _get_num_regions() -
-		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1;
+	return _get_num_usable_regions() -
+		_get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+}
+
+/**
+ * This internal function checks 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 = SYSMPU->WORD[r_index][2];
+
+	/* always return true if this is the thread stack region */
+	if (_get_region_index_by_type(THREAD_STACK_REGION) == r_index) {
+		return 1;
+	}
+
+	if (write) {
+		return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE;
+	}
+
+	return (r_ap & MPU_REGION_READ) == MPU_REGION_READ;
 }
 
 /**
@@ -405,10 +387,10 @@
  */
 int arm_core_mpu_buffer_validate(void *addr, size_t size, int write)
 {
-	u32_t r_index;
+	u8_t r_index;
 
 	/* Iterate all MPU regions */
-	for (r_index = 0; r_index < _get_num_regions(); r_index++) {
+	for (r_index = 0; r_index < _get_num_usable_regions(); r_index++) {
 		if (!_is_enabled_region(r_index) ||
 		    !_is_in_region(r_index, (u32_t)addr, size)) {
 			continue;
@@ -440,12 +422,9 @@
 {
 	u32_t r_index;
 
-	SYS_LOG_DBG("region number: %d", _get_num_regions());
-
-	/* NXP MPU supports up to 16 Regions */
-	if (mpu_config.num_regions > _get_num_regions()) {
-		return;
-	}
+	__ASSERT(mpu_config.num_regions <= _get_num_regions(),
+		 "too many static MPU regions defined");
+	SYS_LOG_DBG("total region count: %d", _get_num_regions());
 
 	/* Disable MPU */
 	SYSMPU->CESR &= ~SYSMPU_CESR_VLD_MASK;
@@ -467,6 +446,23 @@
 
 	nxp_mpu_enabled = 1;
 
+#if defined(CONFIG_APPLICATION_MEMORY)
+	u32_t index, region_attr, base, size;
+
+	/* configure app data portion */
+	index = _get_region_index_by_type(THREAD_APP_DATA_REGION);
+	region_attr = _get_region_attr_by_type(THREAD_APP_DATA_REGION);
+	base = (u32_t)&__app_ram_start;
+	size = (u32_t)&__app_ram_end - (u32_t)&__app_ram_start;
+
+	/* set up app data region if exists, otherwise disable */
+	if (size > 0) {
+		_region_init(index, base, ENDADDR_ROUND(base + size),
+			     region_attr);
+	} else {
+		SYSMPU->WORD[index][3] = 0;
+	}
+#endif
 	/* Make sure that all the registers are set before proceeding */
 	__DSB();
 	__ISB();
diff --git a/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h b/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h
index c4601c8..6c2a48c 100644
--- a/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h
+++ b/include/arch/arm/cortex_m/mpu/arm_core_mpu_dev.h
@@ -30,11 +30,21 @@
  * be managed inside the MPU driver and not escalated.
  */
 /* Thread Stack Region Intent Type */
-#define THREAD_STACK_USER_REGION 0x0	/* fake region for user mode stack */
-#define THREAD_STACK_REGION 0x1
-#define THREAD_APP_DATA_REGION 0x2
-#define THREAD_STACK_GUARD_REGION 0x3
-#define THREAD_DOMAIN_PARTITION_REGION 0x4
+enum {
+#ifdef CONFIG_USERSPACE
+	THREAD_STACK_REGION,
+#endif
+#ifdef CONFIG_APPLICATION_MEMORY
+	THREAD_APP_DATA_REGION,
+#endif
+#ifdef CONFIG_MPU_STACK_GUARD
+	THREAD_STACK_GUARD_REGION,
+#endif
+#ifdef CONFIG_USERSPACE
+	THREAD_DOMAIN_PARTITION_REGION,
+#endif
+	THREAD_MPU_REGION_LAST
+};
 
 #if defined(CONFIG_ARM_CORE_MPU)
 struct k_mem_domain;