blob: 2a85484feb900deeec0474d9ae7d19c530981227 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/mm.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010018
19#include <stdatomic.h>
20#include <stdint.h>
21
Andrew Scull877ae4b2019-07-02 12:52:33 +010022#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010024#include "hf/layout.h"
Andrew Walbran48699362019-05-20 14:38:00 +010025#include "hf/plat/console.h"
Andrew Scull877ae4b2019-07-02 12:52:33 +010026#include "hf/static_assert.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010027
Andrew Walbran2400ed22018-09-27 14:45:58 +010028/**
29 * This file has functions for managing the level 1 and 2 page tables used by
30 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
31 * and then a level 2 mapping per VM. The design assumes that all page tables
32 * contain only 1-1 mappings, aligned on the block boundaries.
33 */
34
Andrew Scull80871322018-08-06 12:04:09 +010035/* The type of addresses stored in the page table. */
36typedef uintvaddr_t ptable_addr_t;
37
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010038/*
39 * For stage 2, the input is an intermediate physical addresses rather than a
40 * virtual address so:
41 */
Andrew Scull80871322018-08-06 12:04:09 +010042static_assert(
43 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
44 "Currently, the same code manages the stage 1 and stage 2 page tables "
45 "which only works if the virtual and intermediate physical addresses "
46 "are the same size. It looks like that assumption might not be holding "
47 "so we need to check that everything is going to be ok.");
48
Andrew Scull4f170f52018-07-19 12:58:20 +010049/* Keep macro alignment */
50/* clang-format off */
51
Andrew Scullda3df7f2019-01-05 17:49:27 +000052#define MM_FLAG_COMMIT 0x01
53#define MM_FLAG_UNMAP 0x02
Andrew Scullda241972019-01-05 18:17:48 +000054#define MM_FLAG_STAGE1 0x04
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010055
Andrew Scull4f170f52018-07-19 12:58:20 +010056/* clang-format on */
57
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010058static struct mm_ptable ptable;
Andrew Scull3c0a90a2019-07-01 11:55:53 +010059static struct spinlock ptable_lock;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010060
Andrew Scullda241972019-01-05 18:17:48 +000061static bool mm_stage2_invalidate = false;
62
63/**
64 * After calling this function, modifications to stage-2 page tables will use
65 * break-before-make and invalidate the TLB for the affected range.
66 */
67void mm_vm_enable_invalidation(void)
68{
69 mm_stage2_invalidate = true;
70}
71
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010072/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010073 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010074 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010075static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010076{
77 return ptr_from_va(va_from_pa(pa));
78}
79
80/**
Andrew Scull80871322018-08-06 12:04:09 +010081 * Rounds an address down to a page boundary.
82 */
83static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
84{
85 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
86}
87
88/**
89 * Rounds an address up to a page boundary.
90 */
91static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
92{
93 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
94}
95
96/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010097 * Calculates the size of the address space represented by a page table entry at
98 * the given level.
99 */
Andrew Sculle9827712018-10-19 14:54:20 +0100100static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100101{
Andrew Scull78d6fd92018-09-06 15:08:36 +0100102 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100103}
104
105/**
Andrew Scullcae45572018-12-13 15:46:30 +0000106 * Gets the address of the start of the next block of the given size. The size
107 * must be a power of two.
108 */
109static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
110 size_t block_size)
111{
112 return (addr + block_size) & ~(block_size - 1);
113}
114
115/**
116 * Gets the physical address of the start of the next block of the given size.
117 * The size must be a power of two.
118 */
119static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
120{
121 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
122}
123
124/**
Andrew Scull80871322018-08-06 12:04:09 +0100125 * For a given address, calculates the maximum (plus one) address that can be
126 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100127 */
Andrew Sculle9827712018-10-19 14:54:20 +0100128static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100129{
130 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000131
Andrew Scull80871322018-08-06 12:04:09 +0100132 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100133}
134
135/**
Andrew Scull80871322018-08-06 12:04:09 +0100136 * For a given address, calculates the index at which its entry is stored in a
137 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100138 */
Andrew Sculle9827712018-10-19 14:54:20 +0100139static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100140{
Andrew Scull80871322018-08-06 12:04:09 +0100141 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000142
Andrew Scull78d6fd92018-09-06 15:08:36 +0100143 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100144}
145
146/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000147 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100148 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000149static struct mm_page_table *mm_alloc_page_tables(size_t count,
150 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100151{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000152 if (count == 1) {
153 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100154 }
155
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000156 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100157}
158
159/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000160 * Returns the maximum level in the page table given the flags.
161 */
162static uint8_t mm_max_level(int flags)
163{
164 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
165 : arch_mm_stage2_max_level();
166}
167
168/**
169 * Returns the number of root-level tables given the flags.
170 */
171static uint8_t mm_root_table_count(int flags)
172{
173 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
174 : arch_mm_stage2_root_table_count();
175}
176
177/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000178 * Invalidates the TLB for the given address range.
179 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000180static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000181{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000182 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000183 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
184 } else {
185 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
186 }
187}
188
189/**
190 * Frees all page-table-related memory associated with the given pte at the
191 * given level, including any subtables recursively.
192 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000193static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000194{
195 struct mm_page_table *table;
196 uint64_t i;
197
198 if (!arch_mm_pte_is_table(pte, level)) {
199 return;
200 }
201
202 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000203 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000204 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000205 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000206 }
207
208 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000209 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000210}
211
212/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000213 * Initialises the given page table.
214 */
215static bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
216{
217 uint8_t i;
218 size_t j;
219 struct mm_page_table *tables;
220 uint8_t root_table_count = mm_root_table_count(flags);
221
222 tables = mm_alloc_page_tables(root_table_count, ppool);
223 if (tables == NULL) {
224 return false;
225 }
226
227 for (i = 0; i < root_table_count; i++) {
228 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
229 tables[i].entries[j] =
230 arch_mm_absent_pte(mm_max_level(flags));
231 }
232 }
233
234 /*
235 * TODO: halloc could return a virtual or physical address if mm not
236 * enabled?
237 */
238 t->root = pa_init((uintpaddr_t)tables);
239
240 return true;
241}
242
243/**
244 * Frees all memory associated with the give page table.
245 */
246static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
247{
248 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
249 uint8_t level = mm_max_level(flags);
250 uint8_t root_table_count = mm_root_table_count(flags);
251 uint8_t i;
252 uint64_t j;
253
254 for (i = 0; i < root_table_count; ++i) {
255 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
256 mm_free_page_pte(tables[i].entries[j], level, ppool);
257 }
258 }
259
260 mpool_add_chunk(ppool, tables,
261 sizeof(struct mm_page_table) * root_table_count);
262}
263
264/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000265 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000266 * are valid, it performs a break-before-make sequence where it first writes an
267 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
268 * This is to prevent cases where CPUs have different 'valid' values in their
269 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000270 */
271static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000272 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000273{
274 pte_t v = *pte;
275
276 /*
277 * We need to do the break-before-make sequence if both values are
Andrew Scull3cd9e262019-01-08 17:59:22 +0000278 * present and the TLB is being invalidated.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000279 */
Andrew Scullda241972019-01-05 18:17:48 +0000280 if (((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) &&
281 arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000282 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000283 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000284 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000285 }
286
287 /* Assign the new pte. */
288 *pte = new_pte;
289
290 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000291 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000292}
293
294/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100295 * Populates the provided page table entry with a reference to another table if
296 * needed, that is, if it does not yet point to another table.
297 *
298 * Returns a pointer to the table the entry now points to.
299 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000300static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
301 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000302 int flags,
303 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100304{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100305 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306 pte_t v = *pte;
307 pte_t new_pte;
308 size_t i;
309 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100310 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100311
312 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100313 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000314 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100315 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100316
317 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000318 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100319 if (ntable == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100320 dlog("Failed to allocate memory for page table\n");
321 return NULL;
322 }
323
324 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100325 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100326 inc = mm_entry_size(level_below);
327 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000328 arch_mm_block_from_pte(v, level),
329 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100330 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100331 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100332 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100333 }
334
335 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100336 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
337 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100338 new_pte += inc;
339 }
340
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000341 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100342 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000343
344 /* Replace the pte entry, doing a break-before-make if needed. */
345 mm_replace_entry(begin, pte,
346 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000347 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100348
349 return ntable;
350}
351
352/**
Andrew Walbran6324fc92018-10-03 11:46:43 +0100353 * Returns whether all entries in this table are absent.
354 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000355static bool mm_page_table_is_empty(struct mm_page_table *table, uint8_t level)
Andrew Walbran6324fc92018-10-03 11:46:43 +0100356{
357 uint64_t i;
358
Andrew Scull4e5f8142018-10-12 14:37:19 +0100359 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
360 if (arch_mm_pte_is_present(table->entries[i], level)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100361 return false;
362 }
363 }
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000364
Andrew Walbran6324fc92018-10-03 11:46:43 +0100365 return true;
366}
367
368/**
Andrew Scull80871322018-08-06 12:04:09 +0100369 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100370 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000371 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100372 *
373 * This function calls itself recursively if it needs to update additional
374 * levels, but the recursion is bound by the maximum number of levels in a page
375 * table.
376 */
Andrew Scull80871322018-08-06 12:04:09 +0100377static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100378 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000379 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100380{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100381 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100382 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100383 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000384 bool commit = flags & MM_FLAG_COMMIT;
385 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100386
Andrew Scull265ada92018-07-30 15:19:01 +0100387 /* Cap end so that we don't go over the current level max. */
388 if (end > level_end) {
389 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100390 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100391
392 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100393 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100394 if (unmap ? !arch_mm_pte_is_present(*pte, level)
395 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000396 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100397 /*
398 * If the entry is already mapped with the right
399 * attributes, or already absent in the case of
400 * unmapping, no need to do anything; carry on to the
401 * next entry.
402 */
403 } else if ((end - begin) >= entry_size &&
404 (unmap || arch_mm_is_block_allowed(level)) &&
405 (begin & (entry_size - 1)) == 0) {
406 /*
407 * If the entire entry is within the region we want to
408 * map, map/unmap the whole entry.
409 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100410 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000411 pte_t new_pte =
412 unmap ? arch_mm_absent_pte(level)
413 : arch_mm_block_pte(level, pa,
414 attrs);
415 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000416 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100417 }
418 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100419 /*
420 * If the entry is already a subtable get it; otherwise
421 * replace it with an equivalent subtable and get that.
422 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000423 struct mm_page_table *nt = mm_populate_table_pte(
424 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100425 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100426 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100427 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100428
Andrew Walbran6324fc92018-10-03 11:46:43 +0100429 /*
430 * Recurse to map/unmap the appropriate entries within
431 * the subtable.
432 */
Andrew Scull80871322018-08-06 12:04:09 +0100433 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000434 flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100435 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100436 }
Andrew Walbran6324fc92018-10-03 11:46:43 +0100437
438 /*
439 * If the subtable is now empty, replace it with an
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000440 * absent entry at this level. We never need to do
441 * break-before-makes here because we are assigning
442 * an absent value.
Andrew Walbran6324fc92018-10-03 11:46:43 +0100443 */
444 if (commit && unmap &&
Andrew Scull1ba470e2018-10-31 15:14:31 +0000445 mm_page_table_is_empty(nt, level - 1)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100446 pte_t v = *pte;
447 *pte = arch_mm_absent_pte(level);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000448 mm_free_page_pte(v, level, ppool);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100449 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100450 }
451
Andrew Scullcae45572018-12-13 15:46:30 +0000452 begin = mm_start_of_next_block(begin, entry_size);
453 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100454 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100455 }
456
457 return true;
458}
459
460/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000461 * Updates the page table from the root to map the given address range to a
462 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000463 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000464 */
465static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
466 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000467 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000468{
469 size_t root_table_size = mm_entry_size(root_level);
470 struct mm_page_table *table =
471 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
472
473 while (begin < end) {
474 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000475 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000476 return false;
477 }
Andrew Scullcae45572018-12-13 15:46:30 +0000478 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000479 table++;
480 }
481
482 return true;
483}
484
485/**
Andrew Scull80871322018-08-06 12:04:09 +0100486 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000487 * or not mapped into the address space with the architecture-agnostic mode
488 * provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100489 */
Andrew Sculla6da8342018-11-01 12:29:49 +0000490static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000491 paddr_t pa_end, uint64_t attrs, int flags,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000492 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100493{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000494 uint8_t root_level = mm_max_level(flags) + 1;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000495 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000496 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000497 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
498 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100499
Andrew Scull1ba470e2018-10-31 15:14:31 +0000500 /*
Andrew Scullf8252932019-04-04 13:51:22 +0100501 * Assert condition to communicate the API constraint of mm_max_level(),
502 * that isn't encoded in the types, to the static analyzer.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000503 */
Andrew Scull877ae4b2019-07-02 12:52:33 +0100504 CHECK(root_level >= 2);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000505
506 /* Cap end to stay within the bounds of the page table. */
507 if (end > ptable_end) {
508 end = ptable_end;
509 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100510
511 /*
512 * Do it in two steps to prevent leaving the table in a halfway updated
513 * state. In such a two-step implementation, the table may be left with
514 * extra internal tables, but no different mapping on failure.
515 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000516 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool) ||
Andrew Scull1ba470e2018-10-31 15:14:31 +0000517 !mm_map_root(t, begin, end, attrs, root_level,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000518 flags | MM_FLAG_COMMIT, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100519 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100520 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100521
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100522 /* Invalidate the tlb. */
Andrew Scullda241972019-01-05 18:17:48 +0000523 if ((flags & MM_FLAG_STAGE1) || mm_stage2_invalidate) {
Andrew Scullda3df7f2019-01-05 17:49:27 +0000524 mm_invalidate_tlb(begin, end, flags);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100525 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100526
527 return true;
528}
529
530/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100531 * Writes the given table to the debug log, calling itself recursively to
532 * write sub-tables.
533 */
Andrew Sculle9827712018-10-19 14:54:20 +0100534static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100535 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100536{
537 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000538
Andrew Scull4e5f8142018-10-12 14:37:19 +0100539 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
540 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100541 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100542 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100543
Andrew Scull4e5f8142018-10-12 14:37:19 +0100544 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
545 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100546
Andrew Scull4e5f8142018-10-12 14:37:19 +0100547 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100548 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100549 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000550 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100551 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100552 }
553 }
554}
555
556/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000557 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100558 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000559static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100560{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000561 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000562 uint8_t max_level = mm_max_level(flags);
563 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000564 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000565
Andrew Scull1ba470e2018-10-31 15:14:31 +0000566 for (i = 0; i < root_table_count; ++i) {
567 mm_dump_table_recursive(&tables[i], max_level, max_level);
568 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100569}
570
571/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000572 * Given the table PTE entries all have identical attributes, returns the single
573 * entry with which it can be replaced. Note that the table PTE will no longer
574 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000575 *
576 * If the table is freed, the memory is freed directly rather than calling
577 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100578 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000579static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
580 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100581{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100582 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100583 uint64_t block_attrs;
584 uint64_t table_attrs;
585 uint64_t combined_attrs;
586 paddr_t block_address;
587
Andrew Scullb6b9b562018-12-21 14:41:35 +0000588 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
589
590 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
591 /* Free the table and return an absent entry. */
592 mpool_free(ppool, table);
593 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100594 }
595
Andrew Scullb6b9b562018-12-21 14:41:35 +0000596 /* Might not be possible to merge the table into a single block. */
597 if (!arch_mm_is_block_allowed(level)) {
598 return table_pte;
599 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000600
Andrew Scullb6b9b562018-12-21 14:41:35 +0000601 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000602 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000603 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100604 combined_attrs =
605 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000606 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000607
Andrew Scullb6b9b562018-12-21 14:41:35 +0000608 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000609 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100610 return arch_mm_block_pte(level, block_address, combined_attrs);
611}
612
613/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000614 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000615 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100616 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000617static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
618 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100619{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100620 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100621 uint64_t i;
622 uint64_t attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100623
624 if (!arch_mm_pte_is_table(entry, level)) {
625 return entry;
626 }
627
Andrew Scull3681b8d2018-12-12 14:22:59 +0000628 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100629
630 /*
631 * Check if all entries are blocks with the same flags or are all
Andrew Scullb6b9b562018-12-21 14:41:35 +0000632 * absent. It assumes addresses are contiguous due to identity mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100633 */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000634 attrs = arch_mm_pte_attrs(table->entries[0], level);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100635 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000636 /* First try to defrag the entry, in case it is a subtable. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000637 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
638 level - 1, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100639
Andrew Walbran2400ed22018-09-27 14:45:58 +0100640 /*
Andrew Scullb6b9b562018-12-21 14:41:35 +0000641 * If the entry isn't a block or has different attributes then
642 * it isn't possible to defragment it.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100643 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100644 if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
Andrew Scull3681b8d2018-12-12 14:22:59 +0000645 arch_mm_pte_attrs(table->entries[i], level) != attrs) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000646 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100647 }
648 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000649
650 return mm_merge_table_pte(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100651}
652
653/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100654 * Defragments the given page table by converting page table references to
655 * blocks whenever possible.
656 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000657static void mm_ptable_defrag(struct mm_ptable *t, int flags,
658 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100659{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000660 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000661 uint8_t level = mm_max_level(flags);
662 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000663 uint8_t i;
664 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100665
666 /*
667 * Loop through each entry in the table. If it points to another table,
668 * check if that table can be replaced by a block or an absent entry.
669 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000670 for (i = 0; i < root_table_count; ++i) {
671 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
672 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000673 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000674 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100675 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100676}
677
678/**
Andrew Scull81e85092018-12-12 12:56:20 +0000679 * Gets the attributes applied to the given range of stage-2 addresses at the
680 * given level.
681 *
682 * The `got_attrs` argument is initially passed as false until `attrs` contains
683 * attributes of the memory region at which point it is passed as true.
684 *
685 * The value returned in `attrs` is only valid if the function returns true.
686 *
687 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100688 */
Andrew Scull81e85092018-12-12 12:56:20 +0000689static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
690 ptable_addr_t begin, ptable_addr_t end,
691 uint8_t level, bool got_attrs,
692 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100693{
Andrew Scull81e85092018-12-12 12:56:20 +0000694 pte_t *pte = &table->entries[mm_index(begin, level)];
695 ptable_addr_t level_end = mm_level_end(begin, level);
696 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100697
Andrew Scull81e85092018-12-12 12:56:20 +0000698 /* Cap end so that we don't go over the current level max. */
699 if (end > level_end) {
700 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100701 }
702
Andrew Scull81e85092018-12-12 12:56:20 +0000703 /* Check that each entry is owned. */
704 while (begin < end) {
705 if (arch_mm_pte_is_table(*pte, level)) {
706 if (!mm_ptable_get_attrs_level(
707 mm_page_table_from_pa(
708 arch_mm_table_from_pte(*pte,
709 level)),
710 begin, end, level - 1, got_attrs, attrs)) {
711 return false;
712 }
713 got_attrs = true;
714 } else {
715 if (!got_attrs) {
716 *attrs = arch_mm_pte_attrs(*pte, level);
717 got_attrs = true;
718 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
719 return false;
720 }
721 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100722
Andrew Scull81e85092018-12-12 12:56:20 +0000723 begin = mm_start_of_next_block(begin, entry_size);
724 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100725 }
726
Andrew Scullc66a04d2018-12-07 13:41:56 +0000727 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000728 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100729}
730
731/**
Andrew Scull81e85092018-12-12 12:56:20 +0000732 * Gets the attributes applies to the given range of addresses in the stage-2
733 * table.
734 *
735 * The value returned in `attrs` is only valid if the function returns true.
736 *
737 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100738 */
Andrew Scull81e85092018-12-12 12:56:20 +0000739static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
740 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100741{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000742 int flags = 0;
743 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000744 uint8_t root_level = max_level + 1;
745 size_t root_table_size = mm_entry_size(root_level);
746 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000747 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000748 struct mm_page_table *table;
749 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100750
Andrew Scull81e85092018-12-12 12:56:20 +0000751 begin = mm_round_down_to_page(begin);
752 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100753
Andrew Scull81e85092018-12-12 12:56:20 +0000754 /* Fail if the addresses are out of range. */
755 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000756 return false;
757 }
758
Andrew Scull81e85092018-12-12 12:56:20 +0000759 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
760 while (begin < end) {
761 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
762 got_attrs, attrs)) {
763 return false;
764 }
765
766 got_attrs = true;
767 begin = mm_start_of_next_block(begin, root_table_size);
768 table++;
769 }
770
771 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100772}
773
Andrew Scullda3df7f2019-01-05 17:49:27 +0000774bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100775{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000776 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100777}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100778
Andrew Scullda3df7f2019-01-05 17:49:27 +0000779void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000780{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000781 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000782}
783
784/**
Andrew Scull80871322018-08-06 12:04:09 +0100785 * Updates a VM's page table such that the given physical address range is
786 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100787 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100788 */
Andrew Scull80871322018-08-06 12:04:09 +0100789bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000790 int mode, ipaddr_t *ipa, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100791{
Andrew Scullda241972019-01-05 18:17:48 +0000792 int flags = 0;
Andrew Scullda3df7f2019-01-05 17:49:27 +0000793 bool success = mm_ptable_identity_update(
794 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
795 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100796
797 if (success && ipa != NULL) {
798 *ipa = ipa_from_pa(begin);
799 }
800
801 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100802}
803
804/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000805 * Updates the VM's table such that the given physical address range has no
806 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100807 */
Andrew Scullda241972019-01-05 18:17:48 +0000808bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000809 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100810{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000811 return mm_ptable_identity_update(
812 t, begin, end,
813 arch_mm_mode_to_stage2_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
814 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000815 MM_FLAG_UNMAP, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100816}
817
818/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000819 * Unmaps the hypervisor pages from the given page table.
820 */
Andrew Scullda241972019-01-05 18:17:48 +0000821bool mm_vm_unmap_hypervisor(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000822{
823 /* TODO: If we add pages dynamically, they must be included here too. */
Andrew Scullda241972019-01-05 18:17:48 +0000824 return mm_vm_unmap(t, layout_text_begin(), layout_text_end(), ppool) &&
825 mm_vm_unmap(t, layout_rodata_begin(), layout_rodata_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000826 ppool) &&
Andrew Scullda241972019-01-05 18:17:48 +0000827 mm_vm_unmap(t, layout_data_begin(), layout_data_end(), ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000828}
829
830/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000831 * Write the given page table of a VM to the debug log.
832 */
833void mm_vm_dump(struct mm_ptable *t)
834{
835 mm_ptable_dump(t, 0);
836}
837
838/**
839 * Defragments the VM page table.
840 */
841void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
842{
843 mm_ptable_defrag(t, 0, ppool);
844}
845
846/**
Andrew Scull81e85092018-12-12 12:56:20 +0000847 * Gets the mode of the give range of intermediate physical addresses if they
848 * are mapped with the same mode.
849 *
850 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100851 */
Andrew Scull81e85092018-12-12 12:56:20 +0000852bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
853 int *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100854{
Andrew Scull81e85092018-12-12 12:56:20 +0000855 uint64_t attrs;
856 bool ret;
857
858 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
859 if (ret) {
860 *mode = arch_mm_stage2_attrs_to_mode(attrs);
861 }
862
863 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100864}
865
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100866static struct mm_stage1_locked mm_stage1_lock_unsafe(void)
867{
868 return (struct mm_stage1_locked){.ptable = &ptable};
869}
870
871struct mm_stage1_locked mm_lock_stage1(void)
872{
873 sl_lock(&ptable_lock);
874 return mm_stage1_lock_unsafe();
875}
876
877void mm_unlock_stage1(struct mm_stage1_locked *lock)
878{
Andrew Scull877ae4b2019-07-02 12:52:33 +0100879 CHECK(lock->ptable == &ptable);
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100880 sl_unlock(&ptable_lock);
881 lock->ptable = NULL;
882}
883
Andrew Scull80871322018-08-06 12:04:09 +0100884/**
Andrew Scull80871322018-08-06 12:04:09 +0100885 * Updates the hypervisor page table such that the given physical address range
886 * is mapped into the address space at the corresponding address range in the
887 * architecture-agnostic mode provided.
888 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100889void *mm_identity_map(struct mm_stage1_locked stage1_locked, paddr_t begin,
890 paddr_t end, int mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100891{
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100892 if (mm_ptable_identity_update(stage1_locked.ptable, begin, end,
Andrew Scullda241972019-01-05 18:17:48 +0000893 arch_mm_mode_to_stage1_attrs(mode),
894 MM_FLAG_STAGE1, ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100895 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100896 }
897
898 return NULL;
899}
900
901/**
902 * Updates the hypervisor table such that the given physical address range is
903 * not mapped in the address space.
904 */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100905bool mm_unmap(struct mm_stage1_locked stage1_locked, paddr_t begin, paddr_t end,
906 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100907{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000908 return mm_ptable_identity_update(
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100909 stage1_locked.ptable, begin, end,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000910 arch_mm_mode_to_stage1_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
911 MM_MODE_SHARED),
Andrew Scullda241972019-01-05 18:17:48 +0000912 MM_FLAG_STAGE1 | MM_FLAG_UNMAP, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100913}
914
915/**
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100916 * Defragments the hypervisor page table.
917 */
918void mm_defrag(struct mm_stage1_locked stage1_locked, struct mpool *ppool)
919{
920 mm_ptable_defrag(stage1_locked.ptable, MM_FLAG_STAGE1, ppool);
921}
922
923/**
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100924 * Initialises memory management for the hypervisor itself.
925 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000926bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100927{
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100928 /* Locking is not enabled yet so fake it, */
929 struct mm_stage1_locked stage1_locked = mm_stage1_lock_unsafe();
930
Andrew Walbranac5b2612019-07-12 16:44:19 +0100931 dlog("text: %#x - %#x\n", pa_addr(layout_text_begin()),
Andrew Scullfdd716e2018-12-20 05:37:31 +0000932 pa_addr(layout_text_end()));
Andrew Walbranac5b2612019-07-12 16:44:19 +0100933 dlog("rodata: %#x - %#x\n", pa_addr(layout_rodata_begin()),
Andrew Scullfdd716e2018-12-20 05:37:31 +0000934 pa_addr(layout_rodata_end()));
Andrew Walbranac5b2612019-07-12 16:44:19 +0100935 dlog("data: %#x - %#x\n", pa_addr(layout_data_begin()),
Andrew Scullfdd716e2018-12-20 05:37:31 +0000936 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100937
Andrew Scullda3df7f2019-01-05 17:49:27 +0000938 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Scullfdd716e2018-12-20 05:37:31 +0000939 dlog("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100940 return false;
941 }
942
Andrew Walbran48699362019-05-20 14:38:00 +0100943 /* Let console driver map pages for itself. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100944 plat_console_mm_init(stage1_locked, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100945
946 /* Map each section. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100947 mm_identity_map(stage1_locked, layout_text_begin(), layout_text_end(),
948 MM_MODE_X, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100949
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100950 mm_identity_map(stage1_locked, layout_rodata_begin(),
951 layout_rodata_end(), MM_MODE_R, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100952
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100953 mm_identity_map(stage1_locked, layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000954 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100955
Andrew Scull1ba470e2018-10-31 15:14:31 +0000956 return arch_mm_init(ptable.root, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100957}
958
959bool mm_cpu_init(void)
960{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000961 return arch_mm_init(ptable.root, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100962}