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