1 /*
2 * Copyright 2019 Broadcom
3 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4 *
5 * Copyright (c) 2021 BayLibre, SAS
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/cache.h>
11 #include <zephyr/device.h>
12 #include <zephyr/init.h>
13 #include <zephyr/kernel.h>
14 #include <kernel_arch_func.h>
15 #include <kernel_arch_interface.h>
16 #include <kernel_internal.h>
17 #include <zephyr/logging/log.h>
18 #include <zephyr/arch/arm64/cpu.h>
19 #include <zephyr/arch/arm64/lib_helpers.h>
20 #include <zephyr/arch/arm64/mm.h>
21 #include <zephyr/linker/linker-defs.h>
22 #include <zephyr/spinlock.h>
23 #include <zephyr/sys/util.h>
24
25 #include "mmu.h"
26
27 LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
28
29 static uint64_t xlat_tables[CONFIG_MAX_XLAT_TABLES * Ln_XLAT_NUM_ENTRIES]
30 __aligned(Ln_XLAT_NUM_ENTRIES * sizeof(uint64_t));
31 static int xlat_use_count[CONFIG_MAX_XLAT_TABLES];
32 static struct k_spinlock xlat_lock;
33
34 /* Usage count value range */
35 #define XLAT_PTE_COUNT_MASK GENMASK(15, 0)
36 #define XLAT_REF_COUNT_UNIT BIT(16)
37
38 /* Returns a reference to a free table */
new_table(void)39 static uint64_t *new_table(void)
40 {
41 uint64_t *table;
42 unsigned int i;
43
44 /* Look for a free table. */
45 for (i = 0U; i < CONFIG_MAX_XLAT_TABLES; i++) {
46 if (xlat_use_count[i] == 0) {
47 table = &xlat_tables[i * Ln_XLAT_NUM_ENTRIES];
48 xlat_use_count[i] = XLAT_REF_COUNT_UNIT;
49 MMU_DEBUG("allocating table [%d]%p\n", i, table);
50 return table;
51 }
52 }
53
54 LOG_ERR("CONFIG_MAX_XLAT_TABLES, too small");
55 return NULL;
56 }
57
table_index(uint64_t * pte)58 static inline unsigned int table_index(uint64_t *pte)
59 {
60 unsigned int i = (pte - xlat_tables) / Ln_XLAT_NUM_ENTRIES;
61
62 __ASSERT(i < CONFIG_MAX_XLAT_TABLES, "table %p out of range", pte);
63 return i;
64 }
65
66 /* Adjusts usage count and returns current count. */
table_usage(uint64_t * table,int adjustment)67 static int table_usage(uint64_t *table, int adjustment)
68 {
69 unsigned int i = table_index(table);
70 int prev_count = xlat_use_count[i];
71 int new_count = prev_count + adjustment;
72
73 /* be reasonable not to always create a debug flood */
74 if ((IS_ENABLED(DUMP_PTE) && adjustment != 0) || new_count == 0) {
75 MMU_DEBUG("table [%d]%p: usage %#x -> %#x\n", i, table, prev_count, new_count);
76 }
77
78 __ASSERT(new_count >= 0,
79 "table use count underflow");
80 __ASSERT(new_count == 0 || new_count >= XLAT_REF_COUNT_UNIT,
81 "table in use with no reference to it");
82 __ASSERT((new_count & XLAT_PTE_COUNT_MASK) <= Ln_XLAT_NUM_ENTRIES,
83 "table PTE count overflow");
84
85 xlat_use_count[i] = new_count;
86 return new_count;
87 }
88
inc_table_ref(uint64_t * table)89 static inline void inc_table_ref(uint64_t *table)
90 {
91 table_usage(table, XLAT_REF_COUNT_UNIT);
92 }
93
dec_table_ref(uint64_t * table)94 static inline void dec_table_ref(uint64_t *table)
95 {
96 int ref_unit = XLAT_REF_COUNT_UNIT;
97
98 table_usage(table, -ref_unit);
99 }
100
is_table_unused(uint64_t * table)101 static inline bool is_table_unused(uint64_t *table)
102 {
103 return (table_usage(table, 0) & XLAT_PTE_COUNT_MASK) == 0;
104 }
105
is_table_single_referenced(uint64_t * table)106 static inline bool is_table_single_referenced(uint64_t *table)
107 {
108 return table_usage(table, 0) < (2 * XLAT_REF_COUNT_UNIT);
109 }
110
111 #ifdef CONFIG_TEST
112 /* Hooks to let test code peek at table states */
113
arm64_mmu_nb_free_tables(void)114 int arm64_mmu_nb_free_tables(void)
115 {
116 int count = 0;
117
118 for (int i = 0; i < CONFIG_MAX_XLAT_TABLES; i++) {
119 if (xlat_use_count[i] == 0) {
120 count++;
121 }
122 }
123
124 return count;
125 }
126
arm64_mmu_tables_total_usage(void)127 int arm64_mmu_tables_total_usage(void)
128 {
129 int count = 0;
130
131 for (int i = 0; i < CONFIG_MAX_XLAT_TABLES; i++) {
132 count += xlat_use_count[i];
133 }
134
135 return count;
136 }
137
138 #endif /* CONFIG_TEST */
139
is_free_desc(uint64_t desc)140 static inline bool is_free_desc(uint64_t desc)
141 {
142 return (desc & PTE_DESC_TYPE_MASK) == PTE_INVALID_DESC;
143 }
144
is_table_desc(uint64_t desc,unsigned int level)145 static inline bool is_table_desc(uint64_t desc, unsigned int level)
146 {
147 return level != XLAT_LAST_LEVEL &&
148 (desc & PTE_DESC_TYPE_MASK) == PTE_TABLE_DESC;
149 }
150
is_block_desc(uint64_t desc)151 static inline bool is_block_desc(uint64_t desc)
152 {
153 return (desc & PTE_DESC_TYPE_MASK) == PTE_BLOCK_DESC;
154 }
155
pte_desc_table(uint64_t desc)156 static inline uint64_t *pte_desc_table(uint64_t desc)
157 {
158 uint64_t address = desc & PTE_PHYSADDR_MASK;
159
160 /* tables use a 1:1 physical:virtual mapping */
161 return (uint64_t *)address;
162 }
163
is_desc_block_aligned(uint64_t desc,unsigned int level_size)164 static inline bool is_desc_block_aligned(uint64_t desc, unsigned int level_size)
165 {
166 bool aligned = (desc & PTE_PHYSADDR_MASK & (level_size - 1)) == 0;
167
168 if (!aligned) {
169 MMU_DEBUG("misaligned desc 0x%016llx for block size 0x%x\n",
170 desc, level_size);
171 }
172
173 return aligned;
174 }
175
is_desc_superset(uint64_t desc1,uint64_t desc2,unsigned int level)176 static inline bool is_desc_superset(uint64_t desc1, uint64_t desc2,
177 unsigned int level)
178 {
179 uint64_t mask = DESC_ATTRS_MASK | GENMASK64(47, LEVEL_TO_VA_SIZE_SHIFT(level));
180
181 return (desc1 & mask) == (desc2 & mask);
182 }
183
184 #if DUMP_PTE
debug_show_pte(uint64_t * pte,unsigned int level)185 static void debug_show_pte(uint64_t *pte, unsigned int level)
186 {
187 MMU_DEBUG("%.*s", level * 2U, ". . . ");
188 MMU_DEBUG("[%d]%p: ", table_index(pte), pte);
189
190 if (is_free_desc(*pte)) {
191 MMU_DEBUG("---\n");
192 return;
193 }
194
195 MMU_DEBUG("0x%016llx ", *pte);
196
197 if (is_table_desc(*pte, level)) {
198 uint64_t *table = pte_desc_table(*pte);
199
200 MMU_DEBUG("[Table] [%d]%p\n", table_index(table), table);
201 return;
202 }
203
204 if (is_block_desc(*pte)) {
205 MMU_DEBUG("[Block] ");
206 } else {
207 MMU_DEBUG("[Page] ");
208 }
209
210 uint8_t mem_type = (*pte >> 2) & MT_TYPE_MASK;
211
212 MMU_DEBUG((mem_type == MT_NORMAL) ? "MEM" :
213 ((mem_type == MT_NORMAL_NC) ? "NC" : "DEV"));
214 MMU_DEBUG((*pte & PTE_BLOCK_DESC_AP_RO) ? "-RO" : "-RW");
215 MMU_DEBUG((*pte & PTE_BLOCK_DESC_NS) ? "-NS" : "-S");
216 MMU_DEBUG((*pte & PTE_BLOCK_DESC_AP_ELx) ? "-ELx" : "-ELh");
217 MMU_DEBUG((*pte & PTE_BLOCK_DESC_PXN) ? "-PXN" : "-PX");
218 MMU_DEBUG((*pte & PTE_BLOCK_DESC_UXN) ? "-UXN" : "-UX");
219 MMU_DEBUG("\n");
220 }
221 #else
debug_show_pte(uint64_t * pte,unsigned int level)222 static inline void debug_show_pte(uint64_t *pte, unsigned int level) { }
223 #endif
224
set_pte_table_desc(uint64_t * pte,uint64_t * table,unsigned int level)225 static void set_pte_table_desc(uint64_t *pte, uint64_t *table, unsigned int level)
226 {
227 /* Point pte to new table */
228 *pte = PTE_TABLE_DESC | (uint64_t)table;
229 debug_show_pte(pte, level);
230 }
231
set_pte_block_desc(uint64_t * pte,uint64_t desc,unsigned int level)232 static void set_pte_block_desc(uint64_t *pte, uint64_t desc, unsigned int level)
233 {
234 if (desc) {
235 desc |= (level == XLAT_LAST_LEVEL) ? PTE_PAGE_DESC : PTE_BLOCK_DESC;
236 }
237 *pte = desc;
238 debug_show_pte(pte, level);
239 }
240
expand_to_table(uint64_t * pte,unsigned int level)241 static uint64_t *expand_to_table(uint64_t *pte, unsigned int level)
242 {
243 uint64_t *table;
244
245 __ASSERT(level < XLAT_LAST_LEVEL, "can't expand last level");
246
247 table = new_table();
248 if (!table) {
249 return NULL;
250 }
251
252 if (!is_free_desc(*pte)) {
253 /*
254 * If entry at current level was already populated
255 * then we need to reflect that in the new table.
256 */
257 uint64_t desc = *pte;
258 unsigned int i, stride_shift;
259
260 MMU_DEBUG("expanding PTE 0x%016llx into table [%d]%p\n",
261 desc, table_index(table), table);
262 __ASSERT(is_block_desc(desc), "");
263
264 if (level + 1 == XLAT_LAST_LEVEL) {
265 desc |= PTE_PAGE_DESC;
266 }
267
268 stride_shift = LEVEL_TO_VA_SIZE_SHIFT(level + 1);
269 for (i = 0U; i < Ln_XLAT_NUM_ENTRIES; i++) {
270 table[i] = desc | (i << stride_shift);
271 }
272 table_usage(table, Ln_XLAT_NUM_ENTRIES);
273 } else {
274 /*
275 * Adjust usage count for parent table's entry
276 * that will no longer be free.
277 */
278 table_usage(pte, 1);
279 }
280
281 /* Link the new table in place of the pte it replaces */
282 set_pte_table_desc(pte, table, level);
283
284 return table;
285 }
286
set_mapping(uint64_t * top_table,uintptr_t virt,size_t size,uint64_t desc,bool may_overwrite)287 static int set_mapping(uint64_t *top_table, uintptr_t virt, size_t size,
288 uint64_t desc, bool may_overwrite)
289 {
290 uint64_t *table = top_table;
291 uint64_t *pte;
292 uint64_t level_size;
293 unsigned int level = BASE_XLAT_LEVEL;
294
295 while (size) {
296 __ASSERT(level <= XLAT_LAST_LEVEL,
297 "max translation table level exceeded\n");
298
299 /* Locate PTE for given virtual address and page table level */
300 pte = &table[XLAT_TABLE_VA_IDX(virt, level)];
301
302 if (is_table_desc(*pte, level)) {
303 /* Move to the next translation table level */
304 level++;
305 table = pte_desc_table(*pte);
306 continue;
307 }
308
309 if (!may_overwrite && !is_free_desc(*pte)) {
310 /* the entry is already allocated */
311 LOG_ERR("entry already in use: "
312 "level %d pte %p *pte 0x%016llx",
313 level, pte, *pte);
314 return -EBUSY;
315 }
316
317 level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
318
319 if (is_desc_superset(*pte, desc, level)) {
320 /* This block already covers our range */
321 level_size -= (virt & (level_size - 1));
322 if (level_size > size) {
323 level_size = size;
324 }
325 goto move_on;
326 }
327
328 if ((size < level_size) || (virt & (level_size - 1)) ||
329 !is_desc_block_aligned(desc, level_size)) {
330 /* Range doesn't fit, create subtable */
331 table = expand_to_table(pte, level);
332 if (!table) {
333 return -ENOMEM;
334 }
335 level++;
336 continue;
337 }
338
339 /* Adjust usage count for corresponding table */
340 if (is_free_desc(*pte)) {
341 table_usage(pte, 1);
342 }
343 /* Create block/page descriptor */
344 set_pte_block_desc(pte, desc, level);
345
346 move_on:
347 virt += level_size;
348 desc += level_size;
349 size -= level_size;
350
351 /* Range is mapped, start again for next range */
352 table = top_table;
353 level = BASE_XLAT_LEVEL;
354 }
355
356 return 0;
357 }
358
del_mapping(uint64_t * table,uintptr_t virt,size_t size,unsigned int level)359 static void del_mapping(uint64_t *table, uintptr_t virt, size_t size,
360 unsigned int level)
361 {
362 size_t step, level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
363 uint64_t *pte, *subtable;
364
365 for ( ; size; virt += step, size -= step) {
366 step = level_size - (virt & (level_size - 1));
367 if (step > size) {
368 step = size;
369 }
370 pte = &table[XLAT_TABLE_VA_IDX(virt, level)];
371
372 if (is_free_desc(*pte)) {
373 continue;
374 }
375
376 if (is_table_desc(*pte, level)) {
377 subtable = pte_desc_table(*pte);
378 del_mapping(subtable, virt, step, level + 1);
379 if (!is_table_unused(subtable)) {
380 continue;
381 }
382 dec_table_ref(subtable);
383 } else {
384 /*
385 * We assume that block mappings will be unmapped
386 * as a whole and not partially.
387 */
388 __ASSERT(step == level_size, "");
389 }
390
391 /* free this entry */
392 *pte = 0;
393 table_usage(pte, -1);
394 }
395 }
396
397 #ifdef CONFIG_USERSPACE
398
dup_table(uint64_t * src_table,unsigned int level)399 static uint64_t *dup_table(uint64_t *src_table, unsigned int level)
400 {
401 uint64_t *dst_table = new_table();
402 int i, usage_count = 0;
403
404 if (!dst_table) {
405 return NULL;
406 }
407
408 MMU_DEBUG("dup (level %d) [%d]%p to [%d]%p\n", level,
409 table_index(src_table), src_table,
410 table_index(dst_table), dst_table);
411
412 for (i = 0; i < Ln_XLAT_NUM_ENTRIES; i++) {
413 /*
414 * After the table duplication, each table can be independently
415 * updated. Thus, entries may become non-global.
416 * To keep the invariants very simple, we thus force the non-global
417 * bit on duplication. Moreover, there is no process to revert this
418 * (e.g. in `globalize_table`). Could be improved in future work.
419 */
420 if (!is_free_desc(src_table[i]) && !is_table_desc(src_table[i], level)) {
421 src_table[i] |= PTE_BLOCK_DESC_NG;
422 }
423
424 dst_table[i] = src_table[i];
425 if (is_table_desc(dst_table[i], level)) {
426 inc_table_ref(pte_desc_table(dst_table[i]));
427 }
428 if (!is_free_desc(dst_table[i])) {
429 usage_count++;
430 }
431 }
432 table_usage(dst_table, usage_count);
433
434 return dst_table;
435 }
436
privatize_table(uint64_t * dst_table,uint64_t * src_table,uintptr_t virt,size_t size,unsigned int level)437 static int privatize_table(uint64_t *dst_table, uint64_t *src_table,
438 uintptr_t virt, size_t size, unsigned int level)
439 {
440 size_t step, level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
441 unsigned int i;
442 int ret;
443
444 for ( ; size; virt += step, size -= step) {
445 step = level_size - (virt & (level_size - 1));
446 if (step > size) {
447 step = size;
448 }
449 i = XLAT_TABLE_VA_IDX(virt, level);
450
451 if (!is_table_desc(dst_table[i], level) ||
452 !is_table_desc(src_table[i], level)) {
453 /* this entry is already private */
454 continue;
455 }
456
457 uint64_t *dst_subtable = pte_desc_table(dst_table[i]);
458 uint64_t *src_subtable = pte_desc_table(src_table[i]);
459
460 if (dst_subtable == src_subtable) {
461 /* need to make a private copy of this table */
462 dst_subtable = dup_table(src_subtable, level + 1);
463 if (!dst_subtable) {
464 return -ENOMEM;
465 }
466 set_pte_table_desc(&dst_table[i], dst_subtable, level);
467 dec_table_ref(src_subtable);
468 }
469
470 ret = privatize_table(dst_subtable, src_subtable,
471 virt, step, level + 1);
472 if (ret) {
473 return ret;
474 }
475 }
476
477 return 0;
478 }
479
480 /*
481 * Make the given virtual address range private in dst_pt with regards to
482 * src_pt. By "private" this means that corresponding page tables in dst_pt
483 * will be duplicated so not to share the same table(s) with src_pt.
484 * If corresponding page tables in dst_pt are already distinct from src_pt
485 * then nothing is done. This allows for subsequent mapping changes in that
486 * range to affect only dst_pt.
487 */
privatize_page_range(struct arm_mmu_ptables * dst_pt,struct arm_mmu_ptables * src_pt,uintptr_t virt_start,size_t size,const char * name)488 static int privatize_page_range(struct arm_mmu_ptables *dst_pt,
489 struct arm_mmu_ptables *src_pt,
490 uintptr_t virt_start, size_t size,
491 const char *name)
492 {
493 k_spinlock_key_t key;
494 int ret;
495
496 MMU_DEBUG("privatize [%s]: virt %lx size %lx\n",
497 name, virt_start, size);
498
499 key = k_spin_lock(&xlat_lock);
500
501 ret = privatize_table(dst_pt->base_xlat_table, src_pt->base_xlat_table,
502 virt_start, size, BASE_XLAT_LEVEL);
503
504 k_spin_unlock(&xlat_lock, key);
505 return ret;
506 }
507
discard_table(uint64_t * table,unsigned int level)508 static void discard_table(uint64_t *table, unsigned int level)
509 {
510 unsigned int i;
511 int free_count = 0;
512
513 for (i = 0U; i < Ln_XLAT_NUM_ENTRIES; i++) {
514 if (is_table_desc(table[i], level)) {
515 uint64_t *subtable = pte_desc_table(table[i]);
516
517 if (is_table_single_referenced(subtable)) {
518 discard_table(subtable, level + 1);
519 }
520 dec_table_ref(subtable);
521 }
522 if (!is_free_desc(table[i])) {
523 table[i] = 0U;
524 free_count++;
525 }
526 }
527 table_usage(table, -free_count);
528 }
529
globalize_table(uint64_t * dst_table,uint64_t * src_table,uintptr_t virt,size_t size,unsigned int level)530 static int globalize_table(uint64_t *dst_table, uint64_t *src_table,
531 uintptr_t virt, size_t size, unsigned int level)
532 {
533 size_t step, level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
534 unsigned int i;
535 int ret;
536
537 for ( ; size; virt += step, size -= step) {
538 step = level_size - (virt & (level_size - 1));
539 if (step > size) {
540 step = size;
541 }
542 i = XLAT_TABLE_VA_IDX(virt, level);
543
544 if (dst_table[i] == src_table[i]) {
545 /* already identical to global table */
546 continue;
547 }
548
549 if (is_free_desc(src_table[i]) &&
550 is_table_desc(dst_table[i], level)) {
551 uint64_t *subtable = pte_desc_table(dst_table[i]);
552
553 del_mapping(subtable, virt, step, level + 1);
554 if (is_table_unused(subtable)) {
555 /* unreference the empty table */
556 dst_table[i] = 0;
557 table_usage(dst_table, -1);
558 dec_table_ref(subtable);
559 }
560 continue;
561 }
562
563 if (step != level_size) {
564 /* boundary falls in the middle of this pte */
565 __ASSERT(is_table_desc(src_table[i], level),
566 "can't have partial block pte here");
567 if (!is_table_desc(dst_table[i], level)) {
568 /* we need more fine grained boundaries */
569 if (!expand_to_table(&dst_table[i], level)) {
570 return -ENOMEM;
571 }
572 }
573 ret = globalize_table(pte_desc_table(dst_table[i]),
574 pte_desc_table(src_table[i]),
575 virt, step, level + 1);
576 if (ret) {
577 return ret;
578 }
579 continue;
580 }
581
582 /* we discard current pte and replace with global one */
583
584 uint64_t *old_table = is_table_desc(dst_table[i], level) ?
585 pte_desc_table(dst_table[i]) : NULL;
586
587 if (is_free_desc(dst_table[i])) {
588 table_usage(dst_table, 1);
589 }
590 if (is_free_desc(src_table[i])) {
591 table_usage(dst_table, -1);
592 }
593 if (is_table_desc(src_table[i], level)) {
594 inc_table_ref(pte_desc_table(src_table[i]));
595 }
596 dst_table[i] = src_table[i];
597 debug_show_pte(&dst_table[i], level);
598
599 if (old_table) {
600 /* we can discard the whole branch */
601 discard_table(old_table, level + 1);
602 dec_table_ref(old_table);
603 }
604 }
605
606 return 0;
607 }
608
609 /*
610 * Globalize the given virtual address range in dst_pt from src_pt. We make
611 * it global by sharing as much page table content from src_pt as possible,
612 * including page tables themselves, and corresponding private tables in
613 * dst_pt are then discarded. If page tables in the given range are already
614 * shared then nothing is done. If page table sharing is not possible then
615 * page table entries in dst_pt are synchronized with those from src_pt.
616 */
globalize_page_range(struct arm_mmu_ptables * dst_pt,struct arm_mmu_ptables * src_pt,uintptr_t virt_start,size_t size,const char * name)617 static int globalize_page_range(struct arm_mmu_ptables *dst_pt,
618 struct arm_mmu_ptables *src_pt,
619 uintptr_t virt_start, size_t size,
620 const char *name)
621 {
622 k_spinlock_key_t key;
623 int ret;
624
625 MMU_DEBUG("globalize [%s]: virt %lx size %lx\n",
626 name, virt_start, size);
627
628 key = k_spin_lock(&xlat_lock);
629
630 ret = globalize_table(dst_pt->base_xlat_table, src_pt->base_xlat_table,
631 virt_start, size, BASE_XLAT_LEVEL);
632
633 k_spin_unlock(&xlat_lock, key);
634 return ret;
635 }
636
637 #endif /* CONFIG_USERSPACE */
638
get_region_desc(uint32_t attrs)639 static uint64_t get_region_desc(uint32_t attrs)
640 {
641 unsigned int mem_type;
642 uint64_t desc = 0U;
643
644 /* NS bit for security memory access from secure state */
645 desc |= (attrs & MT_NS) ? PTE_BLOCK_DESC_NS : 0;
646
647 /*
648 * AP bits for EL0 / ELh Data access permission
649 *
650 * AP[2:1] ELh EL0
651 * +--------------------+
652 * 00 RW NA
653 * 01 RW RW
654 * 10 RO NA
655 * 11 RO RO
656 */
657
658 /* AP bits for Data access permission */
659 desc |= (attrs & MT_RW) ? PTE_BLOCK_DESC_AP_RW : PTE_BLOCK_DESC_AP_RO;
660
661 /* Mirror permissions to EL0 */
662 desc |= (attrs & MT_RW_AP_ELx) ?
663 PTE_BLOCK_DESC_AP_ELx : PTE_BLOCK_DESC_AP_EL_HIGHER;
664
665 /* the access flag */
666 desc |= PTE_BLOCK_DESC_AF;
667
668 /* memory attribute index field */
669 mem_type = MT_TYPE(attrs);
670 desc |= PTE_BLOCK_DESC_MEMTYPE(mem_type);
671
672 switch (mem_type) {
673 case MT_DEVICE_nGnRnE:
674 case MT_DEVICE_nGnRE:
675 case MT_DEVICE_GRE:
676 /* Access to Device memory and non-cacheable memory are coherent
677 * for all observers in the system and are treated as
678 * Outer shareable, so, for these 2 types of memory,
679 * it is not strictly needed to set shareability field
680 */
681 desc |= PTE_BLOCK_DESC_OUTER_SHARE;
682 /* Map device memory as execute-never */
683 desc |= PTE_BLOCK_DESC_PXN;
684 desc |= PTE_BLOCK_DESC_UXN;
685 break;
686 case MT_NORMAL_NC:
687 case MT_NORMAL:
688 /* Make Normal RW memory as execute never */
689 if ((attrs & MT_RW) || (attrs & MT_P_EXECUTE_NEVER))
690 desc |= PTE_BLOCK_DESC_PXN;
691
692 if (((attrs & MT_RW) && (attrs & MT_RW_AP_ELx)) ||
693 (attrs & MT_U_EXECUTE_NEVER))
694 desc |= PTE_BLOCK_DESC_UXN;
695
696 if (mem_type == MT_NORMAL)
697 desc |= PTE_BLOCK_DESC_INNER_SHARE;
698 else
699 desc |= PTE_BLOCK_DESC_OUTER_SHARE;
700 }
701
702 /* non-Global bit */
703 if (attrs & MT_NG) {
704 desc |= PTE_BLOCK_DESC_NG;
705 }
706
707 return desc;
708 }
709
__add_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)710 static int __add_map(struct arm_mmu_ptables *ptables, const char *name,
711 uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
712 {
713 uint64_t desc = get_region_desc(attrs);
714 bool may_overwrite = !(attrs & MT_NO_OVERWRITE);
715
716 MMU_DEBUG("mmap [%s]: virt %lx phys %lx size %lx attr %llx %s overwrite\n",
717 name, virt, phys, size, desc,
718 may_overwrite ? "may" : "no");
719 __ASSERT(((virt | phys | size) & (CONFIG_MMU_PAGE_SIZE - 1)) == 0,
720 "address/size are not page aligned\n");
721 desc |= phys;
722 return set_mapping(ptables->base_xlat_table, virt, size, desc, may_overwrite);
723 }
724
add_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)725 static int add_map(struct arm_mmu_ptables *ptables, const char *name,
726 uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
727 {
728 k_spinlock_key_t key;
729 int ret;
730
731 key = k_spin_lock(&xlat_lock);
732 ret = __add_map(ptables, name, phys, virt, size, attrs);
733 k_spin_unlock(&xlat_lock, key);
734 return ret;
735 }
736
remove_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t virt,size_t size)737 static void remove_map(struct arm_mmu_ptables *ptables, const char *name,
738 uintptr_t virt, size_t size)
739 {
740 k_spinlock_key_t key;
741
742 MMU_DEBUG("unmmap [%s]: virt %lx size %lx\n", name, virt, size);
743 __ASSERT(((virt | size) & (CONFIG_MMU_PAGE_SIZE - 1)) == 0,
744 "address/size are not page aligned\n");
745
746 key = k_spin_lock(&xlat_lock);
747 del_mapping(ptables->base_xlat_table, virt, size, BASE_XLAT_LEVEL);
748 k_spin_unlock(&xlat_lock, key);
749 }
750
invalidate_tlb_all(void)751 static void invalidate_tlb_all(void)
752 {
753 __asm__ volatile (
754 "dsb ishst; tlbi vmalle1; dsb ish; isb"
755 : : : "memory");
756 }
757
758 /* zephyr execution regions with appropriate attributes */
759
760 struct arm_mmu_flat_range {
761 char *name;
762 void *start;
763 void *end;
764 uint32_t attrs;
765 };
766
767 static const struct arm_mmu_flat_range mmu_zephyr_ranges[] = {
768
769 /* Mark the zephyr execution regions (data, bss, noinit, etc.)
770 * cacheable, read-write
771 * Note: read-write region is marked execute-never internally
772 */
773 { .name = "zephyr_data",
774 .start = _image_ram_start,
775 .end = _image_ram_end,
776 .attrs = MT_NORMAL | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE },
777
778 /* Mark text segment cacheable,read only and executable */
779 { .name = "zephyr_code",
780 .start = __text_region_start,
781 .end = __text_region_end,
782 .attrs = MT_NORMAL | MT_P_RX_U_RX | MT_DEFAULT_SECURE_STATE },
783
784 /* Mark rodata segment cacheable, read only and execute-never */
785 { .name = "zephyr_rodata",
786 .start = __rodata_region_start,
787 .end = __rodata_region_end,
788 .attrs = MT_NORMAL | MT_P_RO_U_RO | MT_DEFAULT_SECURE_STATE },
789
790 #ifdef CONFIG_NOCACHE_MEMORY
791 /* Mark nocache segment noncachable, read-write and execute-never */
792 { .name = "nocache_data",
793 .start = _nocache_ram_start,
794 .end = _nocache_ram_end,
795 .attrs = MT_NORMAL_NC | MT_P_RW_U_RW | MT_DEFAULT_SECURE_STATE },
796 #endif
797 };
798
add_arm_mmu_flat_range(struct arm_mmu_ptables * ptables,const struct arm_mmu_flat_range * range,uint32_t extra_flags)799 static inline void add_arm_mmu_flat_range(struct arm_mmu_ptables *ptables,
800 const struct arm_mmu_flat_range *range,
801 uint32_t extra_flags)
802 {
803 uintptr_t address = (uintptr_t)range->start;
804 size_t size = (uintptr_t)range->end - address;
805
806 if (size) {
807 /* MMU not yet active: must use unlocked version */
808 __add_map(ptables, range->name, address, address,
809 size, range->attrs | extra_flags);
810 }
811 }
812
add_arm_mmu_region(struct arm_mmu_ptables * ptables,const struct arm_mmu_region * region,uint32_t extra_flags)813 static inline void add_arm_mmu_region(struct arm_mmu_ptables *ptables,
814 const struct arm_mmu_region *region,
815 uint32_t extra_flags)
816 {
817 if (region->size || region->attrs) {
818 /* MMU not yet active: must use unlocked version */
819 __add_map(ptables, region->name, region->base_pa, region->base_va,
820 region->size, region->attrs | extra_flags);
821 }
822 }
823
inv_dcache_after_map_helper(void * virt,size_t size,uint32_t attrs)824 static inline void inv_dcache_after_map_helper(void *virt, size_t size, uint32_t attrs)
825 {
826 /*
827 * DC IVAC instruction requires write access permission to the VA,
828 * otherwise it can generate a permission fault
829 */
830 if ((attrs & MT_RW) != MT_RW) {
831 return;
832 }
833
834 if (MT_TYPE(attrs) == MT_NORMAL || MT_TYPE(attrs) == MT_NORMAL_WT) {
835 sys_cache_data_invd_range(virt, size);
836 }
837 }
838
setup_page_tables(struct arm_mmu_ptables * ptables)839 static void setup_page_tables(struct arm_mmu_ptables *ptables)
840 {
841 unsigned int index;
842 const struct arm_mmu_flat_range *range;
843 const struct arm_mmu_region *region;
844 uintptr_t max_va = 0, max_pa = 0;
845
846 MMU_DEBUG("xlat tables:\n");
847 for (index = 0U; index < CONFIG_MAX_XLAT_TABLES; index++)
848 MMU_DEBUG("%d: %p\n", index, xlat_tables + index * Ln_XLAT_NUM_ENTRIES);
849
850 for (index = 0U; index < mmu_config.num_regions; index++) {
851 region = &mmu_config.mmu_regions[index];
852 max_va = MAX(max_va, region->base_va + region->size);
853 max_pa = MAX(max_pa, region->base_pa + region->size);
854 }
855
856 __ASSERT(max_va <= (1ULL << CONFIG_ARM64_VA_BITS),
857 "Maximum VA not supported\n");
858 __ASSERT(max_pa <= (1ULL << CONFIG_ARM64_PA_BITS),
859 "Maximum PA not supported\n");
860
861 /* setup translation table for zephyr execution regions */
862 for (index = 0U; index < ARRAY_SIZE(mmu_zephyr_ranges); index++) {
863 range = &mmu_zephyr_ranges[index];
864 add_arm_mmu_flat_range(ptables, range, 0);
865 }
866
867 /*
868 * Create translation tables for user provided platform regions.
869 * Those must not conflict with our default mapping.
870 */
871 for (index = 0U; index < mmu_config.num_regions; index++) {
872 region = &mmu_config.mmu_regions[index];
873 add_arm_mmu_region(ptables, region, MT_NO_OVERWRITE);
874 }
875
876 invalidate_tlb_all();
877
878 for (index = 0U; index < ARRAY_SIZE(mmu_zephyr_ranges); index++) {
879 size_t size;
880
881 range = &mmu_zephyr_ranges[index];
882 size = POINTER_TO_UINT(range->end) - POINTER_TO_UINT(range->start);
883 inv_dcache_after_map_helper(range->start, size, range->attrs);
884 }
885
886 for (index = 0U; index < mmu_config.num_regions; index++) {
887 region = &mmu_config.mmu_regions[index];
888 inv_dcache_after_map_helper(UINT_TO_POINTER(region->base_va), region->size,
889 region->attrs);
890 }
891 }
892
893 /* Translation table control register settings */
get_tcr(int el)894 static uint64_t get_tcr(int el)
895 {
896 uint64_t tcr;
897 uint64_t va_bits = CONFIG_ARM64_VA_BITS;
898 uint64_t tcr_ps_bits;
899
900 tcr_ps_bits = TCR_PS_BITS;
901
902 if (el == 1) {
903 tcr = (tcr_ps_bits << TCR_EL1_IPS_SHIFT);
904 /*
905 * TCR_EL1.EPD1: Disable translation table walk for addresses
906 * that are translated using TTBR1_EL1.
907 */
908 tcr |= TCR_EPD1_DISABLE;
909 } else {
910 tcr = (tcr_ps_bits << TCR_EL3_PS_SHIFT);
911 }
912
913 tcr |= TCR_T0SZ(va_bits);
914
915 /*
916 * Translation table walk is cacheable, inner/outer WBWA and
917 * inner shareable. Due to Cortex-A57 erratum #822227 we must
918 * set TG1[1] = 4KB.
919 */
920 tcr |= TCR_TG1_4K | TCR_TG0_4K | TCR_SHARED_INNER |
921 TCR_ORGN_WBWA | TCR_IRGN_WBWA;
922
923 return tcr;
924 }
925
enable_mmu_el1(struct arm_mmu_ptables * ptables,unsigned int flags)926 static void enable_mmu_el1(struct arm_mmu_ptables *ptables, unsigned int flags)
927 {
928 ARG_UNUSED(flags);
929 uint64_t val;
930
931 /* Set MAIR, TCR and TBBR registers */
932 write_mair_el1(MEMORY_ATTRIBUTES);
933 write_tcr_el1(get_tcr(1));
934 write_ttbr0_el1((uint64_t)ptables->base_xlat_table);
935
936 /* Ensure these changes are seen before MMU is enabled */
937 barrier_isync_fence_full();
938
939 /* Enable the MMU and data cache */
940 val = read_sctlr_el1();
941 write_sctlr_el1(val | SCTLR_M_BIT | SCTLR_C_BIT);
942
943 /* Ensure the MMU enable takes effect immediately */
944 barrier_isync_fence_full();
945
946 MMU_DEBUG("MMU enabled with dcache\n");
947 }
948
949 /* ARM MMU Driver Initial Setup */
950
951 static struct arm_mmu_ptables kernel_ptables;
952 #ifdef CONFIG_USERSPACE
953 static sys_slist_t domain_list;
954 #endif
955
956 /*
957 * @brief MMU default configuration
958 *
959 * This function provides the default configuration mechanism for the Memory
960 * Management Unit (MMU).
961 */
z_arm64_mm_init(bool is_primary_core)962 void z_arm64_mm_init(bool is_primary_core)
963 {
964 unsigned int flags = 0U;
965
966 __ASSERT(CONFIG_MMU_PAGE_SIZE == KB(4),
967 "Only 4K page size is supported\n");
968
969 __ASSERT(GET_EL(read_currentel()) == MODE_EL1,
970 "Exception level not EL1, MMU not enabled!\n");
971
972 /* Ensure that MMU is already not enabled */
973 __ASSERT((read_sctlr_el1() & SCTLR_M_BIT) == 0, "MMU is already enabled\n");
974
975 /*
976 * Only booting core setup up the page tables.
977 */
978 if (is_primary_core) {
979 kernel_ptables.base_xlat_table = new_table();
980 setup_page_tables(&kernel_ptables);
981 }
982
983 /* currently only EL1 is supported */
984 enable_mmu_el1(&kernel_ptables, flags);
985 }
986
sync_domains(uintptr_t virt,size_t size,const char * name)987 static void sync_domains(uintptr_t virt, size_t size, const char *name)
988 {
989 #ifdef CONFIG_USERSPACE
990 sys_snode_t *node;
991 struct arch_mem_domain *domain;
992 struct arm_mmu_ptables *domain_ptables;
993 k_spinlock_key_t key;
994 int ret;
995
996 key = k_spin_lock(&z_mem_domain_lock);
997 SYS_SLIST_FOR_EACH_NODE(&domain_list, node) {
998 domain = CONTAINER_OF(node, struct arch_mem_domain, node);
999 domain_ptables = &domain->ptables;
1000 ret = globalize_page_range(domain_ptables, &kernel_ptables,
1001 virt, size, name);
1002 if (ret) {
1003 LOG_ERR("globalize_page_range() returned %d", ret);
1004 }
1005 }
1006 k_spin_unlock(&z_mem_domain_lock, key);
1007 #endif
1008 }
1009
__arch_mem_map(void * virt,uintptr_t phys,size_t size,uint32_t flags)1010 static int __arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
1011 {
1012 struct arm_mmu_ptables *ptables;
1013 uint32_t entry_flags = MT_DEFAULT_SECURE_STATE | MT_P_RX_U_NA | MT_NO_OVERWRITE;
1014
1015 /* Always map in the kernel page tables */
1016 ptables = &kernel_ptables;
1017
1018 /* Translate flags argument into HW-recognized entry flags. */
1019 switch (flags & K_MEM_CACHE_MASK) {
1020 /*
1021 * K_MEM_CACHE_NONE, K_MEM_ARM_DEVICE_nGnRnE => MT_DEVICE_nGnRnE
1022 * (Device memory nGnRnE)
1023 * K_MEM_ARM_DEVICE_nGnRE => MT_DEVICE_nGnRE
1024 * (Device memory nGnRE)
1025 * K_MEM_ARM_DEVICE_GRE => MT_DEVICE_GRE
1026 * (Device memory GRE)
1027 * K_MEM_ARM_NORMAL_NC => MT_NORMAL_NC
1028 * (Normal memory Non-cacheable)
1029 * K_MEM_CACHE_WB => MT_NORMAL
1030 * (Normal memory Outer WB + Inner WB)
1031 * K_MEM_CACHE_WT => MT_NORMAL_WT
1032 * (Normal memory Outer WT + Inner WT)
1033 */
1034 case K_MEM_CACHE_NONE:
1035 /* K_MEM_CACHE_NONE equal to K_MEM_ARM_DEVICE_nGnRnE */
1036 /* case K_MEM_ARM_DEVICE_nGnRnE: */
1037 entry_flags |= MT_DEVICE_nGnRnE;
1038 break;
1039 case K_MEM_ARM_DEVICE_nGnRE:
1040 entry_flags |= MT_DEVICE_nGnRE;
1041 break;
1042 case K_MEM_ARM_DEVICE_GRE:
1043 entry_flags |= MT_DEVICE_GRE;
1044 break;
1045 case K_MEM_ARM_NORMAL_NC:
1046 entry_flags |= MT_NORMAL_NC;
1047 break;
1048 case K_MEM_CACHE_WT:
1049 entry_flags |= MT_NORMAL_WT;
1050 break;
1051 case K_MEM_CACHE_WB:
1052 entry_flags |= MT_NORMAL;
1053 break;
1054 default:
1055 return -ENOTSUP;
1056 }
1057
1058 if ((flags & K_MEM_PERM_RW) != 0U) {
1059 entry_flags |= MT_RW;
1060 }
1061
1062 if ((flags & K_MEM_PERM_EXEC) == 0U) {
1063 entry_flags |= MT_P_EXECUTE_NEVER;
1064 }
1065
1066 if ((flags & K_MEM_PERM_USER) != 0U) {
1067 entry_flags |= MT_RW_AP_ELx;
1068 }
1069
1070 return add_map(ptables, "generic", phys, (uintptr_t)virt, size, entry_flags);
1071 }
1072
arch_mem_map(void * virt,uintptr_t phys,size_t size,uint32_t flags)1073 void arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
1074 {
1075 int ret = __arch_mem_map(virt, phys, size, flags);
1076
1077 if (ret) {
1078 LOG_ERR("__arch_mem_map() returned %d", ret);
1079 k_panic();
1080 } else {
1081 uint32_t mem_flags = flags & K_MEM_CACHE_MASK;
1082
1083 sync_domains((uintptr_t)virt, size, "mem_map");
1084 invalidate_tlb_all();
1085
1086 switch (mem_flags) {
1087 case K_MEM_CACHE_WB:
1088 case K_MEM_CACHE_WT:
1089 mem_flags = (mem_flags == K_MEM_CACHE_WB) ? MT_NORMAL : MT_NORMAL_WT;
1090 mem_flags |= (flags & K_MEM_PERM_RW) ? MT_RW : 0;
1091 inv_dcache_after_map_helper(virt, size, mem_flags);
1092 default:
1093 break;
1094 }
1095 }
1096 }
1097
arch_mem_unmap(void * addr,size_t size)1098 void arch_mem_unmap(void *addr, size_t size)
1099 {
1100 remove_map(&kernel_ptables, "generic", (uintptr_t)addr, size);
1101 sync_domains((uintptr_t)addr, size, "mem_unmap");
1102 invalidate_tlb_all();
1103 }
1104
arch_page_phys_get(void * virt,uintptr_t * phys)1105 int arch_page_phys_get(void *virt, uintptr_t *phys)
1106 {
1107 uint64_t par;
1108 int key;
1109
1110 key = arch_irq_lock();
1111 __asm__ volatile ("at S1E1R, %0" : : "r" (virt));
1112 barrier_isync_fence_full();
1113 par = read_par_el1();
1114 arch_irq_unlock(key);
1115
1116 if (par & BIT(0)) {
1117 return -EFAULT;
1118 }
1119
1120 if (phys) {
1121 *phys = par & GENMASK64(47, 12);
1122 }
1123 return 0;
1124 }
1125
arch_virt_region_align(uintptr_t phys,size_t size)1126 size_t arch_virt_region_align(uintptr_t phys, size_t size)
1127 {
1128 size_t alignment = CONFIG_MMU_PAGE_SIZE;
1129 size_t level_size;
1130 int level;
1131
1132 for (level = XLAT_LAST_LEVEL; level >= BASE_XLAT_LEVEL; level--) {
1133 level_size = 1 << LEVEL_TO_VA_SIZE_SHIFT(level);
1134
1135 if (size < level_size) {
1136 break;
1137 }
1138
1139 if ((phys & (level_size - 1))) {
1140 break;
1141 }
1142
1143 alignment = level_size;
1144 }
1145
1146 return alignment;
1147 }
1148
1149 #ifdef CONFIG_USERSPACE
1150
1151 static uint16_t next_asid = 1;
1152
get_asid(uint64_t ttbr0)1153 static uint16_t get_asid(uint64_t ttbr0)
1154 {
1155 return ttbr0 >> TTBR_ASID_SHIFT;
1156 }
1157
1158 static void z_arm64_swap_ptables(struct k_thread *incoming);
1159
arch_mem_domain_max_partitions_get(void)1160 int arch_mem_domain_max_partitions_get(void)
1161 {
1162 return CONFIG_MAX_DOMAIN_PARTITIONS;
1163 }
1164
arch_mem_domain_init(struct k_mem_domain * domain)1165 int arch_mem_domain_init(struct k_mem_domain *domain)
1166 {
1167 struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
1168 k_spinlock_key_t key;
1169 uint16_t asid;
1170
1171 MMU_DEBUG("%s\n", __func__);
1172
1173 key = k_spin_lock(&xlat_lock);
1174
1175 /*
1176 * Pick a new ASID. We use round-robin
1177 * Note: `next_asid` is an uint16_t and `VM_ASID_BITS` could
1178 * be up to 16, hence `next_asid` might overflow to 0 below.
1179 */
1180 asid = next_asid++;
1181 if ((next_asid >= (1UL << VM_ASID_BITS)) || (next_asid == 0)) {
1182 next_asid = 1;
1183 }
1184
1185 domain_ptables->base_xlat_table =
1186 dup_table(kernel_ptables.base_xlat_table, BASE_XLAT_LEVEL);
1187 k_spin_unlock(&xlat_lock, key);
1188 if (!domain_ptables->base_xlat_table) {
1189 return -ENOMEM;
1190 }
1191
1192 domain_ptables->ttbr0 = (((uint64_t)asid) << TTBR_ASID_SHIFT) |
1193 ((uint64_t)(uintptr_t)domain_ptables->base_xlat_table);
1194
1195 sys_slist_append(&domain_list, &domain->arch.node);
1196 return 0;
1197 }
1198
private_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)1199 static int private_map(struct arm_mmu_ptables *ptables, const char *name,
1200 uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
1201 {
1202 int ret;
1203
1204 ret = privatize_page_range(ptables, &kernel_ptables, virt, size, name);
1205 __ASSERT(ret == 0, "privatize_page_range() returned %d", ret);
1206 ret = add_map(ptables, name, phys, virt, size, attrs | MT_NG);
1207 __ASSERT(ret == 0, "add_map() returned %d", ret);
1208 invalidate_tlb_all();
1209
1210 inv_dcache_after_map_helper(UINT_TO_POINTER(virt), size, attrs);
1211 return ret;
1212 }
1213
reset_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t addr,size_t size)1214 static int reset_map(struct arm_mmu_ptables *ptables, const char *name,
1215 uintptr_t addr, size_t size)
1216 {
1217 int ret;
1218
1219 ret = globalize_page_range(ptables, &kernel_ptables, addr, size, name);
1220 __ASSERT(ret == 0, "globalize_page_range() returned %d", ret);
1221 invalidate_tlb_all();
1222
1223 return ret;
1224 }
1225
arch_mem_domain_partition_add(struct k_mem_domain * domain,uint32_t partition_id)1226 int arch_mem_domain_partition_add(struct k_mem_domain *domain,
1227 uint32_t partition_id)
1228 {
1229 struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
1230 struct k_mem_partition *ptn = &domain->partitions[partition_id];
1231
1232 return private_map(domain_ptables, "partition", ptn->start, ptn->start,
1233 ptn->size, ptn->attr.attrs | MT_NORMAL);
1234 }
1235
arch_mem_domain_partition_remove(struct k_mem_domain * domain,uint32_t partition_id)1236 int arch_mem_domain_partition_remove(struct k_mem_domain *domain,
1237 uint32_t partition_id)
1238 {
1239 struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
1240 struct k_mem_partition *ptn = &domain->partitions[partition_id];
1241
1242 return reset_map(domain_ptables, "partition removal",
1243 ptn->start, ptn->size);
1244 }
1245
map_thread_stack(struct k_thread * thread,struct arm_mmu_ptables * ptables)1246 static int map_thread_stack(struct k_thread *thread,
1247 struct arm_mmu_ptables *ptables)
1248 {
1249 return private_map(ptables, "thread_stack", thread->stack_info.start,
1250 thread->stack_info.start, thread->stack_info.size,
1251 MT_P_RW_U_RW | MT_NORMAL);
1252 }
1253
arch_mem_domain_thread_add(struct k_thread * thread)1254 int arch_mem_domain_thread_add(struct k_thread *thread)
1255 {
1256 struct arm_mmu_ptables *old_ptables, *domain_ptables;
1257 struct k_mem_domain *domain;
1258 bool is_user, is_migration;
1259 int ret = 0;
1260
1261 domain = thread->mem_domain_info.mem_domain;
1262 domain_ptables = &domain->arch.ptables;
1263 old_ptables = thread->arch.ptables;
1264
1265 is_user = (thread->base.user_options & K_USER) != 0;
1266 is_migration = (old_ptables != NULL) && is_user;
1267
1268 if (is_migration) {
1269 ret = map_thread_stack(thread, domain_ptables);
1270 }
1271
1272 thread->arch.ptables = domain_ptables;
1273 if (thread == _current) {
1274 z_arm64_swap_ptables(thread);
1275 } else {
1276 #ifdef CONFIG_SMP
1277 /* the thread could be running on another CPU right now */
1278 z_arm64_mem_cfg_ipi();
1279 #endif
1280 }
1281
1282 if (is_migration) {
1283 ret = reset_map(old_ptables, __func__, thread->stack_info.start,
1284 thread->stack_info.size);
1285 }
1286
1287 return ret;
1288 }
1289
arch_mem_domain_thread_remove(struct k_thread * thread)1290 int arch_mem_domain_thread_remove(struct k_thread *thread)
1291 {
1292 struct arm_mmu_ptables *domain_ptables;
1293 struct k_mem_domain *domain;
1294
1295 domain = thread->mem_domain_info.mem_domain;
1296 domain_ptables = &domain->arch.ptables;
1297
1298 if ((thread->base.user_options & K_USER) == 0) {
1299 return 0;
1300 }
1301
1302 if ((thread->base.thread_state & _THREAD_DEAD) == 0) {
1303 return 0;
1304 }
1305
1306 return reset_map(domain_ptables, __func__, thread->stack_info.start,
1307 thread->stack_info.size);
1308 }
1309
z_arm64_swap_ptables(struct k_thread * incoming)1310 static void z_arm64_swap_ptables(struct k_thread *incoming)
1311 {
1312 struct arm_mmu_ptables *ptables = incoming->arch.ptables;
1313 uint64_t curr_ttbr0 = read_ttbr0_el1();
1314 uint64_t new_ttbr0 = ptables->ttbr0;
1315
1316 if (curr_ttbr0 == new_ttbr0) {
1317 return; /* Already the right tables */
1318 }
1319
1320 MMU_DEBUG("TTBR0 switch from %#llx to %#llx\n", curr_ttbr0, new_ttbr0);
1321 z_arm64_set_ttbr0(new_ttbr0);
1322
1323 if (get_asid(curr_ttbr0) == get_asid(new_ttbr0)) {
1324 invalidate_tlb_all();
1325 }
1326 }
1327
z_arm64_thread_mem_domains_init(struct k_thread * incoming)1328 void z_arm64_thread_mem_domains_init(struct k_thread *incoming)
1329 {
1330 struct arm_mmu_ptables *ptables;
1331
1332 if ((incoming->base.user_options & K_USER) == 0)
1333 return;
1334
1335 ptables = incoming->arch.ptables;
1336
1337 /* Map the thread stack */
1338 map_thread_stack(incoming, ptables);
1339
1340 z_arm64_swap_ptables(incoming);
1341 }
1342
z_arm64_swap_mem_domains(struct k_thread * incoming)1343 void z_arm64_swap_mem_domains(struct k_thread *incoming)
1344 {
1345 z_arm64_swap_ptables(incoming);
1346 }
1347
1348 #endif /* CONFIG_USERSPACE */
1349