blob: 11fb47594d3ffc6e71982581ae02c450b1aa4bc1 [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 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000160static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000161{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000162 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000163 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
164 } else {
165 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
166 }
167}
168
169/**
170 * Frees all page-table-related memory associated with the given pte at the
171 * given level, including any subtables recursively.
172 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000173static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000174{
175 struct mm_page_table *table;
176 uint64_t i;
177
178 if (!arch_mm_pte_is_table(pte, level)) {
179 return;
180 }
181
182 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000183 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000184 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000185 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000186 }
187
188 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000189 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000190}
191
192/**
David Brazdil711fbe92019-08-06 13:39:58 +0100193 * Returns the first address which cannot be encoded in page tables given by
194 * `flags`. It is the exclusive end of the address space created by the tables.
195 */
196ptable_addr_t mm_ptable_addr_space_end(int flags)
197{
198 return mm_root_table_count(flags) *
199 mm_entry_size(mm_max_level(flags) + 1);
200}
201
202/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000203 * Initialises the given page table.
204 */
David Brazdil711fbe92019-08-06 13:39:58 +0100205bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000206{
207 uint8_t i;
208 size_t j;
209 struct mm_page_table *tables;
210 uint8_t root_table_count = mm_root_table_count(flags);
211
212 tables = mm_alloc_page_tables(root_table_count, ppool);
213 if (tables == NULL) {
214 return false;
215 }
216
217 for (i = 0; i < root_table_count; i++) {
218 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
219 tables[i].entries[j] =
220 arch_mm_absent_pte(mm_max_level(flags));
221 }
222 }
223
224 /*
225 * TODO: halloc could return a virtual or physical address if mm not
226 * enabled?
227 */
228 t->root = pa_init((uintpaddr_t)tables);
229
230 return true;
231}
232
233/**
234 * Frees all memory associated with the give page table.
235 */
236static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
237{
238 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
239 uint8_t level = mm_max_level(flags);
240 uint8_t root_table_count = mm_root_table_count(flags);
241 uint8_t i;
242 uint64_t j;
243
244 for (i = 0; i < root_table_count; ++i) {
245 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
246 mm_free_page_pte(tables[i].entries[j], level, ppool);
247 }
248 }
249
250 mpool_add_chunk(ppool, tables,
251 sizeof(struct mm_page_table) * root_table_count);
252}
253
254/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000255 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000256 * are valid, it performs a break-before-make sequence where it first writes an
257 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
258 * This is to prevent cases where CPUs have different 'valid' values in their
259 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000260 */
261static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000262 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000263{
264 pte_t v = *pte;
265
266 /*
267 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000268 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000269 */
Andrew Scullda241972019-01-05 18:17:48 +0000270 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
271 arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000272 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000273 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000274 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000275 }
276
277 /* Assign the new pte. */
278 *pte = new_pte;
279
280 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000281 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000282}
283
284/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100285 * Populates the provided page table entry with a reference to another table if
286 * needed, that is, if it does not yet point to another table.
287 *
288 * Returns a pointer to the table the entry now points to.
289 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000290static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
291 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000292 int flags,
293 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100294{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100295 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100296 pte_t v = *pte;
297 pte_t new_pte;
298 size_t i;
299 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100300 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100301
302 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100303 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000304 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100305 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306
307 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000308 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100309 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000310 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100311 return NULL;
312 }
313
314 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100315 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100316 inc = mm_entry_size(level_below);
317 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000318 arch_mm_block_from_pte(v, level),
319 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100320 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100321 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100322 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100323 }
324
325 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100326 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
327 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100328 new_pte += inc;
329 }
330
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000331 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100332 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000333
334 /* Replace the pte entry, doing a break-before-make if needed. */
335 mm_replace_entry(begin, pte,
336 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000337 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100338
339 return ntable;
340}
341
342/**
Andrew Scull80871322018-08-06 12:04:09 +0100343 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100344 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000345 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100346 *
347 * This function calls itself recursively if it needs to update additional
348 * levels, but the recursion is bound by the maximum number of levels in a page
349 * table.
350 */
Andrew Scull80871322018-08-06 12:04:09 +0100351static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100352 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000353 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100354{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100355 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100356 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100357 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000358 bool commit = flags & MM_FLAG_COMMIT;
359 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100360
Andrew Scull265ada92018-07-30 15:19:01 +0100361 /* Cap end so that we don't go over the current level max. */
362 if (end > level_end) {
363 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100364 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100365
366 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100367 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100368 if (unmap ? !arch_mm_pte_is_present(*pte, level)
369 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000370 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100371 /*
372 * If the entry is already mapped with the right
373 * attributes, or already absent in the case of
374 * unmapping, no need to do anything; carry on to the
375 * next entry.
376 */
377 } else if ((end - begin) >= entry_size &&
378 (unmap || arch_mm_is_block_allowed(level)) &&
379 (begin & (entry_size - 1)) == 0) {
380 /*
381 * If the entire entry is within the region we want to
382 * map, map/unmap the whole entry.
383 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100384 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000385 pte_t new_pte =
386 unmap ? arch_mm_absent_pte(level)
387 : arch_mm_block_pte(level, pa,
388 attrs);
389 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000390 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100391 }
392 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100393 /*
394 * If the entry is already a subtable get it; otherwise
395 * replace it with an equivalent subtable and get that.
396 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000397 struct mm_page_table *nt = mm_populate_table_pte(
398 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100399 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100400 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100401 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100402
Andrew Walbran6324fc92018-10-03 11:46:43 +0100403 /*
404 * Recurse to map/unmap the appropriate entries within
405 * the subtable.
406 */
Andrew Scull80871322018-08-06 12:04:09 +0100407 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000408 flags, ppool)) {
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 }
412
Andrew Scullcae45572018-12-13 15:46:30 +0000413 begin = mm_start_of_next_block(begin, entry_size);
414 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100415 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100416 }
417
418 return true;
419}
420
421/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000422 * Updates the page table from the root to map the given address range to a
423 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000424 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000425 */
426static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
427 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000428 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000429{
430 size_t root_table_size = mm_entry_size(root_level);
431 struct mm_page_table *table =
432 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
433
434 while (begin < end) {
435 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000436 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000437 return false;
438 }
Andrew Scullcae45572018-12-13 15:46:30 +0000439 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000440 table++;
441 }
442
443 return true;
444}
445
446/**
Andrew Scull80871322018-08-06 12:04:09 +0100447 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000448 * or not mapped into the address space with the architecture-agnostic mode
Andrew Walbran58a6e542019-11-19 14:23:15 +0000449 * provided. Only commits the change if MM_FLAG_COMMIT is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100450 */
Andrew Walbran58a6e542019-11-19 14:23:15 +0000451static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
452 paddr_t pa_end, uint64_t attrs, int flags,
453 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100454{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000455 uint8_t root_level = mm_max_level(flags) + 1;
David Brazdil711fbe92019-08-06 13:39:58 +0100456 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000457 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
458 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100459
Andrew Scull1ba470e2018-10-31 15:14:31 +0000460 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100461 * Assert condition to communicate the API constraint of mm_max_level(),
462 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000463 */
Andrew Scull877ae4b2019-07-02 12:52:33 +0100464 CHECK(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000465
466 /* Cap end to stay within the bounds of the page table. */
467 if (end > ptable_end) {
468 end = ptable_end;
469 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100470
Andrew Walbran58a6e542019-11-19 14:23:15 +0000471 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool)) {
472 return false;
473 }
474
475 /* Invalidate the TLB. */
476 if ((flags & MM_FLAG_COMMIT) &&
477 ((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate)) {
478 mm_invalidate_tlb(begin, end, flags);
479 }
480
481 return true;
482}
483
Andrew Scull4e83cef2019-11-19 14:17:54 +0000484/*
485 * Prepares the given page table for the given address mapping such that it
486 * will be able to commit the change without failure. It does so by ensuring
487 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100488 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000489 *
490 * In particular, multiple calls to this function will result in the
491 * corresponding calls to commit the changes to succeed.
492 */
493static bool mm_ptable_identity_prepare(struct mm_ptable *t, paddr_t pa_begin,
494 paddr_t pa_end, uint64_t attrs,
495 int flags, struct mpool *ppool)
496{
497 flags &= ~MM_FLAG_COMMIT;
498 return mm_ptable_identity_map(t, pa_begin, pa_end, attrs, flags, ppool);
499}
500
501/**
502 * Commits the given address mapping to the page table assuming the operation
503 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
504 * ensure this condition.
505 *
506 * Without the table being properly prepared, the commit may only partially
507 * complete if it runs out of memory resulting in an inconsistent state that
508 * isn't handled.
509 *
510 * Since the non-failure assumtion is used in the reasoning about the atomicity
511 * of higher level memory operations, any detected violations result in a panic.
512 *
513 * TODO: remove ppool argument to be sure no changes are made.
514 */
515static void mm_ptable_identity_commit(struct mm_ptable *t, paddr_t pa_begin,
516 paddr_t pa_end, uint64_t attrs, int flags,
517 struct mpool *ppool)
518{
519 CHECK(mm_ptable_identity_map(t, pa_begin, pa_end, attrs,
520 flags | MM_FLAG_COMMIT, ppool));
521}
522
Andrew Walbran58a6e542019-11-19 14:23:15 +0000523/**
524 * Updates the given table such that the given physical address range is mapped
525 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000526 * provided.
527 *
528 * The page table is updated using the separate prepare and commit stages so
529 * that, on failure, a partial update of the address space cannot happen. The
530 * table may be left with extra internal tables but the address space is
531 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000532 */
533static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
534 paddr_t pa_end, uint64_t attrs, int flags,
535 struct mpool *ppool)
536{
Andrew Scull4e83cef2019-11-19 14:17:54 +0000537 if (!mm_ptable_identity_prepare(t, pa_begin, pa_end, attrs, flags,
538 ppool)) {
539 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100540 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100541
Andrew Scull4e83cef2019-11-19 14:17:54 +0000542 mm_ptable_identity_commit(t, pa_begin, pa_end, attrs, flags, ppool);
543
544 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100545}
546
547/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100548 * Writes the given table to the debug log, calling itself recursively to
549 * write sub-tables.
550 */
Andrew Sculle9827712018-10-19 14:54:20 +0100551static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100552 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100553{
554 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000555
Andrew Scull4e5f8142018-10-12 14:37:19 +0100556 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
557 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100558 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100559 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100560
Andrew Scull4e5f8142018-10-12 14:37:19 +0100561 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
562 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100563
Andrew Scull4e5f8142018-10-12 14:37:19 +0100564 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100565 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100566 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000567 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100568 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100569 }
570 }
571}
572
573/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000574 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100575 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000576static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100577{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000578 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000579 uint8_t max_level = mm_max_level(flags);
580 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000581 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000582
Andrew Scull1ba470e2018-10-31 15:14:31 +0000583 for (i = 0; i < root_table_count; ++i) {
584 mm_dump_table_recursive(&tables[i], max_level, max_level);
585 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100586}
587
588/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000589 * Given the table PTE entries all have identical attributes, returns the single
590 * entry with which it can be replaced. Note that the table PTE will no longer
591 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000592 *
593 * If the table is freed, the memory is freed directly rather than calling
594 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100595 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000596static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
597 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100598{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100599 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100600 uint64_t block_attrs;
601 uint64_t table_attrs;
602 uint64_t combined_attrs;
603 paddr_t block_address;
604
Andrew Scullb6b9b562018-12-21 14:41:35 +0000605 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
606
607 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
608 /* Free the table and return an absent entry. */
609 mpool_free(ppool, table);
610 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100611 }
612
Andrew Scullb6b9b562018-12-21 14:41:35 +0000613 /* Might not be possible to merge the table into a single block. */
614 if (!arch_mm_is_block_allowed(level)) {
615 return table_pte;
616 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000617
Andrew Scullb6b9b562018-12-21 14:41:35 +0000618 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000619 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000620 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100621 combined_attrs =
622 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000623 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000624
Andrew Scullb6b9b562018-12-21 14:41:35 +0000625 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000626 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100627 return arch_mm_block_pte(level, block_address, combined_attrs);
628}
629
630/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000631 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000632 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100633 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000634static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
635 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100636{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100637 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100638 uint64_t i;
Andrew Scull12122ce2019-11-19 14:21:07 +0000639 bool mergeable;
640 bool base_present;
641 uint64_t base_attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100642
643 if (!arch_mm_pte_is_table(entry, level)) {
644 return entry;
645 }
646
Andrew Scull3681b8d2018-12-12 14:22:59 +0000647 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100648
Andrew Scull12122ce2019-11-19 14:21:07 +0000649 /* Defrag the first entry in the table and use it as the base entry. */
650 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
651 table->entries[0] =
652 mm_ptable_defrag_entry(table->entries[0], level - 1, ppool);
653 base_present = arch_mm_pte_is_present(table->entries[0], level - 1);
654 base_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
655
Andrew Walbran2400ed22018-09-27 14:45:58 +0100656 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000657 * Defrag the remaining entries in the table and check whether they are
658 * compatible with the base entry meaning the table can be merged into a
659 * block entry. It assumes addresses are contiguous due to identity
660 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100661 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000662 mergeable = true;
663 for (i = 1; i < MM_PTE_PER_PAGE; ++i) {
664 bool present;
665
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000666 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
667 level - 1, ppool);
Andrew Scull12122ce2019-11-19 14:21:07 +0000668 present = arch_mm_pte_is_present(table->entries[i], level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100669
Andrew Scull12122ce2019-11-19 14:21:07 +0000670 if (present != base_present) {
671 mergeable = false;
672 continue;
673 }
674
675 if (!present) {
676 continue;
677 }
678
679 if (!arch_mm_pte_is_block(table->entries[i], level - 1)) {
680 mergeable = false;
681 continue;
682 }
683
684 if (arch_mm_pte_attrs(table->entries[i], level - 1) !=
685 base_attrs) {
686 mergeable = false;
687 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100688 }
689 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000690
Andrew Scull12122ce2019-11-19 14:21:07 +0000691 if (mergeable) {
692 return mm_merge_table_pte(entry, level, ppool);
693 }
694
695 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100696}
697
698/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100699 * Defragments the given page table by converting page table references to
700 * blocks whenever possible.
701 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000702static void mm_ptable_defrag(struct mm_ptable *t, int flags,
703 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100704{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000705 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000706 uint8_t level = mm_max_level(flags);
707 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000708 uint8_t i;
709 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100710
711 /*
712 * Loop through each entry in the table. If it points to another table,
713 * check if that table can be replaced by a block or an absent entry.
714 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000715 for (i = 0; i < root_table_count; ++i) {
716 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
717 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000718 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000719 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100720 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100721}
722
723/**
Andrew Scull81e85092018-12-12 12:56:20 +0000724 * Gets the attributes applied to the given range of stage-2 addresses at the
725 * given level.
726 *
727 * The `got_attrs` argument is initially passed as false until `attrs` contains
728 * attributes of the memory region at which point it is passed as true.
729 *
730 * The value returned in `attrs` is only valid if the function returns true.
731 *
732 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100733 */
Andrew Scull81e85092018-12-12 12:56:20 +0000734static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
735 ptable_addr_t begin, ptable_addr_t end,
736 uint8_t level, bool got_attrs,
737 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100738{
Andrew Scull81e85092018-12-12 12:56:20 +0000739 pte_t *pte = &table->entries[mm_index(begin, level)];
740 ptable_addr_t level_end = mm_level_end(begin, level);
741 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100742
Andrew Scull81e85092018-12-12 12:56:20 +0000743 /* Cap end so that we don't go over the current level max. */
744 if (end > level_end) {
745 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100746 }
747
Andrew Scull81e85092018-12-12 12:56:20 +0000748 /* Check that each entry is owned. */
749 while (begin < end) {
750 if (arch_mm_pte_is_table(*pte, level)) {
751 if (!mm_ptable_get_attrs_level(
752 mm_page_table_from_pa(
753 arch_mm_table_from_pte(*pte,
754 level)),
755 begin, end, level - 1, got_attrs, attrs)) {
756 return false;
757 }
758 got_attrs = true;
759 } else {
760 if (!got_attrs) {
761 *attrs = arch_mm_pte_attrs(*pte, level);
762 got_attrs = true;
763 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
764 return false;
765 }
766 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100767
Andrew Scull81e85092018-12-12 12:56:20 +0000768 begin = mm_start_of_next_block(begin, entry_size);
769 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100770 }
771
Andrew Scullc66a04d2018-12-07 13:41:56 +0000772 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000773 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100774}
775
776/**
Andrew Scull81e85092018-12-12 12:56:20 +0000777 * Gets the attributes applies to the given range of addresses in the stage-2
778 * table.
779 *
780 * The value returned in `attrs` is only valid if the function returns true.
781 *
782 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100783 */
Andrew Scull81e85092018-12-12 12:56:20 +0000784static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
785 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100786{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000787 int flags = 0;
788 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000789 uint8_t root_level = max_level + 1;
790 size_t root_table_size = mm_entry_size(root_level);
791 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000792 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000793 struct mm_page_table *table;
794 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100795
Andrew Scull81e85092018-12-12 12:56:20 +0000796 begin = mm_round_down_to_page(begin);
797 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100798
Andrew Scull81e85092018-12-12 12:56:20 +0000799 /* Fail if the addresses are out of range. */
800 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000801 return false;
802 }
803
Andrew Scull81e85092018-12-12 12:56:20 +0000804 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
805 while (begin < end) {
806 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
807 got_attrs, attrs)) {
808 return false;
809 }
810
811 got_attrs = true;
812 begin = mm_start_of_next_block(begin, root_table_size);
813 table++;
814 }
815
816 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100817}
818
Andrew Scullda3df7f2019-01-05 17:49:27 +0000819bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100820{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000821 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100822}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100823
Andrew Scullda3df7f2019-01-05 17:49:27 +0000824void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000825{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000826 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000827}
828
829/**
Andrew Scull73b89542019-11-20 17:31:26 +0000830 * Selects flags to pass to the page table manipulation operation based on the
831 * mapping mode.
832 */
833static int mm_mode_to_flags(uint32_t mode)
834{
835 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
836 return MM_FLAG_UNMAP;
837 }
838
839 return 0;
840}
841
842/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000843 * See `mm_ptable_identity_prepare`.
844 *
845 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000846 *
847 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000848 */
849bool mm_vm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
850 uint32_t mode, struct mpool *ppool)
851{
852 int flags = mm_mode_to_flags(mode);
853
854 return mm_ptable_identity_prepare(t, begin, end,
855 arch_mm_mode_to_stage2_attrs(mode),
856 flags, ppool);
857}
858
859/**
860 * See `mm_ptable_identity_commit`.
861 *
862 * `mm_vm_identity_prepare` must be called before this for the same mapping.
863 */
864void mm_vm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000865 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000866{
867 int flags = mm_mode_to_flags(mode);
868
869 mm_ptable_identity_commit(t, begin, end,
870 arch_mm_mode_to_stage2_attrs(mode), flags,
871 ppool);
872
873 if (ipa != NULL) {
874 *ipa = ipa_from_pa(begin);
875 }
876}
877
878/**
Andrew Scull80871322018-08-06 12:04:09 +0100879 * Updates a VM's page table such that the given physical address range is
880 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100881 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000882 *
883 * mm_vm_defrag should always be called after a series of page table updates,
884 * whether they succeed or fail. This is because on failure extra page table
885 * entries may have been allocated and then not used, while on success it may be
886 * possible to compact the page table by merging several entries into a block.
887 *
888 * Returns true on success, or false if the update failed and no changes were
889 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100890 */
Andrew Scull80871322018-08-06 12:04:09 +0100891bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000892 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100893{
Andrew Scull73b89542019-11-20 17:31:26 +0000894 int flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000895 bool success = mm_ptable_identity_update(
896 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
897 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100898
899 if (success && ipa != NULL) {
900 *ipa = ipa_from_pa(begin);
901 }
902
903 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100904}
905
906/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000907 * Updates the VM's table such that the given physical address range has no
908 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100909 */
Andrew Scullda241972019-01-05 18:17:48 +0000910bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000911 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100912{
Andrew Scull73b89542019-11-20 17:31:26 +0000913 uint32_t mode = MM_MODE_UNMAPPED_MASK;
914
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000915 return mm_vm_identity_map(t, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +0100916}
917
918/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000919 * Write the given page table of a VM to the debug log.
920 */
921void mm_vm_dump(struct mm_ptable *t)
922{
923 mm_ptable_dump(t, 0);
924}
925
926/**
927 * Defragments the VM page table.
928 */
929void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
930{
931 mm_ptable_defrag(t, 0, ppool);
932}
933
934/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100935 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +0000936 * are mapped with the same mode.
937 *
938 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100939 */
Andrew Scull81e85092018-12-12 12:56:20 +0000940bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100941 uint32_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100942{
Andrew Scull81e85092018-12-12 12:56:20 +0000943 uint64_t attrs;
944 bool ret;
945
946 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
947 if (ret) {
948 *mode = arch_mm_stage2_attrs_to_mode(attrs);
949 }
950
951 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100952}
953
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100954static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
955{
956 return (struct mm_stage1_locked){.ptable = &ptable};
957}
958
959struct mm_stage1_locked mm_lock_stage1(void)
960{
961 sl_lock(&ptable_lock);
962 return mm_stage1_lock_unsafe();
963}
964
965void mm_unlock_stage1(struct mm_stage1_locked *lock)
966{
Andrew Scull877ae4b2019-07-02 12:52:33 +0100967 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100968 sl_unlock(&ptable_lock);
969 lock->ptable = NULL;
970}
971
Andrew Scull80871322018-08-06 12:04:09 +0100972/**
Andrew Scull80871322018-08-06 12:04:09 +0100973 * Updates the hypervisor page table such that the given physical address range
974 * is mapped into the address space at the corresponding address range in the
975 * architecture-agnostic mode provided.
976 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100977void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100978 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100979{
Andrew Scull73b89542019-11-20 17:31:26 +0000980 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
981
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100982 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +0000983 arch_mm_mode_to_stage1_attrs(mode), flags,
984 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100985 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100986 }
987
988 return NULL;
989}
990
991/**
992 * Updates the hypervisor table such that the given physical address range is
993 * not mapped in the address space.
994 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100995bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
996 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100997{
Andrew Scull73b89542019-11-20 17:31:26 +0000998 uint32_t mode = MM_MODE_UNMAPPED_MASK;
999
1000 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001001}
1002
1003/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001004 * Defragments the hypervisor page table.
1005 */
1006void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1007{
1008 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
1009}
1010
1011/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001012 * Initialises memory management for the hypervisor itself.
1013 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001014bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001015{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001016 /* Locking is not enabled yet so fake it, */
1017 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1018
Andrew Walbran17eebf92020-02-05 16:35:49 +00001019 dlog_info("text: %#x - %#x\n", pa_addr(layout_text_begin()),
1020 pa_addr(layout_text_end()));
1021 dlog_info("rodata: %#x - %#x\n", pa_addr(layout_rodata_begin()),
1022 pa_addr(layout_rodata_end()));
1023 dlog_info("data: %#x - %#x\n", pa_addr(layout_data_begin()),
1024 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001025
Andrew Scullda3df7f2019-01-05 17:49:27 +00001026 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001027 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001028 return false;
1029 }
1030
Andrew Walbran48699362019-05-20 14:38:00 +01001031 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001032 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001033
1034 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001035 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
1036 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001037
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001038 mm_identity_map(stage1_locked, layout_rodata_begin(),
1039 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001040
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001041 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001042 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001043
Andrew Scullb2910562019-09-17 14:08:27 +01001044 return arch_mm_init(ptable.root);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001045}