blob: d361b71cc1c66a30bca89454028467a9756ef92d [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 Scull4e5f8142018-10-12 14:37:19 +010057 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010058 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010059static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010060{
61 return ptr_from_va(va_from_pa(pa));
62}
63
64/**
Andrew Scull80871322018-08-06 12:04:09 +010065 * Rounds an address down to a page boundary.
66 */
67static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
68{
Karl Meakin25954e32025-02-07 16:12:51 +000069 return align_down(addr, PAGE_SIZE);
Andrew Scull80871322018-08-06 12:04:09 +010070}
71
72/**
73 * Rounds an address up to a page boundary.
74 */
75static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
76{
Karl Meakin25954e32025-02-07 16:12:51 +000077 return align_up(addr, PAGE_SIZE);
Andrew Scull80871322018-08-06 12:04:09 +010078}
79
80/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010081 * Calculates the size of the address space represented by a page table entry at
Karl Meakina3a9f952025-02-08 00:11:16 +000082 * the given level. See also Arm ARM, table D8-15
83 * - `level == 4`: 256 TiB (1 << 48)
84 * - `level == 3`: 512 GiB (1 << 39)
85 * - `level == 2`: 1 GiB (1 << 30)
86 * - `level == 1`: 2 MiB (1 << 21)
87 * - `level == 0`: 4 KiB (1 << 12)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010088 */
Karl Meakin07a69ab2025-02-07 14:53:19 +000089static size_t mm_entry_size(mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010090{
Karl Meakina3a9f952025-02-08 00:11:16 +000091 assert(level <= 4);
Andrew Scull78d6fd92018-09-06 15:08:36 +010092 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010093}
94
95/**
Karl Meakina3a9f952025-02-08 00:11:16 +000096 * Get the start address of the range mapped by the next block of the given
97 * level.
Andrew Scullcae45572018-12-13 15:46:30 +000098 */
99static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
Karl Meakin25954e32025-02-07 16:12:51 +0000100 mm_level_t level)
Andrew Scullcae45572018-12-13 15:46:30 +0000101{
Karl Meakina3a9f952025-02-08 00:11:16 +0000102 assert(level <= 4);
Karl Meakin25954e32025-02-07 16:12:51 +0000103 return align_up(addr + 1, mm_entry_size(level));
Andrew Scullcae45572018-12-13 15:46:30 +0000104}
105
106/**
Andrew Scull80871322018-08-06 12:04:09 +0100107 * For a given address, calculates the maximum (plus one) address that can be
108 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100109 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000110static ptable_addr_t mm_level_end(ptable_addr_t addr, mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100111{
112 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000113
Andrew Scull80871322018-08-06 12:04:09 +0100114 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100115}
116
117/**
Andrew Scull80871322018-08-06 12:04:09 +0100118 * For a given address, calculates the index at which its entry is stored in a
Karl Meakina3a9f952025-02-08 00:11:16 +0000119 * table at the given level. See also Arm ARM, table D8-14
120 * - `level == 4`: bits[51:48]
121 * - `level == 3`: bits[47:39]
122 * - `level == 2`: bits[38:30]
123 * - `level == 1`: bits[29:21]
124 * - `level == 0`: bits[20:12]
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100125 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000126static size_t mm_index(ptable_addr_t addr, mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100127{
Andrew Scull80871322018-08-06 12:04:09 +0100128 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000129
Andrew Scull78d6fd92018-09-06 15:08:36 +0100130 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100131}
132
133/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000134 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100135 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000136static struct mm_page_table *mm_alloc_page_tables(size_t count,
137 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100138{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000139 if (count == 1) {
140 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100141 }
142
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000143 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100144}
145
146/**
Karl Meakina3a9f952025-02-08 00:11:16 +0000147 * Returns the root level in the page table given the flags.
Andrew Scullda3df7f2019-01-05 17:49:27 +0000148 */
Karl Meakina3a9f952025-02-08 00:11:16 +0000149static mm_level_t mm_root_level(struct mm_flags flags)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000150{
Karl Meakina3a9f952025-02-08 00:11:16 +0000151 return flags.stage1 ? arch_mm_stage1_root_level()
152 : arch_mm_stage2_root_level();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000153}
154
155/**
156 * Returns the number of root-level tables given the flags.
157 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000158static uint8_t mm_root_table_count(struct mm_flags flags)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000159{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000160 return flags.stage1 ? arch_mm_stage1_root_table_count()
161 : arch_mm_stage2_root_table_count();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000162}
163
164/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000165 * Invalidates the TLB for the given address range.
166 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000167static void mm_invalidate_tlb(const struct mm_ptable *ptable,
168 ptable_addr_t begin, ptable_addr_t end,
169 struct mm_flags flags, bool non_secure)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000170{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000171 if (flags.stage1) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000172 arch_mm_invalidate_stage1_range(ptable->id, va_init(begin),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800173 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000174 } else {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000175 arch_mm_invalidate_stage2_range(ptable->id, ipa_init(begin),
Olivier Deprez6f400372022-03-07 09:31:08 +0100176 ipa_init(end), non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000177 }
178}
179
180/**
181 * Frees all page-table-related memory associated with the given pte at the
182 * given level, including any subtables recursively.
183 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100184// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin07a69ab2025-02-07 14:53:19 +0000185static void mm_free_page_pte(pte_t pte, mm_level_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000186{
187 struct mm_page_table *table;
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000188
189 if (!arch_mm_pte_is_table(pte, level)) {
190 return;
191 }
192
193 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000194 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Karl Meakind64aaf82025-02-08 01:12:55 +0000195 for (size_t i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000196 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000197 }
198
199 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000200 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000201}
202
203/**
David Brazdil711fbe92019-08-06 13:39:58 +0100204 * Returns the first address which cannot be encoded in page tables given by
205 * `flags`. It is the exclusive end of the address space created by the tables.
206 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000207ptable_addr_t mm_ptable_addr_space_end(struct mm_flags flags)
David Brazdil711fbe92019-08-06 13:39:58 +0100208{
Karl Meakina3a9f952025-02-08 00:11:16 +0000209 return mm_root_table_count(flags) * mm_entry_size(mm_root_level(flags));
David Brazdil711fbe92019-08-06 13:39:58 +0100210}
211
212/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000213 * Initialises the given page table.
214 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000215bool mm_ptable_init(struct mm_ptable *ptable, mm_asid_t id,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000216 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000217{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000218 struct mm_page_table *root_tables;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000219 uint8_t root_table_count = mm_root_table_count(flags);
Karl Meakina3a9f952025-02-08 00:11:16 +0000220 mm_level_t root_level = mm_root_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000221
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000222 root_tables = mm_alloc_page_tables(root_table_count, ppool);
223 if (root_tables == NULL) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000224 return false;
225 }
226
Karl Meakind64aaf82025-02-08 01:12:55 +0000227 for (size_t i = 0; i < root_table_count; i++) {
228 for (size_t j = 0; j < MM_PTE_PER_PAGE; j++) {
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000229 root_tables[i].entries[j] =
Karl Meakina3a9f952025-02-08 00:11:16 +0000230 arch_mm_absent_pte(root_level - 1);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000231 }
232 }
233
234 /*
235 * TODO: halloc could return a virtual or physical address if mm not
236 * enabled?
237 */
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000238 ptable->root_tables = root_tables;
Karl Meakind64aaf82025-02-08 01:12:55 +0000239 ptable->id = id;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000240 return true;
241}
242
243/**
244 * Frees all memory associated with the give page table.
245 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000246static void mm_ptable_fini(const struct mm_ptable *ptable,
247 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000248{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000249 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakina3a9f952025-02-08 00:11:16 +0000250 mm_level_t root_level = mm_root_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000251 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000252
Karl Meakind64aaf82025-02-08 01:12:55 +0000253 for (size_t i = 0; i < root_table_count; ++i) {
254 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000255 mm_free_page_pte(root_tables[i].entries[j],
256 root_level - 1, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000257 }
258 }
259
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000260 mpool_add_chunk(ppool, root_tables,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000261 sizeof(struct mm_page_table) * root_table_count);
262}
263
264/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000265 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000266 * are valid, it performs a break-before-make sequence where it first writes an
267 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
268 * This is to prevent cases where CPUs have different 'valid' values in their
269 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000271static void mm_replace_entry(const struct mm_ptable *ptable,
272 ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000273 mm_level_t level, struct mm_flags flags,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000274 bool non_secure, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000275{
276 pte_t v = *pte;
277
278 /*
279 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000280 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000281 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000282 if ((flags.stage1 || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800283 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000284 *pte = arch_mm_absent_pte(level);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000285 mm_invalidate_tlb(ptable, begin, begin + mm_entry_size(level),
286 flags, non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000287 }
288
289 /* Assign the new pte. */
290 *pte = new_pte;
291
292 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000293 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000294}
295
296/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100297 * Populates the provided page table entry with a reference to another table if
298 * needed, that is, if it does not yet point to another table.
299 *
300 * Returns a pointer to the table the entry now points to.
301 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000302static struct mm_page_table *mm_populate_table_pte(
303 const struct mm_ptable *ptable, ptable_addr_t begin, pte_t *pte,
304 mm_level_t level, struct mm_flags flags, bool non_secure,
305 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100307 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100308 pte_t v = *pte;
309 pte_t new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100310 size_t inc;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000311 mm_level_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100312
313 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100314 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000315 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100316 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100317
318 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000319 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100320 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000321 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100322 return NULL;
323 }
324
325 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100326 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100327 inc = mm_entry_size(level_below);
328 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000329 arch_mm_block_from_pte(v, level),
330 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100331 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100332 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100333 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100334 }
335
336 /* Initialise entries in the new table. */
Karl Meakind64aaf82025-02-08 01:12:55 +0000337 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100338 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100339 new_pte += inc;
340 }
341
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000342 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100343 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000344
345 /* Replace the pte entry, doing a break-before-make if needed. */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000346 mm_replace_entry(ptable, begin, pte,
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000347 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000348 level, flags, non_secure, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100349
350 return ntable;
351}
352
353/**
Andrew Scull80871322018-08-06 12:04:09 +0100354 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100355 * physical range using the provided (architecture-specific) attributes. Or if
Karl Meakin1fd4b822025-02-01 17:13:47 +0000356 * `flags.unmap` is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100357 *
358 * This function calls itself recursively if it needs to update additional
359 * levels, but the recursion is bound by the maximum number of levels in a page
360 * table.
361 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100362// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000363static bool mm_map_level(struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin25954e32025-02-07 16:12:51 +0000364 ptable_addr_t end, mm_attr_t attrs,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000365 struct mm_page_table *child_table, mm_level_t level,
366 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100367{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000368 pte_t *pte = &child_table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100369 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100370 size_t entry_size = mm_entry_size(level);
Karl Meakin1fd4b822025-02-01 17:13:47 +0000371 bool commit = flags.commit;
372 bool unmap = flags.unmap;
373 bool non_secure = ((attrs & (1ULL << 57)) != 0);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100374
Andrew Scull265ada92018-07-30 15:19:01 +0100375 /* Cap end so that we don't go over the current level max. */
376 if (end > level_end) {
377 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100378 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100379
380 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100381 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100382 if (unmap ? !arch_mm_pte_is_present(*pte, level)
383 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000384 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100385 /*
386 * If the entry is already mapped with the right
387 * attributes, or already absent in the case of
388 * unmapping, no need to do anything; carry on to the
389 * next entry.
390 */
391 } else if ((end - begin) >= entry_size &&
392 (unmap || arch_mm_is_block_allowed(level)) &&
Karl Meakin25954e32025-02-07 16:12:51 +0000393 is_aligned(begin, entry_size)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100394 /*
395 * If the entire entry is within the region we want to
396 * map, map/unmap the whole entry.
397 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100398 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000399 pte_t new_pte =
400 unmap ? arch_mm_absent_pte(level)
Karl Meakin25954e32025-02-07 16:12:51 +0000401 : arch_mm_block_pte(
402 level, pa_init(begin),
403 attrs);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000404 mm_replace_entry(ptable, begin, pte, new_pte,
405 level, flags, non_secure,
406 ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100407 }
408 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100409 /*
410 * If the entry is already a subtable get it; otherwise
411 * replace it with an equivalent subtable and get that.
412 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000413 struct mm_page_table *nt =
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000414 mm_populate_table_pte(ptable, begin, pte, level,
415 flags, non_secure, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100416 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100417 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100418 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100419
Andrew Walbran6324fc92018-10-03 11:46:43 +0100420 /*
421 * Recurse to map/unmap the appropriate entries within
422 * the subtable.
423 */
Karl Meakin25954e32025-02-07 16:12:51 +0000424 if (!mm_map_level(ptable, begin, end, attrs, nt,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000425 level - 1, flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100426 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100427 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100428 }
429
Karl Meakin25954e32025-02-07 16:12:51 +0000430 begin = mm_start_of_next_block(begin, level);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100431 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100432 }
433
434 return true;
435}
436
437/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000438 * Updates the page table from the root to map the given address range to a
Karl Meakinaac38012025-02-07 23:57:37 +0000439 * physical range using the provided (architecture-specific) attributes.
440 *
441 * Flags:
442 * - `flags.unmap`: unmap the given range instead of mapping it.
443 * - `flags.commit`: the change is only committed if this flag is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100444 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000445static bool mm_ptable_identity_map(struct mm_ptable *ptable, paddr_t pa_begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000446 paddr_t pa_end, mm_attr_t attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000447 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100448{
Karl Meakina3a9f952025-02-08 00:11:16 +0000449 mm_level_t root_level = mm_root_level(flags);
David Brazdil711fbe92019-08-06 13:39:58 +0100450 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000451 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
Karl Meakinc17ab272025-02-08 03:29:17 +0000452 ptable_addr_t begin = mm_round_down_to_page(pa_addr(pa_begin));
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000453 struct mm_page_table *root_table =
454 &ptable->root_tables[mm_index(begin, root_level)];
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100455
Andrew Scull1ba470e2018-10-31 15:14:31 +0000456 /*
Karl Meakina3a9f952025-02-08 00:11:16 +0000457 * Assert condition to communicate the API constraint of
458 * mm_root_level(), that isn't encoded in the types, to the static
459 * analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000460 */
Karl Meakina3a9f952025-02-08 00:11:16 +0000461 assert(root_level >= 3);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000462
463 /* Cap end to stay within the bounds of the page table. */
464 if (end > ptable_end) {
465 end = ptable_end;
466 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100467
Karl Meakinaac38012025-02-07 23:57:37 +0000468 while (begin < end) {
469 if (!mm_map_level(ptable, begin, end, attrs, root_table,
470 root_level - 1, flags, ppool)) {
471 return false;
472 }
473 begin = mm_start_of_next_block(begin, root_level);
474 root_table++;
Andrew Walbran58a6e542019-11-19 14:23:15 +0000475 }
476
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800477 /*
478 * All TLB invalidations must be complete already if any entries were
479 * replaced by mm_replace_entry. Sync all page table writes so that code
480 * following this can use them.
481 */
482 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000483
484 return true;
485}
486
Andrew Scull4e83cef2019-11-19 14:17:54 +0000487/*
488 * Prepares the given page table for the given address mapping such that it
489 * will be able to commit the change without failure. It does so by ensuring
490 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100491 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000492 *
493 * In particular, multiple calls to this function will result in the
494 * corresponding calls to commit the changes to succeed.
495 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000496static bool mm_ptable_identity_prepare(struct mm_ptable *ptable,
497 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000498 mm_attr_t attrs, struct mm_flags flags,
Karl Meakind64aaf82025-02-08 01:12:55 +0000499 struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000500{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000501 flags.commit = false;
Karl Meakind64aaf82025-02-08 01:12:55 +0000502 return mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
503 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000504}
505
506/**
507 * Commits the given address mapping to the page table assuming the operation
508 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
509 * ensure this condition.
510 *
511 * Without the table being properly prepared, the commit may only partially
512 * complete if it runs out of memory resulting in an inconsistent state that
513 * isn't handled.
514 *
515 * Since the non-failure assumtion is used in the reasoning about the atomicity
516 * of higher level memory operations, any detected violations result in a panic.
517 *
518 * TODO: remove ppool argument to be sure no changes are made.
519 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000520static void mm_ptable_identity_commit(struct mm_ptable *ptable,
521 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000522 mm_attr_t attrs, struct mm_flags flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000523 struct mpool *ppool)
524{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000525 flags.commit = true;
526 CHECK(mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
527 ppool));
Andrew Scull4e83cef2019-11-19 14:17:54 +0000528}
529
Andrew Walbran58a6e542019-11-19 14:23:15 +0000530/**
531 * Updates the given table such that the given physical address range is mapped
532 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000533 * provided.
534 *
535 * The page table is updated using the separate prepare and commit stages so
536 * that, on failure, a partial update of the address space cannot happen. The
537 * table may be left with extra internal tables but the address space is
538 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000539 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000540static bool mm_ptable_identity_update(struct mm_ptable *ptable,
541 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000542 mm_attr_t attrs, struct mm_flags flags,
Andrew Walbran58a6e542019-11-19 14:23:15 +0000543 struct mpool *ppool)
544{
Karl Meakind64aaf82025-02-08 01:12:55 +0000545 if (!mm_ptable_identity_prepare(ptable, pa_begin, pa_end, attrs, flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000546 ppool)) {
547 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100548 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100549
Karl Meakind64aaf82025-02-08 01:12:55 +0000550 mm_ptable_identity_commit(ptable, pa_begin, pa_end, attrs, flags,
551 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000552
553 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100554}
555
Karl Meakinc88ad412025-02-11 16:04:49 +0000556static void mm_dump_entries(const pte_t *entries, mm_level_t level,
557 uint32_t indent);
558
559static void mm_dump_block_entry(pte_t entry, mm_level_t level, uint32_t indent)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100560{
Karl Meakinc88ad412025-02-11 16:04:49 +0000561 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
562 paddr_t addr = arch_mm_block_from_pte(entry, level);
563
564 if (arch_mm_pte_is_valid(entry, level)) {
565 if (level == 0) {
566 dlog("page {\n");
567 } else {
568 dlog("block {\n");
569 }
570 } else {
571 dlog("invalid_block {\n");
572 }
573
574 indent += 1;
575 {
576 dlog_indent(indent, ".addr = %#016lx\n", pa_addr(addr));
577 dlog_indent(indent, ".attrs = %#016lx\n", attrs);
578 }
579 indent -= 1;
580 dlog_indent(indent, "}");
581}
582
583// NOLINTNEXTLINE(misc-no-recursion)
584static void mm_dump_table_entry(pte_t entry, mm_level_t level, uint32_t indent)
585{
586 dlog("table {\n");
587 indent += 1;
588 {
589 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
590 paddr_t addr = arch_mm_table_from_pte(entry, level);
591 const struct mm_page_table *child_table =
592 mm_page_table_from_pa(addr);
593
594 dlog_indent(indent, ".pte = %#016lx,\n", entry);
595 dlog_indent(indent, ".attrs = %#016lx,\n", attrs);
596 dlog_indent(indent, ".addr = %#016lx,\n", pa_addr(addr));
597 dlog_indent(indent, ".entries = ");
598 mm_dump_entries(child_table->entries, level - 1, indent);
599 dlog(",\n");
600 }
601 indent -= 1;
602 dlog_indent(indent, "}");
603}
604
605// NOLINTNEXTLINE(misc-no-recursion)
606static void mm_dump_entry(pte_t entry, mm_level_t level, uint32_t indent)
607{
608 switch (arch_mm_pte_type(entry, level)) {
609 case PTE_TYPE_ABSENT:
610 dlog("absent {}");
611 break;
612 case PTE_TYPE_INVALID_BLOCK:
613 case PTE_TYPE_VALID_BLOCK: {
614 mm_dump_block_entry(entry, level, indent);
615 break;
616 }
617 case PTE_TYPE_TABLE: {
618 mm_dump_table_entry(entry, level, indent);
619 break;
620 }
621 }
622}
623
624// NOLINTNEXTLINE(misc-no-recursion)
625static void mm_dump_entries(const pte_t *entries, mm_level_t level,
626 uint32_t indent)
627{
628 dlog("{\n");
629 indent += 1;
630
Karl Meakind64aaf82025-02-08 01:12:55 +0000631 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Karl Meakinc88ad412025-02-11 16:04:49 +0000632 pte_t entry = entries[i];
Karl Meakin100b0b22025-02-08 00:59:25 +0000633
634 if (arch_mm_pte_is_absent(entry, level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100635 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100636 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100637
Karl Meakinc88ad412025-02-11 16:04:49 +0000638 dlog_indent(indent, "[level = %u, index = %zu] = ", level, i);
639 mm_dump_entry(entry, level, indent);
640 dlog(",\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100641 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000642
643 indent -= 1;
644 dlog_indent(indent, "}");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100645}
646
647/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000648 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100649 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000650static void mm_ptable_dump(const struct mm_ptable *ptable,
651 struct mm_flags flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100652{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000653 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakina3a9f952025-02-08 00:11:16 +0000654 mm_level_t root_level = mm_root_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000655 uint8_t root_table_count = mm_root_table_count(flags);
Karl Meakinc88ad412025-02-11 16:04:49 +0000656 uint32_t indent = 0;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000657
Karl Meakinc88ad412025-02-11 16:04:49 +0000658 dlog_indent(indent, "mm_ptable {\n");
659 indent += 1;
660 {
661 dlog_indent(indent, ".stage = %s,\n",
662 flags.stage1 ? "stage1" : "stage2");
663 dlog_indent(indent, ".id = %hu,\n", ptable->id);
664 dlog_indent(indent, ".root_tables = {\n");
665
666 indent += 1;
667 {
668 for (size_t i = 0; i < root_table_count; ++i) {
669 dlog_indent(
670 indent,
671 "[level = %u, index = %zu].entries = ",
672 root_level, i);
673 mm_dump_entries(root_tables[i].entries,
674 root_level - 1, indent);
675 dlog(",\n");
676 }
677 }
678 indent -= 1;
679 dlog_indent(indent, "},\n");
Andrew Scull1ba470e2018-10-31 15:14:31 +0000680 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000681 indent -= 1;
682 dlog_indent(indent, "}\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100683}
684
685/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000686 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800687 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100688 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000689static pte_t mm_merge_table_pte(pte_t table_pte, mm_level_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100690{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100691 struct mm_page_table *table;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000692 mm_attr_t block_attrs;
693 mm_attr_t table_attrs;
694 mm_attr_t combined_attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100695 paddr_t block_address;
696
Andrew Scullb6b9b562018-12-21 14:41:35 +0000697 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
698
699 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000700 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100701 }
702
Andrew Scullb6b9b562018-12-21 14:41:35 +0000703 /* Might not be possible to merge the table into a single block. */
704 if (!arch_mm_is_block_allowed(level)) {
705 return table_pte;
706 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000707
Andrew Scullb6b9b562018-12-21 14:41:35 +0000708 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000709 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000710 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100711 combined_attrs =
712 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000713 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000714
Andrew Walbran2400ed22018-09-27 14:45:58 +0100715 return arch_mm_block_pte(level, block_address, combined_attrs);
716}
717
718/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000719 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000720 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100721 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100722// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000723static void mm_ptable_defrag_entry(struct mm_ptable *ptable,
724 ptable_addr_t base_addr, pte_t *entry,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000725 mm_level_t level, struct mm_flags flags,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000726 bool non_secure, struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100727{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000728 struct mm_page_table *child_table;
Andrew Scull12122ce2019-11-19 14:21:07 +0000729 bool mergeable;
730 bool base_present;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000731 mm_attr_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800732 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100733
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800734 if (!arch_mm_pte_is_table(*entry, level)) {
735 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100736 }
737
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000738 child_table =
739 mm_page_table_from_pa(arch_mm_table_from_pte(*entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100740
Andrew Scull12122ce2019-11-19 14:21:07 +0000741 /* Defrag the first entry in the table and use it as the base entry. */
742 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800743
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000744 mm_ptable_defrag_entry(ptable, base_addr, &(child_table->entries[0]),
745 level - 1, flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800746
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000747 base_present =
748 arch_mm_pte_is_present(child_table->entries[0], level - 1);
749 base_attrs = arch_mm_pte_attrs(child_table->entries[0], level - 1);
Andrew Scull12122ce2019-11-19 14:21:07 +0000750
Andrew Walbran2400ed22018-09-27 14:45:58 +0100751 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000752 * Defrag the remaining entries in the table and check whether they are
753 * compatible with the base entry meaning the table can be merged into a
754 * block entry. It assumes addresses are contiguous due to identity
755 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100756 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000757 mergeable = true;
Karl Meakind64aaf82025-02-08 01:12:55 +0000758 for (size_t i = 1; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000759 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800760 ptable_addr_t block_addr =
761 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000762
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000763 mm_ptable_defrag_entry(ptable, block_addr,
764 &(child_table->entries[i]), level - 1,
765 flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800766
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000767 present = arch_mm_pte_is_present(child_table->entries[i],
768 level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100769
Andrew Scull12122ce2019-11-19 14:21:07 +0000770 if (present != base_present) {
771 mergeable = false;
772 continue;
773 }
774
775 if (!present) {
776 continue;
777 }
778
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000779 if (!arch_mm_pte_is_block(child_table->entries[i], level - 1)) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000780 mergeable = false;
781 continue;
782 }
783
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000784 if (arch_mm_pte_attrs(child_table->entries[i], level - 1) !=
Andrew Scull12122ce2019-11-19 14:21:07 +0000785 base_attrs) {
786 mergeable = false;
787 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100788 }
789 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000790
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800791 if (!mergeable) {
792 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000793 }
794
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800795 new_entry = mm_merge_table_pte(*entry, level);
796 if (*entry != new_entry) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000797 mm_replace_entry(ptable, base_addr, entry, (uintptr_t)new_entry,
798 level, flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800799 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100800}
801
802/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100803 * Defragments the given page table by converting page table references to
804 * blocks whenever possible.
805 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000806static void mm_ptable_defrag(struct mm_ptable *ptable, struct mm_flags flags,
807 bool non_secure, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100808{
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000809 struct mm_page_table *root_tables = ptable->root_tables;
Karl Meakina3a9f952025-02-08 00:11:16 +0000810 mm_level_t root_level = mm_root_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000811 uint8_t root_table_count = mm_root_table_count(flags);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800812 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100813
814 /*
815 * Loop through each entry in the table. If it points to another table,
816 * check if that table can be replaced by a block or an absent entry.
817 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000818 for (size_t i = 0; i < root_table_count; ++i) {
819 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Karl Meakina3a9f952025-02-08 00:11:16 +0000820 mm_ptable_defrag_entry(
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000821 ptable, block_addr, &root_tables[i].entries[j],
Karl Meakina3a9f952025-02-08 00:11:16 +0000822 root_level - 1, flags, non_secure, ppool);
823 block_addr = mm_start_of_next_block(block_addr,
824 root_level - 1);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000825 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100826 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800827
828 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100829}
830
831/**
Andrew Scull81e85092018-12-12 12:56:20 +0000832 * Gets the attributes applied to the given range of stage-2 addresses at the
833 * given level.
834 *
835 * The `got_attrs` argument is initially passed as false until `attrs` contains
836 * attributes of the memory region at which point it is passed as true.
837 *
838 * The value returned in `attrs` is only valid if the function returns true.
839 *
840 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100841 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100842// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakind64aaf82025-02-08 01:12:55 +0000843static bool mm_ptable_get_attrs_level(const struct mm_page_table *table,
Andrew Scull81e85092018-12-12 12:56:20 +0000844 ptable_addr_t begin, ptable_addr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000845 mm_level_t level, bool got_attrs,
846 mm_attr_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100847{
Karl Meakind64aaf82025-02-08 01:12:55 +0000848 const pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000849 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100850
Andrew Scull81e85092018-12-12 12:56:20 +0000851 /* Cap end so that we don't go over the current level max. */
852 if (end > level_end) {
853 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100854 }
855
Andrew Scull81e85092018-12-12 12:56:20 +0000856 /* Check that each entry is owned. */
857 while (begin < end) {
858 if (arch_mm_pte_is_table(*pte, level)) {
859 if (!mm_ptable_get_attrs_level(
860 mm_page_table_from_pa(
861 arch_mm_table_from_pte(*pte,
862 level)),
863 begin, end, level - 1, got_attrs, attrs)) {
864 return false;
865 }
866 got_attrs = true;
867 } else {
868 if (!got_attrs) {
869 *attrs = arch_mm_pte_attrs(*pte, level);
870 got_attrs = true;
871 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
872 return false;
873 }
874 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100875
Karl Meakin25954e32025-02-07 16:12:51 +0000876 begin = mm_start_of_next_block(begin, level);
Andrew Scull81e85092018-12-12 12:56:20 +0000877 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100878 }
879
Andrew Scullc66a04d2018-12-07 13:41:56 +0000880 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000881 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100882}
883
884/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800885 * Gets the attributes applied to the given range of addresses in the page
886 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000887 *
888 * The value returned in `attrs` is only valid if the function returns true.
889 *
890 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100891 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000892static bool mm_get_attrs(const struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000893 ptable_addr_t end, mm_attr_t *attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000894 struct mm_flags flags)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100895{
Karl Meakina3a9f952025-02-08 00:11:16 +0000896 mm_level_t root_level = mm_root_level(flags);
Karl Meakin25954e32025-02-07 16:12:51 +0000897 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000898 struct mm_page_table *root_table;
Andrew Scull81e85092018-12-12 12:56:20 +0000899 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100900
Andrew Scull81e85092018-12-12 12:56:20 +0000901 begin = mm_round_down_to_page(begin);
902 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100903
Andrew Scull81e85092018-12-12 12:56:20 +0000904 /* Fail if the addresses are out of range. */
905 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000906 return false;
907 }
908
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000909 root_table = &ptable->root_tables[mm_index(begin, root_level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000910 while (begin < end) {
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000911 if (!mm_ptable_get_attrs_level(root_table, begin, end,
Karl Meakina3a9f952025-02-08 00:11:16 +0000912 root_level - 1, got_attrs,
913 attrs)) {
Andrew Scull81e85092018-12-12 12:56:20 +0000914 return false;
915 }
916
917 got_attrs = true;
Karl Meakin25954e32025-02-07 16:12:51 +0000918 begin = mm_start_of_next_block(begin, root_level);
Karl Meakine1aeb1d2025-02-08 00:35:14 +0000919 root_table++;
Andrew Scull81e85092018-12-12 12:56:20 +0000920 }
921
922 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100923}
924
Karl Meakin07a69ab2025-02-07 14:53:19 +0000925bool mm_vm_init(struct mm_ptable *ptable, mm_asid_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100926{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000927 return mm_ptable_init(ptable, id, (struct mm_flags){0}, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100928}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100929
Karl Meakind64aaf82025-02-08 01:12:55 +0000930void mm_vm_fini(const struct mm_ptable *ptable, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000931{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000932 mm_ptable_fini(ptable, (struct mm_flags){0}, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000933}
934
935/**
Andrew Scull73b89542019-11-20 17:31:26 +0000936 * Selects flags to pass to the page table manipulation operation based on the
937 * mapping mode.
938 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000939static struct mm_flags mm_mode_to_flags(mm_mode_t mode)
Andrew Scull73b89542019-11-20 17:31:26 +0000940{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000941 struct mm_flags flags = {0};
942
Andrew Scull73b89542019-11-20 17:31:26 +0000943 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
Karl Meakin1fd4b822025-02-01 17:13:47 +0000944 flags.unmap = true;
Andrew Scull73b89542019-11-20 17:31:26 +0000945 }
946
Karl Meakin1fd4b822025-02-01 17:13:47 +0000947 return flags;
Andrew Scull73b89542019-11-20 17:31:26 +0000948}
949
950/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000951 * See `mm_ptable_identity_prepare`.
952 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800953 * This must be called before `mm_identity_commit` for the same mapping.
954 *
955 * Returns true on success, or false if the update would fail.
956 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000957bool mm_identity_prepare(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000958 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800959{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000960 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000961
Karl Meakin1fd4b822025-02-01 17:13:47 +0000962 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800963
Karl Meakind64aaf82025-02-08 01:12:55 +0000964 return mm_ptable_identity_prepare(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800965 arch_mm_mode_to_stage1_attrs(mode),
966 flags, ppool);
967}
968
969/**
970 * See `mm_ptable_identity_commit`.
971 *
972 * `mm_identity_prepare` must be called before this for the same mapping.
973 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000974void *mm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000975 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800976{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000977 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000978
Karl Meakin1fd4b822025-02-01 17:13:47 +0000979 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800980
Karl Meakind64aaf82025-02-08 01:12:55 +0000981 mm_ptable_identity_commit(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800982 arch_mm_mode_to_stage1_attrs(mode), flags,
983 ppool);
984 return ptr_from_va(va_from_pa(begin));
985}
986
987/**
988 * See `mm_ptable_identity_prepare`.
989 *
Andrew Scull4e83cef2019-11-19 14:17:54 +0000990 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000991 *
992 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000993 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000994bool mm_vm_identity_prepare(struct mm_ptable *ptable, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000995 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000996{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000997 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000998
Karl Meakind64aaf82025-02-08 01:12:55 +0000999 return mm_ptable_identity_prepare(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +00001000 arch_mm_mode_to_stage2_attrs(mode),
1001 flags, ppool);
1002}
1003
1004/**
1005 * See `mm_ptable_identity_commit`.
1006 *
1007 * `mm_vm_identity_prepare` must be called before this for the same mapping.
1008 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001009void mm_vm_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, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +00001011{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001012 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +00001013
Karl Meakind64aaf82025-02-08 01:12:55 +00001014 mm_ptable_identity_commit(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +00001015 arch_mm_mode_to_stage2_attrs(mode), flags,
1016 ppool);
1017
1018 if (ipa != NULL) {
1019 *ipa = ipa_from_pa(begin);
1020 }
1021}
1022
1023/**
Andrew Scull80871322018-08-06 12:04:09 +01001024 * Updates a VM's page table such that the given physical address range is
1025 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +01001026 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +00001027 *
1028 * mm_vm_defrag should always be called after a series of page table updates,
1029 * whether they succeed or fail. This is because on failure extra page table
1030 * entries may have been allocated and then not used, while on success it may be
1031 * possible to compact the page table by merging several entries into a block.
1032 *
1033 * Returns true on success, or false if the update failed and no changes were
1034 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001035 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001036bool mm_vm_identity_map(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001037 mm_mode_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001038{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001039 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001040 bool success = mm_ptable_identity_update(
Karl Meakind64aaf82025-02-08 01:12:55 +00001041 ptable, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
Andrew Scullda3df7f2019-01-05 17:49:27 +00001042 ppool);
Andrew Scull80871322018-08-06 12:04:09 +01001043
1044 if (success && ipa != NULL) {
1045 *ipa = ipa_from_pa(begin);
1046 }
1047
1048 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001049}
1050
1051/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001052 * Updates the VM's table such that the given physical address range has no
1053 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +01001054 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001055bool mm_vm_unmap(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001056 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001057{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001058 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001059
Karl Meakind64aaf82025-02-08 01:12:55 +00001060 return mm_vm_identity_map(ptable, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +01001061}
1062
1063/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001064 * Write the given page table of a VM to the debug log.
1065 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001066void mm_vm_dump(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001067{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001068 mm_ptable_dump(ptable, (struct mm_flags){0});
Andrew Scullda3df7f2019-01-05 17:49:27 +00001069}
1070
1071/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001072 * Defragments a stage1 page table.
1073 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001074void mm_stage1_defrag(struct mm_ptable *ptable, struct mpool *ppool)
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001075{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001076 mm_ptable_defrag(ptable, (struct mm_flags){.stage1 = true}, false,
1077 ppool);
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001078}
1079
1080/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001081 * Defragments the VM page table.
1082 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001083void mm_vm_defrag(struct mm_ptable *ptable, struct mpool *ppool,
1084 bool non_secure)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001085{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001086 mm_ptable_defrag(ptable, (struct mm_flags){0}, non_secure, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001087}
1088
1089/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +01001090 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +00001091 * are mapped with the same mode.
1092 *
1093 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001094 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001095bool mm_vm_get_mode(const struct mm_ptable *ptable, ipaddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001096 ipaddr_t end, mm_mode_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001097{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001098 mm_attr_t attrs;
Andrew Scull81e85092018-12-12 12:56:20 +00001099 bool ret;
1100
Karl Meakin1fd4b822025-02-01 17:13:47 +00001101 ret = mm_get_attrs(ptable, ipa_addr(begin), ipa_addr(end), &attrs,
1102 (struct mm_flags){0});
Andrew Scull81e85092018-12-12 12:56:20 +00001103 if (ret) {
1104 *mode = arch_mm_stage2_attrs_to_mode(attrs);
1105 }
1106
1107 return ret;
Andrew Scull80871322018-08-06 12:04:09 +01001108}
1109
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001110/**
1111 * Gets the mode of the given range of virtual addresses if they
1112 * are mapped with the same mode.
1113 *
1114 * Returns true if the range is mapped with the same mode and false otherwise.
1115 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001116bool mm_get_mode(const struct mm_ptable *ptable, vaddr_t begin, vaddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001117 mm_mode_t *mode)
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001118{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001119 mm_attr_t attrs;
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001120 bool ret;
1121
Karl Meakind64aaf82025-02-08 01:12:55 +00001122 ret = mm_get_attrs(ptable, va_addr(begin), va_addr(end), &attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +00001123 (struct mm_flags){.stage1 = true});
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001124 if (ret) {
1125 *mode = arch_mm_stage1_attrs_to_mode(attrs);
1126 }
1127
1128 return ret;
1129}
1130
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001131static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1132{
1133 return (struct mm_stage1_locked){.ptable = &ptable};
1134}
1135
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001136struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1137{
1138 return (struct mm_stage1_locked){.ptable = ptable};
1139}
1140
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001141struct mm_stage1_locked mm_lock_stage1(void)
1142{
1143 sl_lock(&ptable_lock);
1144 return mm_stage1_lock_unsafe();
1145}
1146
1147void mm_unlock_stage1(struct mm_stage1_locked *lock)
1148{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001149 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001150 sl_unlock(&ptable_lock);
1151 lock->ptable = NULL;
1152}
1153
Andrew Scull80871322018-08-06 12:04:09 +01001154/**
Andrew Scull80871322018-08-06 12:04:09 +01001155 * Updates the hypervisor page table such that the given physical address range
1156 * is mapped into the address space at the corresponding address range in the
1157 * architecture-agnostic mode provided.
1158 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001159void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001160 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001161{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001162 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +00001163
Karl Meakin1fd4b822025-02-01 17:13:47 +00001164 flags.stage1 = true;
Andrew Scull73b89542019-11-20 17:31:26 +00001165
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001166 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001167 arch_mm_mode_to_stage1_attrs(mode), flags,
1168 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001169 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001170 }
1171
1172 return NULL;
1173}
1174
1175/**
1176 * Updates the hypervisor table such that the given physical address range is
1177 * not mapped in the address space.
1178 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001179bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1180 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001181{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001182 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001183
1184 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001185}
1186
1187/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001188 * Defragments the hypervisor page table.
1189 */
1190void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1191{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001192 mm_ptable_defrag(stage1_locked.ptable,
1193 (struct mm_flags){.stage1 = true}, false, ppool);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001194}
1195
1196/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001197 * Initialises memory management for the hypervisor itself.
1198 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001199bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001200{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001201 /* Locking is not enabled yet so fake it, */
1202 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
Karl Meakin07a69ab2025-02-07 14:53:19 +00001203 struct mm_flags flags = {.stage1 = true};
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001204
Karl Meakine8937d92024-03-19 16:04:25 +00001205 dlog_info("text: %#lx - %#lx\n", pa_addr(layout_text_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001206 pa_addr(layout_text_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001207 dlog_info("rodata: %#lx - %#lx\n", pa_addr(layout_rodata_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001208 pa_addr(layout_rodata_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001209 dlog_info("data: %#lx - %#lx\n", pa_addr(layout_data_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001210 pa_addr(layout_data_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001211 dlog_info("stacks: %#lx - %#lx\n", pa_addr(layout_stacks_begin()),
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001212 pa_addr(layout_stacks_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001213
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001214 /* ASID 0 is reserved for use by the hypervisor. */
Karl Meakin07a69ab2025-02-07 14:53:19 +00001215 if (!mm_ptable_init(&ptable, 0, flags, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001216 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001217 return false;
1218 }
1219
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001220 /* Initialize arch_mm before calling below mapping routines */
Karl Meakine1aeb1d2025-02-08 00:35:14 +00001221 if (!arch_mm_init(&ptable)) {
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001222 return false;
1223 }
1224
Andrew Walbran48699362019-05-20 14:38:00 +01001225 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001226 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001227
1228 /* Map each section. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001229 CHECK(mm_identity_map(stage1_locked, layout_text_begin(),
1230 layout_text_end(), MM_MODE_X, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001231
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001232 CHECK(mm_identity_map(stage1_locked, layout_rodata_begin(),
1233 layout_rodata_end(), MM_MODE_R, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001234
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001235 CHECK(mm_identity_map(stage1_locked, layout_data_begin(),
1236 layout_data_end(), MM_MODE_R | MM_MODE_W,
1237 ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001238
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001239 /* Arch-specific stack mapping. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001240 CHECK(arch_stack_mm_init(stage1_locked, ppool));
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001241
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001242 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001243}