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 <cache.h>
11 #include <device.h>
12 #include <init.h>
13 #include <kernel.h>
14 #include <kernel_arch_func.h>
15 #include <kernel_arch_interface.h>
16 #include <kernel_internal.h>
17 #include <logging/log.h>
18 #include <arch/arm64/cpu.h>
19 #include <arch/arm64/lib_helpers.h>
20 #include <arch/arm64/arm_mmu.h>
21 #include <linker/linker-defs.h>
22 #include <spinlock.h>
23 #include <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 uint16_t xlat_use_count[CONFIG_MAX_XLAT_TABLES];
32 static struct k_spinlock xlat_lock;
33 
34 /* Returns a reference to a free table */
new_table(void)35 static uint64_t *new_table(void)
36 {
37 	unsigned int i;
38 
39 	/* Look for a free table. */
40 	for (i = 0U; i < CONFIG_MAX_XLAT_TABLES; i++) {
41 		if (xlat_use_count[i] == 0U) {
42 			xlat_use_count[i] = 1U;
43 			return &xlat_tables[i * Ln_XLAT_NUM_ENTRIES];
44 		}
45 	}
46 
47 	LOG_ERR("CONFIG_MAX_XLAT_TABLES, too small");
48 	return NULL;
49 }
50 
table_index(uint64_t * pte)51 static inline unsigned int table_index(uint64_t *pte)
52 {
53 	unsigned int i = (pte - xlat_tables) / Ln_XLAT_NUM_ENTRIES;
54 
55 	__ASSERT(i < CONFIG_MAX_XLAT_TABLES, "table %p out of range", pte);
56 	return i;
57 }
58 
59 /* Makes a table free for reuse. */
free_table(uint64_t * table)60 static void free_table(uint64_t *table)
61 {
62 	unsigned int i = table_index(table);
63 
64 	MMU_DEBUG("freeing table [%d]%p\n", i, table);
65 	__ASSERT(xlat_use_count[i] == 1U, "table still in use");
66 	xlat_use_count[i] = 0U;
67 }
68 
69 /* Adjusts usage count and returns current count. */
table_usage(uint64_t * table,int adjustment)70 static int table_usage(uint64_t *table, int adjustment)
71 {
72 	unsigned int i = table_index(table);
73 
74 	xlat_use_count[i] += adjustment;
75 	__ASSERT(xlat_use_count[i] > 0, "usage count underflow");
76 	return xlat_use_count[i];
77 }
78 
is_table_unused(uint64_t * table)79 static inline bool is_table_unused(uint64_t *table)
80 {
81 	return table_usage(table, 0) == 1;
82 }
83 
is_free_desc(uint64_t desc)84 static inline bool is_free_desc(uint64_t desc)
85 {
86 	return (desc & PTE_DESC_TYPE_MASK) == PTE_INVALID_DESC;
87 }
88 
is_table_desc(uint64_t desc,unsigned int level)89 static inline bool is_table_desc(uint64_t desc, unsigned int level)
90 {
91 	return level != XLAT_LAST_LEVEL &&
92 	       (desc & PTE_DESC_TYPE_MASK) == PTE_TABLE_DESC;
93 }
94 
is_block_desc(uint64_t desc)95 static inline bool is_block_desc(uint64_t desc)
96 {
97 	return (desc & PTE_DESC_TYPE_MASK) == PTE_BLOCK_DESC;
98 }
99 
pte_desc_table(uint64_t desc)100 static inline uint64_t *pte_desc_table(uint64_t desc)
101 {
102 	uint64_t address = desc & GENMASK(47, PAGE_SIZE_SHIFT);
103 
104 	return (uint64_t *)address;
105 }
106 
is_desc_superset(uint64_t desc1,uint64_t desc2,unsigned int level)107 static inline bool is_desc_superset(uint64_t desc1, uint64_t desc2,
108 				    unsigned int level)
109 {
110 	uint64_t mask = DESC_ATTRS_MASK | GENMASK(47, LEVEL_TO_VA_SIZE_SHIFT(level));
111 
112 	return (desc1 & mask) == (desc2 & mask);
113 }
114 
115 #if DUMP_PTE
debug_show_pte(uint64_t * pte,unsigned int level)116 static void debug_show_pte(uint64_t *pte, unsigned int level)
117 {
118 	MMU_DEBUG("%.*s", level * 2U, ". . . ");
119 	MMU_DEBUG("[%d]%p: ", table_index(pte), pte);
120 
121 	if (is_free_desc(*pte)) {
122 		MMU_DEBUG("---\n");
123 		return;
124 	}
125 
126 	if (is_table_desc(*pte, level)) {
127 		uint64_t *table = pte_desc_table(*pte);
128 
129 		MMU_DEBUG("[Table] [%d]%p\n", table_index(table), table);
130 		return;
131 	}
132 
133 	if (is_block_desc(*pte)) {
134 		MMU_DEBUG("[Block] ");
135 	} else {
136 		MMU_DEBUG("[Page] ");
137 	}
138 
139 	uint8_t mem_type = (*pte >> 2) & MT_TYPE_MASK;
140 
141 	MMU_DEBUG((mem_type == MT_NORMAL) ? "MEM" :
142 		  ((mem_type == MT_NORMAL_NC) ? "NC" : "DEV"));
143 	MMU_DEBUG((*pte & PTE_BLOCK_DESC_AP_RO) ? "-RO" : "-RW");
144 	MMU_DEBUG((*pte & PTE_BLOCK_DESC_NS) ? "-NS" : "-S");
145 	MMU_DEBUG((*pte & PTE_BLOCK_DESC_AP_ELx) ? "-ELx" : "-ELh");
146 	MMU_DEBUG((*pte & PTE_BLOCK_DESC_PXN) ? "-PXN" : "-PX");
147 	MMU_DEBUG((*pte & PTE_BLOCK_DESC_UXN) ? "-UXN" : "-UX");
148 	MMU_DEBUG("\n");
149 }
150 #else
debug_show_pte(uint64_t * pte,unsigned int level)151 static inline void debug_show_pte(uint64_t *pte, unsigned int level) { }
152 #endif
153 
set_pte_table_desc(uint64_t * pte,uint64_t * table,unsigned int level)154 static void set_pte_table_desc(uint64_t *pte, uint64_t *table, unsigned int level)
155 {
156 	/* Point pte to new table */
157 	*pte = PTE_TABLE_DESC | (uint64_t)table;
158 	debug_show_pte(pte, level);
159 }
160 
set_pte_block_desc(uint64_t * pte,uint64_t desc,unsigned int level)161 static void set_pte_block_desc(uint64_t *pte, uint64_t desc, unsigned int level)
162 {
163 	if (desc) {
164 		desc |= (level == XLAT_LAST_LEVEL) ? PTE_PAGE_DESC : PTE_BLOCK_DESC;
165 	}
166 	*pte = desc;
167 	debug_show_pte(pte, level);
168 }
169 
expand_to_table(uint64_t * pte,unsigned int level)170 static uint64_t *expand_to_table(uint64_t *pte, unsigned int level)
171 {
172 	uint64_t *table;
173 
174 	__ASSERT(level < XLAT_LAST_LEVEL, "can't expand last level");
175 
176 	table = new_table();
177 	if (!table) {
178 		return NULL;
179 	}
180 
181 	if (!is_free_desc(*pte)) {
182 		/*
183 		 * If entry at current level was already populated
184 		 * then we need to reflect that in the new table.
185 		 */
186 		uint64_t desc = *pte;
187 		unsigned int i, stride_shift;
188 
189 		MMU_DEBUG("expanding PTE 0x%016llx into table [%d]%p\n",
190 			  desc, table_index(table), table);
191 		__ASSERT(is_block_desc(desc), "");
192 
193 		if (level + 1 == XLAT_LAST_LEVEL) {
194 			desc |= PTE_PAGE_DESC;
195 		}
196 
197 		stride_shift = LEVEL_TO_VA_SIZE_SHIFT(level + 1);
198 		for (i = 0U; i < Ln_XLAT_NUM_ENTRIES; i++) {
199 			table[i] = desc | (i << stride_shift);
200 		}
201 		table_usage(table, Ln_XLAT_NUM_ENTRIES);
202 	} else {
203 		/*
204 		 * Adjust usage count for parent table's entry
205 		 * that will no longer be free.
206 		 */
207 		table_usage(pte, 1);
208 	}
209 
210 	/* Link the new table in place of the pte it replaces */
211 	set_pte_table_desc(pte, table, level);
212 	table_usage(table, 1);
213 
214 	return table;
215 }
216 
set_mapping(struct arm_mmu_ptables * ptables,uintptr_t virt,size_t size,uint64_t desc,bool may_overwrite)217 static int set_mapping(struct arm_mmu_ptables *ptables,
218 		       uintptr_t virt, size_t size,
219 		       uint64_t desc, bool may_overwrite)
220 {
221 	uint64_t *pte, *ptes[XLAT_LAST_LEVEL + 1];
222 	uint64_t level_size;
223 	uint64_t *table = ptables->base_xlat_table;
224 	unsigned int level = BASE_XLAT_LEVEL;
225 	int ret = 0;
226 
227 	while (size) {
228 		__ASSERT(level <= XLAT_LAST_LEVEL,
229 			 "max translation table level exceeded\n");
230 
231 		/* Locate PTE for given virtual address and page table level */
232 		pte = &table[XLAT_TABLE_VA_IDX(virt, level)];
233 		ptes[level] = pte;
234 
235 		if (is_table_desc(*pte, level)) {
236 			/* Move to the next translation table level */
237 			level++;
238 			table = pte_desc_table(*pte);
239 			continue;
240 		}
241 
242 		if (!may_overwrite && !is_free_desc(*pte)) {
243 			/* the entry is already allocated */
244 			LOG_ERR("entry already in use: "
245 				"level %d pte %p *pte 0x%016llx",
246 				level, pte, *pte);
247 			ret = -EBUSY;
248 			break;
249 		}
250 
251 		level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
252 
253 		if (is_desc_superset(*pte, desc, level)) {
254 			/* This block already covers our range */
255 			level_size -= (virt & (level_size - 1));
256 			if (level_size > size) {
257 				level_size = size;
258 			}
259 			goto move_on;
260 		}
261 
262 		if ((size < level_size) || (virt & (level_size - 1))) {
263 			/* Range doesn't fit, create subtable */
264 			table = expand_to_table(pte, level);
265 			if (!table) {
266 				ret = -ENOMEM;
267 				break;
268 			}
269 			level++;
270 			continue;
271 		}
272 
273 		/* Adjust usage count for corresponding table */
274 		if (is_free_desc(*pte)) {
275 			table_usage(pte, 1);
276 		}
277 		if (!desc) {
278 			table_usage(pte, -1);
279 		}
280 		/* Create (or erase) block/page descriptor */
281 		set_pte_block_desc(pte, desc, level);
282 
283 		/* recursively free unused tables if any */
284 		while (level != BASE_XLAT_LEVEL &&
285 		       is_table_unused(pte)) {
286 			free_table(pte);
287 			pte = ptes[--level];
288 			set_pte_block_desc(pte, 0, level);
289 			table_usage(pte, -1);
290 		}
291 
292 move_on:
293 		virt += level_size;
294 		desc += desc ? level_size : 0;
295 		size -= level_size;
296 
297 		/* Range is mapped, start again for next range */
298 		table = ptables->base_xlat_table;
299 		level = BASE_XLAT_LEVEL;
300 	}
301 
302 	return ret;
303 }
304 
305 #ifdef CONFIG_USERSPACE
306 
dup_table(uint64_t * src_table,unsigned int level)307 static uint64_t *dup_table(uint64_t *src_table, unsigned int level)
308 {
309 	uint64_t *dst_table = new_table();
310 	int i;
311 
312 	if (!dst_table) {
313 		return NULL;
314 	}
315 
316 	MMU_DEBUG("dup (level %d) [%d]%p to [%d]%p\n", level,
317 		  table_index(src_table), src_table,
318 		  table_index(dst_table), dst_table);
319 
320 	for (i = 0; i < Ln_XLAT_NUM_ENTRIES; i++) {
321 		dst_table[i] = src_table[i];
322 		if (is_table_desc(src_table[i], level)) {
323 			table_usage(pte_desc_table(src_table[i]), 1);
324 		}
325 		if (!is_free_desc(dst_table[i])) {
326 			table_usage(dst_table, 1);
327 		}
328 	}
329 
330 	return dst_table;
331 }
332 
privatize_table(uint64_t * dst_table,uint64_t * src_table,uintptr_t virt,size_t size,unsigned int level)333 static int privatize_table(uint64_t *dst_table, uint64_t *src_table,
334 			   uintptr_t virt, size_t size, unsigned int level)
335 {
336 	size_t step, level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
337 	unsigned int i;
338 	int ret;
339 
340 	for ( ; size; virt += step, size -= step) {
341 		step = level_size - (virt & (level_size - 1));
342 		if (step > size) {
343 			step = size;
344 		}
345 		i = XLAT_TABLE_VA_IDX(virt, level);
346 
347 		if (!is_table_desc(dst_table[i], level) ||
348 		    !is_table_desc(src_table[i], level)) {
349 			/* this entry is already private */
350 			continue;
351 		}
352 
353 		uint64_t *dst_subtable = pte_desc_table(dst_table[i]);
354 		uint64_t *src_subtable = pte_desc_table(src_table[i]);
355 
356 		if (dst_subtable == src_subtable) {
357 			/* need to make a private copy of this table */
358 			dst_subtable = dup_table(src_subtable, level + 1);
359 			if (!dst_subtable) {
360 				return -ENOMEM;
361 			}
362 			set_pte_table_desc(&dst_table[i], dst_subtable, level);
363 			table_usage(dst_subtable, 1);
364 			table_usage(src_subtable, -1);
365 		}
366 
367 		ret = privatize_table(dst_subtable, src_subtable,
368 				      virt, step, level + 1);
369 		if (ret) {
370 			return ret;
371 		}
372 	}
373 
374 	return 0;
375 }
376 
377 /*
378  * Make the given virtual address range private in dst_pt with regards to
379  * src_pt. By "private" this means that corresponding page tables in dst_pt
380  * will be duplicated so not to share the same table(s) with src_pt.
381  * If corresponding page tables in dst_pt are already distinct from src_pt
382  * then nothing is done. This allows for subsequent mapping changes in that
383  * range to affect only dst_pt.
384  */
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)385 static int privatize_page_range(struct arm_mmu_ptables *dst_pt,
386 				struct arm_mmu_ptables *src_pt,
387 				uintptr_t virt_start, size_t size,
388 				const char *name)
389 {
390 	k_spinlock_key_t key;
391 	int ret;
392 
393 	MMU_DEBUG("privatize [%s]: virt %lx size %lx\n",
394 		  name, virt_start, size);
395 
396 	key = k_spin_lock(&xlat_lock);
397 
398 	ret = privatize_table(dst_pt->base_xlat_table, src_pt->base_xlat_table,
399 			      virt_start, size, BASE_XLAT_LEVEL);
400 
401 	k_spin_unlock(&xlat_lock, key);
402 	return ret;
403 }
404 
discard_table(uint64_t * table,unsigned int level)405 static void discard_table(uint64_t *table, unsigned int level)
406 {
407 	unsigned int i;
408 
409 	for (i = 0U; Ln_XLAT_NUM_ENTRIES; i++) {
410 		if (is_table_desc(table[i], level)) {
411 			table_usage(pte_desc_table(table[i]), -1);
412 			discard_table(pte_desc_table(table[i]), level + 1);
413 		}
414 		if (!is_free_desc(table[i])) {
415 			table[i] = 0U;
416 			table_usage(table, -1);
417 		}
418 	}
419 	free_table(table);
420 }
421 
globalize_table(uint64_t * dst_table,uint64_t * src_table,uintptr_t virt,size_t size,unsigned int level)422 static int globalize_table(uint64_t *dst_table, uint64_t *src_table,
423 			   uintptr_t virt, size_t size, unsigned int level)
424 {
425 	size_t step, level_size = 1ULL << LEVEL_TO_VA_SIZE_SHIFT(level);
426 	unsigned int i;
427 	int ret;
428 
429 	for ( ; size; virt += step, size -= step) {
430 		step = level_size - (virt & (level_size - 1));
431 		if (step > size) {
432 			step = size;
433 		}
434 		i = XLAT_TABLE_VA_IDX(virt, level);
435 
436 		if (dst_table[i] == src_table[i]) {
437 			/* already identical to global table */
438 			continue;
439 		}
440 
441 		if (step != level_size) {
442 			/* boundary falls in the middle of this pte */
443 			__ASSERT(is_table_desc(src_table[i], level),
444 				 "can't have partial block pte here");
445 			if (!is_table_desc(dst_table[i], level)) {
446 				/* we need more fine grained boundaries */
447 				if (!expand_to_table(&dst_table[i], level)) {
448 					return -ENOMEM;
449 				}
450 			}
451 			ret = globalize_table(pte_desc_table(dst_table[i]),
452 					      pte_desc_table(src_table[i]),
453 					      virt, step, level + 1);
454 			if (ret) {
455 				return ret;
456 			}
457 			continue;
458 		}
459 
460 		/* we discard current pte and replace with global one */
461 
462 		uint64_t *old_table = is_table_desc(dst_table[i], level) ?
463 					pte_desc_table(dst_table[i]) : NULL;
464 
465 		dst_table[i] = src_table[i];
466 		debug_show_pte(&dst_table[i], level);
467 		if (is_table_desc(src_table[i], level)) {
468 			table_usage(pte_desc_table(src_table[i]), 1);
469 		}
470 
471 		if (old_table) {
472 			/* we can discard the whole branch */
473 			table_usage(old_table, -1);
474 			discard_table(old_table, level + 1);
475 		}
476 	}
477 
478 	return 0;
479 }
480 
481 /*
482  * Globalize the given virtual address range in dst_pt from src_pt. We make
483  * it global by sharing as much page table content from src_pt as possible,
484  * including page tables themselves, and corresponding private tables in
485  * dst_pt are then discarded. If page tables in the given range are already
486  * shared then nothing is done. If page table sharing is not possible then
487  * page table entries in dst_pt are synchronized with those from src_pt.
488  */
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)489 static int globalize_page_range(struct arm_mmu_ptables *dst_pt,
490 				struct arm_mmu_ptables *src_pt,
491 				uintptr_t virt_start, size_t size,
492 				const char *name)
493 {
494 	k_spinlock_key_t key;
495 	int ret;
496 
497 	MMU_DEBUG("globalize [%s]: virt %lx size %lx\n",
498 		  name, virt_start, size);
499 
500 	key = k_spin_lock(&xlat_lock);
501 
502 	ret = globalize_table(dst_pt->base_xlat_table, src_pt->base_xlat_table,
503 			      virt_start, size, BASE_XLAT_LEVEL);
504 
505 	k_spin_unlock(&xlat_lock, key);
506 	return ret;
507 }
508 
509 #endif /* CONFIG_USERSPACE */
510 
get_region_desc(uint32_t attrs)511 static uint64_t get_region_desc(uint32_t attrs)
512 {
513 	unsigned int mem_type;
514 	uint64_t desc = 0U;
515 
516 	/* NS bit for security memory access from secure state */
517 	desc |= (attrs & MT_NS) ? PTE_BLOCK_DESC_NS : 0;
518 
519 	/*
520 	 * AP bits for EL0 / ELh Data access permission
521 	 *
522 	 *   AP[2:1]   ELh  EL0
523 	 * +--------------------+
524 	 *     00      RW   NA
525 	 *     01      RW   RW
526 	 *     10      RO   NA
527 	 *     11      RO   RO
528 	 */
529 
530 	/* AP bits for Data access permission */
531 	desc |= (attrs & MT_RW) ? PTE_BLOCK_DESC_AP_RW : PTE_BLOCK_DESC_AP_RO;
532 
533 	/* Mirror permissions to EL0 */
534 	desc |= (attrs & MT_RW_AP_ELx) ?
535 		 PTE_BLOCK_DESC_AP_ELx : PTE_BLOCK_DESC_AP_EL_HIGHER;
536 
537 	/* the access flag */
538 	desc |= PTE_BLOCK_DESC_AF;
539 
540 	/* memory attribute index field */
541 	mem_type = MT_TYPE(attrs);
542 	desc |= PTE_BLOCK_DESC_MEMTYPE(mem_type);
543 
544 	switch (mem_type) {
545 	case MT_DEVICE_nGnRnE:
546 	case MT_DEVICE_nGnRE:
547 	case MT_DEVICE_GRE:
548 		/* Access to Device memory and non-cacheable memory are coherent
549 		 * for all observers in the system and are treated as
550 		 * Outer shareable, so, for these 2 types of memory,
551 		 * it is not strictly needed to set shareability field
552 		 */
553 		desc |= PTE_BLOCK_DESC_OUTER_SHARE;
554 		/* Map device memory as execute-never */
555 		desc |= PTE_BLOCK_DESC_PXN;
556 		desc |= PTE_BLOCK_DESC_UXN;
557 		break;
558 	case MT_NORMAL_NC:
559 	case MT_NORMAL:
560 		/* Make Normal RW memory as execute never */
561 		if ((attrs & MT_RW) || (attrs & MT_P_EXECUTE_NEVER))
562 			desc |= PTE_BLOCK_DESC_PXN;
563 
564 		if (((attrs & MT_RW) && (attrs & MT_RW_AP_ELx)) ||
565 		     (attrs & MT_U_EXECUTE_NEVER))
566 			desc |= PTE_BLOCK_DESC_UXN;
567 
568 		if (mem_type == MT_NORMAL)
569 			desc |= PTE_BLOCK_DESC_INNER_SHARE;
570 		else
571 			desc |= PTE_BLOCK_DESC_OUTER_SHARE;
572 	}
573 
574 	return desc;
575 }
576 
__add_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)577 static int __add_map(struct arm_mmu_ptables *ptables, const char *name,
578 		     uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
579 {
580 	uint64_t desc = get_region_desc(attrs);
581 	bool may_overwrite = !(attrs & MT_NO_OVERWRITE);
582 
583 	MMU_DEBUG("mmap [%s]: virt %lx phys %lx size %lx attr %llx\n",
584 		  name, virt, phys, size, desc);
585 	__ASSERT(((virt | phys | size) & (CONFIG_MMU_PAGE_SIZE - 1)) == 0,
586 		 "address/size are not page aligned\n");
587 	desc |= phys;
588 	return set_mapping(ptables, virt, size, desc, may_overwrite);
589 }
590 
add_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)591 static int add_map(struct arm_mmu_ptables *ptables, const char *name,
592 		   uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
593 {
594 	k_spinlock_key_t key;
595 	int ret;
596 
597 	key = k_spin_lock(&xlat_lock);
598 	ret = __add_map(ptables, name, phys, virt, size, attrs);
599 	k_spin_unlock(&xlat_lock, key);
600 	return ret;
601 }
602 
remove_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t virt,size_t size)603 static int remove_map(struct arm_mmu_ptables *ptables, const char *name,
604 		      uintptr_t virt, size_t size)
605 {
606 	k_spinlock_key_t key;
607 	int ret;
608 
609 	MMU_DEBUG("unmmap [%s]: virt %lx size %lx\n", name, virt, size);
610 	__ASSERT(((virt | size) & (CONFIG_MMU_PAGE_SIZE - 1)) == 0,
611 		 "address/size are not page aligned\n");
612 
613 	key = k_spin_lock(&xlat_lock);
614 	ret = set_mapping(ptables, virt, size, 0, true);
615 	k_spin_unlock(&xlat_lock, key);
616 	return ret;
617 }
618 
invalidate_tlb_all(void)619 static void invalidate_tlb_all(void)
620 {
621 	__asm__ volatile (
622 	"tlbi vmalle1; dsb sy; isb"
623 	: : : "memory");
624 }
625 
626 /* zephyr execution regions with appropriate attributes */
627 
628 struct arm_mmu_flat_range {
629 	char *name;
630 	void *start;
631 	void *end;
632 	uint32_t attrs;
633 };
634 
635 static const struct arm_mmu_flat_range mmu_zephyr_ranges[] = {
636 
637 	/* Mark the zephyr execution regions (data, bss, noinit, etc.)
638 	 * cacheable, read-write
639 	 * Note: read-write region is marked execute-never internally
640 	 */
641 	{ .name  = "zephyr_data",
642 	  .start = _image_ram_start,
643 	  .end   = _image_ram_end,
644 	  .attrs = MT_NORMAL | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE },
645 
646 	/* Mark text segment cacheable,read only and executable */
647 	{ .name  = "zephyr_code",
648 	  .start = __text_region_start,
649 	  .end   = __text_region_end,
650 	  .attrs = MT_NORMAL | MT_P_RX_U_RX | MT_DEFAULT_SECURE_STATE },
651 
652 	/* Mark rodata segment cacheable, read only and execute-never */
653 	{ .name  = "zephyr_rodata",
654 	  .start = __rodata_region_start,
655 	  .end   = __rodata_region_end,
656 	  .attrs = MT_NORMAL | MT_P_RO_U_RO | MT_DEFAULT_SECURE_STATE },
657 };
658 
add_arm_mmu_flat_range(struct arm_mmu_ptables * ptables,const struct arm_mmu_flat_range * range,uint32_t extra_flags)659 static inline void add_arm_mmu_flat_range(struct arm_mmu_ptables *ptables,
660 					  const struct arm_mmu_flat_range *range,
661 					  uint32_t extra_flags)
662 {
663 	uintptr_t address = (uintptr_t)range->start;
664 	size_t size = (uintptr_t)range->end - address;
665 
666 	if (size) {
667 		/* MMU not yet active: must use unlocked version */
668 		__add_map(ptables, range->name, address, address,
669 			  size, range->attrs | extra_flags);
670 	}
671 }
672 
add_arm_mmu_region(struct arm_mmu_ptables * ptables,const struct arm_mmu_region * region,uint32_t extra_flags)673 static inline void add_arm_mmu_region(struct arm_mmu_ptables *ptables,
674 				      const struct arm_mmu_region *region,
675 				      uint32_t extra_flags)
676 {
677 	if (region->size || region->attrs) {
678 		/* MMU not yet active: must use unlocked version */
679 		__add_map(ptables, region->name, region->base_pa, region->base_va,
680 			  region->size, region->attrs | extra_flags);
681 	}
682 }
683 
setup_page_tables(struct arm_mmu_ptables * ptables)684 static void setup_page_tables(struct arm_mmu_ptables *ptables)
685 {
686 	unsigned int index;
687 	const struct arm_mmu_flat_range *range;
688 	const struct arm_mmu_region *region;
689 	uintptr_t max_va = 0, max_pa = 0;
690 
691 	MMU_DEBUG("xlat tables:\n");
692 	for (index = 0U; index < CONFIG_MAX_XLAT_TABLES; index++)
693 		MMU_DEBUG("%d: %p\n", index, xlat_tables + index * Ln_XLAT_NUM_ENTRIES);
694 
695 	for (index = 0U; index < mmu_config.num_regions; index++) {
696 		region = &mmu_config.mmu_regions[index];
697 		max_va = MAX(max_va, region->base_va + region->size);
698 		max_pa = MAX(max_pa, region->base_pa + region->size);
699 	}
700 
701 	__ASSERT(max_va <= (1ULL << CONFIG_ARM64_VA_BITS),
702 		 "Maximum VA not supported\n");
703 	__ASSERT(max_pa <= (1ULL << CONFIG_ARM64_PA_BITS),
704 		 "Maximum PA not supported\n");
705 
706 	/* setup translation table for zephyr execution regions */
707 	for (index = 0U; index < ARRAY_SIZE(mmu_zephyr_ranges); index++) {
708 		range = &mmu_zephyr_ranges[index];
709 		add_arm_mmu_flat_range(ptables, range, 0);
710 	}
711 
712 	/*
713 	 * Create translation tables for user provided platform regions.
714 	 * Those must not conflict with our default mapping.
715 	 */
716 	for (index = 0U; index < mmu_config.num_regions; index++) {
717 		region = &mmu_config.mmu_regions[index];
718 		add_arm_mmu_region(ptables, region, MT_NO_OVERWRITE);
719 	}
720 
721 	invalidate_tlb_all();
722 }
723 
724 /* Translation table control register settings */
get_tcr(int el)725 static uint64_t get_tcr(int el)
726 {
727 	uint64_t tcr;
728 	uint64_t va_bits = CONFIG_ARM64_VA_BITS;
729 	uint64_t tcr_ps_bits;
730 
731 	tcr_ps_bits = TCR_PS_BITS;
732 
733 	if (el == 1) {
734 		tcr = (tcr_ps_bits << TCR_EL1_IPS_SHIFT);
735 		/*
736 		 * TCR_EL1.EPD1: Disable translation table walk for addresses
737 		 * that are translated using TTBR1_EL1.
738 		 */
739 		tcr |= TCR_EPD1_DISABLE;
740 	} else
741 		tcr = (tcr_ps_bits << TCR_EL3_PS_SHIFT);
742 
743 	tcr |= TCR_T0SZ(va_bits);
744 	/*
745 	 * Translation table walk is cacheable, inner/outer WBWA and
746 	 * inner shareable
747 	 */
748 	tcr |= TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA;
749 
750 	return tcr;
751 }
752 
enable_mmu_el1(struct arm_mmu_ptables * ptables,unsigned int flags)753 static void enable_mmu_el1(struct arm_mmu_ptables *ptables, unsigned int flags)
754 {
755 	ARG_UNUSED(flags);
756 	uint64_t val;
757 
758 	/* Set MAIR, TCR and TBBR registers */
759 	write_mair_el1(MEMORY_ATTRIBUTES);
760 	write_tcr_el1(get_tcr(1));
761 	write_ttbr0_el1((uint64_t)ptables->base_xlat_table);
762 
763 	/* Ensure these changes are seen before MMU is enabled */
764 	isb();
765 
766 	/* Invalidate all data caches before enable them */
767 	sys_cache_data_all(K_CACHE_INVD);
768 
769 	/* Enable the MMU and data cache */
770 	val = read_sctlr_el1();
771 	write_sctlr_el1(val | SCTLR_M_BIT | SCTLR_C_BIT);
772 
773 	/* Ensure the MMU enable takes effect immediately */
774 	isb();
775 
776 	MMU_DEBUG("MMU enabled with dcache\n");
777 }
778 
779 /* ARM MMU Driver Initial Setup */
780 
781 static struct arm_mmu_ptables kernel_ptables;
782 #ifdef CONFIG_USERSPACE
783 static sys_slist_t domain_list;
784 #endif
785 
786 /*
787  * @brief MMU default configuration
788  *
789  * This function provides the default configuration mechanism for the Memory
790  * Management Unit (MMU).
791  */
z_arm64_mm_init(bool is_primary_core)792 void z_arm64_mm_init(bool is_primary_core)
793 {
794 	unsigned int flags = 0U;
795 
796 	__ASSERT(CONFIG_MMU_PAGE_SIZE == KB(4),
797 		 "Only 4K page size is supported\n");
798 
799 	__ASSERT(GET_EL(read_currentel()) == MODE_EL1,
800 		 "Exception level not EL1, MMU not enabled!\n");
801 
802 	/* Ensure that MMU is already not enabled */
803 	__ASSERT((read_sctlr_el1() & SCTLR_M_BIT) == 0, "MMU is already enabled\n");
804 
805 	/*
806 	 * Only booting core setup up the page tables.
807 	 */
808 	if (is_primary_core) {
809 		kernel_ptables.base_xlat_table = new_table();
810 		setup_page_tables(&kernel_ptables);
811 	}
812 
813 	/* currently only EL1 is supported */
814 	enable_mmu_el1(&kernel_ptables, flags);
815 }
816 
sync_domains(uintptr_t virt,size_t size)817 static void sync_domains(uintptr_t virt, size_t size)
818 {
819 #ifdef CONFIG_USERSPACE
820 	sys_snode_t *node;
821 	struct arch_mem_domain *domain;
822 	struct arm_mmu_ptables *domain_ptables;
823 	k_spinlock_key_t key;
824 	int ret;
825 
826 	key = k_spin_lock(&z_mem_domain_lock);
827 	SYS_SLIST_FOR_EACH_NODE(&domain_list, node) {
828 		domain = CONTAINER_OF(node, struct arch_mem_domain, node);
829 		domain_ptables = &domain->ptables;
830 		ret = globalize_page_range(domain_ptables, &kernel_ptables,
831 					   virt, size, "generic");
832 		if (ret) {
833 			LOG_ERR("globalize_page_range() returned %d", ret);
834 		}
835 	}
836 	k_spin_unlock(&z_mem_domain_lock, key);
837 #endif
838 }
839 
__arch_mem_map(void * virt,uintptr_t phys,size_t size,uint32_t flags)840 static int __arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
841 {
842 	struct arm_mmu_ptables *ptables;
843 	uint32_t entry_flags = MT_SECURE | MT_P_RX_U_NA;
844 
845 	/* Always map in the kernel page tables */
846 	ptables = &kernel_ptables;
847 
848 	/* Translate flags argument into HW-recognized entry flags. */
849 	switch (flags & K_MEM_CACHE_MASK) {
850 	/*
851 	 * K_MEM_CACHE_NONE => MT_DEVICE_nGnRnE
852 	 *			(Device memory nGnRnE)
853 	 * K_MEM_CACHE_WB   => MT_NORMAL
854 	 *			(Normal memory Outer WB + Inner WB)
855 	 * K_MEM_CACHE_WT   => MT_NORMAL_WT
856 	 *			(Normal memory Outer WT + Inner WT)
857 	 */
858 	case K_MEM_CACHE_NONE:
859 		entry_flags |= MT_DEVICE_nGnRnE;
860 		break;
861 	case K_MEM_CACHE_WT:
862 		entry_flags |= MT_NORMAL_WT;
863 		break;
864 	case K_MEM_CACHE_WB:
865 		entry_flags |= MT_NORMAL;
866 		break;
867 	default:
868 		return -ENOTSUP;
869 	}
870 
871 	if ((flags & K_MEM_PERM_RW) != 0U) {
872 		entry_flags |= MT_RW;
873 	}
874 
875 	if ((flags & K_MEM_PERM_EXEC) == 0U) {
876 		entry_flags |= MT_P_EXECUTE_NEVER;
877 	}
878 
879 	if ((flags & K_MEM_PERM_USER) != 0U) {
880 		return -ENOTSUP;
881 	}
882 
883 	return add_map(ptables, "generic", phys, (uintptr_t)virt, size, entry_flags);
884 }
885 
arch_mem_map(void * virt,uintptr_t phys,size_t size,uint32_t flags)886 void arch_mem_map(void *virt, uintptr_t phys, size_t size, uint32_t flags)
887 {
888 	int ret = __arch_mem_map(virt, phys, size, flags);
889 
890 	if (ret) {
891 		LOG_ERR("__arch_mem_map() returned %d", ret);
892 		k_panic();
893 	} else {
894 		sync_domains((uintptr_t)virt, size);
895 		invalidate_tlb_all();
896 	}
897 }
898 
arch_mem_unmap(void * addr,size_t size)899 void arch_mem_unmap(void *addr, size_t size)
900 {
901 	int ret = remove_map(&kernel_ptables, "generic", (uintptr_t)addr, size);
902 
903 	if (ret) {
904 		LOG_ERR("remove_map() returned %d", ret);
905 	} else {
906 		sync_domains((uintptr_t)addr, size);
907 		invalidate_tlb_all();
908 	}
909 }
910 
arch_page_phys_get(void * virt,uintptr_t * phys)911 int arch_page_phys_get(void *virt, uintptr_t *phys)
912 {
913 	uint64_t par;
914 	int key;
915 
916 	key = arch_irq_lock();
917 	__asm__ volatile ("at S1E1R, %0" : : "r" (virt));
918 	isb();
919 	par = read_sysreg(PAR_EL1);
920 	arch_irq_unlock(key);
921 
922 	if (par & BIT(0)) {
923 		return -EFAULT;
924 	}
925 
926 	if (phys) {
927 		*phys = par & GENMASK(47, 12);
928 	}
929 	return 0;
930 }
931 
932 #ifdef CONFIG_USERSPACE
933 
is_ptable_active(struct arm_mmu_ptables * ptables)934 static inline bool is_ptable_active(struct arm_mmu_ptables *ptables)
935 {
936 	return read_sysreg(ttbr0_el1) == (uintptr_t)ptables->base_xlat_table;
937 }
938 
arch_mem_domain_max_partitions_get(void)939 int arch_mem_domain_max_partitions_get(void)
940 {
941 	return CONFIG_MAX_DOMAIN_PARTITIONS;
942 }
943 
arch_mem_domain_init(struct k_mem_domain * domain)944 int arch_mem_domain_init(struct k_mem_domain *domain)
945 {
946 	struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
947 	k_spinlock_key_t key;
948 
949 	MMU_DEBUG("%s\n", __func__);
950 
951 	key = k_spin_lock(&xlat_lock);
952 	domain_ptables->base_xlat_table =
953 		dup_table(kernel_ptables.base_xlat_table, BASE_XLAT_LEVEL);
954 	k_spin_unlock(&xlat_lock, key);
955 	if (!domain_ptables->base_xlat_table) {
956 		return -ENOMEM;
957 	}
958 	sys_slist_append(&domain_list, &domain->arch.node);
959 	return 0;
960 }
961 
private_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t phys,uintptr_t virt,size_t size,uint32_t attrs)962 static void private_map(struct arm_mmu_ptables *ptables, const char *name,
963 			uintptr_t phys, uintptr_t virt, size_t size, uint32_t attrs)
964 {
965 	int ret;
966 
967 	ret = privatize_page_range(ptables, &kernel_ptables, virt, size, name);
968 	__ASSERT(ret == 0, "privatize_page_range() returned %d", ret);
969 	ret = add_map(ptables, name, phys, virt, size, attrs);
970 	__ASSERT(ret == 0, "add_map() returned %d", ret);
971 	if (is_ptable_active(ptables)) {
972 		invalidate_tlb_all();
973 	}
974 }
975 
reset_map(struct arm_mmu_ptables * ptables,const char * name,uintptr_t addr,size_t size)976 static void reset_map(struct arm_mmu_ptables *ptables, const char *name,
977 		      uintptr_t addr, size_t size)
978 {
979 	int ret;
980 
981 	ret = globalize_page_range(ptables, &kernel_ptables, addr, size, name);
982 	__ASSERT(ret == 0, "globalize_page_range() returned %d", ret);
983 	if (is_ptable_active(ptables)) {
984 		invalidate_tlb_all();
985 	}
986 }
987 
arch_mem_domain_partition_add(struct k_mem_domain * domain,uint32_t partition_id)988 void arch_mem_domain_partition_add(struct k_mem_domain *domain,
989 				   uint32_t partition_id)
990 {
991 	struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
992 	struct k_mem_partition *ptn = &domain->partitions[partition_id];
993 
994 	private_map(domain_ptables, "partition", ptn->start, ptn->start,
995 		    ptn->size, ptn->attr.attrs | MT_NORMAL);
996 }
997 
arch_mem_domain_partition_remove(struct k_mem_domain * domain,uint32_t partition_id)998 void arch_mem_domain_partition_remove(struct k_mem_domain *domain,
999 				      uint32_t partition_id)
1000 {
1001 	struct arm_mmu_ptables *domain_ptables = &domain->arch.ptables;
1002 	struct k_mem_partition *ptn = &domain->partitions[partition_id];
1003 
1004 	reset_map(domain_ptables, "partition removal", ptn->start, ptn->size);
1005 }
1006 
map_thread_stack(struct k_thread * thread,struct arm_mmu_ptables * ptables)1007 static void map_thread_stack(struct k_thread *thread,
1008 			     struct arm_mmu_ptables *ptables)
1009 {
1010 	private_map(ptables, "thread_stack", thread->stack_info.start,
1011 		    thread->stack_info.start, thread->stack_info.size,
1012 		    MT_P_RW_U_RW | MT_NORMAL);
1013 }
1014 
arch_mem_domain_thread_add(struct k_thread * thread)1015 void arch_mem_domain_thread_add(struct k_thread *thread)
1016 {
1017 	struct arm_mmu_ptables *old_ptables, *domain_ptables;
1018 	struct k_mem_domain *domain;
1019 	bool is_user, is_migration;
1020 
1021 	domain = thread->mem_domain_info.mem_domain;
1022 	domain_ptables = &domain->arch.ptables;
1023 	old_ptables = thread->arch.ptables;
1024 
1025 	is_user = (thread->base.user_options & K_USER) != 0;
1026 	is_migration = (old_ptables != NULL) && is_user;
1027 
1028 	if (is_migration) {
1029 		map_thread_stack(thread, domain_ptables);
1030 	}
1031 
1032 	thread->arch.ptables = domain_ptables;
1033 	if (thread == _current) {
1034 		if (!is_ptable_active(domain_ptables)) {
1035 			z_arm64_swap_ptables(thread);
1036 		}
1037 	} else {
1038 #ifdef CONFIG_SMP
1039 		/* the thread could be running on another CPU right now */
1040 		z_arm64_ptable_ipi();
1041 #endif
1042 	}
1043 
1044 	if (is_migration) {
1045 		reset_map(old_ptables, __func__, thread->stack_info.start,
1046 				thread->stack_info.size);
1047 	}
1048 }
1049 
arch_mem_domain_thread_remove(struct k_thread * thread)1050 void arch_mem_domain_thread_remove(struct k_thread *thread)
1051 {
1052 	struct arm_mmu_ptables *domain_ptables;
1053 	struct k_mem_domain *domain;
1054 
1055 	domain = thread->mem_domain_info.mem_domain;
1056 	domain_ptables = &domain->arch.ptables;
1057 
1058 	if ((thread->base.user_options & K_USER) == 0) {
1059 		return;
1060 	}
1061 
1062 	if ((thread->base.thread_state & _THREAD_DEAD) == 0) {
1063 		return;
1064 	}
1065 
1066 	reset_map(domain_ptables, __func__, thread->stack_info.start,
1067 		  thread->stack_info.size);
1068 }
1069 
z_arm64_swap_ptables(struct k_thread * incoming)1070 void z_arm64_swap_ptables(struct k_thread *incoming)
1071 {
1072 	struct arm_mmu_ptables *ptables = incoming->arch.ptables;
1073 
1074 	if (!is_ptable_active(ptables)) {
1075 		z_arm64_set_ttbr0((uintptr_t)ptables->base_xlat_table);
1076 	}
1077 }
1078 
z_arm64_thread_pt_init(struct k_thread * incoming)1079 void z_arm64_thread_pt_init(struct k_thread *incoming)
1080 {
1081 	struct arm_mmu_ptables *ptables;
1082 
1083 	if ((incoming->base.user_options & K_USER) == 0)
1084 		return;
1085 
1086 	ptables = incoming->arch.ptables;
1087 
1088 	/* Map the thread stack */
1089 	map_thread_stack(incoming, ptables);
1090 
1091 	z_arm64_swap_ptables(incoming);
1092 }
1093 
1094 #endif /* CONFIG_USERSPACE */
1095