blob: 74002c54e52568bc0806bd1f046ac341e0e5a50b [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
Maksims Svecovs134b8f92022-03-04 15:14:09 +000014#include "hf/arch/init.h"
Karl Meakin07a69ab2025-02-07 14:53:19 +000015#include "hf/arch/mm.h"
Maksims Svecovs134b8f92022-03-04 15:14:09 +000016
Andrew Scull877ae4b2019-07-02 12:52:33 +010017#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010018#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010019#include "hf/layout.h"
Andrew Walbran48699362019-05-20 14:38:00 +010020#include "hf/plat/console.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010021#include "hf/static_assert.h"
Karl Meakin25954e32025-02-07 16:12:51 +000022#include "hf/std.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010023
Andrew Walbran2400ed22018-09-27 14:45:58 +010024/**
25 * This file has functions for managing the level 1 and 2 page tables used by
26 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
27 * and then a level 2 mapping per VM. The design assumes that all page tables
28 * contain only 1-1 mappings, aligned on the block boundaries.
29 */
30
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010031/*
32 * For stage 2, the input is an intermediate physical addresses rather than a
33 * virtual address so:
34 */
Andrew Scull80871322018-08-06 12:04:09 +010035static_assert(
36 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
37 "Currently, the same code manages the stage 1 and stage 2 page tables "
38 "which only works if the virtual and intermediate physical addresses "
39 "are the same size. It looks like that assumption might not be holding "
40 "so we need to check that everything is going to be ok.");
41
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010042static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010043static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010044
Andrew Scullda241972019-01-05 18:17:48 +000045static bool mm_stage2_invalidate = false;
46
47/**
48 * After calling this function, modifications to stage-2 page tables will use
49 * break-before-make and invalidate the TLB for the affected range.
50 */
51void mm_vm_enable_invalidation(void)
52{
53 mm_stage2_invalidate = true;
54}
55
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010056/**
Andrew Scull80871322018-08-06 12:04:09 +010057 * Rounds an address down to a page boundary.
58 */
59static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
60{
Karl Meakin25954e32025-02-07 16:12:51 +000061 return align_down(addr, PAGE_SIZE);
Andrew Scull80871322018-08-06 12:04:09 +010062}
63
64/**
65 * Rounds an address up to a page boundary.
66 */
67static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
68{
Karl Meakin25954e32025-02-07 16:12:51 +000069 return align_up(addr, PAGE_SIZE);
Andrew Scull80871322018-08-06 12:04:09 +010070}
71
72/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010073 * Calculates the size of the address space represented by a page table entry at
Karl Meakina3a9f952025-02-08 00:11:16 +000074 * the given level. See also Arm ARM, table D8-15
75 * - `level == 4`: 256 TiB (1 << 48)
76 * - `level == 3`: 512 GiB (1 << 39)
77 * - `level == 2`: 1 GiB (1 << 30)
78 * - `level == 1`: 2 MiB (1 << 21)
79 * - `level == 0`: 4 KiB (1 << 12)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010080 */
Karl Meakin07a69ab2025-02-07 14:53:19 +000081static size_t mm_entry_size(mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010082{
Karl Meakina3a9f952025-02-08 00:11:16 +000083 assert(level <= 4);
Andrew Scull78d6fd92018-09-06 15:08:36 +010084 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010085}
86
87/**
Karl Meakina3a9f952025-02-08 00:11:16 +000088 * Get the start address of the range mapped by the next block of the given
89 * level.
Andrew Scullcae45572018-12-13 15:46:30 +000090 */
91static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
Karl Meakin25954e32025-02-07 16:12:51 +000092 mm_level_t level)
Andrew Scullcae45572018-12-13 15:46:30 +000093{
Karl Meakina3a9f952025-02-08 00:11:16 +000094 assert(level <= 4);
Karl Meakin25954e32025-02-07 16:12:51 +000095 return align_up(addr + 1, mm_entry_size(level));
Andrew Scullcae45572018-12-13 15:46:30 +000096}
97
98/**
Andrew Scull80871322018-08-06 12:04:09 +010099 * For a given address, calculates the maximum (plus one) address that can be
100 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100101 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000102static ptable_addr_t mm_level_end(ptable_addr_t addr, mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100103{
104 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000105
Andrew Scull80871322018-08-06 12:04:09 +0100106 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100107}
108
109/**
Andrew Scull80871322018-08-06 12:04:09 +0100110 * For a given address, calculates the index at which its entry is stored in a
Karl Meakina3a9f952025-02-08 00:11:16 +0000111 * table at the given level. See also Arm ARM, table D8-14
112 * - `level == 4`: bits[51:48]
113 * - `level == 3`: bits[47:39]
114 * - `level == 2`: bits[38:30]
115 * - `level == 1`: bits[29:21]
116 * - `level == 0`: bits[20:12]
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100117 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000118static size_t mm_index(ptable_addr_t addr, mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100119{
Andrew Scull80871322018-08-06 12:04:09 +0100120 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000121
Andrew Scull78d6fd92018-09-06 15:08:36 +0100122 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100123}
124
125/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000126 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100127 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000128static struct mm_page_table *mm_alloc_page_tables(size_t count,
129 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100130{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000131 if (count == 1) {
132 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100133 }
134
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000135 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100136}
137
138/**
Karl Meakina3a9f952025-02-08 00:11:16 +0000139 * Returns the root level in the page table given the flags.
Andrew Scullda3df7f2019-01-05 17:49:27 +0000140 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000141static mm_level_t mm_root_level(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000142{
Karl Meakin0f506a12025-02-08 23:28:45 +0000143 return ptable->stage1 ? arch_mm_stage1_root_level()
144 : arch_mm_stage2_root_level();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000145}
146
147/**
148 * Returns the number of root-level tables given the flags.
149 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000150static uint8_t mm_root_table_count(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000151{
Karl Meakin0f506a12025-02-08 23:28:45 +0000152 return ptable->stage1 ? arch_mm_stage1_root_table_count()
153 : arch_mm_stage2_root_table_count();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000154}
155
156/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000157 * Invalidates the TLB for the given address range.
158 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000159static void mm_invalidate_tlb(const struct mm_ptable *ptable,
160 ptable_addr_t begin, ptable_addr_t end,
Karl Meakin0f506a12025-02-08 23:28:45 +0000161 bool non_secure)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000162{
Karl Meakin0f506a12025-02-08 23:28:45 +0000163 if (ptable->stage1) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000164 arch_mm_invalidate_stage1_range(ptable->id, va_init(begin),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800165 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000166 } else {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000167 arch_mm_invalidate_stage2_range(ptable->id, ipa_init(begin),
Olivier Deprez6f400372022-03-07 09:31:08 +0100168 ipa_init(end), non_secure);
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)
Karl Meakin07a69ab2025-02-07 14:53:19 +0000177static void mm_free_page_pte(pte_t pte, mm_level_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000178{
179 struct mm_page_table *table;
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000180
181 if (!arch_mm_pte_is_table(pte, level)) {
182 return;
183 }
184
185 /* Recursively free any subtables. */
Karl Meakinaacfd4f2025-02-08 19:30:52 +0000186 table = arch_mm_table_from_pte(pte, level);
Karl Meakind64aaf82025-02-08 01:12:55 +0000187 for (size_t i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000188 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000189 }
190
191 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000192 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000193}
194
195/**
David Brazdil711fbe92019-08-06 13:39:58 +0100196 * Returns the first address which cannot be encoded in page tables given by
197 * `flags`. It is the exclusive end of the address space created by the tables.
198 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000199ptable_addr_t mm_ptable_addr_space_end(const struct mm_ptable *ptable)
David Brazdil711fbe92019-08-06 13:39:58 +0100200{
Karl Meakin0f506a12025-02-08 23:28:45 +0000201 return mm_root_table_count(ptable) *
202 mm_entry_size(mm_root_level(ptable));
David Brazdil711fbe92019-08-06 13:39:58 +0100203}
204
205/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000206 * Initialises the given page table.
207 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000208bool mm_ptable_init(struct mm_ptable *ptable, mm_asid_t id, bool stage1,
209 struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000210{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000211 struct mm_page_table *root_tables;
Karl Meakin0f506a12025-02-08 23:28:45 +0000212 uint8_t root_table_count = stage1 ? arch_mm_stage1_root_table_count()
213 : arch_mm_stage2_root_table_count();
214 mm_level_t root_level = stage1 ? arch_mm_stage1_root_level()
215 : arch_mm_stage2_root_level();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000216
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000217 root_tables = mm_alloc_page_tables(root_table_count, ppool);
218 if (root_tables == NULL) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000219 return false;
220 }
221
Karl Meakind64aaf82025-02-08 01:12:55 +0000222 for (size_t i = 0; i < root_table_count; i++) {
223 for (size_t j = 0; j < MM_PTE_PER_PAGE; j++) {
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000224 root_tables[i].entries[j] =
Karl Meakina3a9f952025-02-08 00:11:16 +0000225 arch_mm_absent_pte(root_level - 1);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000226 }
227 }
228
229 /*
230 * TODO: halloc could return a virtual or physical address if mm not
231 * enabled?
232 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000233 ptable->id = id;
Karl Meakin0f506a12025-02-08 23:28:45 +0000234 ptable->root_tables = root_tables;
235 ptable->stage1 = stage1;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000236 return true;
237}
238
239/**
240 * Frees all memory associated with the give page table.
241 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000242static void mm_ptable_fini(const struct mm_ptable *ptable, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000243{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000244 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakin0f506a12025-02-08 23:28:45 +0000245 mm_level_t root_level = mm_root_level(ptable);
246 uint8_t root_table_count = mm_root_table_count(ptable);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000247
Karl Meakind64aaf82025-02-08 01:12:55 +0000248 for (size_t i = 0; i < root_table_count; ++i) {
249 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000250 mm_free_page_pte(root_tables[i].entries[j],
251 root_level - 1, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000252 }
253 }
254
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000255 mpool_add_chunk(ppool, root_tables,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000256 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 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000266static void mm_replace_entry(const struct mm_ptable *ptable,
267 ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Karl Meakin0f506a12025-02-08 23:28:45 +0000268 mm_level_t level, bool non_secure,
269 struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270{
271 pte_t v = *pte;
272
273 /*
274 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000275 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000276 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000277 if ((ptable->stage1 || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800278 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000279 *pte = arch_mm_absent_pte(level);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000280 mm_invalidate_tlb(ptable, begin, begin + mm_entry_size(level),
Karl Meakin0f506a12025-02-08 23:28:45 +0000281 non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000282 }
283
284 /* Assign the new pte. */
285 *pte = new_pte;
286
287 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000288 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000289}
290
291/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100292 * Populates the provided page table entry with a reference to another table if
293 * needed, that is, if it does not yet point to another table.
294 *
295 * Returns a pointer to the table the entry now points to.
296 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000297static struct mm_page_table *mm_populate_table_pte(struct mm_ptable *ptable,
298 ptable_addr_t begin,
299 pte_t *pte, mm_level_t level,
300 bool non_secure,
301 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100303 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100304 pte_t v = *pte;
305 pte_t new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306 size_t inc;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000307 mm_level_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)) {
Karl Meakinaacfd4f2025-02-08 19:30:52 +0000311 return 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. */
Karl Meakind64aaf82025-02-08 01:12:55 +0000333 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100334 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. */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000342 mm_replace_entry(ptable, begin, pte,
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000343 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Karl Meakin0f506a12025-02-08 23:28:45 +0000344 level, non_secure, ppool);
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
Karl Meakin1fd4b822025-02-01 17:13:47 +0000352 * `flags.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)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000359static bool mm_map_level(struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin25954e32025-02-07 16:12:51 +0000360 ptable_addr_t end, mm_attr_t attrs,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000361 struct mm_page_table *child_table, mm_level_t level,
362 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100363{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000364 pte_t *pte = &child_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);
Karl Meakin1fd4b822025-02-01 17:13:47 +0000367 bool commit = flags.commit;
368 bool unmap = flags.unmap;
369 bool non_secure = ((attrs & (1ULL << 57)) != 0);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100370
Andrew Scull265ada92018-07-30 15:19:01 +0100371 /* Cap end so that we don't go over the current level max. */
372 if (end > level_end) {
373 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100374 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100375
376 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100377 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100378 if (unmap ? !arch_mm_pte_is_present(*pte, level)
379 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000380 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100381 /*
382 * If the entry is already mapped with the right
383 * attributes, or already absent in the case of
384 * unmapping, no need to do anything; carry on to the
385 * next entry.
386 */
387 } else if ((end - begin) >= entry_size &&
388 (unmap || arch_mm_is_block_allowed(level)) &&
Karl Meakin25954e32025-02-07 16:12:51 +0000389 is_aligned(begin, entry_size)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100390 /*
391 * If the entire entry is within the region we want to
392 * map, map/unmap the whole entry.
393 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100394 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000395 pte_t new_pte =
396 unmap ? arch_mm_absent_pte(level)
Karl Meakin25954e32025-02-07 16:12:51 +0000397 : arch_mm_block_pte(
398 level, pa_init(begin),
399 attrs);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000400 mm_replace_entry(ptable, begin, pte, new_pte,
Karl Meakin0f506a12025-02-08 23:28:45 +0000401 level, non_secure, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100402 }
403 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100404 /*
405 * If the entry is already a subtable get it; otherwise
406 * replace it with an equivalent subtable and get that.
407 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000408 struct mm_page_table *nt = mm_populate_table_pte(
409 ptable, begin, pte, level, non_secure, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100410 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100411 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100412 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100413
Andrew Walbran6324fc92018-10-03 11:46:43 +0100414 /*
415 * Recurse to map/unmap the appropriate entries within
416 * the subtable.
417 */
Karl Meakin25954e32025-02-07 16:12:51 +0000418 if (!mm_map_level(ptable, begin, end, attrs, nt,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000419 level - 1, flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100420 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100421 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100422 }
423
Karl Meakin25954e32025-02-07 16:12:51 +0000424 begin = mm_start_of_next_block(begin, level);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100425 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100426 }
427
428 return true;
429}
430
431/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000432 * Updates the page table from the root to map the given address range to a
Karl Meakinaac38012025-02-07 23:57:37 +0000433 * physical range using the provided (architecture-specific) attributes.
434 *
435 * Flags:
436 * - `flags.unmap`: unmap the given range instead of mapping it.
437 * - `flags.commit`: the change is only committed if this flag is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100438 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000439static bool mm_ptable_identity_map(struct mm_ptable *ptable, paddr_t pa_begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000440 paddr_t pa_end, mm_attr_t attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000441 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100442{
Karl Meakin0f506a12025-02-08 23:28:45 +0000443 mm_level_t root_level = mm_root_level(ptable);
444 ptable_addr_t ptable_end = mm_ptable_addr_space_end(ptable);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000445 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
Karl Meakinc17ab272025-02-08 03:29:17 +0000446 ptable_addr_t begin = mm_round_down_to_page(pa_addr(pa_begin));
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000447 struct mm_page_table *root_table =
448 &ptable->root_tables[mm_index(begin, root_level)];
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100449
Andrew Scull1ba470e2018-10-31 15:14:31 +0000450 /*
Karl Meakina3a9f952025-02-08 00:11:16 +0000451 * Assert condition to communicate the API constraint of
452 * mm_root_level(), that isn't encoded in the types, to the static
453 * analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000454 */
Karl Meakina3a9f952025-02-08 00:11:16 +0000455 assert(root_level >= 3);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000456
457 /* Cap end to stay within the bounds of the page table. */
458 if (end > ptable_end) {
Karl Meakin30506952025-02-18 18:13:06 +0000459 dlog_verbose(
460 "ptable_map: input range end falls outside of ptable "
461 "address space (%#016lx > %#016lx), capping to ptable "
462 "address space end\n",
463 end, ptable_end);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000464 end = ptable_end;
465 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100466
Karl Meakin30506952025-02-18 18:13:06 +0000467 if (begin >= end) {
468 dlog_verbose(
469 "ptable_map: input range is backwards (%#016lx >= "
470 "%#016lx), request will have no effect\n",
471 begin, end);
472 } else if (pa_addr(pa_begin) >= pa_addr(pa_end)) {
473 dlog_verbose(
474 "ptable_map: input range was backwards (%#016lx >= "
475 "%#016lx), but due to rounding the range %#016lx to "
476 "%#016lx will be mapped\n",
477 begin, end, pa_addr(pa_begin), pa_addr(pa_end));
478 }
479
Karl Meakinaac38012025-02-07 23:57:37 +0000480 while (begin < end) {
481 if (!mm_map_level(ptable, begin, end, attrs, root_table,
482 root_level - 1, flags, ppool)) {
483 return false;
484 }
485 begin = mm_start_of_next_block(begin, root_level);
486 root_table++;
Andrew Walbran58a6e542019-11-19 14:23:15 +0000487 }
488
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800489 /*
490 * All TLB invalidations must be complete already if any entries were
491 * replaced by mm_replace_entry. Sync all page table writes so that code
492 * following this can use them.
493 */
494 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000495
496 return true;
497}
498
Andrew Scull4e83cef2019-11-19 14:17:54 +0000499/*
500 * Prepares the given page table for the given address mapping such that it
501 * will be able to commit the change without failure. It does so by ensuring
502 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100503 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000504 *
505 * In particular, multiple calls to this function will result in the
506 * corresponding calls to commit the changes to succeed.
507 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000508static bool mm_ptable_identity_prepare(struct mm_ptable *ptable,
509 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000510 mm_attr_t attrs, struct mm_flags flags,
Karl Meakind64aaf82025-02-08 01:12:55 +0000511 struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000512{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000513 flags.commit = false;
Karl Meakind64aaf82025-02-08 01:12:55 +0000514 return mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
515 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000516}
517
518/**
519 * Commits the given address mapping to the page table assuming the operation
520 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
521 * ensure this condition.
522 *
523 * Without the table being properly prepared, the commit may only partially
524 * complete if it runs out of memory resulting in an inconsistent state that
525 * isn't handled.
526 *
527 * Since the non-failure assumtion is used in the reasoning about the atomicity
528 * of higher level memory operations, any detected violations result in a panic.
529 *
530 * TODO: remove ppool argument to be sure no changes are made.
531 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000532static void mm_ptable_identity_commit(struct mm_ptable *ptable,
533 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000534 mm_attr_t attrs, struct mm_flags flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000535 struct mpool *ppool)
536{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000537 flags.commit = true;
538 CHECK(mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
539 ppool));
Andrew Scull4e83cef2019-11-19 14:17:54 +0000540}
541
Andrew Walbran58a6e542019-11-19 14:23:15 +0000542/**
543 * Updates the given table such that the given physical address range is mapped
544 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000545 * provided.
546 *
547 * The page table is updated using the separate prepare and commit stages so
548 * that, on failure, a partial update of the address space cannot happen. The
549 * table may be left with extra internal tables but the address space is
550 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000551 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000552static bool mm_ptable_identity_update(struct mm_ptable *ptable,
553 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000554 mm_attr_t attrs, struct mm_flags flags,
Andrew Walbran58a6e542019-11-19 14:23:15 +0000555 struct mpool *ppool)
556{
Karl Meakind64aaf82025-02-08 01:12:55 +0000557 if (!mm_ptable_identity_prepare(ptable, pa_begin, pa_end, attrs, flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000558 ppool)) {
559 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100560 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100561
Karl Meakind64aaf82025-02-08 01:12:55 +0000562 mm_ptable_identity_commit(ptable, pa_begin, pa_end, attrs, flags,
563 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000564
565 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100566}
567
Karl Meakinc88ad412025-02-11 16:04:49 +0000568static void mm_dump_entries(const pte_t *entries, mm_level_t level,
569 uint32_t indent);
570
571static void mm_dump_block_entry(pte_t entry, mm_level_t level, uint32_t indent)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100572{
Karl Meakinc88ad412025-02-11 16:04:49 +0000573 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
574 paddr_t addr = arch_mm_block_from_pte(entry, level);
575
576 if (arch_mm_pte_is_valid(entry, level)) {
577 if (level == 0) {
578 dlog("page {\n");
579 } else {
580 dlog("block {\n");
581 }
582 } else {
583 dlog("invalid_block {\n");
584 }
585
586 indent += 1;
587 {
588 dlog_indent(indent, ".addr = %#016lx\n", pa_addr(addr));
589 dlog_indent(indent, ".attrs = %#016lx\n", attrs);
590 }
591 indent -= 1;
592 dlog_indent(indent, "}");
593}
594
595// NOLINTNEXTLINE(misc-no-recursion)
596static void mm_dump_table_entry(pte_t entry, mm_level_t level, uint32_t indent)
597{
598 dlog("table {\n");
599 indent += 1;
600 {
601 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
Karl Meakinc88ad412025-02-11 16:04:49 +0000602 const struct mm_page_table *child_table =
Karl Meakinaacfd4f2025-02-08 19:30:52 +0000603 arch_mm_table_from_pte(entry, level);
604 paddr_t addr = pa_init((uintpaddr_t)child_table);
Karl Meakinc88ad412025-02-11 16:04:49 +0000605
606 dlog_indent(indent, ".pte = %#016lx,\n", entry);
607 dlog_indent(indent, ".attrs = %#016lx,\n", attrs);
608 dlog_indent(indent, ".addr = %#016lx,\n", pa_addr(addr));
609 dlog_indent(indent, ".entries = ");
610 mm_dump_entries(child_table->entries, level - 1, indent);
611 dlog(",\n");
612 }
613 indent -= 1;
614 dlog_indent(indent, "}");
615}
616
617// NOLINTNEXTLINE(misc-no-recursion)
618static void mm_dump_entry(pte_t entry, mm_level_t level, uint32_t indent)
619{
620 switch (arch_mm_pte_type(entry, level)) {
621 case PTE_TYPE_ABSENT:
622 dlog("absent {}");
623 break;
624 case PTE_TYPE_INVALID_BLOCK:
625 case PTE_TYPE_VALID_BLOCK: {
626 mm_dump_block_entry(entry, level, indent);
627 break;
628 }
629 case PTE_TYPE_TABLE: {
630 mm_dump_table_entry(entry, level, indent);
631 break;
632 }
633 }
634}
635
636// NOLINTNEXTLINE(misc-no-recursion)
637static void mm_dump_entries(const pte_t *entries, mm_level_t level,
638 uint32_t indent)
639{
640 dlog("{\n");
641 indent += 1;
642
Karl Meakind64aaf82025-02-08 01:12:55 +0000643 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Karl Meakinc88ad412025-02-11 16:04:49 +0000644 pte_t entry = entries[i];
Karl Meakin100b0b22025-02-08 00:59:25 +0000645
646 if (arch_mm_pte_is_absent(entry, level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100647 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100648 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100649
Karl Meakinc88ad412025-02-11 16:04:49 +0000650 dlog_indent(indent, "[level = %u, index = %zu] = ", level, i);
651 mm_dump_entry(entry, level, indent);
652 dlog(",\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100653 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000654
655 indent -= 1;
656 dlog_indent(indent, "}");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100657}
658
659/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000660 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100661 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000662static void mm_ptable_dump(const struct mm_ptable *ptable)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100663{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000664 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakin0f506a12025-02-08 23:28:45 +0000665 mm_level_t root_level = mm_root_level(ptable);
666 uint8_t root_table_count = mm_root_table_count(ptable);
Karl Meakinc88ad412025-02-11 16:04:49 +0000667 uint32_t indent = 0;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000668
Karl Meakinc88ad412025-02-11 16:04:49 +0000669 dlog_indent(indent, "mm_ptable {\n");
670 indent += 1;
671 {
672 dlog_indent(indent, ".stage = %s,\n",
Karl Meakin0f506a12025-02-08 23:28:45 +0000673 ptable->stage1 ? "stage1" : "stage2");
Karl Meakinc88ad412025-02-11 16:04:49 +0000674 dlog_indent(indent, ".id = %hu,\n", ptable->id);
675 dlog_indent(indent, ".root_tables = {\n");
676
677 indent += 1;
678 {
679 for (size_t i = 0; i < root_table_count; ++i) {
680 dlog_indent(
681 indent,
682 "[level = %u, index = %zu].entries = ",
683 root_level, i);
684 mm_dump_entries(root_tables[i].entries,
685 root_level - 1, indent);
686 dlog(",\n");
687 }
688 }
689 indent -= 1;
690 dlog_indent(indent, "},\n");
Andrew Scull1ba470e2018-10-31 15:14:31 +0000691 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000692 indent -= 1;
693 dlog_indent(indent, "}\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100694}
695
696/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000697 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800698 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100699 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000700static pte_t mm_merge_table_pte(pte_t table_pte, mm_level_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100701{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100702 struct mm_page_table *table;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000703 mm_attr_t block_attrs;
704 mm_attr_t table_attrs;
705 mm_attr_t combined_attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100706 paddr_t block_address;
707
Karl Meakinaacfd4f2025-02-08 19:30:52 +0000708 table = arch_mm_table_from_pte(table_pte, level);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000709
710 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000711 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100712 }
713
Andrew Scullb6b9b562018-12-21 14:41:35 +0000714 /* Might not be possible to merge the table into a single block. */
715 if (!arch_mm_is_block_allowed(level)) {
716 return table_pte;
717 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000718
Andrew Scullb6b9b562018-12-21 14:41:35 +0000719 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000720 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000721 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100722 combined_attrs =
723 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000724 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000725
Andrew Walbran2400ed22018-09-27 14:45:58 +0100726 return arch_mm_block_pte(level, block_address, combined_attrs);
727}
728
729/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000730 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000731 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100732 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100733// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000734static void mm_ptable_defrag_entry(struct mm_ptable *ptable,
735 ptable_addr_t base_addr, pte_t *entry,
Karl Meakin0f506a12025-02-08 23:28:45 +0000736 mm_level_t level, bool non_secure,
737 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100738{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000739 struct mm_page_table *child_table;
Andrew Scull12122ce2019-11-19 14:21:07 +0000740 bool mergeable;
741 bool base_present;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000742 mm_attr_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800743 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100744
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800745 if (!arch_mm_pte_is_table(*entry, level)) {
746 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100747 }
748
Karl Meakinaacfd4f2025-02-08 19:30:52 +0000749 child_table = arch_mm_table_from_pte(*entry, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100750
Andrew Scull12122ce2019-11-19 14:21:07 +0000751 /* Defrag the first entry in the table and use it as the base entry. */
752 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800753
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000754 mm_ptable_defrag_entry(ptable, base_addr, &(child_table->entries[0]),
Karl Meakin0f506a12025-02-08 23:28:45 +0000755 level - 1, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800756
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000757 base_present =
758 arch_mm_pte_is_present(child_table->entries[0], level - 1);
759 base_attrs = arch_mm_pte_attrs(child_table->entries[0], level - 1);
Andrew Scull12122ce2019-11-19 14:21:07 +0000760
Andrew Walbran2400ed22018-09-27 14:45:58 +0100761 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000762 * Defrag the remaining entries in the table and check whether they are
763 * compatible with the base entry meaning the table can be merged into a
764 * block entry. It assumes addresses are contiguous due to identity
765 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100766 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000767 mergeable = true;
Karl Meakind64aaf82025-02-08 01:12:55 +0000768 for (size_t i = 1; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000769 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800770 ptable_addr_t block_addr =
771 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000772
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000773 mm_ptable_defrag_entry(ptable, block_addr,
774 &(child_table->entries[i]), level - 1,
Karl Meakin0f506a12025-02-08 23:28:45 +0000775 non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800776
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000777 present = arch_mm_pte_is_present(child_table->entries[i],
778 level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100779
Andrew Scull12122ce2019-11-19 14:21:07 +0000780 if (present != base_present) {
781 mergeable = false;
782 continue;
783 }
784
785 if (!present) {
786 continue;
787 }
788
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000789 if (!arch_mm_pte_is_block(child_table->entries[i], level - 1)) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000790 mergeable = false;
791 continue;
792 }
793
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000794 if (arch_mm_pte_attrs(child_table->entries[i], level - 1) !=
Andrew Scull12122ce2019-11-19 14:21:07 +0000795 base_attrs) {
796 mergeable = false;
797 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100798 }
799 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000800
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800801 if (!mergeable) {
802 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000803 }
804
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800805 new_entry = mm_merge_table_pte(*entry, level);
806 if (*entry != new_entry) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000807 mm_replace_entry(ptable, base_addr, entry, (uintptr_t)new_entry,
Karl Meakin0f506a12025-02-08 23:28:45 +0000808 level, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800809 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100810}
811
812/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100813 * Defragments the given page table by converting page table references to
814 * blocks whenever possible.
815 */
Karl Meakin0f506a12025-02-08 23:28:45 +0000816static void mm_ptable_defrag(struct mm_ptable *ptable, bool non_secure,
817 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100818{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000819 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakin0f506a12025-02-08 23:28:45 +0000820 mm_level_t root_level = mm_root_level(ptable);
821 uint8_t root_table_count = mm_root_table_count(ptable);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800822 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100823
824 /*
825 * Loop through each entry in the table. If it points to another table,
826 * check if that table can be replaced by a block or an absent entry.
827 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000828 for (size_t i = 0; i < root_table_count; ++i) {
829 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Karl Meakina3a9f952025-02-08 00:11:16 +0000830 mm_ptable_defrag_entry(
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000831 ptable, block_addr, &root_tables[i].entries[j],
Karl Meakin0f506a12025-02-08 23:28:45 +0000832 root_level - 1, non_secure, ppool);
Karl Meakina3a9f952025-02-08 00:11:16 +0000833 block_addr = mm_start_of_next_block(block_addr,
834 root_level - 1);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000835 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100836 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800837
838 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100839}
840
Karl Meakind127a452025-02-18 16:25:45 +0000841struct mm_get_attrs_state {
842 /**
843 * The attributes the range is mapped with.
844 * Only valid if `got_attrs` is true.
845 */
846 mm_attr_t attrs;
847 /**
848 * The address of the first page that does not match the attributes of
849 * the pages before it in the range.
850 * Only valid if `got_mismatch` is true.
851 */
852 ptable_addr_t mismatch;
853 bool got_attrs : 1;
854 bool got_mismatch : 1;
855};
856
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100857/**
Andrew Scull81e85092018-12-12 12:56:20 +0000858 * Gets the attributes applied to the given range of stage-2 addresses at the
859 * given level.
860 *
861 * The `got_attrs` argument is initially passed as false until `attrs` contains
862 * attributes of the memory region at which point it is passed as true.
863 *
864 * The value returned in `attrs` is only valid if the function returns true.
865 *
866 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100867 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100868// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakind127a452025-02-18 16:25:45 +0000869static struct mm_get_attrs_state mm_ptable_get_attrs_level(
870 const struct mm_page_table *table, ptable_addr_t begin,
871 ptable_addr_t end, mm_level_t level, struct mm_get_attrs_state state)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100872{
Karl Meakind64aaf82025-02-08 01:12:55 +0000873 const pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000874 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100875
Andrew Scull81e85092018-12-12 12:56:20 +0000876 /* Cap end so that we don't go over the current level max. */
877 if (end > level_end) {
878 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100879 }
880
Andrew Scull81e85092018-12-12 12:56:20 +0000881 /* Check that each entry is owned. */
Karl Meakind127a452025-02-18 16:25:45 +0000882 while (begin < end && !state.got_mismatch) {
Karl Meakind9696472025-02-18 14:07:25 +0000883 switch (arch_mm_pte_type(*pte, level)) {
884 case PTE_TYPE_TABLE: {
885 const struct mm_page_table *child_table =
886 arch_mm_table_from_pte(*pte, level);
Karl Meakind127a452025-02-18 16:25:45 +0000887 state = mm_ptable_get_attrs_level(
888 child_table, begin, end, level - 1, state);
Karl Meakind9696472025-02-18 14:07:25 +0000889 break;
890 }
891
892 case PTE_TYPE_ABSENT:
893 case PTE_TYPE_INVALID_BLOCK:
894 case PTE_TYPE_VALID_BLOCK: {
895 mm_attr_t block_attrs = arch_mm_pte_attrs(*pte, level);
896
Karl Meakind127a452025-02-18 16:25:45 +0000897 if (state.got_attrs && block_attrs != state.attrs) {
898 state.mismatch = begin;
899 state.got_mismatch = true;
900 continue;
Andrew Scull81e85092018-12-12 12:56:20 +0000901 }
Karl Meakind127a452025-02-18 16:25:45 +0000902
903 state.got_attrs = true;
904 state.attrs = block_attrs;
Karl Meakind9696472025-02-18 14:07:25 +0000905 break;
906 }
Andrew Scull81e85092018-12-12 12:56:20 +0000907 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100908
Karl Meakin25954e32025-02-07 16:12:51 +0000909 begin = mm_start_of_next_block(begin, level);
Andrew Scull81e85092018-12-12 12:56:20 +0000910 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100911 }
912
Andrew Scullc66a04d2018-12-07 13:41:56 +0000913 /* The entry is a valid block. */
Karl Meakind127a452025-02-18 16:25:45 +0000914 return state;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100915}
916
917/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800918 * Gets the attributes applied to the given range of addresses in the page
919 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000920 *
921 * The value returned in `attrs` is only valid if the function returns true.
922 *
923 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100924 */
Karl Meakind127a452025-02-18 16:25:45 +0000925static struct mm_get_attrs_state mm_get_attrs(const struct mm_ptable *ptable,
926 ptable_addr_t begin,
927 ptable_addr_t end)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100928{
Karl Meakin0f506a12025-02-08 23:28:45 +0000929 mm_level_t root_level = mm_root_level(ptable);
930 ptable_addr_t ptable_end = mm_ptable_addr_space_end(ptable);
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000931 struct mm_page_table *root_table;
Karl Meakind127a452025-02-18 16:25:45 +0000932 struct mm_get_attrs_state state = {0};
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100933
Karl Meakin30506952025-02-18 18:13:06 +0000934 if (begin >= end) {
935 dlog_verbose(
936 "mm_get: input range is backwards (%#016lx >= "
937 "%#016lx)\n",
938 begin, end);
939 }
940
Andrew Scull81e85092018-12-12 12:56:20 +0000941 begin = mm_round_down_to_page(begin);
942 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100943
Andrew Scull81e85092018-12-12 12:56:20 +0000944 /* Fail if the addresses are out of range. */
945 if (end > ptable_end) {
Karl Meakind127a452025-02-18 16:25:45 +0000946 return state;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000947 }
948
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000949 root_table = &ptable->root_tables[mm_index(begin, root_level)];
Karl Meakind127a452025-02-18 16:25:45 +0000950 while (begin < end && !state.got_mismatch) {
951 state = mm_ptable_get_attrs_level(root_table, begin, end,
952 root_level - 1, state);
Andrew Scull81e85092018-12-12 12:56:20 +0000953
Karl Meakin25954e32025-02-07 16:12:51 +0000954 begin = mm_start_of_next_block(begin, root_level);
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000955 root_table++;
Andrew Scull81e85092018-12-12 12:56:20 +0000956 }
957
Karl Meakind127a452025-02-18 16:25:45 +0000958 return state;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100959}
960
Karl Meakin07a69ab2025-02-07 14:53:19 +0000961bool mm_vm_init(struct mm_ptable *ptable, mm_asid_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100962{
Karl Meakin0f506a12025-02-08 23:28:45 +0000963 return mm_ptable_init(ptable, id, false, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100964}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100965
Karl Meakind64aaf82025-02-08 01:12:55 +0000966void mm_vm_fini(const struct mm_ptable *ptable, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000967{
Karl Meakin0f506a12025-02-08 23:28:45 +0000968 mm_ptable_fini(ptable, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000969}
970
971/**
Andrew Scull73b89542019-11-20 17:31:26 +0000972 * Selects flags to pass to the page table manipulation operation based on the
973 * mapping mode.
974 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000975static struct mm_flags mm_mode_to_flags(mm_mode_t mode)
Andrew Scull73b89542019-11-20 17:31:26 +0000976{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000977 struct mm_flags flags = {0};
978
Andrew Scull73b89542019-11-20 17:31:26 +0000979 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
Karl Meakin1fd4b822025-02-01 17:13:47 +0000980 flags.unmap = true;
Andrew Scull73b89542019-11-20 17:31:26 +0000981 }
982
Karl Meakin1fd4b822025-02-01 17:13:47 +0000983 return flags;
Andrew Scull73b89542019-11-20 17:31:26 +0000984}
985
986/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000987 * See `mm_ptable_identity_prepare`.
988 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800989 * This must be called before `mm_identity_commit` for the same mapping.
990 *
991 * Returns true on success, or false if the update would fail.
992 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000993bool mm_identity_prepare(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000994 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800995{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000996 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000997
Karl Meakin0f506a12025-02-08 23:28:45 +0000998 assert(ptable->stage1);
Karl Meakind64aaf82025-02-08 01:12:55 +0000999 return mm_ptable_identity_prepare(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -08001000 arch_mm_mode_to_stage1_attrs(mode),
1001 flags, ppool);
1002}
1003
1004/**
1005 * See `mm_ptable_identity_commit`.
1006 *
1007 * `mm_identity_prepare` must be called before this for the same mapping.
1008 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001009void *mm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001010 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -08001011{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001012 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +00001013
Karl Meakin0f506a12025-02-08 23:28:45 +00001014 assert(ptable->stage1);
Karl Meakind64aaf82025-02-08 01:12:55 +00001015 mm_ptable_identity_commit(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -08001016 arch_mm_mode_to_stage1_attrs(mode), flags,
1017 ppool);
1018 return ptr_from_va(va_from_pa(begin));
1019}
1020
1021/**
1022 * See `mm_ptable_identity_prepare`.
1023 *
Andrew Scull4e83cef2019-11-19 14:17:54 +00001024 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +00001025 *
1026 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +00001027 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001028bool mm_vm_identity_prepare(struct mm_ptable *ptable, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001029 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +00001030{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001031 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +00001032
Karl Meakind64aaf82025-02-08 01:12:55 +00001033 return mm_ptable_identity_prepare(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +00001034 arch_mm_mode_to_stage2_attrs(mode),
1035 flags, ppool);
1036}
1037
1038/**
1039 * See `mm_ptable_identity_commit`.
1040 *
1041 * `mm_vm_identity_prepare` must be called before this for the same mapping.
1042 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001043void mm_vm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001044 mm_mode_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +00001045{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001046 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +00001047
Karl Meakind64aaf82025-02-08 01:12:55 +00001048 mm_ptable_identity_commit(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +00001049 arch_mm_mode_to_stage2_attrs(mode), flags,
1050 ppool);
1051
1052 if (ipa != NULL) {
1053 *ipa = ipa_from_pa(begin);
1054 }
1055}
1056
1057/**
Andrew Scull80871322018-08-06 12:04:09 +01001058 * Updates a VM's page table such that the given physical address range is
1059 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +01001060 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +00001061 *
1062 * mm_vm_defrag should always be called after a series of page table updates,
1063 * whether they succeed or fail. This is because on failure extra page table
1064 * entries may have been allocated and then not used, while on success it may be
1065 * possible to compact the page table by merging several entries into a block.
1066 *
1067 * Returns true on success, or false if the update failed and no changes were
1068 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001069 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001070bool mm_vm_identity_map(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001071 mm_mode_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001072{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001073 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001074 bool success = mm_ptable_identity_update(
Karl Meakind64aaf82025-02-08 01:12:55 +00001075 ptable, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
Andrew Scullda3df7f2019-01-05 17:49:27 +00001076 ppool);
Andrew Scull80871322018-08-06 12:04:09 +01001077
1078 if (success && ipa != NULL) {
1079 *ipa = ipa_from_pa(begin);
1080 }
1081
1082 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001083}
1084
1085/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001086 * Updates the VM's table such that the given physical address range has no
1087 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +01001088 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001089bool mm_vm_unmap(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001090 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001091{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001092 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001093
Karl Meakind64aaf82025-02-08 01:12:55 +00001094 return mm_vm_identity_map(ptable, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +01001095}
1096
1097/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001098 * Write the given page table of a VM to the debug log.
1099 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001100void mm_vm_dump(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001101{
Karl Meakin0f506a12025-02-08 23:28:45 +00001102 mm_ptable_dump(ptable);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001103}
1104
1105/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001106 * Defragments a stage1 page table.
1107 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001108void mm_stage1_defrag(struct mm_ptable *ptable, struct mpool *ppool)
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001109{
Karl Meakin0f506a12025-02-08 23:28:45 +00001110 assert(ptable->stage1);
1111 mm_ptable_defrag(ptable, false, ppool);
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001112}
1113
1114/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001115 * Defragments the VM page table.
1116 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001117void mm_vm_defrag(struct mm_ptable *ptable, struct mpool *ppool,
1118 bool non_secure)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001119{
Karl Meakin0f506a12025-02-08 23:28:45 +00001120 mm_ptable_defrag(ptable, non_secure, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001121}
1122
1123/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +01001124 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +00001125 * are mapped with the same mode.
1126 *
1127 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001128 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001129bool mm_vm_get_mode(const struct mm_ptable *ptable, ipaddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001130 ipaddr_t end, mm_mode_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001131{
Karl Meakind127a452025-02-18 16:25:45 +00001132 struct mm_get_attrs_state ret;
1133 bool success;
Andrew Scull81e85092018-12-12 12:56:20 +00001134
Karl Meakind127a452025-02-18 16:25:45 +00001135 ret = mm_get_attrs(ptable, ipa_addr(begin), ipa_addr(end));
1136 success = ret.got_attrs && !ret.got_mismatch;
1137
1138 if (success && mode != NULL) {
1139 *mode = arch_mm_stage2_attrs_to_mode(ret.attrs);
Andrew Scull81e85092018-12-12 12:56:20 +00001140 }
1141
Karl Meakind127a452025-02-18 16:25:45 +00001142 return success;
Andrew Scull80871322018-08-06 12:04:09 +01001143}
1144
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001145/**
1146 * Gets the mode of the given range of virtual addresses if they
1147 * are mapped with the same mode.
1148 *
1149 * Returns true if the range is mapped with the same mode and false otherwise.
1150 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001151bool mm_get_mode(const struct mm_ptable *ptable, vaddr_t begin, vaddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001152 mm_mode_t *mode)
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001153{
Karl Meakind127a452025-02-18 16:25:45 +00001154 struct mm_get_attrs_state ret;
1155 bool success;
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001156
Karl Meakin0f506a12025-02-08 23:28:45 +00001157 assert(ptable->stage1);
Karl Meakind127a452025-02-18 16:25:45 +00001158
1159 ret = mm_get_attrs(ptable, va_addr(begin), va_addr(end));
1160 success = ret.got_attrs && !ret.got_mismatch;
1161
1162 if (success && mode != NULL) {
1163 *mode = arch_mm_stage1_attrs_to_mode(ret.attrs);
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001164 }
1165
Karl Meakind127a452025-02-18 16:25:45 +00001166 return success;
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001167}
1168
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001169static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1170{
1171 return (struct mm_stage1_locked){.ptable = &ptable};
1172}
1173
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001174struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1175{
1176 return (struct mm_stage1_locked){.ptable = ptable};
1177}
1178
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001179struct mm_stage1_locked mm_lock_stage1(void)
1180{
1181 sl_lock(&ptable_lock);
1182 return mm_stage1_lock_unsafe();
1183}
1184
1185void mm_unlock_stage1(struct mm_stage1_locked *lock)
1186{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001187 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001188 sl_unlock(&ptable_lock);
1189 lock->ptable = NULL;
1190}
1191
Andrew Scull80871322018-08-06 12:04:09 +01001192/**
Andrew Scull80871322018-08-06 12:04:09 +01001193 * Updates the hypervisor page table such that the given physical address range
1194 * is mapped into the address space at the corresponding address range in the
1195 * architecture-agnostic mode provided.
1196 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001197void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001198 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001199{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001200 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +00001201
Karl Meakin0f506a12025-02-08 23:28:45 +00001202 assert(stage1_locked.ptable->stage1);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001203 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001204 arch_mm_mode_to_stage1_attrs(mode), flags,
1205 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001206 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001207 }
1208
1209 return NULL;
1210}
1211
1212/**
1213 * Updates the hypervisor table such that the given physical address range is
1214 * not mapped in the address space.
1215 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001216bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1217 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001218{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001219 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001220
1221 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001222}
1223
1224/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001225 * Defragments the hypervisor page table.
1226 */
1227void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1228{
Karl Meakin0f506a12025-02-08 23:28:45 +00001229 assert(stage1_locked.ptable->stage1);
1230 mm_ptable_defrag(stage1_locked.ptable, false, ppool);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001231}
1232
1233/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001234 * Initialises memory management for the hypervisor itself.
1235 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001236bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001237{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001238 /* Locking is not enabled yet so fake it, */
1239 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1240
Karl Meakine8937d92024-03-19 16:04:25 +00001241 dlog_info("text: %#lx - %#lx\n", pa_addr(layout_text_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001242 pa_addr(layout_text_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001243 dlog_info("rodata: %#lx - %#lx\n", pa_addr(layout_rodata_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001244 pa_addr(layout_rodata_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001245 dlog_info("data: %#lx - %#lx\n", pa_addr(layout_data_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001246 pa_addr(layout_data_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001247 dlog_info("stacks: %#lx - %#lx\n", pa_addr(layout_stacks_begin()),
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001248 pa_addr(layout_stacks_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001249
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001250 /* ASID 0 is reserved for use by the hypervisor. */
Karl Meakin0f506a12025-02-08 23:28:45 +00001251 if (!mm_ptable_init(&ptable, 0, true, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001252 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001253 return false;
1254 }
1255
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001256 /* Initialize arch_mm before calling below mapping routines */
Karl Meakine1aeb1d2025-02-08 00:35:14 +00001257 if (!arch_mm_init(&ptable)) {
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001258 return false;
1259 }
1260
Andrew Walbran48699362019-05-20 14:38:00 +01001261 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001262 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001263
1264 /* Map each section. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001265 CHECK(mm_identity_map(stage1_locked, layout_text_begin(),
1266 layout_text_end(), MM_MODE_X, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001267
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001268 CHECK(mm_identity_map(stage1_locked, layout_rodata_begin(),
1269 layout_rodata_end(), MM_MODE_R, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001270
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001271 CHECK(mm_identity_map(stage1_locked, layout_data_begin(),
1272 layout_data_end(), MM_MODE_R | MM_MODE_W,
1273 ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001274
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001275 /* Arch-specific stack mapping. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001276 CHECK(arch_stack_mm_init(stage1_locked, ppool));
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001277
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001278 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001279}