1 /*
2  * Copyright (c) 2017 Linaro Limited.
3  * Copyright (c) 2018 Nordic Semiconductor ASA.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #ifndef ZEPHYR_ARCH_ARM_CORE_AARCH32_MPU_ARM_MPU_V7_INTERNAL_H_
9 #define ZEPHYR_ARCH_ARM_CORE_AARCH32_MPU_ARM_MPU_V7_INTERNAL_H_
10 
11 
12 #include <zephyr/sys/math_extras.h>
13 #include <arm_mpu_internal.h>
14 
15 #define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
16 #include <zephyr/logging/log.h>
17 
18 /* Global MPU configuration at system initialization. */
mpu_init(void)19 static void mpu_init(void)
20 {
21 	/* No specific configuration at init for ARMv7-M MPU. */
22 }
23 
24 /* This internal function performs MPU region initialization.
25  *
26  * Note:
27  *   The caller must provide a valid region index.
28  */
region_init(const uint32_t index,const struct arm_mpu_region * region_conf)29 static void region_init(const uint32_t index,
30 	const struct arm_mpu_region *region_conf)
31 {
32 	/* Select the region you want to access */
33 	set_region_number(index);
34 
35 	/* Configure the region */
36 #if defined(CONFIG_CPU_AARCH32_CORTEX_R)
37 	/*
38 	 * Clear size register, which disables the entry.  It cannot be
39 	 * enabled as we reconfigure it.
40 	 */
41 	set_region_size(0);
42 
43 	set_region_base_address(region_conf->base & MPU_RBAR_ADDR_Msk);
44 	set_region_attributes(region_conf->attr.rasr);
45 	set_region_size(region_conf->size | MPU_RASR_ENABLE_Msk);
46 #else
47 	MPU->RBAR = (region_conf->base & MPU_RBAR_ADDR_Msk)
48 				| MPU_RBAR_VALID_Msk | index;
49 	MPU->RASR = region_conf->attr.rasr | MPU_RASR_ENABLE_Msk;
50 	LOG_DBG("[%d] 0x%08x 0x%08x",
51 		index, region_conf->base, region_conf->attr.rasr);
52 #endif
53 }
54 
55 /* @brief Partition sanity check
56  *
57  * This internal function performs run-time sanity check for
58  * MPU region start address and size.
59  *
60  * @param part Pointer to the data structure holding the partition
61  *             information (must be valid).
62  */
mpu_partition_is_valid(const struct z_arm_mpu_partition * part)63 static int mpu_partition_is_valid(const struct z_arm_mpu_partition *part)
64 {
65 	/* Partition size must be power-of-two,
66 	 * and greater or equal to the minimum
67 	 * MPU region size. Start address of the
68 	 * partition must align with size.
69 	 */
70 	int partition_is_valid =
71 		((part->size & (part->size - 1U)) == 0U)
72 		&&
73 		(part->size >= CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE)
74 		&&
75 		((part->start & (part->size - 1U)) == 0U);
76 
77 	return partition_is_valid;
78 }
79 
80 /**
81  * This internal function converts the region size to
82  * the SIZE field value of MPU_RASR.
83  *
84  * Note: If size is not a power-of-two, it is rounded-up to the next
85  * power-of-two value, and the returned SIZE field value corresponds
86  * to that power-of-two value.
87  */
size_to_mpu_rasr_size(uint32_t size)88 static inline uint32_t size_to_mpu_rasr_size(uint32_t size)
89 {
90 	/* The minimal supported region size is 32 bytes */
91 	if (size <= 32U) {
92 		return REGION_32B;
93 	}
94 
95 	/*
96 	 * A size value greater than 2^31 could not be handled by
97 	 * round_up_to_next_power_of_two() properly. We handle
98 	 * it separately here.
99 	 */
100 	if (size > (1UL << 31)) {
101 		return REGION_4G;
102 	}
103 
104 	return ((32 - __builtin_clz(size - 1U) - 2 + 1) << MPU_RASR_SIZE_Pos) &
105 		MPU_RASR_SIZE_Msk;
106 }
107 
108 /**
109  * This internal function is utilized by the MPU driver to combine a given
110  * region attribute configuration and size and fill-in a driver-specific
111  * structure with the correct MPU region configuration.
112  */
get_region_attr_from_mpu_partition_info(arm_mpu_region_attr_t * p_attr,const k_mem_partition_attr_t * attr,uint32_t base,uint32_t size)113 static inline void get_region_attr_from_mpu_partition_info(
114 	arm_mpu_region_attr_t *p_attr,
115 	const k_mem_partition_attr_t *attr, uint32_t base, uint32_t size)
116 {
117 	/* in ARMv7-M MPU the base address is not required
118 	 * to determine region attributes
119 	 */
120 	(void) base;
121 
122 #if defined(CONFIG_CPU_AARCH32_CORTEX_R)
123 	(void) size;
124 
125 	p_attr->rasr = attr->rasr_attr;
126 #else
127 	p_attr->rasr = attr->rasr_attr | size_to_mpu_rasr_size(size);
128 #endif
129 }
130 
131 #if defined(CONFIG_USERSPACE)
132 
133 /**
134  * This internal function returns the minimum HW MPU region index
135  * that may hold the configuration of a dynamic memory region.
136  *
137  * Trivial for ARMv7-M MPU, where dynamic memory areas are programmed
138  * in MPU regions indices right after the static regions.
139  */
get_dyn_region_min_index(void)140 static inline int get_dyn_region_min_index(void)
141 {
142 	return static_regions_num;
143 }
144 
145 /* Only a single bit is set for all user accessible permissions.
146  * In ARMv7-M MPU this is bit AP[1].
147  */
148 #define MPU_USER_READ_ACCESSIBLE_Msk (P_RW_U_RO & P_RW_U_RW & P_RO_U_RO & RO)
149 
150 /**
151  * This internal function checks if the region is user accessible or not.
152  *
153  * Note:
154  *   The caller must provide a valid region number.
155  */
is_user_accessible_region(uint32_t r_index,int write)156 static inline int is_user_accessible_region(uint32_t r_index, int write)
157 {
158 	uint32_t r_ap = get_region_ap(r_index);
159 
160 
161 	if (write != 0) {
162 		return r_ap == P_RW_U_RW;
163 	}
164 
165 	return r_ap & MPU_USER_READ_ACCESSIBLE_Msk;
166 }
167 
168 /**
169  * This internal function validates whether a given memory buffer
170  * is user accessible or not.
171  */
mpu_buffer_validate(const void * addr,size_t size,int write)172 static inline int mpu_buffer_validate(const void *addr, size_t size, int write)
173 {
174 	int32_t r_index;
175 	int rc = -EPERM;
176 
177 	int key = arch_irq_lock();
178 
179 	/* Iterate all mpu regions in reversed order */
180 	for (r_index = get_num_regions() - 1U; r_index >= 0;  r_index--) {
181 		if (!is_enabled_region(r_index) ||
182 		    !is_in_region(r_index, (uint32_t)addr, size)) {
183 			continue;
184 		}
185 
186 		/* For ARM MPU, higher region number takes priority.
187 		 * Since we iterate all mpu regions in reversed order, so
188 		 * we can stop the iteration immediately once we find the
189 		 * matched region that grants permission or denies access.
190 		 */
191 		if (is_user_accessible_region(r_index, write)) {
192 			rc = 0;
193 		} else {
194 			rc = -EPERM;
195 		}
196 		break;
197 	}
198 
199 	arch_irq_unlock(key);
200 	return rc;
201 }
202 
203 #endif /* CONFIG_USERSPACE */
204 
205 static int mpu_configure_region(const uint8_t index,
206 	const struct z_arm_mpu_partition *new_region);
207 
208 static int mpu_configure_regions(const struct z_arm_mpu_partition
209 	regions[], uint8_t regions_num, uint8_t start_reg_index,
210 	bool do_sanity_check);
211 
212 /* This internal function programs the static MPU regions.
213  *
214  * It returns the number of MPU region indices configured.
215  *
216  * Note:
217  * If the static MPU regions configuration has not been successfully
218  * performed, the error signal is propagated to the caller of the function.
219  */
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)220 static int mpu_configure_static_mpu_regions(const struct z_arm_mpu_partition
221 	static_regions[], const uint8_t regions_num,
222 	const uint32_t background_area_base,
223 	const uint32_t background_area_end)
224 {
225 	int mpu_reg_index = static_regions_num;
226 
227 	/* In ARMv7-M architecture the static regions are
228 	 * programmed on top of SRAM region configuration.
229 	 */
230 	ARG_UNUSED(background_area_base);
231 	ARG_UNUSED(background_area_end);
232 
233 	mpu_reg_index = mpu_configure_regions(static_regions,
234 		regions_num, mpu_reg_index, true);
235 
236 	static_regions_num = mpu_reg_index;
237 
238 	return mpu_reg_index;
239 }
240 
241 /* This internal function programs the dynamic MPU regions.
242  *
243  * It returns the number of MPU region indices configured.
244  *
245  * Note:
246  * If the dynamic MPU regions configuration has not been successfully
247  * performed, the error signal is propagated to the caller of the function.
248  */
mpu_configure_dynamic_mpu_regions(const struct z_arm_mpu_partition dynamic_regions[],uint8_t regions_num)249 static int mpu_configure_dynamic_mpu_regions(const struct z_arm_mpu_partition
250 	dynamic_regions[], uint8_t regions_num)
251 {
252 	int mpu_reg_index = static_regions_num;
253 
254 	/* In ARMv7-M architecture the dynamic regions are
255 	 * programmed on top of existing SRAM region configuration.
256 	 */
257 
258 	mpu_reg_index = mpu_configure_regions(dynamic_regions,
259 		regions_num, mpu_reg_index, false);
260 
261 	if (mpu_reg_index != -EINVAL) {
262 
263 		/* Disable the non-programmed MPU regions. */
264 		for (int i = mpu_reg_index; i < get_num_regions(); i++) {
265 			ARM_MPU_ClrRegion(i);
266 		}
267 	}
268 
269 	return mpu_reg_index;
270 }
271 
mpu_clear_region(uint32_t rnr)272 static inline void mpu_clear_region(uint32_t rnr)
273 {
274 	ARM_MPU_ClrRegion(rnr);
275 }
276 
277 #endif	/* ZEPHYR_ARCH_ARM_CORE_AARCH32_MPU_ARM_MPU_V7_INTERNAL_H_ */
278