blob: be87e691b23c6ca72379841aad7dd8bd726b4ba0 [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"
Andrew Walbran48699362019-05-20 14:38:00 +010025#include "hf/plat/console.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010026
Andrew Walbran2400ed22018-09-27 14:45:58 +010027/**
28 * This file has functions for managing the level 1 and 2 page tables used by
29 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
30 * and then a level 2 mapping per VM. The design assumes that all page tables
31 * contain only 1-1 mappings, aligned on the block boundaries.
32 */
33
Andrew Scull80871322018-08-06 12:04:09 +010034/* The type of addresses stored in the page table. */
35typedef uintvaddr_t ptable_addr_t;
36
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010037/*
38 * For stage 2, the input is an intermediate physical addresses rather than a
39 * virtual address so:
40 */
Andrew Scull80871322018-08-06 12:04:09 +010041static_assert(
42 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
43 "Currently, the same code manages the stage 1 and stage 2 page tables "
44 "which only works if the virtual and intermediate physical addresses "
45 "are the same size. It looks like that assumption might not be holding "
46 "so we need to check that everything is going to be ok.");
47
Andrew Scull4f170f52018-07-19 12:58:20 +010048/* Keep macro alignment */
49/* clang-format off */
50
Andrew Scullda3df7f2019-01-05 17:49:27 +000051#define MM_FLAG_COMMIT 0x01
52#define MM_FLAG_UNMAP 0x02
Andrew Scullda241972019-01-05 18:17:48 +000053#define MM_FLAG_STAGE1 0x04
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010054
Andrew Scull4f170f52018-07-19 12:58:20 +010055/* clang-format on */
56
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010057static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010058static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010059
Andrew Scullda241972019-01-05 18:17:48 +000060static bool mm_stage2_invalidate = false;
61
62/**
63 * After calling this function, modifications to stage-2 page tables will use
64 * break-before-make and invalidate the TLB for the affected range.
65 */
66void mm_vm_enable_invalidation(void)
67{
68 mm_stage2_invalidate = true;
69}
70
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010071/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010072 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010073 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010074static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010075{
76 return ptr_from_va(va_from_pa(pa));
77}
78
79/**
Andrew Scull80871322018-08-06 12:04:09 +010080 * Rounds an address down to a page boundary.
81 */
82static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
83{
84 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
85}
86
87/**
88 * Rounds an address up to a page boundary.
89 */
90static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
91{
92 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
93}
94
95/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010096 * Calculates the size of the address space represented by a page table entry at
97 * the given level.
98 */
Andrew Sculle9827712018-10-19 14:54:20 +010099static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100100{
Andrew Scull78d6fd92018-09-06 15:08:36 +0100101 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100102}
103
104/**
Andrew Scullcae45572018-12-13 15:46:30 +0000105 * Gets the address of the start of the next block of the given size. The size
106 * must be a power of two.
107 */
108static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
109 size_t block_size)
110{
111 return (addr + block_size) & ~(block_size - 1);
112}
113
114/**
115 * Gets the physical address of the start of the next block of the given size.
116 * The size must be a power of two.
117 */
118static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
119{
120 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
121}
122
123/**
Andrew Scull80871322018-08-06 12:04:09 +0100124 * For a given address, calculates the maximum (plus one) address that can be
125 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100126 */
Andrew Sculle9827712018-10-19 14:54:20 +0100127static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100128{
129 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000130
Andrew Scull80871322018-08-06 12:04:09 +0100131 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100132}
133
134/**
Andrew Scull80871322018-08-06 12:04:09 +0100135 * For a given address, calculates the index at which its entry is stored in a
136 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100137 */
Andrew Sculle9827712018-10-19 14:54:20 +0100138static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100139{
Andrew Scull80871322018-08-06 12:04:09 +0100140 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000141
Andrew Scull78d6fd92018-09-06 15:08:36 +0100142 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100143}
144
145/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000146 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100147 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000148static struct mm_page_table *mm_alloc_page_tables(size_t count,
149 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100150{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000151 if (count == 1) {
152 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100153 }
154
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000155 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100156}
157
158/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000159 * Returns the maximum level in the page table given the flags.
160 */
161static uint8_t mm_max_level(int flags)
162{
163 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
164 : arch_mm_stage2_max_level();
165}
166
167/**
168 * Returns the number of root-level tables given the flags.
169 */
170static uint8_t mm_root_table_count(int flags)
171{
172 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
173 : arch_mm_stage2_root_table_count();
174}
175
176/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000177 * Invalidates the TLB for the given address range.
178 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000179static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000180{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000181 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000182 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
183 } else {
184 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
185 }
186}
187
188/**
189 * Frees all page-table-related memory associated with the given pte at the
190 * given level, including any subtables recursively.
191 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000192static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000193{
194 struct mm_page_table *table;
195 uint64_t i;
196
197 if (!arch_mm_pte_is_table(pte, level)) {
198 return;
199 }
200
201 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000202 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000203 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000204 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000205 }
206
207 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000208 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000209}
210
211/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000212 * Initialises the given page table.
213 */
214static bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
215{
216 uint8_t i;
217 size_t j;
218 struct mm_page_table *tables;
219 uint8_t root_table_count = mm_root_table_count(flags);
220
221 tables = mm_alloc_page_tables(root_table_count, ppool);
222 if (tables == NULL) {
223 return false;
224 }
225
226 for (i = 0; i < root_table_count; i++) {
227 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
228 tables[i].entries[j] =
229 arch_mm_absent_pte(mm_max_level(flags));
230 }
231 }
232
233 /*
234 * TODO: halloc could return a virtual or physical address if mm not
235 * enabled?
236 */
237 t->root = pa_init((uintpaddr_t)tables);
238
239 return true;
240}
241
242/**
243 * Frees all memory associated with the give page table.
244 */
245static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
246{
247 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
248 uint8_t level = mm_max_level(flags);
249 uint8_t root_table_count = mm_root_table_count(flags);
250 uint8_t i;
251 uint64_t j;
252
253 for (i = 0; i < root_table_count; ++i) {
254 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
255 mm_free_page_pte(tables[i].entries[j], level, ppool);
256 }
257 }
258
259 mpool_add_chunk(ppool, tables,
260 sizeof(struct mm_page_table) * root_table_count);
261}
262
263/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000264 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000265 * are valid, it performs a break-before-make sequence where it first writes an
266 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
267 * This is to prevent cases where CPUs have different 'valid' values in their
268 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000269 */
270static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000271 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000272{
273 pte_t v = *pte;
274
275 /*
276 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000277 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000278 */
Andrew Scullda241972019-01-05 18:17:48 +0000279 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
280 arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000281 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000282 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000283 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000284 }
285
286 /* Assign the new pte. */
287 *pte = new_pte;
288
289 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000290 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000291}
292
293/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100294 * Populates the provided page table entry with a reference to another table if
295 * needed, that is, if it does not yet point to another table.
296 *
297 * Returns a pointer to the table the entry now points to.
298 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000299static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
300 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000301 int flags,
302 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100303{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100304 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100305 pte_t v = *pte;
306 pte_t new_pte;
307 size_t i;
308 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100309 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100310
311 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100312 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000313 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100314 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100315
316 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000317 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100318 if (ntable == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100319 dlog("Failed to allocate memory for page table\n");
320 return NULL;
321 }
322
323 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100324 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100325 inc = mm_entry_size(level_below);
326 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000327 arch_mm_block_from_pte(v, level),
328 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100329 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100330 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100331 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100332 }
333
334 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100335 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
336 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100337 new_pte += inc;
338 }
339
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000340 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100341 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000342
343 /* Replace the pte entry, doing a break-before-make if needed. */
344 mm_replace_entry(begin, pte,
345 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000346 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100347
348 return ntable;
349}
350
351/**
Andrew Walbran6324fc92018-10-03 11:46:43 +0100352 * Returns whether all entries in this table are absent.
353 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000354static bool mm_page_table_is_empty(struct mm_page_table *table, uint8_t level)
Andrew Walbran6324fc92018-10-03 11:46:43 +0100355{
356 uint64_t i;
357
Andrew Scull4e5f8142018-10-12 14:37:19 +0100358 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
359 if (arch_mm_pte_is_present(table->entries[i], level)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100360 return false;
361 }
362 }
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000363
Andrew Walbran6324fc92018-10-03 11:46:43 +0100364 return true;
365}
366
367/**
Andrew Scull80871322018-08-06 12:04:09 +0100368 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100369 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000370 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100371 *
372 * This function calls itself recursively if it needs to update additional
373 * levels, but the recursion is bound by the maximum number of levels in a page
374 * table.
375 */
Andrew Scull80871322018-08-06 12:04:09 +0100376static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100377 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000378 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100379{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100380 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100381 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100382 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000383 bool commit = flags & MM_FLAG_COMMIT;
384 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100385
Andrew Scull265ada92018-07-30 15:19:01 +0100386 /* Cap end so that we don't go over the current level max. */
387 if (end > level_end) {
388 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100389 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100390
391 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100392 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100393 if (unmap ? !arch_mm_pte_is_present(*pte, level)
394 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000395 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100396 /*
397 * If the entry is already mapped with the right
398 * attributes, or already absent in the case of
399 * unmapping, no need to do anything; carry on to the
400 * next entry.
401 */
402 } else if ((end - begin) >= entry_size &&
403 (unmap || arch_mm_is_block_allowed(level)) &&
404 (begin & (entry_size - 1)) == 0) {
405 /*
406 * If the entire entry is within the region we want to
407 * map, map/unmap the whole entry.
408 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100409 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000410 pte_t new_pte =
411 unmap ? arch_mm_absent_pte(level)
412 : arch_mm_block_pte(level, pa,
413 attrs);
414 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000415 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100416 }
417 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100418 /*
419 * If the entry is already a subtable get it; otherwise
420 * replace it with an equivalent subtable and get that.
421 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000422 struct mm_page_table *nt = mm_populate_table_pte(
423 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100424 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100425 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100426 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100427
Andrew Walbran6324fc92018-10-03 11:46:43 +0100428 /*
429 * Recurse to map/unmap the appropriate entries within
430 * the subtable.
431 */
Andrew Scull80871322018-08-06 12:04:09 +0100432 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000433 flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100434 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100435 }
Andrew Walbran6324fc92018-10-03 11:46:43 +0100436
437 /*
438 * If the subtable is now empty, replace it with an
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000439 * absent entry at this level. We never need to do
440 * break-before-makes here because we are assigning
441 * an absent value.
Andrew Walbran6324fc92018-10-03 11:46:43 +0100442 */
443 if (commit && unmap &&
Andrew Scull1ba470e2018-10-31 15:14:31 +0000444 mm_page_table_is_empty(nt, level - 1)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100445 pte_t v = *pte;
446 *pte = arch_mm_absent_pte(level);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000447 mm_free_page_pte(v, level, ppool);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100448 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100449 }
450
Andrew Scullcae45572018-12-13 15:46:30 +0000451 begin = mm_start_of_next_block(begin, entry_size);
452 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100453 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100454 }
455
456 return true;
457}
458
459/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000460 * Updates the page table from the root to map the given address range to a
461 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000462 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000463 */
464static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
465 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000466 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000467{
468 size_t root_table_size = mm_entry_size(root_level);
469 struct mm_page_table *table =
470 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
471
472 while (begin < end) {
473 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000474 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000475 return false;
476 }
Andrew Scullcae45572018-12-13 15:46:30 +0000477 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000478 table++;
479 }
480
481 return true;
482}
483
484/**
Andrew Scull80871322018-08-06 12:04:09 +0100485 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000486 * or not mapped into the address space with the architecture-agnostic mode
487 * provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100488 */
Andrew Sculla6da8342018-11-01 12:29:49 +0000489static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000490 paddr_t pa_end, uint64_t attrs, int flags,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000491 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100492{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000493 uint8_t root_level = mm_max_level(flags) + 1;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000494 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000495 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000496 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
497 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100498
Andrew Scull1ba470e2018-10-31 15:14:31 +0000499 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100500 * Assert condition to communicate the API constraint of mm_max_level(),
501 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000502 */
Andrew Scullf8252932019-04-04 13:51:22 +0100503 assert(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000504
505 /* Cap end to stay within the bounds of the page table. */
506 if (end > ptable_end) {
507 end = ptable_end;
508 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100509
510 /*
511 * Do it in two steps to prevent leaving the table in a halfway updated
512 * state. In such a two-step implementation, the table may be left with
513 * extra internal tables, but no different mapping on failure.
514 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000515 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool) ||
Andrew Scull1ba470e2018-10-31 15:14:31 +0000516 !mm_map_root(t, begin, end, attrs, root_level,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000517 flags | MM_FLAG_COMMIT, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100518 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100519 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100520
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100521 /* Invalidate the tlb. */
Andrew Scullda241972019-01-05 18:17:48 +0000522 if ((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000523 mm_invalidate_tlb(begin, end, flags);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100524 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100525
526 return true;
527}
528
529/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100530 * Writes the given table to the debug log, calling itself recursively to
531 * write sub-tables.
532 */
Andrew Sculle9827712018-10-19 14:54:20 +0100533static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100534 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100535{
536 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000537
Andrew Scull4e5f8142018-10-12 14:37:19 +0100538 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
539 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100540 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100541 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100542
Andrew Scull4e5f8142018-10-12 14:37:19 +0100543 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
544 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100545
Andrew Scull4e5f8142018-10-12 14:37:19 +0100546 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100547 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100548 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000549 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100550 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100551 }
552 }
553}
554
555/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000556 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100557 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000558static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100559{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000560 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000561 uint8_t max_level = mm_max_level(flags);
562 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000563 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000564
Andrew Scull1ba470e2018-10-31 15:14:31 +0000565 for (i = 0; i < root_table_count; ++i) {
566 mm_dump_table_recursive(&tables[i], max_level, max_level);
567 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100568}
569
570/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000571 * Given the table PTE entries all have identical attributes, returns the single
572 * entry with which it can be replaced. Note that the table PTE will no longer
573 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000574 *
575 * If the table is freed, the memory is freed directly rather than calling
576 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100577 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000578static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
579 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100580{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100581 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100582 uint64_t block_attrs;
583 uint64_t table_attrs;
584 uint64_t combined_attrs;
585 paddr_t block_address;
586
Andrew Scullb6b9b562018-12-21 14:41:35 +0000587 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
588
589 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
590 /* Free the table and return an absent entry. */
591 mpool_free(ppool, table);
592 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100593 }
594
Andrew Scullb6b9b562018-12-21 14:41:35 +0000595 /* Might not be possible to merge the table into a single block. */
596 if (!arch_mm_is_block_allowed(level)) {
597 return table_pte;
598 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000599
Andrew Scullb6b9b562018-12-21 14:41:35 +0000600 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000601 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000602 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100603 combined_attrs =
604 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000605 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000606
Andrew Scullb6b9b562018-12-21 14:41:35 +0000607 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000608 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100609 return arch_mm_block_pte(level, block_address, combined_attrs);
610}
611
612/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000613 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000614 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100615 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000616static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
617 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100618{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100619 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100620 uint64_t i;
621 uint64_t attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100622
623 if (!arch_mm_pte_is_table(entry, level)) {
624 return entry;
625 }
626
Andrew Scull3681b8d2018-12-12 14:22:59 +0000627 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100628
629 /*
630 * Check if all entries are blocks with the same flags or are all
Andrew Scullb6b9b562018-12-21 14:41:35 +0000631 * absent. It assumes addresses are contiguous due to identity mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100632 */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000633 attrs = arch_mm_pte_attrs(table->entries[0], level);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100634 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000635 /* First try to defrag the entry, in case it is a subtable. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000636 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
637 level - 1, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100638
Andrew Walbran2400ed22018-09-27 14:45:58 +0100639 /*
Andrew Scullb6b9b562018-12-21 14:41:35 +0000640 * If the entry isn't a block or has different attributes then
641 * it isn't possible to defragment it.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100642 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100643 if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
Andrew Scull3681b8d2018-12-12 14:22:59 +0000644 arch_mm_pte_attrs(table->entries[i], level) != attrs) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000645 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100646 }
647 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000648
649 return mm_merge_table_pte(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100650}
651
652/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100653 * Defragments the given page table by converting page table references to
654 * blocks whenever possible.
655 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000656static void mm_ptable_defrag(struct mm_ptable *t, int flags,
657 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100658{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000659 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000660 uint8_t level = mm_max_level(flags);
661 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000662 uint8_t i;
663 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100664
665 /*
666 * Loop through each entry in the table. If it points to another table,
667 * check if that table can be replaced by a block or an absent entry.
668 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000669 for (i = 0; i < root_table_count; ++i) {
670 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
671 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000672 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000673 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100674 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100675}
676
677/**
Andrew Scull81e85092018-12-12 12:56:20 +0000678 * Gets the attributes applied to the given range of stage-2 addresses at the
679 * given level.
680 *
681 * The `got_attrs` argument is initially passed as false until `attrs` contains
682 * attributes of the memory region at which point it is passed as true.
683 *
684 * The value returned in `attrs` is only valid if the function returns true.
685 *
686 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100687 */
Andrew Scull81e85092018-12-12 12:56:20 +0000688static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
689 ptable_addr_t begin, ptable_addr_t end,
690 uint8_t level, bool got_attrs,
691 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100692{
Andrew Scull81e85092018-12-12 12:56:20 +0000693 pte_t *pte = &table->entries[mm_index(begin, level)];
694 ptable_addr_t level_end = mm_level_end(begin, level);
695 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100696
Andrew Scull81e85092018-12-12 12:56:20 +0000697 /* Cap end so that we don't go over the current level max. */
698 if (end > level_end) {
699 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100700 }
701
Andrew Scull81e85092018-12-12 12:56:20 +0000702 /* Check that each entry is owned. */
703 while (begin < end) {
704 if (arch_mm_pte_is_table(*pte, level)) {
705 if (!mm_ptable_get_attrs_level(
706 mm_page_table_from_pa(
707 arch_mm_table_from_pte(*pte,
708 level)),
709 begin, end, level - 1, got_attrs, attrs)) {
710 return false;
711 }
712 got_attrs = true;
713 } else {
714 if (!got_attrs) {
715 *attrs = arch_mm_pte_attrs(*pte, level);
716 got_attrs = true;
717 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
718 return false;
719 }
720 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100721
Andrew Scull81e85092018-12-12 12:56:20 +0000722 begin = mm_start_of_next_block(begin, entry_size);
723 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100724 }
725
Andrew Scullc66a04d2018-12-07 13:41:56 +0000726 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000727 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100728}
729
730/**
Andrew Scull81e85092018-12-12 12:56:20 +0000731 * Gets the attributes applies to the given range of addresses in the stage-2
732 * table.
733 *
734 * The value returned in `attrs` is only valid if the function returns true.
735 *
736 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100737 */
Andrew Scull81e85092018-12-12 12:56:20 +0000738static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
739 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100740{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000741 int flags = 0;
742 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000743 uint8_t root_level = max_level + 1;
744 size_t root_table_size = mm_entry_size(root_level);
745 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000746 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000747 struct mm_page_table *table;
748 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100749
Andrew Scull81e85092018-12-12 12:56:20 +0000750 begin = mm_round_down_to_page(begin);
751 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100752
Andrew Scull81e85092018-12-12 12:56:20 +0000753 /* Fail if the addresses are out of range. */
754 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000755 return false;
756 }
757
Andrew Scull81e85092018-12-12 12:56:20 +0000758 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
759 while (begin < end) {
760 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
761 got_attrs, attrs)) {
762 return false;
763 }
764
765 got_attrs = true;
766 begin = mm_start_of_next_block(begin, root_table_size);
767 table++;
768 }
769
770 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100771}
772
Andrew Scullda3df7f2019-01-05 17:49:27 +0000773bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100774{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000775 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100776}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100777
Andrew Scullda3df7f2019-01-05 17:49:27 +0000778void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000779{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000780 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000781}
782
783/**
Andrew Scull80871322018-08-06 12:04:09 +0100784 * Updates a VM's page table such that the given physical address range is
785 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100786 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100787 */
Andrew Scull80871322018-08-06 12:04:09 +0100788bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000789 int mode, ipaddr_t *ipa, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100790{
Andrew Scullda241972019-01-05 18:17:48 +0000791 int flags = 0;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000792 bool success = mm_ptable_identity_update(
793 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
794 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100795
796 if (success && ipa != NULL) {
797 *ipa = ipa_from_pa(begin);
798 }
799
800 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100801}
802
803/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000804 * Updates the VM's table such that the given physical address range has no
805 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100806 */
Andrew Scullda241972019-01-05 18:17:48 +0000807bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000808 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100809{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000810 return mm_ptable_identity_update(
811 t, begin, end,
812 arch_mm_mode_to_stage2_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
813 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000814 MM_FLAG_UNMAP, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100815}
816
817/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000818 * Unmaps the hypervisor pages from the given page table.
819 */
Andrew Scullda241972019-01-05 18:17:48 +0000820bool mm_vm_unmap_hypervisor(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000821{
822 /* TODO: If we add pages dynamically, they must be included here too. */
Andrew Scullda241972019-01-05 18:17:48 +0000823 return mm_vm_unmap(t, layout_text_begin(), layout_text_end(), ppool) &&
824 mm_vm_unmap(t, layout_rodata_begin(), layout_rodata_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000825 ppool) &&
Andrew Scullda241972019-01-05 18:17:48 +0000826 mm_vm_unmap(t, layout_data_begin(), layout_data_end(), ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000827}
828
829/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000830 * Write the given page table of a VM to the debug log.
831 */
832void mm_vm_dump(struct mm_ptable *t)
833{
834 mm_ptable_dump(t, 0);
835}
836
837/**
838 * Defragments the VM page table.
839 */
840void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
841{
842 mm_ptable_defrag(t, 0, ppool);
843}
844
845/**
Andrew Scull81e85092018-12-12 12:56:20 +0000846 * Gets the mode of the give range of intermediate physical addresses if they
847 * are mapped with the same mode.
848 *
849 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100850 */
Andrew Scull81e85092018-12-12 12:56:20 +0000851bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
852 int *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100853{
Andrew Scull81e85092018-12-12 12:56:20 +0000854 uint64_t attrs;
855 bool ret;
856
857 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
858 if (ret) {
859 *mode = arch_mm_stage2_attrs_to_mode(attrs);
860 }
861
862 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100863}
864
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100865static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
866{
867 return (struct mm_stage1_locked){.ptable = &ptable};
868}
869
870struct mm_stage1_locked mm_lock_stage1(void)
871{
872 sl_lock(&ptable_lock);
873 return mm_stage1_lock_unsafe();
874}
875
876void mm_unlock_stage1(struct mm_stage1_locked *lock)
877{
878 assert(lock->ptable == &ptable);
879 sl_unlock(&ptable_lock);
880 lock->ptable = NULL;
881}
882
Andrew Scull80871322018-08-06 12:04:09 +0100883/**
Andrew Scull80871322018-08-06 12:04:09 +0100884 * Updates the hypervisor page table such that the given physical address range
885 * is mapped into the address space at the corresponding address range in the
886 * architecture-agnostic mode provided.
887 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100888void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
889 paddr_t end, int mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100890{
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100891 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scullda241972019-01-05 18:17:48 +0000892 arch_mm_mode_to_stage1_attrs(mode),
893 MM_FLAG_STAGE1, ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100894 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100895 }
896
897 return NULL;
898}
899
900/**
901 * Updates the hypervisor table such that the given physical address range is
902 * not mapped in the address space.
903 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100904bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
905 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100906{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000907 return mm_ptable_identity_update(
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100908 stage1_locked.ptable, begin, end,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000909 arch_mm_mode_to_stage1_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
910 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000911 MM_FLAG_STAGE1 | MM_FLAG_UNMAP, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100912}
913
914/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100915 * Defragments the hypervisor page table.
916 */
917void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
918{
919 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
920}
921
922/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100923 * Initialises memory management for the hypervisor itself.
924 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000925bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100926{
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100927 /* Locking is not enabled yet so fake it, */
928 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
929
Andrew Scullfdd716e2018-12-20 05:37:31 +0000930 dlog("text: 0x%x - 0x%x\n", pa_addr(layout_text_begin()),
931 pa_addr(layout_text_end()));
932 dlog("rodata: 0x%x - 0x%x\n", pa_addr(layout_rodata_begin()),
933 pa_addr(layout_rodata_end()));
934 dlog("data: 0x%x - 0x%x\n", pa_addr(layout_data_begin()),
935 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100936
Andrew Scullda3df7f2019-01-05 17:49:27 +0000937 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Scullfdd716e2018-12-20 05:37:31 +0000938 dlog("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100939 return false;
940 }
941
Andrew Walbran48699362019-05-20 14:38:00 +0100942 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100943 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100944
945 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100946 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
947 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100948
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100949 mm_identity_map(stage1_locked, layout_rodata_begin(),
950 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100951
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100952 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000953 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100954
Andrew Scull1ba470e2018-10-31 15:14:31 +0000955 return arch_mm_init(ptable.root, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100956}
957
958bool mm_cpu_init(void)
959{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000960 return arch_mm_init(ptable.root, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100961}