blob: 28f542837b6d62ee2c8f74442d1b51a4a6887f50 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
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
Andrew Scull80871322018-08-06 12:04:09 +010019#include <assert.h>
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010020#include <stdatomic.h>
21#include <stdint.h>
22
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010024#include "hf/layout.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010025
Andrew Walbran2400ed22018-09-27 14:45:58 +010026/**
27 * This file has functions for managing the level 1 and 2 page tables used by
28 * Hafnium. There is a level 1 mapping used by Hafnium itself to access memory,
29 * and then a level 2 mapping per VM. The design assumes that all page tables
30 * contain only 1-1 mappings, aligned on the block boundaries.
31 */
32
Andrew Scull80871322018-08-06 12:04:09 +010033/* The type of addresses stored in the page table. */
34typedef uintvaddr_t ptable_addr_t;
35
Wedson Almeida Filhob2c159e2018-10-25 13:27:47 +010036/*
37 * For stage 2, the input is an intermediate physical addresses rather than a
38 * virtual address so:
39 */
Andrew Scull80871322018-08-06 12:04:09 +010040static_assert(
41 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
42 "Currently, the same code manages the stage 1 and stage 2 page tables "
43 "which only works if the virtual and intermediate physical addresses "
44 "are the same size. It looks like that assumption might not be holding "
45 "so we need to check that everything is going to be ok.");
46
Andrew Scull4f170f52018-07-19 12:58:20 +010047/* Keep macro alignment */
48/* clang-format off */
49
Andrew Scullda3df7f2019-01-05 17:49:27 +000050#define MM_FLAG_COMMIT 0x01
51#define MM_FLAG_UNMAP 0x02
52#define MM_FLAG_NOINVALIDATE 0x04
53#define MM_FLAG_STAGE1 0x08
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;
58
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010059/**
Andrew Scull4e5f8142018-10-12 14:37:19 +010060 * Get the page table from the physical address.
Andrew Walbran2400ed22018-09-27 14:45:58 +010061 */
Andrew Scull4e5f8142018-10-12 14:37:19 +010062static struct mm_page_table *mm_page_table_from_pa(paddr_t pa)
Andrew Walbran2400ed22018-09-27 14:45:58 +010063{
64 return ptr_from_va(va_from_pa(pa));
65}
66
67/**
Andrew Scull80871322018-08-06 12:04:09 +010068 * Rounds an address down to a page boundary.
69 */
70static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
71{
72 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
73}
74
75/**
76 * Rounds an address up to a page boundary.
77 */
78static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
79{
80 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
81}
82
83/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010084 * Calculates the size of the address space represented by a page table entry at
85 * the given level.
86 */
Andrew Sculle9827712018-10-19 14:54:20 +010087static size_t mm_entry_size(uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010088{
Andrew Scull78d6fd92018-09-06 15:08:36 +010089 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010090}
91
92/**
Andrew Scullcae45572018-12-13 15:46:30 +000093 * Gets the address of the start of the next block of the given size. The size
94 * must be a power of two.
95 */
96static ptable_addr_t mm_start_of_next_block(ptable_addr_t addr,
97 size_t block_size)
98{
99 return (addr + block_size) & ~(block_size - 1);
100}
101
102/**
103 * Gets the physical address of the start of the next block of the given size.
104 * The size must be a power of two.
105 */
106static paddr_t mm_pa_start_of_next_block(paddr_t pa, size_t block_size)
107{
108 return pa_init((pa_addr(pa) + block_size) & ~(block_size - 1));
109}
110
111/**
Andrew Scull80871322018-08-06 12:04:09 +0100112 * For a given address, calculates the maximum (plus one) address that can be
113 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100114 */
Andrew Sculle9827712018-10-19 14:54:20 +0100115static ptable_addr_t mm_level_end(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100116{
117 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000118
Andrew Scull80871322018-08-06 12:04:09 +0100119 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100120}
121
122/**
Andrew Scull80871322018-08-06 12:04:09 +0100123 * For a given address, calculates the index at which its entry is stored in a
124 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100125 */
Andrew Sculle9827712018-10-19 14:54:20 +0100126static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100127{
Andrew Scull80871322018-08-06 12:04:09 +0100128 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000129
Andrew Scull78d6fd92018-09-06 15:08:36 +0100130 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100131}
132
133/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000134 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100135 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000136static struct mm_page_table *mm_alloc_page_tables(size_t count,
137 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100138{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000139 if (count == 1) {
140 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100141 }
142
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000143 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100144}
145
146/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000147 * Returns the maximum level in the page table given the flags.
148 */
149static uint8_t mm_max_level(int flags)
150{
151 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_max_level()
152 : arch_mm_stage2_max_level();
153}
154
155/**
156 * Returns the number of root-level tables given the flags.
157 */
158static uint8_t mm_root_table_count(int flags)
159{
160 return (flags & MM_FLAG_STAGE1) ? arch_mm_stage1_root_table_count()
161 : arch_mm_stage2_root_table_count();
162}
163
164/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000165 * Invalidates the TLB for the given address range.
166 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000167static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end, int flags)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000168{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000169 if (flags & MM_FLAG_STAGE1) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000170 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
171 } else {
172 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
173 }
174}
175
176/**
177 * Frees all page-table-related memory associated with the given pte at the
178 * given level, including any subtables recursively.
179 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000180static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000181{
182 struct mm_page_table *table;
183 uint64_t i;
184
185 if (!arch_mm_pte_is_table(pte, level)) {
186 return;
187 }
188
189 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000190 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000191 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000192 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000193 }
194
195 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000196 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000197}
198
199/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000200 * Initialises the given page table.
201 */
202static bool mm_ptable_init(struct mm_ptable *t, int flags, struct mpool *ppool)
203{
204 uint8_t i;
205 size_t j;
206 struct mm_page_table *tables;
207 uint8_t root_table_count = mm_root_table_count(flags);
208
209 tables = mm_alloc_page_tables(root_table_count, ppool);
210 if (tables == NULL) {
211 return false;
212 }
213
214 for (i = 0; i < root_table_count; i++) {
215 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
216 tables[i].entries[j] =
217 arch_mm_absent_pte(mm_max_level(flags));
218 }
219 }
220
221 /*
222 * TODO: halloc could return a virtual or physical address if mm not
223 * enabled?
224 */
225 t->root = pa_init((uintpaddr_t)tables);
226
227 return true;
228}
229
230/**
231 * Frees all memory associated with the give page table.
232 */
233static void mm_ptable_fini(struct mm_ptable *t, int flags, struct mpool *ppool)
234{
235 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
236 uint8_t level = mm_max_level(flags);
237 uint8_t root_table_count = mm_root_table_count(flags);
238 uint8_t i;
239 uint64_t j;
240
241 for (i = 0; i < root_table_count; ++i) {
242 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
243 mm_free_page_pte(tables[i].entries[j], level, ppool);
244 }
245 }
246
247 mpool_add_chunk(ppool, tables,
248 sizeof(struct mm_page_table) * root_table_count);
249}
250
251/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000252 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000253 * are valid, it performs a break-before-make sequence where it first writes an
254 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
255 * This is to prevent cases where CPUs have different 'valid' values in their
256 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000257 */
258static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000259 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000260{
261 pte_t v = *pte;
262
263 /*
264 * We need to do the break-before-make sequence if both values are
265 * present, and if it hasn't been inhibited by the NOBBM flag.
266 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000267 if (!(flags & MM_FLAG_NOINVALIDATE) && arch_mm_pte_is_valid(v, level) &&
Andrew Scullc66a04d2018-12-07 13:41:56 +0000268 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000269 *pte = arch_mm_absent_pte(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000270 mm_invalidate_tlb(begin, begin + mm_entry_size(level), flags);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000271 }
272
273 /* Assign the new pte. */
274 *pte = new_pte;
275
276 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000277 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000278}
279
280/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100281 * Populates the provided page table entry with a reference to another table if
282 * needed, that is, if it does not yet point to another table.
283 *
284 * Returns a pointer to the table the entry now points to.
285 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000286static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
287 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000288 int flags,
289 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100290{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100291 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100292 pte_t v = *pte;
293 pte_t new_pte;
294 size_t i;
295 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100296 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100297
298 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100299 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000300 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100301 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302
303 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000304 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100305 if (ntable == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306 dlog("Failed to allocate memory for page table\n");
307 return NULL;
308 }
309
310 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100311 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100312 inc = mm_entry_size(level_below);
313 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000314 arch_mm_block_from_pte(v, level),
315 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100316 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100317 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100318 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100319 }
320
321 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100322 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
323 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100324 new_pte += inc;
325 }
326
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000327 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100328 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000329
330 /* Replace the pte entry, doing a break-before-make if needed. */
331 mm_replace_entry(begin, pte,
332 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000333 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100334
335 return ntable;
336}
337
338/**
Andrew Walbran6324fc92018-10-03 11:46:43 +0100339 * Returns whether all entries in this table are absent.
340 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000341static bool mm_page_table_is_empty(struct mm_page_table *table, uint8_t level)
Andrew Walbran6324fc92018-10-03 11:46:43 +0100342{
343 uint64_t i;
344
Andrew Scull4e5f8142018-10-12 14:37:19 +0100345 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
346 if (arch_mm_pte_is_present(table->entries[i], level)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100347 return false;
348 }
349 }
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000350
Andrew Walbran6324fc92018-10-03 11:46:43 +0100351 return true;
352}
353
354/**
Andrew Scull80871322018-08-06 12:04:09 +0100355 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100356 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000357 * MM_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100358 *
359 * This function calls itself recursively if it needs to update additional
360 * levels, but the recursion is bound by the maximum number of levels in a page
361 * table.
362 */
Andrew Scull80871322018-08-06 12:04:09 +0100363static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100364 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000365 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100366{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100367 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100368 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100369 size_t entry_size = mm_entry_size(level);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000370 bool commit = flags & MM_FLAG_COMMIT;
371 bool unmap = flags & MM_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100372
Andrew Scull265ada92018-07-30 15:19:01 +0100373 /* Cap end so that we don't go over the current level max. */
374 if (end > level_end) {
375 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100376 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100377
378 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100379 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100380 if (unmap ? !arch_mm_pte_is_present(*pte, level)
381 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000382 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100383 /*
384 * If the entry is already mapped with the right
385 * attributes, or already absent in the case of
386 * unmapping, no need to do anything; carry on to the
387 * next entry.
388 */
389 } else if ((end - begin) >= entry_size &&
390 (unmap || arch_mm_is_block_allowed(level)) &&
391 (begin & (entry_size - 1)) == 0) {
392 /*
393 * If the entire entry is within the region we want to
394 * map, map/unmap the whole entry.
395 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100396 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000397 pte_t new_pte =
398 unmap ? arch_mm_absent_pte(level)
399 : arch_mm_block_pte(level, pa,
400 attrs);
401 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000402 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100403 }
404 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100405 /*
406 * If the entry is already a subtable get it; otherwise
407 * replace it with an equivalent subtable and get that.
408 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000409 struct mm_page_table *nt = mm_populate_table_pte(
410 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100411 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100412 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100413 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100414
Andrew Walbran6324fc92018-10-03 11:46:43 +0100415 /*
416 * Recurse to map/unmap the appropriate entries within
417 * the subtable.
418 */
Andrew Scull80871322018-08-06 12:04:09 +0100419 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000420 flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100421 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100422 }
Andrew Walbran6324fc92018-10-03 11:46:43 +0100423
424 /*
425 * If the subtable is now empty, replace it with an
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000426 * absent entry at this level. We never need to do
427 * break-before-makes here because we are assigning
428 * an absent value.
Andrew Walbran6324fc92018-10-03 11:46:43 +0100429 */
430 if (commit && unmap &&
Andrew Scull1ba470e2018-10-31 15:14:31 +0000431 mm_page_table_is_empty(nt, level - 1)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100432 pte_t v = *pte;
433 *pte = arch_mm_absent_pte(level);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000434 mm_free_page_pte(v, level, ppool);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100435 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100436 }
437
Andrew Scullcae45572018-12-13 15:46:30 +0000438 begin = mm_start_of_next_block(begin, entry_size);
439 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100440 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100441 }
442
443 return true;
444}
445
446/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000447 * Updates the page table from the root to map the given address range to a
448 * physical range using the provided (architecture-specific) attributes. Or if
Andrew Scullda3df7f2019-01-05 17:49:27 +0000449 * MM_FLAG_UNMAP is set, unmap the given range instead.
Andrew Scull1ba470e2018-10-31 15:14:31 +0000450 */
451static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
452 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000453 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000454{
455 size_t root_table_size = mm_entry_size(root_level);
456 struct mm_page_table *table =
457 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
458
459 while (begin < end) {
460 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000461 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000462 return false;
463 }
Andrew Scullcae45572018-12-13 15:46:30 +0000464 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000465 table++;
466 }
467
468 return true;
469}
470
471/**
Andrew Scull80871322018-08-06 12:04:09 +0100472 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000473 * or not mapped into the address space with the architecture-agnostic mode
474 * provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100475 */
Andrew Sculla6da8342018-11-01 12:29:49 +0000476static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000477 paddr_t pa_end, uint64_t attrs, int flags,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000478 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100479{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000480 uint8_t root_level = mm_max_level(flags) + 1;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000481 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000482 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000483 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
484 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100485
Andrew Scull1ba470e2018-10-31 15:14:31 +0000486 /*
487 * TODO: replace with assertions that the max level will be greater than
488 * 0 and less than 255 so wrapping will not be a problem and will not
489 * lead to subsequent overflows.
490 */
491 if (root_level == 0 || root_level == 1) {
492 return false;
493 }
494
495 /* Cap end to stay within the bounds of the page table. */
496 if (end > ptable_end) {
497 end = ptable_end;
498 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100499
500 /*
501 * Do it in two steps to prevent leaving the table in a halfway updated
502 * state. In such a two-step implementation, the table may be left with
503 * extra internal tables, but no different mapping on failure.
504 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000505 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool) ||
Andrew Scull1ba470e2018-10-31 15:14:31 +0000506 !mm_map_root(t, begin, end, attrs, root_level,
Andrew Scullda3df7f2019-01-05 17:49:27 +0000507 flags | MM_FLAG_COMMIT, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100508 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100509 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100510
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100511 /* Invalidate the tlb. */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000512 if (!(flags & MM_FLAG_NOINVALIDATE)) {
513 mm_invalidate_tlb(begin, end, flags);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100514 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100515
516 return true;
517}
518
519/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100520 * Writes the given table to the debug log, calling itself recursively to
521 * write sub-tables.
522 */
Andrew Sculle9827712018-10-19 14:54:20 +0100523static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100524 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100525{
526 uint64_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000527
Andrew Scull4e5f8142018-10-12 14:37:19 +0100528 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
529 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100530 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100531 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100532
Andrew Scull4e5f8142018-10-12 14:37:19 +0100533 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
534 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100535
Andrew Scull4e5f8142018-10-12 14:37:19 +0100536 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100537 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100538 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000539 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100540 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100541 }
542 }
543}
544
545/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000546 * Writes the given table to the debug log.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100547 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000548static void mm_ptable_dump(struct mm_ptable *t, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100549{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000550 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000551 uint8_t max_level = mm_max_level(flags);
552 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000553 uint8_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000554
Andrew Scull1ba470e2018-10-31 15:14:31 +0000555 for (i = 0; i < root_table_count; ++i) {
556 mm_dump_table_recursive(&tables[i], max_level, max_level);
557 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100558}
559
560/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000561 * Given the table PTE entries all have identical attributes, returns the single
562 * entry with which it can be replaced. Note that the table PTE will no longer
563 * be valid after calling this function as the table may have been freed.
Andrew Scullb6b9b562018-12-21 14:41:35 +0000564 *
565 * If the table is freed, the memory is freed directly rather than calling
566 * `mm_free_page_pte()` as it is known to not have subtables.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100567 */
Andrew Scullb6b9b562018-12-21 14:41:35 +0000568static pte_t mm_merge_table_pte(pte_t table_pte, uint8_t level,
569 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100570{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100571 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100572 uint64_t block_attrs;
573 uint64_t table_attrs;
574 uint64_t combined_attrs;
575 paddr_t block_address;
576
Andrew Scullb6b9b562018-12-21 14:41:35 +0000577 table = mm_page_table_from_pa(arch_mm_table_from_pte(table_pte, level));
578
579 if (!arch_mm_pte_is_present(table->entries[0], level - 1)) {
580 /* Free the table and return an absent entry. */
581 mpool_free(ppool, table);
582 return arch_mm_absent_pte(level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100583 }
584
Andrew Scullb6b9b562018-12-21 14:41:35 +0000585 /* Might not be possible to merge the table into a single block. */
586 if (!arch_mm_is_block_allowed(level)) {
587 return table_pte;
588 }
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000589
Andrew Scullb6b9b562018-12-21 14:41:35 +0000590 /* Replace table with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000591 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
Andrew Scullb6b9b562018-12-21 14:41:35 +0000592 table_attrs = arch_mm_pte_attrs(table_pte, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100593 combined_attrs =
594 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000595 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000596
Andrew Scullb6b9b562018-12-21 14:41:35 +0000597 /* Free the table and return a block. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000598 mpool_free(ppool, table);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100599 return arch_mm_block_pte(level, block_address, combined_attrs);
600}
601
602/**
Wedson Almeida Filhoac8ad012018-12-17 18:00:29 +0000603 * Defragments the given PTE by recursively replacing any tables with blocks or
Andrew Scullb6b9b562018-12-21 14:41:35 +0000604 * absent entries where possible.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100605 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000606static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
607 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100608{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100609 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100610 uint64_t i;
611 uint64_t attrs;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100612
613 if (!arch_mm_pte_is_table(entry, level)) {
614 return entry;
615 }
616
Andrew Scull3681b8d2018-12-12 14:22:59 +0000617 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100618
619 /*
620 * Check if all entries are blocks with the same flags or are all
Andrew Scullb6b9b562018-12-21 14:41:35 +0000621 * absent. It assumes addresses are contiguous due to identity mapping.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100622 */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000623 attrs = arch_mm_pte_attrs(table->entries[0], level);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100624 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000625 /* First try to defrag the entry, in case it is a subtable. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000626 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
627 level - 1, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100628
Andrew Walbran2400ed22018-09-27 14:45:58 +0100629 /*
Andrew Scullb6b9b562018-12-21 14:41:35 +0000630 * If the entry isn't a block or has different attributes then
631 * it isn't possible to defragment it.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100632 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100633 if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
Andrew Scull3681b8d2018-12-12 14:22:59 +0000634 arch_mm_pte_attrs(table->entries[i], level) != attrs) {
Andrew Scullb6b9b562018-12-21 14:41:35 +0000635 return entry;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100636 }
637 }
Andrew Scullb6b9b562018-12-21 14:41:35 +0000638
639 return mm_merge_table_pte(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100640}
641
642/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100643 * Defragments the given page table by converting page table references to
644 * blocks whenever possible.
645 */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000646static void mm_ptable_defrag(struct mm_ptable *t, int flags,
647 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100648{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000649 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Scullda3df7f2019-01-05 17:49:27 +0000650 uint8_t level = mm_max_level(flags);
651 uint8_t root_table_count = mm_root_table_count(flags);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000652 uint8_t i;
653 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100654
655 /*
656 * Loop through each entry in the table. If it points to another table,
657 * check if that table can be replaced by a block or an absent entry.
658 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000659 for (i = 0; i < root_table_count; ++i) {
660 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
661 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000662 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000663 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100664 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100665}
666
667/**
Andrew Scull81e85092018-12-12 12:56:20 +0000668 * Gets the attributes applied to the given range of stage-2 addresses at the
669 * given level.
670 *
671 * The `got_attrs` argument is initially passed as false until `attrs` contains
672 * attributes of the memory region at which point it is passed as true.
673 *
674 * The value returned in `attrs` is only valid if the function returns true.
675 *
676 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100677 */
Andrew Scull81e85092018-12-12 12:56:20 +0000678static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
679 ptable_addr_t begin, ptable_addr_t end,
680 uint8_t level, bool got_attrs,
681 uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100682{
Andrew Scull81e85092018-12-12 12:56:20 +0000683 pte_t *pte = &table->entries[mm_index(begin, level)];
684 ptable_addr_t level_end = mm_level_end(begin, level);
685 size_t entry_size = mm_entry_size(level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100686
Andrew Scull81e85092018-12-12 12:56:20 +0000687 /* Cap end so that we don't go over the current level max. */
688 if (end > level_end) {
689 end = level_end;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100690 }
691
Andrew Scull81e85092018-12-12 12:56:20 +0000692 /* Check that each entry is owned. */
693 while (begin < end) {
694 if (arch_mm_pte_is_table(*pte, level)) {
695 if (!mm_ptable_get_attrs_level(
696 mm_page_table_from_pa(
697 arch_mm_table_from_pte(*pte,
698 level)),
699 begin, end, level - 1, got_attrs, attrs)) {
700 return false;
701 }
702 got_attrs = true;
703 } else {
704 if (!got_attrs) {
705 *attrs = arch_mm_pte_attrs(*pte, level);
706 got_attrs = true;
707 } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
708 return false;
709 }
710 }
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100711
Andrew Scull81e85092018-12-12 12:56:20 +0000712 begin = mm_start_of_next_block(begin, entry_size);
713 pte++;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100714 }
715
Andrew Scullc66a04d2018-12-07 13:41:56 +0000716 /* The entry is a valid block. */
Andrew Scull81e85092018-12-12 12:56:20 +0000717 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100718}
719
720/**
Andrew Scull81e85092018-12-12 12:56:20 +0000721 * Gets the attributes applies to the given range of addresses in the stage-2
722 * table.
723 *
724 * The value returned in `attrs` is only valid if the function returns true.
725 *
726 * Returns true if the whole range has the same attributes and false otherwise.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100727 */
Andrew Scull81e85092018-12-12 12:56:20 +0000728static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
729 ptable_addr_t end, uint64_t *attrs)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100730{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000731 int flags = 0;
732 uint8_t max_level = mm_max_level(flags);
Andrew Scull81e85092018-12-12 12:56:20 +0000733 uint8_t root_level = max_level + 1;
734 size_t root_table_size = mm_entry_size(root_level);
735 ptable_addr_t ptable_end =
Andrew Scullda3df7f2019-01-05 17:49:27 +0000736 mm_root_table_count(flags) * mm_entry_size(root_level);
Andrew Scull81e85092018-12-12 12:56:20 +0000737 struct mm_page_table *table;
738 bool got_attrs = false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100739
Andrew Scull81e85092018-12-12 12:56:20 +0000740 begin = mm_round_down_to_page(begin);
741 end = mm_round_up_to_page(end);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100742
Andrew Scull81e85092018-12-12 12:56:20 +0000743 /* Fail if the addresses are out of range. */
744 if (end > ptable_end) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000745 return false;
746 }
747
Andrew Scull81e85092018-12-12 12:56:20 +0000748 table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
749 while (begin < end) {
750 if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
751 got_attrs, attrs)) {
752 return false;
753 }
754
755 got_attrs = true;
756 begin = mm_start_of_next_block(begin, root_table_size);
757 table++;
758 }
759
760 return got_attrs;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100761}
762
Andrew Scullda3df7f2019-01-05 17:49:27 +0000763bool mm_vm_init(struct mm_ptable *t, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100764{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000765 return mm_ptable_init(t, 0, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100766}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100767
Andrew Scullda3df7f2019-01-05 17:49:27 +0000768void mm_vm_fini(struct mm_ptable *t, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000769{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000770 mm_ptable_fini(t, 0, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000771}
772
773/**
Andrew Scull80871322018-08-06 12:04:09 +0100774 * Updates a VM's page table such that the given physical address range is
775 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100776 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100777 */
Andrew Scull80871322018-08-06 12:04:09 +0100778bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000779 int mode, ipaddr_t *ipa, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100780{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000781 int flags = (mode & MM_MODE_NOINVALIDATE ? MM_FLAG_NOINVALIDATE : 0);
782 bool success = mm_ptable_identity_update(
783 t, begin, end, arch_mm_mode_to_stage2_attrs(mode), flags,
784 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100785
786 if (success && ipa != NULL) {
787 *ipa = ipa_from_pa(begin);
788 }
789
790 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100791}
792
793/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000794 * Updates the VM's table such that the given physical address range has no
795 * connection to the VM.
Andrew Scull80871322018-08-06 12:04:09 +0100796 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000797bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end, int mode,
798 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100799{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000800 int flags = (mode & MM_MODE_NOINVALIDATE ? MM_FLAG_NOINVALIDATE : 0) |
801 MM_FLAG_UNMAP;
802 return mm_ptable_identity_update(
803 t, begin, end,
804 arch_mm_mode_to_stage2_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
805 MM_MODE_SHARED),
806 flags, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100807}
808
809/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000810 * Unmaps the hypervisor pages from the given page table.
811 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000812bool mm_vm_unmap_hypervisor(struct mm_ptable *t, int mode, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000813{
814 /* TODO: If we add pages dynamically, they must be included here too. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000815 return mm_vm_unmap(t, layout_text_begin(), layout_text_end(), mode,
816 ppool) &&
817 mm_vm_unmap(t, layout_rodata_begin(), layout_rodata_end(), mode,
818 ppool) &&
819 mm_vm_unmap(t, layout_data_begin(), layout_data_end(), mode,
820 ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000821}
822
823/**
Andrew Scullda3df7f2019-01-05 17:49:27 +0000824 * Write the given page table of a VM to the debug log.
825 */
826void mm_vm_dump(struct mm_ptable *t)
827{
828 mm_ptable_dump(t, 0);
829}
830
831/**
832 * Defragments the VM page table.
833 */
834void mm_vm_defrag(struct mm_ptable *t, struct mpool *ppool)
835{
836 mm_ptable_defrag(t, 0, ppool);
837}
838
839/**
Andrew Scull81e85092018-12-12 12:56:20 +0000840 * Gets the mode of the give range of intermediate physical addresses if they
841 * are mapped with the same mode.
842 *
843 * Returns true if the range is mapped with the same mode and false otherwise.
Andrew Scull80871322018-08-06 12:04:09 +0100844 */
Andrew Scull81e85092018-12-12 12:56:20 +0000845bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
846 int *mode)
Andrew Scull80871322018-08-06 12:04:09 +0100847{
Andrew Scull81e85092018-12-12 12:56:20 +0000848 uint64_t attrs;
849 bool ret;
850
851 ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
852 if (ret) {
853 *mode = arch_mm_stage2_attrs_to_mode(attrs);
854 }
855
856 return ret;
Andrew Scull80871322018-08-06 12:04:09 +0100857}
858
859/**
Andrew Scull80871322018-08-06 12:04:09 +0100860 * Updates the hypervisor page table such that the given physical address range
861 * is mapped into the address space at the corresponding address range in the
862 * architecture-agnostic mode provided.
863 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000864void *mm_identity_map(paddr_t begin, paddr_t end, int mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100865{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000866 int flags = (mode & MM_MODE_NOINVALIDATE ? MM_FLAG_NOINVALIDATE : 0) |
867 MM_FLAG_STAGE1;
868
869 if (mm_ptable_identity_update(&ptable, begin, end,
870 arch_mm_mode_to_stage1_attrs(mode), flags,
871 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100872 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100873 }
874
875 return NULL;
876}
877
878/**
879 * Updates the hypervisor table such that the given physical address range is
880 * not mapped in the address space.
881 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000882bool mm_unmap(paddr_t begin, paddr_t end, int mode, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100883{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000884 int flags = (mode & MM_MODE_NOINVALIDATE ? MM_FLAG_NOINVALIDATE : 0) |
885 MM_FLAG_STAGE1 | MM_FLAG_UNMAP;
886 return mm_ptable_identity_update(
887 &ptable, begin, end,
888 arch_mm_mode_to_stage1_attrs(MM_MODE_UNOWNED | MM_MODE_INVALID |
889 MM_MODE_SHARED),
890 flags, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100891}
892
893/**
894 * Initialises memory management for the hypervisor itself.
895 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000896bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100897{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000898 dlog("text: 0x%x - 0x%x\n", pa_addr(layout_text_begin()),
899 pa_addr(layout_text_end()));
900 dlog("rodata: 0x%x - 0x%x\n", pa_addr(layout_rodata_begin()),
901 pa_addr(layout_rodata_end()));
902 dlog("data: 0x%x - 0x%x\n", pa_addr(layout_data_begin()),
903 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100904
Andrew Scullda3df7f2019-01-05 17:49:27 +0000905 if (!mm_ptable_init(&ptable, MM_FLAG_STAGE1, ppool)) {
Andrew Scullfdd716e2018-12-20 05:37:31 +0000906 dlog("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100907 return false;
908 }
909
910 /* Map page for uart. */
911 /* TODO: We may not want to map this. */
Andrew Scullda3df7f2019-01-05 17:49:27 +0000912 mm_identity_map(pa_init(PL011_BASE),
913 pa_add(pa_init(PL011_BASE), PAGE_SIZE),
914 MM_MODE_R | MM_MODE_W | MM_MODE_D, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100915
916 /* Map each section. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000917 mm_identity_map(layout_text_begin(), layout_text_end(), MM_MODE_X,
918 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100919
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000920 mm_identity_map(layout_rodata_begin(), layout_rodata_end(), MM_MODE_R,
921 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100922
Andrew Scull5991ec92018-10-08 14:55:02 +0100923 mm_identity_map(layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000924 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100925
Andrew Scull1ba470e2018-10-31 15:14:31 +0000926 return arch_mm_init(ptable.root, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100927}
928
929bool mm_cpu_init(void)
930{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000931 return arch_mm_init(ptable.root, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100932}
933
934/**
935 * Defragments the hypervisor page table.
936 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000937void mm_defrag(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100938{
Andrew Scullda3df7f2019-01-05 17:49:27 +0000939 mm_ptable_defrag(&ptable, MM_FLAG_STAGE1, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100940}