blob: 937d4923d69ef3a121c4b63f70accd0fffd706bd [file]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <autoconf.h>
#include <assert.h>
#include <sel4/sel4.h>
#include <vka/object.h>
#include <vka/capops.h>
#include "../../../test.h"
#include "../../../helpers.h"
/* Arbitrarily start mapping 128mb into the virtual address range */
#define EPT_MAP_BASE 0x8000000
#define OFFSET_4MB (BIT(22))
#define OFFSET_2MB (OFFSET_4MB >> 1)
/* None of these tests actually check that mappings succeeded. This would require
* constructing a vcpu, creating a thread and having some code compiled to run
* inside a vt-x instance. I consider this too much work, and am largely checking
* that none of these operations will cause the kernel to explode */
static int map_ept_from_pdpt(env_t env, seL4_CPtr pml4, seL4_CPtr pdpt, seL4_CPtr *pd, seL4_CPtr *pt, seL4_CPtr *frame)
{
int error = 0;
#ifdef CONFIG_VTX
*pd = vka_alloc_ept_page_directory_leaky(&env->vka);
test_assert(*pd);
*pt = vka_alloc_ept_page_table_leaky(&env->vka);
test_assert(*pt);
*frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(*frame);
error = seL4_X86_EPTPD_Map(*pd, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPT_Map(*pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_MapEPT(*frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
#endif /* CONFIG_VTX */
return error;
}
static int map_ept_set(env_t env, seL4_CPtr *pml4, seL4_CPtr *pdpt, seL4_CPtr *pd, seL4_CPtr *pt, seL4_CPtr *frame)
{
int error = 0;
#ifdef CONFIG_VTX
*pml4 = vka_alloc_ept_pml4_leaky(&env->vka);
test_assert(*pml4);
*pdpt = vka_alloc_ept_page_directory_pointer_table_leaky(&env->vka);
test_assert(*pdpt);
error = seL4_X86_ASIDPool_Assign(env->asid_pool, *pml4);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPDPT_Map(*pdpt, *pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
error = map_ept_from_pdpt(env, *pml4, *pdpt, pd, pt, frame);
#endif /* CONFIG_VTX */
return error;
}
static int map_ept_set_large_from_pdpt(env_t env, seL4_CPtr pml4, seL4_CPtr pdpt, seL4_CPtr *pd, seL4_CPtr *frame)
{
int error = 0;
#ifdef CONFIG_VTX
*pd = vka_alloc_ept_page_directory_leaky(&env->vka);
test_assert(*pd);
*frame = vka_alloc_frame_leaky(&env->vka, seL4_LargePageBits);
test_assert(*frame);
error = seL4_X86_EPTPD_Map(*pd, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
if (error != seL4_NoError) {
return error;
}
error = seL4_X86_Page_MapEPT(*frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
if (error != seL4_NoError) {
return error;
}
#endif /* CONFIG_VTX */
return error;
}
static int map_ept_set_large(env_t env, seL4_CPtr *pml4, seL4_CPtr *pdpt, seL4_CPtr *pd, seL4_CPtr *frame)
{
int error = 0;
#ifdef CONFIG_VTX
*pml4 = vka_alloc_ept_pml4_leaky(&env->vka);
test_assert(*pml4);
*pdpt = vka_alloc_ept_page_directory_pointer_table_leaky(&env->vka);
test_assert(*pdpt);
error = seL4_X86_ASIDPool_Assign(env->asid_pool, *pml4);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPDPT_Map(*pdpt, *pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
if (error != seL4_NoError) {
return error;
}
error = map_ept_set_large_from_pdpt(env, *pml4, *pdpt, pd, frame);
#endif /* CONFIG_VTX */
return error;
}
static int test_ept_basic_ept(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0001, "Testing basic EPT mapping", test_ept_basic_ept, config_set(CONFIG_VTX))
#ifndef CONFIG_VTX
#define seL4_X86_EPTPT_Unmap(x) 0
#define seL4_X86_EPTPD_Unmap(x) 0
#endif /* CONFIG_VTX */
static int
test_ept_basic_map_unmap(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPT_Unmap(pt);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
error = map_ept_from_pdpt(env, pml4, pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPT_Unmap(pt);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0002, "Test basic EPT mapping then unmapping", test_ept_basic_map_unmap,
config_set(CONFIG_VTX))
static int
test_ept_basic_map_unmap_large(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, frame;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
error = map_ept_set_large_from_pdpt(env, pml4, pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0003, "Test basic EPT mapping then unmapping of large frame", test_ept_basic_map_unmap_large,
config_set(CONFIG_VTX))
static int
test_ept_regression_1(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, frame;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(frame);
test_assert(error != seL4_NoError);
error = map_ept_set_large_from_pdpt(env, pml4, pdpt, &pd, &frame);
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT1001, "EPT Regression: Unmap large frame then invoke EPT PD unmap on frame", test_ept_regression_1,
config_set(CONFIG_VTX))
static int
test_ept_regression_2(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, frame;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
error = map_ept_set_large_from_pdpt(env, pml4, pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_EPTPD_Unmap(frame);
test_assert(error != seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT1002, "EPT Regression: Invoke EPT PD Unmap on large frame", test_ept_regression_2,
config_set(CONFIG_VTX))
static int
test_ept_no_overlapping_4k(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(frame);
#ifdef CONFIG_VTX
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
#endif /* CONFIG_VTX */
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0004, "Test EPT cannot map overlapping 4k pages", test_ept_no_overlapping_4k,
config_set(CONFIG_VTX))
static int
test_ept_no_overlapping_large(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, frame;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
frame = vka_alloc_frame_leaky(&env->vka, seL4_LargePageBits);
test_assert(frame);
#ifdef CONFIG_VTX
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
#endif /* CONFIG_VTX */
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0005, "Test EPT cannot map overlapping large pages", test_ept_no_overlapping_large,
config_set(CONFIG_VTX))
static int
test_ept_aligned_4m(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, frame, frame2;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
frame2 = vka_alloc_frame_leaky(&env->vka, PAGE_BITS_4M);
test_assert(frame2);
/* Try and map a page at +2m */
#ifdef CONFIG_VTX
error = seL4_X86_Page_MapEPT(frame2, pml4, EPT_MAP_BASE + OFFSET_2MB, seL4_AllRights, seL4_X86_Default_VMAttributes);
test_assert(error != seL4_NoError);
/* But we should be able to map it at +4m */
error = seL4_X86_Page_MapEPT(frame2, pml4, EPT_MAP_BASE + OFFSET_4MB, seL4_AllRights, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
/* Unmap them both */
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame2);
test_error_eq(error, seL4_NoError);
/* Now map the first one at +2m, which should just flat out fail */
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE + OFFSET_2MB, seL4_AllRights, seL4_X86_Default_VMAttributes);
#endif /* CONFIG_VTX */
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0006, "Test EPT 4M mappings must be 4M aligned and cannot overlap", test_ept_aligned_4m,
config_set(CONFIG_VTX) &&config_set(CONFIG_ARCH_IA32))
static int
test_ept_no_overlapping_pt_4m(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set_large(env, &pml4, &pdpt, &pd, &frame);
test_error_eq(error, seL4_NoError);
#ifdef CONFIG_VTX
pt = vka_alloc_ept_page_table_leaky(&env->vka);
test_assert(pt);
/* now try and map a PT at both 2m entries */
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_assert(error != seL4_NoError);
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE + OFFSET_2MB, seL4_X86_Default_VMAttributes);
test_assert(error != seL4_NoError);
/* unmap PT and frame */
error = seL4_X86_EPTPT_Unmap(pt);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_error_eq(error, seL4_NoError);
/* put the page table in this time */
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
/* now try the frame */
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
test_assert(error != seL4_NoError);
/* unmap the PT */
error = seL4_X86_EPTPT_Unmap(pt);
test_error_eq(error, seL4_NoError);
/* put the PT and the +2m location, then try the frame */
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE + OFFSET_2MB, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
test_assert(error != seL4_NoError);
#endif
return sel4test_get_result();
}
DEFINE_TEST(EPT0007, "Test EPT 4m frame and PT cannot overlap", test_ept_no_overlapping_pt_4m,
config_set(CONFIG_VTX) &&config_set(CONFIG_ARCH_IA32))
static int
test_ept_map_remap_pd(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
#ifdef CONFIG_VTX
/* unmap the pd */
error = seL4_X86_EPTPD_Unmap(pd);
test_error_eq(error, seL4_NoError);
/* now map it back in */
error = seL4_X86_EPTPD_Map(pd, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
/* should be able to map in a new PT */
pt = vka_alloc_ept_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
#endif /* CONFIG_VTX */
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0008, "Test EPT map and remap PD", test_ept_map_remap_pd, config_set(CONFIG_VTX))
static int
test_ept_no_overlapping_pt(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
/* Mapping in a new PT should fail */
#ifdef CONFIG_VTX
pt = vka_alloc_ept_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
#endif
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0009, "Test EPT no overlapping PT", test_ept_no_overlapping_pt, config_set(CONFIG_VTX))
static int
test_ept_no_overlapping_pd(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
/* Mapping in a new PT should fail */
#ifdef CONFIG_VTX
pd = vka_alloc_ept_page_directory_leaky(&env->vka);
test_assert(pd);
error = seL4_X86_EPTPD_Map(pd, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
#endif
test_assert(error != seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0010, "Test EPT no overlapping PD", test_ept_no_overlapping_pd, config_set(CONFIG_VTX))
static int
test_ept_map_remap_pt(env_t env)
{
int error;
seL4_CPtr pml4, pdpt, pd, pt, frame;
error = map_ept_set(env, &pml4, &pdpt, &pd, &pt, &frame);
test_error_eq(error, seL4_NoError);
#ifdef CONFIG_VTX
/* unmap the pt */
error = seL4_X86_EPTPT_Unmap(pt);
test_error_eq(error, seL4_NoError);
/* now map it back in */
error = seL4_X86_EPTPT_Map(pt, pml4, EPT_MAP_BASE, seL4_X86_Default_VMAttributes);
test_error_eq(error, seL4_NoError);
/* should be able to map in a frame now */
frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(frame);
error = seL4_X86_Page_MapEPT(frame, pml4, EPT_MAP_BASE, seL4_AllRights, seL4_X86_Default_VMAttributes);
#endif
test_error_eq(error, seL4_NoError);
return sel4test_get_result();
}
DEFINE_TEST(EPT0011, "Test EPT map and remap PT", test_ept_map_remap_pt, config_set(CONFIG_VTX))