blob: 6de6bc62bca00016369343ab82e6d6afbbbb5efe [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 Scull5c496a32019-04-04 11:57:33 +010022#include "hf/assert.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"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010025
Andrew Walbran2400ed22018-09-27 14:45:58 +010026/**
27 * This file has functions for managing the level 1 and 2 page tables used by
28 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
29 * and then a level 2 mapping per VM. The design assumes that all page tables
30 * contain only 1-1 mappings, aligned on the block boundaries.
31 */
32
Andrew Scull80871322018-08-06 12:04:09 +010033/* The type of addresses stored in the page table. */
34typedef uintvaddr_t ptable_addr_t;
35
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010036/*
37 * For stage 2, the input is an intermediate physical addresses rather than a
38 * virtual address so:
39 */
Andrew Scull80871322018-08-06 12:04:09 +010040static_assert(
41 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
42 "Currently, the same code manages the stage 1 and stage 2 page tables "
43 "which only works if the virtual and intermediate physical addresses "
44 "are the same size. It looks like that assumption might not be holding "
45 "so we need to check that everything is going to be ok.");
46
Andrew Scull4f170f52018-07-19 12:58:20 +010047/* Keep macro alignment */
48/* clang-format off */
49
Andrew Scullda3df7f2019-01-05 17:49:27 +000050#define MM_FLAG_COMMIT 0x01
51#define MM_FLAG_UNMAP 0x02
Andrew Scullda241972019-01-05 18:17:48 +000052#define MM_FLAG_STAGE1 0x04
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010053
Andrew Scull4f170f52018-07-19 12:58:20 +010054/* clang-format on */
55
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010056static struct mm_ptable ptable;
57
Andrew Scullda241972019-01-05 18:17:48 +000058static bool mm_stage2_invalidate = false;
59
60/**
61 * After calling this function, modifications to stage-2 page tables will use
62 * break-before-make and invalidate the TLB for the affected range.
63 */
64void mm_vm_enable_invalidation(void)
65{
66 mm_stage2_invalidate = true;
67}
68
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010069/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010070 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010071 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010072static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010073{
74 return ptr_from_va(va_from_pa(pa));
75}
76
77/**
Andrew Scull80871322018-08-06 12:04:09 +010078 * Rounds an address down to a page boundary.
79 */
80static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
81{
82 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
83}
84
85/**
86 * Rounds an address up to a page boundary.
87 */
88static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
89{
90 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
91}
92
93/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010094 * Calculates the size of the address space represented by a page table entry at
95 * the given level.
96 */
Andrew Sculle9827712018-10-19 14:54:20 +010097static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010098{
Andrew Scull78d6fd92018-09-06 15:08:36 +010099 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100100}
101
102/**
Andrew Scullcae45572018-12-13 15:46:30 +0000103 * Gets the address of the start of the next block of the given size. The size
104 * must be a power of two.
105 */
106static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
107 size_t block_size)
108{
109 return (addr + block_size) & ~(block_size - 1);
110}
111
112/**
113 * Gets the physical address of the start of the next block of the given size.
114 * The size must be a power of two.
115 */
116static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
117{
118 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
119}
120
121/**
Andrew Scull80871322018-08-06 12:04:09 +0100122 * For a given address, calculates the maximum (plus one) address that can be
123 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100124 */
Andrew Sculle9827712018-10-19 14:54:20 +0100125static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100126{
127 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000128
Andrew Scull80871322018-08-06 12:04:09 +0100129 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100130}
131
132/**
Andrew Scull80871322018-08-06 12:04:09 +0100133 * For a given address, calculates the index at which its entry is stored in a
134 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100135 */
Andrew Sculle9827712018-10-19 14:54:20 +0100136static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100137{
Andrew Scull80871322018-08-06 12:04:09 +0100138 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000139
Andrew Scull78d6fd92018-09-06 15:08:36 +0100140 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100141}
142
143/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000144 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100145 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000146static struct mm_page_table *mm_alloc_page_tables(size_t count,
147 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100148{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000149 if (count == 1) {
150 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100151 }
152
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000153 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100154}
155
156/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000157 * Returns the maximum level in the page table given the flags.
158 */
159static uint8_t mm_max_level(int flags)
160{
161 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
162 : arch_mm_stage2_max_level();
163}
164
165/**
166 * Returns the number of root-level tables given the flags.
167 */
168static uint8_t mm_root_table_count(int flags)
169{
170 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
171 : arch_mm_stage2_root_table_count();
172}
173
174/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000175 * Invalidates the TLB for the given address range.
176 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000177static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000178{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000179 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000180 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
181 } else {
182 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
183 }
184}
185
186/**
187 * Frees all page-table-related memory associated with the given pte at the
188 * given level, including any subtables recursively.
189 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000190static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000191{
192 struct mm_page_table *table;
193 uint64_t i;
194
195 if (!arch_mm_pte_is_table(pte, level)) {
196 return;
197 }
198
199 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000200 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000201 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000202 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000203 }
204
205 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000206 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000207}
208
209/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000210 * Initialises the given page table.
211 */
212static bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
213{
214 uint8_t i;
215 size_t j;
216 struct mm_page_table *tables;
217 uint8_t root_table_count = mm_root_table_count(flags);
218
219 tables = mm_alloc_page_tables(root_table_count, ppool);
220 if (tables == NULL) {
221 return false;
222 }
223
224 for (i = 0; i < root_table_count; i++) {
225 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
226 tables[i].entries[j] =
227 arch_mm_absent_pte(mm_max_level(flags));
228 }
229 }
230
231 /*
232 * TODO: halloc could return a virtual or physical address if mm not
233 * enabled?
234 */
235 t->root = pa_init((uintpaddr_t)tables);
236
237 return true;
238}
239
240/**
241 * Frees all memory associated with the give page table.
242 */
243static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
244{
245 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
246 uint8_t level = mm_max_level(flags);
247 uint8_t root_table_count = mm_root_table_count(flags);
248 uint8_t i;
249 uint64_t j;
250
251 for (i = 0; i < root_table_count; ++i) {
252 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
253 mm_free_page_pte(tables[i].entries[j], level, ppool);
254 }
255 }
256
257 mpool_add_chunk(ppool, tables,
258 sizeof(struct mm_page_table) * root_table_count);
259}
260
261/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000262 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000263 * are valid, it performs a break-before-make sequence where it first writes an
264 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
265 * This is to prevent cases where CPUs have different 'valid' values in their
266 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000267 */
268static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000269 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270{
271 pte_t v = *pte;
272
273 /*
274 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000275 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000276 */
Andrew Scullda241972019-01-05 18:17:48 +0000277 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
278 arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000279 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000280 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000281 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000282 }
283
284 /* Assign the new pte. */
285 *pte = new_pte;
286
287 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000288 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000289}
290
291/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100292 * Populates the provided page table entry with a reference to another table if
293 * needed, that is, if it does not yet point to another table.
294 *
295 * Returns a pointer to the table the entry now points to.
296 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000297static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
298 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000299 int flags,
300 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100301{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100302 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100303 pte_t v = *pte;
304 pte_t new_pte;
305 size_t i;
306 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100307 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100308
309 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100310 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000311 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100312 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100313
314 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000315 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100316 if (ntable == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100317 dlog("Failed to allocate memory for page table\n");
318 return NULL;
319 }
320
321 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100322 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100323 inc = mm_entry_size(level_below);
324 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000325 arch_mm_block_from_pte(v, level),
326 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100327 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100328 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100329 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100330 }
331
332 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100333 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
334 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100335 new_pte += inc;
336 }
337
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000338 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100339 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000340
341 /* Replace the pte entry, doing a break-before-make if needed. */
342 mm_replace_entry(begin, pte,
343 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000344 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100345
346 return ntable;
347}
348
349/**
Andrew Walbran6324fc92018-10-03 11:46:43 +0100350 * Returns whether all entries in this table are absent.
351 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000352static bool mm_page_table_is_empty(struct mm_page_table *table, uint8_t level)
Andrew Walbran6324fc92018-10-03 11:46:43 +0100353{
354 uint64_t i;
355
Andrew Scull4e5f8142018-10-12 14:37:19 +0100356 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
357 if (arch_mm_pte_is_present(table->entries[i], level)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100358 return false;
359 }
360 }
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000361
Andrew Walbran6324fc92018-10-03 11:46:43 +0100362 return true;
363}
364
365/**
Andrew Scull80871322018-08-06 12:04:09 +0100366 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100367 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000368 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100369 *
370 * This function calls itself recursively if it needs to update additional
371 * levels, but the recursion is bound by the maximum number of levels in a page
372 * table.
373 */
Andrew Scull80871322018-08-06 12:04:09 +0100374static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100375 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000376 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100377{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100378 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100379 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100380 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000381 bool commit = flags & MM_FLAG_COMMIT;
382 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100383
Andrew Scull265ada92018-07-30 15:19:01 +0100384 /* Cap end so that we don't go over the current level max. */
385 if (end > level_end) {
386 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100387 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100388
389 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100390 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100391 if (unmap ? !arch_mm_pte_is_present(*pte, level)
392 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000393 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100394 /*
395 * If the entry is already mapped with the right
396 * attributes, or already absent in the case of
397 * unmapping, no need to do anything; carry on to the
398 * next entry.
399 */
400 } else if ((end - begin) >= entry_size &&
401 (unmap || arch_mm_is_block_allowed(level)) &&
402 (begin & (entry_size - 1)) == 0) {
403 /*
404 * If the entire entry is within the region we want to
405 * map, map/unmap the whole entry.
406 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100407 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000408 pte_t new_pte =
409 unmap ? arch_mm_absent_pte(level)
410 : arch_mm_block_pte(level, pa,
411 attrs);
412 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000413 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100414 }
415 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100416 /*
417 * If the entry is already a subtable get it; otherwise
418 * replace it with an equivalent subtable and get that.
419 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000420 struct mm_page_table *nt = mm_populate_table_pte(
421 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100422 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100423 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100424 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100425
Andrew Walbran6324fc92018-10-03 11:46:43 +0100426 /*
427 * Recurse to map/unmap the appropriate entries within
428 * the subtable.
429 */
Andrew Scull80871322018-08-06 12:04:09 +0100430 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000431 flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100432 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100433 }
Andrew Walbran6324fc92018-10-03 11:46:43 +0100434
435 /*
436 * If the subtable is now empty, replace it with an
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000437 * absent entry at this level. We never need to do
438 * break-before-makes here because we are assigning
439 * an absent value.
Andrew Walbran6324fc92018-10-03 11:46:43 +0100440 */
441 if (commit && unmap &&
Andrew Scull1ba470e2018-10-31 15:14:31 +0000442 mm_page_table_is_empty(nt, level - 1)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100443 pte_t v = *pte;
444 *pte = arch_mm_absent_pte(level);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000445 mm_free_page_pte(v, level, ppool);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100446 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100447 }
448
Andrew Scullcae45572018-12-13 15:46:30 +0000449 begin = mm_start_of_next_block(begin, entry_size);
450 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100451 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100452 }
453
454 return true;
455}
456
457/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000458 * Updates the page table from the root to map the given address range to a
459 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000460 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000461 */
462static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
463 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000464 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000465{
466 size_t root_table_size = mm_entry_size(root_level);
467 struct mm_page_table *table =
468 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
469
470 while (begin < end) {
471 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000472 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000473 return false;
474 }
Andrew Scullcae45572018-12-13 15:46:30 +0000475 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000476 table++;
477 }
478
479 return true;
480}
481
482/**
Andrew Scull80871322018-08-06 12:04:09 +0100483 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000484 * or not mapped into the address space with the architecture-agnostic mode
485 * provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100486 */
Andrew Sculla6da8342018-11-01 12:29:49 +0000487static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000488 paddr_t pa_end, uint64_t attrs, int flags,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000489 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100490{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000491 uint8_t root_level = mm_max_level(flags) + 1;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000492 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000493 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000494 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
495 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100496
Andrew Scull1ba470e2018-10-31 15:14:31 +0000497 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100498 * Assert condition to communicate the API constraint of mm_max_level(),
499 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000500 */
Andrew Scullf8252932019-04-04 13:51:22 +0100501 assert(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000502
503 /* Cap end to stay within the bounds of the page table. */
504 if (end > ptable_end) {
505 end = ptable_end;
506 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100507
508 /*
509 * Do it in two steps to prevent leaving the table in a halfway updated
510 * state. In such a two-step implementation, the table may be left with
511 * extra internal tables, but no different mapping on failure.
512 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000513 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool) ||
Andrew Scull1ba470e2018-10-31 15:14:31 +0000514 !mm_map_root(t, begin, end, attrs, root_level,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000515 flags | MM_FLAG_COMMIT, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100516 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100517 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100518
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100519 /* Invalidate the tlb. */
Andrew Scullda241972019-01-05 18:17:48 +0000520 if ((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000521 mm_invalidate_tlb(begin, end, flags);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100522 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100523
524 return true;
525}
526
527/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100528 * Writes the given table to the debug log, calling itself recursively to
529 * write sub-tables.
530 */
Andrew Sculle9827712018-10-19 14:54:20 +0100531static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100532 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100533{
534 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000535
Andrew Scull4e5f8142018-10-12 14:37:19 +0100536 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
537 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100538 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100539 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100540
Andrew Scull4e5f8142018-10-12 14:37:19 +0100541 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
542 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100543
Andrew Scull4e5f8142018-10-12 14:37:19 +0100544 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100545 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100546 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000547 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100548 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100549 }
550 }
551}
552
553/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000554 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100555 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000556static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100557{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000558 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000559 uint8_t max_level = mm_max_level(flags);
560 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000561 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000562
Andrew Scull1ba470e2018-10-31 15:14:31 +0000563 for (i = 0; i < root_table_count; ++i) {
564 mm_dump_table_recursive(&tables[i], max_level, max_level);
565 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100566}
567
568/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000569 * Given the table PTE entries all have identical attributes, returns the single
570 * entry with which it can be replaced. Note that the table PTE will no longer
571 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000572 *
573 * If the table is freed, the memory is freed directly rather than calling
574 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100575 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000576static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
577 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100578{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100579 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100580 uint64_t block_attrs;
581 uint64_t table_attrs;
582 uint64_t combined_attrs;
583 paddr_t block_address;
584
Andrew Scullb6b9b562018-12-21 14:41:35 +0000585 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
586
587 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
588 /* Free the table and return an absent entry. */
589 mpool_free(ppool, table);
590 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100591 }
592
Andrew Scullb6b9b562018-12-21 14:41:35 +0000593 /* Might not be possible to merge the table into a single block. */
594 if (!arch_mm_is_block_allowed(level)) {
595 return table_pte;
596 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000597
Andrew Scullb6b9b562018-12-21 14:41:35 +0000598 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000599 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000600 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100601 combined_attrs =
602 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000603 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000604
Andrew Scullb6b9b562018-12-21 14:41:35 +0000605 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000606 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100607 return arch_mm_block_pte(level, block_address, combined_attrs);
608}
609
610/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000611 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000612 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100613 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000614static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
615 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100616{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100617 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100618 uint64_t i;
619 uint64_t attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100620
621 if (!arch_mm_pte_is_table(entry, level)) {
622 return entry;
623 }
624
Andrew Scull3681b8d2018-12-12 14:22:59 +0000625 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100626
627 /*
628 * Check if all entries are blocks with the same flags or are all
Andrew Scullb6b9b562018-12-21 14:41:35 +0000629 * absent. It assumes addresses are contiguous due to identity mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100630 */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000631 attrs = arch_mm_pte_attrs(table->entries[0], level);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100632 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000633 /* First try to defrag the entry, in case it is a subtable. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000634 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
635 level - 1, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100636
Andrew Walbran2400ed22018-09-27 14:45:58 +0100637 /*
Andrew Scullb6b9b562018-12-21 14:41:35 +0000638 * If the entry isn't a block or has different attributes then
639 * it isn't possible to defragment it.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100640 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100641 if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
Andrew Scull3681b8d2018-12-12 14:22:59 +0000642 arch_mm_pte_attrs(table->entries[i], level) != attrs) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000643 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100644 }
645 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000646
647 return mm_merge_table_pte(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100648}
649
650/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100651 * Defragments the given page table by converting page table references to
652 * blocks whenever possible.
653 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000654static void mm_ptable_defrag(struct mm_ptable *t, int flags,
655 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100656{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000657 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000658 uint8_t level = mm_max_level(flags);
659 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000660 uint8_t i;
661 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100662
663 /*
664 * Loop through each entry in the table. If it points to another table,
665 * check if that table can be replaced by a block or an absent entry.
666 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000667 for (i = 0; i < root_table_count; ++i) {
668 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
669 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000670 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000671 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100672 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100673}
674
675/**
Andrew Scull81e85092018-12-12 12:56:20 +0000676 * Gets the attributes applied to the given range of stage-2 addresses at the
677 * given level.
678 *
679 * The `got_attrs` argument is initially passed as false until `attrs` contains
680 * attributes of the memory region at which point it is passed as true.
681 *
682 * The value returned in `attrs` is only valid if the function returns true.
683 *
684 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100685 */
Andrew Scull81e85092018-12-12 12:56:20 +0000686static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
687 ptable_addr_t begin, ptable_addr_t end,
688 uint8_t level, bool got_attrs,
689 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100690{
Andrew Scull81e85092018-12-12 12:56:20 +0000691 pte_t *pte = &table->entries[mm_index(begin, level)];
692 ptable_addr_t level_end = mm_level_end(begin, level);
693 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100694
Andrew Scull81e85092018-12-12 12:56:20 +0000695 /* Cap end so that we don't go over the current level max. */
696 if (end > level_end) {
697 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100698 }
699
Andrew Scull81e85092018-12-12 12:56:20 +0000700 /* Check that each entry is owned. */
701 while (begin < end) {
702 if (arch_mm_pte_is_table(*pte, level)) {
703 if (!mm_ptable_get_attrs_level(
704 mm_page_table_from_pa(
705 arch_mm_table_from_pte(*pte,
706 level)),
707 begin, end, level - 1, got_attrs, attrs)) {
708 return false;
709 }
710 got_attrs = true;
711 } else {
712 if (!got_attrs) {
713 *attrs = arch_mm_pte_attrs(*pte, level);
714 got_attrs = true;
715 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
716 return false;
717 }
718 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100719
Andrew Scull81e85092018-12-12 12:56:20 +0000720 begin = mm_start_of_next_block(begin, entry_size);
721 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100722 }
723
Andrew Scullc66a04d2018-12-07 13:41:56 +0000724 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000725 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100726}
727
728/**
Andrew Scull81e85092018-12-12 12:56:20 +0000729 * Gets the attributes applies to the given range of addresses in the stage-2
730 * table.
731 *
732 * The value returned in `attrs` is only valid if the function returns true.
733 *
734 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100735 */
Andrew Scull81e85092018-12-12 12:56:20 +0000736static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
737 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100738{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000739 int flags = 0;
740 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000741 uint8_t root_level = max_level + 1;
742 size_t root_table_size = mm_entry_size(root_level);
743 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000744 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000745 struct mm_page_table *table;
746 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100747
Andrew Scull81e85092018-12-12 12:56:20 +0000748 begin = mm_round_down_to_page(begin);
749 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100750
Andrew Scull81e85092018-12-12 12:56:20 +0000751 /* Fail if the addresses are out of range. */
752 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000753 return false;
754 }
755
Andrew Scull81e85092018-12-12 12:56:20 +0000756 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
757 while (begin < end) {
758 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
759 got_attrs, attrs)) {
760 return false;
761 }
762
763 got_attrs = true;
764 begin = mm_start_of_next_block(begin, root_table_size);
765 table++;
766 }
767
768 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100769}
770
Andrew Scullda3df7f2019-01-05 17:49:27 +0000771bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100772{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000773 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100774}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100775
Andrew Scullda3df7f2019-01-05 17:49:27 +0000776void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000777{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000778 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000779}
780
781/**
Andrew Scull80871322018-08-06 12:04:09 +0100782 * Updates a VM's page table such that the given physical address range is
783 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100784 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100785 */
Andrew Scull80871322018-08-06 12:04:09 +0100786bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000787 int mode, ipaddr_t *ipa, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100788{
Andrew Scullda241972019-01-05 18:17:48 +0000789 int flags = 0;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000790 bool success = mm_ptable_identity_update(
791 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
792 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100793
794 if (success && ipa != NULL) {
795 *ipa = ipa_from_pa(begin);
796 }
797
798 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100799}
800
801/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000802 * Updates the VM's table such that the given physical address range has no
803 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100804 */
Andrew Scullda241972019-01-05 18:17:48 +0000805bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000806 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100807{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000808 return mm_ptable_identity_update(
809 t, begin, end,
810 arch_mm_mode_to_stage2_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
811 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000812 MM_FLAG_UNMAP, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100813}
814
815/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000816 * Unmaps the hypervisor pages from the given page table.
817 */
Andrew Scullda241972019-01-05 18:17:48 +0000818bool mm_vm_unmap_hypervisor(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000819{
820 /* TODO: If we add pages dynamically, they must be included here too. */
Andrew Scullda241972019-01-05 18:17:48 +0000821 return mm_vm_unmap(t, layout_text_begin(), layout_text_end(), ppool) &&
822 mm_vm_unmap(t, layout_rodata_begin(), layout_rodata_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000823 ppool) &&
Andrew Scullda241972019-01-05 18:17:48 +0000824 mm_vm_unmap(t, layout_data_begin(), layout_data_end(), ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000825}
826
827/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000828 * Write the given page table of a VM to the debug log.
829 */
830void mm_vm_dump(struct mm_ptable *t)
831{
832 mm_ptable_dump(t, 0);
833}
834
835/**
836 * Defragments the VM page table.
837 */
838void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
839{
840 mm_ptable_defrag(t, 0, ppool);
841}
842
843/**
Andrew Scull81e85092018-12-12 12:56:20 +0000844 * Gets the mode of the give range of intermediate physical addresses if they
845 * are mapped with the same mode.
846 *
847 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100848 */
Andrew Scull81e85092018-12-12 12:56:20 +0000849bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
850 int *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100851{
Andrew Scull81e85092018-12-12 12:56:20 +0000852 uint64_t attrs;
853 bool ret;
854
855 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
856 if (ret) {
857 *mode = arch_mm_stage2_attrs_to_mode(attrs);
858 }
859
860 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100861}
862
863/**
Andrew Scull80871322018-08-06 12:04:09 +0100864 * Updates the hypervisor page table such that the given physical address range
865 * is mapped into the address space at the corresponding address range in the
866 * architecture-agnostic mode provided.
867 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000868void *mm_identity_map(paddr_t begin, paddr_t end, int mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100869{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000870 if (mm_ptable_identity_update(&ptable, begin, end,
Andrew Scullda241972019-01-05 18:17:48 +0000871 arch_mm_mode_to_stage1_attrs(mode),
872 MM_FLAG_STAGE1, ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100873 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100874 }
875
876 return NULL;
877}
878
879/**
880 * Updates the hypervisor table such that the given physical address range is
881 * not mapped in the address space.
882 */
Andrew Scullda241972019-01-05 18:17:48 +0000883bool mm_unmap(paddr_t begin, paddr_t end, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100884{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000885 return mm_ptable_identity_update(
886 &ptable, begin, end,
887 arch_mm_mode_to_stage1_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
888 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000889 MM_FLAG_STAGE1 | MM_FLAG_UNMAP, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100890}
891
892/**
893 * Initialises memory management for the hypervisor itself.
894 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000895bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100896{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000897 dlog("text: 0x%x - 0x%x\n", pa_addr(layout_text_begin()),
898 pa_addr(layout_text_end()));
899 dlog("rodata: 0x%x - 0x%x\n", pa_addr(layout_rodata_begin()),
900 pa_addr(layout_rodata_end()));
901 dlog("data: 0x%x - 0x%x\n", pa_addr(layout_data_begin()),
902 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100903
Andrew Scullda3df7f2019-01-05 17:49:27 +0000904 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Scullfdd716e2018-12-20 05:37:31 +0000905 dlog("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100906 return false;
907 }
908
909 /* Map page for uart. */
910 /* TODO: We may not want to map this. */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000911 mm_identity_map(pa_init(PL011_BASE),
912 pa_add(pa_init(PL011_BASE), PAGE_SIZE),
913 MM_MODE_R | MM_MODE_W | MM_MODE_D, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100914
915 /* Map each section. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000916 mm_identity_map(layout_text_begin(), layout_text_end(), MM_MODE_X,
917 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100918
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000919 mm_identity_map(layout_rodata_begin(), layout_rodata_end(), MM_MODE_R,
920 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100921
Andrew Scull5991ec92018-10-08 14:55:02 +0100922 mm_identity_map(layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000923 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100924
Andrew Scull1ba470e2018-10-31 15:14:31 +0000925 return arch_mm_init(ptable.root, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100926}
927
928bool mm_cpu_init(void)
929{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000930 return arch_mm_init(ptable.root, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100931}
932
933/**
934 * Defragments the hypervisor page table.
935 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000936void mm_defrag(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100937{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000938 mm_ptable_defrag(&ptable, MM_FLAG_STAGE1, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100939}