1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <assert.h> 14 15#include <sel4/sel4.h> 16#include <vka/object.h> 17#include <vka/capops.h> 18 19#include "../helpers.h" 20 21/* Arbitrarily start mapping 256mb into the virtual address range */ 22#define IOPT_MAP_BASE 0x10000000 23 24/* None of these tests actually check that mappings succeed sensibly. This would 25 * require having a device that does DMA and making it perform operations. 26 * I consider this too much work, and am largely checking that none of these 27 * operations will cause the kernel to explode */ 28 29#define MAX_IOPT_DEPTH 8 30 31/* The depth at which we expect the last PT to be at before 4K frame 32 * mappings. This is 'expected' depth as the actual depth can change 33 * per machine. Ideally this value should be exported from the kernel */ 34#define EXPECTED_PT_DEPTH 3 35 36typedef struct iopt_cptrs { 37 int depth; 38 seL4_CPtr pts[MAX_IOPT_DEPTH]; 39} iopt_cptrs_t; 40 41#define FAKE_PCI_DEVICE 0x216u 42#define DOMAIN_ID 0xf 43 44#ifdef CONFIG_IOMMU 45 46static int map_iopt_from_iospace(env_t env, seL4_CPtr iospace, iopt_cptrs_t *pts, seL4_CPtr *frame) 47{ 48 int error = seL4_NoError; 49 50 pts->depth = 0; 51 /* Allocate and map page tables until we can map a frame */ 52 *frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 53 test_assert(*frame); 54 55 while (seL4_X86_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE) == seL4_FailedLookup) { 56 test_assert(pts->depth < MAX_IOPT_DEPTH); 57 pts->pts[pts->depth] = vka_alloc_io_page_table_leaky(&env->vka); 58 test_assert(pts->pts[pts->depth]); 59 error = seL4_X86_IOPageTable_Map(pts->pts[pts->depth], iospace, IOPT_MAP_BASE); 60 test_eq(error, seL4_NoError); 61 pts->depth++; 62 } 63 test_eq(error, seL4_NoError); 64 65 return error; 66} 67 68static int map_iopt_set(env_t env, seL4_CPtr *iospace, iopt_cptrs_t *pts, seL4_CPtr *frame) 69{ 70 int error; 71 cspacepath_t master_path, iospace_path; 72 73 /* Allocate a random device ID that hopefully doesn't exist have any 74 * RMRR regions */ 75 error = vka_cspace_alloc(&env->vka, iospace); 76 test_error_eq(error, seL4_NoError); 77 vka_cspace_make_path(&env->vka, *iospace, &iospace_path); 78 vka_cspace_make_path(&env->vka, env->io_space, &master_path); 79 error = vka_cnode_mint(&iospace_path, &master_path, seL4_AllRights, (DOMAIN_ID << 16) | FAKE_PCI_DEVICE); 80 test_eq(error, seL4_NoError); 81 82 error = map_iopt_from_iospace(env, *iospace, pts, frame); 83 84 return error; 85} 86 87static void delete_iospace(env_t env, seL4_CPtr iospace) 88{ 89 cspacepath_t path; 90 vka_cspace_make_path(&env->vka, iospace, &path); 91 vka_cnode_delete(&path); 92} 93 94static int test_iopt_basic_iopt(env_t env) 95{ 96 int error; 97 seL4_CPtr iospace, frame; 98 iopt_cptrs_t pts; 99 error = map_iopt_set(env, &iospace, &pts, &frame); 100 test_eq(error, seL4_NoError); 101 102 delete_iospace(env, iospace); 103 return sel4test_get_result(); 104} 105DEFINE_TEST(IOPT0001, "Testing basic IOPT mapping", test_iopt_basic_iopt, true) 106 107static int 108test_iopt_basic_map_unmap(env_t env) 109{ 110 int error; 111 int i; 112 iopt_cptrs_t pts; 113 seL4_CPtr iospace, frame; 114 error = map_iopt_set(env, &iospace, &pts, &frame); 115 test_eq(error, seL4_NoError); 116 117 error = seL4_X86_Page_Unmap(frame); 118 test_eq(error, seL4_NoError); 119 for (i = pts.depth - 1; i >= 0; i--) { 120 error = seL4_X86_IOPageTable_Unmap(pts.pts[i]); 121 test_eq(error, seL4_NoError); 122 } 123 124 error = map_iopt_from_iospace(env, iospace, &pts, &frame); 125 test_eq(error, seL4_NoError); 126 127 for (i = 0; i < pts.depth; i++) { 128 error = seL4_X86_IOPageTable_Unmap(pts.pts[i]); 129 test_eq(error, seL4_NoError); 130 } 131 132 error = seL4_X86_Page_Unmap(frame); 133 test_eq(error, seL4_NoError); 134 135 delete_iospace(env, iospace); 136 return sel4test_get_result(); 137} 138DEFINE_TEST(IOPT0002, "Test basic IOPT mapping then unmapping", test_iopt_basic_map_unmap, true) 139 140static int 141test_iopt_no_overlapping_4k(env_t env) 142{ 143 int error; 144 iopt_cptrs_t pts; 145 seL4_CPtr iospace, frame; 146 error = map_iopt_set(env, &iospace, &pts, &frame); 147 test_eq(error, seL4_NoError); 148 149 frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 150 test_assert(frame); 151 error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE); 152 test_assert(error != seL4_NoError); 153 154 delete_iospace(env, iospace); 155 return sel4test_get_result(); 156} 157DEFINE_TEST(IOPT0004, "Test IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true) 158 159static int 160test_iopt_map_remap_top_pt(env_t env) 161{ 162 int error; 163 iopt_cptrs_t pts; 164 seL4_CPtr iospace, frame, pt; 165 error = map_iopt_set(env, &iospace, &pts, &frame); 166 test_eq(error, seL4_NoError); 167 168 /* unmap the top PT */ 169 error = seL4_X86_IOPageTable_Unmap(pts.pts[EXPECTED_PT_DEPTH]); 170 test_eq(error, seL4_NoError); 171 172 /* now map it back in */ 173 error = seL4_X86_IOPageTable_Map(pts.pts[EXPECTED_PT_DEPTH], iospace, IOPT_MAP_BASE); 174 test_eq(error, seL4_NoError); 175 176 /* it should retain its old mappings, and mapping in a new PT should fail */ 177 pt = vka_alloc_io_page_table_leaky(&env->vka); 178 test_assert(pt); 179 error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE); 180 test_assert(error != seL4_NoError); 181 182 delete_iospace(env, iospace); 183 return sel4test_get_result(); 184} 185DEFINE_TEST(IOPT0008, "Test IOPT map and remap top PT", test_iopt_map_remap_top_pt, true) 186 187static int 188test_iopt_no_overlapping_pt(env_t env) 189{ 190 int error; 191 iopt_cptrs_t pts; 192 seL4_CPtr iospace, frame, pt; 193 error = map_iopt_set(env, &iospace, &pts, &frame); 194 test_eq(error, seL4_NoError); 195 196 /* Mapping in a new PT should fail */ 197 pt = vka_alloc_io_page_table_leaky(&env->vka); 198 test_assert(pt); 199 error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE); 200 test_assert(error != seL4_NoError); 201 202 delete_iospace(env, iospace); 203 return sel4test_get_result(); 204} 205DEFINE_TEST(IOPT0009, "Test iopt no overlapping PT", test_iopt_no_overlapping_pt, true) 206 207static int 208test_iopt_map_remap_pt(env_t env) 209{ 210 int error; 211 iopt_cptrs_t pts; 212 seL4_CPtr iospace, frame; 213 error = map_iopt_set(env, &iospace, &pts, &frame); 214 test_eq(error, seL4_NoError); 215 216 /* unmap the pt */ 217 error = seL4_X86_IOPageTable_Unmap(pts.pts[pts.depth - 1]); 218 test_eq(error, seL4_NoError); 219 220 /* now map it back in */ 221 error = seL4_X86_IOPageTable_Map(pts.pts[pts.depth - 1], iospace, IOPT_MAP_BASE); 222 test_eq(error, seL4_NoError); 223 224 /* it should retain its old mappings, and mapping in a new frame should fail */ 225 frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 226 test_assert(frame); 227 error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE); 228 test_assert(error != seL4_NoError); 229 230 delete_iospace(env, iospace); 231 return sel4test_get_result(); 232} 233DEFINE_TEST(IOPT0011, "Test IOPT map and remap PT", test_iopt_map_remap_pt, true) 234 235#endif /* CONFIG_IOMMU */ 236 237#ifdef CONFIG_TK1_SMMU 238/* tests for ARM SystemMMU */ 239 240#define IOPT_MAP_BASE 0x10000000 241 242static int 243map_iopt_from_iospace(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt, seL4_CPtr *frame) 244{ 245 int error; 246 *frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 247 test_assert(*frame); 248 error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE); 249 250 if (error == seL4_FailedLookup) { 251 *iopt = vka_alloc_io_page_table_leaky(&env->vka); 252 test_assert(*iopt); 253 error = seL4_ARM_IOPageTable_Map(*iopt, iospace, IOPT_MAP_BASE); 254 test_eq(error, seL4_NoError); 255 error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE); 256 test_eq(error, seL4_NoError); 257 } 258 test_eq(error, seL4_NoError); 259 260 return error; 261 262} 263 264static int map_iopt_set(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt_cptr, seL4_CPtr *frame) 265{ 266 int error = map_iopt_from_iospace(env, iospace, iopt_cptr, frame); 267 return error; 268 269} 270 271static void delete_iospace(env_t env, seL4_CPtr iospace) 272{ 273 cspacepath_t path; 274 vka_cspace_make_path(&env->vka, iospace, &path); 275 vka_cnode_delete(&path); 276} 277 278static int test_iopt_basic_iopt(env_t env) 279{ 280 int error; 281 seL4_CPtr pt = 0; 282 seL4_CPtr frame = 0; 283 seL4_SlotRegion caps = env->io_space_caps; 284 int cap_count = caps.end - caps.start + 1; 285 seL4_CPtr cap = caps.start; 286 287 for (int i = 0; i < cap_count; i++) { 288 error = map_iopt_set(env, cap + i, &pt, &frame); 289 test_eq(error, seL4_NoError); 290 delete_iospace(env, cap + i); 291 } 292 return sel4test_get_result(); 293} 294DEFINE_TEST(IOPT0001, "Testing basic ARM IOPT mapping", test_iopt_basic_iopt, true); 295 296static int test_iopt_basic_map_unmap(env_t env) 297{ 298 int error; 299 int i; 300 seL4_CPtr iospace, pt, frame; 301 seL4_SlotRegion caps = env->io_space_caps; 302 int cap_count = caps.end - caps.start + 1; 303 304 for (i = 0; i < cap_count; i++) { 305 iospace = caps.start + i; 306 error = map_iopt_set(env, iospace, &pt, &frame); 307 test_eq(error, seL4_NoError); 308 309 error = seL4_ARM_Page_Unmap(frame); 310 test_eq(error, seL4_NoError); 311 312 error = seL4_ARM_IOPageTable_Unmap(pt); 313 test_eq(error, seL4_NoError); 314 315 error = map_iopt_from_iospace(env, iospace, &pt, &frame); 316 test_eq(error, seL4_NoError); 317 318 error = seL4_ARM_IOPageTable_Unmap(pt); 319 test_eq(error, seL4_NoError); 320 error = seL4_ARM_Page_Unmap(frame); 321 test_eq(error, seL4_NoError); 322 delete_iospace(env, iospace); 323 } 324 return sel4test_get_result(); 325} 326DEFINE_TEST(IOPT0002, "Test basic ARM IOPT mapping then unmapping", test_iopt_basic_map_unmap, true) 327 328static int 329test_iopt_no_overlapping_4k(env_t env) 330{ 331 int error; 332 int i; 333 seL4_CPtr iospace, pt, frame; 334 seL4_SlotRegion caps = env->io_space_caps; 335 int cap_count = caps.end - caps.start + 1; 336 for (i = 0; i < cap_count; i++) { 337 iospace = caps.start + i; 338 error = map_iopt_set(env, iospace, &pt, &frame); 339 test_eq(error, seL4_NoError); 340 341 frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 342 test_assert(frame); 343 /* mapping in a new frame should fail */ 344 error = seL4_ARM_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE); 345 test_assert(error != seL4_NoError); 346 delete_iospace(env, iospace); 347 } 348 349 return sel4test_get_result(); 350} 351DEFINE_TEST(IOPT0004, "Test ARM IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true) 352 353static int 354test_iopt_map_remap_pt(env_t env) 355{ 356 int error; 357 int i; 358 seL4_CPtr iospace, pt, frame; 359 seL4_SlotRegion caps = env->io_space_caps; 360 int cap_count = caps.end - caps.start + 1; 361 for (i = 0; i < cap_count; i++) { 362 iospace = caps.start + i; 363 error = map_iopt_set(env, iospace, &pt, &frame); 364 test_eq(error, seL4_NoError); 365 366 /* unmap the PT */ 367 error = seL4_ARM_IOPageTable_Unmap(pt); 368 test_eq(error, seL4_NoError); 369 370 /* now map it back in */ 371 error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE); 372 test_eq(error, seL4_NoError); 373 374 /* it should retain its old mappings, and mapping in a new PT should fail */ 375 pt = vka_alloc_io_page_table_leaky(&env->vka); 376 test_assert(pt); 377 error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE); 378 test_assert(error != seL4_NoError); 379 delete_iospace(env, iospace); 380 } 381 382 return sel4test_get_result(); 383} 384DEFINE_TEST(IOPT0008, "Test ARM IOPT map and remap PT", test_iopt_map_remap_pt, true) 385 386static int 387test_iopt_no_overlapping_pt(env_t env) 388{ 389 int error; 390 int i; 391 seL4_CPtr iospace, pt, frame; 392 seL4_SlotRegion caps = env->io_space_caps; 393 int cap_count = caps.end - caps.start + 1; 394 for (i = 0; i < cap_count; i++) { 395 iospace = caps.start + i; 396 error = map_iopt_set(env, iospace, &pt, &frame); 397 test_eq(error, seL4_NoError); 398 399 /* Mapping in a new PT should fail */ 400 pt = vka_alloc_io_page_table_leaky(&env->vka); 401 test_assert(pt); 402 error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE); 403 test_assert(error != seL4_NoError); 404 delete_iospace(env, iospace); 405 } 406 return sel4test_get_result(); 407} 408DEFINE_TEST(IOPT0009, "Test ARM iopt no overlapping PT", test_iopt_no_overlapping_pt, true) 409 410#endif 411