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 <autoconf.h> 14#include <sel4test-driver/gen_config.h> 15#include <assert.h> 16#include <stddef.h> 17#include <stdio.h> 18#include <stdlib.h> 19 20#include <sel4/sel4.h> 21#include <vka/object.h> 22#include <vka/capops.h> 23#include <sel4utils/util.h> 24#include <sel4utils/mapping.h> 25 26#include "../helpers.h" 27#include "frame_type.h" 28 29void touch_data(void *vaddr, char old_data, char new_data, size_t size_bits) 30{ 31 char *data = (char *)vaddr; 32 /* we test a sample of the bytes in the frame to ensure a couple of things 33 1. a frame of the correct size is mapped in 34 2. for larger frames that no part of the large frame shares a region with another part. 35 this could happen with ARM large pages and super sections as they are comprised of 36 16 entries in a paging structure, and not just a single entry in a higher level structure 37 */ 38 for (size_t i = 0; i < 16; i++) { 39 size_t offset = BIT(size_bits) / 16 * i; 40 test_eq(data[offset], old_data); 41 data[offset] = new_data; 42 test_eq(data[offset], new_data); 43 } 44} 45 46static int test_frame_exported(env_t env) 47{ 48 /* Reserve a location that is aligned and large enough to map our 49 * largest kind of frame */ 50 void *vaddr; 51 reservation_t reserve = vspace_reserve_range_aligned(&env->vspace, VSPACE_RV_SIZE, VSPACE_RV_ALIGN_BITS, 52 seL4_AllRights, 1, &vaddr); 53 test_assert(reserve.res); 54 55 /* loop through frame sizes, allocate, map and touch them until we run out 56 * of memory. */ 57 size_t mem_total = 0; 58 int err; 59 for (int i = 0; i < ARRAY_SIZE(frame_types); i++) { 60 bool once = false; 61 while (1) { 62 /* Allocate the frame */ 63 seL4_CPtr frame = vka_alloc_frame_leaky(&env->vka, frame_types[i].size_bits); 64 if (!frame) { 65 break; 66 } 67 once = true; 68 mem_total += BIT(frame_types[i].size_bits); 69 70 uintptr_t cookie = 0; 71 err = vspace_map_pages_at_vaddr(&env->vspace, &frame, &cookie, (void *)vaddr, 1, frame_types[i].size_bits, reserve); 72 test_error_eq(err, seL4_NoError); 73 74 /* Touch the memory */ 75 char *data = (char *)vaddr; 76 touch_data(data, 0, 'U', frame_types[i].size_bits); 77 78 err = seL4_ARCH_Page_Map(frame, 79 env->page_directory, 80 (seL4_Word) vaddr, 81 seL4_AllRights, 82 seL4_ARCH_Default_VMAttributes); 83 test_error_eq(err, seL4_NoError); 84 /* ensure the memory is what it was before and touch it again */ 85 touch_data(vaddr, 'U', 'V', frame_types[i].size_bits); 86 87 vspace_unmap_pages(&env->vspace, (void *)vaddr, 1, frame_types[i].size_bits, VSPACE_PRESERVE); 88 test_error_eq(err, seL4_NoError); 89 } 90 test_assert(once); 91 } 92 return sel4test_get_result(); 93} 94DEFINE_TEST(FRAMEEXPORTS0001, "Test that we can access all exported frames", test_frame_exported, true) 95 96/* Wait for a VM fault originating on the given EP the return the virtual 97 * address it occurred at. Returns the sentinel 0xffffffff if the message 98 * received was not a VM fault. 99 */ 100static int handle(seL4_CPtr fault_ep, seL4_CPtr reply, seL4_Word arg3, seL4_Word arg4) 101{ 102 seL4_MessageInfo_t info = api_recv(fault_ep, NULL, reply); 103 if (seL4_MessageInfo_get_label(info) == seL4_Fault_VMFault) { 104 return (int)seL4_GetMR(1); 105 } else { 106 return (int)0xffffffff; 107 } 108} 109 110#if defined(CONFIG_ARCH_ARM) 111/* XN support is only implemented for ARM currently. */ 112 113/* Function that generates a fault. If we're mapped XN we should instruction 114 * fault at the start of the function. If not we should data fault on 0x42. 115 */ 116static int fault(seL4_Word arg1, seL4_Word arg2, seL4_Word arg3, seL4_Word arg4) 117{ 118 *(char *)0x42 = 'c'; 119 return 0; 120} 121 122static int test_xn(env_t env, seL4_ArchObjectType frame_type) 123{ 124 int err; 125 /* Find the size of the frame type we want to test. */ 126 int sz_bits = 0; 127 for (unsigned int i = 0; i < ARRAY_SIZE(frame_types); i++) { 128 if (frame_types[i].type == frame_type) { 129 sz_bits = frame_types[i].size_bits; 130 break; 131 } 132 } 133 test_assert(sz_bits != 0); 134 135 /* Get ourselves a frame. */ 136 seL4_CPtr frame_cap = vka_alloc_frame_leaky(&env->vka, sz_bits); 137 test_assert(frame_cap != seL4_CapNull); 138 139 /* Map it in */ 140 uintptr_t cookie = 0; 141 void *dest = vspace_map_pages(&env->vspace, &frame_cap, &cookie, seL4_AllRights, 1, sz_bits, 1); 142 test_assert(dest != NULL); 143 144 /* Set up a function we're going to have another thread call. Assume that 145 * the function is no more than 100 bytes long. 146 */ 147 memcpy(dest, (void *)fault, 100); 148 149 /* Unify the instruction and data caches so our code is seen */ 150 seL4_ARM_Page_Unify_Instruction(frame_cap, 0, BIT(sz_bits)); 151 152 /* First setup a fault endpoint. 153 */ 154 seL4_CPtr fault_ep = vka_alloc_endpoint_leaky(&env->vka); 155 cspacepath_t path; 156 vka_cspace_make_path(&env->vka, fault_ep, &path); 157 test_assert(fault_ep != seL4_CapNull); 158 159 /* Then setup the thread that will, itself, fault. */ 160 helper_thread_t faulter; 161 create_helper_thread(env, &faulter); 162 set_helper_priority(env, &faulter, 100); 163 err = api_tcb_set_space(get_helper_tcb(&faulter), 164 fault_ep, 165 env->cspace_root, 166 api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits), 167 env->page_directory, seL4_NilData); 168 start_helper(env, &faulter, dest, 0, 0, 0, 0); 169 170 /* Now a fault handler that will catch and diagnose its fault. */ 171 helper_thread_t handler; 172 create_helper_thread(env, &handler); 173 set_helper_priority(env, &handler, 100); 174 start_helper(env, &handler, handle, fault_ep, get_helper_reply(&faulter), 0, 0); 175 176 /* Wait for the fault to happen */ 177 void *res = (void *)(seL4_Word)wait_for_helper(&handler); 178 179 test_assert(res == (void *)0x42); 180 181 cleanup_helper(env, &handler); 182 cleanup_helper(env, &faulter); 183 184 /* Now let's remap the page with XN set and confirm that we can't execute 185 * in it any more. 186 */ 187 err = seL4_ARM_Page_Map(frame_cap, env->page_directory, (seL4_Word) dest, seL4_AllRights, 188 seL4_ARM_Default_VMAttributes | seL4_ARM_ExecuteNever); 189 test_error_eq(err, 0); 190 191 /* The page should still contain our code from before. */ 192 test_assert(!memcmp(dest, (void *)fault, 100)); 193 194 /* We need to reallocate a fault EP because the thread cleanup above 195 * inadvertently destroys the EP we were using. 196 */ 197 fault_ep = vka_alloc_endpoint_leaky(&env->vka); 198 test_assert(fault_ep != seL4_CapNull); 199 200 /* Recreate our two threads. */ 201 create_helper_thread(env, &faulter); 202 set_helper_priority(env, &faulter, 100); 203 err = api_tcb_configure(get_helper_tcb(&faulter), 204 fault_ep, seL4_CapNull, 205 get_helper_sched_context(&faulter), 206 env->cspace_root, 207 api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits), 208 env->page_directory, seL4_NilData, 209 faulter.thread.ipc_buffer_addr, 210 faulter.thread.ipc_buffer); 211 start_helper(env, &faulter, dest, 0, 0, 0, 0); 212 create_helper_thread(env, &handler); 213 set_helper_priority(env, &handler, 100); 214 start_helper(env, &handler, handle, fault_ep, get_helper_reply(&faulter), 0, 0); 215 216 /* Wait for the fault to happen */ 217 res = (void *)(seL4_Word)wait_for_helper(&handler); 218 219 /* Confirm that, this time, we faulted at the start of the XN-mapped page. */ 220 test_assert(res == (void *)dest); 221 222 /* Resource tear down. */ 223 cleanup_helper(env, &handler); 224 cleanup_helper(env, &faulter); 225 226 return sel4test_get_result(); 227} 228 229static int test_xn_small_frame(env_t env) 230{ 231 return test_xn(env, seL4_ARM_SmallPageObject); 232} 233DEFINE_TEST(FRAMEXN0001, "Test that we can map a small frame XN", test_xn_small_frame, config_set(CONFIG_ARCH_ARM)) 234 235static int test_xn_large_frame(env_t env) 236{ 237 return test_xn(env, seL4_ARM_LargePageObject); 238} 239DEFINE_TEST(FRAMEXN0002, "Test that we can map a large frame XN", test_xn_large_frame, config_set(CONFIG_ARCH_ARM)) 240#endif /* CONFIG_ARCH_ARM */ 241 242static int test_device_frame_ipcbuf(env_t env) 243{ 244 cspacepath_t path; 245 cspacepath_t frame_path; 246 int error; 247 error = vka_cspace_alloc_path(&env->vka, &path); 248 vka_cspace_make_path(&env->vka, env->device_frame, &frame_path); 249 vka_cnode_copy(&path, &frame_path, seL4_AllRights); 250 test_error_eq(error, 0); 251 252 helper_thread_t other; 253 create_helper_thread(env, &other); 254 /* Try and create a thread with a device frame as its IPC buffer */ 255 error = api_tcb_configure(get_helper_tcb(&other), 256 0, seL4_CapNull, 257 get_helper_sched_context(&other), 258 env->cspace_root, 259 api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits), 260 env->page_directory, seL4_NilData, 261 get_helper_ipc_buffer_addr(&other), 262 path.capPtr); 263 test_neq(error, 0); 264 cleanup_helper(env, &other); 265 266 return sel4test_get_result(); 267} 268DEFINE_TEST(FRAMEDIPC0001, "Test that we cannot create a thread with an IPC buffer that is a frame", 269 test_device_frame_ipcbuf, !config_set(CONFIG_PLAT_SPIKE)) 270 271static int wait_func(seL4_Word ep) 272{ 273 seL4_Send(ep, seL4_MessageInfo_new(0, 0, 0, 0)); 274 seL4_Send(ep, seL4_MessageInfo_new(0, 0, 0, 0)); 275 return 0; 276} 277 278static int test_switch_device_frame_ipcbuf(env_t env) 279{ 280 cspacepath_t path; 281 cspacepath_t frame_path; 282 int error; 283 seL4_CPtr ep; 284 error = vka_cspace_alloc_path(&env->vka, &path); 285 vka_cspace_make_path(&env->vka, env->device_frame, &frame_path); 286 vka_cnode_copy(&path, &frame_path, seL4_AllRights); 287 test_error_eq(error, 0); 288 289 ep = vka_alloc_endpoint_leaky(&env->vka); 290 test_assert(ep != seL4_CapNull); 291 292 helper_thread_t other; 293 create_helper_thread(env, &other); 294 /* start the thread and make sure it works */ 295 start_helper(env, &other, (helper_fn_t)wait_func, (seL4_Word)ep, 0, 0, 0); 296 seL4_Wait(ep, NULL); 297 /* now switch its IPC buffer, which should fail */ 298 error = seL4_TCB_SetIPCBuffer(get_helper_tcb(&other), get_helper_ipc_buffer_addr(&other), path.capPtr); 299 test_neq(error, 0); 300 /* thread should still be working */ 301 seL4_Wait(ep, NULL); 302 /* all done */ 303 wait_for_helper(&other); 304 cleanup_helper(env, &other); 305 return sel4test_get_result(); 306} 307DEFINE_TEST(FRAMEDIPC0002, "Test that we cannot switch a threads IPC buffer to a device frame", 308 test_switch_device_frame_ipcbuf, !config_set(CONFIG_PLAT_SPIKE)) 309 310static int touch_data_fault(seL4_Word data, seL4_Word fault_ep, seL4_Word arg3, seL4_Word arg4) 311{ 312 *(volatile int *)data = 42; 313 /* if we got here we should wake the fault handler up with an error so the test doesn't hang forever. 314 * we do that by generating a different fault */ 315 utils_undefined_instruction(); 316 return sel4test_get_result(); 317} 318 319static int test_unmap_on_delete(env_t env) 320{ 321 int err; 322 /* Get ourselves a frame. */ 323 cspacepath_t frame_path; 324 seL4_CPtr frame_cap = vka_alloc_frame_leaky(&env->vka, seL4_PageBits); 325 test_assert(frame_cap != seL4_CapNull); 326 vka_cspace_make_path(&env->vka, frame_cap, &frame_path); 327 328 /* create a copy of the frame cap */ 329 cspacepath_t frame_copy; 330 err = vka_cspace_alloc_path(&env->vka, &frame_copy); 331 test_error_eq(err, seL4_NoError); 332 vka_cnode_copy(&frame_copy, &frame_path, seL4_AllRights); 333 334 /* Map it in */ 335 uintptr_t cookie = 0; 336 void *dest = vspace_map_pages(&env->vspace, &frame_copy.capPtr, &cookie, seL4_AllRights, 1, seL4_PageBits, 1); 337 test_assert(dest != NULL); 338 339 /* verify we can access it */ 340 *(volatile int *)dest = 0; 341 342 /* now delete the copy of the frame we mapped in */ 343 vka_cnode_delete(&frame_copy); 344 345 /* Setup a fault endpoint. */ 346 seL4_CPtr fault_ep = vka_alloc_endpoint_leaky(&env->vka); 347 cspacepath_t path; 348 vka_cspace_make_path(&env->vka, fault_ep, &path); 349 test_assert(fault_ep != seL4_CapNull); 350 351 /* Then setup the thread that will fault. */ 352 helper_thread_t faulter; 353 create_helper_thread(env, &faulter); 354 set_helper_priority(env, &faulter, 100); 355 err = api_tcb_set_space(get_helper_tcb(&faulter), 356 fault_ep, 357 env->cspace_root, 358 api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits), 359 env->page_directory, seL4_NilData); 360 start_helper(env, &faulter, touch_data_fault, (seL4_Word)dest, 0, 0, 0); 361 362 /* Now a fault handler that will catch and diagnose its fault. */ 363 helper_thread_t handler; 364 create_helper_thread(env, &handler); 365 set_helper_priority(env, &handler, 100); 366 start_helper(env, &handler, handle, fault_ep, get_helper_reply(&faulter), 0, 0); 367 368 /* Wait for the fault to happen */ 369 void *res = (void *)(seL4_Word)wait_for_helper(&handler); 370 371 /* check that we got a fault as expected */ 372 test_eq((uintptr_t)res, (uintptr_t)dest); 373 374 cleanup_helper(env, &handler); 375 cleanup_helper(env, &faulter); 376 377 return sel4test_get_result(); 378} 379DEFINE_TEST(FRAMEDIPC0003, "Test that deleting a frame cap unmaps the frame", test_unmap_on_delete, true) 380