blob: 8075d02cfc91b41e1ddcd6c36118c9e0c21f3b47 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Andrew Scull18834872018-10-12 11:48:09 +01007 */
8
Andrew Scull18c78fc2018-08-20 12:57:41 +01009#include "hf/mm.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010010
11#include <stdatomic.h>
12#include <stdint.h>
13
Daniel Boulbya2f8c662021-11-26 17:52:53 +000014#include "hf/assert.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010015#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010016#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010017#include "hf/layout.h"
Andrew Walbran48699362019-05-20 14:38:00 +010018#include "hf/plat/console.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010019#include "hf/static_assert.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010020
Andrew Walbran2400ed22018-09-27 14:45:58 +010021/**
22 * This file has functions for managing the level 1 and 2 page tables used by
23 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
24 * and then a level 2 mapping per VM. The design assumes that all page tables
25 * contain only 1-1 mappings, aligned on the block boundaries.
26 */
27
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010028/*
29 * For stage 2, the input is an intermediate physical addresses rather than a
30 * virtual address so:
31 */
Andrew Scull80871322018-08-06 12:04:09 +010032static_assert(
33 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
34 "Currently, the same code manages the stage 1 and stage 2 page tables "
35 "which only works if the virtual and intermediate physical addresses "
36 "are the same size. It looks like that assumption might not be holding "
37 "so we need to check that everything is going to be ok.");
38
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010039static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010040static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010041
Andrew Scullda241972019-01-05 18:17:48 +000042static bool mm_stage2_invalidate = false;
43
44/**
45 * After calling this function, modifications to stage-2 page tables will use
46 * break-before-make and invalidate the TLB for the affected range.
47 */
48void mm_vm_enable_invalidation(void)
49{
50 mm_stage2_invalidate = true;
51}
52
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010053/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010054 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010055 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010056static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010057{
58 return ptr_from_va(va_from_pa(pa));
59}
60
61/**
Andrew Scull80871322018-08-06 12:04:09 +010062 * Rounds an address down to a page boundary.
63 */
64static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
65{
66 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
67}
68
69/**
70 * Rounds an address up to a page boundary.
71 */
72static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
73{
74 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
75}
76
77/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010078 * Calculates the size of the address space represented by a page table entry at
79 * the given level.
80 */
Andrew Sculle9827712018-10-19 14:54:20 +010081static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010082{
Andrew Scull78d6fd92018-09-06 15:08:36 +010083 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010084}
85
86/**
Andrew Scullcae45572018-12-13 15:46:30 +000087 * Gets the address of the start of the next block of the given size. The size
88 * must be a power of two.
89 */
90static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
91 size_t block_size)
92{
93 return (addr + block_size) & ~(block_size - 1);
94}
95
96/**
97 * Gets the physical address of the start of the next block of the given size.
98 * The size must be a power of two.
99 */
100static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
101{
102 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
103}
104
105/**
Andrew Scull80871322018-08-06 12:04:09 +0100106 * For a given address, calculates the maximum (plus one) address that can be
107 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100108 */
Andrew Sculle9827712018-10-19 14:54:20 +0100109static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100110{
111 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000112
Andrew Scull80871322018-08-06 12:04:09 +0100113 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100114}
115
116/**
Andrew Scull80871322018-08-06 12:04:09 +0100117 * For a given address, calculates the index at which its entry is stored in a
118 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100119 */
Andrew Sculle9827712018-10-19 14:54:20 +0100120static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100121{
Andrew Scull80871322018-08-06 12:04:09 +0100122 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000123
Andrew Scull78d6fd92018-09-06 15:08:36 +0100124 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100125}
126
127/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000128 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100129 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000130static struct mm_page_table *mm_alloc_page_tables(size_t count,
131 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100132{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000133 if (count == 1) {
134 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100135 }
136
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000137 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100138}
139
140/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000141 * Returns the maximum level in the page table given the flags.
142 */
143static uint8_t mm_max_level(int flags)
144{
145 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
146 : arch_mm_stage2_max_level();
147}
148
149/**
150 * Returns the number of root-level tables given the flags.
151 */
152static uint8_t mm_root_table_count(int flags)
153{
154 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
155 : arch_mm_stage2_root_table_count();
156}
157
158/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000159 * Invalidates the TLB for the given address range.
160 */
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800161static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags,
162 uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000163{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000164 if (flags & MM_FLAG_STAGE1) {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800165 arch_mm_invalidate_stage1_range(id, va_init(begin),
166 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000167 } else {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800168 arch_mm_invalidate_stage2_range(id, ipa_init(begin),
169 ipa_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000170 }
171}
172
173/**
174 * Frees all page-table-related memory associated with the given pte at the
175 * given level, including any subtables recursively.
176 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100177// NOLINTNEXTLINE(misc-no-recursion)
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000178static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000179{
180 struct mm_page_table *table;
181 uint64_t i;
182
183 if (!arch_mm_pte_is_table(pte, level)) {
184 return;
185 }
186
187 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000188 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000189 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000190 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000191 }
192
193 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000194 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000195}
196
197/**
David Brazdil711fbe92019-08-06 13:39:58 +0100198 * Returns the first address which cannot be encoded in page tables given by
199 * `flags`. It is the exclusive end of the address space created by the tables.
200 */
201ptable_addr_t mm_ptable_addr_space_end(int flags)
202{
203 return mm_root_table_count(flags) *
204 mm_entry_size(mm_max_level(flags) + 1);
205}
206
207/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000208 * Initialises the given page table.
209 */
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800210bool mm_ptable_init(struct mm_ptable *t, uint16_t id, int flags,
211 struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000212{
213 uint8_t i;
214 size_t j;
215 struct mm_page_table *tables;
216 uint8_t root_table_count = mm_root_table_count(flags);
217
218 tables = mm_alloc_page_tables(root_table_count, ppool);
219 if (tables == NULL) {
220 return false;
221 }
222
223 for (i = 0; i < root_table_count; i++) {
224 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
225 tables[i].entries[j] =
226 arch_mm_absent_pte(mm_max_level(flags));
227 }
228 }
229
230 /*
231 * TODO: halloc could return a virtual or physical address if mm not
232 * enabled?
233 */
234 t->root = pa_init((uintpaddr_t)tables);
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800235 t->id = id;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000236 return true;
237}
238
239/**
240 * Frees all memory associated with the give page table.
241 */
242static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
243{
244 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
245 uint8_t level = mm_max_level(flags);
246 uint8_t root_table_count = mm_root_table_count(flags);
247 uint8_t i;
248 uint64_t j;
249
250 for (i = 0; i < root_table_count; ++i) {
251 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
252 mm_free_page_pte(tables[i].entries[j], level, ppool);
253 }
254 }
255
256 mpool_add_chunk(ppool, tables,
257 sizeof(struct mm_page_table) * root_table_count);
258}
259
260/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000261 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000262 * are valid, it performs a break-before-make sequence where it first writes an
263 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
264 * This is to prevent cases where CPUs have different 'valid' values in their
265 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000266 */
267static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800268 uint8_t level, int flags, struct mpool *ppool,
269 uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270{
271 pte_t v = *pte;
272
273 /*
274 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000275 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000276 */
Andrew Scullda241972019-01-05 18:17:48 +0000277 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800278 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000279 *pte = arch_mm_absent_pte(level);
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800280 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags,
281 id);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000282 }
283
284 /* Assign the new pte. */
285 *pte = new_pte;
286
287 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000288 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000289}
290
291/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100292 * Populates the provided page table entry with a reference to another table if
293 * needed, that is, if it does not yet point to another table.
294 *
295 * Returns a pointer to the table the entry now points to.
296 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000297static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
298 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000299 int flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800300 struct mpool *ppool,
301 uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100303 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100304 pte_t v = *pte;
305 pte_t new_pte;
306 size_t i;
307 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100308 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100309
310 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100311 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000312 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100313 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100314
315 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000316 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100317 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000318 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100319 return NULL;
320 }
321
322 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100323 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100324 inc = mm_entry_size(level_below);
325 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000326 arch_mm_block_from_pte(v, level),
327 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100328 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100329 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100330 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100331 }
332
333 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100334 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
335 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100336 new_pte += inc;
337 }
338
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000339 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100340 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000341
342 /* Replace the pte entry, doing a break-before-make if needed. */
343 mm_replace_entry(begin, pte,
344 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800345 level, flags, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100346
347 return ntable;
348}
349
350/**
Andrew Scull80871322018-08-06 12:04:09 +0100351 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100352 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000353 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100354 *
355 * This function calls itself recursively if it needs to update additional
356 * levels, but the recursion is bound by the maximum number of levels in a page
357 * table.
358 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100359// NOLINTNEXTLINE(misc-no-recursion)
Andrew Scull80871322018-08-06 12:04:09 +0100360static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100361 uint64_t attrs, struct mm_page_table *table,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800362 uint8_t level, int flags, struct mpool *ppool,
363 uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100364{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100365 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100366 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100367 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000368 bool commit = flags & MM_FLAG_COMMIT;
369 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100370
Andrew Scull265ada92018-07-30 15:19:01 +0100371 /* Cap end so that we don't go over the current level max. */
372 if (end > level_end) {
373 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100374 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100375
376 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100377 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100378 if (unmap ? !arch_mm_pte_is_present(*pte, level)
379 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000380 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100381 /*
382 * If the entry is already mapped with the right
383 * attributes, or already absent in the case of
384 * unmapping, no need to do anything; carry on to the
385 * next entry.
386 */
387 } else if ((end - begin) >= entry_size &&
388 (unmap || arch_mm_is_block_allowed(level)) &&
389 (begin & (entry_size - 1)) == 0) {
390 /*
391 * If the entire entry is within the region we want to
392 * map, map/unmap the whole entry.
393 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100394 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000395 pte_t new_pte =
396 unmap ? arch_mm_absent_pte(level)
397 : arch_mm_block_pte(level, pa,
398 attrs);
399 mm_replace_entry(begin, pte, new_pte, level,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800400 flags, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100401 }
402 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100403 /*
404 * If the entry is already a subtable get it; otherwise
405 * replace it with an equivalent subtable and get that.
406 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000407 struct mm_page_table *nt = mm_populate_table_pte(
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800408 begin, pte, level, flags, ppool, id);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100409 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100410 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100411 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100412
Andrew Walbran6324fc92018-10-03 11:46:43 +0100413 /*
414 * Recurse to map/unmap the appropriate entries within
415 * the subtable.
416 */
Andrew Scull80871322018-08-06 12:04:09 +0100417 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800418 flags, ppool, id)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100419 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100420 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100421 }
422
Andrew Scullcae45572018-12-13 15:46:30 +0000423 begin = mm_start_of_next_block(begin, entry_size);
424 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100425 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100426 }
427
428 return true;
429}
430
431/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000432 * Updates the page table from the root to map the given address range to a
433 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000434 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000435 */
436static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
437 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000438 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000439{
440 size_t root_table_size = mm_entry_size(root_level);
441 struct mm_page_table *table =
442 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
443
444 while (begin < end) {
445 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800446 root_level - 1, flags, ppool, t->id)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000447 return false;
448 }
Andrew Scullcae45572018-12-13 15:46:30 +0000449 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000450 table++;
451 }
452
453 return true;
454}
455
456/**
Andrew Scull80871322018-08-06 12:04:09 +0100457 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000458 * or not mapped into the address space with the architecture-agnostic mode
Andrew Walbran58a6e542019-11-19 14:23:15 +0000459 * provided. Only commits the change if MM_FLAG_COMMIT is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100460 */
Andrew Walbran58a6e542019-11-19 14:23:15 +0000461static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
462 paddr_t pa_end, uint64_t attrs, int flags,
463 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100464{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000465 uint8_t root_level = mm_max_level(flags) + 1;
David Brazdil711fbe92019-08-06 13:39:58 +0100466 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000467 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
468 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100469
Andrew Scull1ba470e2018-10-31 15:14:31 +0000470 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100471 * Assert condition to communicate the API constraint of mm_max_level(),
472 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000473 */
Daniel Boulbya2f8c662021-11-26 17:52:53 +0000474 assert(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000475
476 /* Cap end to stay within the bounds of the page table. */
477 if (end > ptable_end) {
478 end = ptable_end;
479 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100480
Andrew Walbran58a6e542019-11-19 14:23:15 +0000481 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool)) {
482 return false;
483 }
484
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800485 /*
486 * All TLB invalidations must be complete already if any entries were
487 * replaced by mm_replace_entry. Sync all page table writes so that code
488 * following this can use them.
489 */
490 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000491
492 return true;
493}
494
Andrew Scull4e83cef2019-11-19 14:17:54 +0000495/*
496 * Prepares the given page table for the given address mapping such that it
497 * will be able to commit the change without failure. It does so by ensuring
498 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100499 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000500 *
501 * In particular, multiple calls to this function will result in the
502 * corresponding calls to commit the changes to succeed.
503 */
504static bool mm_ptable_identity_prepare(struct mm_ptable *t, paddr_t pa_begin,
505 paddr_t pa_end, uint64_t attrs,
506 int flags, struct mpool *ppool)
507{
508 flags &= ~MM_FLAG_COMMIT;
509 return mm_ptable_identity_map(t, pa_begin, pa_end, attrs, flags, ppool);
510}
511
512/**
513 * Commits the given address mapping to the page table assuming the operation
514 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
515 * ensure this condition.
516 *
517 * Without the table being properly prepared, the commit may only partially
518 * complete if it runs out of memory resulting in an inconsistent state that
519 * isn't handled.
520 *
521 * Since the non-failure assumtion is used in the reasoning about the atomicity
522 * of higher level memory operations, any detected violations result in a panic.
523 *
524 * TODO: remove ppool argument to be sure no changes are made.
525 */
526static void mm_ptable_identity_commit(struct mm_ptable *t, paddr_t pa_begin,
527 paddr_t pa_end, uint64_t attrs, int flags,
528 struct mpool *ppool)
529{
530 CHECK(mm_ptable_identity_map(t, pa_begin, pa_end, attrs,
531 flags | MM_FLAG_COMMIT, ppool));
532}
533
Andrew Walbran58a6e542019-11-19 14:23:15 +0000534/**
535 * Updates the given table such that the given physical address range is mapped
536 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000537 * provided.
538 *
539 * The page table is updated using the separate prepare and commit stages so
540 * that, on failure, a partial update of the address space cannot happen. The
541 * table may be left with extra internal tables but the address space is
542 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000543 */
544static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
545 paddr_t pa_end, uint64_t attrs, int flags,
546 struct mpool *ppool)
547{
Andrew Scull4e83cef2019-11-19 14:17:54 +0000548 if (!mm_ptable_identity_prepare(t, pa_begin, pa_end, attrs, flags,
549 ppool)) {
550 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100551 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100552
Andrew Scull4e83cef2019-11-19 14:17:54 +0000553 mm_ptable_identity_commit(t, pa_begin, pa_end, attrs, flags, ppool);
554
555 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100556}
557
558/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100559 * Writes the given table to the debug log, calling itself recursively to
560 * write sub-tables.
561 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100562// NOLINTNEXTLINE(misc-no-recursion)
Andrew Sculle9827712018-10-19 14:54:20 +0100563static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100564 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100565{
566 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000567
Andrew Scull4e5f8142018-10-12 14:37:19 +0100568 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
569 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100570 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100571 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100572
Andrew Scull4e5f8142018-10-12 14:37:19 +0100573 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
574 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100575
Andrew Scull4e5f8142018-10-12 14:37:19 +0100576 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100577 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100578 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000579 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100580 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100581 }
582 }
583}
584
585/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000586 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100587 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000588static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100589{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000590 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000591 uint8_t max_level = mm_max_level(flags);
592 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000593 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000594
Andrew Scull1ba470e2018-10-31 15:14:31 +0000595 for (i = 0; i < root_table_count; ++i) {
596 mm_dump_table_recursive(&tables[i], max_level, max_level);
597 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100598}
599
600/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000601 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800602 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100603 */
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800604static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100605{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100606 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100607 uint64_t block_attrs;
608 uint64_t table_attrs;
609 uint64_t combined_attrs;
610 paddr_t block_address;
611
Andrew Scullb6b9b562018-12-21 14:41:35 +0000612 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
613
614 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000615 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100616 }
617
Andrew Scullb6b9b562018-12-21 14:41:35 +0000618 /* Might not be possible to merge the table into a single block. */
619 if (!arch_mm_is_block_allowed(level)) {
620 return table_pte;
621 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000622
Andrew Scullb6b9b562018-12-21 14:41:35 +0000623 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000624 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000625 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100626 combined_attrs =
627 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000628 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000629
Andrew Walbran2400ed22018-09-27 14:45:58 +0100630 return arch_mm_block_pte(level, block_address, combined_attrs);
631}
632
633/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000634 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000635 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100636 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100637// NOLINTNEXTLINE(misc-no-recursion)
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800638static void mm_ptable_defrag_entry(ptable_addr_t base_addr, pte_t *entry,
639 uint8_t level, int flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800640 struct mpool *ppool, uint16_t id)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100641{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100642 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100643 uint64_t i;
Andrew Scull12122ce2019-11-19 14:21:07 +0000644 bool mergeable;
645 bool base_present;
646 uint64_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800647 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100648
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800649 if (!arch_mm_pte_is_table(*entry, level)) {
650 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100651 }
652
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800653 table = mm_page_table_from_pa(arch_mm_table_from_pte(*entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100654
Andrew Scull12122ce2019-11-19 14:21:07 +0000655 /* Defrag the first entry in the table and use it as the base entry. */
656 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800657
658 mm_ptable_defrag_entry(base_addr, &(table->entries[0]), level - 1,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800659 flags, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800660
Andrew Scull12122ce2019-11-19 14:21:07 +0000661 base_present = arch_mm_pte_is_present(table->entries[0], level - 1);
662 base_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
663
Andrew Walbran2400ed22018-09-27 14:45:58 +0100664 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000665 * Defrag the remaining entries in the table and check whether they are
666 * compatible with the base entry meaning the table can be merged into a
667 * block entry. It assumes addresses are contiguous due to identity
668 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100669 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000670 mergeable = true;
671 for (i = 1; i < MM_PTE_PER_PAGE; ++i) {
672 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800673 ptable_addr_t block_addr =
674 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000675
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800676 mm_ptable_defrag_entry(block_addr, &(table->entries[i]),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800677 level - 1, flags, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800678
Andrew Scull12122ce2019-11-19 14:21:07 +0000679 present = arch_mm_pte_is_present(table->entries[i], level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100680
Andrew Scull12122ce2019-11-19 14:21:07 +0000681 if (present != base_present) {
682 mergeable = false;
683 continue;
684 }
685
686 if (!present) {
687 continue;
688 }
689
690 if (!arch_mm_pte_is_block(table->entries[i], level - 1)) {
691 mergeable = false;
692 continue;
693 }
694
695 if (arch_mm_pte_attrs(table->entries[i], level - 1) !=
696 base_attrs) {
697 mergeable = false;
698 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100699 }
700 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000701
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800702 if (!mergeable) {
703 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000704 }
705
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800706 new_entry = mm_merge_table_pte(*entry, level);
707 if (*entry != new_entry) {
708 mm_replace_entry(base_addr, entry, new_entry, level, flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800709 ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800710 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100711}
712
713/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100714 * Defragments the given page table by converting page table references to
715 * blocks whenever possible.
716 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000717static void mm_ptable_defrag(struct mm_ptable *t, int flags,
718 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100719{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000720 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000721 uint8_t level = mm_max_level(flags);
722 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000723 uint8_t i;
724 uint64_t j;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800725 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100726
727 /*
728 * Loop through each entry in the table. If it points to another table,
729 * check if that table can be replaced by a block or an absent entry.
730 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000731 for (i = 0; i < root_table_count; ++i) {
732 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800733 mm_ptable_defrag_entry(block_addr,
734 &(tables[i].entries[j]), level,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800735 flags, ppool, t->id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800736 block_addr = mm_start_of_next_block(
737 block_addr, mm_entry_size(level));
Andrew Scull1ba470e2018-10-31 15:14:31 +0000738 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100739 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800740
741 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100742}
743
744/**
Andrew Scull81e85092018-12-12 12:56:20 +0000745 * Gets the attributes applied to the given range of stage-2 addresses at the
746 * given level.
747 *
748 * The `got_attrs` argument is initially passed as false until `attrs` contains
749 * attributes of the memory region at which point it is passed as true.
750 *
751 * The value returned in `attrs` is only valid if the function returns true.
752 *
753 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100754 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100755// NOLINTNEXTLINE(misc-no-recursion)
Andrew Scull81e85092018-12-12 12:56:20 +0000756static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
757 ptable_addr_t begin, ptable_addr_t end,
758 uint8_t level, bool got_attrs,
759 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100760{
Andrew Scull81e85092018-12-12 12:56:20 +0000761 pte_t *pte = &table->entries[mm_index(begin, level)];
762 ptable_addr_t level_end = mm_level_end(begin, level);
763 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100764
Andrew Scull81e85092018-12-12 12:56:20 +0000765 /* Cap end so that we don't go over the current level max. */
766 if (end > level_end) {
767 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100768 }
769
Andrew Scull81e85092018-12-12 12:56:20 +0000770 /* Check that each entry is owned. */
771 while (begin < end) {
772 if (arch_mm_pte_is_table(*pte, level)) {
773 if (!mm_ptable_get_attrs_level(
774 mm_page_table_from_pa(
775 arch_mm_table_from_pte(*pte,
776 level)),
777 begin, end, level - 1, got_attrs, attrs)) {
778 return false;
779 }
780 got_attrs = true;
781 } else {
782 if (!got_attrs) {
783 *attrs = arch_mm_pte_attrs(*pte, level);
784 got_attrs = true;
785 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
786 return false;
787 }
788 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100789
Andrew Scull81e85092018-12-12 12:56:20 +0000790 begin = mm_start_of_next_block(begin, entry_size);
791 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100792 }
793
Andrew Scullc66a04d2018-12-07 13:41:56 +0000794 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000795 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100796}
797
798/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800799 * Gets the attributes applied to the given range of addresses in the page
800 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000801 *
802 * The value returned in `attrs` is only valid if the function returns true.
803 *
804 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100805 */
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800806static bool mm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
807 ptable_addr_t end, uint64_t *attrs, int flags)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100808{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000809 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000810 uint8_t root_level = max_level + 1;
811 size_t root_table_size = mm_entry_size(root_level);
812 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000813 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000814 struct mm_page_table *table;
815 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100816
Andrew Scull81e85092018-12-12 12:56:20 +0000817 begin = mm_round_down_to_page(begin);
818 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100819
Andrew Scull81e85092018-12-12 12:56:20 +0000820 /* Fail if the addresses are out of range. */
821 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000822 return false;
823 }
824
Andrew Scull81e85092018-12-12 12:56:20 +0000825 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
826 while (begin < end) {
827 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
828 got_attrs, attrs)) {
829 return false;
830 }
831
832 got_attrs = true;
833 begin = mm_start_of_next_block(begin, root_table_size);
834 table++;
835 }
836
837 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100838}
839
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800840bool mm_vm_init(struct mm_ptable *t, uint16_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100841{
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800842 return mm_ptable_init(t, id, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100843}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100844
Andrew Scullda3df7f2019-01-05 17:49:27 +0000845void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000846{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000847 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000848}
849
850/**
Andrew Scull73b89542019-11-20 17:31:26 +0000851 * Selects flags to pass to the page table manipulation operation based on the
852 * mapping mode.
853 */
854static int mm_mode_to_flags(uint32_t mode)
855{
856 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
857 return MM_FLAG_UNMAP;
858 }
859
860 return 0;
861}
862
863/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000864 * See `mm_ptable_identity_prepare`.
865 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800866 * This must be called before `mm_identity_commit` for the same mapping.
867 *
868 * Returns true on success, or false if the update would fail.
869 */
870bool mm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
871 uint32_t mode, struct mpool *ppool)
872{
873 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
874
875 return mm_ptable_identity_prepare(t, begin, end,
876 arch_mm_mode_to_stage1_attrs(mode),
877 flags, ppool);
878}
879
880/**
881 * See `mm_ptable_identity_commit`.
882 *
883 * `mm_identity_prepare` must be called before this for the same mapping.
884 */
885void *mm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
886 uint32_t mode, struct mpool *ppool)
887{
888 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
889
890 mm_ptable_identity_commit(t, begin, end,
891 arch_mm_mode_to_stage1_attrs(mode), flags,
892 ppool);
893 return ptr_from_va(va_from_pa(begin));
894}
895
896/**
897 * See `mm_ptable_identity_prepare`.
898 *
Andrew Scull4e83cef2019-11-19 14:17:54 +0000899 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000900 *
901 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000902 */
903bool mm_vm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
904 uint32_t mode, struct mpool *ppool)
905{
906 int flags = mm_mode_to_flags(mode);
907
908 return mm_ptable_identity_prepare(t, begin, end,
909 arch_mm_mode_to_stage2_attrs(mode),
910 flags, ppool);
911}
912
913/**
914 * See `mm_ptable_identity_commit`.
915 *
916 * `mm_vm_identity_prepare` must be called before this for the same mapping.
917 */
918void mm_vm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000919 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000920{
921 int flags = mm_mode_to_flags(mode);
922
923 mm_ptable_identity_commit(t, begin, end,
924 arch_mm_mode_to_stage2_attrs(mode), flags,
925 ppool);
926
927 if (ipa != NULL) {
928 *ipa = ipa_from_pa(begin);
929 }
930}
931
932/**
Andrew Scull80871322018-08-06 12:04:09 +0100933 * Updates a VM's page table such that the given physical address range is
934 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100935 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000936 *
937 * mm_vm_defrag should always be called after a series of page table updates,
938 * whether they succeed or fail. This is because on failure extra page table
939 * entries may have been allocated and then not used, while on success it may be
940 * possible to compact the page table by merging several entries into a block.
941 *
942 * Returns true on success, or false if the update failed and no changes were
943 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100944 */
Andrew Scull80871322018-08-06 12:04:09 +0100945bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000946 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100947{
Andrew Scull73b89542019-11-20 17:31:26 +0000948 int flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000949 bool success = mm_ptable_identity_update(
950 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
951 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100952
953 if (success && ipa != NULL) {
954 *ipa = ipa_from_pa(begin);
955 }
956
957 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100958}
959
960/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000961 * Updates the VM's table such that the given physical address range has no
962 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100963 */
Andrew Scullda241972019-01-05 18:17:48 +0000964bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000965 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100966{
Andrew Scull73b89542019-11-20 17:31:26 +0000967 uint32_t mode = MM_MODE_UNMAPPED_MASK;
968
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000969 return mm_vm_identity_map(t, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +0100970}
971
972/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000973 * Write the given page table of a VM to the debug log.
974 */
975void mm_vm_dump(struct mm_ptable *t)
976{
977 mm_ptable_dump(t, 0);
978}
979
980/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -0700981 * Defragments a stage1 page table.
982 */
983void mm_stage1_defrag(struct mm_ptable *t, struct mpool *ppool)
984{
985 mm_ptable_defrag(t, MM_FLAG_STAGE1, ppool);
986}
987
988/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000989 * Defragments the VM page table.
990 */
991void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
992{
993 mm_ptable_defrag(t, 0, ppool);
994}
995
996/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100997 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +0000998 * are mapped with the same mode.
999 *
1000 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001001 */
Andrew Scull81e85092018-12-12 12:56:20 +00001002bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
Andrew Walbran1281ed42019-10-22 17:23:40 +01001003 uint32_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001004{
Andrew Scull81e85092018-12-12 12:56:20 +00001005 uint64_t attrs;
1006 bool ret;
1007
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001008 ret = mm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs, 0);
Andrew Scull81e85092018-12-12 12:56:20 +00001009 if (ret) {
1010 *mode = arch_mm_stage2_attrs_to_mode(attrs);
1011 }
1012
1013 return ret;
Andrew Scull80871322018-08-06 12:04:09 +01001014}
1015
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001016/**
1017 * Gets the mode of the given range of virtual addresses if they
1018 * are mapped with the same mode.
1019 *
1020 * Returns true if the range is mapped with the same mode and false otherwise.
1021 */
1022bool mm_get_mode(struct mm_ptable *t, vaddr_t begin, vaddr_t end,
1023 uint32_t *mode)
1024{
1025 uint64_t attrs;
1026 bool ret;
1027
1028 ret = mm_get_attrs(t, va_addr(begin), va_addr(end), &attrs,
1029 MM_FLAG_STAGE1);
1030 if (ret) {
1031 *mode = arch_mm_stage1_attrs_to_mode(attrs);
1032 }
1033
1034 return ret;
1035}
1036
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001037static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1038{
1039 return (struct mm_stage1_locked){.ptable = &ptable};
1040}
1041
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001042struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1043{
1044 return (struct mm_stage1_locked){.ptable = ptable};
1045}
1046
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001047struct mm_stage1_locked mm_lock_stage1(void)
1048{
1049 sl_lock(&ptable_lock);
1050 return mm_stage1_lock_unsafe();
1051}
1052
1053void mm_unlock_stage1(struct mm_stage1_locked *lock)
1054{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001055 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001056 sl_unlock(&ptable_lock);
1057 lock->ptable = NULL;
1058}
1059
Andrew Scull80871322018-08-06 12:04:09 +01001060/**
Andrew Scull80871322018-08-06 12:04:09 +01001061 * Updates the hypervisor page table such that the given physical address range
1062 * is mapped into the address space at the corresponding address range in the
1063 * architecture-agnostic mode provided.
1064 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001065void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Andrew Walbran1281ed42019-10-22 17:23:40 +01001066 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001067{
Andrew Scull73b89542019-11-20 17:31:26 +00001068 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
1069
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001070 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001071 arch_mm_mode_to_stage1_attrs(mode), flags,
1072 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001073 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001074 }
1075
1076 return NULL;
1077}
1078
1079/**
1080 * Updates the hypervisor table such that the given physical address range is
1081 * not mapped in the address space.
1082 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001083bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1084 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001085{
Andrew Scull73b89542019-11-20 17:31:26 +00001086 uint32_t mode = MM_MODE_UNMAPPED_MASK;
1087
1088 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001089}
1090
1091/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001092 * Defragments the hypervisor page table.
1093 */
1094void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1095{
1096 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
1097}
1098
1099/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001100 * Initialises memory management for the hypervisor itself.
1101 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001102bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001103{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001104 /* Locking is not enabled yet so fake it, */
1105 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1106
Andrew Walbran17eebf92020-02-05 16:35:49 +00001107 dlog_info("text: %#x - %#x\n", pa_addr(layout_text_begin()),
1108 pa_addr(layout_text_end()));
1109 dlog_info("rodata: %#x - %#x\n", pa_addr(layout_rodata_begin()),
1110 pa_addr(layout_rodata_end()));
1111 dlog_info("data: %#x - %#x\n", pa_addr(layout_data_begin()),
1112 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001113
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001114 /* ASID 0 is reserved for use by the hypervisor. */
1115 if (!mm_ptable_init(&ptable, 0, MM_FLAG_STAGE1, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001116 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001117 return false;
1118 }
1119
Andrew Walbran48699362019-05-20 14:38:00 +01001120 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001121 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001122
1123 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001124 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
1125 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001126
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001127 mm_identity_map(stage1_locked, layout_rodata_begin(),
1128 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001129
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001130 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001131 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001132
Andrew Scullb2910562019-09-17 14:08:27 +01001133 return arch_mm_init(ptable.root);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001134}