1 /*
2  * Copyright (c) 2017 Linaro Limited.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/init.h>
9 #include <zephyr/kernel.h>
10 #include <soc.h>
11 #include "arm_core_mpu_dev.h"
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/sys/math_extras.h>
14 #include <zephyr/sys/barrier.h>
15 #include <zephyr/linker/linker-defs.h>
16 #include <zephyr/mem_mgmt/mem_attr.h>
17 #include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
18 
19 #define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_DECLARE(mpu);
22 
23 #define NODE_HAS_PROP_AND_OR(node_id, prop) \
24 	DT_NODE_HAS_PROP(node_id, prop) ||
25 
26 BUILD_ASSERT((DT_FOREACH_STATUS_OKAY_NODE_VARGS(
27 	      NODE_HAS_PROP_AND_OR, zephyr_memory_region_mpu) false) == false,
28 	      "`zephyr,memory-region-mpu` was deprecated in favor of `zephyr,memory-attr`");
29 
30 /*
31  * Global status variable holding the number of HW MPU region indices, which
32  * have been reserved by the MPU driver to program the static (fixed) memory
33  * regions.
34  */
35 static uint8_t static_regions_num;
36 
37 /* Global MPU configuration at system initialization. */
mpu_init(void)38 static void mpu_init(void)
39 {
40 #if defined(CONFIG_SOC_FAMILY_KINETIS)
41 	/* Enable clock for the Memory Protection Unit (MPU). */
42 	CLOCK_EnableClock(kCLOCK_Sysmpu0);
43 #endif
44 }
45 
46 /**
47  *  Get the number of supported MPU regions.
48  */
get_num_regions(void)49 static inline uint8_t get_num_regions(void)
50 {
51 	return FSL_FEATURE_SYSMPU_DESCRIPTOR_COUNT;
52 }
53 
54 /* @brief Partition sanity check
55  *
56  * This internal function performs run-time sanity check for
57  * MPU region start address and size.
58  *
59  * @param part Pointer to the data structure holding the partition
60  *             information (must be valid).
61  */
mpu_partition_is_valid(const struct z_arm_mpu_partition * part)62 static int mpu_partition_is_valid(const struct z_arm_mpu_partition *part)
63 {
64 	/* Partition size must be a multiple of the minimum MPU region
65 	 * size. Start address of the partition must align with the
66 	 * minimum MPU region size.
67 	 */
68 	int partition_is_valid =
69 		(part->size != 0U)
70 		&&
71 		((part->size &
72 			(~(CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE - 1)))
73 			== part->size)
74 		&&
75 		((part->start &
76 			(CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE - 1)) == 0U);
77 
78 	return partition_is_valid;
79 }
80 
81 /* This internal function performs MPU region initialization.
82  *
83  * Note:
84  *   The caller must provide a valid region index.
85  */
region_init(const uint32_t index,const struct nxp_mpu_region * region_conf)86 static void region_init(const uint32_t index,
87 	const struct nxp_mpu_region *region_conf)
88 {
89 	uint32_t region_base = region_conf->base;
90 	uint32_t region_end = region_conf->end;
91 	uint32_t region_attr = region_conf->attr.attr;
92 
93 	if (index == 0U) {
94 		/* The MPU does not allow writes from the core to affect the
95 		 * RGD0 start or end addresses nor the permissions associated
96 		 * with the debugger; it can only write the permission fields
97 		 * associated with the other masters. These protections
98 		 * guarantee that the debugger always has access to the entire
99 		 * address space.
100 		 */
101 		__ASSERT(region_base == SYSMPU->WORD[index][0],
102 			 "Region %d base address got 0x%08x expected 0x%08x",
103 			 index, region_base, (uint32_t)SYSMPU->WORD[index][0]);
104 
105 		__ASSERT(region_end == SYSMPU->WORD[index][1],
106 			 "Region %d end address got 0x%08x expected 0x%08x",
107 			 index, region_end, (uint32_t)SYSMPU->WORD[index][1]);
108 
109 		/* Changes to the RGD0_WORD2 alterable fields should be done
110 		 * via a write to RGDAAC0.
111 		 */
112 		SYSMPU->RGDAAC[index] = region_attr;
113 
114 	} else {
115 		SYSMPU->WORD[index][0] = region_base;
116 		SYSMPU->WORD[index][1] = region_end;
117 		SYSMPU->WORD[index][2] = region_attr;
118 		SYSMPU->WORD[index][3] = SYSMPU_WORD_VLD_MASK;
119 	}
120 
121 	LOG_DBG("[%02d] 0x%08x 0x%08x 0x%08x 0x%08x", index,
122 		    (uint32_t)SYSMPU->WORD[index][0],
123 		    (uint32_t)SYSMPU->WORD[index][1],
124 		    (uint32_t)SYSMPU->WORD[index][2],
125 		    (uint32_t)SYSMPU->WORD[index][3]);
126 
127 }
128 
region_allocate_and_init(const uint8_t index,const struct nxp_mpu_region * region_conf)129 static int region_allocate_and_init(const uint8_t index,
130 	const struct nxp_mpu_region *region_conf)
131 {
132 	/* Attempt to allocate new region index. */
133 	if (index > (get_num_regions() - 1)) {
134 
135 		/* No available MPU region index. */
136 		LOG_ERR("Failed to allocate new MPU region %u\n", index);
137 		return -EINVAL;
138 	}
139 
140 	LOG_DBG("Program MPU region at index 0x%x", index);
141 
142 	/* Program region */
143 	region_init(index, region_conf);
144 
145 	return index;
146 }
147 
148 #define _BUILD_REGION_CONF(reg, _ATTR)						\
149 	(struct nxp_mpu_region) { .name = (reg).dt_name,			\
150 				  .base = (reg).dt_addr,			\
151 				  .end  = (reg).dt_addr + (reg).dt_size,	\
152 				  .attr = _ATTR,				\
153 				}
154 
155 /* This internal function programs the MPU regions defined in the DT when using
156  * the `zephyr,memory-attr = <( DT_MEM_ARM(...) )>` property.
157  */
mpu_configure_regions_from_dt(uint8_t * reg_index)158 static int mpu_configure_regions_from_dt(uint8_t *reg_index)
159 {
160 	const struct mem_attr_region_t *region;
161 	size_t num_regions;
162 
163 	num_regions = mem_attr_get_regions(&region);
164 
165 	for (size_t idx = 0; idx < num_regions; idx++) {
166 		struct nxp_mpu_region region_conf;
167 
168 		switch (DT_MEM_ARM_GET(region[idx].dt_attr)) {
169 		case DT_MEM_ARM_MPU_RAM:
170 			region_conf = _BUILD_REGION_CONF(region[idx], REGION_RAM_ATTR);
171 			break;
172 #ifdef REGION_FLASH_ATTR
173 		case DT_MEM_ARM_MPU_FLASH:
174 			region_conf = _BUILD_REGION_CONF(region[idx], REGION_FLASH_ATTR);
175 			break;
176 #endif
177 #ifdef REGION_IO_ATTR
178 		case DT_MEM_ARM_MPU_IO:
179 			region_conf = _BUILD_REGION_CONF(region[idx], REGION_IO_ATTR);
180 			break;
181 #endif
182 		default:
183 			/* Either the specified `ATTR_MPU_*` attribute does not
184 			 * exists or the `REGION_*_ATTR` macro is not defined
185 			 * for that attribute.
186 			 */
187 			LOG_ERR("Invalid attribute for the region\n");
188 			return -EINVAL;
189 		}
190 
191 		if (region_allocate_and_init((*reg_index),
192 					     (const struct nxp_mpu_region *) &region_conf) < 0) {
193 			return -EINVAL;
194 		}
195 
196 		(*reg_index)++;
197 	}
198 
199 	return 0;
200 }
201 
202 /**
203  * This internal function is utilized by the MPU driver to combine a given
204  * region attribute configuration and size and fill-in a driver-specific
205  * structure with the correct MPU region attribute configuration.
206  */
get_region_attr_from_mpu_partition_info(nxp_mpu_region_attr_t * p_attr,const k_mem_partition_attr_t * attr,uint32_t base,uint32_t size)207 static inline void get_region_attr_from_mpu_partition_info(
208 	nxp_mpu_region_attr_t *p_attr,
209 	const k_mem_partition_attr_t *attr, uint32_t base, uint32_t size)
210 {
211 	/* in NXP MPU the base address and size are not required
212 	 * to determine region attributes
213 	 */
214 	(void) base;
215 	(void) size;
216 
217 	p_attr->attr = attr->ap_attr;
218 }
219 
220 /* This internal function programs an MPU region
221  * of a given configuration at a given MPU index.
222  */
mpu_configure_region(const uint8_t index,const struct z_arm_mpu_partition * new_region)223 static int mpu_configure_region(const uint8_t index,
224 	const struct z_arm_mpu_partition *new_region)
225 {
226 	struct nxp_mpu_region region_conf;
227 
228 	LOG_DBG("Configure MPU region at index 0x%x", index);
229 
230 	/* Populate internal NXP MPU region configuration structure. */
231 	region_conf.base = new_region->start;
232 	region_conf.end = (new_region->start + new_region->size - 1);
233 	get_region_attr_from_mpu_partition_info(&region_conf.attr,
234 		&new_region->attr, new_region->start, new_region->size);
235 
236 	/* Allocate and program region */
237 	return region_allocate_and_init(index,
238 		(const struct nxp_mpu_region *)&region_conf);
239 }
240 
241 #if defined(CONFIG_MPU_STACK_GUARD)
242 /* This internal function partitions the SRAM MPU region */
mpu_sram_partitioning(uint8_t index,const struct z_arm_mpu_partition * p_region)243 static int mpu_sram_partitioning(uint8_t index,
244 	const struct z_arm_mpu_partition *p_region)
245 {
246 	/*
247 	 * The NXP MPU manages the permissions of the overlapping regions
248 	 * doing the logical OR in between them, hence they can't be used
249 	 * for stack/stack guard protection. For this reason we need to
250 	 * perform a partitioning of the SRAM area in such a way that the
251 	 * guard region does not overlap with the (background) SRAM regions
252 	 * holding the default SRAM access permission configuration.
253 	 * In other words, the SRAM is split in two different regions.
254 	 */
255 
256 	/*
257 	 * SRAM partitioning needs to be performed in a strict order.
258 	 * First, we program a new MPU region with the default SRAM
259 	 * access permissions for the SRAM area _after_ the stack
260 	 * guard. Note that the permissions are stored in the global
261 	 * array:
262 	 *      'mpu_config.mpu_regions[]', on 'sram_region' index.
263 	 */
264 	struct nxp_mpu_region added_sram_region;
265 
266 	added_sram_region.base = p_region->start + p_region->size;
267 	added_sram_region.end =
268 		mpu_config.mpu_regions[mpu_config.sram_region].end;
269 	added_sram_region.attr.attr =
270 		mpu_config.mpu_regions[mpu_config.sram_region].attr.attr;
271 
272 	if (region_allocate_and_init(index,
273 				     (const struct nxp_mpu_region *)&added_sram_region) < 0) {
274 		return -EINVAL;
275 	}
276 
277 	/* Increment, as an additional region index has been consumed. */
278 	index++;
279 
280 	/* Second, adjust the original SRAM region to end at the beginning
281 	 * of the stack guard.
282 	 */
283 	struct nxp_mpu_region adjusted_sram_region;
284 
285 	adjusted_sram_region.base =
286 		mpu_config.mpu_regions[mpu_config.sram_region].base;
287 	adjusted_sram_region.end = p_region->start - 1;
288 	adjusted_sram_region.attr.attr =
289 		mpu_config.mpu_regions[mpu_config.sram_region].attr.attr;
290 
291 	region_init(mpu_config.sram_region,
292 		(const struct nxp_mpu_region *)&adjusted_sram_region);
293 
294 	return index;
295 }
296 #endif /* CONFIG_MPU_STACK_GUARD */
297 
298 /* This internal function programs a set of given MPU regions
299  * over a background memory area, optionally performing a
300  * sanity check of the memory regions to be programmed.
301  */
mpu_configure_regions(const struct z_arm_mpu_partition regions[],uint8_t regions_num,uint8_t start_reg_index,bool do_sanity_check)302 static int mpu_configure_regions(const struct z_arm_mpu_partition regions[],
303 				 uint8_t regions_num, uint8_t start_reg_index,
304 				 bool do_sanity_check)
305 {
306 	int i;
307 	int reg_index = start_reg_index;
308 
309 	for (i = 0; i < regions_num; i++) {
310 		if (regions[i].size == 0U) {
311 			continue;
312 		}
313 		/* Non-empty region. */
314 
315 		if (do_sanity_check &&
316 				(!mpu_partition_is_valid(&regions[i]))) {
317 			LOG_ERR("Partition %u: sanity check failed.", i);
318 			return -EINVAL;
319 		}
320 
321 #if defined(CONFIG_MPU_STACK_GUARD)
322 		if (regions[i].attr.ap_attr == MPU_REGION_SU_RX) {
323 			unsigned int key;
324 
325 			/* Attempt to configure an MPU Stack Guard region; this
326 			 * will require splitting of the underlying SRAM region
327 			 * into two SRAM regions, leaving out the guard area to
328 			 * be programmed afterwards.
329 			 */
330 			key = irq_lock();
331 			reg_index =
332 				mpu_sram_partitioning(reg_index, &regions[i]);
333 			irq_unlock(key);
334 		}
335 #endif /* CONFIG_MPU_STACK_GUARD */
336 
337 		if (reg_index == -EINVAL) {
338 			return reg_index;
339 		}
340 
341 		reg_index = mpu_configure_region(reg_index, &regions[i]);
342 
343 		if (reg_index == -EINVAL) {
344 			return reg_index;
345 		}
346 
347 		/* Increment number of programmed MPU indices. */
348 		reg_index++;
349 	}
350 
351 	return reg_index;
352 }
353 
354 /* This internal function programs the static MPU regions.
355  *
356  * It returns the number of MPU region indices configured.
357  *
358  * Note:
359  * If the static MPU regions configuration has not been successfully
360  * performed, the error signal is propagated to the caller of the function.
361  */
mpu_configure_static_mpu_regions(const struct z_arm_mpu_partition static_regions[],const uint8_t regions_num,const uint32_t background_area_base,const uint32_t background_area_end)362 static int mpu_configure_static_mpu_regions(
363 	const struct z_arm_mpu_partition static_regions[],
364 	const uint8_t regions_num, const uint32_t background_area_base,
365 	const uint32_t background_area_end)
366 {
367 	int mpu_reg_index = static_regions_num;
368 
369 	/* In NXP MPU architecture the static regions are
370 	 * programmed on top of SRAM region configuration.
371 	 */
372 	ARG_UNUSED(background_area_base);
373 	ARG_UNUSED(background_area_end);
374 
375 	mpu_reg_index = mpu_configure_regions(static_regions,
376 		regions_num, mpu_reg_index, true);
377 
378 	static_regions_num = mpu_reg_index;
379 
380 	return mpu_reg_index;
381 }
382 
383 /* This internal function programs the dynamic MPU regions.
384  *
385  * It returns the number of MPU region indices configured.
386  *
387  * Note:
388  * If the dynamic MPU regions configuration has not been successfully
389  * performed, the error signal is propagated to the caller of the function.
390  */
mpu_configure_dynamic_mpu_regions(const struct z_arm_mpu_partition dynamic_regions[],uint8_t regions_num)391 static int mpu_configure_dynamic_mpu_regions(
392 	const struct z_arm_mpu_partition dynamic_regions[],
393 	uint8_t regions_num)
394 {
395 	unsigned int key;
396 
397 	/*
398 	 * Programming the NXP MPU has to be done with care to avoid race
399 	 * conditions that will cause memory faults. The NXP MPU is composed
400 	 * of a number of memory region descriptors. The number of descriptors
401 	 * varies depending on the SOC. Each descriptor has a start addr, end
402 	 * addr, attribute, and valid. When the MPU is enabled, access to
403 	 * memory space is checked for access protection errors through an
404 	 * OR operation of all of the valid MPU descriptors.
405 	 *
406 	 * Writing the start/end/attribute descriptor register will clear the
407 	 * valid bit for that descriptor. This presents a problem because if
408 	 * the current program stack is in that region or if an ISR occurs
409 	 * that switches state and uses that region a memory fault will be
410 	 * triggered. Note that local variable access can also cause stack
411 	 * accesses while programming these registers depending on the compiler
412 	 * optimization level.
413 	 *
414 	 * To avoid the race condition a temporary descriptor is set to enable
415 	 * access to all of memory before the call to mpu_configure_regions()
416 	 * to configure the dynamic memory regions. After, the temporary
417 	 * descriptor is invalidated if the mpu_configure_regions() didn't
418 	 * overwrite it.
419 	 */
420 	key = irq_lock();
421 	/* Use last descriptor region as temporary descriptor */
422 	region_init(get_num_regions()-1, (const struct nxp_mpu_region *)
423 		&mpu_config.mpu_regions[mpu_config.sram_region]);
424 
425 	/* Now reset the main SRAM region */
426 	region_init(mpu_config.sram_region, (const struct nxp_mpu_region *)
427 		&mpu_config.mpu_regions[mpu_config.sram_region]);
428 	irq_unlock(key);
429 
430 	int mpu_reg_index = static_regions_num;
431 
432 	/* In NXP MPU architecture the dynamic regions are
433 	 * programmed on top of existing SRAM region configuration.
434 	 */
435 
436 	mpu_reg_index = mpu_configure_regions(dynamic_regions,
437 		regions_num, mpu_reg_index, false);
438 
439 	if (mpu_reg_index != -EINVAL) {
440 
441 		/* Disable the non-programmed MPU regions. */
442 		for (int i = mpu_reg_index; i < get_num_regions(); i++) {
443 
444 			LOG_DBG("disable region 0x%x", i);
445 			/* Disable region */
446 			SYSMPU->WORD[i][0] = 0;
447 			SYSMPU->WORD[i][1] = 0;
448 			SYSMPU->WORD[i][2] = 0;
449 			SYSMPU->WORD[i][3] = 0;
450 		}
451 	}
452 
453 	return mpu_reg_index;
454 }
455 
456 /* ARM Core MPU Driver API Implementation for NXP MPU */
457 
458 /**
459  * @brief enable the MPU
460  */
arm_core_mpu_enable(void)461 void arm_core_mpu_enable(void)
462 {
463 	/* Enable MPU */
464 	SYSMPU->CESR |= SYSMPU_CESR_VLD_MASK;
465 
466 	/* Make sure that all the registers are set before proceeding */
467 	barrier_dsync_fence_full();
468 	barrier_isync_fence_full();
469 }
470 
471 /**
472  * @brief disable the MPU
473  */
arm_core_mpu_disable(void)474 void arm_core_mpu_disable(void)
475 {
476 	/* Force any outstanding transfers to complete before disabling MPU */
477 	barrier_dmem_fence_full();
478 
479 	/* Disable MPU */
480 	SYSMPU->CESR &= ~SYSMPU_CESR_VLD_MASK;
481 	/* Clear MPU error status */
482 	SYSMPU->CESR |=  SYSMPU_CESR_SPERR_MASK;
483 }
484 
485 #if defined(CONFIG_USERSPACE)
486 
mpu_region_get_base(uint32_t r_index)487 static inline uint32_t mpu_region_get_base(uint32_t r_index)
488 {
489 	return SYSMPU->WORD[r_index][0];
490 }
491 
mpu_region_get_size(uint32_t r_index)492 static inline uint32_t mpu_region_get_size(uint32_t r_index)
493 {
494 	/* <END> + 1 - <BASE> */
495 	return (SYSMPU->WORD[r_index][1] + 1) - SYSMPU->WORD[r_index][0];
496 }
497 
498 /**
499  * This internal function checks if region is enabled or not.
500  *
501  * Note:
502  *   The caller must provide a valid region number.
503  */
is_enabled_region(uint32_t r_index)504 static inline int is_enabled_region(uint32_t r_index)
505 {
506 	return SYSMPU->WORD[r_index][3] & SYSMPU_WORD_VLD_MASK;
507 }
508 
509 /**
510  * This internal function checks if the given buffer is in the region.
511  *
512  * Note:
513  *   The caller must provide a valid region number.
514  */
is_in_region(uint32_t r_index,uint32_t start,uint32_t size)515 static inline int is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
516 {
517 	uint32_t r_addr_start;
518 	uint32_t r_addr_end;
519 	uint32_t end;
520 
521 	r_addr_start = SYSMPU->WORD[r_index][0];
522 	r_addr_end = SYSMPU->WORD[r_index][1];
523 
524 	size = size == 0U ? 0U : size - 1U;
525 	if (u32_add_overflow(start, size, &end)) {
526 		return 0;
527 	}
528 
529 	if ((start >= r_addr_start) && (end <= r_addr_end)) {
530 		return 1;
531 	}
532 
533 	return 0;
534 }
535 
536 /**
537  * @brief update configuration of an active memory partition
538  */
arm_core_mpu_mem_partition_config_update(struct z_arm_mpu_partition * partition,k_mem_partition_attr_t * new_attr)539 void arm_core_mpu_mem_partition_config_update(
540 	struct z_arm_mpu_partition *partition,
541 	k_mem_partition_attr_t *new_attr)
542 {
543 	/* Find the partition. ASSERT if not found. */
544 	uint8_t i;
545 	uint8_t reg_index = get_num_regions();
546 
547 	for (i = static_regions_num; i < get_num_regions(); i++) {
548 		if (!is_enabled_region(i)) {
549 			continue;
550 		}
551 
552 		uint32_t base = mpu_region_get_base(i);
553 
554 		if (base != partition->start) {
555 			continue;
556 		}
557 
558 		uint32_t size = mpu_region_get_size(i);
559 
560 		if (size != partition->size) {
561 			continue;
562 		}
563 
564 		/* Region found */
565 		reg_index = i;
566 		break;
567 	}
568 	__ASSERT(reg_index != get_num_regions(),
569 		 "Memory domain partition not found\n");
570 
571 	/* Modify the permissions */
572 	partition->attr = *new_attr;
573 	mpu_configure_region(reg_index, partition);
574 }
575 
576 /**
577  * @brief get the maximum number of available (free) MPU region indices
578  *        for configuring dynamic MPU partitions
579  */
arm_core_mpu_get_max_available_dyn_regions(void)580 int arm_core_mpu_get_max_available_dyn_regions(void)
581 {
582 	return get_num_regions() - static_regions_num;
583 }
584 
585 /**
586  * This internal function checks if the region is user accessible or not
587  *
588  * Note:
589  *   The caller must provide a valid region number.
590  */
is_user_accessible_region(uint32_t r_index,int write)591 static inline int is_user_accessible_region(uint32_t r_index, int write)
592 {
593 	uint32_t r_ap = SYSMPU->WORD[r_index][2];
594 
595 	if (write != 0) {
596 		return (r_ap & MPU_REGION_WRITE) == MPU_REGION_WRITE;
597 	}
598 
599 	return (r_ap & MPU_REGION_READ) == MPU_REGION_READ;
600 }
601 
602 /**
603  * @brief validate the given buffer is user accessible or not
604  */
arm_core_mpu_buffer_validate(const void * addr,size_t size,int write)605 int arm_core_mpu_buffer_validate(const void *addr, size_t size, int write)
606 {
607 	uint8_t r_index;
608 
609 	/* Iterate through all MPU regions */
610 	for (r_index = 0U; r_index < get_num_regions(); r_index++) {
611 		if (!is_enabled_region(r_index) ||
612 		    !is_in_region(r_index, (uint32_t)addr, size)) {
613 			continue;
614 		}
615 
616 		/* For NXP MPU, priority is given to granting permission over
617 		 * denying access for overlapping region.
618 		 * So we can stop the iteration immediately once we find the
619 		 * matched region that grants permission.
620 		 */
621 		if (is_user_accessible_region(r_index, write)) {
622 			return 0;
623 		}
624 	}
625 
626 	return -EPERM;
627 }
628 
629 #endif /* CONFIG_USERSPACE */
630 
631 /**
632  * @brief configure fixed (static) MPU regions.
633  */
arm_core_mpu_configure_static_mpu_regions(const struct z_arm_mpu_partition static_regions[],const uint8_t regions_num,const uint32_t background_area_start,const uint32_t background_area_end)634 void arm_core_mpu_configure_static_mpu_regions(
635 	const struct z_arm_mpu_partition static_regions[],
636 	const uint8_t regions_num, const uint32_t background_area_start,
637 	const uint32_t background_area_end)
638 {
639 	if (mpu_configure_static_mpu_regions(static_regions, regions_num,
640 					     background_area_start,
641 					     background_area_end) == -EINVAL) {
642 
643 		__ASSERT(0, "Configuring %u static MPU regions failed\n",
644 			regions_num);
645 	}
646 }
647 
648 /**
649  * @brief configure dynamic MPU regions.
650  */
arm_core_mpu_configure_dynamic_mpu_regions(const struct z_arm_mpu_partition dynamic_regions[],uint8_t regions_num)651 void arm_core_mpu_configure_dynamic_mpu_regions(
652 	const struct z_arm_mpu_partition dynamic_regions[], uint8_t regions_num)
653 {
654 	if (mpu_configure_dynamic_mpu_regions(dynamic_regions,
655 					      regions_num) == -EINVAL) {
656 
657 		__ASSERT(0, "Configuring %u dynamic MPU regions failed\n",
658 			regions_num);
659 	}
660 }
661 
662 /* NXP MPU Driver Initial Setup */
663 
664 /*
665  * @brief MPU default configuration
666  *
667  * This function provides the default configuration mechanism for the Memory
668  * Protection Unit (MPU).
669  */
z_arm_mpu_init(void)670 int z_arm_mpu_init(void)
671 {
672 	uint32_t r_index;
673 
674 	if (mpu_config.num_regions > get_num_regions()) {
675 		/* Attempt to configure more MPU regions than
676 		 * what is supported by hardware. As this operation
677 		 * may be executed during system (pre-kernel) initialization,
678 		 * we want to ensure we can detect an attempt to
679 		 * perform invalid configuration.
680 		 */
681 		__ASSERT(0,
682 			"Request to configure: %u regions (supported: %u)\n",
683 			mpu_config.num_regions,
684 			get_num_regions()
685 		);
686 		return -1;
687 	}
688 
689 	LOG_DBG("total region count: %d", get_num_regions());
690 
691 	arm_core_mpu_disable();
692 
693 	/* Architecture-specific configuration */
694 	mpu_init();
695 
696 	/* Program fixed regions configured at SOC definition. */
697 	for (r_index = 0U; r_index < mpu_config.num_regions; r_index++) {
698 		region_init(r_index, &mpu_config.mpu_regions[r_index]);
699 	}
700 
701 	/* Update the number of programmed MPU regions. */
702 	static_regions_num = mpu_config.num_regions;
703 
704 	/* DT-defined MPU regions. */
705 	if (mpu_configure_regions_from_dt(&static_regions_num) == -EINVAL) {
706 		__ASSERT(0, "Failed to allocate MPU regions from DT\n");
707 		return -EINVAL;
708 	}
709 
710 	arm_core_mpu_enable();
711 
712 	return 0;
713 }
714