blob: 9b2c0063acdd51550c72cb038dc909d0d6970e48 [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
82 * the given level.
83 */
Karl Meakin07a69ab2025-02-07 14:53:19 +000084static size_t mm_entry_size(mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010085{
Andrew Scull78d6fd92018-09-06 15:08:36 +010086 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010087}
88
89/**
Karl Meakin25954e32025-02-07 16:12:51 +000090 * Gets the address of the start of the next block of the given level.
Andrew Scullcae45572018-12-13 15:46:30 +000091 */
92static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
Karl Meakin25954e32025-02-07 16:12:51 +000093 mm_level_t level)
Andrew Scullcae45572018-12-13 15:46:30 +000094{
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
111 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100112 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000113static size_t mm_index(ptable_addr_t addr, mm_level_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100114{
Andrew Scull80871322018-08-06 12:04:09 +0100115 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000116
Andrew Scull78d6fd92018-09-06 15:08:36 +0100117 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100118}
119
120/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000121 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100122 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000123static struct mm_page_table *mm_alloc_page_tables(size_t count,
124 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100125{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000126 if (count == 1) {
127 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100128 }
129
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000130 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100131}
132
133/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000134 * Returns the maximum level in the page table given the flags.
135 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000136static mm_level_t mm_max_level(struct mm_flags flags)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000137{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000138 return flags.stage1 ? arch_mm_stage1_max_level()
139 : arch_mm_stage2_max_level();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000140}
141
142/**
143 * Returns the number of root-level tables given the flags.
144 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000145static uint8_t mm_root_table_count(struct mm_flags flags)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000146{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000147 return flags.stage1 ? arch_mm_stage1_root_table_count()
148 : arch_mm_stage2_root_table_count();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000149}
150
151/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000152 * Invalidates the TLB for the given address range.
153 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000154static void mm_invalidate_tlb(const struct mm_ptable *ptable,
155 ptable_addr_t begin, ptable_addr_t end,
156 struct mm_flags flags, bool non_secure)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000157{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000158 if (flags.stage1) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000159 arch_mm_invalidate_stage1_range(ptable->id, va_init(begin),
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800160 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000161 } else {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000162 arch_mm_invalidate_stage2_range(ptable->id, ipa_init(begin),
Olivier Deprez6f400372022-03-07 09:31:08 +0100163 ipa_init(end), non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000164 }
165}
166
167/**
168 * Frees all page-table-related memory associated with the given pte at the
169 * given level, including any subtables recursively.
170 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100171// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin07a69ab2025-02-07 14:53:19 +0000172static void mm_free_page_pte(pte_t pte, mm_level_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000173{
174 struct mm_page_table *table;
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000175
176 if (!arch_mm_pte_is_table(pte, level)) {
177 return;
178 }
179
180 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000181 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Karl Meakind64aaf82025-02-08 01:12:55 +0000182 for (size_t i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000183 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000184 }
185
186 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000187 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000188}
189
190/**
David Brazdil711fbe92019-08-06 13:39:58 +0100191 * Returns the first address which cannot be encoded in page tables given by
192 * `flags`. It is the exclusive end of the address space created by the tables.
193 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000194ptable_addr_t mm_ptable_addr_space_end(struct mm_flags flags)
David Brazdil711fbe92019-08-06 13:39:58 +0100195{
196 return mm_root_table_count(flags) *
197 mm_entry_size(mm_max_level(flags) + 1);
198}
199
200/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000201 * Initialises the given page table.
202 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000203bool mm_ptable_init(struct mm_ptable *ptable, mm_asid_t id,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000204 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000205{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000206 struct mm_page_table *tables;
207 uint8_t root_table_count = mm_root_table_count(flags);
208
209 tables = mm_alloc_page_tables(root_table_count, ppool);
210 if (tables == NULL) {
211 return false;
212 }
213
Karl Meakind64aaf82025-02-08 01:12:55 +0000214 for (size_t i = 0; i < root_table_count; i++) {
215 for (size_t j = 0; j < MM_PTE_PER_PAGE; j++) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000216 tables[i].entries[j] =
217 arch_mm_absent_pte(mm_max_level(flags));
218 }
219 }
220
221 /*
222 * TODO: halloc could return a virtual or physical address if mm not
223 * enabled?
224 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000225 ptable->root = pa_init((uintpaddr_t)tables);
226 ptable->id = id;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000227 return true;
228}
229
230/**
231 * Frees all memory associated with the give page table.
232 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000233static void mm_ptable_fini(const struct mm_ptable *ptable,
234 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000235{
Karl Meakind64aaf82025-02-08 01:12:55 +0000236 struct mm_page_table *tables = mm_page_table_from_pa(ptable->root);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000237 mm_level_t level = mm_max_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000238 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000239
Karl Meakind64aaf82025-02-08 01:12:55 +0000240 for (size_t i = 0; i < root_table_count; ++i) {
241 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000242 mm_free_page_pte(tables[i].entries[j], level, ppool);
243 }
244 }
245
246 mpool_add_chunk(ppool, tables,
247 sizeof(struct mm_page_table) * root_table_count);
248}
249
250/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000251 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000252 * are valid, it performs a break-before-make sequence where it first writes an
253 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
254 * This is to prevent cases where CPUs have different 'valid' values in their
255 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000256 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000257static void mm_replace_entry(const struct mm_ptable *ptable,
258 ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000259 mm_level_t level, struct mm_flags flags,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000260 bool non_secure, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000261{
262 pte_t v = *pte;
263
264 /*
265 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000266 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000267 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000268 if ((flags.stage1 || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800269 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270 *pte = arch_mm_absent_pte(level);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000271 mm_invalidate_tlb(ptable, begin, begin + mm_entry_size(level),
272 flags, non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000273 }
274
275 /* Assign the new pte. */
276 *pte = new_pte;
277
278 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000279 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000280}
281
282/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100283 * Populates the provided page table entry with a reference to another table if
284 * needed, that is, if it does not yet point to another table.
285 *
286 * Returns a pointer to the table the entry now points to.
287 */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000288static struct mm_page_table *mm_populate_table_pte(
289 const struct mm_ptable *ptable, ptable_addr_t begin, pte_t *pte,
290 mm_level_t level, struct mm_flags flags, bool non_secure,
291 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100292{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100293 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100294 pte_t v = *pte;
295 pte_t new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100296 size_t inc;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000297 mm_level_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100298
299 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100300 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000301 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100302 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100303
304 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000305 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100306 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000307 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100308 return NULL;
309 }
310
311 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100312 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100313 inc = mm_entry_size(level_below);
314 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000315 arch_mm_block_from_pte(v, level),
316 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100317 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100318 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100319 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100320 }
321
322 /* Initialise entries in the new table. */
Karl Meakind64aaf82025-02-08 01:12:55 +0000323 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100324 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100325 new_pte += inc;
326 }
327
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000328 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100329 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000330
331 /* Replace the pte entry, doing a break-before-make if needed. */
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000332 mm_replace_entry(ptable, begin, pte,
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000333 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000334 level, flags, non_secure, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100335
336 return ntable;
337}
338
339/**
Andrew Scull80871322018-08-06 12:04:09 +0100340 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100341 * physical range using the provided (architecture-specific) attributes. Or if
Karl Meakin1fd4b822025-02-01 17:13:47 +0000342 * `flags.unmap` is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100343 *
344 * This function calls itself recursively if it needs to update additional
345 * levels, but the recursion is bound by the maximum number of levels in a page
346 * table.
347 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100348// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000349static bool mm_map_level(struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin25954e32025-02-07 16:12:51 +0000350 ptable_addr_t end, mm_attr_t attrs,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000351 struct mm_page_table *child_table, mm_level_t level,
352 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100353{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000354 pte_t *pte = &child_table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100355 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100356 size_t entry_size = mm_entry_size(level);
Karl Meakin1fd4b822025-02-01 17:13:47 +0000357 bool commit = flags.commit;
358 bool unmap = flags.unmap;
359 bool non_secure = ((attrs & (1ULL << 57)) != 0);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100360
Andrew Scull265ada92018-07-30 15:19:01 +0100361 /* Cap end so that we don't go over the current level max. */
362 if (end > level_end) {
363 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100364 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100365
366 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100367 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100368 if (unmap ? !arch_mm_pte_is_present(*pte, level)
369 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000370 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100371 /*
372 * If the entry is already mapped with the right
373 * attributes, or already absent in the case of
374 * unmapping, no need to do anything; carry on to the
375 * next entry.
376 */
377 } else if ((end - begin) >= entry_size &&
378 (unmap || arch_mm_is_block_allowed(level)) &&
Karl Meakin25954e32025-02-07 16:12:51 +0000379 is_aligned(begin, entry_size)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100380 /*
381 * If the entire entry is within the region we want to
382 * map, map/unmap the whole entry.
383 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100384 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000385 pte_t new_pte =
386 unmap ? arch_mm_absent_pte(level)
Karl Meakin25954e32025-02-07 16:12:51 +0000387 : arch_mm_block_pte(
388 level, pa_init(begin),
389 attrs);
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000390 mm_replace_entry(ptable, begin, pte, new_pte,
391 level, flags, non_secure,
392 ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100393 }
394 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100395 /*
396 * If the entry is already a subtable get it; otherwise
397 * replace it with an equivalent subtable and get that.
398 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000399 struct mm_page_table *nt =
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000400 mm_populate_table_pte(ptable, begin, pte, level,
401 flags, non_secure, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100402 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100403 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100404 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100405
Andrew Walbran6324fc92018-10-03 11:46:43 +0100406 /*
407 * Recurse to map/unmap the appropriate entries within
408 * the subtable.
409 */
Karl Meakin25954e32025-02-07 16:12:51 +0000410 if (!mm_map_level(ptable, begin, end, attrs, nt,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000411 level - 1, flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100412 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100413 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100414 }
415
Karl Meakin25954e32025-02-07 16:12:51 +0000416 begin = mm_start_of_next_block(begin, level);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100417 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100418 }
419
420 return true;
421}
422
423/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000424 * Updates the page table from the root to map the given address range to a
Karl Meakinaac38012025-02-07 23:57:37 +0000425 * physical range using the provided (architecture-specific) attributes.
426 *
427 * Flags:
428 * - `flags.unmap`: unmap the given range instead of mapping it.
429 * - `flags.commit`: the change is only committed if this flag is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100430 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000431static bool mm_ptable_identity_map(struct mm_ptable *ptable, paddr_t pa_begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000432 paddr_t pa_end, mm_attr_t attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000433 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100434{
Karl Meakin07a69ab2025-02-07 14:53:19 +0000435 mm_level_t root_level = mm_max_level(flags) + 1;
David Brazdil711fbe92019-08-06 13:39:58 +0100436 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000437 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
Karl Meakinc17ab272025-02-08 03:29:17 +0000438 ptable_addr_t begin = mm_round_down_to_page(pa_addr(pa_begin));
Karl Meakinaac38012025-02-07 23:57:37 +0000439 struct mm_page_table *root_table = &mm_page_table_from_pa(
440 ptable->root)[mm_index(begin, root_level)];
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100441
Andrew Scull1ba470e2018-10-31 15:14:31 +0000442 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100443 * Assert condition to communicate the API constraint of mm_max_level(),
444 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000445 */
Daniel Boulbya2f8c662021-11-26 17:52:53 +0000446 assert(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000447
448 /* Cap end to stay within the bounds of the page table. */
449 if (end > ptable_end) {
450 end = ptable_end;
451 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100452
Karl Meakinaac38012025-02-07 23:57:37 +0000453 while (begin < end) {
454 if (!mm_map_level(ptable, begin, end, attrs, root_table,
455 root_level - 1, flags, ppool)) {
456 return false;
457 }
458 begin = mm_start_of_next_block(begin, root_level);
459 root_table++;
Andrew Walbran58a6e542019-11-19 14:23:15 +0000460 }
461
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800462 /*
463 * All TLB invalidations must be complete already if any entries were
464 * replaced by mm_replace_entry. Sync all page table writes so that code
465 * following this can use them.
466 */
467 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000468
469 return true;
470}
471
Andrew Scull4e83cef2019-11-19 14:17:54 +0000472/*
473 * Prepares the given page table for the given address mapping such that it
474 * will be able to commit the change without failure. It does so by ensuring
475 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100476 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000477 *
478 * In particular, multiple calls to this function will result in the
479 * corresponding calls to commit the changes to succeed.
480 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000481static bool mm_ptable_identity_prepare(struct mm_ptable *ptable,
482 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000483 mm_attr_t attrs, struct mm_flags flags,
Karl Meakind64aaf82025-02-08 01:12:55 +0000484 struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000485{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000486 flags.commit = false;
Karl Meakind64aaf82025-02-08 01:12:55 +0000487 return mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
488 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000489}
490
491/**
492 * Commits the given address mapping to the page table assuming the operation
493 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
494 * ensure this condition.
495 *
496 * Without the table being properly prepared, the commit may only partially
497 * complete if it runs out of memory resulting in an inconsistent state that
498 * isn't handled.
499 *
500 * Since the non-failure assumtion is used in the reasoning about the atomicity
501 * of higher level memory operations, any detected violations result in a panic.
502 *
503 * TODO: remove ppool argument to be sure no changes are made.
504 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000505static void mm_ptable_identity_commit(struct mm_ptable *ptable,
506 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000507 mm_attr_t attrs, struct mm_flags flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000508 struct mpool *ppool)
509{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000510 flags.commit = true;
511 CHECK(mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
512 ppool));
Andrew Scull4e83cef2019-11-19 14:17:54 +0000513}
514
Andrew Walbran58a6e542019-11-19 14:23:15 +0000515/**
516 * Updates the given table such that the given physical address range is mapped
517 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000518 * provided.
519 *
520 * The page table is updated using the separate prepare and commit stages so
521 * that, on failure, a partial update of the address space cannot happen. The
522 * table may be left with extra internal tables but the address space is
523 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000524 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000525static bool mm_ptable_identity_update(struct mm_ptable *ptable,
526 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000527 mm_attr_t attrs, struct mm_flags flags,
Andrew Walbran58a6e542019-11-19 14:23:15 +0000528 struct mpool *ppool)
529{
Karl Meakind64aaf82025-02-08 01:12:55 +0000530 if (!mm_ptable_identity_prepare(ptable, pa_begin, pa_end, attrs, flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000531 ppool)) {
532 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100533 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100534
Karl Meakind64aaf82025-02-08 01:12:55 +0000535 mm_ptable_identity_commit(ptable, pa_begin, pa_end, attrs, flags,
536 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000537
538 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100539}
540
Karl Meakinc88ad412025-02-11 16:04:49 +0000541static void mm_dump_entries(const pte_t *entries, mm_level_t level,
542 uint32_t indent);
543
544static void mm_dump_block_entry(pte_t entry, mm_level_t level, uint32_t indent)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100545{
Karl Meakinc88ad412025-02-11 16:04:49 +0000546 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
547 paddr_t addr = arch_mm_block_from_pte(entry, level);
548
549 if (arch_mm_pte_is_valid(entry, level)) {
550 if (level == 0) {
551 dlog("page {\n");
552 } else {
553 dlog("block {\n");
554 }
555 } else {
556 dlog("invalid_block {\n");
557 }
558
559 indent += 1;
560 {
561 dlog_indent(indent, ".addr = %#016lx\n", pa_addr(addr));
562 dlog_indent(indent, ".attrs = %#016lx\n", attrs);
563 }
564 indent -= 1;
565 dlog_indent(indent, "}");
566}
567
568// NOLINTNEXTLINE(misc-no-recursion)
569static void mm_dump_table_entry(pte_t entry, mm_level_t level, uint32_t indent)
570{
571 dlog("table {\n");
572 indent += 1;
573 {
574 mm_attr_t attrs = arch_mm_pte_attrs(entry, level);
575 paddr_t addr = arch_mm_table_from_pte(entry, level);
576 const struct mm_page_table *child_table =
577 mm_page_table_from_pa(addr);
578
579 dlog_indent(indent, ".pte = %#016lx,\n", entry);
580 dlog_indent(indent, ".attrs = %#016lx,\n", attrs);
581 dlog_indent(indent, ".addr = %#016lx,\n", pa_addr(addr));
582 dlog_indent(indent, ".entries = ");
583 mm_dump_entries(child_table->entries, level - 1, indent);
584 dlog(",\n");
585 }
586 indent -= 1;
587 dlog_indent(indent, "}");
588}
589
590// NOLINTNEXTLINE(misc-no-recursion)
591static void mm_dump_entry(pte_t entry, mm_level_t level, uint32_t indent)
592{
593 switch (arch_mm_pte_type(entry, level)) {
594 case PTE_TYPE_ABSENT:
595 dlog("absent {}");
596 break;
597 case PTE_TYPE_INVALID_BLOCK:
598 case PTE_TYPE_VALID_BLOCK: {
599 mm_dump_block_entry(entry, level, indent);
600 break;
601 }
602 case PTE_TYPE_TABLE: {
603 mm_dump_table_entry(entry, level, indent);
604 break;
605 }
606 }
607}
608
609// NOLINTNEXTLINE(misc-no-recursion)
610static void mm_dump_entries(const pte_t *entries, mm_level_t level,
611 uint32_t indent)
612{
613 dlog("{\n");
614 indent += 1;
615
Karl Meakind64aaf82025-02-08 01:12:55 +0000616 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Karl Meakinc88ad412025-02-11 16:04:49 +0000617 pte_t entry = entries[i];
Karl Meakin100b0b22025-02-08 00:59:25 +0000618
619 if (arch_mm_pte_is_absent(entry, level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100620 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100621 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100622
Karl Meakinc88ad412025-02-11 16:04:49 +0000623 dlog_indent(indent, "[level = %u, index = %zu] = ", level, i);
624 mm_dump_entry(entry, level, indent);
625 dlog(",\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100626 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000627
628 indent -= 1;
629 dlog_indent(indent, "}");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100630}
631
632/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000633 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100634 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000635static void mm_ptable_dump(const struct mm_ptable *ptable,
636 struct mm_flags flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100637{
Karl Meakinc88ad412025-02-11 16:04:49 +0000638 struct mm_page_table *root_tables = mm_page_table_from_pa(ptable->root);
639 mm_level_t root_level = mm_max_level(flags) + 1;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000640 uint8_t root_table_count = mm_root_table_count(flags);
Karl Meakinc88ad412025-02-11 16:04:49 +0000641 uint32_t indent = 0;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000642
Karl Meakinc88ad412025-02-11 16:04:49 +0000643 dlog_indent(indent, "mm_ptable {\n");
644 indent += 1;
645 {
646 dlog_indent(indent, ".stage = %s,\n",
647 flags.stage1 ? "stage1" : "stage2");
648 dlog_indent(indent, ".id = %hu,\n", ptable->id);
649 dlog_indent(indent, ".root_tables = {\n");
650
651 indent += 1;
652 {
653 for (size_t i = 0; i < root_table_count; ++i) {
654 dlog_indent(
655 indent,
656 "[level = %u, index = %zu].entries = ",
657 root_level, i);
658 mm_dump_entries(root_tables[i].entries,
659 root_level - 1, indent);
660 dlog(",\n");
661 }
662 }
663 indent -= 1;
664 dlog_indent(indent, "},\n");
Andrew Scull1ba470e2018-10-31 15:14:31 +0000665 }
Karl Meakinc88ad412025-02-11 16:04:49 +0000666 indent -= 1;
667 dlog_indent(indent, "}\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100668}
669
670/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000671 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800672 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100673 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000674static pte_t mm_merge_table_pte(pte_t table_pte, mm_level_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100675{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100676 struct mm_page_table *table;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000677 mm_attr_t block_attrs;
678 mm_attr_t table_attrs;
679 mm_attr_t combined_attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100680 paddr_t block_address;
681
Andrew Scullb6b9b562018-12-21 14:41:35 +0000682 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
683
684 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000685 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100686 }
687
Andrew Scullb6b9b562018-12-21 14:41:35 +0000688 /* Might not be possible to merge the table into a single block. */
689 if (!arch_mm_is_block_allowed(level)) {
690 return table_pte;
691 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000692
Andrew Scullb6b9b562018-12-21 14:41:35 +0000693 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000694 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000695 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100696 combined_attrs =
697 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000698 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000699
Andrew Walbran2400ed22018-09-27 14:45:58 +0100700 return arch_mm_block_pte(level, block_address, combined_attrs);
701}
702
703/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000704 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000705 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100706 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100707// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000708static void mm_ptable_defrag_entry(struct mm_ptable *ptable,
709 ptable_addr_t base_addr, pte_t *entry,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000710 mm_level_t level, struct mm_flags flags,
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000711 bool non_secure, struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100712{
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000713 struct mm_page_table *child_table;
Andrew Scull12122ce2019-11-19 14:21:07 +0000714 bool mergeable;
715 bool base_present;
Karl Meakin07a69ab2025-02-07 14:53:19 +0000716 mm_attr_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800717 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100718
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800719 if (!arch_mm_pte_is_table(*entry, level)) {
720 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100721 }
722
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000723 child_table =
724 mm_page_table_from_pa(arch_mm_table_from_pte(*entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100725
Andrew Scull12122ce2019-11-19 14:21:07 +0000726 /* Defrag the first entry in the table and use it as the base entry. */
727 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800728
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000729 mm_ptable_defrag_entry(ptable, base_addr, &(child_table->entries[0]),
730 level - 1, flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800731
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000732 base_present =
733 arch_mm_pte_is_present(child_table->entries[0], level - 1);
734 base_attrs = arch_mm_pte_attrs(child_table->entries[0], level - 1);
Andrew Scull12122ce2019-11-19 14:21:07 +0000735
Andrew Walbran2400ed22018-09-27 14:45:58 +0100736 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000737 * Defrag the remaining entries in the table and check whether they are
738 * compatible with the base entry meaning the table can be merged into a
739 * block entry. It assumes addresses are contiguous due to identity
740 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100741 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000742 mergeable = true;
Karl Meakind64aaf82025-02-08 01:12:55 +0000743 for (size_t i = 1; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000744 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800745 ptable_addr_t block_addr =
746 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000747
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000748 mm_ptable_defrag_entry(ptable, block_addr,
749 &(child_table->entries[i]), level - 1,
750 flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800751
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000752 present = arch_mm_pte_is_present(child_table->entries[i],
753 level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100754
Andrew Scull12122ce2019-11-19 14:21:07 +0000755 if (present != base_present) {
756 mergeable = false;
757 continue;
758 }
759
760 if (!present) {
761 continue;
762 }
763
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000764 if (!arch_mm_pte_is_block(child_table->entries[i], level - 1)) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000765 mergeable = false;
766 continue;
767 }
768
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000769 if (arch_mm_pte_attrs(child_table->entries[i], level - 1) !=
Andrew Scull12122ce2019-11-19 14:21:07 +0000770 base_attrs) {
771 mergeable = false;
772 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100773 }
774 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000775
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800776 if (!mergeable) {
777 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000778 }
779
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800780 new_entry = mm_merge_table_pte(*entry, level);
781 if (*entry != new_entry) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000782 mm_replace_entry(ptable, base_addr, entry, (uintptr_t)new_entry,
783 level, flags, non_secure, ppool);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800784 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100785}
786
787/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100788 * Defragments the given page table by converting page table references to
789 * blocks whenever possible.
790 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000791static void mm_ptable_defrag(struct mm_ptable *ptable, struct mm_flags flags,
792 bool non_secure, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100793{
Karl Meakind64aaf82025-02-08 01:12:55 +0000794 struct mm_page_table *tables = mm_page_table_from_pa(ptable->root);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000795 mm_level_t level = mm_max_level(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000796 uint8_t root_table_count = mm_root_table_count(flags);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800797 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100798
799 /*
800 * Loop through each entry in the table. If it points to another table,
801 * check if that table can be replaced by a block or an absent entry.
802 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000803 for (size_t i = 0; i < root_table_count; ++i) {
804 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Karl Meakin00dbf1b2025-02-07 17:58:39 +0000805 mm_ptable_defrag_entry(ptable, block_addr,
806 &(tables[i].entries[j]), level,
807 flags, non_secure, ppool);
Karl Meakin25954e32025-02-07 16:12:51 +0000808 block_addr = mm_start_of_next_block(block_addr, level);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000809 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100810 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800811
812 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100813}
814
815/**
Andrew Scull81e85092018-12-12 12:56:20 +0000816 * Gets the attributes applied to the given range of stage-2 addresses at the
817 * given level.
818 *
819 * The `got_attrs` argument is initially passed as false until `attrs` contains
820 * attributes of the memory region at which point it is passed as true.
821 *
822 * The value returned in `attrs` is only valid if the function returns true.
823 *
824 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100825 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100826// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakind64aaf82025-02-08 01:12:55 +0000827static bool mm_ptable_get_attrs_level(const struct mm_page_table *table,
Andrew Scull81e85092018-12-12 12:56:20 +0000828 ptable_addr_t begin, ptable_addr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000829 mm_level_t level, bool got_attrs,
830 mm_attr_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100831{
Karl Meakind64aaf82025-02-08 01:12:55 +0000832 const pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000833 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100834
Andrew Scull81e85092018-12-12 12:56:20 +0000835 /* Cap end so that we don't go over the current level max. */
836 if (end > level_end) {
837 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100838 }
839
Andrew Scull81e85092018-12-12 12:56:20 +0000840 /* Check that each entry is owned. */
841 while (begin < end) {
842 if (arch_mm_pte_is_table(*pte, level)) {
843 if (!mm_ptable_get_attrs_level(
844 mm_page_table_from_pa(
845 arch_mm_table_from_pte(*pte,
846 level)),
847 begin, end, level - 1, got_attrs, attrs)) {
848 return false;
849 }
850 got_attrs = true;
851 } else {
852 if (!got_attrs) {
853 *attrs = arch_mm_pte_attrs(*pte, level);
854 got_attrs = true;
855 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
856 return false;
857 }
858 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100859
Karl Meakin25954e32025-02-07 16:12:51 +0000860 begin = mm_start_of_next_block(begin, level);
Andrew Scull81e85092018-12-12 12:56:20 +0000861 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100862 }
863
Andrew Scullc66a04d2018-12-07 13:41:56 +0000864 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000865 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100866}
867
868/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800869 * Gets the attributes applied to the given range of addresses in the page
870 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000871 *
872 * The value returned in `attrs` is only valid if the function returns true.
873 *
874 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100875 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000876static bool mm_get_attrs(const struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000877 ptable_addr_t end, mm_attr_t *attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000878 struct mm_flags flags)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100879{
Karl Meakin07a69ab2025-02-07 14:53:19 +0000880 mm_level_t max_level = mm_max_level(flags);
881 mm_level_t root_level = max_level + 1;
Karl Meakin25954e32025-02-07 16:12:51 +0000882 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000883 struct mm_page_table *table;
884 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100885
Andrew Scull81e85092018-12-12 12:56:20 +0000886 begin = mm_round_down_to_page(begin);
887 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100888
Andrew Scull81e85092018-12-12 12:56:20 +0000889 /* Fail if the addresses are out of range. */
890 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000891 return false;
892 }
893
Karl Meakind64aaf82025-02-08 01:12:55 +0000894 table = &mm_page_table_from_pa(
895 ptable->root)[mm_index(begin, root_level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000896 while (begin < end) {
897 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
898 got_attrs, attrs)) {
899 return false;
900 }
901
902 got_attrs = true;
Karl Meakin25954e32025-02-07 16:12:51 +0000903 begin = mm_start_of_next_block(begin, root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000904 table++;
905 }
906
907 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100908}
909
Karl Meakin07a69ab2025-02-07 14:53:19 +0000910bool mm_vm_init(struct mm_ptable *ptable, mm_asid_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100911{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000912 return mm_ptable_init(ptable, id, (struct mm_flags){0}, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100913}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100914
Karl Meakind64aaf82025-02-08 01:12:55 +0000915void mm_vm_fini(const struct mm_ptable *ptable, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000916{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000917 mm_ptable_fini(ptable, (struct mm_flags){0}, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000918}
919
920/**
Andrew Scull73b89542019-11-20 17:31:26 +0000921 * Selects flags to pass to the page table manipulation operation based on the
922 * mapping mode.
923 */
Karl Meakin07a69ab2025-02-07 14:53:19 +0000924static struct mm_flags mm_mode_to_flags(mm_mode_t mode)
Andrew Scull73b89542019-11-20 17:31:26 +0000925{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000926 struct mm_flags flags = {0};
927
Andrew Scull73b89542019-11-20 17:31:26 +0000928 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
Karl Meakin1fd4b822025-02-01 17:13:47 +0000929 flags.unmap = true;
Andrew Scull73b89542019-11-20 17:31:26 +0000930 }
931
Karl Meakin1fd4b822025-02-01 17:13:47 +0000932 return flags;
Andrew Scull73b89542019-11-20 17:31:26 +0000933}
934
935/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000936 * See `mm_ptable_identity_prepare`.
937 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800938 * This must be called before `mm_identity_commit` for the same mapping.
939 *
940 * Returns true on success, or false if the update would fail.
941 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000942bool mm_identity_prepare(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000943 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800944{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000945 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000946
Karl Meakin1fd4b822025-02-01 17:13:47 +0000947 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800948
Karl Meakind64aaf82025-02-08 01:12:55 +0000949 return mm_ptable_identity_prepare(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800950 arch_mm_mode_to_stage1_attrs(mode),
951 flags, ppool);
952}
953
954/**
955 * See `mm_ptable_identity_commit`.
956 *
957 * `mm_identity_prepare` must be called before this for the same mapping.
958 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000959void *mm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000960 mm_mode_t mode, struct mpool *ppool)
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800961{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000962 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +0000963
Karl Meakin1fd4b822025-02-01 17:13:47 +0000964 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800965
Karl Meakind64aaf82025-02-08 01:12:55 +0000966 mm_ptable_identity_commit(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800967 arch_mm_mode_to_stage1_attrs(mode), flags,
968 ppool);
969 return ptr_from_va(va_from_pa(begin));
970}
971
972/**
973 * See `mm_ptable_identity_prepare`.
974 *
Andrew Scull4e83cef2019-11-19 14:17:54 +0000975 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000976 *
977 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000978 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000979bool mm_vm_identity_prepare(struct mm_ptable *ptable, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000980 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000981{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000982 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000983
Karl Meakind64aaf82025-02-08 01:12:55 +0000984 return mm_ptable_identity_prepare(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000985 arch_mm_mode_to_stage2_attrs(mode),
986 flags, ppool);
987}
988
989/**
990 * See `mm_ptable_identity_commit`.
991 *
992 * `mm_vm_identity_prepare` must be called before this for the same mapping.
993 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000994void mm_vm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +0000995 mm_mode_t mode, struct mpool *ppool, ipaddr_t *ipa)
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 mm_ptable_identity_commit(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +00001000 arch_mm_mode_to_stage2_attrs(mode), flags,
1001 ppool);
1002
1003 if (ipa != NULL) {
1004 *ipa = ipa_from_pa(begin);
1005 }
1006}
1007
1008/**
Andrew Scull80871322018-08-06 12:04:09 +01001009 * Updates a VM's page table such that the given physical address range is
1010 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +01001011 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +00001012 *
1013 * mm_vm_defrag should always be called after a series of page table updates,
1014 * whether they succeed or fail. This is because on failure extra page table
1015 * entries may have been allocated and then not used, while on success it may be
1016 * possible to compact the page table by merging several entries into a block.
1017 *
1018 * Returns true on success, or false if the update failed and no changes were
1019 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001020 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001021bool mm_vm_identity_map(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001022 mm_mode_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001023{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001024 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001025 bool success = mm_ptable_identity_update(
Karl Meakind64aaf82025-02-08 01:12:55 +00001026 ptable, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
Andrew Scullda3df7f2019-01-05 17:49:27 +00001027 ppool);
Andrew Scull80871322018-08-06 12:04:09 +01001028
1029 if (success && ipa != NULL) {
1030 *ipa = ipa_from_pa(begin);
1031 }
1032
1033 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001034}
1035
1036/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001037 * Updates the VM's table such that the given physical address range has no
1038 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +01001039 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001040bool mm_vm_unmap(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001041 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001042{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001043 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001044
Karl Meakind64aaf82025-02-08 01:12:55 +00001045 return mm_vm_identity_map(ptable, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +01001046}
1047
1048/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001049 * Write the given page table of a VM to the debug log.
1050 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001051void mm_vm_dump(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001052{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001053 mm_ptable_dump(ptable, (struct mm_flags){0});
Andrew Scullda3df7f2019-01-05 17:49:27 +00001054}
1055
1056/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001057 * Defragments a stage1 page table.
1058 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001059void mm_stage1_defrag(struct mm_ptable *ptable, struct mpool *ppool)
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001060{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001061 mm_ptable_defrag(ptable, (struct mm_flags){.stage1 = true}, false,
1062 ppool);
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -07001063}
1064
1065/**
Andrew Scullda3df7f2019-01-05 17:49:27 +00001066 * Defragments the VM page table.
1067 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001068void mm_vm_defrag(struct mm_ptable *ptable, struct mpool *ppool,
1069 bool non_secure)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001070{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001071 mm_ptable_defrag(ptable, (struct mm_flags){0}, non_secure, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001072}
1073
1074/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +01001075 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +00001076 * are mapped with the same mode.
1077 *
1078 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001079 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001080bool mm_vm_get_mode(const struct mm_ptable *ptable, ipaddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001081 ipaddr_t end, mm_mode_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001082{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001083 mm_attr_t attrs;
Andrew Scull81e85092018-12-12 12:56:20 +00001084 bool ret;
1085
Karl Meakin1fd4b822025-02-01 17:13:47 +00001086 ret = mm_get_attrs(ptable, ipa_addr(begin), ipa_addr(end), &attrs,
1087 (struct mm_flags){0});
Andrew Scull81e85092018-12-12 12:56:20 +00001088 if (ret) {
1089 *mode = arch_mm_stage2_attrs_to_mode(attrs);
1090 }
1091
1092 return ret;
Andrew Scull80871322018-08-06 12:04:09 +01001093}
1094
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001095/**
1096 * Gets the mode of the given range of virtual addresses if they
1097 * are mapped with the same mode.
1098 *
1099 * Returns true if the range is mapped with the same mode and false otherwise.
1100 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001101bool mm_get_mode(const struct mm_ptable *ptable, vaddr_t begin, vaddr_t end,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001102 mm_mode_t *mode)
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001103{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001104 mm_attr_t attrs;
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001105 bool ret;
1106
Karl Meakind64aaf82025-02-08 01:12:55 +00001107 ret = mm_get_attrs(ptable, va_addr(begin), va_addr(end), &attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +00001108 (struct mm_flags){.stage1 = true});
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001109 if (ret) {
1110 *mode = arch_mm_stage1_attrs_to_mode(attrs);
1111 }
1112
1113 return ret;
1114}
1115
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001116static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1117{
1118 return (struct mm_stage1_locked){.ptable = &ptable};
1119}
1120
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001121struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1122{
1123 return (struct mm_stage1_locked){.ptable = ptable};
1124}
1125
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001126struct mm_stage1_locked mm_lock_stage1(void)
1127{
1128 sl_lock(&ptable_lock);
1129 return mm_stage1_lock_unsafe();
1130}
1131
1132void mm_unlock_stage1(struct mm_stage1_locked *lock)
1133{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001134 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001135 sl_unlock(&ptable_lock);
1136 lock->ptable = NULL;
1137}
1138
Andrew Scull80871322018-08-06 12:04:09 +01001139/**
Andrew Scull80871322018-08-06 12:04:09 +01001140 * Updates the hypervisor page table such that the given physical address range
1141 * is mapped into the address space at the corresponding address range in the
1142 * architecture-agnostic mode provided.
1143 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001144void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Karl Meakin07a69ab2025-02-07 14:53:19 +00001145 paddr_t end, mm_mode_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001146{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001147 struct mm_flags flags = mm_mode_to_flags(mode);
Karl Meakin07a69ab2025-02-07 14:53:19 +00001148
Karl Meakin1fd4b822025-02-01 17:13:47 +00001149 flags.stage1 = true;
Andrew Scull73b89542019-11-20 17:31:26 +00001150
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001151 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001152 arch_mm_mode_to_stage1_attrs(mode), flags,
1153 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001154 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001155 }
1156
1157 return NULL;
1158}
1159
1160/**
1161 * Updates the hypervisor table such that the given physical address range is
1162 * not mapped in the address space.
1163 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001164bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1165 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001166{
Karl Meakin07a69ab2025-02-07 14:53:19 +00001167 mm_mode_t mode = MM_MODE_UNMAPPED_MASK;
Andrew Scull73b89542019-11-20 17:31:26 +00001168
1169 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001170}
1171
1172/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001173 * Defragments the hypervisor page table.
1174 */
1175void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1176{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001177 mm_ptable_defrag(stage1_locked.ptable,
1178 (struct mm_flags){.stage1 = true}, false, ppool);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001179}
1180
1181/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001182 * Initialises memory management for the hypervisor itself.
1183 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001184bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001185{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001186 /* Locking is not enabled yet so fake it, */
1187 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
Karl Meakin07a69ab2025-02-07 14:53:19 +00001188 struct mm_flags flags = {.stage1 = true};
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001189
Karl Meakine8937d92024-03-19 16:04:25 +00001190 dlog_info("text: %#lx - %#lx\n", pa_addr(layout_text_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001191 pa_addr(layout_text_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001192 dlog_info("rodata: %#lx - %#lx\n", pa_addr(layout_rodata_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001193 pa_addr(layout_rodata_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001194 dlog_info("data: %#lx - %#lx\n", pa_addr(layout_data_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001195 pa_addr(layout_data_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001196 dlog_info("stacks: %#lx - %#lx\n", pa_addr(layout_stacks_begin()),
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001197 pa_addr(layout_stacks_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001198
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001199 /* ASID 0 is reserved for use by the hypervisor. */
Karl Meakin07a69ab2025-02-07 14:53:19 +00001200 if (!mm_ptable_init(&ptable, 0, flags, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001201 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001202 return false;
1203 }
1204
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001205 /* Initialize arch_mm before calling below mapping routines */
1206 if (!arch_mm_init(ptable.root)) {
1207 return false;
1208 }
1209
Andrew Walbran48699362019-05-20 14:38:00 +01001210 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001211 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001212
1213 /* Map each section. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001214 CHECK(mm_identity_map(stage1_locked, layout_text_begin(),
1215 layout_text_end(), MM_MODE_X, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001216
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001217 CHECK(mm_identity_map(stage1_locked, layout_rodata_begin(),
1218 layout_rodata_end(), MM_MODE_R, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001219
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001220 CHECK(mm_identity_map(stage1_locked, layout_data_begin(),
1221 layout_data_end(), MM_MODE_R | MM_MODE_W,
1222 ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001223
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001224 /* Arch-specific stack mapping. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001225 CHECK(arch_stack_mm_init(stage1_locked, ppool));
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001226
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001227 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001228}