blob: cd1e663b001b93d8bebc549c3b94dbc0016ed35f [file] [log] [blame]
Andrew Walbran9fa106c2018-09-28 14:19:29 +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
17extern "C" {
18#include "hf/mm.h"
19
20#include "hf/arch/mm.h"
21
22#include "hf/alloc.h"
23}
24
Andrew Scull1ba470e2018-10-31 15:14:31 +000025#include <limits>
Andrew Walbran9fa106c2018-09-28 14:19:29 +010026#include <memory>
Andrew Scull1ba470e2018-10-31 15:14:31 +000027#include <span>
28#include <vector>
Andrew Walbran9fa106c2018-09-28 14:19:29 +010029
30#include <gmock/gmock.h>
31
Andrew Scull232d5602018-10-15 11:07:45 +010032namespace
33{
Andrew Scull1ba470e2018-10-31 15:14:31 +000034using namespace ::std::placeholders;
Andrew Walbran9fa106c2018-09-28 14:19:29 +010035
Andrew Scull1ba470e2018-10-31 15:14:31 +000036using ::testing::AllOf;
37using ::testing::Contains;
38using ::testing::Each;
39using ::testing::Eq;
40using ::testing::SizeIs;
41using ::testing::Truly;
42
43constexpr size_t TEST_HEAP_SIZE = PAGE_SIZE * 16;
Andrew Scull232d5602018-10-15 11:07:45 +010044const int TOP_LEVEL = arch_mm_max_level(0);
45const pte_t ABSENT_ENTRY = arch_mm_absent_pte(TOP_LEVEL);
Andrew Scull1ba470e2018-10-31 15:14:31 +000046const paddr_t VM_MEM_END = pa_init(0x200'0000'0000);
Andrew Walbran9fa106c2018-09-28 14:19:29 +010047
48/**
49 * Calculates the size of the address space represented by a page table entry at
50 * the given level.
51 */
Andrew Scull232d5602018-10-15 11:07:45 +010052size_t mm_entry_size(int level)
Andrew Walbran9fa106c2018-09-28 14:19:29 +010053{
54 return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
55}
56
57/**
Andrew Scull1ba470e2018-10-31 15:14:31 +000058 * Get an STL representation of the page table.
Andrew Scull4e5f8142018-10-12 14:37:19 +010059 */
Andrew Scull1ba470e2018-10-31 15:14:31 +000060std::span<pte_t, MM_PTE_PER_PAGE> get_table(paddr_t pa)
Andrew Scull4e5f8142018-10-12 14:37:19 +010061{
Andrew Scull1ba470e2018-10-31 15:14:31 +000062 auto table = reinterpret_cast<struct mm_page_table *>(
Andrew Scull4e5f8142018-10-12 14:37:19 +010063 ptr_from_va(va_from_pa(pa)));
Andrew Scull1ba470e2018-10-31 15:14:31 +000064 return std::span<pte_t>(table->entries, std::end(table->entries));
Andrew Scull4e5f8142018-10-12 14:37:19 +010065}
66
67/**
Andrew Scull1ba470e2018-10-31 15:14:31 +000068 * Get an STL representation of the ptable.
Andrew Scull4e5f8142018-10-12 14:37:19 +010069 */
Andrew Scull1ba470e2018-10-31 15:14:31 +000070std::vector<std::span<pte_t, MM_PTE_PER_PAGE>> get_ptable(
71 const struct mm_ptable &ptable, int mode)
Andrew Scull4e5f8142018-10-12 14:37:19 +010072{
Andrew Scull1ba470e2018-10-31 15:14:31 +000073 std::vector<std::span<pte_t, MM_PTE_PER_PAGE>> all;
74 const uint8_t root_table_count = arch_mm_root_table_count(mode);
75 for (uint8_t i = 0; i < root_table_count; ++i) {
76 all.push_back(get_table(
77 pa_add(ptable.root, i * sizeof(struct mm_page_table))));
Andrew Walbran9fa106c2018-09-28 14:19:29 +010078 }
Andrew Scull1ba470e2018-10-31 15:14:31 +000079 return all;
Andrew Walbran9fa106c2018-09-28 14:19:29 +010080}
81
Andrew Scull1ba470e2018-10-31 15:14:31 +000082class mm : public ::testing::Test
Andrew Walbran9fa106c2018-09-28 14:19:29 +010083{
Andrew Scull1ba470e2018-10-31 15:14:31 +000084 void SetUp() override
85 {
86 /*
87 * TODO: replace with direct use of stdlib allocator so
88 * sanitizers are more effective.
89 */
90 test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
91 halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
Andrew Walbran9fa106c2018-09-28 14:19:29 +010092 }
Andrew Scull1ba470e2018-10-31 15:14:31 +000093
94 std::unique_ptr<uint8_t[]> test_heap;
95};
Andrew Walbran9fa106c2018-09-28 14:19:29 +010096
97/**
Andrew Scull1ba470e2018-10-31 15:14:31 +000098 * A new table is initially empty.
Andrew Walbran9fa106c2018-09-28 14:19:29 +010099 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000100TEST_F(mm, ptable_init_empty)
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100101{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000102 constexpr int mode = MM_MODE_STAGE1;
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100103 struct mm_ptable ptable;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000104 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
105 EXPECT_THAT(get_ptable(ptable, mode),
106 AllOf(SizeIs(1), Each(Each(ABSENT_ENTRY))));
107 mm_ptable_fini(&ptable, mode);
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100108}
109
110/**
Andrew Scull1ba470e2018-10-31 15:14:31 +0000111 * Each new concatenated table is initially empty.
112 */
113TEST_F(mm, ptable_init_concatenated_empty)
114{
115 constexpr int mode = 0;
116 struct mm_ptable ptable;
117 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
118 EXPECT_THAT(get_ptable(ptable, mode),
119 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
120 mm_ptable_fini(&ptable, mode);
121}
122
123/**
124 * Only the first page is mapped with all others left absent.
125 */
126TEST_F(mm, map_first_page)
127{
128 constexpr int mode = 0;
129 const paddr_t page_begin = pa_init(0);
130 const paddr_t page_end = pa_add(page_begin, PAGE_SIZE);
131 struct mm_ptable ptable;
132 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
133 ASSERT_TRUE(mm_vm_identity_map(&ptable, page_begin, page_end, mode,
134 nullptr));
135
136 auto tables = get_ptable(ptable, mode);
137 EXPECT_THAT(tables, SizeIs(4));
138 ASSERT_THAT(TOP_LEVEL, Eq(2));
139
140 /* Check that the first page is mapped and nothing else. */
141 EXPECT_THAT(std::span(tables).last(3), Each(Each(ABSENT_ENTRY)));
142
143 auto table_l2 = tables.front();
144 EXPECT_THAT(table_l2.subspan(1), Each(ABSENT_ENTRY));
145 ASSERT_TRUE(arch_mm_pte_is_table(table_l2[0], TOP_LEVEL));
146
147 auto table_l1 = get_table(arch_mm_table_from_pte(table_l2[0]));
148 EXPECT_THAT(table_l1.subspan(1), Each(ABSENT_ENTRY));
149 ASSERT_TRUE(arch_mm_pte_is_table(table_l1[0], TOP_LEVEL - 1));
150
151 auto table_l0 = get_table(arch_mm_table_from_pte(table_l1[0]));
152 EXPECT_THAT(table_l0.subspan(1), Each(ABSENT_ENTRY));
153 ASSERT_TRUE(arch_mm_pte_is_block(table_l0[0], TOP_LEVEL - 2));
154 EXPECT_THAT(pa_addr(arch_mm_block_from_pte(table_l0[0])),
155 Eq(pa_addr(page_begin)));
156
157 mm_ptable_fini(&ptable, mode);
158}
159
160/**
161 * The start address is rounded down and the end address is rounded up to page
162 * boundaries.
163 */
164TEST_F(mm, map_round_to_page)
165{
166 constexpr int mode = 0;
167 const paddr_t map_begin = pa_init(0x200'0000'0000 - PAGE_SIZE + 23);
168 const paddr_t map_end = pa_add(map_begin, 268);
169 ipaddr_t ipa = ipa_init(-1);
170 struct mm_ptable ptable;
171 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
172 ASSERT_TRUE(
173 mm_vm_identity_map(&ptable, map_begin, map_end, mode, &ipa));
174 EXPECT_THAT(ipa_addr(ipa), Eq(pa_addr(map_begin)));
175
176 auto tables = get_ptable(ptable, mode);
177 EXPECT_THAT(tables, SizeIs(4));
178 ASSERT_THAT(TOP_LEVEL, Eq(2));
179
180 /* Check that the last page is mapped, and nothing else. */
181 EXPECT_THAT(std::span(tables).first(3), Each(Each(ABSENT_ENTRY)));
182
183 auto table_l2 = tables.back();
184 EXPECT_THAT(table_l2.first(table_l2.size() - 1), Each(ABSENT_ENTRY));
185 ASSERT_TRUE(arch_mm_pte_is_table(table_l2.last(1)[0], TOP_LEVEL));
186
187 auto table_l1 = get_table(arch_mm_table_from_pte(table_l2.last(1)[0]));
188 EXPECT_THAT(table_l1.first(table_l1.size() - 1), Each(ABSENT_ENTRY));
189 ASSERT_TRUE(arch_mm_pte_is_table(table_l1.last(1)[0], TOP_LEVEL - 1));
190
191 auto table_l0 = get_table(arch_mm_table_from_pte(table_l1.last(1)[0]));
192 EXPECT_THAT(table_l0.first(table_l0.size() - 1), Each(ABSENT_ENTRY));
193 ASSERT_TRUE(arch_mm_pte_is_block(table_l0.last(1)[0], TOP_LEVEL - 2));
194 EXPECT_THAT(pa_addr(arch_mm_block_from_pte(table_l0.last(1)[0])),
195 Eq(0x200'0000'0000 - PAGE_SIZE));
196
197 mm_ptable_fini(&ptable, mode);
198}
199
200/**
201 * Map a two page range over the boundary of two tables.
202 */
203TEST_F(mm, map_across_tables)
204{
205 constexpr int mode = 0;
206 const paddr_t map_begin = pa_init(0x80'0000'0000 - PAGE_SIZE);
207 const paddr_t map_end = pa_add(map_begin, 2 * PAGE_SIZE);
208 struct mm_ptable ptable;
209 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
210 ASSERT_TRUE(
211 mm_vm_identity_map(&ptable, map_begin, map_end, mode, nullptr));
212
213 auto tables = get_ptable(ptable, mode);
214 EXPECT_THAT(tables, SizeIs(4));
215 EXPECT_THAT(std::span(tables).last(2), Each(Each(ABSENT_ENTRY)));
216 ASSERT_THAT(TOP_LEVEL, Eq(2));
217
218 /* Check only the last page of the first table is mapped. */
219 auto table0_l2 = tables.front();
220 EXPECT_THAT(table0_l2.first(table0_l2.size() - 1), Each(ABSENT_ENTRY));
221 ASSERT_TRUE(arch_mm_pte_is_table(table0_l2.last(1)[0], TOP_LEVEL));
222
223 auto table0_l1 =
224 get_table(arch_mm_table_from_pte(table0_l2.last(1)[0]));
225 EXPECT_THAT(table0_l1.first(table0_l1.size() - 1), Each(ABSENT_ENTRY));
226 ASSERT_TRUE(arch_mm_pte_is_table(table0_l1.last(1)[0], TOP_LEVEL - 1));
227
228 auto table0_l0 =
229 get_table(arch_mm_table_from_pte(table0_l1.last(1)[0]));
230 EXPECT_THAT(table0_l0.first(table0_l0.size() - 1), Each(ABSENT_ENTRY));
231 ASSERT_TRUE(arch_mm_pte_is_block(table0_l0.last(1)[0], TOP_LEVEL - 2));
232 EXPECT_THAT(pa_addr(arch_mm_block_from_pte(table0_l0.last(1)[0])),
233 Eq(pa_addr(map_begin)));
234
235 /* Checl only the first page of the second table is mapped. */
236 auto table1_l2 = tables[1];
237 EXPECT_THAT(table1_l2.subspan(1), Each(ABSENT_ENTRY));
238 ASSERT_TRUE(arch_mm_pte_is_table(table1_l2[0], TOP_LEVEL));
239
240 auto table1_l1 = get_table(arch_mm_table_from_pte(table1_l2[0]));
241 EXPECT_THAT(table1_l1.subspan(1), Each(ABSENT_ENTRY));
242 ASSERT_TRUE(arch_mm_pte_is_table(table1_l1[0], TOP_LEVEL - 1));
243
244 auto table1_l0 = get_table(arch_mm_table_from_pte(table1_l1[0]));
245 EXPECT_THAT(table1_l0.subspan(1), Each(ABSENT_ENTRY));
246 ASSERT_TRUE(arch_mm_pte_is_block(table1_l0[0], TOP_LEVEL - 2));
247 EXPECT_THAT(pa_addr(arch_mm_block_from_pte(table1_l0[0])),
248 Eq(pa_addr(pa_add(map_begin, PAGE_SIZE))));
249
250 mm_ptable_fini(&ptable, mode);
251}
252
253/**
254 * Mapping all of memory creates blocks at the highest level.
255 */
256TEST_F(mm, map_all_at_top_level)
257{
258 constexpr int mode = 0;
259 struct mm_ptable ptable;
260 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
261 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
262 nullptr));
263 auto tables = get_ptable(ptable, mode);
264 EXPECT_THAT(
265 tables,
266 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
267 _1, TOP_LEVEL))))));
268 for (uint64_t i = 0; i < tables.size(); ++i) {
269 for (uint64_t j = 0; j < MM_PTE_PER_PAGE; ++j) {
270 EXPECT_THAT(
271 pa_addr(arch_mm_block_from_pte(tables[i][j])),
272 Eq((i * mm_entry_size(TOP_LEVEL + 1)) +
273 (j * mm_entry_size(TOP_LEVEL))))
274 << "i=" << i << " j=" << j;
275 }
276 }
277 mm_ptable_fini(&ptable, mode);
278}
279
280/**
281 * Map all memory then trying to map a page again doesn't introduce a special
282 * mapping for that particular page.
283 */
284TEST_F(mm, map_already_mapped)
285{
286 constexpr int mode = 0;
287 ipaddr_t ipa = ipa_init(-1);
288 struct mm_ptable ptable;
289 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
290 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
291 nullptr));
292 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), pa_init(PAGE_SIZE),
293 mode, &ipa));
294 EXPECT_THAT(ipa_addr(ipa), Eq(0));
295 EXPECT_THAT(
296 get_ptable(ptable, mode),
297 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
298 _1, TOP_LEVEL))))));
299 mm_ptable_fini(&ptable, mode);
300}
301
302/**
303 * Mapping a reverse range, i.e. the end comes before the start, is treated as
304 * an empty range so no mappings are made.
305 */
306TEST_F(mm, map_reverse_range)
307{
308 constexpr int mode = 0;
309 ipaddr_t ipa = ipa_init(-1);
310 struct mm_ptable ptable;
311 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
312 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0x1234'5678),
313 pa_init(0x5000), mode, &ipa));
314 EXPECT_THAT(ipa_addr(ipa), Eq(0x1234'5678));
315 EXPECT_THAT(get_ptable(ptable, mode),
316 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
317 mm_ptable_fini(&ptable, mode);
318}
319
320/**
321 * Mapping a reverse range in the same page will map the page because the start
322 * of the range is rounded down and the end is rounded up.
323 *
324 * This serves as a form of documentation of behaviour rather than a
325 * requirement. Check whether any code relies on this before changing it.
326 */
327TEST_F(mm, map_reverse_range_quirk)
328{
329 constexpr int mode = 0;
330 ipaddr_t ipa = ipa_init(-1);
331 struct mm_ptable ptable;
332 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
333 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(20), pa_init(10), mode,
334 &ipa));
335 EXPECT_THAT(ipa_addr(ipa), Eq(20));
336 EXPECT_TRUE(mm_vm_is_mapped(&ptable, ipa, mode));
337 mm_ptable_fini(&ptable, mode);
338}
339
340/**
341 * Mapping a range up to the maximum address causes the range end to wrap to
342 * zero as it is rounded up to a page boundary meaning no memory is mapped.
343 *
344 * This serves as a form of documentation of behaviour rather than a
345 * requirement. Check whether any code relies on this before changing it.
346 */
347TEST_F(mm, map_last_address_quirk)
348{
349 constexpr int mode = 0;
350 ipaddr_t ipa = ipa_init(-1);
351 struct mm_ptable ptable;
352 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
353 ASSERT_TRUE(mm_vm_identity_map(
354 &ptable, pa_init(0),
355 pa_init(std::numeric_limits<uintpaddr_t>::max()), mode, &ipa));
356 EXPECT_THAT(ipa_addr(ipa), Eq(0));
357 EXPECT_THAT(get_ptable(ptable, mode),
358 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
359 mm_ptable_fini(&ptable, mode);
360}
361
362/**
363 * Mapping a range that goes beyond the available memory clamps to the available
364 * range.
365 */
366TEST_F(mm, map_clamp_to_range)
367{
368 constexpr int mode = 0;
369 struct mm_ptable ptable;
370 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
371 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0),
372 pa_init(0xf32'0000'0000'0000), mode,
373 nullptr));
374 EXPECT_THAT(
375 get_ptable(ptable, mode),
376 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
377 _1, TOP_LEVEL))))));
378 mm_ptable_fini(&ptable, mode);
379}
380
381/**
382 * Mapping a range outside of the available memory is ignored and doesn't alter
383 * the page tables.
384 */
385TEST_F(mm, map_ignore_out_of_range)
386{
387 constexpr int mode = 0;
388 ipaddr_t ipa = ipa_init(-1);
389 struct mm_ptable ptable;
390 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
391 ASSERT_TRUE(mm_vm_identity_map(
392 &ptable, VM_MEM_END, pa_init(0xf0'0000'0000'0000), mode, &ipa));
393 EXPECT_THAT(ipa_addr(ipa), Eq(pa_addr(VM_MEM_END)));
394 EXPECT_THAT(get_ptable(ptable, mode),
395 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
396 mm_ptable_fini(&ptable, 0);
397}
398
399/**
400 * Map a single page and then map all of memory which replaces the single page
401 * mapping with a higher level block mapping.
402 */
403TEST_F(mm, map_block_replaces_table)
404{
405 constexpr int mode = 0;
406 const paddr_t page_begin = pa_init(34567 * PAGE_SIZE);
407 const paddr_t page_end = pa_add(page_begin, PAGE_SIZE);
408 struct mm_ptable ptable;
409 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
410 ASSERT_TRUE(mm_vm_identity_map(&ptable, page_begin, page_end, mode,
411 nullptr));
412 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
413 nullptr));
414 EXPECT_THAT(
415 get_ptable(ptable, mode),
416 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
417 _1, TOP_LEVEL))))));
418 mm_ptable_fini(&ptable, mode);
419}
420
421/**
422 * Map all memory at the top level, unmapping a page and remapping at a lower
423 * level does not result in all memory being mapped at the top level again.
424 */
425TEST_F(mm, map_does_not_defrag)
426{
427 constexpr int mode = 0;
428 const paddr_t page_begin = pa_init(12000 * PAGE_SIZE);
429 const paddr_t page_end = pa_add(page_begin, PAGE_SIZE);
430 struct mm_ptable ptable;
431 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
432 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
433 nullptr));
434 ASSERT_TRUE(mm_vm_unmap(&ptable, page_begin, page_end, mode));
435 ASSERT_TRUE(mm_vm_identity_map(&ptable, page_begin, page_end, mode,
436 nullptr));
437 EXPECT_THAT(get_ptable(ptable, mode),
438 AllOf(SizeIs(4),
439 Each(Each(Truly(std::bind(arch_mm_pte_is_present, _1,
440 TOP_LEVEL)))),
441 Contains(Contains(Truly(std::bind(
442 arch_mm_pte_is_block, _1, TOP_LEVEL)))),
443 Contains(Contains(Truly(std::bind(
444 arch_mm_pte_is_table, _1, TOP_LEVEL))))));
445 mm_ptable_fini(&ptable, mode);
446}
447
448/**
449 * If nothing is mapped, unmapping the hypervisor has no effect.
450 */
451TEST_F(mm, vm_unmap_hypervisor_not_mapped)
452{
453 constexpr int mode = 0;
454 struct mm_ptable ptable;
455 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
456 EXPECT_TRUE(mm_vm_unmap_hypervisor(&ptable, mode));
457 EXPECT_THAT(get_ptable(ptable, mode),
458 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
459 mm_ptable_fini(&ptable, mode);
460}
461
462/**
463 * If range is not mapped, unmapping has no effect.
464 */
465TEST_F(mm, unmap_not_mapped)
466{
467 constexpr int mode = 0;
468 struct mm_ptable ptable;
469 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
470 EXPECT_TRUE(
471 mm_vm_unmap(&ptable, pa_init(12345), pa_init(987652), mode));
472 EXPECT_THAT(get_ptable(ptable, mode),
473 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
474 mm_ptable_fini(&ptable, mode);
475}
476
477/**
478 * Unmapping everything should result in an empty page table with no subtables.
479 */
480TEST_F(mm, unmap_all)
481{
482 constexpr int mode = 0;
483 const paddr_t l0_begin = pa_init(uintpaddr_t(524421) * PAGE_SIZE);
484 const paddr_t l0_end = pa_add(l0_begin, 17 * PAGE_SIZE);
485 const paddr_t l1_begin = pa_init(3 * mm_entry_size(1));
486 const paddr_t l1_end = pa_add(l1_begin, 5 * mm_entry_size(1));
487 struct mm_ptable ptable;
488 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
489 ASSERT_TRUE(
490 mm_vm_identity_map(&ptable, l0_begin, l0_end, mode, nullptr));
491 ASSERT_TRUE(
492 mm_vm_identity_map(&ptable, l1_begin, l1_end, mode, nullptr));
493 EXPECT_TRUE(mm_vm_unmap(&ptable, pa_init(0), VM_MEM_END, mode));
494 EXPECT_THAT(get_ptable(ptable, mode),
495 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
496 mm_ptable_fini(&ptable, mode);
497}
498
499/**
500 * Unmap range is rounded to the containing pages.
501 */
502TEST_F(mm, unmap_round_to_page)
503{
504 constexpr int mode = 0;
505 const paddr_t map_begin = pa_init(0x160'0000'0000 + PAGE_SIZE);
506 const paddr_t map_end = pa_add(map_begin, PAGE_SIZE);
507 struct mm_ptable ptable;
508 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
509 ASSERT_TRUE(
510 mm_vm_identity_map(&ptable, map_begin, map_end, mode, nullptr));
511 ASSERT_TRUE(mm_vm_unmap(&ptable, pa_add(map_begin, 93),
512 pa_add(map_begin, 99), mode));
513 EXPECT_THAT(get_ptable(ptable, mode),
514 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
515 mm_ptable_fini(&ptable, mode);
516}
517
518/**
519 * Unmap a range that of page mappings that spans multiple concatenated tables.
520 */
521TEST_F(mm, unmap_across_tables)
522{
523 constexpr int mode = 0;
524 const paddr_t map_begin = pa_init(0x180'0000'0000 - PAGE_SIZE);
525 const paddr_t map_end = pa_add(map_begin, 2 * PAGE_SIZE);
526 struct mm_ptable ptable;
527 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
528 ASSERT_TRUE(
529 mm_vm_identity_map(&ptable, map_begin, map_end, mode, nullptr));
530 ASSERT_TRUE(mm_vm_unmap(&ptable, map_begin, map_end, mode));
531 EXPECT_THAT(get_ptable(ptable, mode),
532 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
533 mm_ptable_fini(&ptable, mode);
534}
535
536/**
537 * Unmapping outside the range of memory had no effect.
538 */
539TEST_F(mm, unmap_out_of_range)
540{
541 constexpr int mode = 0;
542 struct mm_ptable ptable;
543 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
544 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
545 nullptr));
546 ASSERT_TRUE(mm_vm_unmap(&ptable, VM_MEM_END, pa_init(0x4000'0000'0000),
547 mode));
548 EXPECT_THAT(
549 get_ptable(ptable, mode),
550 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
551 _1, TOP_LEVEL))))));
552 mm_ptable_fini(&ptable, mode);
553}
554
555/**
556 * Unmapping a reverse range, i.e. the end comes before the start, is treated as
557 * an empty range so no change is made.
558 */
559TEST_F(mm, unmap_reverse_range)
560{
561 constexpr int mode = 0;
562 struct mm_ptable ptable;
563 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
564 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
565 nullptr));
566 ASSERT_TRUE(mm_vm_unmap(&ptable, pa_init(0x80'a000'0000), pa_init(27),
567 mode));
568 EXPECT_THAT(
569 get_ptable(ptable, mode),
570 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
571 _1, TOP_LEVEL))))));
572 mm_ptable_fini(&ptable, mode);
573}
574
575/**
576 * Unmapping a reverse range in the same page will unmap the page because the
577 * start of the range is rounded down and the end is rounded up.
578 *
579 * This serves as a form of documentation of behaviour rather than a
580 * requirement. Check whether any code relies on this before changing it.
581 */
582TEST_F(mm, unmap_reverse_range_quirk)
583{
584 constexpr int mode = 0;
585 const paddr_t page_begin = pa_init(0x180'0000'0000);
586 const paddr_t page_end = pa_add(page_begin, PAGE_SIZE);
587 struct mm_ptable ptable;
588 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
589 ASSERT_TRUE(mm_vm_identity_map(&ptable, page_begin, page_end, mode,
590 nullptr));
591 ASSERT_TRUE(mm_vm_unmap(&ptable, pa_add(page_begin, 100),
592 pa_add(page_begin, 50), mode));
593 EXPECT_THAT(get_ptable(ptable, mode),
594 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
595 mm_ptable_fini(&ptable, mode);
596}
597
598/**
599 * Unmapping a range up to the maximum address causes the range end to wrap to
600 * zero as it is rounded up to a page boundary meaning no change is made.
601 *
602 * This serves as a form of documentation of behaviour rather than a
603 * requirement. Check whether any code relies on this before changing it.
604 */
605TEST_F(mm, unmap_last_address_quirk)
606{
607 constexpr int mode = 0;
608 struct mm_ptable ptable;
609 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
610 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
611 nullptr));
612 ASSERT_TRUE(mm_vm_unmap(
613 &ptable, pa_init(0),
614 pa_init(std::numeric_limits<uintpaddr_t>::max()), mode));
615 EXPECT_THAT(
616 get_ptable(ptable, mode),
617 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
618 _1, TOP_LEVEL))))));
619 mm_ptable_fini(&ptable, mode);
620}
621
622/**
623 * Mapping then unmapping a page does not defrag the table.
624 */
625TEST_F(mm, unmap_does_not_defrag)
626{
627 constexpr int mode = 0;
628 const paddr_t l0_begin = pa_init(5555 * PAGE_SIZE);
629 const paddr_t l0_end = pa_add(l0_begin, 13 * PAGE_SIZE);
630 const paddr_t l1_begin = pa_init(666 * mm_entry_size(1));
631 const paddr_t l1_end = pa_add(l1_begin, 5 * mm_entry_size(1));
632 struct mm_ptable ptable;
633 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
634 ASSERT_TRUE(
635 mm_vm_identity_map(&ptable, l0_begin, l0_end, mode, nullptr));
636 ASSERT_TRUE(
637 mm_vm_identity_map(&ptable, l1_begin, l1_end, mode, nullptr));
638 ASSERT_TRUE(mm_vm_unmap(&ptable, l0_begin, l0_end, mode));
639 ASSERT_TRUE(mm_vm_unmap(&ptable, l1_begin, l1_end, mode));
640 EXPECT_THAT(get_ptable(ptable, mode),
641 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
642 mm_ptable_fini(&ptable, MM_MODE_STAGE1);
643}
644
645/**
646 * Nothing is mapped in an empty table.
647 */
648TEST_F(mm, is_mapped_empty)
649{
650 constexpr int mode = 0;
651 struct mm_ptable ptable;
652 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
653 EXPECT_FALSE(mm_vm_is_mapped(&ptable, ipa_init(0), mode));
654 EXPECT_FALSE(mm_vm_is_mapped(&ptable, ipa_init(0x8123'2344), mode));
655 EXPECT_FALSE(mm_vm_is_mapped(&ptable, ipa_init(0x1e0'0000'0073), mode));
656 mm_ptable_fini(&ptable, mode);
657}
658
659/**
660 * Everything is mapped in a full table.
661 */
662TEST_F(mm, is_mapped_all)
663{
664 constexpr int mode = 0;
665 struct mm_ptable ptable;
666 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
667 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
668 nullptr));
669 EXPECT_TRUE(mm_vm_is_mapped(&ptable, ipa_init(0), mode));
670 EXPECT_TRUE(mm_vm_is_mapped(&ptable, ipa_init(0xf247'a7b3), mode));
671 EXPECT_TRUE(mm_vm_is_mapped(&ptable, ipa_init(0x1ff'7bfa'983b), mode));
672 mm_ptable_fini(&ptable, mode);
673}
674
675/**
676 * A page is mapped for the range [begin, end).
677 */
678TEST_F(mm, is_mapped_page)
679{
680 constexpr int mode = 0;
681 const paddr_t page_begin = pa_init(0x100'0000'0000);
682 const paddr_t page_end = pa_add(page_begin, PAGE_SIZE);
683 struct mm_ptable ptable;
684 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
685 ASSERT_TRUE(mm_vm_identity_map(&ptable, page_begin, page_end, mode,
686 nullptr));
687 EXPECT_TRUE(mm_vm_is_mapped(&ptable, ipa_from_pa(page_begin), mode));
688 EXPECT_TRUE(mm_vm_is_mapped(
689 &ptable, ipa_from_pa(pa_add(page_begin, 127)), mode));
690 EXPECT_FALSE(mm_vm_is_mapped(&ptable, ipa_from_pa(page_end), mode));
691 mm_ptable_fini(&ptable, mode);
692}
693
694/**
695 * Everything out of range is not mapped.
696 */
697TEST_F(mm, is_mapped_out_of_range)
698{
699 constexpr int mode = 0;
700 struct mm_ptable ptable;
701 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
702 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
703 nullptr));
704 EXPECT_FALSE(mm_vm_is_mapped(&ptable, ipa_from_pa(VM_MEM_END), mode));
705 EXPECT_FALSE(
706 mm_vm_is_mapped(&ptable, ipa_init(0x1000'adb7'8123), mode));
707 EXPECT_FALSE(mm_vm_is_mapped(
708 &ptable, ipa_init(std::numeric_limits<uintpaddr_t>::max()),
709 mode));
710 mm_ptable_fini(&ptable, mode);
711}
712
713/**
714 * Defragging an entirely empty table has no effect.
715 */
716TEST_F(mm, defrag_empty)
717{
718 constexpr int mode = 0;
719 struct mm_ptable ptable;
720 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
721 mm_ptable_defrag(&ptable, mode);
722 EXPECT_THAT(get_ptable(ptable, mode),
723 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
724 mm_ptable_fini(&ptable, mode);
725}
726
727/**
728 * Defragging a table with some empty subtables (even nested) results in
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100729 * an empty table.
730 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000731TEST_F(mm, defrag_empty_subtables)
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100732{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000733 constexpr int mode = 0;
734 const paddr_t l0_begin = pa_init(120000 * PAGE_SIZE);
735 const paddr_t l0_end = pa_add(l0_begin, PAGE_SIZE);
736 const paddr_t l1_begin = pa_init(3 * mm_entry_size(1));
737 const paddr_t l1_end = pa_add(l1_begin, 5 * mm_entry_size(1));
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100738 struct mm_ptable ptable;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000739 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
740 ASSERT_TRUE(
741 mm_vm_identity_map(&ptable, l0_begin, l0_end, mode, nullptr));
742 ASSERT_TRUE(
743 mm_vm_identity_map(&ptable, l0_begin, l0_end, mode, nullptr));
744 ASSERT_TRUE(mm_vm_unmap(&ptable, l0_begin, l0_end, mode));
745 ASSERT_TRUE(mm_vm_unmap(&ptable, l1_begin, l1_end, mode));
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100746 mm_ptable_defrag(&ptable, 0);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000747 EXPECT_THAT(get_ptable(ptable, mode),
748 AllOf(SizeIs(4), Each(Each(ABSENT_ENTRY))));
749 mm_ptable_fini(&ptable, mode);
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100750}
751
752/**
753 * Any subtable with all blocks with the same attributes should be replaced
754 * with a single block.
755 */
Andrew Scull1ba470e2018-10-31 15:14:31 +0000756TEST_F(mm, defrag_block_subtables)
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100757{
Andrew Scull1ba470e2018-10-31 15:14:31 +0000758 constexpr int mode = 0;
759 const paddr_t begin = pa_init(39456 * mm_entry_size(1));
760 const paddr_t middle = pa_add(begin, 67 * PAGE_SIZE);
761 const paddr_t end = pa_add(begin, 4 * mm_entry_size(1));
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100762 struct mm_ptable ptable;
Andrew Scull1ba470e2018-10-31 15:14:31 +0000763 ASSERT_TRUE(mm_ptable_init(&ptable, mode));
764 ASSERT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), VM_MEM_END, mode,
765 nullptr));
766 ASSERT_TRUE(mm_vm_unmap(&ptable, begin, end, mode));
767 ASSERT_TRUE(mm_vm_identity_map(&ptable, begin, middle, mode, nullptr));
768 ASSERT_TRUE(mm_vm_identity_map(&ptable, middle, end, mode, nullptr));
Andrew Walbran9fa106c2018-09-28 14:19:29 +0100769 mm_ptable_defrag(&ptable, 0);
Andrew Scull1ba470e2018-10-31 15:14:31 +0000770 EXPECT_THAT(
771 get_ptable(ptable, mode),
772 AllOf(SizeIs(4), Each(Each(Truly(std::bind(arch_mm_pte_is_block,
773 _1, TOP_LEVEL))))));
774 mm_ptable_fini(&ptable, mode);
Andrew Walbran6324fc92018-10-03 11:46:43 +0100775}
776
Andrew Scull232d5602018-10-15 11:07:45 +0100777} /* namespace */