blob: 16a5a939aee8e93eaa9fb02322c171643bb47ca9 [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
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +000050#define MAP_FLAG_COMMIT 0x01
51#define MAP_FLAG_UNMAP 0x02
52#define MAP_FLAG_NOBBM 0x04
53#define MAP_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;
Andrew Scull80871322018-08-06 12:04:09 +0100118 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100119}
120
121/**
Andrew Scull80871322018-08-06 12:04:09 +0100122 * For a given address, calculates the index at which its entry is stored in a
123 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100124 */
Andrew Sculle9827712018-10-19 14:54:20 +0100125static size_t mm_index(ptable_addr_t addr, uint8_t level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100126{
Andrew Scull80871322018-08-06 12:04:09 +0100127 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Andrew Scull78d6fd92018-09-06 15:08:36 +0100128 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100129}
130
131/**
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000132 * Allocates a new page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +0100133 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000134static struct mm_page_table *mm_alloc_page_tables(size_t count,
135 struct mpool *ppool)
Andrew Scull4e5f8142018-10-12 14:37:19 +0100136{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000137 if (count == 1) {
138 return mpool_alloc(ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100139 }
140
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000141 return mpool_alloc_contiguous(ppool, count, count);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100142}
143
144/**
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000145 * Invalidates the TLB for the given address range.
146 */
147static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end,
148 bool stage1)
149{
150 if (stage1) {
151 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
152 } else {
153 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
154 }
155}
156
157/**
158 * Frees all page-table-related memory associated with the given pte at the
159 * given level, including any subtables recursively.
160 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000161static void mm_free_page_pte(pte_t pte, uint8_t level, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000162{
163 struct mm_page_table *table;
164 uint64_t i;
165
166 if (!arch_mm_pte_is_table(pte, level)) {
167 return;
168 }
169
170 /* Recursively free any subtables. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000171 table = mm_page_table_from_pa(arch_mm_table_from_pte(pte, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000172 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000173 mm_free_page_pte(table->entries[i], level - 1, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000174 }
175
176 /* Free the table itself. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000177 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000178}
179
180/**
181 * Replaces a page table entry with the given value. If both old and new values
Andrew Scullc66a04d2018-12-07 13:41:56 +0000182 * are valid, it performs a break-before-make sequence where it first writes an
183 * invalid value to the PTE, flushes the TLB, then writes the actual new value.
184 * This is to prevent cases where CPUs have different 'valid' values in their
185 * TLBs, which may result in issues for example in cache coherency.
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000186 */
187static void mm_replace_entry(ptable_addr_t begin, pte_t *pte, pte_t new_pte,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000188 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000189{
190 pte_t v = *pte;
191
192 /*
193 * We need to do the break-before-make sequence if both values are
194 * present, and if it hasn't been inhibited by the NOBBM flag.
195 */
Andrew Scullc66a04d2018-12-07 13:41:56 +0000196 if (!(flags & MAP_FLAG_NOBBM) && arch_mm_pte_is_valid(v, level) &&
197 arch_mm_pte_is_valid(new_pte, level)) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000198 *pte = arch_mm_absent_pte(level);
199 mm_invalidate_tlb(begin, begin + mm_entry_size(level),
200 flags & MAP_FLAG_STAGE1);
201 }
202
203 /* Assign the new pte. */
204 *pte = new_pte;
205
206 /* Free pages that aren't in use anymore. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000207 mm_free_page_pte(v, level, ppool);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000208}
209
210/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100211 * Populates the provided page table entry with a reference to another table if
212 * needed, that is, if it does not yet point to another table.
213 *
214 * Returns a pointer to the table the entry now points to.
215 */
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000216static struct mm_page_table *mm_populate_table_pte(ptable_addr_t begin,
217 pte_t *pte, uint8_t level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000218 int flags,
219 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100220{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100221 struct mm_page_table *ntable;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100222 pte_t v = *pte;
223 pte_t new_pte;
224 size_t i;
225 size_t inc;
Andrew Sculle9827712018-10-19 14:54:20 +0100226 uint8_t level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100227
228 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100229 if (arch_mm_pte_is_table(v, level)) {
Andrew Scull3681b8d2018-12-12 14:22:59 +0000230 return mm_page_table_from_pa(arch_mm_table_from_pte(v, level));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100231 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100232
233 /* Allocate a new table. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000234 ntable = mm_alloc_page_tables(1, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100235 if (ntable == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100236 dlog("Failed to allocate memory for page table\n");
237 return NULL;
238 }
239
240 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100241 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100242 inc = mm_entry_size(level_below);
243 new_pte = arch_mm_block_pte(level_below,
Andrew Scull3681b8d2018-12-12 14:22:59 +0000244 arch_mm_block_from_pte(v, level),
245 arch_mm_pte_attrs(v, level));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100246 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100247 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100248 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100249 }
250
251 /* Initialise entries in the new table. */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100252 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
253 ntable->entries[i] = new_pte;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100254 new_pte += inc;
255 }
256
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000257 /* Ensure initialisation is visible before updating the pte. */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100258 atomic_thread_fence(memory_order_release);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000259
260 /* Replace the pte entry, doing a break-before-make if needed. */
261 mm_replace_entry(begin, pte,
262 arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000263 level, flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100264
265 return ntable;
266}
267
268/**
Andrew Walbran6324fc92018-10-03 11:46:43 +0100269 * Returns whether all entries in this table are absent.
270 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000271static bool mm_page_table_is_empty(struct mm_page_table *table, uint8_t level)
Andrew Walbran6324fc92018-10-03 11:46:43 +0100272{
273 uint64_t i;
274
Andrew Scull4e5f8142018-10-12 14:37:19 +0100275 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
276 if (arch_mm_pte_is_present(table->entries[i], level)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100277 return false;
278 }
279 }
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000280
Andrew Walbran6324fc92018-10-03 11:46:43 +0100281 return true;
282}
283
284/**
Andrew Scull80871322018-08-06 12:04:09 +0100285 * Updates the page table at the given level to map the given address range to a
Andrew Walbran6324fc92018-10-03 11:46:43 +0100286 * physical range using the provided (architecture-specific) attributes. Or if
287 * MAP_FLAG_UNMAP is set, unmap the given range instead.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100288 *
289 * This function calls itself recursively if it needs to update additional
290 * levels, but the recursion is bound by the maximum number of levels in a page
291 * table.
292 */
Andrew Scull80871322018-08-06 12:04:09 +0100293static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Sculle9827712018-10-19 14:54:20 +0100294 uint64_t attrs, struct mm_page_table *table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000295 uint8_t level, int flags, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100296{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100297 pte_t *pte = &table->entries[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100298 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100299 size_t entry_size = mm_entry_size(level);
300 bool commit = flags & MAP_FLAG_COMMIT;
Andrew Walbran6324fc92018-10-03 11:46:43 +0100301 bool unmap = flags & MAP_FLAG_UNMAP;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302
Andrew Scull265ada92018-07-30 15:19:01 +0100303 /* Cap end so that we don't go over the current level max. */
304 if (end > level_end) {
305 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100306 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100307
308 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100309 while (begin < end) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100310 if (unmap ? !arch_mm_pte_is_present(*pte, level)
311 : arch_mm_pte_is_block(*pte, level) &&
Andrew Scull3681b8d2018-12-12 14:22:59 +0000312 arch_mm_pte_attrs(*pte, level) == attrs) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100313 /*
314 * If the entry is already mapped with the right
315 * attributes, or already absent in the case of
316 * unmapping, no need to do anything; carry on to the
317 * next entry.
318 */
319 } else if ((end - begin) >= entry_size &&
320 (unmap || arch_mm_is_block_allowed(level)) &&
321 (begin & (entry_size - 1)) == 0) {
322 /*
323 * If the entire entry is within the region we want to
324 * map, map/unmap the whole entry.
325 */
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100326 if (commit) {
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000327 pte_t new_pte =
328 unmap ? arch_mm_absent_pte(level)
329 : arch_mm_block_pte(level, pa,
330 attrs);
331 mm_replace_entry(begin, pte, new_pte, level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000332 flags, ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100333 }
334 } else {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100335 /*
336 * If the entry is already a subtable get it; otherwise
337 * replace it with an equivalent subtable and get that.
338 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000339 struct mm_page_table *nt = mm_populate_table_pte(
340 begin, pte, level, flags, ppool);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100341 if (nt == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100342 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100343 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100344
Andrew Walbran6324fc92018-10-03 11:46:43 +0100345 /*
346 * Recurse to map/unmap the appropriate entries within
347 * the subtable.
348 */
Andrew Scull80871322018-08-06 12:04:09 +0100349 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000350 flags, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100351 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100352 }
Andrew Walbran6324fc92018-10-03 11:46:43 +0100353
354 /*
355 * If the subtable is now empty, replace it with an
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000356 * absent entry at this level. We never need to do
357 * break-before-makes here because we are assigning
358 * an absent value.
Andrew Walbran6324fc92018-10-03 11:46:43 +0100359 */
360 if (commit && unmap &&
Andrew Scull1ba470e2018-10-31 15:14:31 +0000361 mm_page_table_is_empty(nt, level - 1)) {
Andrew Walbran6324fc92018-10-03 11:46:43 +0100362 pte_t v = *pte;
363 *pte = arch_mm_absent_pte(level);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000364 mm_free_page_pte(v, level, ppool);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100365 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100366 }
367
Andrew Scullcae45572018-12-13 15:46:30 +0000368 begin = mm_start_of_next_block(begin, entry_size);
369 pa = mm_pa_start_of_next_block(pa, entry_size);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100370 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100371 }
372
373 return true;
374}
375
376/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000377 * Updates the page table from the root to map the given address range to a
378 * physical range using the provided (architecture-specific) attributes. Or if
379 * MAP_FLAG_UNMAP is set, unmap the given range instead.
380 */
381static bool mm_map_root(struct mm_ptable *t, ptable_addr_t begin,
382 ptable_addr_t end, uint64_t attrs, uint8_t root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000383 int flags, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000384{
385 size_t root_table_size = mm_entry_size(root_level);
386 struct mm_page_table *table =
387 &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
388
389 while (begin < end) {
390 if (!mm_map_level(begin, end, pa_init(begin), attrs, table,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000391 root_level - 1, flags, ppool)) {
Andrew Scull1ba470e2018-10-31 15:14:31 +0000392 return false;
393 }
Andrew Scullcae45572018-12-13 15:46:30 +0000394 begin = mm_start_of_next_block(begin, root_table_size);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000395 table++;
396 }
397
398 return true;
399}
400
401/**
Andrew Scull80871322018-08-06 12:04:09 +0100402 * Updates the given table such that the given physical address range is mapped
Andrew Sculla6da8342018-11-01 12:29:49 +0000403 * or not mapped into the address space with the architecture-agnostic mode
404 * provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100405 */
Andrew Sculla6da8342018-11-01 12:29:49 +0000406static bool mm_ptable_identity_update(struct mm_ptable *t, paddr_t pa_begin,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000407 paddr_t pa_end, int mode,
408 struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100409{
Andrew Scullc66a04d2018-12-07 13:41:56 +0000410 uint64_t attrs = arch_mm_mode_to_attrs(mode);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000411 int flags = (mode & MM_MODE_NOINVALIDATE ? MAP_FLAG_NOBBM : 0) |
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000412 (mode & MM_MODE_STAGE1 ? MAP_FLAG_STAGE1 : 0) |
Andrew Scullc66a04d2018-12-07 13:41:56 +0000413 (mode & MM_MODE_INVALID && mode & MM_MODE_UNOWNED
414 ? MAP_FLAG_UNMAP
415 : 0);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000416 uint8_t root_level = arch_mm_max_level(mode) + 1;
417 ptable_addr_t ptable_end =
418 arch_mm_root_table_count(mode) * mm_entry_size(root_level);
419 ptable_addr_t end = mm_round_up_to_page(pa_addr(pa_end));
420 ptable_addr_t begin = pa_addr(arch_mm_clear_pa(pa_begin));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100421
Andrew Scull1ba470e2018-10-31 15:14:31 +0000422 /*
423 * TODO: replace with assertions that the max level will be greater than
424 * 0 and less than 255 so wrapping will not be a problem and will not
425 * lead to subsequent overflows.
426 */
427 if (root_level == 0 || root_level == 1) {
428 return false;
429 }
430
431 /* Cap end to stay within the bounds of the page table. */
432 if (end > ptable_end) {
433 end = ptable_end;
434 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100435
436 /*
437 * Do it in two steps to prevent leaving the table in a halfway updated
438 * state. In such a two-step implementation, the table may be left with
439 * extra internal tables, but no different mapping on failure.
440 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000441 if (!mm_map_root(t, begin, end, attrs, root_level, flags, ppool) ||
Andrew Scull1ba470e2018-10-31 15:14:31 +0000442 !mm_map_root(t, begin, end, attrs, root_level,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000443 flags | MAP_FLAG_COMMIT, ppool)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100444 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100445 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100446
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100447 /* Invalidate the tlb. */
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100448 if (!(mode & MM_MODE_NOINVALIDATE)) {
449 mm_invalidate_tlb(begin, end, (mode & MM_MODE_STAGE1) != 0);
450 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100451
452 return true;
453}
454
455/**
Andrew Sculla6da8342018-11-01 12:29:49 +0000456 * Updates the given table such that the given physical address range is mapped
457 * into the address space with the architecture-agnostic mode provided.
458 */
459static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000460 paddr_t pa_end, int mode,
461 struct mpool *ppool)
Andrew Sculla6da8342018-11-01 12:29:49 +0000462{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000463 return mm_ptable_identity_update(t, pa_begin, pa_end, mode, ppool);
Andrew Sculla6da8342018-11-01 12:29:49 +0000464}
465
466/**
Andrew Scull80871322018-08-06 12:04:09 +0100467 * Updates the given table such that the given physical address range is not
468 * mapped into the address space.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100469 */
Andrew Scull80871322018-08-06 12:04:09 +0100470static bool mm_ptable_unmap(struct mm_ptable *t, paddr_t pa_begin,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000471 paddr_t pa_end, int mode, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100472{
Andrew Scullc66a04d2018-12-07 13:41:56 +0000473 return mm_ptable_identity_update(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000474 t, pa_begin, pa_end, mode | MM_MODE_UNOWNED | MM_MODE_INVALID,
475 ppool);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100476}
477
478/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100479 * Writes the given table to the debug log, calling itself recursively to
480 * write sub-tables.
481 */
Andrew Sculle9827712018-10-19 14:54:20 +0100482static void mm_dump_table_recursive(struct mm_page_table *table, uint8_t level,
Andrew Scull4e5f8142018-10-12 14:37:19 +0100483 int max_level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100484{
485 uint64_t i;
Andrew Scull4e5f8142018-10-12 14:37:19 +0100486 for (i = 0; i < MM_PTE_PER_PAGE; i++) {
487 if (!arch_mm_pte_is_present(table->entries[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100488 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100489 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100490
Andrew Scull4e5f8142018-10-12 14:37:19 +0100491 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i,
492 table->entries[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100493
Andrew Scull4e5f8142018-10-12 14:37:19 +0100494 if (arch_mm_pte_is_table(table->entries[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100495 mm_dump_table_recursive(
Andrew Scull4e5f8142018-10-12 14:37:19 +0100496 mm_page_table_from_pa(arch_mm_table_from_pte(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000497 table->entries[i], level)),
Andrew Scull80871322018-08-06 12:04:09 +0100498 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100499 }
500 }
501}
502
503/**
504 * Write the given table to the debug log.
505 */
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100506void mm_ptable_dump(struct mm_ptable *t, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100507{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000508 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100509 int max_level = arch_mm_max_level(mode);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000510 uint8_t root_table_count = arch_mm_root_table_count(mode);
511 uint8_t i;
512 for (i = 0; i < root_table_count; ++i) {
513 mm_dump_table_recursive(&tables[i], max_level, max_level);
514 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100515}
516
517/**
Andrew Walbran2400ed22018-09-27 14:45:58 +0100518 * Given that `entry` is a subtable but its entries are all absent, return the
519 * absent entry with which it can be replaced. Note that `entry` will no longer
520 * be valid after calling this function as the subtable will have been freed.
521 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000522static pte_t mm_table_pte_to_absent(pte_t entry, uint8_t level,
523 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100524{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100525 struct mm_page_table *table =
Andrew Scull3681b8d2018-12-12 14:22:59 +0000526 mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000527
Andrew Walbran2400ed22018-09-27 14:45:58 +0100528 /*
529 * Free the subtable. This is safe to do directly (rather than
530 * using mm_free_page_pte) because we know by this point that it
531 * doesn't have any subtables of its own.
532 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000533 mpool_free(ppool, table);
Wedson Almeida Filho7c913232018-11-23 18:20:29 +0000534
Andrew Walbran2400ed22018-09-27 14:45:58 +0100535 /* Replace subtable with a single absent entry. */
536 return arch_mm_absent_pte(level);
537}
538
539/**
540 * Given that `entry` is a subtable and its entries are all identical, return
541 * the single block entry with which it can be replaced if possible. Note that
542 * `entry` will no longer be valid after calling this function as the subtable
543 * may have been freed.
544 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000545static pte_t mm_table_pte_to_block(pte_t entry, uint8_t level,
546 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100547{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100548 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100549 uint64_t block_attrs;
550 uint64_t table_attrs;
551 uint64_t combined_attrs;
552 paddr_t block_address;
553
554 if (!arch_mm_is_block_allowed(level)) {
555 return entry;
556 }
557
Andrew Scull3681b8d2018-12-12 14:22:59 +0000558 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000559
560 /* Replace subtable with a single block, with equivalent attributes. */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000561 block_attrs = arch_mm_pte_attrs(table->entries[0], level - 1);
562 table_attrs = arch_mm_pte_attrs(entry, level);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100563 combined_attrs =
564 arch_mm_combine_table_entry_attrs(table_attrs, block_attrs);
Andrew Scull3681b8d2018-12-12 14:22:59 +0000565 block_address = arch_mm_block_from_pte(table->entries[0], level - 1);
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000566
Andrew Walbran2400ed22018-09-27 14:45:58 +0100567 /* Free the subtable. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000568 mpool_free(ppool, table);
569
Andrew Walbran2400ed22018-09-27 14:45:58 +0100570 /*
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000571 * We can assume that the block is aligned properly because all virtual
572 * addresses are aligned by definition, and we have a 1-1 mapping from
573 * virtual to physical addresses.
Andrew Walbran2400ed22018-09-27 14:45:58 +0100574 */
575 return arch_mm_block_pte(level, block_address, combined_attrs);
576}
577
578/**
579 * Defragment the given ptable entry by recursively replacing any tables with
580 * block or absent entries where possible.
581 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000582static pte_t mm_ptable_defrag_entry(pte_t entry, uint8_t level,
583 struct mpool *ppool)
Andrew Walbran2400ed22018-09-27 14:45:58 +0100584{
Andrew Scull4e5f8142018-10-12 14:37:19 +0100585 struct mm_page_table *table;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100586 uint64_t i;
587 uint64_t attrs;
588 bool identical_blocks_so_far = true;
589 bool all_absent_so_far = true;
590
591 if (!arch_mm_pte_is_table(entry, level)) {
592 return entry;
593 }
594
Andrew Scull3681b8d2018-12-12 14:22:59 +0000595 table = mm_page_table_from_pa(arch_mm_table_from_pte(entry, level));
Andrew Walbran2400ed22018-09-27 14:45:58 +0100596
597 /*
598 * Check if all entries are blocks with the same flags or are all
599 * absent.
600 */
Andrew Scull3681b8d2018-12-12 14:22:59 +0000601 attrs = arch_mm_pte_attrs(table->entries[0], level);
Andrew Scull4e5f8142018-10-12 14:37:19 +0100602 for (i = 0; i < MM_PTE_PER_PAGE; ++i) {
Andrew Walbran2400ed22018-09-27 14:45:58 +0100603 /*
604 * First try to defrag the entry, in case it is a subtable.
605 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000606 table->entries[i] = mm_ptable_defrag_entry(table->entries[i],
607 level - 1, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100608
Andrew Scull4e5f8142018-10-12 14:37:19 +0100609 if (arch_mm_pte_is_present(table->entries[i], level - 1)) {
Andrew Walbran2400ed22018-09-27 14:45:58 +0100610 all_absent_so_far = false;
611 }
612
613 /*
614 * If the entry is a block, check that the flags are the same as
615 * what we have so far.
616 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100617 if (!arch_mm_pte_is_block(table->entries[i], level - 1) ||
Andrew Scull3681b8d2018-12-12 14:22:59 +0000618 arch_mm_pte_attrs(table->entries[i], level) != attrs) {
Andrew Walbran2400ed22018-09-27 14:45:58 +0100619 identical_blocks_so_far = false;
620 }
621 }
622 if (identical_blocks_so_far) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000623 return mm_table_pte_to_block(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100624 }
625 if (all_absent_so_far) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000626 return mm_table_pte_to_absent(entry, level, ppool);
Andrew Walbran2400ed22018-09-27 14:45:58 +0100627 }
628 return entry;
629}
630
631/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100632 * Defragments the given page table by converting page table references to
633 * blocks whenever possible.
634 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000635void mm_ptable_defrag(struct mm_ptable *t, int mode, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100636{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000637 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Sculle9827712018-10-19 14:54:20 +0100638 uint8_t level = arch_mm_max_level(mode);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000639 uint8_t root_table_count = arch_mm_root_table_count(mode);
640 uint8_t i;
641 uint64_t j;
Andrew Walbran2400ed22018-09-27 14:45:58 +0100642
643 /*
644 * Loop through each entry in the table. If it points to another table,
645 * check if that table can be replaced by a block or an absent entry.
646 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000647 for (i = 0; i < root_table_count; ++i) {
648 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
649 tables[i].entries[j] = mm_ptable_defrag_entry(
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000650 tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000651 }
Andrew Walbran2400ed22018-09-27 14:45:58 +0100652 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100653}
654
655/**
Andrew Scullc66a04d2018-12-07 13:41:56 +0000656 * Determines if the given address is valid in the address space of the given
657 * page table by recursively traversing all levels of the page table.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100658 */
Andrew Scull4e5f8142018-10-12 14:37:19 +0100659static bool mm_is_mapped_recursive(struct mm_page_table *table,
Andrew Sculle9827712018-10-19 14:54:20 +0100660 ptable_addr_t addr, uint8_t level)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100661{
662 pte_t pte;
Andrew Scull80871322018-08-06 12:04:09 +0100663 ptable_addr_t va_level_end = mm_level_end(addr, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100664
665 /* It isn't mapped if it doesn't fit in the table. */
Andrew Scull80871322018-08-06 12:04:09 +0100666 if (addr >= va_level_end) {
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100667 return false;
668 }
669
Andrew Scull4e5f8142018-10-12 14:37:19 +0100670 pte = table->entries[mm_index(addr, level)];
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100671
Andrew Scullc66a04d2018-12-07 13:41:56 +0000672 if (!arch_mm_pte_is_valid(pte, level)) {
673 return false;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100674 }
675
Andrew Scull78d6fd92018-09-06 15:08:36 +0100676 if (arch_mm_pte_is_table(pte, level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100677 return mm_is_mapped_recursive(
Andrew Scull3681b8d2018-12-12 14:22:59 +0000678 mm_page_table_from_pa(
679 arch_mm_table_from_pte(pte, level)),
Andrew Scull4e5f8142018-10-12 14:37:19 +0100680 addr, level - 1);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100681 }
682
Andrew Scullc66a04d2018-12-07 13:41:56 +0000683 /* The entry is a valid block. */
684 return true;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100685}
686
687/**
Andrew Scullc66a04d2018-12-07 13:41:56 +0000688 * Determines if the given address is valid in the address space of the given
689 * page table.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100690 */
Andrew Scull80871322018-08-06 12:04:09 +0100691static bool mm_ptable_is_mapped(struct mm_ptable *t, ptable_addr_t addr,
692 int mode)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100693{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000694 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
Andrew Sculle9827712018-10-19 14:54:20 +0100695 uint8_t level = arch_mm_max_level(mode);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000696 size_t index;
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100697
Andrew Scull80871322018-08-06 12:04:09 +0100698 addr = mm_round_down_to_page(addr);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000699 index = mm_index(addr, level + 1);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100700
Andrew Scull1ba470e2018-10-31 15:14:31 +0000701 if (index >= arch_mm_root_table_count(mode)) {
702 return false;
703 }
704
705 return mm_is_mapped_recursive(&tables[index], addr, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100706}
707
708/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100709 * Initialises the given page table.
710 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000711bool mm_ptable_init(struct mm_ptable *t, int mode, struct mpool *ppool)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100712{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000713 uint8_t i;
714 size_t j;
715 struct mm_page_table *tables;
716 uint8_t root_table_count = arch_mm_root_table_count(mode);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100717
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000718 tables = mm_alloc_page_tables(root_table_count, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000719 if (tables == NULL) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100720 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100721 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100722
Andrew Scull1ba470e2018-10-31 15:14:31 +0000723 for (i = 0; i < root_table_count; i++) {
724 for (j = 0; j < MM_PTE_PER_PAGE; j++) {
725 tables[i].entries[j] =
726 arch_mm_absent_pte(arch_mm_max_level(mode));
727 }
Andrew Scull7364a8e2018-07-19 15:39:29 +0100728 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100729
Andrew Scull265ada92018-07-30 15:19:01 +0100730 /* TODO: halloc could return a virtual or physical address if mm not
731 * enabled? */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000732 t->root = pa_init((uintpaddr_t)tables);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100733
734 return true;
735}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100736
737/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000738 * Frees all memory associated with the give page table.
739 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000740void mm_ptable_fini(struct mm_ptable *t, int mode, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000741{
742 struct mm_page_table *tables = mm_page_table_from_pa(t->root);
743 uint8_t level = arch_mm_max_level(mode);
744 uint8_t root_table_count = arch_mm_root_table_count(mode);
745 uint8_t i;
746 uint64_t j;
747
748 for (i = 0; i < root_table_count; ++i) {
749 for (j = 0; j < MM_PTE_PER_PAGE; ++j) {
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000750 mm_free_page_pte(tables[i].entries[j], level, ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000751 }
752 }
753
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000754 mpool_add_chunk(ppool, tables,
755 sizeof(struct mm_page_table) * root_table_count);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000756}
757
758/**
Andrew Scull80871322018-08-06 12:04:09 +0100759 * Updates a VM's page table such that the given physical address range is
760 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100761 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100762 */
Andrew Scull80871322018-08-06 12:04:09 +0100763bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000764 int mode, ipaddr_t *ipa, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100765{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000766 bool success = mm_ptable_identity_map(t, begin, end,
767 mode & ~MM_MODE_STAGE1, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100768
769 if (success && ipa != NULL) {
770 *ipa = ipa_from_pa(begin);
771 }
772
773 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100774}
775
776/**
Andrew Scull80871322018-08-06 12:04:09 +0100777 * Updates the VM's table such that the given physical address range is not
778 * mapped in the address space.
779 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000780bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end, int mode,
781 struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100782{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000783 return mm_ptable_unmap(t, begin, end, mode & ~MM_MODE_STAGE1, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100784}
785
786/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000787 * Unmaps the hypervisor pages from the given page table.
788 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000789bool mm_vm_unmap_hypervisor(struct mm_ptable *t, int mode, struct mpool *ppool)
Andrew Scull1ba470e2018-10-31 15:14:31 +0000790{
791 /* TODO: If we add pages dynamically, they must be included here too. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000792 return mm_vm_unmap(t, layout_text_begin(), layout_text_end(), mode,
793 ppool) &&
794 mm_vm_unmap(t, layout_rodata_begin(), layout_rodata_end(), mode,
795 ppool) &&
796 mm_vm_unmap(t, layout_data_begin(), layout_data_end(), mode,
797 ppool);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000798}
799
800/**
Andrew Scull80871322018-08-06 12:04:09 +0100801 * Checks whether the given intermediate physical addess is mapped in the given
802 * page table of a VM.
803 */
804bool mm_vm_is_mapped(struct mm_ptable *t, ipaddr_t ipa, int mode)
805{
806 return mm_ptable_is_mapped(t, ipa_addr(ipa), mode & ~MM_MODE_STAGE1);
807}
808
809/**
810 * Translates an intermediate physical address to a physical address. Addresses
811 * are currently identity mapped so this is a simple type convertion. Returns
812 * true if the address was mapped in the table and the address was converted.
813 */
814bool mm_vm_translate(struct mm_ptable *t, ipaddr_t ipa, paddr_t *pa)
815{
816 bool mapped = mm_vm_is_mapped(t, ipa, 0);
817
818 if (mapped) {
819 *pa = pa_init(ipa_addr(ipa));
820 }
821
822 return mapped;
823}
824
825/**
826 * Updates the hypervisor page table such that the given physical address range
827 * is mapped into the address space at the corresponding address range in the
828 * architecture-agnostic mode provided.
829 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000830void *mm_identity_map(paddr_t begin, paddr_t end, int mode, struct mpool *ppool)
Andrew Scull80871322018-08-06 12:04:09 +0100831{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000832 if (mm_ptable_identity_map(&ptable, begin, end, mode | MM_MODE_STAGE1,
833 ppool)) {
Andrew Scull4e5f8142018-10-12 14:37:19 +0100834 return ptr_from_va(va_from_pa(begin));
Andrew Scull80871322018-08-06 12:04:09 +0100835 }
836
837 return NULL;
838}
839
840/**
841 * Updates the hypervisor table such that the given physical address range is
842 * not mapped in the address space.
843 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000844bool mm_unmap(paddr_t begin, paddr_t end, int mode, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100845{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000846 return mm_ptable_unmap(&ptable, begin, end, mode | MM_MODE_STAGE1,
847 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100848}
849
850/**
851 * Initialises memory management for the hypervisor itself.
852 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000853bool mm_init(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100854{
Andrew Scullcb0a7412018-11-06 17:28:14 +0000855 dlog_nosync("text: 0x%x - 0x%x\n", pa_addr(layout_text_begin()),
856 pa_addr(layout_text_end()));
857 dlog_nosync("rodata: 0x%x - 0x%x\n", pa_addr(layout_rodata_begin()),
858 pa_addr(layout_rodata_end()));
859 dlog_nosync("data: 0x%x - 0x%x\n", pa_addr(layout_data_begin()),
860 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100861
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000862 if (!mm_ptable_init(&ptable, MM_MODE_STAGE1, ppool)) {
Andrew Scullcb0a7412018-11-06 17:28:14 +0000863 dlog_nosync("Unable to allocate memory for page table.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100864 return false;
865 }
866
867 /* Map page for uart. */
868 /* TODO: We may not want to map this. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000869 mm_ptable_identity_map(
870 &ptable, pa_init(PL011_BASE),
871 pa_add(pa_init(PL011_BASE), PAGE_SIZE),
872 MM_MODE_R | MM_MODE_W | MM_MODE_D | MM_MODE_STAGE1, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100873
874 /* Map each section. */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000875 mm_identity_map(layout_text_begin(), layout_text_end(), MM_MODE_X,
876 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100877
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000878 mm_identity_map(layout_rodata_begin(), layout_rodata_end(), MM_MODE_R,
879 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100880
Andrew Scull5991ec92018-10-08 14:55:02 +0100881 mm_identity_map(layout_data_begin(), layout_data_end(),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000882 MM_MODE_R | MM_MODE_W, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100883
Andrew Scull1ba470e2018-10-31 15:14:31 +0000884 return arch_mm_init(ptable.root, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100885}
886
887bool mm_cpu_init(void)
888{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000889 return arch_mm_init(ptable.root, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100890}
891
892/**
893 * Defragments the hypervisor page table.
894 */
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000895void mm_defrag(struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100896{
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000897 mm_ptable_defrag(&ptable, MM_MODE_STAGE1, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100898}