Stopping map and unmap from adding subtables unnecessarily.
Also added tests for both map and unmap.
Change-Id: I9398e890d1e0e5ba0fe544b8192df8f866df684d
diff --git a/src/mm_test.cc b/src/mm_test.cc
index 05324f7..d0c976d 100644
--- a/src/mm_test.cc
+++ b/src/mm_test.cc
@@ -162,3 +162,185 @@
<< "i=" << i;
}
}
+
+/** If nothing is mapped, unmapping the hypervisor should have no effect. */
+TEST(mm, ptable_unmap_hypervisor_not_mapped)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_absent(table);
+
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ EXPECT_TRUE(mm_ptable_unmap_hypervisor(&ptable, 0));
+
+ for (uint64_t i = 0; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(table[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+}
+
+/**
+ * Unmapping everything should result in an empty page table with no subtables.
+ */
+TEST(mm, vm_unmap)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ pte_t *subtable_a = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ pte_t *subtable_aa = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_absent(table);
+ init_absent(subtable_a);
+ init_absent(subtable_aa);
+
+ subtable_aa[0] = arch_mm_block_pte(TOP_LEVEL - 2, pa_init(0), 0);
+ subtable_a[0] = arch_mm_table_pte(TOP_LEVEL - 1,
+ pa_init((uintpaddr_t)subtable_aa));
+ table[0] =
+ arch_mm_table_pte(TOP_LEVEL, pa_init((uintpaddr_t)subtable_a));
+
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ EXPECT_TRUE(mm_vm_unmap(&ptable, pa_init(0), pa_init(1), 0));
+
+ for (uint64_t i = 0; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(table[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+}
+
+/**
+ * Mapping a range should result in just the corresponding pages being mapped.
+ */
+TEST(mm, vm_identity_map)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ /* Start with an empty page table. */
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_absent(table);
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ /* Try mapping the first page. */
+ ipaddr_t ipa = ipa_init(-1);
+ EXPECT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), pa_init(PAGE_SIZE),
+ 0, &ipa));
+ EXPECT_THAT(ipa_addr(ipa), Eq(0));
+
+ /* Check that the first page is mapped, and nothing else. */
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(table[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ ASSERT_TRUE(arch_mm_pte_is_table(table[0], TOP_LEVEL));
+ pte_t *subtable_a = (pte_t *)ptr_from_va(
+ va_from_pa(arch_mm_table_from_pte(table[0])));
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(subtable_a[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ ASSERT_TRUE(arch_mm_pte_is_table(subtable_a[0], TOP_LEVEL - 1));
+ pte_t *subtable_aa = (pte_t *)ptr_from_va(
+ va_from_pa(arch_mm_table_from_pte(subtable_a[0])));
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(subtable_aa[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ EXPECT_TRUE(arch_mm_pte_is_block(subtable_aa[0], TOP_LEVEL - 2));
+ EXPECT_THAT(pa_addr(arch_mm_block_from_pte(subtable_aa[0])), Eq(0));
+}
+
+/** Mapping a range that is already mapped should be a no-op. */
+TEST(mm, vm_identity_map_already_mapped)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ /* Start with a full page table mapping everything. */
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_blocks(table, TOP_LEVEL, pa_init(0), 0);
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ /* Try mapping the first page. */
+ ipaddr_t ipa = ipa_init(-1);
+ EXPECT_TRUE(mm_vm_identity_map(&ptable, pa_init(0), pa_init(PAGE_SIZE),
+ 0, &ipa));
+ EXPECT_THAT(ipa_addr(ipa), Eq(0));
+
+ /*
+ * The table should still be full of blocks, with no subtables or
+ * anything else.
+ */
+ for (uint64_t i = 0; i < ENTRY_COUNT; ++i) {
+ EXPECT_TRUE(arch_mm_pte_is_block(table[i], TOP_LEVEL))
+ << "i=" << i;
+ }
+}
+
+/** Mapping a single page should result in just that page being mapped. */
+TEST(mm, vm_identity_map_page)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ /* Start with an empty page table. */
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_absent(table);
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ /* Try mapping the first page. */
+ ipaddr_t ipa = ipa_init(-1);
+ EXPECT_TRUE(mm_vm_identity_map_page(&ptable, pa_init(0), 0, &ipa));
+ EXPECT_THAT(ipa_addr(ipa), Eq(0));
+
+ /* Check that the first page is mapped, and nothing else. */
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(table[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ ASSERT_TRUE(arch_mm_pte_is_table(table[0], TOP_LEVEL));
+ pte_t *subtable_a = (pte_t *)ptr_from_va(
+ va_from_pa(arch_mm_table_from_pte(table[0])));
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(subtable_a[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ ASSERT_TRUE(arch_mm_pte_is_table(subtable_a[0], TOP_LEVEL - 1));
+ pte_t *subtable_aa = (pte_t *)ptr_from_va(
+ va_from_pa(arch_mm_table_from_pte(subtable_a[0])));
+ for (uint64_t i = 1; i < ENTRY_COUNT; ++i) {
+ EXPECT_THAT(subtable_aa[i], Eq(ABSENT_ENTRY)) << "i=" << i;
+ }
+ EXPECT_TRUE(arch_mm_pte_is_block(subtable_aa[0], TOP_LEVEL - 2));
+ EXPECT_THAT(pa_addr(arch_mm_block_from_pte(subtable_aa[0])), Eq(0));
+}
+
+/** Mapping a page that is already mapped should be a no-op. */
+TEST(mm, vm_identity_map_page_already_mapped)
+{
+ auto test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
+ halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
+
+ /* Start with a full page table mapping everything. */
+ pte_t *table = (pte_t *)halloc_aligned(PAGE_SIZE, PAGE_SIZE);
+ init_blocks(table, TOP_LEVEL, pa_init(0), 0);
+ struct mm_ptable ptable;
+ ptable.table = pa_init((uintpaddr_t)table);
+
+ /* Try mapping the first page. */
+ ipaddr_t ipa = ipa_init(-1);
+ EXPECT_TRUE(mm_vm_identity_map_page(&ptable, pa_init(0), 0, &ipa));
+ EXPECT_THAT(ipa_addr(ipa), Eq(0));
+
+ /*
+ * The table should still be full of blocks, with no subtables or
+ * anything else.
+ */
+ for (uint64_t i = 0; i < ENTRY_COUNT; ++i) {
+ EXPECT_TRUE(arch_mm_pte_is_block(table[i], TOP_LEVEL))
+ << "i=" << i;
+ }
+}