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 <kernel_structs.h>
12 : :
13 : : #include "arm_core_mpu_dev.h"
14 : : #include <linker/linker-defs.h>
15 : :
16 : : #define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
17 : : #include <logging/log.h>
18 : : LOG_MODULE_REGISTER(mpu);
19 : :
20 : : /*
21 : : * Maximum number of dynamic memory partitions that may be supplied to the MPU
22 : : * driver for programming during run-time. Note that the actual number of the
23 : : * available MPU regions for dynamic programming depends on the number of the
24 : : * static MPU regions currently being programmed, and the total number of HW-
25 : : * available MPU regions. This macro is only used internally in function
26 : : * z_arm_configure_dynamic_mpu_regions(), to reserve sufficient area for the
27 : : * array of dynamic regions passed to the underlying driver.
28 : : */
29 : : #if defined(CONFIG_USERSPACE)
30 : : #define _MAX_DYNAMIC_MPU_REGIONS_NUM \
31 : : CONFIG_MAX_DOMAIN_PARTITIONS + /* User thread stack */ 1 + \
32 : : (IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0)
33 : : #else
34 : : #define _MAX_DYNAMIC_MPU_REGIONS_NUM \
35 : : (IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0)
36 : : #endif /* CONFIG_USERSPACE */
37 : :
38 : : /* Convenience macros to denote the start address and the size of the system
39 : : * memory area, where dynamic memory regions may be programmed at run-time.
40 : : */
41 : : #if defined(CONFIG_USERSPACE)
42 : : #define _MPU_DYNAMIC_REGIONS_AREA_START ((uint32_t)&_app_smem_start)
43 : : #else
44 : : #define _MPU_DYNAMIC_REGIONS_AREA_START ((uint32_t)&__kernel_ram_start)
45 : : #endif /* CONFIG_USERSPACE */
46 : : #define _MPU_DYNAMIC_REGIONS_AREA_SIZE ((uint32_t)&__kernel_ram_end - \
47 : : _MPU_DYNAMIC_REGIONS_AREA_START)
48 : :
49 : : #if !defined(CONFIG_MULTITHREADING) && defined(CONFIG_MPU_STACK_GUARD)
50 : : extern K_THREAD_STACK_DEFINE(z_main_stack, CONFIG_MAIN_STACK_SIZE);
51 : : #endif
52 : :
53 : : #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) \
54 : : && defined(CONFIG_MPU_STACK_GUARD)
55 : : uint32_t z_arm_mpu_stack_guard_and_fpu_adjust(struct k_thread *thread);
56 : : #endif
57 : :
58 : : #if defined(CONFIG_CODE_DATA_RELOCATION_SRAM)
59 : : extern char __sram_text_start[];
60 : : extern char __sram_text_size[];
61 : : #endif
62 : :
63 : : static const struct z_arm_mpu_partition static_regions[] = {
64 : : #if defined(CONFIG_COVERAGE_GCOV) && defined(CONFIG_USERSPACE)
65 : : {
66 : : /* GCOV code coverage accounting area. Needs User permissions
67 : : * to function
68 : : */
69 : : .start = (uint32_t)&__gcov_bss_start,
70 : : .size = (uint32_t)&__gcov_bss_size,
71 : : .attr = K_MEM_PARTITION_P_RW_U_RW,
72 : : },
73 : : #endif /* CONFIG_COVERAGE_GCOV && CONFIG_USERSPACE */
74 : : #if defined(CONFIG_NOCACHE_MEMORY)
75 : : {
76 : : /* Special non-cacheable RAM area */
77 : : .start = (uint32_t)&_nocache_ram_start,
78 : : .size = (uint32_t)&_nocache_ram_size,
79 : : .attr = K_MEM_PARTITION_P_RW_U_NA_NOCACHE,
80 : : },
81 : : #endif /* CONFIG_NOCACHE_MEMORY */
82 : : #if defined(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT)
83 : : {
84 : : /* Special RAM area for program text */
85 : : .start = (uint32_t)&__ramfunc_start,
86 : : .size = (uint32_t)&__ramfunc_size,
87 : : .attr = K_MEM_PARTITION_P_RX_U_RX,
88 : : },
89 : : #endif /* CONFIG_ARCH_HAS_RAMFUNC_SUPPORT */
90 : : #if defined(CONFIG_CODE_DATA_RELOCATION_SRAM)
91 : : {
92 : : /* RAM area for relocated text */
93 : : .start = (uint32_t)&__sram_text_start,
94 : : .size = (uint32_t)&__sram_text_size,
95 : : .attr = K_MEM_PARTITION_P_RX_U_RX,
96 : : },
97 : : #endif /* CONFIG_CODE_DATA_RELOCATION_SRAM */
98 : : #if !defined(CONFIG_MULTITHREADING) && defined(CONFIG_MPU_STACK_GUARD)
99 : : /* Main stack MPU guard to detect overflow.
100 : : * Note:
101 : : * FPU_SHARING and USERSPACE are not supported features
102 : : * under CONFIG_MULTITHREADING=n, so the MPU guard (if
103 : : * exists) is reserved aside of CONFIG_MAIN_STACK_SIZE
104 : : * and there is no requirement for larger guard area (FP
105 : : * context is not stacked).
106 : : */
107 : : {
108 : : .start = (uint32_t)z_main_stack,
109 : : .size = (uint32_t)MPU_GUARD_ALIGN_AND_SIZE,
110 : : .attr = K_MEM_PARTITION_P_RO_U_NA,
111 : : },
112 : : #endif /* !CONFIG_MULTITHREADING && CONFIG_MPU_STACK_GUARD */
113 : : };
114 : :
115 : : /**
116 : : * @brief Use the HW-specific MPU driver to program
117 : : * the static MPU regions.
118 : : *
119 : : * Program the static MPU regions using the HW-specific MPU driver. The
120 : : * function is meant to be invoked only once upon system initialization.
121 : : *
122 : : * If the function attempts to configure a number of regions beyond the
123 : : * MPU HW limitations, the system behavior will be undefined.
124 : : *
125 : : * For some MPU architectures, such as the unmodified ARMv8-M MPU,
126 : : * the function must execute with MPU enabled.
127 : : */
128 : 1 : void z_arm_configure_static_mpu_regions(void)
129 : : {
130 : : /* Configure the static MPU regions within firmware SRAM boundaries.
131 : : * Start address of the image is given by _image_ram_start. The end
132 : : * of the firmware SRAM area is marked by __kernel_ram_end, taking
133 : : * into account the unused SRAM area, as well.
134 : : */
135 : 1 : arm_core_mpu_configure_static_mpu_regions(static_regions,
136 : : ARRAY_SIZE(static_regions),
137 : : (uint32_t)&_image_ram_start,
138 : : (uint32_t)&__kernel_ram_end);
139 : :
140 : : #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) && \
141 : : defined(CONFIG_MULTITHREADING)
142 : : /* Define a constant array of z_arm_mpu_partition objects that holds the
143 : : * boundaries of the areas, inside which dynamic region programming
144 : : * is allowed. The information is passed to the underlying driver at
145 : : * initialization.
146 : : */
147 : 1 : const struct z_arm_mpu_partition dyn_region_areas[] = {
148 : : {
149 : 1 : .start = _MPU_DYNAMIC_REGIONS_AREA_START,
150 : 1 : .size = _MPU_DYNAMIC_REGIONS_AREA_SIZE,
151 : : }
152 : : };
153 : :
154 : 1 : arm_core_mpu_mark_areas_for_dynamic_regions(dyn_region_areas,
155 : : ARRAY_SIZE(dyn_region_areas));
156 : : #endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */
157 : 1 : }
158 : :
159 : : /**
160 : : * @brief Use the HW-specific MPU driver to program
161 : : * the dynamic MPU regions.
162 : : *
163 : : * Program the dynamic MPU regions using the HW-specific MPU
164 : : * driver. This function is meant to be invoked every time the
165 : : * memory map is to be re-programmed, e.g during thread context
166 : : * switch, entering user mode, reconfiguring memory domain, etc.
167 : : *
168 : : * For some MPU architectures, such as the unmodified ARMv8-M MPU,
169 : : * the function must execute with MPU enabled.
170 : : *
171 : : * This function is not inherently thread-safe, but the memory domain
172 : : * spinlock needs to be held anyway.
173 : : */
174 : 0 : void z_arm_configure_dynamic_mpu_regions(struct k_thread *thread)
175 : : {
176 : : /* Define an array of z_arm_mpu_partition objects to hold the configuration
177 : : * of the respective dynamic MPU regions to be programmed for
178 : : * the given thread. The array of partitions (along with its
179 : : * actual size) will be supplied to the underlying MPU driver.
180 : : *
181 : : * The drivers of what regions get configured are CONFIG_USERSPACE,
182 : : * CONFIG_MPU_STACK_GUARD, and K_USER/supervisor threads.
183 : : *
184 : : * If CONFIG_USERSPACE is defined and the thread is a member of any
185 : : * memory domain then any partitions defined within that domain get a
186 : : * defined region.
187 : : *
188 : : * If CONFIG_USERSPACE is defined and the thread is a user thread
189 : : * (K_USER) the usermode thread stack is defined a region.
190 : : *
191 : : * IF CONFIG_MPU_STACK_GUARD is defined the thread is a supervisor
192 : : * thread, the stack guard will be defined in front of the
193 : : * thread->stack_info.start. On a K_USER thread, the guard is defined
194 : : * in front of the privilege mode stack, thread->arch.priv_stack_start.
195 : : */
196 : : static struct z_arm_mpu_partition
197 : : dynamic_regions[_MAX_DYNAMIC_MPU_REGIONS_NUM];
198 : :
199 : 0 : uint8_t region_num = 0U;
200 : :
201 : : #if defined(CONFIG_USERSPACE)
202 : : /* Memory domain */
203 : : LOG_DBG("configure thread %p's domain", thread);
204 : : struct k_mem_domain *mem_domain = thread->mem_domain_info.mem_domain;
205 : :
206 : : if (mem_domain) {
207 : : LOG_DBG("configure domain: %p", mem_domain);
208 : : uint32_t num_partitions = mem_domain->num_partitions;
209 : : struct k_mem_partition *partition;
210 : : int i;
211 : :
212 : : LOG_DBG("configure domain: %p", mem_domain);
213 : :
214 : : for (i = 0; i < CONFIG_MAX_DOMAIN_PARTITIONS; i++) {
215 : : partition = &mem_domain->partitions[i];
216 : : if (partition->size == 0) {
217 : : /* Zero size indicates a non-existing
218 : : * memory partition.
219 : : */
220 : : continue;
221 : : }
222 : : LOG_DBG("set region 0x%lx 0x%x",
223 : : partition->start, partition->size);
224 : : __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
225 : : "Out-of-bounds error for dynamic region map.");
226 : :
227 : : dynamic_regions[region_num].start = partition->start;
228 : : dynamic_regions[region_num].size = partition->size;
229 : : dynamic_regions[region_num].attr = partition->attr;
230 : :
231 : : region_num++;
232 : : num_partitions--;
233 : : if (num_partitions == 0U) {
234 : : break;
235 : : }
236 : : }
237 : : }
238 : : /* Thread user stack */
239 : : LOG_DBG("configure user thread %p's context", thread);
240 : : if (thread->arch.priv_stack_start) {
241 : : /* K_USER thread stack needs a region */
242 : : uintptr_t base = (uintptr_t)thread->stack_obj;
243 : : size_t size = thread->stack_info.size +
244 : : (thread->stack_info.start - base);
245 : :
246 : : __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
247 : : "Out-of-bounds error for dynamic region map.");
248 : :
249 : : dynamic_regions[region_num].start = base;
250 : : dynamic_regions[region_num].size = size;
251 : : dynamic_regions[region_num].attr = K_MEM_PARTITION_P_RW_U_RW;
252 : :
253 : : region_num++;
254 : : }
255 : : #endif /* CONFIG_USERSPACE */
256 : :
257 : : #if defined(CONFIG_MPU_STACK_GUARD)
258 : : /* Define a stack guard region for either the thread stack or the
259 : : * supervisor/privilege mode stack depending on the type of thread
260 : : * being mapped.
261 : : */
262 : :
263 : : /* Privileged stack guard */
264 : : uintptr_t guard_start;
265 : : size_t guard_size = MPU_GUARD_ALIGN_AND_SIZE;
266 : :
267 : : #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
268 : : guard_size = z_arm_mpu_stack_guard_and_fpu_adjust(thread);
269 : : #endif
270 : :
271 : : #if defined(CONFIG_USERSPACE)
272 : : if (thread->arch.priv_stack_start) {
273 : : /* A K_USER thread has the stack guard protecting the privilege
274 : : * stack and not on the usermode stack because the user mode
275 : : * stack already has its own defined memory region.
276 : : */
277 : : guard_start = thread->arch.priv_stack_start - guard_size;
278 : :
279 : : __ASSERT((uintptr_t)&z_priv_stacks_ram_start <= guard_start,
280 : : "Guard start: (0x%lx) below privilege stacks boundary: (%p)",
281 : : guard_start, z_priv_stacks_ram_start);
282 : : } else
283 : : #endif /* CONFIG_USERSPACE */
284 : : {
285 : : /* A supervisor thread only has the normal thread stack to
286 : : * protect with a stack guard.
287 : : */
288 : : guard_start = thread->stack_info.start - guard_size;
289 : : #ifdef CONFIG_USERSPACE
290 : : __ASSERT((uintptr_t)thread->stack_obj == guard_start,
291 : : "Guard start (0x%lx) not beginning at stack object (%p)\n",
292 : : guard_start, thread->stack_obj);
293 : : #endif /* CONFIG_USERSPACE */
294 : : }
295 : :
296 : : __ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
297 : : "Out-of-bounds error for dynamic region map.");
298 : :
299 : : dynamic_regions[region_num].start = guard_start;
300 : : dynamic_regions[region_num].size = guard_size;
301 : : dynamic_regions[region_num].attr = K_MEM_PARTITION_P_RO_U_NA;
302 : :
303 : : region_num++;
304 : : #endif /* CONFIG_MPU_STACK_GUARD */
305 : :
306 : : /* Configure the dynamic MPU regions */
307 : 0 : arm_core_mpu_configure_dynamic_mpu_regions(dynamic_regions,
308 : : region_num);
309 : 0 : }
310 : :
311 : : #if defined(CONFIG_USERSPACE)
312 : : int arch_mem_domain_max_partitions_get(void)
313 : : {
314 : : int available_regions = arm_core_mpu_get_max_available_dyn_regions();
315 : :
316 : : available_regions -=
317 : : ARM_CORE_MPU_NUM_MPU_REGIONS_FOR_THREAD_STACK;
318 : :
319 : : if (IS_ENABLED(CONFIG_MPU_STACK_GUARD)) {
320 : : available_regions -=
321 : : ARM_CORE_MPU_NUM_MPU_REGIONS_FOR_MPU_STACK_GUARD;
322 : : }
323 : :
324 : : return ARM_CORE_MPU_MAX_DOMAIN_PARTITIONS_GET(available_regions);
325 : : }
326 : :
327 : : int arch_buffer_validate(void *addr, size_t size, int write)
328 : : {
329 : : return arm_core_mpu_buffer_validate(addr, size, write);
330 : : }
331 : :
332 : : #endif /* CONFIG_USERSPACE */
|