Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2017 Linaro Limited.
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : #include <device.h>
8 : : #include <init.h>
9 : : #include <kernel.h>
10 : : #include <soc.h>
11 : : #include "arm_core_mpu_dev.h"
12 : : #include <linker/linker-defs.h>
13 : : #include <kernel_arch_data.h>
14 : :
15 : : #define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
16 : : #include <logging/log.h>
17 : : LOG_MODULE_DECLARE(mpu);
18 : :
19 : : #if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)
20 : : /* The order here is on purpose since ARMv8-M SoCs may define
21 : : * CONFIG_ARMV6_M_ARMV8_M_BASELINE or CONFIG_ARMV7_M_ARMV8_M_MAINLINE
22 : : * so we want to check for ARMv8-M first.
23 : : */
24 : : #define MPU_NODEID DT_INST(0, arm_armv8m_mpu)
25 : : #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
26 : : #define MPU_NODEID DT_INST(0, arm_armv7m_mpu)
27 : : #elif defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
28 : : #define MPU_NODEID DT_INST(0, arm_armv6m_mpu)
29 : : #endif
30 : :
31 : : #if DT_NODE_HAS_PROP(MPU_NODEID, arm_num_mpu_regions)
32 : : #define NUM_MPU_REGIONS DT_PROP(MPU_NODEID, arm_num_mpu_regions)
33 : : #endif
34 : :
35 : : /*
36 : : * Global status variable holding the number of HW MPU region indices, which
37 : : * have been reserved by the MPU driver to program the static (fixed) memory
38 : : * regions.
39 : : */
40 : : static uint8_t static_regions_num;
41 : :
42 : : /* Include architecture-specific internal headers. */
43 : : #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \
44 : : defined(CONFIG_CPU_CORTEX_M3) || \
45 : : defined(CONFIG_CPU_CORTEX_M4) || \
46 : : defined(CONFIG_CPU_CORTEX_M7) || \
47 : : defined(CONFIG_CPU_AARCH32_CORTEX_R)
48 : : #include "arm_mpu_v7_internal.h"
49 : : #elif defined(CONFIG_CPU_CORTEX_M23) || \
50 : : defined(CONFIG_CPU_CORTEX_M33) || \
51 : : defined(CONFIG_CPU_CORTEX_M55)
52 : : #include "arm_mpu_v8_internal.h"
53 : : #else
54 : : #error "Unsupported ARM CPU"
55 : : #endif
56 : :
57 : 0 : static int region_allocate_and_init(const uint8_t index,
58 : : const struct arm_mpu_region *region_conf)
59 : : {
60 : : /* Attempt to allocate new region index. */
61 [ # # ]: 0 : if (index > (get_num_regions() - 1U)) {
62 : :
63 : : /* No available MPU region index. */
64 [ # # ]: 0 : LOG_ERR("Failed to allocate new MPU region %u\n", index);
65 : 0 : return -EINVAL;
66 : : }
67 : :
68 [ # # ]: 0 : LOG_DBG("Program MPU region at index 0x%x", index);
69 : :
70 : : /* Program region */
71 : 0 : region_init(index, region_conf);
72 : :
73 : 0 : return index;
74 : : }
75 : :
76 : : /* This internal function programs an MPU region
77 : : * of a given configuration at a given MPU index.
78 : : */
79 : 0 : static int mpu_configure_region(const uint8_t index,
80 : : const struct z_arm_mpu_partition *new_region)
81 : : {
82 : : struct arm_mpu_region region_conf;
83 : :
84 [ # # ]: 0 : LOG_DBG("Configure MPU region at index 0x%x", index);
85 : :
86 : : /* Populate internal ARM MPU region configuration structure. */
87 : 0 : region_conf.base = new_region->start;
88 : : #if defined(CONFIG_CPU_AARCH32_CORTEX_R)
89 : : region_conf.size = size_to_mpu_rasr_size(new_region->size);
90 : : #endif
91 : 0 : get_region_attr_from_mpu_partition_info(®ion_conf.attr,
92 : 0 : &new_region->attr, new_region->start, new_region->size);
93 : :
94 : : /* Allocate and program region */
95 : 0 : return region_allocate_and_init(index,
96 : : (const struct arm_mpu_region *)®ion_conf);
97 : : }
98 : :
99 : : #if !defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) || \
100 : : !defined(CONFIG_MPU_GAP_FILLING)
101 : : /* This internal function programs a set of given MPU regions
102 : : * over a background memory area, optionally performing a
103 : : * sanity check of the memory regions to be programmed.
104 : : */
105 : : static int mpu_configure_regions(const struct z_arm_mpu_partition
106 : : regions[], uint8_t regions_num, uint8_t start_reg_index,
107 : : bool do_sanity_check)
108 : : {
109 : : int i;
110 : : int reg_index = start_reg_index;
111 : :
112 : : for (i = 0; i < regions_num; i++) {
113 : : if (regions[i].size == 0U) {
114 : : continue;
115 : : }
116 : : /* Non-empty region. */
117 : :
118 : : if (do_sanity_check &&
119 : : (!mpu_partition_is_valid(®ions[i]))) {
120 : : LOG_ERR("Partition %u: sanity check failed.", i);
121 : : return -EINVAL;
122 : : }
123 : :
124 : : reg_index = mpu_configure_region(reg_index, ®ions[i]);
125 : :
126 : : if (reg_index == -EINVAL) {
127 : : return reg_index;
128 : : }
129 : :
130 : : /* Increment number of programmed MPU indices. */
131 : : reg_index++;
132 : : }
133 : :
134 : : return reg_index;
135 : : }
136 : : #endif
137 : :
138 : : /* ARM Core MPU Driver API Implementation for ARM MPU */
139 : :
140 : :
141 : : #if defined(CONFIG_CPU_AARCH32_CORTEX_R)
142 : : /**
143 : : * @brief enable the MPU by setting bit in SCTRL register
144 : : */
145 : : void arm_core_mpu_enable(void)
146 : : {
147 : : uint32_t val;
148 : :
149 : : val = __get_SCTLR();
150 : : val |= SCTLR_MPU_ENABLE;
151 : : /* Make sure that all the registers are set before proceeding */
152 : : __DSB();
153 : : __set_SCTLR(val);
154 : : __ISB();
155 : : }
156 : :
157 : : /**
158 : : * @brief disable the MPU by clearing bit in SCTRL register
159 : : */
160 : : void arm_core_mpu_disable(void)
161 : : {
162 : : uint32_t val;
163 : :
164 : : val = __get_SCTLR();
165 : : val &= ~SCTLR_MPU_ENABLE;
166 : : /* Force any outstanding transfers to complete before disabling MPU */
167 : : __DSB();
168 : : __set_SCTLR(val);
169 : : __ISB();
170 : : }
171 : : #else
172 : : /**
173 : : * @brief enable the MPU
174 : : */
175 : 1 : void arm_core_mpu_enable(void)
176 : : {
177 : : /* Enable MPU and use the default memory map as a
178 : : * background region for privileged software access.
179 : : */
180 : 1 : MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;
181 : :
182 : : /* Make sure that all the registers are set before proceeding */
183 : : __DSB();
184 : : __ISB();
185 : 1 : }
186 : :
187 : : /**
188 : : * @brief disable the MPU
189 : : */
190 : 1 : void arm_core_mpu_disable(void)
191 : : {
192 : : /* Force any outstanding transfers to complete before disabling MPU */
193 : : __DMB();
194 : :
195 : : /* Disable MPU */
196 : 1 : MPU->CTRL = 0;
197 : 1 : }
198 : : #endif
199 : :
200 : : #if defined(CONFIG_USERSPACE)
201 : : /**
202 : : * @brief update configuration of an active memory partition
203 : : */
204 : : void arm_core_mpu_mem_partition_config_update(
205 : : struct z_arm_mpu_partition *partition,
206 : : k_mem_partition_attr_t *new_attr)
207 : : {
208 : : /* Find the partition. ASSERT if not found. */
209 : : uint8_t i;
210 : : uint8_t reg_index = get_num_regions();
211 : :
212 : : for (i = get_dyn_region_min_index(); i < get_num_regions(); i++) {
213 : : if (!is_enabled_region(i)) {
214 : : continue;
215 : : }
216 : :
217 : : uint32_t base = mpu_region_get_base(i);
218 : :
219 : : if (base != partition->start) {
220 : : continue;
221 : : }
222 : :
223 : : uint32_t size = mpu_region_get_size(i);
224 : :
225 : : if (size != partition->size) {
226 : : continue;
227 : : }
228 : :
229 : : /* Region found */
230 : : reg_index = i;
231 : : break;
232 : : }
233 : : __ASSERT(reg_index != get_num_regions(),
234 : : "Memory domain partition %p size %zu not found\n",
235 : : (void *)partition->start, partition->size);
236 : :
237 : : /* Modify the permissions */
238 : : partition->attr = *new_attr;
239 : : mpu_configure_region(reg_index, partition);
240 : : }
241 : :
242 : : /**
243 : : * @brief get the maximum number of available (free) MPU region indices
244 : : * for configuring dynamic MPU partitions
245 : : */
246 : : int arm_core_mpu_get_max_available_dyn_regions(void)
247 : : {
248 : : return get_num_regions() - static_regions_num;
249 : : }
250 : :
251 : : /**
252 : : * @brief validate the given buffer is user accessible or not
253 : : *
254 : : * Presumes the background mapping is NOT user accessible.
255 : : */
256 : : int arm_core_mpu_buffer_validate(void *addr, size_t size, int write)
257 : : {
258 : : return mpu_buffer_validate(addr, size, write);
259 : : }
260 : :
261 : : #endif /* CONFIG_USERSPACE */
262 : :
263 : : /**
264 : : * @brief configure fixed (static) MPU regions.
265 : : */
266 : 1 : void arm_core_mpu_configure_static_mpu_regions(const struct z_arm_mpu_partition
267 : : static_regions[], const uint8_t regions_num,
268 : : const uint32_t background_area_start, const uint32_t background_area_end)
269 : : {
270 [ - + ]: 1 : if (mpu_configure_static_mpu_regions(static_regions, regions_num,
271 : : background_area_start, background_area_end) == -EINVAL) {
272 : :
273 : 0 : __ASSERT(0, "Configuring %u static MPU regions failed\n",
274 : : regions_num);
275 : : }
276 : 1 : }
277 : :
278 : : #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS)
279 : : /**
280 : : * @brief mark memory areas for dynamic region configuration
281 : : */
282 : 1 : void arm_core_mpu_mark_areas_for_dynamic_regions(
283 : : const struct z_arm_mpu_partition dyn_region_areas[],
284 : : const uint8_t dyn_region_areas_num)
285 : : {
286 [ - + ]: 1 : if (mpu_mark_areas_for_dynamic_regions(dyn_region_areas,
287 : : dyn_region_areas_num) == -EINVAL) {
288 : :
289 : 0 : __ASSERT(0, "Marking %u areas for dynamic regions failed\n",
290 : : dyn_region_areas_num);
291 : : }
292 : 1 : }
293 : : #endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */
294 : :
295 : : /**
296 : : * @brief configure dynamic MPU regions.
297 : : */
298 : 0 : void arm_core_mpu_configure_dynamic_mpu_regions(const struct z_arm_mpu_partition
299 : : dynamic_regions[], uint8_t regions_num)
300 : : {
301 [ # # ]: 0 : if (mpu_configure_dynamic_mpu_regions(dynamic_regions, regions_num)
302 : : == -EINVAL) {
303 : :
304 : 0 : __ASSERT(0, "Configuring %u dynamic MPU regions failed\n",
305 : : regions_num);
306 : : }
307 : 0 : }
308 : :
309 : : /* ARM MPU Driver Initial Setup */
310 : :
311 : : /*
312 : : * @brief MPU default configuration
313 : : *
314 : : * This function provides the default configuration mechanism for the Memory
315 : : * Protection Unit (MPU).
316 : : */
317 : 1 : int z_arm_mpu_init(void)
318 : : {
319 : : uint32_t r_index;
320 : :
321 [ - + ]: 1 : if (mpu_config.num_regions > get_num_regions()) {
322 : : /* Attempt to configure more MPU regions than
323 : : * what is supported by hardware. As this operation
324 : : * is executed during system (pre-kernel) initialization,
325 : : * we want to ensure we can detect an attempt to
326 : : * perform invalid configuration.
327 : : */
328 : 0 : __ASSERT(0,
329 : : "Request to configure: %u regions (supported: %u)\n",
330 : : mpu_config.num_regions,
331 : : get_num_regions()
332 : : );
333 : 0 : return -1;
334 : : }
335 : :
336 [ + - ]: 1 : LOG_DBG("total region count: %d", get_num_regions());
337 : :
338 : 1 : arm_core_mpu_disable();
339 : :
340 : : #if defined(CONFIG_NOCACHE_MEMORY)
341 : : /* Clean and invalidate data cache if it is enabled and
342 : : * that was not already done at boot
343 : : */
344 : : #if !defined(CONFIG_INIT_ARCH_HW_AT_BOOT)
345 : : if (SCB->CCR & SCB_CCR_DC_Msk) {
346 : : SCB_CleanInvalidateDCache();
347 : : }
348 : : #endif
349 : : #endif /* CONFIG_NOCACHE_MEMORY */
350 : :
351 : : /* Architecture-specific configuration */
352 : 1 : mpu_init();
353 : :
354 : : /* Program fixed regions configured at SOC definition. */
355 [ + + ]: 3 : for (r_index = 0U; r_index < mpu_config.num_regions; r_index++) {
356 : 2 : region_init(r_index, &mpu_config.mpu_regions[r_index]);
357 : : }
358 : :
359 : : /* Update the number of programmed MPU regions. */
360 : 1 : static_regions_num = mpu_config.num_regions;
361 : :
362 : :
363 : 1 : arm_core_mpu_enable();
364 : :
365 : : /* Program additional fixed flash region for null-pointer
366 : : * dereferencing detection (debug feature)
367 : : */
368 : : #if defined(CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU)
369 : : #if (defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)) && \
370 : : (CONFIG_FLASH_BASE_ADDRESS > CONFIG_CORTEX_M_NULL_POINTER_EXCEPTION_PAGE_SIZE)
371 : : #pragma message "Null-Pointer exception detection cannot be configured on un-mapped flash areas"
372 : : #else
373 : : const struct z_arm_mpu_partition unmap_region = {
374 : : .start = 0x0,
375 : : .size = CONFIG_CORTEX_M_NULL_POINTER_EXCEPTION_PAGE_SIZE,
376 : : #if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)
377 : : /* Overlapping region (with any permissions)
378 : : * will result in fault generation
379 : : */
380 : : .attr = K_MEM_PARTITION_P_RO_U_NA,
381 : : #else
382 : : /* Explicit no-access policy */
383 : : .attr = K_MEM_PARTITION_P_NA_U_NA,
384 : : #endif
385 : : };
386 : :
387 : : /* The flash region for null pointer dereferencing detection shall
388 : : * comply with the regular MPU partition definition restrictions
389 : : * (size and alignment).
390 : : */
391 : : _ARCH_MEM_PARTITION_ALIGN_CHECK(0x0,
392 : : CONFIG_CORTEX_M_NULL_POINTER_EXCEPTION_PAGE_SIZE);
393 : :
394 : : #if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)
395 : : /* ARMv8-M requires that the area:
396 : : * 0x0 - CORTEX_M_NULL_POINTER_EXCEPTION_PAGE_SIZE
397 : : * is not unmapped (belongs to a valid MPU region already).
398 : : */
399 : : if ((arm_cmse_mpu_region_get(0x0) == -EINVAL) ||
400 : : (arm_cmse_mpu_region_get(
401 : : CONFIG_CORTEX_M_NULL_POINTER_EXCEPTION_PAGE_SIZE - 1)
402 : : == -EINVAL)) {
403 : : __ASSERT(0,
404 : : "Null pointer detection page unmapped\n");
405 : : }
406 : : #endif
407 : :
408 : : if (mpu_configure_region(static_regions_num, &unmap_region) == -EINVAL) {
409 : :
410 : : __ASSERT(0,
411 : : "Programming null-pointer detection region failed\n");
412 : : return -EINVAL;
413 : : }
414 : :
415 : : static_regions_num++;
416 : :
417 : : #endif
418 : : #endif /* CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU */
419 : :
420 : : /* Sanity check for number of regions in Cortex-M0+, M3, and M4. */
421 : : #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \
422 : : defined(CONFIG_CPU_CORTEX_M3) || \
423 : : defined(CONFIG_CPU_CORTEX_M4)
424 : : __ASSERT(
425 : : (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos == 8,
426 : : "Invalid number of MPU regions\n");
427 : : #elif defined(NUM_MPU_REGIONS)
428 [ - + ]: 1 : __ASSERT(
429 : : (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos ==
430 : : NUM_MPU_REGIONS,
431 : : "Invalid number of MPU regions\n");
432 : : #endif /* CORTEX_M0PLUS || CPU_CORTEX_M3 || CPU_CORTEX_M4 */
433 : :
434 : 1 : return 0;
435 : : }
|