-
Notifications
You must be signed in to change notification settings - Fork 6.5k
/
arm_core_mpu.c
352 lines (313 loc) · 10.7 KB
/
arm_core_mpu.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
* Copyright (c) 2017 Linaro Limited.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <soc.h>
#include "arm_core_mpu_dev.h"
#include <linker/linker-defs.h>
#define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(mpu);
/*
* Maximum number of dynamic memory partitions that may be supplied to the MPU
* driver for programming during run-time. Note that the actual number of the
* available MPU regions for dynamic programming depends on the number of the
* static MPU regions currently being programmed, and the total number of HW-
* available MPU regions. This macro is only used internally in function
* z_arch_configure_dynamic_mpu_regions(), to reserve sufficient area for the
* array of dynamic regions passed to the underlying driver.
*/
#if defined(CONFIG_USERSPACE)
#define _MAX_DYNAMIC_MPU_REGIONS_NUM \
CONFIG_MAX_DOMAIN_PARTITIONS + /* User thread stack */ 1 + \
(IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0)
#else
#define _MAX_DYNAMIC_MPU_REGIONS_NUM \
(IS_ENABLED(CONFIG_MPU_STACK_GUARD) ? 1 : 0)
#endif /* CONFIG_USERSPACE */
/* Convenience macros to denote the start address and the size of the system
* memory area, where dynamic memory regions may be programmed at run-time.
*/
#if defined(CONFIG_USERSPACE)
#define _MPU_DYNAMIC_REGIONS_AREA_START ((u32_t)&_app_smem_start)
#else
#define _MPU_DYNAMIC_REGIONS_AREA_START ((u32_t)&__kernel_ram_start)
#endif /* CONFIG_USERSPACE */
#define _MPU_DYNAMIC_REGIONS_AREA_SIZE ((u32_t)&__kernel_ram_end - \
_MPU_DYNAMIC_REGIONS_AREA_START)
/**
* @brief Use the HW-specific MPU driver to program
* the static MPU regions.
*
* Program the static MPU regions using the HW-specific MPU driver. The
* function is meant to be invoked only once upon system initialization.
*
* If the function attempts to configure a number of regions beyond the
* MPU HW limitations, the system behavior will be undefined.
*
* For some MPU architectures, such as the unmodified ARMv8-M MPU,
* the function must execute with MPU enabled.
*/
void z_arch_configure_static_mpu_regions(void)
{
#if defined(CONFIG_COVERAGE_GCOV) && defined(CONFIG_USERSPACE)
const struct k_mem_partition gcov_region =
{
.start = (u32_t)&__gcov_bss_start,
.size = (u32_t)&__gcov_bss_size,
.attr = K_MEM_PARTITION_P_RW_U_RW,
};
#endif /* CONFIG_COVERAGE_GCOV && CONFIG_USERSPACE */
#if defined(CONFIG_NOCACHE_MEMORY)
const struct k_mem_partition nocache_region =
{
.start = (u32_t)&_nocache_ram_start,
.size = (u32_t)&_nocache_ram_size,
.attr = K_MEM_PARTITION_P_RW_U_NA_NOCACHE,
};
#endif /* CONFIG_NOCACHE_MEMORY */
#if defined(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT)
const struct k_mem_partition ramfunc_region =
{
.start = (u32_t)&_ramfunc_ram_start,
.size = (u32_t)&_ramfunc_ram_size,
.attr = K_MEM_PARTITION_P_RX_U_RX,
};
#endif /* CONFIG_ARCH_HAS_RAMFUNC_SUPPORT */
/* Define a constant array of k_mem_partition objects
* to hold the configuration of the respective static
* MPU regions.
*/
const struct k_mem_partition *static_regions[] = {
#if defined(CONFIG_COVERAGE_GCOV) && defined(CONFIG_USERSPACE)
&gcov_region,
#endif /* CONFIG_COVERAGE_GCOV && CONFIG_USERSPACE */
#if defined(CONFIG_NOCACHE_MEMORY)
&nocache_region,
#endif /* CONFIG_NOCACHE_MEMORY */
#if defined(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT)
&ramfunc_region
#endif /* CONFIG_ARCH_HAS_RAMFUNC_SUPPORT */
};
/* Configure the static MPU regions within firmware SRAM boundaries.
* Start address of the image is given by _image_ram_start. The end
* of the firmware SRAM area is marked by __kernel_ram_end, taking
* into account the unused SRAM area, as well.
*/
arm_core_mpu_configure_static_mpu_regions(static_regions,
ARRAY_SIZE(static_regions),
(u32_t)&_image_ram_start,
(u32_t)&__kernel_ram_end);
#if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS)
/* Define a constant array of k_mem_partition objects that holds the
* boundaries of the areas, inside which dynamic region programming
* is allowed. The information is passed to the underlying driver at
* initialization.
*/
const struct k_mem_partition dyn_region_areas[] = {
{
.start = _MPU_DYNAMIC_REGIONS_AREA_START,
.size = _MPU_DYNAMIC_REGIONS_AREA_SIZE,
}
};
arm_core_mpu_mark_areas_for_dynamic_regions(dyn_region_areas,
ARRAY_SIZE(dyn_region_areas));
#endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */
}
/**
* @brief Use the HW-specific MPU driver to program
* the dynamic MPU regions.
*
* Program the dynamic MPU regions using the HW-specific MPU
* driver. This function is meant to be invoked every time the
* memory map is to be re-programmed, e.g during thread context
* switch, entering user mode, reconfiguring memory domain, etc.
*
* For some MPU architectures, such as the unmodified ARMv8-M MPU,
* the function must execute with MPU enabled.
*/
void z_arch_configure_dynamic_mpu_regions(struct k_thread *thread)
{
/* Define an array of k_mem_partition objects to hold the configuration
* of the respective dynamic MPU regions to be programmed for
* the given thread. The array of partitions (along with its
* actual size) will be supplied to the underlying MPU driver.
*/
struct k_mem_partition *dynamic_regions[_MAX_DYNAMIC_MPU_REGIONS_NUM];
u8_t region_num = 0U;
#if defined(CONFIG_USERSPACE)
struct k_mem_partition thread_stack;
/* Memory domain */
LOG_DBG("configure thread %p's domain", thread);
struct k_mem_domain *mem_domain = thread->mem_domain_info.mem_domain;
if (mem_domain) {
LOG_DBG("configure domain: %p", mem_domain);
u32_t num_partitions = mem_domain->num_partitions;
struct k_mem_partition partition;
int i;
LOG_DBG("configure domain: %p", mem_domain);
for (i = 0; i < CONFIG_MAX_DOMAIN_PARTITIONS; i++) {
partition = mem_domain->partitions[i];
if (partition.size == 0) {
/* Zero size indicates a non-existing
* memory partition.
*/
continue;
}
LOG_DBG("set region 0x%lx 0x%x",
partition.start, partition.size);
__ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
"Out-of-bounds error for dynamic region map.");
dynamic_regions[region_num] =
&mem_domain->partitions[i];
region_num++;
num_partitions--;
if (num_partitions == 0U) {
break;
}
}
}
/* Thread user stack */
LOG_DBG("configure user thread %p's context", thread);
if (thread->arch.priv_stack_start) {
u32_t base = (u32_t)thread->stack_obj;
u32_t size = thread->stack_info.size +
(thread->stack_info.start - base);
__ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
"Out-of-bounds error for dynamic region map.");
thread_stack = (const struct k_mem_partition)
{base, size, K_MEM_PARTITION_P_RW_U_RW};
dynamic_regions[region_num] = &thread_stack;
region_num++;
}
#endif /* CONFIG_USERSPACE */
#if defined(CONFIG_MPU_STACK_GUARD)
struct k_mem_partition guard;
/* Privileged stack guard */
u32_t guard_start;
#if defined(CONFIG_USERSPACE)
if (thread->arch.priv_stack_start) {
guard_start = thread->arch.priv_stack_start -
MPU_GUARD_ALIGN_AND_SIZE;
__ASSERT((u32_t)&z_priv_stacks_ram_start <= guard_start,
"Guard start: (0x%x) below privilege stacks boundary: (0x%x)",
guard_start, (u32_t)&z_priv_stacks_ram_start);
} else {
guard_start = thread->stack_info.start -
MPU_GUARD_ALIGN_AND_SIZE;
__ASSERT((u32_t)thread->stack_obj == guard_start,
"Guard start (0x%x) not beginning at stack object (0x%x)\n",
guard_start, (u32_t)thread->stack_obj);
}
#else
guard_start = thread->stack_info.start -
MPU_GUARD_ALIGN_AND_SIZE;
#endif /* CONFIG_USERSPACE */
__ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
"Out-of-bounds error for dynamic region map.");
guard = (const struct k_mem_partition)
{
guard_start,
MPU_GUARD_ALIGN_AND_SIZE,
K_MEM_PARTITION_P_RO_U_NA
};
dynamic_regions[region_num] = &guard;
region_num++;
#endif /* CONFIG_MPU_STACK_GUARD */
/* Configure the dynamic MPU regions */
arm_core_mpu_configure_dynamic_mpu_regions(
(const struct k_mem_partition **)dynamic_regions,
region_num);
}
#if defined(CONFIG_USERSPACE)
/**
* @brief Get the maximum number of partitions for a memory domain
* that is supported by the MPU hardware, and with respect
* to the current static memory region configuration.
*/
int z_arch_mem_domain_max_partitions_get(void)
{
int available_regions = arm_core_mpu_get_max_available_dyn_regions();
available_regions -=
ARM_CORE_MPU_NUM_MPU_REGIONS_FOR_THREAD_STACK;
if (IS_ENABLED(CONFIG_MPU_STACK_GUARD)) {
available_regions -=
ARM_CORE_MPU_NUM_MPU_REGIONS_FOR_MPU_STACK_GUARD;
}
return ARM_CORE_MPU_MAX_DOMAIN_PARTITIONS_GET(available_regions);
}
/**
* @brief Configure the memory domain of the thread.
*/
void z_arch_mem_domain_configure(struct k_thread *thread)
{
/* Request to configure memory domain for a thread.
* This triggers re-programming of the entire dynamic
* memory map.
*/
z_arch_configure_dynamic_mpu_regions(thread);
}
/*
* @brief Reset the MPU configuration related to the memory domain
* partitions
*
* @param domain pointer to the memory domain (must be valid)
*/
void z_arch_mem_domain_destroy(struct k_mem_domain *domain)
{
/* This function will reset the access permission configuration
* of the active partitions of the memory domain.
*/
int i;
struct k_mem_partition partition;
/* Partitions belonging to the memory domain will be reset
* to default (Privileged RW, Unprivileged NA) permissions.
*/
k_mem_partition_attr_t reset_attr = K_MEM_PARTITION_P_RW_U_NA;
for (i = 0; i < CONFIG_MAX_DOMAIN_PARTITIONS; i++) {
partition = domain->partitions[i];
if (partition.size == 0U) {
/* Zero size indicates a non-existing
* memory partition.
*/
continue;
}
arm_core_mpu_mem_partition_config_update(&partition,
&reset_attr);
}
}
/*
* @brief Remove a partition from the memory domain
*
* @param domain pointer to the memory domain (must be valid
* @param partition_id the ID (sequence) number of the memory domain
* partition (must be a valid partition).
*/
void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
u32_t partition_id)
{
/* Request to remove a partition from a memory domain.
* This resets the access permissions of the partition
* to default (Privileged RW, Unprivileged NA).
*/
k_mem_partition_attr_t reset_attr = K_MEM_PARTITION_P_RW_U_NA;
arm_core_mpu_mem_partition_config_update(
&domain->partitions[partition_id], &reset_attr);
}
void z_arch_mem_domain_partition_add(struct k_mem_domain *domain,
u32_t partition_id)
{
/* No-op on this architecture */
}
/*
* Validate the given buffer is user accessible or not
*/
int z_arch_buffer_validate(void *addr, size_t size, int write)
{
return arm_core_mpu_buffer_validate(addr, size, write);
}
#endif /* CONFIG_USERSPACE */