blob: 896725c6185883d4caf0e46d3a46e3ff79ee6b84 [file] [log] [blame]
Andrew Scull18c78fc2018-08-20 12:57:41 +01001#include "hf/mm.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +01002
Andrew Scull80871322018-08-06 12:04:09 +01003#include <assert.h>
Wedson Almeida Filhofed69022018-07-11 15:39:12 +01004#include <stdatomic.h>
5#include <stdint.h>
6
Andrew Scull18c78fc2018-08-20 12:57:41 +01007#include "hf/alloc.h"
8#include "hf/dlog.h"
Andrew Scull5991ec92018-10-08 14:55:02 +01009#include "hf/layout.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010010
Andrew Scull80871322018-08-06 12:04:09 +010011/* The type of addresses stored in the page table. */
12typedef uintvaddr_t ptable_addr_t;
13
14/* For stage 2, the input is an intermediate physical addresses rather than a
15 * virtual address so: */
16static_assert(
17 sizeof(ptable_addr_t) == sizeof(uintpaddr_t),
18 "Currently, the same code manages the stage 1 and stage 2 page tables "
19 "which only works if the virtual and intermediate physical addresses "
20 "are the same size. It looks like that assumption might not be holding "
21 "so we need to check that everything is going to be ok.");
22
Andrew Scull4f170f52018-07-19 12:58:20 +010023/* Keep macro alignment */
24/* clang-format off */
25
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010026#define MAP_FLAG_SYNC 0x01
27#define MAP_FLAG_COMMIT 0x02
28
Andrew Scull4f170f52018-07-19 12:58:20 +010029/* clang-format on */
30
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010031static struct mm_ptable ptable;
32
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010033/**
Andrew Scull80871322018-08-06 12:04:09 +010034 * Rounds an address down to a page boundary.
35 */
36static ptable_addr_t mm_round_down_to_page(ptable_addr_t addr)
37{
38 return addr & ~((ptable_addr_t)(PAGE_SIZE - 1));
39}
40
41/**
42 * Rounds an address up to a page boundary.
43 */
44static ptable_addr_t mm_round_up_to_page(ptable_addr_t addr)
45{
46 return mm_round_down_to_page(addr + PAGE_SIZE - 1);
47}
48
49/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010050 * Calculates the size of the address space represented by a page table entry at
51 * the given level.
52 */
53static inline size_t mm_entry_size(int level)
54{
Andrew Scull78d6fd92018-09-06 15:08:36 +010055 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010056}
57
58/**
Andrew Scull80871322018-08-06 12:04:09 +010059 * For a given address, calculates the maximum (plus one) address that can be
60 * represented by the same table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010061 */
Andrew Scull80871322018-08-06 12:04:09 +010062static inline ptable_addr_t mm_level_end(ptable_addr_t addr, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010063{
64 size_t offset = PAGE_BITS + (level + 1) * PAGE_LEVEL_BITS;
Andrew Scull80871322018-08-06 12:04:09 +010065 return ((addr >> offset) + 1) << offset;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010066}
67
68/**
Andrew Scull80871322018-08-06 12:04:09 +010069 * For a given address, calculates the index at which its entry is stored in a
70 * table at the given level.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010071 */
Andrew Scull80871322018-08-06 12:04:09 +010072static inline size_t mm_index(ptable_addr_t addr, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010073{
Andrew Scull80871322018-08-06 12:04:09 +010074 ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
Andrew Scull78d6fd92018-09-06 15:08:36 +010075 return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010076}
77
78/**
79 * Populates the provided page table entry with a reference to another table if
80 * needed, that is, if it does not yet point to another table.
81 *
82 * Returns a pointer to the table the entry now points to.
83 */
84static pte_t *mm_populate_table_pte(pte_t *pte, int level, bool sync_alloc)
85{
86 pte_t *ntable;
87 pte_t v = *pte;
88 pte_t new_pte;
89 size_t i;
90 size_t inc;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +010091 int level_below = level - 1;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010092
93 /* Just return pointer to table if it's already populated. */
Andrew Scull78d6fd92018-09-06 15:08:36 +010094 if (arch_mm_pte_is_table(v, level)) {
95 return ptr_from_va(va_from_pa(arch_mm_table_from_pte(v)));
Andrew Scull7364a8e2018-07-19 15:39:29 +010096 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010097
98 /* Allocate a new table. */
99 ntable = (sync_alloc ? halloc_aligned : halloc_aligned_nosync)(
Andrew Scull4f170f52018-07-19 12:58:20 +0100100 PAGE_SIZE, PAGE_SIZE);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100101 if (!ntable) {
102 dlog("Failed to allocate memory for page table\n");
103 return NULL;
104 }
105
106 /* Determine template for new pte and its increment. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100107 if (arch_mm_pte_is_block(v, level)) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100108 inc = mm_entry_size(level_below);
109 new_pte = arch_mm_block_pte(level_below,
110 arch_mm_block_from_pte(v),
111 arch_mm_pte_attrs(v));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100112 } else {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100113 inc = 0;
Andrew Walbran1b99f9d2018-10-03 17:54:40 +0100114 new_pte = arch_mm_absent_pte(level_below);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100115 }
116
117 /* Initialise entries in the new table. */
118 for (i = 0; i < PAGE_SIZE / sizeof(paddr_t); i++) {
119 ntable[i] = new_pte;
120 new_pte += inc;
121 }
122
123 /*
124 * Ensure initialisation is visible before updating the actual pte, then
125 * update it.
126 */
127 atomic_thread_fence(memory_order_release);
Andrew Scull78d6fd92018-09-06 15:08:36 +0100128 *pte = arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100129
130 return ntable;
131}
132
133/**
134 * Frees all page-table-related memory associated with the given pte at the
Andrew Walbran5bf935c2018-09-28 14:21:54 +0100135 * given level, including any subtables recursively.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100136 */
Andrew Walbran5bf935c2018-09-28 14:21:54 +0100137static void mm_free_page_pte(pte_t pte, int level)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100138{
Andrew Walbran5bf935c2018-09-28 14:21:54 +0100139 pte_t *table;
140 uint64_t i;
141
142 if (!arch_mm_pte_is_table(pte, level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100143 return;
Andrew Walbran5bf935c2018-09-28 14:21:54 +0100144 }
145
146 table = ptr_from_va(va_from_pa(arch_mm_table_from_pte(pte)));
147 /* Recursively free any subtables. */
148 for (i = 0; i < PAGE_SIZE / sizeof(pte_t); ++i) {
149 mm_free_page_pte(table[i], level - 1);
150 }
151
152 /* Free the table itself. */
153 hfree(table);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100154}
155
156/**
Andrew Scull80871322018-08-06 12:04:09 +0100157 * Updates the page table at the given level to map the given address range to a
158 * physical range using the provided (architecture-specific) attributes.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100159 *
160 * This function calls itself recursively if it needs to update additional
161 * levels, but the recursion is bound by the maximum number of levels in a page
162 * table.
163 */
Andrew Scull80871322018-08-06 12:04:09 +0100164static bool mm_map_level(ptable_addr_t begin, ptable_addr_t end, paddr_t pa,
Andrew Scull265ada92018-07-30 15:19:01 +0100165 uint64_t attrs, pte_t *table, int level, int flags)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100166{
Andrew Scullf3d45592018-09-20 14:30:22 +0100167 pte_t *pte = &table[mm_index(begin, level)];
Andrew Scull80871322018-08-06 12:04:09 +0100168 ptable_addr_t level_end = mm_level_end(begin, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100169 size_t entry_size = mm_entry_size(level);
170 bool commit = flags & MAP_FLAG_COMMIT;
171 bool sync = flags & MAP_FLAG_SYNC;
172
Andrew Scull265ada92018-07-30 15:19:01 +0100173 /* Cap end so that we don't go over the current level max. */
174 if (end > level_end) {
175 end = level_end;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100176 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100177
178 /* Fill each entry in the table. */
Andrew Scull265ada92018-07-30 15:19:01 +0100179 while (begin < end) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100180 if ((end - begin) >= entry_size &&
181 arch_mm_is_block_allowed(level) &&
182 (begin & (entry_size - 1)) == 0) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100183 if (commit) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100184 pte_t v = *pte;
Andrew Scull78d6fd92018-09-06 15:08:36 +0100185 *pte = arch_mm_block_pte(level, pa, attrs);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100186 /* TODO: Add barrier. How do we ensure this
187 * isn't in use by another CPU? Send IPI? */
Andrew Walbran5bf935c2018-09-28 14:21:54 +0100188 mm_free_page_pte(v, level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100189 }
190 } else {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100191 pte_t *nt = mm_populate_table_pte(pte, level, sync);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100192 if (!nt) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100193 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100194 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100195
Andrew Scull80871322018-08-06 12:04:09 +0100196 if (!mm_map_level(begin, end, pa, attrs, nt, level - 1,
197 flags)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100198 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100199 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100200 }
201
Andrew Scull265ada92018-07-30 15:19:01 +0100202 begin = (begin + entry_size) & ~(entry_size - 1);
203 pa = pa_init((pa_addr(pa) + entry_size) & ~(entry_size - 1));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100204 pte++;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100205 }
206
207 return true;
208}
209
210/**
Andrew Scull80871322018-08-06 12:04:09 +0100211 * Invalidates the TLB for the given address range.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100212 */
Andrew Scull80871322018-08-06 12:04:09 +0100213static void mm_invalidate_tlb(ptable_addr_t begin, ptable_addr_t end,
214 bool stage1)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100215{
Andrew Scull7364a8e2018-07-19 15:39:29 +0100216 if (stage1) {
Andrew Scull80871322018-08-06 12:04:09 +0100217 arch_mm_invalidate_stage1_range(va_init(begin), va_init(end));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100218 } else {
Andrew Scull80871322018-08-06 12:04:09 +0100219 arch_mm_invalidate_stage2_range(ipa_init(begin), ipa_init(end));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100220 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100221}
222
223/**
Andrew Scull80871322018-08-06 12:04:09 +0100224 * Updates the given table such that the given physical address range is mapped
225 * into the address space with the corresponding address range in the
226 * architecture-agnostic mode provided.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100227 */
Andrew Scull80871322018-08-06 12:04:09 +0100228static bool mm_ptable_identity_map(struct mm_ptable *t, paddr_t pa_begin,
229 paddr_t pa_end, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100230{
231 uint64_t attrs = arch_mm_mode_to_attrs(mode);
232 int flags = (mode & MM_MODE_NOSYNC) ? 0 : MAP_FLAG_SYNC;
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100233 int level = arch_mm_max_level(mode);
Andrew Scull8dce4982018-08-06 13:02:20 +0100234 pte_t *table = ptr_from_va(va_from_pa(t->table));
Andrew Scull80871322018-08-06 12:04:09 +0100235 ptable_addr_t begin;
236 ptable_addr_t end;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100237
Andrew Scull80871322018-08-06 12:04:09 +0100238 pa_begin = arch_mm_clear_pa(pa_begin);
239 begin = pa_addr(pa_begin);
240 end = mm_round_up_to_page(pa_addr(pa_end));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100241
242 /*
243 * Do it in two steps to prevent leaving the table in a halfway updated
244 * state. In such a two-step implementation, the table may be left with
245 * extra internal tables, but no different mapping on failure.
246 */
Andrew Scull80871322018-08-06 12:04:09 +0100247 if (!mm_map_level(begin, end, pa_begin, attrs, table, level, flags)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100248 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100249 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100250
Andrew Scull80871322018-08-06 12:04:09 +0100251 mm_map_level(begin, end, pa_begin, attrs, table, level,
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100252 flags | MAP_FLAG_COMMIT);
253
254 /* Invalidate the tlb. */
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100255 if (!(mode & MM_MODE_NOINVALIDATE)) {
256 mm_invalidate_tlb(begin, end, (mode & MM_MODE_STAGE1) != 0);
257 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100258
259 return true;
260}
261
262/**
Andrew Scull80871322018-08-06 12:04:09 +0100263 * Updates the given table such that the given physical address range is not
264 * mapped into the address space.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100265 */
Andrew Scull80871322018-08-06 12:04:09 +0100266static bool mm_ptable_unmap(struct mm_ptable *t, paddr_t pa_begin,
267 paddr_t pa_end, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100268{
269 int flags = (mode & MM_MODE_NOSYNC) ? 0 : MAP_FLAG_SYNC;
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100270 int level = arch_mm_max_level(mode);
Andrew Scull8dce4982018-08-06 13:02:20 +0100271 pte_t *table = ptr_from_va(va_from_pa(t->table));
Andrew Scull80871322018-08-06 12:04:09 +0100272 ptable_addr_t begin;
273 ptable_addr_t end;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100274
Andrew Scull80871322018-08-06 12:04:09 +0100275 pa_begin = arch_mm_clear_pa(pa_begin);
276 begin = pa_addr(pa_begin);
277 end = mm_round_up_to_page(pa_addr(pa_end));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100278
Andrew Scullfe636b12018-07-30 14:15:54 +0100279 /* Also do updates in two steps, similarly to mm_ptable_identity_map. */
Andrew Scull80871322018-08-06 12:04:09 +0100280 if (!mm_map_level(begin, end, pa_begin, 0, table, level, flags)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100281 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100282 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100283
Andrew Scull80871322018-08-06 12:04:09 +0100284 mm_map_level(begin, end, pa_begin, 0, table, level,
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100285 flags | MAP_FLAG_COMMIT);
286
287 /* Invalidate the tlb. */
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100288 if (!(mode & MM_MODE_NOINVALIDATE)) {
289 mm_invalidate_tlb(begin, end, (mode & MM_MODE_STAGE1) != 0);
290 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100291
292 return true;
293}
294
295/**
Andrew Scull80871322018-08-06 12:04:09 +0100296 * Updates the given table such that a single physical address page is mapped
297 * into the address space with the corresponding address page in the provided
298 * architecture-agnostic mode.
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100299 */
Andrew Scull80871322018-08-06 12:04:09 +0100300static bool mm_ptable_identity_map_page(struct mm_ptable *t, paddr_t pa,
301 int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100302{
303 size_t i;
304 uint64_t attrs = arch_mm_mode_to_attrs(mode);
Andrew Scull8dce4982018-08-06 13:02:20 +0100305 pte_t *table = ptr_from_va(va_from_pa(t->table));
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100306 bool sync = !(mode & MM_MODE_NOSYNC);
Andrew Scull80871322018-08-06 12:04:09 +0100307 ptable_addr_t addr;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100308
Andrew Scull80871322018-08-06 12:04:09 +0100309 pa = arch_mm_clear_pa(pa);
310 addr = pa_addr(pa);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100311
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100312 for (i = arch_mm_max_level(mode); i > 0; i--) {
Andrew Scullf3d45592018-09-20 14:30:22 +0100313 table = mm_populate_table_pte(&table[mm_index(addr, i)], i,
Andrew Scull80871322018-08-06 12:04:09 +0100314 sync);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100315 if (!table) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100316 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100317 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100318 }
319
Andrew Scull80871322018-08-06 12:04:09 +0100320 i = mm_index(addr, 0);
Andrew Scull78d6fd92018-09-06 15:08:36 +0100321 table[i] = arch_mm_block_pte(0, pa, attrs);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100322 return true;
323}
324
325/**
326 * Writes the given table to the debug log, calling itself recursively to
327 * write sub-tables.
328 */
329static void mm_dump_table_recursive(pte_t *table, int level, int max_level)
330{
331 uint64_t i;
332 for (i = 0; i < PAGE_SIZE / sizeof(pte_t); i++) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100333 if (!arch_mm_pte_is_present(table[i], level)) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100334 continue;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100335 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100336
337 dlog("%*s%x: %x\n", 4 * (max_level - level), "", i, table[i]);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100338
Andrew Scull78d6fd92018-09-06 15:08:36 +0100339 if (arch_mm_pte_is_table(table[i], level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100340 mm_dump_table_recursive(
341 ptr_from_va(va_from_pa(
Andrew Scull78d6fd92018-09-06 15:08:36 +0100342 arch_mm_table_from_pte(table[i]))),
Andrew Scull80871322018-08-06 12:04:09 +0100343 level - 1, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100344 }
345 }
346}
347
348/**
349 * Write the given table to the debug log.
350 */
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100351void mm_ptable_dump(struct mm_ptable *t, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100352{
Andrew Scull8dce4982018-08-06 13:02:20 +0100353 pte_t *table = ptr_from_va(va_from_pa(t->table));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100354 int max_level = arch_mm_max_level(mode);
Andrew Scull265ada92018-07-30 15:19:01 +0100355 mm_dump_table_recursive(table, max_level, max_level);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100356}
357
358/**
359 * Defragments the given page table by converting page table references to
360 * blocks whenever possible.
361 */
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100362void mm_ptable_defrag(struct mm_ptable *t, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100363{
364 /* TODO: Implement. */
Andrew Scull020ae692018-07-19 16:20:14 +0100365 (void)t;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100366 (void)mode;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100367}
368
369/**
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100370 * Unmaps the hypervisor pages from the given page table.
371 */
372bool mm_ptable_unmap_hypervisor(struct mm_ptable *t, int mode)
373{
374 /* TODO: If we add pages dynamically, they must be included here too. */
Andrew Scull5991ec92018-10-08 14:55:02 +0100375 return mm_ptable_unmap(t, layout_text_begin(), layout_text_end(),
376 mode) &&
377 mm_ptable_unmap(t, layout_rodata_begin(), layout_rodata_end(),
378 mode) &&
379 mm_ptable_unmap(t, layout_data_begin(), layout_data_end(), mode);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100380}
381
382/**
Andrew Scull80871322018-08-06 12:04:09 +0100383 * Determines if the given address is mapped in the given page table by
384 * recursively traversing all levels of the page table.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100385 */
Andrew Scull80871322018-08-06 12:04:09 +0100386static bool mm_is_mapped_recursive(const pte_t *table, ptable_addr_t addr,
387 int level)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100388{
389 pte_t pte;
Andrew Scull80871322018-08-06 12:04:09 +0100390 ptable_addr_t va_level_end = mm_level_end(addr, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100391
392 /* It isn't mapped if it doesn't fit in the table. */
Andrew Scull80871322018-08-06 12:04:09 +0100393 if (addr >= va_level_end) {
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100394 return false;
395 }
396
397 pte = table[mm_index(addr, level)];
398
Andrew Scull78d6fd92018-09-06 15:08:36 +0100399 if (arch_mm_pte_is_block(pte, level)) {
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100400 return true;
401 }
402
Andrew Scull78d6fd92018-09-06 15:08:36 +0100403 if (arch_mm_pte_is_table(pte, level)) {
Andrew Scull80871322018-08-06 12:04:09 +0100404 return mm_is_mapped_recursive(
Andrew Scull78d6fd92018-09-06 15:08:36 +0100405 ptr_from_va(va_from_pa(arch_mm_table_from_pte(pte))),
Andrew Scull80871322018-08-06 12:04:09 +0100406 addr, level - 1);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100407 }
408
Andrew Scull78d6fd92018-09-06 15:08:36 +0100409 /* The entry is not present. */
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100410 return false;
411}
412
413/**
Andrew Scull80871322018-08-06 12:04:09 +0100414 * Determines if the given address is mapped in the given page table.
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100415 */
Andrew Scull80871322018-08-06 12:04:09 +0100416static bool mm_ptable_is_mapped(struct mm_ptable *t, ptable_addr_t addr,
417 int mode)
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100418{
Andrew Scull8dce4982018-08-06 13:02:20 +0100419 pte_t *table = ptr_from_va(va_from_pa(t->table));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100420 int level = arch_mm_max_level(mode);
421
Andrew Scull80871322018-08-06 12:04:09 +0100422 addr = mm_round_down_to_page(addr);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100423
Andrew Scull265ada92018-07-30 15:19:01 +0100424 return mm_is_mapped_recursive(table, addr, level);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100425}
426
427/**
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100428 * Initialises the given page table.
429 */
Andrew Scull8c3a63a2018-09-20 13:38:34 +0100430bool mm_ptable_init(struct mm_ptable *t, int mode)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100431{
432 size_t i;
433 pte_t *table;
434
Andrew Scull7364a8e2018-07-19 15:39:29 +0100435 if (mode & MM_MODE_NOSYNC) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100436 table = halloc_aligned_nosync(PAGE_SIZE, PAGE_SIZE);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100437 } else {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100438 table = halloc_aligned(PAGE_SIZE, PAGE_SIZE);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100439 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100440
Andrew Scull7364a8e2018-07-19 15:39:29 +0100441 if (!table) {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100442 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100443 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100444
Andrew Scull7364a8e2018-07-19 15:39:29 +0100445 for (i = 0; i < PAGE_SIZE / sizeof(pte_t); i++) {
Andrew Scull78d6fd92018-09-06 15:08:36 +0100446 table[i] = arch_mm_absent_pte(arch_mm_max_level(mode));
Andrew Scull7364a8e2018-07-19 15:39:29 +0100447 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100448
Andrew Scull265ada92018-07-30 15:19:01 +0100449 /* TODO: halloc could return a virtual or physical address if mm not
450 * enabled? */
451 t->table = pa_init((uintpaddr_t)table);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100452
453 return true;
454}
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100455
456/**
Andrew Scull80871322018-08-06 12:04:09 +0100457 * Updates a VM's page table such that the given physical address range is
458 * mapped in the address space at the corresponding address range in the
Andrew Scullfe636b12018-07-30 14:15:54 +0100459 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100460 */
Andrew Scull80871322018-08-06 12:04:09 +0100461bool mm_vm_identity_map(struct mm_ptable *t, paddr_t begin, paddr_t end,
462 int mode, ipaddr_t *ipa)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100463{
Andrew Scull80871322018-08-06 12:04:09 +0100464 bool success =
465 mm_ptable_identity_map(t, begin, end, mode & ~MM_MODE_STAGE1);
466
467 if (success && ipa != NULL) {
468 *ipa = ipa_from_pa(begin);
469 }
470
471 return success;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100472}
473
474/**
Andrew Scull80871322018-08-06 12:04:09 +0100475 * Updates a VM's page table such that the given physical address page is
476 * mapped in the address space at the corresponding address page in the
477 * architecture-agnostic mode provided.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100478 */
Andrew Scull80871322018-08-06 12:04:09 +0100479bool mm_vm_identity_map_page(struct mm_ptable *t, paddr_t begin, int mode,
480 ipaddr_t *ipa)
481{
482 bool success =
483 mm_ptable_identity_map_page(t, begin, mode & ~MM_MODE_STAGE1);
484
485 if (success && ipa != NULL) {
486 *ipa = ipa_from_pa(begin);
487 }
488
489 return success;
490}
491
492/**
493 * Updates the VM's table such that the given physical address range is not
494 * mapped in the address space.
495 */
496bool mm_vm_unmap(struct mm_ptable *t, paddr_t begin, paddr_t end, int mode)
497{
498 return mm_ptable_unmap(t, begin, end, mode & ~MM_MODE_STAGE1);
499}
500
501/**
502 * Checks whether the given intermediate physical addess is mapped in the given
503 * page table of a VM.
504 */
505bool mm_vm_is_mapped(struct mm_ptable *t, ipaddr_t ipa, int mode)
506{
507 return mm_ptable_is_mapped(t, ipa_addr(ipa), mode & ~MM_MODE_STAGE1);
508}
509
510/**
511 * Translates an intermediate physical address to a physical address. Addresses
512 * are currently identity mapped so this is a simple type convertion. Returns
513 * true if the address was mapped in the table and the address was converted.
514 */
515bool mm_vm_translate(struct mm_ptable *t, ipaddr_t ipa, paddr_t *pa)
516{
517 bool mapped = mm_vm_is_mapped(t, ipa, 0);
518
519 if (mapped) {
520 *pa = pa_init(ipa_addr(ipa));
521 }
522
523 return mapped;
524}
525
526/**
527 * Updates the hypervisor page table such that the given physical address range
528 * is mapped into the address space at the corresponding address range in the
529 * architecture-agnostic mode provided.
530 */
531void *mm_identity_map(paddr_t begin, paddr_t end, int mode)
532{
533 if (mm_ptable_identity_map(&ptable, begin, end,
534 mode | MM_MODE_STAGE1)) {
535 return ptr_from_va(va_from_pa(begin));
536 }
537
538 return NULL;
539}
540
541/**
542 * Updates the hypervisor table such that the given physical address range is
543 * not mapped in the address space.
544 */
545bool mm_unmap(paddr_t begin, paddr_t end, int mode)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100546{
547 return mm_ptable_unmap(&ptable, begin, end, mode | MM_MODE_STAGE1);
548}
549
550/**
551 * Initialises memory management for the hypervisor itself.
552 */
553bool mm_init(void)
554{
Andrew Scull5991ec92018-10-08 14:55:02 +0100555 dlog("text: 0x%x - 0x%x\n", pa_addr(layout_text_begin()),
556 pa_addr(layout_text_end()));
557 dlog("rodata: 0x%x - 0x%x\n", pa_addr(layout_rodata_begin()),
558 pa_addr(layout_rodata_end()));
559 dlog("data: 0x%x - 0x%x\n", pa_addr(layout_data_begin()),
560 pa_addr(layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100561
Andrew Scull8c3a63a2018-09-20 13:38:34 +0100562 if (!mm_ptable_init(&ptable, MM_MODE_NOSYNC | MM_MODE_STAGE1)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100563 dlog("Unable to allocate memory for page table.\n");
564 return false;
565 }
566
567 /* Map page for uart. */
568 /* TODO: We may not want to map this. */
Andrew Scull80871322018-08-06 12:04:09 +0100569 mm_ptable_identity_map_page(&ptable, pa_init(PL011_BASE),
Andrew Scullfe636b12018-07-30 14:15:54 +0100570 MM_MODE_R | MM_MODE_W | MM_MODE_D |
571 MM_MODE_NOSYNC | MM_MODE_STAGE1);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100572
573 /* Map each section. */
Andrew Scull5991ec92018-10-08 14:55:02 +0100574 mm_identity_map(layout_text_begin(), layout_text_end(),
Andrew Scullfe636b12018-07-30 14:15:54 +0100575 MM_MODE_X | MM_MODE_NOSYNC);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100576
Andrew Scull5991ec92018-10-08 14:55:02 +0100577 mm_identity_map(layout_rodata_begin(), layout_rodata_end(),
Andrew Scullfe636b12018-07-30 14:15:54 +0100578 MM_MODE_R | MM_MODE_NOSYNC);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100579
Andrew Scull5991ec92018-10-08 14:55:02 +0100580 mm_identity_map(layout_data_begin(), layout_data_end(),
Andrew Scullfe636b12018-07-30 14:15:54 +0100581 MM_MODE_R | MM_MODE_W | MM_MODE_NOSYNC);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100582
Andrew Scull265ada92018-07-30 15:19:01 +0100583 return arch_mm_init(ptable.table, true);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100584}
585
586bool mm_cpu_init(void)
587{
Andrew Scull265ada92018-07-30 15:19:01 +0100588 return arch_mm_init(ptable.table, false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100589}
590
591/**
592 * Defragments the hypervisor page table.
593 */
594void mm_defrag(void)
595{
596 mm_ptable_defrag(&ptable, MM_MODE_STAGE1);
597}