blob: 78ff8800a6a02448b62d927e2214f48aea9a4d1f [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"
15
Daniel Boulbya2f8c662021-11-26 17:52:53 +000016#include "hf/assert.h"
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"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010022
Andrew Walbran2400ed22018-09-27 14:45:58 +010023/**
24 * This file has functions for managing the level 1 and 2 page tables used by
25 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
26 * and then a level 2 mapping per VM. The design assumes that all page tables
27 * contain only 1-1 mappings, aligned on the block boundaries.
28 */
29
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010030/*
31 * For stage 2, the input is an intermediate physical addresses rather than a
32 * virtual address so:
33 */
Andrew Scull80871322018-08-06 12:04:09 +010034static_assert(
35 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
36 "Currently, the same code manages the stage 1 and stage 2 page tables "
37 "which only works if the virtual and intermediate physical addresses "
38 "are the same size. It looks like that assumption might not be holding "
39 "so we need to check that everything is going to be ok.");
40
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010041static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010042static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010043
Andrew Scullda241972019-01-05 18:17:48 +000044static bool mm_stage2_invalidate = false;
45
46/**
47 * After calling this function, modifications to stage-2 page tables will use
48 * break-before-make and invalidate the TLB for the affected range.
49 */
50void mm_vm_enable_invalidation(void)
51{
52 mm_stage2_invalidate = true;
53}
54
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010055/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010056 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010057 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010058static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010059{
60 return ptr_from_va(va_from_pa(pa));
61}
62
63/**
Andrew Scull80871322018-08-06 12:04:09 +010064 * Rounds an address down to a page boundary.
65 */
66static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
67{
68 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
69}
70
71/**
72 * Rounds an address up to a page boundary.
73 */
74static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
75{
76 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
77}
78
79/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010080 * Calculates the size of the address space represented by a page table entry at
81 * the given level.
82 */
Andrew Sculle9827712018-10-19 14:54:20 +010083static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010084{
Andrew Scull78d6fd92018-09-06 15:08:36 +010085 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010086}
87
88/**
Andrew Scullcae45572018-12-13 15:46:30 +000089 * Gets the address of the start of the next block of the given size. The size
90 * must be a power of two.
91 */
92static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
93 size_t block_size)
94{
95 return (addr + block_size) & ~(block_size - 1);
96}
97
98/**
99 * Gets the physical address of the start of the next block of the given size.
100 * The size must be a power of two.
101 */
102static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
103{
104 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
105}
106
107/**
Andrew Scull80871322018-08-06 12:04:09 +0100108 * For a given address, calculates the maximum (plus one) address that can be
109 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100110 */
Andrew Sculle9827712018-10-19 14:54:20 +0100111static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100112{
113 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000114
Andrew Scull80871322018-08-06 12:04:09 +0100115 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100116}
117
118/**
Andrew Scull80871322018-08-06 12:04:09 +0100119 * For a given address, calculates the index at which its entry is stored in a
120 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100121 */
Andrew Sculle9827712018-10-19 14:54:20 +0100122static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100123{
Andrew Scull80871322018-08-06 12:04:09 +0100124 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000125
Andrew Scull78d6fd92018-09-06 15:08:36 +0100126 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100127}
128
129/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000130 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100131 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000132static struct mm_page_table *mm_alloc_page_tables(size_t count,
133 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100134{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000135 if (count == 1) {
136 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100137 }
138
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000139 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100140}
141
142/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000143 * Returns the maximum level in the page table given the flags.
144 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000145static uint8_t mm_max_level(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_max_level()
148 : arch_mm_stage2_max_level();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000149}
150
151/**
152 * Returns the number of root-level tables given the flags.
153 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000154static uint8_t mm_root_table_count(struct mm_flags flags)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000155{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000156 return flags.stage1 ? arch_mm_stage1_root_table_count()
157 : arch_mm_stage2_root_table_count();
Andrew Scullda3df7f2019-01-05 17:49:27 +0000158}
159
160/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000161 * Invalidates the TLB for the given address range.
162 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000163static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end,
164 struct mm_flags flags, bool non_secure,
165 uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000166{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000167 if (flags.stage1) {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800168 arch_mm_invalidate_stage1_range(id, va_init(begin),
169 va_init(end));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000170 } else {
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800171 arch_mm_invalidate_stage2_range(id, ipa_init(begin),
Olivier Deprez6f400372022-03-07 09:31:08 +0100172 ipa_init(end), non_secure);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000173 }
174}
175
176/**
177 * Frees all page-table-related memory associated with the given pte at the
178 * given level, including any subtables recursively.
179 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100180// NOLINTNEXTLINE(misc-no-recursion)
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000181static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000182{
183 struct mm_page_table *table;
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000184
185 if (!arch_mm_pte_is_table(pte, level)) {
186 return;
187 }
188
189 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000190 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Karl Meakind64aaf82025-02-08 01:12:55 +0000191 for (size_t i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000192 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000193 }
194
195 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000196 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000197}
198
199/**
David Brazdil711fbe92019-08-06 13:39:58 +0100200 * Returns the first address which cannot be encoded in page tables given by
201 * `flags`. It is the exclusive end of the address space created by the tables.
202 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000203ptable_addr_t mm_ptable_addr_space_end(struct mm_flags flags)
David Brazdil711fbe92019-08-06 13:39:58 +0100204{
205 return mm_root_table_count(flags) *
206 mm_entry_size(mm_max_level(flags) + 1);
207}
208
209/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000210 * Initialises the given page table.
211 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000212bool mm_ptable_init(struct mm_ptable *ptable, uint16_t id,
213 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000214{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000215 struct mm_page_table *tables;
216 uint8_t root_table_count = mm_root_table_count(flags);
217
218 tables = mm_alloc_page_tables(root_table_count, ppool);
219 if (tables == NULL) {
220 return false;
221 }
222
Karl Meakind64aaf82025-02-08 01:12:55 +0000223 for (size_t i = 0; i < root_table_count; i++) {
224 for (size_t j = 0; j < MM_PTE_PER_PAGE; j++) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000225 tables[i].entries[j] =
226 arch_mm_absent_pte(mm_max_level(flags));
227 }
228 }
229
230 /*
231 * TODO: halloc could return a virtual or physical address if mm not
232 * enabled?
233 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000234 ptable->root = pa_init((uintpaddr_t)tables);
235 ptable->id = id;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000236 return true;
237}
238
239/**
240 * Frees all memory associated with the give page table.
241 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000242static void mm_ptable_fini(const struct mm_ptable *ptable,
243 struct mm_flags flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000244{
Karl Meakind64aaf82025-02-08 01:12:55 +0000245 struct mm_page_table *tables = mm_page_table_from_pa(ptable->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000246 uint8_t level = mm_max_level(flags);
247 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000248
Karl Meakind64aaf82025-02-08 01:12:55 +0000249 for (size_t i = 0; i < root_table_count; ++i) {
250 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000251 mm_free_page_pte(tables[i].entries[j], level, ppool);
252 }
253 }
254
255 mpool_add_chunk(ppool, tables,
256 sizeof(struct mm_page_table) * root_table_count);
257}
258
259/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000260 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000261 * are valid, it performs a break-before-make sequence where it first writes an
262 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
263 * This is to prevent cases where CPUs have different 'valid' values in their
264 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000265 */
266static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000267 uint8_t level, struct mm_flags flags,
268 bool non_secure, struct mpool *ppool, uint16_t id)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000269{
270 pte_t v = *pte;
271
272 /*
273 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000274 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000275 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000276 if ((flags.stage1 || mm_stage2_invalidate) &&
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800277 arch_mm_pte_is_valid(v, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000278 *pte = arch_mm_absent_pte(level);
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800279 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000280 non_secure, id);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000281 }
282
283 /* Assign the new pte. */
284 *pte = new_pte;
285
286 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000287 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000288}
289
290/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100291 * Populates the provided page table entry with a reference to another table if
292 * needed, that is, if it does not yet point to another table.
293 *
294 * Returns a pointer to the table the entry now points to.
295 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000296static struct mm_page_table *mm_populate_table_pte(
297 ptable_addr_t begin, pte_t *pte, uint8_t level, struct mm_flags flags,
298 bool non_secure, struct mpool *ppool, uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100299{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100300 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100301 pte_t v = *pte;
302 pte_t new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100303 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100304 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100305
306 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100307 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000308 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100309 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100310
311 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000312 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100313 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000314 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100315 return NULL;
316 }
317
318 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100319 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100320 inc = mm_entry_size(level_below);
321 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000322 arch_mm_block_from_pte(v, level),
323 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100324 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100325 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100326 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100327 }
328
329 /* Initialise entries in the new table. */
Karl Meakind64aaf82025-02-08 01:12:55 +0000330 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100331 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100332 new_pte += inc;
333 }
334
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000335 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100336 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000337
338 /* Replace the pte entry, doing a break-before-make if needed. */
339 mm_replace_entry(begin, pte,
340 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Karl Meakin1fd4b822025-02-01 17:13:47 +0000341 level, flags, non_secure, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100342
343 return ntable;
344}
345
346/**
Andrew Scull80871322018-08-06 12:04:09 +0100347 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100348 * physical range using the provided (architecture-specific) attributes. Or if
Karl Meakin1fd4b822025-02-01 17:13:47 +0000349 * `flags.unmap` is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100350 *
351 * This function calls itself recursively if it needs to update additional
352 * levels, but the recursion is bound by the maximum number of levels in a page
353 * table.
354 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100355// NOLINTNEXTLINE(misc-no-recursion)
Andrew Scull80871322018-08-06 12:04:09 +0100356static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100357 uint64_t attrs, struct mm_page_table *table,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000358 uint8_t level, struct mm_flags flags,
359 struct mpool *ppool, uint16_t id)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100360{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100361 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100362 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100363 size_t entry_size = mm_entry_size(level);
Karl Meakin1fd4b822025-02-01 17:13:47 +0000364 bool commit = flags.commit;
365 bool unmap = flags.unmap;
366 bool non_secure = ((attrs & (1ULL << 57)) != 0);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100367
Andrew Scull265ada92018-07-30 15:19:01 +0100368 /* Cap end so that we don't go over the current level max. */
369 if (end > level_end) {
370 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100371 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100372
373 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100374 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100375 if (unmap ? !arch_mm_pte_is_present(*pte, level)
376 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000377 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100378 /*
379 * If the entry is already mapped with the right
380 * attributes, or already absent in the case of
381 * unmapping, no need to do anything; carry on to the
382 * next entry.
383 */
384 } else if ((end - begin) >= entry_size &&
385 (unmap || arch_mm_is_block_allowed(level)) &&
386 (begin & (entry_size - 1)) == 0) {
387 /*
388 * If the entire entry is within the region we want to
389 * map, map/unmap the whole entry.
390 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100391 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000392 pte_t new_pte =
393 unmap ? arch_mm_absent_pte(level)
394 : arch_mm_block_pte(level, pa,
395 attrs);
396 mm_replace_entry(begin, pte, new_pte, level,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000397 flags, non_secure, ppool, id);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100398 }
399 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100400 /*
401 * If the entry is already a subtable get it; otherwise
402 * replace it with an equivalent subtable and get that.
403 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000404 struct mm_page_table *nt =
405 mm_populate_table_pte(begin, pte, level, flags,
406 non_secure, ppool, id);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100407 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100408 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100409 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100410
Andrew Walbran6324fc92018-10-03 11:46:43 +0100411 /*
412 * Recurse to map/unmap the appropriate entries within
413 * the subtable.
414 */
Andrew Scull80871322018-08-06 12:04:09 +0100415 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Raghu Krishnamurthy8fdd6df2021-02-03 18:30:59 -0800416 flags, ppool, id)) {
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 }
420
Andrew Scullcae45572018-12-13 15:46:30 +0000421 begin = mm_start_of_next_block(begin, entry_size);
422 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100423 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100424 }
425
426 return true;
427}
428
429/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000430 * Updates the page table from the root to map the given address range to a
431 * physical range using the provided (architecture-specific) attributes. Or if
Karl Meakin1fd4b822025-02-01 17:13:47 +0000432 * `flags.unmap` is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000433 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000434static bool mm_map_root(struct mm_ptable *ptable, ptable_addr_t begin,
Andrew Scull1ba470e2018-10-31 15:14:31 +0000435 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000436 struct mm_flags flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000437{
438 size_t root_table_size = mm_entry_size(root_level);
Karl Meakind64aaf82025-02-08 01:12:55 +0000439 struct mm_page_table *table = &mm_page_table_from_pa(
440 ptable->root)[mm_index(begin, root_level)];
Andrew Scull1ba470e2018-10-31 15:14:31 +0000441
442 while (begin < end) {
443 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Karl Meakind64aaf82025-02-08 01:12:55 +0000444 root_level - 1, flags, ppool, ptable->id)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000445 return false;
446 }
Andrew Scullcae45572018-12-13 15:46:30 +0000447 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000448 table++;
449 }
450
451 return true;
452}
453
454/**
Andrew Scull80871322018-08-06 12:04:09 +0100455 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000456 * or not mapped into the address space with the architecture-agnostic mode
Karl Meakin1fd4b822025-02-01 17:13:47 +0000457 * provided. Only commits the change if `flags.commit` is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100458 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000459static bool mm_ptable_identity_map(struct mm_ptable *ptable, paddr_t pa_begin,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000460 paddr_t pa_end, uint64_t attrs,
461 struct mm_flags flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100462{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000463 uint8_t root_level = mm_max_level(flags) + 1;
David Brazdil711fbe92019-08-06 13:39:58 +0100464 ptable_addr_t ptable_end = mm_ptable_addr_space_end(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000465 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
466 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100467
Andrew Scull1ba470e2018-10-31 15:14:31 +0000468 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100469 * Assert condition to communicate the API constraint of mm_max_level(),
470 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000471 */
Daniel Boulbya2f8c662021-11-26 17:52:53 +0000472 assert(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000473
474 /* Cap end to stay within the bounds of the page table. */
475 if (end > ptable_end) {
476 end = ptable_end;
477 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100478
Karl Meakind64aaf82025-02-08 01:12:55 +0000479 if (!mm_map_root(ptable, begin, end, attrs, root_level, flags, ppool)) {
Andrew Walbran58a6e542019-11-19 14:23:15 +0000480 return false;
481 }
482
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800483 /*
484 * All TLB invalidations must be complete already if any entries were
485 * replaced by mm_replace_entry. Sync all page table writes so that code
486 * following this can use them.
487 */
488 arch_mm_sync_table_writes();
Andrew Walbran58a6e542019-11-19 14:23:15 +0000489
490 return true;
491}
492
Andrew Scull4e83cef2019-11-19 14:17:54 +0000493/*
494 * Prepares the given page table for the given address mapping such that it
495 * will be able to commit the change without failure. It does so by ensuring
496 * the smallest granularity needed is available. This remains valid provided
Fuad Tabba9dc276f2020-07-16 09:29:32 +0100497 * subsequent operations do not decrease the granularity.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000498 *
499 * In particular, multiple calls to this function will result in the
500 * corresponding calls to commit the changes to succeed.
501 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000502static bool mm_ptable_identity_prepare(struct mm_ptable *ptable,
503 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000504 uint64_t attrs, struct mm_flags flags,
Karl Meakind64aaf82025-02-08 01:12:55 +0000505 struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000506{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000507 flags.commit = false;
Karl Meakind64aaf82025-02-08 01:12:55 +0000508 return mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
509 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000510}
511
512/**
513 * Commits the given address mapping to the page table assuming the operation
514 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
515 * ensure this condition.
516 *
517 * Without the table being properly prepared, the commit may only partially
518 * complete if it runs out of memory resulting in an inconsistent state that
519 * isn't handled.
520 *
521 * Since the non-failure assumtion is used in the reasoning about the atomicity
522 * of higher level memory operations, any detected violations result in a panic.
523 *
524 * TODO: remove ppool argument to be sure no changes are made.
525 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000526static void mm_ptable_identity_commit(struct mm_ptable *ptable,
527 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000528 uint64_t attrs, struct mm_flags flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000529 struct mpool *ppool)
530{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000531 flags.commit = true;
532 CHECK(mm_ptable_identity_map(ptable, pa_begin, pa_end, attrs, flags,
533 ppool));
Andrew Scull4e83cef2019-11-19 14:17:54 +0000534}
535
Andrew Walbran58a6e542019-11-19 14:23:15 +0000536/**
537 * Updates the given table such that the given physical address range is mapped
538 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000539 * provided.
540 *
541 * The page table is updated using the separate prepare and commit stages so
542 * that, on failure, a partial update of the address space cannot happen. The
543 * table may be left with extra internal tables but the address space is
544 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000545 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000546static bool mm_ptable_identity_update(struct mm_ptable *ptable,
547 paddr_t pa_begin, paddr_t pa_end,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000548 uint64_t attrs, struct mm_flags flags,
Andrew Walbran58a6e542019-11-19 14:23:15 +0000549 struct mpool *ppool)
550{
Karl Meakind64aaf82025-02-08 01:12:55 +0000551 if (!mm_ptable_identity_prepare(ptable, pa_begin, pa_end, attrs, flags,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000552 ppool)) {
553 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100554 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100555
Karl Meakind64aaf82025-02-08 01:12:55 +0000556 mm_ptable_identity_commit(ptable, pa_begin, pa_end, attrs, flags,
557 ppool);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000558
559 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100560}
561
562/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100563 * Writes the given table to the debug log, calling itself recursively to
564 * write sub-tables.
565 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100566// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakind64aaf82025-02-08 01:12:55 +0000567static void mm_dump_table_recursive(const struct mm_page_table *ptable,
568 uint8_t level, int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100569{
Karl Meakind64aaf82025-02-08 01:12:55 +0000570 for (size_t i = 0; i < MM_PTE_PER_PAGE; i++) {
571 if (!arch_mm_pte_is_present(ptable->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100572 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100573 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100574
Karl Meakine8937d92024-03-19 16:04:25 +0000575 dlog("%*s%lx: %lx\n", 4 * (max_level - level), "", i,
Karl Meakind64aaf82025-02-08 01:12:55 +0000576 ptable->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100577
Karl Meakind64aaf82025-02-08 01:12:55 +0000578 if (arch_mm_pte_is_table(ptable->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100579 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100580 mm_page_table_from_pa(arch_mm_table_from_pte(
Karl Meakind64aaf82025-02-08 01:12:55 +0000581 ptable->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100582 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100583 }
584 }
585}
586
587/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000588 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100589 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000590static void mm_ptable_dump(const struct mm_ptable *ptable,
591 struct mm_flags flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100592{
Karl Meakind64aaf82025-02-08 01:12:55 +0000593 struct mm_page_table *tables = mm_page_table_from_pa(ptable->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000594 uint8_t max_level = mm_max_level(flags);
595 uint8_t root_table_count = mm_root_table_count(flags);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000596
Karl Meakind64aaf82025-02-08 01:12:55 +0000597 for (size_t i = 0; i < root_table_count; ++i) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000598 mm_dump_table_recursive(&tables[i], max_level, max_level);
599 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100600}
601
602/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000603 * Given the table PTE entries all have identical attributes, returns the single
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800604 * entry with which it can be replaced.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100605 */
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800606static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100607{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100608 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100609 uint64_t block_attrs;
610 uint64_t table_attrs;
611 uint64_t combined_attrs;
612 paddr_t block_address;
613
Andrew Scullb6b9b562018-12-21 14:41:35 +0000614 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
615
616 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000617 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100618 }
619
Andrew Scullb6b9b562018-12-21 14:41:35 +0000620 /* Might not be possible to merge the table into a single block. */
621 if (!arch_mm_is_block_allowed(level)) {
622 return table_pte;
623 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000624
Andrew Scullb6b9b562018-12-21 14:41:35 +0000625 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000626 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000627 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100628 combined_attrs =
629 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000630 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000631
Andrew Walbran2400ed22018-09-27 14:45:58 +0100632 return arch_mm_block_pte(level, block_address, combined_attrs);
633}
634
635/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000636 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000637 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100638 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100639// NOLINTNEXTLINE(misc-no-recursion)
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800640static void mm_ptable_defrag_entry(ptable_addr_t base_addr, pte_t *entry,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000641 uint8_t level, struct mm_flags flags,
642 bool non_secure, struct mpool *ppool,
643 uint16_t id)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100644{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100645 struct mm_page_table *table;
Andrew Scull12122ce2019-11-19 14:21:07 +0000646 bool mergeable;
647 bool base_present;
648 uint64_t base_attrs;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800649 pte_t new_entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100650
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800651 if (!arch_mm_pte_is_table(*entry, level)) {
652 return;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100653 }
654
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800655 table = mm_page_table_from_pa(arch_mm_table_from_pte(*entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100656
Andrew Scull12122ce2019-11-19 14:21:07 +0000657 /* Defrag the first entry in the table and use it as the base entry. */
658 static_assert(MM_PTE_PER_PAGE >= 1, "There must be at least one PTE.");
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800659
660 mm_ptable_defrag_entry(base_addr, &(table->entries[0]), level - 1,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000661 flags, non_secure, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800662
Andrew Scull12122ce2019-11-19 14:21:07 +0000663 base_present = arch_mm_pte_is_present(table->entries[0], level - 1);
664 base_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
665
Andrew Walbran2400ed22018-09-27 14:45:58 +0100666 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000667 * Defrag the remaining entries in the table and check whether they are
668 * compatible with the base entry meaning the table can be merged into a
669 * block entry. It assumes addresses are contiguous due to identity
670 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100671 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000672 mergeable = true;
Karl Meakind64aaf82025-02-08 01:12:55 +0000673 for (size_t i = 1; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scull12122ce2019-11-19 14:21:07 +0000674 bool present;
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800675 ptable_addr_t block_addr =
676 base_addr + (i * mm_entry_size(level - 1));
Andrew Scull12122ce2019-11-19 14:21:07 +0000677
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800678 mm_ptable_defrag_entry(block_addr, &(table->entries[i]),
Karl Meakin1fd4b822025-02-01 17:13:47 +0000679 level - 1, flags, non_secure, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800680
Andrew Scull12122ce2019-11-19 14:21:07 +0000681 present = arch_mm_pte_is_present(table->entries[i], level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100682
Andrew Scull12122ce2019-11-19 14:21:07 +0000683 if (present != base_present) {
684 mergeable = false;
685 continue;
686 }
687
688 if (!present) {
689 continue;
690 }
691
692 if (!arch_mm_pte_is_block(table->entries[i], level - 1)) {
693 mergeable = false;
694 continue;
695 }
696
697 if (arch_mm_pte_attrs(table->entries[i], level - 1) !=
698 base_attrs) {
699 mergeable = false;
700 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100701 }
702 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000703
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800704 if (!mergeable) {
705 return;
Andrew Scull12122ce2019-11-19 14:21:07 +0000706 }
707
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800708 new_entry = mm_merge_table_pte(*entry, level);
709 if (*entry != new_entry) {
Olivier Deprez6f400372022-03-07 09:31:08 +0100710 mm_replace_entry(base_addr, entry, (uintptr_t)new_entry, level,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000711 flags, non_secure, ppool, id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800712 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100713}
714
715/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100716 * Defragments the given page table by converting page table references to
717 * blocks whenever possible.
718 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000719static void mm_ptable_defrag(struct mm_ptable *ptable, struct mm_flags flags,
720 bool non_secure, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100721{
Karl Meakind64aaf82025-02-08 01:12:55 +0000722 struct mm_page_table *tables = mm_page_table_from_pa(ptable->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000723 uint8_t level = mm_max_level(flags);
724 uint8_t root_table_count = mm_root_table_count(flags);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800725 ptable_addr_t block_addr = 0;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100726
727 /*
728 * Loop through each entry in the table. If it points to another table,
729 * check if that table can be replaced by a block or an absent entry.
730 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000731 for (size_t i = 0; i < root_table_count; ++i) {
732 for (size_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
733 mm_ptable_defrag_entry(
734 block_addr, &(tables[i].entries[j]), level,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000735 flags, non_secure, ppool, ptable->id);
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800736 block_addr = mm_start_of_next_block(
737 block_addr, mm_entry_size(level));
Andrew Scull1ba470e2018-10-31 15:14:31 +0000738 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100739 }
Raghu Krishnamurthyc1012d62021-01-24 19:19:31 -0800740
741 arch_mm_sync_table_writes();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100742}
743
744/**
Andrew Scull81e85092018-12-12 12:56:20 +0000745 * Gets the attributes applied to the given range of stage-2 addresses at the
746 * given level.
747 *
748 * The `got_attrs` argument is initially passed as false until `attrs` contains
749 * attributes of the memory region at which point it is passed as true.
750 *
751 * The value returned in `attrs` is only valid if the function returns true.
752 *
753 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100754 */
Daniel Boulby8adf7482021-09-22 15:12:44 +0100755// NOLINTNEXTLINE(misc-no-recursion)
Karl Meakind64aaf82025-02-08 01:12:55 +0000756static bool mm_ptable_get_attrs_level(const struct mm_page_table *table,
Andrew Scull81e85092018-12-12 12:56:20 +0000757 ptable_addr_t begin, ptable_addr_t end,
758 uint8_t level, bool got_attrs,
759 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100760{
Karl Meakind64aaf82025-02-08 01:12:55 +0000761 const pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000762 ptable_addr_t level_end = mm_level_end(begin, level);
763 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100764
Andrew Scull81e85092018-12-12 12:56:20 +0000765 /* Cap end so that we don't go over the current level max. */
766 if (end > level_end) {
767 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100768 }
769
Andrew Scull81e85092018-12-12 12:56:20 +0000770 /* Check that each entry is owned. */
771 while (begin < end) {
772 if (arch_mm_pte_is_table(*pte, level)) {
773 if (!mm_ptable_get_attrs_level(
774 mm_page_table_from_pa(
775 arch_mm_table_from_pte(*pte,
776 level)),
777 begin, end, level - 1, got_attrs, attrs)) {
778 return false;
779 }
780 got_attrs = true;
781 } else {
782 if (!got_attrs) {
783 *attrs = arch_mm_pte_attrs(*pte, level);
784 got_attrs = true;
785 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
786 return false;
787 }
788 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100789
Andrew Scull81e85092018-12-12 12:56:20 +0000790 begin = mm_start_of_next_block(begin, entry_size);
791 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100792 }
793
Andrew Scullc66a04d2018-12-07 13:41:56 +0000794 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000795 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100796}
797
798/**
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -0800799 * Gets the attributes applied to the given range of addresses in the page
800 * tables.
Andrew Scull81e85092018-12-12 12:56:20 +0000801 *
802 * The value returned in `attrs` is only valid if the function returns true.
803 *
804 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100805 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000806static bool mm_get_attrs(const struct mm_ptable *ptable, ptable_addr_t begin,
Karl Meakin1fd4b822025-02-01 17:13:47 +0000807 ptable_addr_t end, uint64_t *attrs,
808 struct mm_flags flags)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100809{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000810 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000811 uint8_t root_level = max_level + 1;
812 size_t root_table_size = mm_entry_size(root_level);
813 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000814 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000815 struct mm_page_table *table;
816 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100817
Andrew Scull81e85092018-12-12 12:56:20 +0000818 begin = mm_round_down_to_page(begin);
819 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100820
Andrew Scull81e85092018-12-12 12:56:20 +0000821 /* Fail if the addresses are out of range. */
822 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000823 return false;
824 }
825
Karl Meakind64aaf82025-02-08 01:12:55 +0000826 table = &mm_page_table_from_pa(
827 ptable->root)[mm_index(begin, root_level)];
Andrew Scull81e85092018-12-12 12:56:20 +0000828 while (begin < end) {
829 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
830 got_attrs, attrs)) {
831 return false;
832 }
833
834 got_attrs = true;
835 begin = mm_start_of_next_block(begin, root_table_size);
836 table++;
837 }
838
839 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100840}
841
Karl Meakind64aaf82025-02-08 01:12:55 +0000842bool mm_vm_init(struct mm_ptable *ptable, uint16_t id, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100843{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000844 return mm_ptable_init(ptable, id, (struct mm_flags){0}, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100845}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100846
Karl Meakind64aaf82025-02-08 01:12:55 +0000847void mm_vm_fini(const struct mm_ptable *ptable, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000848{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000849 mm_ptable_fini(ptable, (struct mm_flags){0}, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000850}
851
852/**
Andrew Scull73b89542019-11-20 17:31:26 +0000853 * Selects flags to pass to the page table manipulation operation based on the
854 * mapping mode.
855 */
Karl Meakin1fd4b822025-02-01 17:13:47 +0000856static struct mm_flags mm_mode_to_flags(uint32_t mode)
Andrew Scull73b89542019-11-20 17:31:26 +0000857{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000858 struct mm_flags flags = {0};
859
Andrew Scull73b89542019-11-20 17:31:26 +0000860 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
Karl Meakin1fd4b822025-02-01 17:13:47 +0000861 flags.unmap = true;
Andrew Scull73b89542019-11-20 17:31:26 +0000862 }
863
Karl Meakin1fd4b822025-02-01 17:13:47 +0000864 return flags;
Andrew Scull73b89542019-11-20 17:31:26 +0000865}
866
867/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000868 * See `mm_ptable_identity_prepare`.
869 *
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800870 * This must be called before `mm_identity_commit` for the same mapping.
871 *
872 * Returns true on success, or false if the update would fail.
873 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000874bool mm_identity_prepare(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800875 uint32_t mode, struct mpool *ppool)
876{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000877 struct mm_flags flags = mm_mode_to_flags(mode);
878 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800879
Karl Meakind64aaf82025-02-08 01:12:55 +0000880 return mm_ptable_identity_prepare(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800881 arch_mm_mode_to_stage1_attrs(mode),
882 flags, ppool);
883}
884
885/**
886 * See `mm_ptable_identity_commit`.
887 *
888 * `mm_identity_prepare` must be called before this for the same mapping.
889 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000890void *mm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800891 uint32_t mode, struct mpool *ppool)
892{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000893 struct mm_flags flags = mm_mode_to_flags(mode);
894 flags.stage1 = true;
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800895
Karl Meakind64aaf82025-02-08 01:12:55 +0000896 mm_ptable_identity_commit(ptable, begin, end,
Raghu Krishnamurthy43fe93a2021-01-31 16:38:38 -0800897 arch_mm_mode_to_stage1_attrs(mode), flags,
898 ppool);
899 return ptr_from_va(va_from_pa(begin));
900}
901
902/**
903 * See `mm_ptable_identity_prepare`.
904 *
Andrew Scull4e83cef2019-11-19 14:17:54 +0000905 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000906 *
907 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000908 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000909bool mm_vm_identity_prepare(struct mm_ptable *ptable, paddr_t begin,
910 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000911{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000912 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000913
Karl Meakind64aaf82025-02-08 01:12:55 +0000914 return mm_ptable_identity_prepare(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000915 arch_mm_mode_to_stage2_attrs(mode),
916 flags, ppool);
917}
918
919/**
920 * See `mm_ptable_identity_commit`.
921 *
922 * `mm_vm_identity_prepare` must be called before this for the same mapping.
923 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000924void mm_vm_identity_commit(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000925 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000926{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000927 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scull4e83cef2019-11-19 14:17:54 +0000928
Karl Meakind64aaf82025-02-08 01:12:55 +0000929 mm_ptable_identity_commit(ptable, begin, end,
Andrew Scull4e83cef2019-11-19 14:17:54 +0000930 arch_mm_mode_to_stage2_attrs(mode), flags,
931 ppool);
932
933 if (ipa != NULL) {
934 *ipa = ipa_from_pa(begin);
935 }
936}
937
938/**
Andrew Scull80871322018-08-06 12:04:09 +0100939 * Updates a VM's page table such that the given physical address range is
940 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100941 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000942 *
943 * mm_vm_defrag should always be called after a series of page table updates,
944 * whether they succeed or fail. This is because on failure extra page table
945 * entries may have been allocated and then not used, while on success it may be
946 * possible to compact the page table by merging several entries into a block.
947 *
948 * Returns true on success, or false if the update failed and no changes were
949 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100950 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000951bool mm_vm_identity_map(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000952 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100953{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000954 struct mm_flags flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000955 bool success = mm_ptable_identity_update(
Karl Meakind64aaf82025-02-08 01:12:55 +0000956 ptable, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000957 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100958
959 if (success && ipa != NULL) {
960 *ipa = ipa_from_pa(begin);
961 }
962
963 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100964}
965
966/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000967 * Updates the VM's table such that the given physical address range has no
968 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100969 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000970bool mm_vm_unmap(struct mm_ptable *ptable, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000971 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100972{
Andrew Scull73b89542019-11-20 17:31:26 +0000973 uint32_t mode = MM_MODE_UNMAPPED_MASK;
974
Karl Meakind64aaf82025-02-08 01:12:55 +0000975 return mm_vm_identity_map(ptable, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +0100976}
977
978/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000979 * Write the given page table of a VM to the debug log.
980 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000981void mm_vm_dump(const struct mm_ptable *ptable)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000982{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000983 mm_ptable_dump(ptable, (struct mm_flags){0});
Andrew Scullda3df7f2019-01-05 17:49:27 +0000984}
985
986/**
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -0700987 * Defragments a stage1 page table.
988 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000989void mm_stage1_defrag(struct mm_ptable *ptable, struct mpool *ppool)
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -0700990{
Karl Meakin1fd4b822025-02-01 17:13:47 +0000991 mm_ptable_defrag(ptable, (struct mm_flags){.stage1 = true}, false,
992 ppool);
Raghu Krishnamurthy7ad3d142021-03-28 00:47:35 -0700993}
994
995/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000996 * Defragments the VM page table.
997 */
Karl Meakind64aaf82025-02-08 01:12:55 +0000998void mm_vm_defrag(struct mm_ptable *ptable, struct mpool *ppool,
999 bool non_secure)
Andrew Scullda3df7f2019-01-05 17:49:27 +00001000{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001001 mm_ptable_defrag(ptable, (struct mm_flags){0}, non_secure, ppool);
Andrew Scullda3df7f2019-01-05 17:49:27 +00001002}
1003
1004/**
Fuad Tabba9dc276f2020-07-16 09:29:32 +01001005 * Gets the mode of the given range of intermediate physical addresses if they
Andrew Scull81e85092018-12-12 12:56:20 +00001006 * are mapped with the same mode.
1007 *
1008 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +01001009 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001010bool mm_vm_get_mode(const struct mm_ptable *ptable, ipaddr_t begin,
1011 ipaddr_t end, uint32_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +01001012{
Andrew Scull81e85092018-12-12 12:56:20 +00001013 uint64_t attrs;
1014 bool ret;
1015
Karl Meakin1fd4b822025-02-01 17:13:47 +00001016 ret = mm_get_attrs(ptable, ipa_addr(begin), ipa_addr(end), &attrs,
1017 (struct mm_flags){0});
Andrew Scull81e85092018-12-12 12:56:20 +00001018 if (ret) {
1019 *mode = arch_mm_stage2_attrs_to_mode(attrs);
1020 }
1021
1022 return ret;
Andrew Scull80871322018-08-06 12:04:09 +01001023}
1024
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001025/**
1026 * Gets the mode of the given range of virtual addresses if they
1027 * are mapped with the same mode.
1028 *
1029 * Returns true if the range is mapped with the same mode and false otherwise.
1030 */
Karl Meakind64aaf82025-02-08 01:12:55 +00001031bool mm_get_mode(const struct mm_ptable *ptable, vaddr_t begin, vaddr_t end,
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001032 uint32_t *mode)
1033{
1034 uint64_t attrs;
1035 bool ret;
1036
Karl Meakind64aaf82025-02-08 01:12:55 +00001037 ret = mm_get_attrs(ptable, va_addr(begin), va_addr(end), &attrs,
Karl Meakin1fd4b822025-02-01 17:13:47 +00001038 (struct mm_flags){.stage1 = true});
Raghu Krishnamurthy2323d722021-02-12 22:55:38 -08001039 if (ret) {
1040 *mode = arch_mm_stage1_attrs_to_mode(attrs);
1041 }
1042
1043 return ret;
1044}
1045
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001046static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
1047{
1048 return (struct mm_stage1_locked){.ptable = &ptable};
1049}
1050
Raghu Krishnamurthyd3ab8c32021-02-10 19:11:30 -08001051struct mm_stage1_locked mm_lock_ptable_unsafe(struct mm_ptable *ptable)
1052{
1053 return (struct mm_stage1_locked){.ptable = ptable};
1054}
1055
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001056struct mm_stage1_locked mm_lock_stage1(void)
1057{
1058 sl_lock(&ptable_lock);
1059 return mm_stage1_lock_unsafe();
1060}
1061
1062void mm_unlock_stage1(struct mm_stage1_locked *lock)
1063{
Andrew Scull877ae4b2019-07-02 12:52:33 +01001064 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001065 sl_unlock(&ptable_lock);
1066 lock->ptable = NULL;
1067}
1068
Andrew Scull80871322018-08-06 12:04:09 +01001069/**
Andrew Scull80871322018-08-06 12:04:09 +01001070 * Updates the hypervisor page table such that the given physical address range
1071 * is mapped into the address space at the corresponding address range in the
1072 * architecture-agnostic mode provided.
1073 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001074void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Andrew Walbran1281ed42019-10-22 17:23:40 +01001075 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +01001076{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001077 struct mm_flags flags = mm_mode_to_flags(mode);
1078 flags.stage1 = true;
Andrew Scull73b89542019-11-20 17:31:26 +00001079
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001080 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +00001081 arch_mm_mode_to_stage1_attrs(mode), flags,
1082 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +01001083 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +01001084 }
1085
1086 return NULL;
1087}
1088
1089/**
1090 * Updates the hypervisor table such that the given physical address range is
1091 * not mapped in the address space.
1092 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001093bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1094 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001095{
Andrew Scull73b89542019-11-20 17:31:26 +00001096 uint32_t mode = MM_MODE_UNMAPPED_MASK;
1097
1098 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001099}
1100
1101/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001102 * Defragments the hypervisor page table.
1103 */
1104void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1105{
Karl Meakin1fd4b822025-02-01 17:13:47 +00001106 mm_ptable_defrag(stage1_locked.ptable,
1107 (struct mm_flags){.stage1 = true}, false, ppool);
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001108}
1109
1110/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001111 * Initialises memory management for the hypervisor itself.
1112 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001113bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001114{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001115 /* Locking is not enabled yet so fake it, */
1116 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1117
Karl Meakine8937d92024-03-19 16:04:25 +00001118 dlog_info("text: %#lx - %#lx\n", pa_addr(layout_text_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001119 pa_addr(layout_text_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001120 dlog_info("rodata: %#lx - %#lx\n", pa_addr(layout_rodata_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001121 pa_addr(layout_rodata_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001122 dlog_info("data: %#lx - %#lx\n", pa_addr(layout_data_begin()),
Andrew Walbran17eebf92020-02-05 16:35:49 +00001123 pa_addr(layout_data_end()));
Karl Meakine8937d92024-03-19 16:04:25 +00001124 dlog_info("stacks: %#lx - %#lx\n", pa_addr(layout_stacks_begin()),
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001125 pa_addr(layout_stacks_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001126
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -08001127 /* ASID 0 is reserved for use by the hypervisor. */
Karl Meakin1fd4b822025-02-01 17:13:47 +00001128 if (!mm_ptable_init(&ptable, 0, (struct mm_flags){.stage1 = true},
1129 ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001130 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001131 return false;
1132 }
1133
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001134 /* Initialize arch_mm before calling below mapping routines */
1135 if (!arch_mm_init(ptable.root)) {
1136 return false;
1137 }
1138
Andrew Walbran48699362019-05-20 14:38:00 +01001139 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001140 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001141
1142 /* Map each section. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001143 CHECK(mm_identity_map(stage1_locked, layout_text_begin(),
1144 layout_text_end(), MM_MODE_X, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001145
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001146 CHECK(mm_identity_map(stage1_locked, layout_rodata_begin(),
1147 layout_rodata_end(), MM_MODE_R, ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001148
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001149 CHECK(mm_identity_map(stage1_locked, layout_data_begin(),
1150 layout_data_end(), MM_MODE_R | MM_MODE_W,
1151 ppool) != NULL);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001152
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001153 /* Arch-specific stack mapping. */
Raghu Krishnamurthy472a8822022-10-04 21:28:59 -07001154 CHECK(arch_stack_mm_init(stage1_locked, ppool));
Maksims Svecovs134b8f92022-03-04 15:14:09 +00001155
Arunachalam Ganapathy0f0f7062022-01-26 17:09:53 +00001156 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001157}