blob: 364d0ad175fa4c1a960e0d241b2585bb9375e7a2 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stdlib.h>
#include <hypervisor/guest.h>
#include <pretty/hexdump.h>
#include <unittest/unittest.h>
static void* page_addr(void* base, size_t page) {
uintptr_t addr = reinterpret_cast<uintptr_t>(base);
addr += PAGE_SIZE * page;
return reinterpret_cast<void*>(addr);
}
static void hexdump_result(void* actual, void* expected) {
printf("\nactual:\n");
hexdump_ex(page_addr(actual, 0), 16, PAGE_SIZE * 0);
hexdump_ex(page_addr(actual, 1), 16, PAGE_SIZE * 1);
hexdump_ex(page_addr(actual, 2), 16, PAGE_SIZE * 2);
hexdump_ex(page_addr(actual, 3), 32, PAGE_SIZE * 3);
printf("expected:\n");
hexdump_ex(page_addr(expected, 0), 16, PAGE_SIZE * 0);
hexdump_ex(page_addr(expected, 1), 16, PAGE_SIZE * 1);
hexdump_ex(page_addr(expected, 2), 16, PAGE_SIZE * 2);
hexdump_ex(page_addr(expected, 3), 32, PAGE_SIZE * 3);
}
#define ASSERT_EPT_EQ(actual, expected, size, ...) \
do { \
int cmp = memcmp(actual, expected, size); \
if (cmp != 0) \
hexdump_result(actual, expected); \
ASSERT_EQ(cmp, 0, ##__VA_ARGS__); \
} while (0)
#define INITIALIZE_PAGE_TABLE \
{ \
{ \
{ 0 } \
} \
}
typedef struct {
uint64_t entries[512];
} page_table;
enum {
X86_PTE_P = 0x01, /* P Valid */
X86_PTE_RW = 0x02, /* R/W Read/Write */
X86_PTE_PS = 0x80, /* PS Page size */
};
static bool page_table_1gb(void) {
BEGIN_TEST;
uintptr_t pte_off;
page_table actual[4] = INITIALIZE_PAGE_TABLE;
page_table expected[4] = INITIALIZE_PAGE_TABLE;
ASSERT_EQ(guest_create_page_table((uintptr_t)actual, 1 << 30, &pte_off), ZX_OK);
// pml4
expected[0].entries[0] = PAGE_SIZE | X86_PTE_P | X86_PTE_RW;
// pdp
expected[1].entries[0] = X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
ASSERT_EPT_EQ(actual, expected, sizeof(actual));
ASSERT_EQ(pte_off, PAGE_SIZE * 2u);
END_TEST;
}
static bool page_table_2mb(void) {
BEGIN_TEST;
uintptr_t pte_off;
page_table actual[4] = INITIALIZE_PAGE_TABLE;
page_table expected[4] = INITIALIZE_PAGE_TABLE;
ASSERT_EQ(guest_create_page_table((uintptr_t)actual, 2 << 20, &pte_off), ZX_OK);
// pml4
expected[0].entries[0] = PAGE_SIZE | X86_PTE_P | X86_PTE_RW;
// pdp
expected[1].entries[0] = PAGE_SIZE * 2 | X86_PTE_P | X86_PTE_RW;
// pd
expected[2].entries[0] = X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
ASSERT_EPT_EQ(actual, expected, sizeof(actual));
ASSERT_EQ(pte_off, PAGE_SIZE * 3u);
END_TEST;
}
static bool page_table_4kb(void) {
BEGIN_TEST;
uintptr_t pte_off;
page_table actual[4] = INITIALIZE_PAGE_TABLE;
page_table expected[4] = INITIALIZE_PAGE_TABLE;
ASSERT_EQ(guest_create_page_table((uintptr_t)actual, 4 * 4 << 10, &pte_off), ZX_OK);
// pml4
expected[0].entries[0] = PAGE_SIZE | X86_PTE_P | X86_PTE_RW;
// pdp
expected[1].entries[0] = PAGE_SIZE * 2 | X86_PTE_P | X86_PTE_RW;
// pd
expected[2].entries[0] = PAGE_SIZE * 3 | X86_PTE_P | X86_PTE_RW;
// pt
expected[3].entries[0] = PAGE_SIZE * 0 | X86_PTE_P | X86_PTE_RW;
expected[3].entries[1] = PAGE_SIZE * 1 | X86_PTE_P | X86_PTE_RW;
expected[3].entries[2] = PAGE_SIZE * 2 | X86_PTE_P | X86_PTE_RW;
expected[3].entries[3] = PAGE_SIZE * 3 | X86_PTE_P | X86_PTE_RW;
ASSERT_EPT_EQ(actual, expected, sizeof(actual));
ASSERT_EQ(pte_off, PAGE_SIZE * 4u);
END_TEST;
}
static bool page_table_mixed_pages(void) {
BEGIN_TEST;
uintptr_t pte_off;
page_table actual[4] = INITIALIZE_PAGE_TABLE;
page_table expected[4] = INITIALIZE_PAGE_TABLE;
ASSERT_EQ(guest_create_page_table((uintptr_t)actual, (2 << 20) + (4 << 10), &pte_off), ZX_OK);
// pml4
expected[0].entries[0] = PAGE_SIZE | X86_PTE_P | X86_PTE_RW;
// pdp
expected[1].entries[0] = PAGE_SIZE * 2 | X86_PTE_P | X86_PTE_RW;
// pd
expected[2].entries[0] = X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
expected[2].entries[1] = PAGE_SIZE * 3 | X86_PTE_P | X86_PTE_RW;
// pt
expected[3].entries[0] = (2 << 20) | X86_PTE_P | X86_PTE_RW;
ASSERT_EPT_EQ(actual, expected, sizeof(actual));
ASSERT_EQ(pte_off, PAGE_SIZE * 4u);
END_TEST;
}
// Create a page table for 2gb + 123mb + 32kb bytes.
static bool page_table_complex(void) {
BEGIN_TEST;
uintptr_t pte_off;
page_table actual[4] = INITIALIZE_PAGE_TABLE;
page_table expected[4] = INITIALIZE_PAGE_TABLE;
// 2gb + 123mb + 32kb of RAM. This breaks down as follows:
//
// PML4
// > 1 pointer to a PDPT
//
// PDPT
// > 2 direct-mapped 1gb regions
// > 1 ponter to a PD
//
// PD
// > 61 direct-mapped 2mb regions
// > 1 pointer to a PT
//
// PT
// > 264 mapped pages
ASSERT_EQ(guest_create_page_table((uintptr_t)actual, 0x87B08000, &pte_off), ZX_OK);
// pml4
expected[0].entries[0] = PAGE_SIZE | X86_PTE_P | X86_PTE_RW;
// pdp
expected[1].entries[0] = (0l << 30) | X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
expected[1].entries[1] = (1l << 30) | X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
expected[1].entries[2] = PAGE_SIZE * 2 | X86_PTE_P | X86_PTE_RW;
// pd - starts at 2GB
const uint64_t pdp_offset = 2l << 30;
for (int i = 0; i < 62; ++i) {
expected[2].entries[i] = (pdp_offset + (i << 21)) | X86_PTE_P | X86_PTE_RW | X86_PTE_PS;
}
expected[2].entries[61] = PAGE_SIZE * 3 | X86_PTE_P | X86_PTE_RW;
// pt - starts at 2GB + 122MB
const uint64_t pd_offset = pdp_offset + (61l << 21);
for (int i = 0; i < 264; ++i) {
expected[3].entries[i] = (pd_offset + (i << 12)) | X86_PTE_P | X86_PTE_RW;
}
ASSERT_EPT_EQ(actual, expected, sizeof(actual));
ASSERT_EQ(pte_off, PAGE_SIZE * 4u);
END_TEST;
}
BEGIN_TEST_CASE(extended_page_table)
RUN_TEST(page_table_1gb)
RUN_TEST(page_table_2mb)
RUN_TEST(page_table_4kb)
RUN_TEST(page_table_mixed_pages)
RUN_TEST(page_table_complex)
END_TEST_CASE(extended_page_table)