blob: d66cce2125e81ea4b72dd4abd1245699ff29a691 [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 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/mm.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010018
19#include <stdatomic.h>
20#include <stdint.h>
21
Andrew Scull877ae4b2019-07-02 12:52:33 +010022#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010024#include "hf/layout.h"
Andrew Walbran48699362019-05-20 14:38:00 +010025#include "hf/plat/console.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010026#include "hf/static_assert.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010027
Andrew Walbran2400ed22018-09-27 14:45:58 +010028/**
29 * This file has functions for managing the level 1 and 2 page tables used by
30 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
31 * and then a level 2 mapping per VM. The design assumes that all page tables
32 * contain only 1-1 mappings, aligned on the block boundaries.
33 */
34
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010035/*
36 * For stage 2, the input is an intermediate physical addresses rather than a
37 * virtual address so:
38 */
Andrew Scull80871322018-08-06 12:04:09 +010039static_assert(
40 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
41 "Currently, the same code manages the stage 1 and stage 2 page tables "
42 "which only works if the virtual and intermediate physical addresses "
43 "are the same size. It looks like that assumption might not be holding "
44 "so we need to check that everything is going to be ok.");
45
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010046static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010047static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010048
Andrew Scullda241972019-01-05 18:17:48 +000049static bool mm_stage2_invalidate = false;
50
51/**
52 * After calling this function, modifications to stage-2 page tables will use
53 * break-before-make and invalidate the TLB for the affected range.
54 */
55void mm_vm_enable_invalidation(void)
56{
57 mm_stage2_invalidate = true;
58}
59
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010060/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010061 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010062 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010063static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010064{
65 return ptr_from_va(va_from_pa(pa));
66}
67
68/**
Andrew Scull80871322018-08-06 12:04:09 +010069 * Rounds an address down to a page boundary.
70 */
71static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
72{
73 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
74}
75
76/**
77 * Rounds an address up to a page boundary.
78 */
79static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
80{
81 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
82}
83
84/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010085 * Calculates the size of the address space represented by a page table entry at
86 * the given level.
87 */
Andrew Sculle9827712018-10-19 14:54:20 +010088static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010089{
Andrew Scull78d6fd92018-09-06 15:08:36 +010090 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010091}
92
93/**
Andrew Scullcae45572018-12-13 15:46:30 +000094 * Gets the address of the start of the next block of the given size. The size
95 * must be a power of two.
96 */
97static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
98 size_t block_size)
99{
100 return (addr + block_size) & ~(block_size - 1);
101}
102
103/**
104 * Gets the physical address of the start of the next block of the given size.
105 * The size must be a power of two.
106 */
107static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
108{
109 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
110}
111
112/**
Andrew Scull80871322018-08-06 12:04:09 +0100113 * For a given address, calculates the maximum (plus one) address that can be
114 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100115 */
Andrew Sculle9827712018-10-19 14:54:20 +0100116static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100117{
118 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000119
Andrew Scull80871322018-08-06 12:04:09 +0100120 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100121}
122
123/**
Andrew Scull80871322018-08-06 12:04:09 +0100124 * For a given address, calculates the index at which its entry is stored in a
125 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100126 */
Andrew Sculle9827712018-10-19 14:54:20 +0100127static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100128{
Andrew Scull80871322018-08-06 12:04:09 +0100129 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000130
Andrew Scull78d6fd92018-09-06 15:08:36 +0100131 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100132}
133
134/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000135 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100136 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000137static struct mm_page_table *mm_alloc_page_tables(size_t count,
138 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100139{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000140 if (count == 1) {
141 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100142 }
143
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000144 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100145}
146
147/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000148 * Returns the maximum level in the page table given the flags.
149 */
150static uint8_t mm_max_level(int flags)
151{
152 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
153 : arch_mm_stage2_max_level();
154}
155
156/**
157 * Returns the number of root-level tables given the flags.
158 */
159static uint8_t mm_root_table_count(int flags)
160{
161 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
162 : arch_mm_stage2_root_table_count();
163}
164
165/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000166 * Invalidates the TLB for the given address range.
167 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000168static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000169{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000170 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000171 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
172 } else {
173 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
174 }
175}
176
177/**
178 * Frees all page-table-related memory associated with the given pte at the
179 * given level, including any subtables recursively.
180 */
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;
184 uint64_t i;
185
186 if (!arch_mm_pte_is_table(pte, level)) {
187 return;
188 }
189
190 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000191 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000192 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000193 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000194 }
195
196 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000197 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000198}
199
200/**
David Brazdil711fbe92019-08-06 13:39:58 +0100201 * Returns the first address which cannot be encoded in page tables given by
202 * `flags`. It is the exclusive end of the address space created by the tables.
203 */
204ptable_addr_t mm_ptable_addr_space_end(int flags)
205{
206 return mm_root_table_count(flags) *
207 mm_entry_size(mm_max_level(flags) + 1);
208}
209
210/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000211 * Initialises the given page table.
212 */
David Brazdil711fbe92019-08-06 13:39:58 +0100213bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
Andrew Scullda3df7f2019-01-05 17:49:27 +0000214{
215 uint8_t i;
216 size_t j;
217 struct mm_page_table *tables;
218 uint8_t root_table_count = mm_root_table_count(flags);
219
220 tables = mm_alloc_page_tables(root_table_count, ppool);
221 if (tables == NULL) {
222 return false;
223 }
224
225 for (i = 0; i < root_table_count; i++) {
226 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
227 tables[i].entries[j] =
228 arch_mm_absent_pte(mm_max_level(flags));
229 }
230 }
231
232 /*
233 * TODO: halloc could return a virtual or physical address if mm not
234 * enabled?
235 */
236 t->root = pa_init((uintpaddr_t)tables);
237
238 return true;
239}
240
241/**
242 * Frees all memory associated with the give page table.
243 */
244static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
245{
246 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
247 uint8_t level = mm_max_level(flags);
248 uint8_t root_table_count = mm_root_table_count(flags);
249 uint8_t i;
250 uint64_t j;
251
252 for (i = 0; i < root_table_count; ++i) {
253 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
254 mm_free_page_pte(tables[i].entries[j], level, ppool);
255 }
256 }
257
258 mpool_add_chunk(ppool, tables,
259 sizeof(struct mm_page_table) * root_table_count);
260}
261
262/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000263 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000264 * are valid, it performs a break-before-make sequence where it first writes an
265 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
266 * This is to prevent cases where CPUs have different 'valid' values in their
267 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000268 */
269static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000270 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000271{
272 pte_t v = *pte;
273
274 /*
275 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000276 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000277 */
Andrew Scullda241972019-01-05 18:17:48 +0000278 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
279 arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000280 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000281 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000282 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000283 }
284
285 /* Assign the new pte. */
286 *pte = new_pte;
287
288 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000289 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000290}
291
292/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100293 * Populates the provided page table entry with a reference to another table if
294 * needed, that is, if it does not yet point to another table.
295 *
296 * Returns a pointer to the table the entry now points to.
297 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000298static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
299 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000300 int flags,
301 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100303 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100304 pte_t v = *pte;
305 pte_t new_pte;
306 size_t i;
307 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100308 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100309
310 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100311 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000312 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100313 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100314
315 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000316 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100317 if (ntable == NULL) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000318 dlog_error("Failed to allocate memory for page table\n");
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100319 return NULL;
320 }
321
322 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100323 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100324 inc = mm_entry_size(level_below);
325 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000326 arch_mm_block_from_pte(v, level),
327 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100328 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100329 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100330 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100331 }
332
333 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100334 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
335 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100336 new_pte += inc;
337 }
338
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000339 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100340 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000341
342 /* Replace the pte entry, doing a break-before-make if needed. */
343 mm_replace_entry(begin, pte,
344 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000345 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100346
347 return ntable;
348}
349
350/**
Andrew Scull80871322018-08-06 12:04:09 +0100351 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100352 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000353 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100354 *
355 * This function calls itself recursively if it needs to update additional
356 * levels, but the recursion is bound by the maximum number of levels in a page
357 * table.
358 */
Andrew Scull80871322018-08-06 12:04:09 +0100359static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100360 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000361 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100362{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100363 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100364 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100365 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000366 bool commit = flags & MM_FLAG_COMMIT;
367 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100368
Andrew Scull265ada92018-07-30 15:19:01 +0100369 /* Cap end so that we don't go over the current level max. */
370 if (end > level_end) {
371 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100372 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100373
374 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100375 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100376 if (unmap ? !arch_mm_pte_is_present(*pte, level)
377 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000378 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100379 /*
380 * If the entry is already mapped with the right
381 * attributes, or already absent in the case of
382 * unmapping, no need to do anything; carry on to the
383 * next entry.
384 */
385 } else if ((end - begin) >= entry_size &&
386 (unmap || arch_mm_is_block_allowed(level)) &&
387 (begin & (entry_size - 1)) == 0) {
388 /*
389 * If the entire entry is within the region we want to
390 * map, map/unmap the whole entry.
391 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100392 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000393 pte_t new_pte =
394 unmap ? arch_mm_absent_pte(level)
395 : arch_mm_block_pte(level, pa,
396 attrs);
397 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000398 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100399 }
400 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100401 /*
402 * If the entry is already a subtable get it; otherwise
403 * replace it with an equivalent subtable and get that.
404 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000405 struct mm_page_table *nt = mm_populate_table_pte(
406 begin, pte, level, flags, ppool);
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,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000416 flags, ppool)) {
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
Andrew Scullda3df7f2019-01-05 17:49:27 +0000432 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000433 */
434static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
435 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000436 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000437{
438 size_t root_table_size = mm_entry_size(root_level);
439 struct mm_page_table *table =
440 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
441
442 while (begin < end) {
443 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000444 root_level - 1, flags, ppool)) {
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
Andrew Walbran58a6e542019-11-19 14:23:15 +0000457 * provided. Only commits the change if MM_FLAG_COMMIT is set.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100458 */
Andrew Walbran58a6e542019-11-19 14:23:15 +0000459static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
460 paddr_t pa_end, uint64_t attrs, int flags,
461 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 */
Andrew Scull877ae4b2019-07-02 12:52:33 +0100472 CHECK(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
Andrew Walbran58a6e542019-11-19 14:23:15 +0000479 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool)) {
480 return false;
481 }
482
483 /* Invalidate the TLB. */
484 if ((flags & MM_FLAG_COMMIT) &&
485 ((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate)) {
486 mm_invalidate_tlb(begin, end, flags);
487 }
488
489 return true;
490}
491
Andrew Scull4e83cef2019-11-19 14:17:54 +0000492/*
493 * Prepares the given page table for the given address mapping such that it
494 * will be able to commit the change without failure. It does so by ensuring
495 * the smallest granularity needed is available. This remains valid provided
496 * subsequent operations no not decrease the granularity.
497 *
498 * In particular, multiple calls to this function will result in the
499 * corresponding calls to commit the changes to succeed.
500 */
501static bool mm_ptable_identity_prepare(struct mm_ptable *t, paddr_t pa_begin,
502 paddr_t pa_end, uint64_t attrs,
503 int flags, struct mpool *ppool)
504{
505 flags &= ~MM_FLAG_COMMIT;
506 return mm_ptable_identity_map(t, pa_begin, pa_end, attrs, flags, ppool);
507}
508
509/**
510 * Commits the given address mapping to the page table assuming the operation
511 * cannot fail. `mm_ptable_identity_prepare` must used correctly before this to
512 * ensure this condition.
513 *
514 * Without the table being properly prepared, the commit may only partially
515 * complete if it runs out of memory resulting in an inconsistent state that
516 * isn't handled.
517 *
518 * Since the non-failure assumtion is used in the reasoning about the atomicity
519 * of higher level memory operations, any detected violations result in a panic.
520 *
521 * TODO: remove ppool argument to be sure no changes are made.
522 */
523static void mm_ptable_identity_commit(struct mm_ptable *t, paddr_t pa_begin,
524 paddr_t pa_end, uint64_t attrs, int flags,
525 struct mpool *ppool)
526{
527 CHECK(mm_ptable_identity_map(t, pa_begin, pa_end, attrs,
528 flags | MM_FLAG_COMMIT, ppool));
529}
530
Andrew Walbran58a6e542019-11-19 14:23:15 +0000531/**
532 * Updates the given table such that the given physical address range is mapped
533 * or not mapped into the address space with the architecture-agnostic mode
Andrew Scull4e83cef2019-11-19 14:17:54 +0000534 * provided.
535 *
536 * The page table is updated using the separate prepare and commit stages so
537 * that, on failure, a partial update of the address space cannot happen. The
538 * table may be left with extra internal tables but the address space is
539 * unchanged.
Andrew Walbran58a6e542019-11-19 14:23:15 +0000540 */
541static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
542 paddr_t pa_end, uint64_t attrs, int flags,
543 struct mpool *ppool)
544{
Andrew Scull4e83cef2019-11-19 14:17:54 +0000545 if (!mm_ptable_identity_prepare(t, pa_begin, pa_end, attrs, flags,
546 ppool)) {
547 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100548 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100549
Andrew Scull4e83cef2019-11-19 14:17:54 +0000550 mm_ptable_identity_commit(t, pa_begin, pa_end, attrs, flags, ppool);
551
552 return true;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100553}
554
555/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100556 * Writes the given table to the debug log, calling itself recursively to
557 * write sub-tables.
558 */
Andrew Sculle9827712018-10-19 14:54:20 +0100559static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100560 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100561{
562 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000563
Andrew Scull4e5f8142018-10-12 14:37:19 +0100564 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
565 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100566 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100567 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100568
Andrew Scull4e5f8142018-10-12 14:37:19 +0100569 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
570 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100571
Andrew Scull4e5f8142018-10-12 14:37:19 +0100572 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100573 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100574 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000575 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100576 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100577 }
578 }
579}
580
581/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000582 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100583 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000584static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100585{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000586 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000587 uint8_t max_level = mm_max_level(flags);
588 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000589 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000590
Andrew Scull1ba470e2018-10-31 15:14:31 +0000591 for (i = 0; i < root_table_count; ++i) {
592 mm_dump_table_recursive(&tables[i], max_level, max_level);
593 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100594}
595
596/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000597 * Given the table PTE entries all have identical attributes, returns the single
598 * entry with which it can be replaced. Note that the table PTE will no longer
599 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000600 *
601 * If the table is freed, the memory is freed directly rather than calling
602 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100603 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000604static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
605 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100606{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100607 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100608 uint64_t block_attrs;
609 uint64_t table_attrs;
610 uint64_t combined_attrs;
611 paddr_t block_address;
612
Andrew Scullb6b9b562018-12-21 14:41:35 +0000613 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
614
615 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
616 /* Free the table and return an absent entry. */
617 mpool_free(ppool, table);
618 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100619 }
620
Andrew Scullb6b9b562018-12-21 14:41:35 +0000621 /* Might not be possible to merge the table into a single block. */
622 if (!arch_mm_is_block_allowed(level)) {
623 return table_pte;
624 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000625
Andrew Scullb6b9b562018-12-21 14:41:35 +0000626 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000627 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000628 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100629 combined_attrs =
630 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000631 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000632
Andrew Scullb6b9b562018-12-21 14:41:35 +0000633 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000634 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100635 return arch_mm_block_pte(level, block_address, combined_attrs);
636}
637
638/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000639 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000640 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100641 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000642static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
643 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100644{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100645 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100646 uint64_t i;
Andrew Scull12122ce2019-11-19 14:21:07 +0000647 bool mergeable;
648 bool base_present;
649 uint64_t base_attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100650
651 if (!arch_mm_pte_is_table(entry, level)) {
652 return entry;
653 }
654
Andrew Scull3681b8d2018-12-12 14:22:59 +0000655 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.");
659 table->entries[0] =
660 mm_ptable_defrag_entry(table->entries[0], level - 1, ppool);
661 base_present = arch_mm_pte_is_present(table->entries[0], level - 1);
662 base_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
663
Andrew Walbran2400ed22018-09-27 14:45:58 +0100664 /*
Andrew Scull12122ce2019-11-19 14:21:07 +0000665 * Defrag the remaining entries in the table and check whether they are
666 * compatible with the base entry meaning the table can be merged into a
667 * block entry. It assumes addresses are contiguous due to identity
668 * mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100669 */
Andrew Scull12122ce2019-11-19 14:21:07 +0000670 mergeable = true;
671 for (i = 1; i < MM_PTE_PER_PAGE; ++i) {
672 bool present;
673
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000674 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
675 level - 1, ppool);
Andrew Scull12122ce2019-11-19 14:21:07 +0000676 present = arch_mm_pte_is_present(table->entries[i], level - 1);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100677
Andrew Scull12122ce2019-11-19 14:21:07 +0000678 if (present != base_present) {
679 mergeable = false;
680 continue;
681 }
682
683 if (!present) {
684 continue;
685 }
686
687 if (!arch_mm_pte_is_block(table->entries[i], level - 1)) {
688 mergeable = false;
689 continue;
690 }
691
692 if (arch_mm_pte_attrs(table->entries[i], level - 1) !=
693 base_attrs) {
694 mergeable = false;
695 continue;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100696 }
697 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000698
Andrew Scull12122ce2019-11-19 14:21:07 +0000699 if (mergeable) {
700 return mm_merge_table_pte(entry, level, ppool);
701 }
702
703 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100704}
705
706/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100707 * Defragments the given page table by converting page table references to
708 * blocks whenever possible.
709 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000710static void mm_ptable_defrag(struct mm_ptable *t, int flags,
711 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100712{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000713 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000714 uint8_t level = mm_max_level(flags);
715 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000716 uint8_t i;
717 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100718
719 /*
720 * Loop through each entry in the table. If it points to another table,
721 * check if that table can be replaced by a block or an absent entry.
722 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000723 for (i = 0; i < root_table_count; ++i) {
724 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
725 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000726 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000727 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100728 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100729}
730
731/**
Andrew Scull81e85092018-12-12 12:56:20 +0000732 * Gets the attributes applied to the given range of stage-2 addresses at the
733 * given level.
734 *
735 * The `got_attrs` argument is initially passed as false until `attrs` contains
736 * attributes of the memory region at which point it is passed as true.
737 *
738 * The value returned in `attrs` is only valid if the function returns true.
739 *
740 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100741 */
Andrew Scull81e85092018-12-12 12:56:20 +0000742static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
743 ptable_addr_t begin, ptable_addr_t end,
744 uint8_t level, bool got_attrs,
745 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100746{
Andrew Scull81e85092018-12-12 12:56:20 +0000747 pte_t *pte = &table->entries[mm_index(begin, level)];
748 ptable_addr_t level_end = mm_level_end(begin, level);
749 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100750
Andrew Scull81e85092018-12-12 12:56:20 +0000751 /* Cap end so that we don't go over the current level max. */
752 if (end > level_end) {
753 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100754 }
755
Andrew Scull81e85092018-12-12 12:56:20 +0000756 /* Check that each entry is owned. */
757 while (begin < end) {
758 if (arch_mm_pte_is_table(*pte, level)) {
759 if (!mm_ptable_get_attrs_level(
760 mm_page_table_from_pa(
761 arch_mm_table_from_pte(*pte,
762 level)),
763 begin, end, level - 1, got_attrs, attrs)) {
764 return false;
765 }
766 got_attrs = true;
767 } else {
768 if (!got_attrs) {
769 *attrs = arch_mm_pte_attrs(*pte, level);
770 got_attrs = true;
771 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
772 return false;
773 }
774 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100775
Andrew Scull81e85092018-12-12 12:56:20 +0000776 begin = mm_start_of_next_block(begin, entry_size);
777 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100778 }
779
Andrew Scullc66a04d2018-12-07 13:41:56 +0000780 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000781 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100782}
783
784/**
Andrew Scull81e85092018-12-12 12:56:20 +0000785 * Gets the attributes applies to the given range of addresses in the stage-2
786 * table.
787 *
788 * The value returned in `attrs` is only valid if the function returns true.
789 *
790 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100791 */
Andrew Scull81e85092018-12-12 12:56:20 +0000792static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
793 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100794{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000795 int flags = 0;
796 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000797 uint8_t root_level = max_level + 1;
798 size_t root_table_size = mm_entry_size(root_level);
799 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000800 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000801 struct mm_page_table *table;
802 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100803
Andrew Scull81e85092018-12-12 12:56:20 +0000804 begin = mm_round_down_to_page(begin);
805 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100806
Andrew Scull81e85092018-12-12 12:56:20 +0000807 /* Fail if the addresses are out of range. */
808 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000809 return false;
810 }
811
Andrew Scull81e85092018-12-12 12:56:20 +0000812 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
813 while (begin < end) {
814 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
815 got_attrs, attrs)) {
816 return false;
817 }
818
819 got_attrs = true;
820 begin = mm_start_of_next_block(begin, root_table_size);
821 table++;
822 }
823
824 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100825}
826
Andrew Scullda3df7f2019-01-05 17:49:27 +0000827bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100828{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000829 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100830}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100831
Andrew Scullda3df7f2019-01-05 17:49:27 +0000832void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000833{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000834 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000835}
836
837/**
Andrew Scull73b89542019-11-20 17:31:26 +0000838 * Selects flags to pass to the page table manipulation operation based on the
839 * mapping mode.
840 */
841static int mm_mode_to_flags(uint32_t mode)
842{
843 if ((mode & MM_MODE_UNMAPPED_MASK) == MM_MODE_UNMAPPED_MASK) {
844 return MM_FLAG_UNMAP;
845 }
846
847 return 0;
848}
849
850/**
Andrew Scull4e83cef2019-11-19 14:17:54 +0000851 * See `mm_ptable_identity_prepare`.
852 *
853 * This must be called before `mm_vm_identity_commit` for the same mapping.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000854 *
855 * Returns true on success, or false if the update would fail.
Andrew Scull4e83cef2019-11-19 14:17:54 +0000856 */
857bool mm_vm_identity_prepare(struct mm_ptable *t, paddr_t begin, paddr_t end,
858 uint32_t mode, struct mpool *ppool)
859{
860 int flags = mm_mode_to_flags(mode);
861
862 return mm_ptable_identity_prepare(t, begin, end,
863 arch_mm_mode_to_stage2_attrs(mode),
864 flags, ppool);
865}
866
867/**
868 * See `mm_ptable_identity_commit`.
869 *
870 * `mm_vm_identity_prepare` must be called before this for the same mapping.
871 */
872void mm_vm_identity_commit(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000873 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Andrew Scull4e83cef2019-11-19 14:17:54 +0000874{
875 int flags = mm_mode_to_flags(mode);
876
877 mm_ptable_identity_commit(t, begin, end,
878 arch_mm_mode_to_stage2_attrs(mode), flags,
879 ppool);
880
881 if (ipa != NULL) {
882 *ipa = ipa_from_pa(begin);
883 }
884}
885
886/**
Andrew Scull80871322018-08-06 12:04:09 +0100887 * Updates a VM's page table such that the given physical address range is
888 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100889 * architecture-agnostic mode provided.
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000890 *
891 * mm_vm_defrag should always be called after a series of page table updates,
892 * whether they succeed or fail. This is because on failure extra page table
893 * entries may have been allocated and then not used, while on success it may be
894 * possible to compact the page table by merging several entries into a block.
895 *
896 * Returns true on success, or false if the update failed and no changes were
897 * made.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100898 */
Andrew Scull80871322018-08-06 12:04:09 +0100899bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000900 uint32_t mode, struct mpool *ppool, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100901{
Andrew Scull73b89542019-11-20 17:31:26 +0000902 int flags = mm_mode_to_flags(mode);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000903 bool success = mm_ptable_identity_update(
904 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
905 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100906
907 if (success && ipa != NULL) {
908 *ipa = ipa_from_pa(begin);
909 }
910
911 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100912}
913
914/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000915 * Updates the VM's table such that the given physical address range has no
916 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100917 */
Andrew Scullda241972019-01-05 18:17:48 +0000918bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000919 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100920{
Andrew Scull73b89542019-11-20 17:31:26 +0000921 uint32_t mode = MM_MODE_UNMAPPED_MASK;
922
Andrew Walbran8ec2b9f2019-11-25 15:05:40 +0000923 return mm_vm_identity_map(t, begin, end, mode, ppool, NULL);
Andrew Scull80871322018-08-06 12:04:09 +0100924}
925
926/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000927 * Write the given page table of a VM to the debug log.
928 */
929void mm_vm_dump(struct mm_ptable *t)
930{
931 mm_ptable_dump(t, 0);
932}
933
934/**
935 * Defragments the VM page table.
936 */
937void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
938{
939 mm_ptable_defrag(t, 0, ppool);
940}
941
942/**
Andrew Scull81e85092018-12-12 12:56:20 +0000943 * Gets the mode of the give range of intermediate physical addresses if they
944 * are mapped with the same mode.
945 *
946 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100947 */
Andrew Scull81e85092018-12-12 12:56:20 +0000948bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100949 uint32_t *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100950{
Andrew Scull81e85092018-12-12 12:56:20 +0000951 uint64_t attrs;
952 bool ret;
953
954 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
955 if (ret) {
956 *mode = arch_mm_stage2_attrs_to_mode(attrs);
957 }
958
959 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100960}
961
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100962static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
963{
964 return (struct mm_stage1_locked){.ptable = &ptable};
965}
966
967struct mm_stage1_locked mm_lock_stage1(void)
968{
969 sl_lock(&ptable_lock);
970 return mm_stage1_lock_unsafe();
971}
972
973void mm_unlock_stage1(struct mm_stage1_locked *lock)
974{
Andrew Scull877ae4b2019-07-02 12:52:33 +0100975 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100976 sl_unlock(&ptable_lock);
977 lock->ptable = NULL;
978}
979
Andrew Scull80871322018-08-06 12:04:09 +0100980/**
Andrew Scull80871322018-08-06 12:04:09 +0100981 * Updates the hypervisor page table such that the given physical address range
982 * is mapped into the address space at the corresponding address range in the
983 * architecture-agnostic mode provided.
984 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100985void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100986 paddr_t end, uint32_t mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100987{
Andrew Scull73b89542019-11-20 17:31:26 +0000988 int flags = MM_FLAG_STAGE1 | mm_mode_to_flags(mode);
989
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100990 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scull73b89542019-11-20 17:31:26 +0000991 arch_mm_mode_to_stage1_attrs(mode), flags,
992 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100993 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100994 }
995
996 return NULL;
997}
998
999/**
1000 * Updates the hypervisor table such that the given physical address range is
1001 * not mapped in the address space.
1002 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001003bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
1004 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001005{
Andrew Scull73b89542019-11-20 17:31:26 +00001006 uint32_t mode = MM_MODE_UNMAPPED_MASK;
1007
1008 return mm_identity_map(stage1_locked, begin, end, mode, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001009}
1010
1011/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001012 * Defragments the hypervisor page table.
1013 */
1014void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
1015{
1016 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
1017}
1018
1019/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001020 * Initialises memory management for the hypervisor itself.
1021 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001022bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001023{
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001024 /* Locking is not enabled yet so fake it, */
1025 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
1026
Andrew Walbran17eebf92020-02-05 16:35:49 +00001027 dlog_info("text: %#x - %#x\n", pa_addr(layout_text_begin()),
1028 pa_addr(layout_text_end()));
1029 dlog_info("rodata: %#x - %#x\n", pa_addr(layout_rodata_begin()),
1030 pa_addr(layout_rodata_end()));
1031 dlog_info("data: %#x - %#x\n", pa_addr(layout_data_begin()),
1032 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001033
Andrew Scullda3df7f2019-01-05 17:49:27 +00001034 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +00001035 dlog_error("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001036 return false;
1037 }
1038
Andrew Walbran48699362019-05-20 14:38:00 +01001039 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001040 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001041
1042 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001043 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
1044 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001045
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001046 mm_identity_map(stage1_locked, layout_rodata_begin(),
1047 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001048
Andrew Scull3c0a90a2019-07-01 11:55:53 +01001049 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +00001050 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001051
Andrew Scullb2910562019-09-17 14:08:27 +01001052 return arch_mm_init(ptable.root);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001053}