blob: 3d8ca04bb509af552997043595a3e8533c84c953 [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <sel4/sel4.h>
#include <vka/object.h>
#include <vka/capops.h>
#include "../helpers.h"
/* Arbitrarily start mapping 256mb into the virtual address range */
#define IOPT_MAP_BASE 0x10000000
/* None of these tests actually check that mappings succeed sensibly. This would
* require having a device that does DMA and making it perform operations.
* I consider this too much work, and am largely checking that none of these
* operations will cause the kernel to explode */
#define MAX_IOPT_DEPTH 8
/* The depth at which we expect the last PT to be at before 4K frame
* mappings. This is 'expected' depth as the actual depth can change
* per machine. Ideally this value should be exported from the kernel */
#define EXPECTED_PT_DEPTH 3
typedef struct iopt_cptrs {
int depth;
seL4_CPtr pts[MAX_IOPT_DEPTH];
} iopt_cptrs_t;
#define FAKE_PCI_DEVICE 0x216u
#define DOMAIN_ID 0xf
#ifdef CONFIG_IOMMU
static int map_iopt_from_iospace(env_t env, seL4_CPtr iospace, iopt_cptrs_t *pts, seL4_CPtr *frame)
{
int error = seL4_NoError;
pts->depth = 0;
/* Allocate and map page tables until we can map a frame */
*frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(*frame);
while (seL4_X86_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE) == seL4_FailedLookup) {
test_assert(pts->depth < MAX_IOPT_DEPTH);
pts->pts[pts->depth] = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(pts->pts[pts->depth]);
error = seL4_X86_IOPageTable_Map(pts->pts[pts->depth], iospace, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
pts->depth++;
}
test_eq(error, seL4_NoError);
return error;
}
static int map_iopt_set(env_t env, seL4_CPtr *iospace, iopt_cptrs_t *pts, seL4_CPtr *frame)
{
int error;
cspacepath_t master_path, iospace_path;
/* Allocate a random device ID that hopefully doesn't exist have any
* RMRR regions */
error = vka_cspace_alloc(&env->vka, iospace);
test_error_eq(error, seL4_NoError);
vka_cspace_make_path(&env->vka, *iospace, &iospace_path);
vka_cspace_make_path(&env->vka, env->io_space, &master_path);
error = vka_cnode_mint(&iospace_path, &master_path, seL4_AllRights, (DOMAIN_ID << 16) | FAKE_PCI_DEVICE);
test_eq(error, seL4_NoError);
error = map_iopt_from_iospace(env, *iospace, pts, frame);
return error;
}
static void delete_iospace(env_t env, seL4_CPtr iospace)
{
cspacepath_t path;
vka_cspace_make_path(&env->vka, iospace, &path);
vka_cnode_delete(&path);
}
static int test_iopt_basic_iopt(env_t env)
{
int error;
seL4_CPtr iospace, frame;
iopt_cptrs_t pts;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0001, "Testing basic IOPT mapping", test_iopt_basic_iopt, true)
static int
test_iopt_basic_map_unmap(env_t env)
{
int error;
int i;
iopt_cptrs_t pts;
seL4_CPtr iospace, frame;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
error = seL4_X86_Page_Unmap(frame);
test_eq(error, seL4_NoError);
for (i = pts.depth - 1; i >= 0; i--) {
error = seL4_X86_IOPageTable_Unmap(pts.pts[i]);
test_eq(error, seL4_NoError);
}
error = map_iopt_from_iospace(env, iospace, &pts, &frame);
test_eq(error, seL4_NoError);
for (i = 0; i < pts.depth; i++) {
error = seL4_X86_IOPageTable_Unmap(pts.pts[i]);
test_eq(error, seL4_NoError);
}
error = seL4_X86_Page_Unmap(frame);
test_eq(error, seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0002, "Test basic IOPT mapping then unmapping", test_iopt_basic_map_unmap, true)
static int
test_iopt_no_overlapping_4k(env_t env)
{
int error;
iopt_cptrs_t pts;
seL4_CPtr iospace, frame;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(frame);
error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0004, "Test IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true)
static int
test_iopt_map_remap_top_pt(env_t env)
{
int error;
iopt_cptrs_t pts;
seL4_CPtr iospace, frame, pt;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
/* unmap the top PT */
error = seL4_X86_IOPageTable_Unmap(pts.pts[EXPECTED_PT_DEPTH]);
test_eq(error, seL4_NoError);
/* now map it back in */
error = seL4_X86_IOPageTable_Map(pts.pts[EXPECTED_PT_DEPTH], iospace, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
/* it should retain its old mappings, and mapping in a new PT should fail */
pt = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0008, "Test IOPT map and remap top PT", test_iopt_map_remap_top_pt, true)
static int
test_iopt_no_overlapping_pt(env_t env)
{
int error;
iopt_cptrs_t pts;
seL4_CPtr iospace, frame, pt;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
/* Mapping in a new PT should fail */
pt = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0009, "Test iopt no overlapping PT", test_iopt_no_overlapping_pt, true)
static int
test_iopt_map_remap_pt(env_t env)
{
int error;
iopt_cptrs_t pts;
seL4_CPtr iospace, frame;
error = map_iopt_set(env, &iospace, &pts, &frame);
test_eq(error, seL4_NoError);
/* unmap the pt */
error = seL4_X86_IOPageTable_Unmap(pts.pts[pts.depth - 1]);
test_eq(error, seL4_NoError);
/* now map it back in */
error = seL4_X86_IOPageTable_Map(pts.pts[pts.depth - 1], iospace, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
/* it should retain its old mappings, and mapping in a new frame should fail */
frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(frame);
error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
return sel4test_get_result();
}
DEFINE_TEST(IOPT0011, "Test IOPT map and remap PT", test_iopt_map_remap_pt, true)
#endif /* CONFIG_IOMMU */
#ifdef CONFIG_TK1_SMMU
/* tests for ARM SystemMMU */
#define IOPT_MAP_BASE 0x10000000
static int
map_iopt_from_iospace(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt, seL4_CPtr *frame)
{
int error;
*frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(*frame);
error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
if (error == seL4_FailedLookup) {
*iopt = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(*iopt);
error = seL4_ARM_IOPageTable_Map(*iopt, iospace, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
}
test_eq(error, seL4_NoError);
return error;
}
static int map_iopt_set(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt_cptr, seL4_CPtr *frame)
{
int error = map_iopt_from_iospace(env, iospace, iopt_cptr, frame);
return error;
}
static void delete_iospace(env_t env, seL4_CPtr iospace)
{
cspacepath_t path;
vka_cspace_make_path(&env->vka, iospace, &path);
vka_cnode_delete(&path);
}
static int test_iopt_basic_iopt(env_t env)
{
int error;
seL4_CPtr pt = 0;
seL4_CPtr frame = 0;
seL4_SlotRegion caps = env->io_space_caps;
int cap_count = caps.end - caps.start + 1;
seL4_CPtr cap = caps.start;
for (int i = 0; i < cap_count; i++) {
error = map_iopt_set(env, cap + i, &pt, &frame);
test_eq(error, seL4_NoError);
delete_iospace(env, cap + i);
}
return sel4test_get_result();
}
DEFINE_TEST(IOPT0001, "Testing basic ARM IOPT mapping", test_iopt_basic_iopt, true);
static int test_iopt_basic_map_unmap(env_t env)
{
int error;
int i;
seL4_CPtr iospace, pt, frame;
seL4_SlotRegion caps = env->io_space_caps;
int cap_count = caps.end - caps.start + 1;
for (i = 0; i < cap_count; i++) {
iospace = caps.start + i;
error = map_iopt_set(env, iospace, &pt, &frame);
test_eq(error, seL4_NoError);
error = seL4_ARM_Page_Unmap(frame);
test_eq(error, seL4_NoError);
error = seL4_ARM_IOPageTable_Unmap(pt);
test_eq(error, seL4_NoError);
error = map_iopt_from_iospace(env, iospace, &pt, &frame);
test_eq(error, seL4_NoError);
error = seL4_ARM_IOPageTable_Unmap(pt);
test_eq(error, seL4_NoError);
error = seL4_ARM_Page_Unmap(frame);
test_eq(error, seL4_NoError);
delete_iospace(env, iospace);
}
return sel4test_get_result();
}
DEFINE_TEST(IOPT0002, "Test basic ARM IOPT mapping then unmapping", test_iopt_basic_map_unmap, true)
static int
test_iopt_no_overlapping_4k(env_t env)
{
int error;
int i;
seL4_CPtr iospace, pt, frame;
seL4_SlotRegion caps = env->io_space_caps;
int cap_count = caps.end - caps.start + 1;
for (i = 0; i < cap_count; i++) {
iospace = caps.start + i;
error = map_iopt_set(env, iospace, &pt, &frame);
test_eq(error, seL4_NoError);
frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
test_assert(frame);
/* mapping in a new frame should fail */
error = seL4_ARM_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
}
return sel4test_get_result();
}
DEFINE_TEST(IOPT0004, "Test ARM IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true)
static int
test_iopt_map_remap_pt(env_t env)
{
int error;
int i;
seL4_CPtr iospace, pt, frame;
seL4_SlotRegion caps = env->io_space_caps;
int cap_count = caps.end - caps.start + 1;
for (i = 0; i < cap_count; i++) {
iospace = caps.start + i;
error = map_iopt_set(env, iospace, &pt, &frame);
test_eq(error, seL4_NoError);
/* unmap the PT */
error = seL4_ARM_IOPageTable_Unmap(pt);
test_eq(error, seL4_NoError);
/* now map it back in */
error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
test_eq(error, seL4_NoError);
/* it should retain its old mappings, and mapping in a new PT should fail */
pt = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
}
return sel4test_get_result();
}
DEFINE_TEST(IOPT0008, "Test ARM IOPT map and remap PT", test_iopt_map_remap_pt, true)
static int
test_iopt_no_overlapping_pt(env_t env)
{
int error;
int i;
seL4_CPtr iospace, pt, frame;
seL4_SlotRegion caps = env->io_space_caps;
int cap_count = caps.end - caps.start + 1;
for (i = 0; i < cap_count; i++) {
iospace = caps.start + i;
error = map_iopt_set(env, iospace, &pt, &frame);
test_eq(error, seL4_NoError);
/* Mapping in a new PT should fail */
pt = vka_alloc_io_page_table_leaky(&env->vka);
test_assert(pt);
error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
test_assert(error != seL4_NoError);
delete_iospace(env, iospace);
}
return sel4test_get_result();
}
DEFINE_TEST(IOPT0009, "Test ARM iopt no overlapping PT", test_iopt_no_overlapping_pt, true)
#endif