blob: 6f15748c9fee881b5488161780f5d191c73094f0 [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
Andrew Scull877ae4b2019-07-02 12:52:33 +010014#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010015#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010016#include "hf/layout.h"
Andrew Walbran48699362019-05-20 14:38:00 +010017#include "hf/plat/console.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010018#include "hf/static_assert.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010019
Andrew Walbran2400ed22018-09-27 14:45:58 +010020/**
21 * This file has functions for managing the level 1 and 2 page tables used by
22 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
23 * and then a level 2 mapping per VM. The design assumes that all page tables
24 * contain only 1-1 mappings, aligned on the block boundaries.
25 */
26
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010027/*
28 * For stage 2, the input is an intermediate physical addresses rather than a
29 * virtual address so:
30 */
Andrew Scull80871322018-08-06 12:04:09 +010031static_assert(
32 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
33 "Currently, the same code manages the stage 1 and stage 2 page tables "
34 "which only works if the virtual and intermediate physical addresses "
35 "are the same size. It looks like that assumption might not be holding "
36 "so we need to check that everything is going to be ok.");
37
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010038static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010039static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010040
Andrew Scullda241972019-01-05 18:17:48 +000041static bool mm_stage2_invalidate = false;
42
43/**
44 * After calling this function, modifications to stage-2 page tables will use
45 * break-before-make and invalidate the TLB for the affected range.
46 */
47void mm_vm_enable_invalidation(void)
48{
49 mm_stage2_invalidate = true;
50}
51
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010052/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010053 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010054 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010055static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010056{
57 return ptr_from_va(va_from_pa(pa));
58}
59
60/**
Andrew Scull80871322018-08-06 12:04:09 +010061 * Rounds an address down to a page boundary.
62 */
63static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
64{
65 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
66}
67
68/**
69 * Rounds an address up to a page boundary.
70 */
71static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
72{
73 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
74}
75
76/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010077 * Calculates the size of the address space represented by a page table entry at
78 * the given level.
79 */
Andrew Sculle9827712018-10-19 14:54:20 +010080static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010081{
Andrew Scull78d6fd92018-09-06 15:08:36 +010082 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010083}
84
85/**
Andrew Scullcae45572018-12-13 15:46:30 +000086 * Gets the address of the start of the next block of the given size. The size
87 * must be a power of two.
88 */
89static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
90 size_t block_size)
91{
92 return (addr + block_size) & ~(block_size - 1);
93}
94
95/**
96 * Gets the physical address of the start of the next block of the given size.
97 * The size must be a power of two.
98 */
99static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
100{
101 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
102}
103
104/**
Andrew Scull80871322018-08-06 12:04:09 +0100105 * For a given address, calculates the maximum (plus one) address that can be
106 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100107 */
Andrew Sculle9827712018-10-19 14:54:20 +0100108static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100109{
110 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000111
Andrew Scull80871322018-08-06 12:04:09 +0100112 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100113}
114
115/**
Andrew Scull80871322018-08-06 12:04:09 +0100116 * For a given address, calculates the index at which its entry is stored in a
117 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100118 */
Andrew Sculle9827712018-10-19 14:54:20 +0100119static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100120{
Andrew Scull80871322018-08-06 12:04:09 +0100121 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000122
Andrew Scull78d6fd92018-09-06 15:08:36 +0100123 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100124}
125
126/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000127 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100128 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000129static struct mm_page_table *mm_alloc_page_tables(size_t count,
130 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100131{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000132 if (count == 1) {
133 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100134 }
135
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000136 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100137}
138
139/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000140 * Returns the maximum level in the page table given the flags.
141 */
142static uint8_t mm_max_level(int flags)
143{
144 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
145 : arch_mm_stage2_max_level();
146}
147
148/**
149 * Returns the number of root-level tables given the flags.
150 */
151static uint8_t mm_root_table_count(int flags)
152{
153 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
154 : arch_mm_stage2_root_table_count();
155}
156
157/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000158 * Invalidates the TLB for the given address range.
159 */
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800160static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags,
161 uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000162{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000163 if (flags & MM_FLAG_STAGE1) {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800164 arch_mm_invalidate_stage1_range(id, va_init(begin),
165 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000166 } else {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800167 arch_mm_invalidate_stage2_range(id, ipa_init(begin),
168 ipa_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000169 }
170}
171
172/**
173 * Frees all page-table-related memory associated with the given pte at the
174 * given level, including any subtables recursively.
175 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100176// NOLINTNEXTLINE(misc-no-recursion)
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000177static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000178{
179 struct mm_page_table *table;
180 uint64_t i;
181
182 if (!arch_mm_pte_is_table(pte, level)) {
183 return;
184 }
185
186 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000187 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000188 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000189 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000190 }
191
192 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000193 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000194}
195
196/**
David Brazdil711fbe92019-08-06 13:39:58 +0100197 * Returns the first address which cannot be encoded in page tables given by
198 * `flags`. It is the exclusive end of the address space created by the tables.
199 */
200ptable_addr_t mm_ptable_addr_space_end(int flags)
201{
202 return mm_root_table_count(flags) *
203 mm_entry_size(mm_max_level(flags) + 1);
204}
205
206/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000207 * Initialises the given page table.
208 */
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800209bool mm_ptable_init(struct mm_ptable *t, uint16_t id, int flags,
210 struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000211{
212 uint8_t i;
213 size_t j;
214 struct mm_page_table *tables;
215 uint8_t root_table_count = mm_root_table_count(flags);
216
217 tables = mm_alloc_page_tables(root_table_count, ppool);
218 if (tables == NULL) {
219 return false;
220 }
221
222 for (i = 0; i < root_table_count; i++) {
223 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
224 tables[i].entries[j] =
225 arch_mm_absent_pte(mm_max_level(flags));
226 }
227 }
228
229 /*
230 * TODO: halloc could return a virtual or physical address if mm not
231 * enabled?
232 */
233 t->root = pa_init((uintpaddr_t)tables);
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800234 t->id = id;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000235 return true;
236}
237
238/**
239 * Frees all memory associated with the give page table.
240 */
241static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
242{
243 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
244 uint8_t level = mm_max_level(flags);
245 uint8_t root_table_count = mm_root_table_count(flags);
246 uint8_t i;
247 uint64_t j;
248
249 for (i = 0; i < root_table_count; ++i) {
250 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
251 mm_free_page_pte(tables[i].entries[j], level, ppool);
252 }
253 }
254
255 mpool_add_chunk(ppool, tables,
256 sizeof(struct mm_page_table) * root_table_count);
257}
258
259/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000260 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000261 * are valid, it performs a break-before-make sequence where it first writes an
262 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
263 * This is to prevent cases where CPUs have different 'valid' values in their
264 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000265 */
266static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800267 uint8_t level, int flags, struct mpool *ppool,
268 uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000269{
270 pte_t v = *pte;
271
272 /*
273 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000274 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000275 */
Andrew Scullda241972019-01-05 18:17:48 +0000276 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800277 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000278 *pte = arch_mm_absent_pte(level);
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800279 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags,
280 id);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000281 }
282
283 /* Assign the new pte. */
284 *pte = new_pte;
285
286 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000287 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000288}
289
290/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100291 * Populates the provided page table entry with a reference to another table if
292 * needed, that is, if it does not yet point to another table.
293 *
294 * Returns a pointer to the table the entry now points to.
295 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000296static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
297 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000298 int flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800299 struct mpool *ppool,
300 uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100301{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100302 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100303 pte_t v = *pte;
304 pte_t new_pte;
305 size_t i;
306 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100307 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100308
309 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100310 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000311 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100312 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100313
314 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000315 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100316 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000317 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100318 return NULL;
319 }
320
321 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100322 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100323 inc = mm_entry_size(level_below);
324 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000325 arch_mm_block_from_pte(v, level),
326 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100327 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100328 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100329 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100330 }
331
332 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100333 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
334 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100335 new_pte += inc;
336 }
337
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000338 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100339 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000340
341 /* Replace the pte entry, doing a break-before-make if needed. */
342 mm_replace_entry(begin, pte,
343 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800344 level, flags, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100345
346 return ntable;
347}
348
349/**
Andrew Scull80871322018-08-06 12:04:09 +0100350 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100351 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000352 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100353 *
354 * This function calls itself recursively if it needs to update additional
355 * levels, but the recursion is bound by the maximum number of levels in a page
356 * table.
357 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100358// NOLINTNEXTLINE(misc-no-recursion)
Andrew Scull80871322018-08-06 12:04:09 +0100359static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100360 uint64_t attrs, struct mm_page_table *table,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800361 uint8_t level, int flags, struct mpool *ppool,
362 uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100363{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100364 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100365 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100366 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000367 bool commit = flags & MM_FLAG_COMMIT;
368 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100369
Andrew Scull265ada92018-07-30 15:19:01 +0100370 /* Cap end so that we don't go over the current level max. */
371 if (end > level_end) {
372 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100373 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100374
375 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100376 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100377 if (unmap ? !arch_mm_pte_is_present(*pte, level)
378 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000379 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100380 /*
381 * If the entry is already mapped with the right
382 * attributes, or already absent in the case of
383 * unmapping, no need to do anything; carry on to the
384 * next entry.
385 */
386 } else if ((end - begin) >= entry_size &&
387 (unmap || arch_mm_is_block_allowed(level)) &&
388 (begin & (entry_size - 1)) == 0) {
389 /*
390 * If the entire entry is within the region we want to
391 * map, map/unmap the whole entry.
392 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100393 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000394 pte_t new_pte =
395 unmap ? arch_mm_absent_pte(level)
396 : arch_mm_block_pte(level, pa,
397 attrs);
398 mm_replace_entry(begin, pte, new_pte, level,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800399 flags, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100400 }
401 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100402 /*
403 * If the entry is already a subtable get it; otherwise
404 * replace it with an equivalent subtable and get that.
405 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000406 struct mm_page_table *nt = mm_populate_table_pte(
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800407 begin, pte, level, flags, ppool, id);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100408 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100409 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100410 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100411
Andrew Walbran6324fc92018-10-03 11:46:43 +0100412 /*
413 * Recurse to map/unmap the appropriate entries within
414 * the subtable.
415 */
Andrew Scull80871322018-08-06 12:04:09 +0100416 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800417 flags, ppool, id)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100418 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100419 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100420 }
421
Andrew Scullcae45572018-12-13 15:46:30 +0000422 begin = mm_start_of_next_block(begin, entry_size);
423 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100424 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100425 }
426
427 return true;
428}
429
430/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000431 * Updates the page table from the root to map the given address range to a
432 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000433 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000434 */
435static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
436 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000437 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000438{
439 size_t root_table_size = mm_entry_size(root_level);
440 struct mm_page_table *table =
441 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
442
443 while (begin < end) {
444 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800445 root_level - 1, flags, ppool, t->id)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000446 return false;
447 }
Andrew Scullcae45572018-12-13 15:46:30 +0000448 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000449 table++;
450 }
451
452 return true;
453}
454
455/**
Andrew Scull80871322018-08-06 12:04:09 +0100456 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000457 * or not mapped into the address space with the architecture-agnostic mode
Andrew Walbran58a6e542019-11-19 14:23:15 +0000458 * provided. Only commits the change if MM_FLAG_COMMIT is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100459 */
Andrew Walbran58a6e542019-11-19 14:23:15 +0000460static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
461 paddr_t pa_end, uint64_t attrs, int flags,
462 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100463{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000464 uint8_t root_level = mm_max_level(flags) + 1;
David Brazdil711fbe92019-08-06 13:39:58 +0100465 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000466 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
467 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100468
Andrew Scull1ba470e2018-10-31 15:14:31 +0000469 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100470 * Assert condition to communicate the API constraint of mm_max_level(),
471 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000472 */
Andrew Scull877ae4b2019-07-02 12:52:33 +0100473 CHECK(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000474
475 /* Cap end to stay within the bounds of the page table. */
476 if (end > ptable_end) {
477 end = ptable_end;
478 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100479
Andrew Walbran58a6e542019-11-19 14:23:15 +0000480 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool)) {
481 return false;
482 }
483
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800484 /*
485 * All TLB invalidations must be complete already if any entries were
486 * replaced by mm_replace_entry. Sync all page table writes so that code
487 * following this can use them.
488 */
489 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000490
491 return true;
492}
493
Andrew Scull4e83cef2019-11-19 14:17:54 +0000494/*
495 * Prepares the given page table for the given address mapping such that it
496 * will be able to commit the change without failure. It does so by ensuring
497 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100498 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000499 *
500 * In particular, multiple calls to this function will result in the
501 * corresponding calls to commit the changes to succeed.
502 */
503static bool mm_ptable_identity_prepare(struct mm_ptable *t, paddr_t pa_begin,
504 paddr_t pa_end, uint64_t attrs,
505 int flags, struct mpool *ppool)
506{
507 flags &= ~MM_FLAG_COMMIT;
508 return mm_ptable_identity_map(t, pa_begin, pa_end, attrs, flags, ppool);
509}
510
511/**
512 * Commits the given address mapping to the page table assuming the operation
513 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
514 * ensure this condition.
515 *
516 * Without the table being properly prepared, the commit may only partially
517 * complete if it runs out of memory resulting in an inconsistent state that
518 * isn't handled.
519 *
520 * Since the non-failure assumtion is used in the reasoning about the atomicity
521 * of higher level memory operations, any detected violations result in a panic.
522 *
523 * TODO: remove ppool argument to be sure no changes are made.
524 */
525static void mm_ptable_identity_commit(struct mm_ptable *t, paddr_t pa_begin,
526 paddr_t pa_end, uint64_t attrs, int flags,
527 struct mpool *ppool)
528{
529 CHECK(mm_ptable_identity_map(t, pa_begin, pa_end, attrs,
530 flags | MM_FLAG_COMMIT, ppool));
531}
532
Andrew Walbran58a6e542019-11-19 14:23:15 +0000533/**
534 * Updates the given table such that the given physical address range is mapped
535 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000536 * provided.
537 *
538 * The page table is updated using the separate prepare and commit stages so
539 * that, on failure, a partial update of the address space cannot happen. The
540 * table may be left with extra internal tables but the address space is
541 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000542 */
543static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
544 paddr_t pa_end, uint64_t attrs, int flags,
545 struct mpool *ppool)
546{
Andrew Scull4e83cef2019-11-19 14:17:54 +0000547 if (!mm_ptable_identity_prepare(t, pa_begin, pa_end, attrs, flags,
548 ppool)) {
549 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100550 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100551
Andrew Scull4e83cef2019-11-19 14:17:54 +0000552 mm_ptable_identity_commit(t, pa_begin, pa_end, attrs, flags, ppool);
553
554 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100555}
556
557/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100558 * Writes the given table to the debug log, calling itself recursively to
559 * write sub-tables.
560 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100561// NOLINTNEXTLINE(misc-no-recursion)
Andrew Sculle9827712018-10-19 14:54:20 +0100562static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100563 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100564{
565 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000566
Andrew Scull4e5f8142018-10-12 14:37:19 +0100567 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
568 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100569 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100570 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100571
Andrew Scull4e5f8142018-10-12 14:37:19 +0100572 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
573 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100574
Andrew Scull4e5f8142018-10-12 14:37:19 +0100575 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100576 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100577 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000578 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100579 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100580 }
581 }
582}
583
584/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000585 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100586 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000587static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100588{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000589 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000590 uint8_t max_level = mm_max_level(flags);
591 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000592 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000593
Andrew Scull1ba470e2018-10-31 15:14:31 +0000594 for (i = 0; i < root_table_count; ++i) {
595 mm_dump_table_recursive(&tables[i], max_level, max_level);
596 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100597}
598
599/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000600 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800601 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100602 */
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800603static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100604{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100605 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100606 uint64_t block_attrs;
607 uint64_t table_attrs;
608 uint64_t combined_attrs;
609 paddr_t block_address;
610
Andrew Scullb6b9b562018-12-21 14:41:35 +0000611 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
612
613 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000614 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100615 }
616
Andrew Scullb6b9b562018-12-21 14:41:35 +0000617 /* Might not be possible to merge the table into a single block. */
618 if (!arch_mm_is_block_allowed(level)) {
619 return table_pte;
620 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000621
Andrew Scullb6b9b562018-12-21 14:41:35 +0000622 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000623 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000624 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100625 combined_attrs =
626 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000627 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000628
Andrew Walbran2400ed22018-09-27 14:45:58 +0100629 return arch_mm_block_pte(level, block_address, combined_attrs);
630}
631
632/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000633 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000634 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100635 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100636// NOLINTNEXTLINE(misc-no-recursion)
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800637static void mm_ptable_defrag_entry(ptable_addr_t base_addr, pte_t *entry,
638 uint8_t level, int flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800639 struct mpool *ppool, uint16_t id)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100640{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100641 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100642 uint64_t i;
Andrew Scull12122ce2019-11-19 14:21:07 +0000643 bool mergeable;
644 bool base_present;
645 uint64_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800646 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100647
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800648 if (!arch_mm_pte_is_table(*entry, level)) {
649 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100650 }
651
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800652 table = mm_page_table_from_pa(arch_mm_table_from_pte(*entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100653
Andrew Scull12122ce2019-11-19 14:21:07 +0000654 /* Defrag the first entry in the table and use it as the base entry. */
655 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800656
657 mm_ptable_defrag_entry(base_addr, &(table->entries[0]), level - 1,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800658 flags, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800659
Andrew Scull12122ce2019-11-19 14:21:07 +0000660 base_present = arch_mm_pte_is_present(table->entries[0], level - 1);
661 base_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
662
Andrew Walbran2400ed22018-09-27 14:45:58 +0100663 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000664 * Defrag the remaining entries in the table and check whether they are
665 * compatible with the base entry meaning the table can be merged into a
666 * block entry. It assumes addresses are contiguous due to identity
667 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100668 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000669 mergeable = true;
670 for (i = 1; i < MM_PTE_PER_PAGE; ++i) {
671 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800672 ptable_addr_t block_addr =
673 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000674
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800675 mm_ptable_defrag_entry(block_addr, &(table->entries[i]),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800676 level - 1, flags, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800677
Andrew Scull12122ce2019-11-19 14:21:07 +0000678 present = arch_mm_pte_is_present(table->entries[i], level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100679
Andrew Scull12122ce2019-11-19 14:21:07 +0000680 if (present != base_present) {
681 mergeable = false;
682 continue;
683 }
684
685 if (!present) {
686 continue;
687 }
688
689 if (!arch_mm_pte_is_block(table->entries[i], level - 1)) {
690 mergeable = false;
691 continue;
692 }
693
694 if (arch_mm_pte_attrs(table->entries[i], level - 1) !=
695 base_attrs) {
696 mergeable = false;
697 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100698 }
699 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000700
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800701 if (!mergeable) {
702 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000703 }
704
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800705 new_entry = mm_merge_table_pte(*entry, level);
706 if (*entry != new_entry) {
707 mm_replace_entry(base_addr, entry, new_entry, level, flags,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800708 ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800709 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100710}
711
712/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100713 * Defragments the given page table by converting page table references to
714 * blocks whenever possible.
715 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000716static void mm_ptable_defrag(struct mm_ptable *t, int flags,
717 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100718{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000719 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000720 uint8_t level = mm_max_level(flags);
721 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000722 uint8_t i;
723 uint64_t j;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800724 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100725
726 /*
727 * Loop through each entry in the table. If it points to another table,
728 * check if that table can be replaced by a block or an absent entry.
729 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000730 for (i = 0; i < root_table_count; ++i) {
731 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800732 mm_ptable_defrag_entry(block_addr,
733 &(tables[i].entries[j]), level,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800734 flags, ppool, t->id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800735 block_addr = mm_start_of_next_block(
736 block_addr, mm_entry_size(level));
Andrew Scull1ba470e2018-10-31 15:14:31 +0000737 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100738 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800739
740 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100741}
742
743/**
Andrew Scull81e85092018-12-12 12:56:20 +0000744 * Gets the attributes applied to the given range of stage-2 addresses at the
745 * given level.
746 *
747 * The `got_attrs` argument is initially passed as false until `attrs` contains
748 * attributes of the memory region at which point it is passed as true.
749 *
750 * The value returned in `attrs` is only valid if the function returns true.
751 *
752 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100753 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100754// NOLINTNEXTLINE(misc-no-recursion)
Andrew Scull81e85092018-12-12 12:56:20 +0000755static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
756 ptable_addr_t begin, ptable_addr_t end,
757 uint8_t level, bool got_attrs,
758 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100759{
Andrew Scull81e85092018-12-12 12:56:20 +0000760 pte_t *pte = &table->entries[mm_index(begin, level)];
761 ptable_addr_t level_end = mm_level_end(begin, level);
762 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100763
Andrew Scull81e85092018-12-12 12:56:20 +0000764 /* Cap end so that we don't go over the current level max. */
765 if (end > level_end) {
766 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100767 }
768
Andrew Scull81e85092018-12-12 12:56:20 +0000769 /* Check that each entry is owned. */
770 while (begin < end) {
771 if (arch_mm_pte_is_table(*pte, level)) {
772 if (!mm_ptable_get_attrs_level(
773 mm_page_table_from_pa(
774 arch_mm_table_from_pte(*pte,
775 level)),
776 begin, end, level - 1, got_attrs, attrs)) {
777 return false;
778 }
779 got_attrs = true;
780 } else {
781 if (!got_attrs) {
782 *attrs = arch_mm_pte_attrs(*pte, level);
783 got_attrs = true;
784 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
785 return false;
786 }
787 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100788
Andrew Scull81e85092018-12-12 12:56:20 +0000789 begin = mm_start_of_next_block(begin, entry_size);
790 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100791 }
792
Andrew Scullc66a04d2018-12-07 13:41:56 +0000793 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000794 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100795}
796
797/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800798 * Gets the attributes applied to the given range of addresses in the page
799 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000800 *
801 * The value returned in `attrs` is only valid if the function returns true.
802 *
803 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100804 */
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800805static bool mm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
806 ptable_addr_t end, uint64_t *attrs, int flags)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100807{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000808 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000809 uint8_t root_level = max_level + 1;
810 size_t root_table_size = mm_entry_size(root_level);
811 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000812 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000813 struct mm_page_table *table;
814 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100815
Andrew Scull81e85092018-12-12 12:56:20 +0000816 begin = mm_round_down_to_page(begin);
817 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100818
Andrew Scull81e85092018-12-12 12:56:20 +0000819 /* Fail if the addresses are out of range. */
820 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000821 return false;
822 }
823
Andrew Scull81e85092018-12-12 12:56:20 +0000824 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
825 while (begin < end) {
826 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
827 got_attrs, attrs)) {
828 return false;
829 }
830
831 got_attrs = true;
832 begin = mm_start_of_next_block(begin, root_table_size);
833 table++;
834 }
835
836 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100837}
838
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800839bool mm_vm_init(struct mm_ptable *t, uint16_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100840{
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -0800841 return mm_ptable_init(t, id, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100842}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100843
Andrew Scullda3df7f2019-01-05 17:49:27 +0000844void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000845{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000846 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000847}
848
849/**
Andrew Scull73b89542019-11-20 17:31:26 +0000850 * Selects flags to pass to the page table manipulation operation based on the
851 * mapping mode.
852 */
853static int mm_mode_to_flags(uint32_t mode)
854{
855 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
856 return MM_FLAG_UNMAP;
857 }
858
859 return 0;
860}
861
862/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000863 * See `mm_ptable_identity_prepare`.
864 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800865 * This must be called before `mm_identity_commit` for the same mapping.
866 *
867 * Returns true on success, or false if the update would fail.
868 */
869bool mm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
870 uint32_t mode, struct mpool *ppool)
871{
872 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
873
874 return mm_ptable_identity_prepare(t, begin, end,
875 arch_mm_mode_to_stage1_attrs(mode),
876 flags, ppool);
877}
878
879/**
880 * See `mm_ptable_identity_commit`.
881 *
882 * `mm_identity_prepare` must be called before this for the same mapping.
883 */
884void *mm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
885 uint32_t mode, struct mpool *ppool)
886{
887 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
888
889 mm_ptable_identity_commit(t, begin, end,
890 arch_mm_mode_to_stage1_attrs(mode), flags,
891 ppool);
892 return ptr_from_va(va_from_pa(begin));
893}
894
895/**
896 * See `mm_ptable_identity_prepare`.
897 *
Andrew Scull4e83cef2019-11-19 14:17:54 +0000898 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000899 *
900 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000901 */
902bool mm_vm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
903 uint32_t mode, struct mpool *ppool)
904{
905 int flags = mm_mode_to_flags(mode);
906
907 return mm_ptable_identity_prepare(t, begin, end,
908 arch_mm_mode_to_stage2_attrs(mode),
909 flags, ppool);
910}
911
912/**
913 * See `mm_ptable_identity_commit`.
914 *
915 * `mm_vm_identity_prepare` must be called before this for the same mapping.
916 */
917void mm_vm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000918 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000919{
920 int flags = mm_mode_to_flags(mode);
921
922 mm_ptable_identity_commit(t, begin, end,
923 arch_mm_mode_to_stage2_attrs(mode), flags,
924 ppool);
925
926 if (ipa != NULL) {
927 *ipa = ipa_from_pa(begin);
928 }
929}
930
931/**
Andrew Scull80871322018-08-06 12:04:09 +0100932 * Updates a VM's page table such that the given physical address range is
933 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100934 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000935 *
936 * mm_vm_defrag should always be called after a series of page table updates,
937 * whether they succeed or fail. This is because on failure extra page table
938 * entries may have been allocated and then not used, while on success it may be
939 * possible to compact the page table by merging several entries into a block.
940 *
941 * Returns true on success, or false if the update failed and no changes were
942 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100943 */
Andrew Scull80871322018-08-06 12:04:09 +0100944bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000945 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100946{
Andrew Scull73b89542019-11-20 17:31:26 +0000947 int flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000948 bool success = mm_ptable_identity_update(
949 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
950 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100951
952 if (success && ipa != NULL) {
953 *ipa = ipa_from_pa(begin);
954 }
955
956 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100957}
958
959/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000960 * Updates the VM's table such that the given physical address range has no
961 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100962 */
Andrew Scullda241972019-01-05 18:17:48 +0000963bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000964 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100965{
Andrew Scull73b89542019-11-20 17:31:26 +0000966 uint32_t mode = MM_MODE_UNMAPPED_MASK;
967
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000968 return mm_vm_identity_map(t, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +0100969}
970
971/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000972 * Write the given page table of a VM to the debug log.
973 */
974void mm_vm_dump(struct mm_ptable *t)
975{
976 mm_ptable_dump(t, 0);
977}
978
979/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -0700980 * Defragments a stage1 page table.
981 */
982void mm_stage1_defrag(struct mm_ptable *t, struct mpool *ppool)
983{
984 mm_ptable_defrag(t, MM_FLAG_STAGE1, ppool);
985}
986
987/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000988 * Defragments the VM page table.
989 */
990void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
991{
992 mm_ptable_defrag(t, 0, ppool);
993}
994
995/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100996 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +0000997 * are mapped with the same mode.
998 *
999 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001000 */
Andrew Scull81e85092018-12-12 12:56:20 +00001001bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
Andrew Walbran1281ed42019-10-22 17:23:40 +01001002 uint32_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001003{
Andrew Scull81e85092018-12-12 12:56:20 +00001004 uint64_t attrs;
1005 bool ret;
1006
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001007 ret = mm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs, 0);
Andrew Scull81e85092018-12-12 12:56:20 +00001008 if (ret) {
1009 *mode = arch_mm_stage2_attrs_to_mode(attrs);
1010 }
1011
1012 return ret;
Andrew Scull80871322018-08-06 12:04:09 +01001013}
1014
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001015/**
1016 * Gets the mode of the given range of virtual addresses if they
1017 * are mapped with the same mode.
1018 *
1019 * Returns true if the range is mapped with the same mode and false otherwise.
1020 */
1021bool mm_get_mode(struct mm_ptable *t, vaddr_t begin, vaddr_t end,
1022 uint32_t *mode)
1023{
1024 uint64_t attrs;
1025 bool ret;
1026
1027 ret = mm_get_attrs(t, va_addr(begin), va_addr(end), &attrs,
1028 MM_FLAG_STAGE1);
1029 if (ret) {
1030 *mode = arch_mm_stage1_attrs_to_mode(attrs);
1031 }
1032
1033 return ret;
1034}
1035
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001036static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1037{
1038 return (struct mm_stage1_locked){.ptable = &ptable};
1039}
1040
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001041struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1042{
1043 return (struct mm_stage1_locked){.ptable = ptable};
1044}
1045
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001046struct mm_stage1_locked mm_lock_stage1(void)
1047{
1048 sl_lock(&ptable_lock);
1049 return mm_stage1_lock_unsafe();
1050}
1051
1052void mm_unlock_stage1(struct mm_stage1_locked *lock)
1053{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001054 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001055 sl_unlock(&ptable_lock);
1056 lock->ptable = NULL;
1057}
1058
Andrew Scull80871322018-08-06 12:04:09 +01001059/**
Andrew Scull80871322018-08-06 12:04:09 +01001060 * Updates the hypervisor page table such that the given physical address range
1061 * is mapped into the address space at the corresponding address range in the
1062 * architecture-agnostic mode provided.
1063 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001064void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Andrew Walbran1281ed42019-10-22 17:23:40 +01001065 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001066{
Andrew Scull73b89542019-11-20 17:31:26 +00001067 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
1068
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001069 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001070 arch_mm_mode_to_stage1_attrs(mode), flags,
1071 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001072 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001073 }
1074
1075 return NULL;
1076}
1077
1078/**
1079 * Updates the hypervisor table such that the given physical address range is
1080 * not mapped in the address space.
1081 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001082bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1083 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001084{
Andrew Scull73b89542019-11-20 17:31:26 +00001085 uint32_t mode = MM_MODE_UNMAPPED_MASK;
1086
1087 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001088}
1089
1090/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001091 * Defragments the hypervisor page table.
1092 */
1093void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1094{
1095 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
1096}
1097
1098/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001099 * Initialises memory management for the hypervisor itself.
1100 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001101bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001102{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001103 /* Locking is not enabled yet so fake it, */
1104 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1105
Andrew Walbran17eebf92020-02-05 16:35:49 +00001106 dlog_info("text: %#x - %#x\n", pa_addr(layout_text_begin()),
1107 pa_addr(layout_text_end()));
1108 dlog_info("rodata: %#x - %#x\n", pa_addr(layout_rodata_begin()),
1109 pa_addr(layout_rodata_end()));
1110 dlog_info("data: %#x - %#x\n", pa_addr(layout_data_begin()),
1111 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001112
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001113 /* ASID 0 is reserved for use by the hypervisor. */
1114 if (!mm_ptable_init(&ptable, 0, MM_FLAG_STAGE1, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001115 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001116 return false;
1117 }
1118
Andrew Walbran48699362019-05-20 14:38:00 +01001119 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001120 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001121
1122 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001123 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
1124 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001125
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001126 mm_identity_map(stage1_locked, layout_rodata_begin(),
1127 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001128
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001129 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001130 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001131
Andrew Scullb2910562019-09-17 14:08:27 +01001132 return arch_mm_init(ptable.root);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001133}