blob: 8b1fae3a788bc61c7b3ddaed1a58f40e1ed40e60 [file] [log] [blame] [edit]
/*
* 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 <utils/util.h>
#include "../helpers.h"
#include "frame_type.h"
#if defined(CONFIG_ARCH_ARM)
static int fill_memory(seL4_Word addr, seL4_Word size_bytes)
{
test_assert(IS_ALIGNED(addr, sizeof(seL4_Word)));
test_assert(IS_ALIGNED(size_bytes, sizeof(seL4_Word)));
seL4_Word *p = (void *)addr;
seL4_Word size_words = size_bytes / sizeof(seL4_Word);
while (size_words--) {
*p++ = size_words ^ 0xbeefcafe;
}
return SUCCESS;
}
static int __attribute__((warn_unused_result))
check_memory(seL4_Word addr, seL4_Word size_bytes)
{
test_assert(IS_ALIGNED(addr, sizeof(seL4_Word)));
test_assert(IS_ALIGNED(size_bytes, sizeof(seL4_Word)));
seL4_Word *p = (void *)addr;
seL4_Word size_words = size_bytes / sizeof(seL4_Word);
while (size_words--) {
if (*p++ != (size_words ^ 0xbeefcafe)) {
return 0;
}
}
return 1;
}
#if defined(CONFIG_ARCH_AARCH32)
#define LPAGE_SIZE (1 << (vka_get_object_size(seL4_ARM_LargePageObject, 0)))
#define SECT_SIZE (1 << (vka_get_object_size(seL4_ARM_SectionObject, 0)))
#define SUPSECT_SIZE (1 << (vka_get_object_size(seL4_ARM_SuperSectionObject, 0)))
/* Assumes caps can be mapped in at vaddr (= [vaddr,vaddr + 3*size) */
static int do_test_pagetable_tlbflush_on_vaddr_reuse(env_t env, seL4_CPtr cap1, seL4_CPtr cap2, seL4_Word vstart,
seL4_Word size)
{
int error;
seL4_Word vaddr;
volatile seL4_Word *vptr = NULL;
/* map, touch page 1 */
vaddr = vstart;
vptr = (seL4_Word *)vaddr;
error = seL4_ARM_Page_Map(cap1, env->page_directory,
vaddr, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
vptr[0] = 1;
error = seL4_ARM_Page_Unmap(cap1);
test_error_eq(error, 0);
/* map, touch page 2 */
vaddr = vstart + size;
vptr = (seL4_Word *)vaddr;
error = seL4_ARM_Page_Map(cap2, env->page_directory,
vaddr, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
vptr[0] = 2;
error = seL4_ARM_Page_Unmap(cap2);
test_error_eq(error, 0);
/* Test TLB */
vaddr = vstart + 2 * size;
vptr = (seL4_Word *)vaddr;
error = seL4_ARM_Page_Map(cap1, env->page_directory,
vaddr, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
test_check(vptr[0] == 1);
error = seL4_ARM_Page_Map(cap2, env->page_directory,
vaddr, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
test_check(vptr[0] == 2 || !"TLB contains stale entry");
/* clean up */
error = seL4_ARM_Page_Unmap(cap1);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(cap2);
test_error_eq(error, 0);
return sel4test_get_result();
}
static int test_pagetable_arm(env_t env)
{
int error;
/* Grab some free vspace big enough to hold a couple of supersections. */
seL4_Word vstart;
reservation_t reserve = vspace_reserve_range(&env->vspace, SUPSECT_SIZE * 4,
seL4_AllRights, 1, (void **) &vstart);
vstart = ALIGN_UP(vstart, SUPSECT_SIZE * 2);
/* Create us some frames to play with. */
seL4_CPtr small_page = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
seL4_CPtr small_page2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
seL4_CPtr large_page = vka_alloc_object_leaky(&env->vka, seL4_ARM_LargePageObject, 0);
seL4_CPtr section = vka_alloc_object_leaky(&env->vka, seL4_ARM_SectionObject, 0);
seL4_CPtr supersection = vka_alloc_object_leaky(&env->vka, seL4_ARM_SuperSectionObject, 0);
test_assert(small_page != 0);
test_assert(small_page2 != 0);
test_assert(large_page != 0);
test_assert(section != 0);
test_assert(supersection != 0);
/* Also create a pagetable to map the pages into. */
seL4_CPtr pt = vka_alloc_page_table_leaky(&env->vka);
/* Check we can't map the supersection in at an address it's not aligned
* to. */
test_assert(supersection != 0);
error = seL4_ARM_Page_Map(supersection, env->page_directory,
vstart + SUPSECT_SIZE / 2, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_AlignmentError);
/* Check we can map it in somewhere correctly aligned. */
error = seL4_ARM_Page_Map(supersection, env->page_directory,
vstart + SUPSECT_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Now fill it with stuff to check later. */
/* TDDO fx these constants */
error = fill_memory(vstart + SUPSECT_SIZE, SUPSECT_SIZE);
test_assert(error == SUCCESS);
/* Check we now can't map a section over the top. */
error = seL4_ARM_Page_Map(section, env->page_directory,
vstart + SUPSECT_SIZE + SUPSECT_SIZE / 2, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_DeleteFirst);
/* Unmapping the section shouldn't do anything. */
error = seL4_ARM_Page_Unmap(section);
test_error_eq(error, 0);
/* Unmap supersection and try again. */
error = seL4_ARM_Page_Unmap(supersection);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(section, env->page_directory,
vstart + SUPSECT_SIZE + SUPSECT_SIZE / 2, seL4_AllRights,
seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
fill_memory(vstart + SUPSECT_SIZE + SUPSECT_SIZE / 2, SECT_SIZE);
/* Now that a section is there, see if we can map a supersection over the
* top. */
error = seL4_ARM_Page_Map(supersection, env->page_directory,
vstart + SUPSECT_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_DeleteFirst);
if (!check_memory(vstart + SUPSECT_SIZE + SUPSECT_SIZE / 2, SECT_SIZE)) {
return FAILURE;
}
/* Unmap the section, leaving nothing mapped. */
error = seL4_ARM_Page_Unmap(section);
test_error_eq(error, 0);
/* Now, try mapping in the supersection into two places. */
error = seL4_ARM_Page_Map(supersection, env->page_directory,
vstart, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(supersection, env->page_directory,
vstart + SUPSECT_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_InvalidArgument);
/* Now check what we'd written earlier is still there. */
test_assert(check_memory(vstart, SUPSECT_SIZE));
/* Try mapping the section into two places. */
error = seL4_ARM_Page_Map(section, env->page_directory,
vstart + 2 * SUPSECT_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(section, env->page_directory,
vstart + SUPSECT_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_InvalidArgument);
/* Unmap everything again. */
error = seL4_ARM_Page_Unmap(section);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(supersection);
test_error_eq(error, 0);
/* Map a large page somewhere with no pagetable. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_FailedLookup);
/* Map a pagetable at an unaligned address. Oddly enough, this will succeed
* as the kernel silently truncates the address to the nearest correct
* boundary. */
error = seL4_ARM_PageTable_Map(pt, env->page_directory,
vstart + SECT_SIZE + 10, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Map the large page in at an unaligned address. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + SECT_SIZE + LPAGE_SIZE / 2,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_AlignmentError);
/* Map the large page in at an aligned address. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + SECT_SIZE + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Map it again, to a different vaddr, and it should fail. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + SECT_SIZE + 2 * LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_InvalidArgument);
/* Fill it with more stuff to check. */
fill_memory(vstart + SECT_SIZE + LPAGE_SIZE, LPAGE_SIZE);
/* Try mapping a small page over the top of it. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart + SECT_SIZE + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_DeleteFirst);
/* Try mapping a small page elsewhere useful. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart + SECT_SIZE + 3 * LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Fill the small page with useful data too. */
fill_memory(vstart + SECT_SIZE + 3 * LPAGE_SIZE, PAGE_SIZE_4K);
/* Pull the plug on the page table. Apparently a recycle isn't good enough.
* Get another pagetable! */
error = seL4_CNode_Delete(env->cspace_root, pt, seL4_WordBits);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(small_page);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
pt = vka_alloc_page_table_leaky(&env->vka);
/* Map the pagetable somewhere new. */
error = seL4_ARM_PageTable_Map(pt, env->page_directory,
vstart, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Map our small page and large page back in. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart + PAGE_SIZE_4K,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Check their contents. */
test_assert(check_memory(vstart + PAGE_SIZE_4K, PAGE_SIZE_4K));
test_assert(check_memory(vstart + LPAGE_SIZE, LPAGE_SIZE));
/* Now unmap the small page */
error = seL4_ARM_Page_Unmap(small_page);
test_error_eq(error, 0);
/* Unmap the large page. */
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
/* Now map the large page where the small page was. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Now check the contents of the large page. */
test_assert(check_memory(vstart, LPAGE_SIZE));
/* Map the small page elsewhere. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Now unmap the small page */
error = seL4_ARM_Page_Unmap(small_page);
test_error_eq(error, 0);
/* Map a different small page in its place. */
error = seL4_ARM_Page_Map(small_page2, env->page_directory,
vstart + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Fill it in with stuff. */
fill_memory(vstart + LPAGE_SIZE, PAGE_SIZE_4K);
vspace_free_reservation(&env->vspace, reserve);
return sel4test_get_result();
}
DEFINE_TEST(PT0001, "Fun with page tables on ARM", test_pagetable_arm, true)
static int
test_pagetable_tlbflush_on_vaddr_reuse(env_t env)
{
int error;
int result = SUCCESS;
/* Grab some free vspace big enough to hold a couple of supersections. */
void *vstart;
reservation_t reserve = vspace_reserve_range_aligned(&env->vspace, VSPACE_RV_SIZE, VSPACE_RV_ALIGN_BITS,
seL4_AllRights, 1, &vstart);
test_assert(reserve.res);
seL4_CPtr cap1, cap2;
/* Create us some frames to play with. */
/* Also create a pagetable to map the pages into. */
seL4_CPtr pt = vka_alloc_page_table_leaky(&env->vka);
/* supersection */
cap1 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SuperSectionObject, 0);
cap2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SuperSectionObject, 0);
if (do_test_pagetable_tlbflush_on_vaddr_reuse(env, cap1, cap2, (uintptr_t)vstart, SUPSECT_SIZE) == FAILURE) {
result = FAILURE;
}
/* section */
cap1 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SectionObject, 0);
cap2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SectionObject, 0);
if (do_test_pagetable_tlbflush_on_vaddr_reuse(env, cap1, cap2, (uintptr_t)vstart, SUPSECT_SIZE) == FAILURE) {
result = FAILURE;
}
/* map a PT for smaller page objects */
error = seL4_ARM_PageTable_Map(pt, env->page_directory,
(seL4_Word)vstart, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Large page */
cap1 = vka_alloc_object_leaky(&env->vka, seL4_ARM_LargePageObject, 0);
cap2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_LargePageObject, 0);
if (do_test_pagetable_tlbflush_on_vaddr_reuse(env, cap1, cap2, (uintptr_t)vstart, LPAGE_SIZE) == FAILURE) {
result = FAILURE;
}
/* small page */
cap1 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
cap2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
if (do_test_pagetable_tlbflush_on_vaddr_reuse(env, cap1, cap2, (uintptr_t)vstart, PAGE_SIZE_4K) == FAILURE) {
result = FAILURE;
}
return result;
}
DEFINE_TEST(PT0002, "Reusing virtual addresses flushes TLB", test_pagetable_tlbflush_on_vaddr_reuse, true)
#elif defined(CONFIG_ARCH_AARCH64)
#define LPAGE_SIZE (1 << (vka_get_object_size(seL4_ARM_LargePageObject, 0)))
static int
test_pagetable_arm(env_t env)
{
int error;
/* Grab some free vspace big enough to hold a couple of large pages. */
seL4_Word vstart;
reservation_t reserve = vspace_reserve_range(&env->vspace, LPAGE_SIZE * 4,
seL4_AllRights, 1, (void **) &vstart);
vstart = ALIGN_UP(vstart, LPAGE_SIZE * 2);
/* Create us some frames to play with. */
seL4_CPtr small_page = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
seL4_CPtr small_page2 = vka_alloc_object_leaky(&env->vka, seL4_ARM_SmallPageObject, 0);
seL4_CPtr large_page = vka_alloc_object_leaky(&env->vka, seL4_ARM_LargePageObject, 0);
test_assert(small_page != 0);
test_assert(small_page2 != 0);
test_assert(large_page != 0);
/* Also create a pagetable to map the pages into. */
seL4_CPtr pt = vka_alloc_page_table_leaky(&env->vka);
/* Check we can't map the large page in at an address it's not aligned to. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE / 2, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_AlignmentError);
/* Check we can map it in somewhere correctly aligned. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Unmap large page and try again. */
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
fill_memory(vstart + LPAGE_SIZE, LPAGE_SIZE);
/* Unmap the large page, leaving nothing mapped. */
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
/* Now, try mapping in the large page into two places. */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Trying to remap to a different vaddr */
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_InvalidArgument);
/* Now check what we'd written earlier is still there. */
test_assert(check_memory(vstart, LPAGE_SIZE));
/* Unmap everything again. */
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
/* Map a small page somewhere with no pagetable. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart, seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, seL4_FailedLookup);
/* Map a pagetable at an unaligned address. Oddly enough, this will succeed
* as the kernel silently truncates the address to the nearest correct
* boundary. */
error = seL4_ARM_PageTable_Map(pt, env->page_directory,
vstart + LPAGE_SIZE + 10, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Try mapping a small page. */
error = seL4_ARM_Page_Map(small_page, env->page_directory, vstart + LPAGE_SIZE + PAGE_SIZE_4K,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Fill the small page with useful data too. */
fill_memory(vstart + LPAGE_SIZE + PAGE_SIZE_4K, PAGE_SIZE_4K);
/* Pull the plug on the page table. Apparently a recycle isn't good enough.
* Get another pagetable! */
error = seL4_CNode_Delete(env->cspace_root, pt, seL4_WordBits);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(small_page);
test_error_eq(error, 0);
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
pt = vka_alloc_page_table_leaky(&env->vka);
/* Map the pagetable somewhere new. */
error = seL4_ARM_PageTable_Map(pt, env->page_directory,
vstart, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Map our small page and large page back in. */
error = seL4_ARM_Page_Map(small_page, env->page_directory,
vstart + PAGE_SIZE_4K,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
error = seL4_ARM_Page_Map(large_page, env->page_directory,
vstart + LPAGE_SIZE,
seL4_AllRights, seL4_ARM_Default_VMAttributes);
test_error_eq(error, 0);
/* Check their contents. */
test_assert(check_memory(vstart + PAGE_SIZE_4K, PAGE_SIZE_4K));
test_assert(check_memory(vstart + LPAGE_SIZE, LPAGE_SIZE));
/* Now unmap the small page */
error = seL4_ARM_Page_Unmap(small_page);
test_error_eq(error, 0);
/* Unmap the large page. */
error = seL4_ARM_Page_Unmap(large_page);
test_error_eq(error, 0);
vspace_free_reservation(&env->vspace, reserve);
return sel4test_get_result();
}
DEFINE_TEST(PT0001, "Fun with page tables on ARM", test_pagetable_arm, true)
#endif /* CONFIG_ARCH_AARCHxx */
#endif /* CONFIG_ARCH_ARM */