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