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 <sel4test-driver/gen_config.h> 14#include <assert.h> 15#include <stdio.h> 16#include <sel4/sel4.h> 17#include <vka/object.h> 18 19#include "../helpers.h" 20 21static volatile int revoking = 0; 22static volatile int preempt_count = 0; 23 24static int revoke_func(seL4_CNode service, seL4_Word index, seL4_Word depth) 25{ 26 revoking = 1; 27 seL4_CNode_Revoke(service, index, depth); 28 revoking = 2; 29 return 0; 30} 31 32static int preempt_count_func(env_t env) 33{ 34 while (revoking < 2) { 35 sel4test_ntfn_timer_wait(env); 36 if (revoking == 1) { 37 preempt_count++; 38 } 39 } 40 return 0; 41} 42 43static int create_cnode_table(env_t env, int num_cnode_bits, seL4_CPtr ep) 44{ 45 /* Create as many cnodes as possible. We will copy the cap into all 46 * those cnodes. */ 47#define CNODE_SIZE_BITS 12 48 int num_caps = 0; 49 int error = 0; 50 51 ZF_LOGD(" Creating %d caps .", BIT((num_cnode_bits + CNODE_SIZE_BITS))); 52 for (int i = 0; i < (1 << num_cnode_bits); i++) { 53 seL4_CPtr ctable = vka_alloc_cnode_object_leaky(&env->vka, CNODE_SIZE_BITS); 54 if (ctable == seL4_CapNull) { 55 return -1; 56 } 57 58 for (int j = 0; j < BIT(CNODE_SIZE_BITS); j++) { 59 error = seL4_CNode_Copy( 60 ctable, j, CNODE_SIZE_BITS, 61 env->cspace_root, ep, seL4_WordBits, 62 seL4_AllRights); 63 64 test_check(!error); 65 num_caps++; 66 } 67 ZF_LOGD("."); 68 } 69 70 if (num_caps != BIT(num_cnode_bits + CNODE_SIZE_BITS)) { 71 ZF_LOGD("Created %d caps. Couldn't create the required number of caps %d", 72 num_caps, 73 BIT(num_cnode_bits + CNODE_SIZE_BITS)); 74 return -1; 75 } 76 77 return error; 78} 79 80static int test_preempt_revoke_actual(env_t env, int num_cnode_bits) 81{ 82 helper_thread_t revoke_thread, preempt_thread; 83 int error; 84 uint64_t timeout_val = 0; 85 86 /* Create an endpoint cap that will be derived many times. */ 87 seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka); 88 89 create_helper_thread(env, &revoke_thread); 90 create_helper_thread(env, &preempt_thread); 91 92 set_helper_priority(env, &preempt_thread, 101); 93 set_helper_priority(env, &revoke_thread, 100); 94 95 /* First create a ctable and fill it with copied ep caps 96 * and time the revoke operation 97 */ 98 error = create_cnode_table(env, num_cnode_bits, ep); 99 test_error_eq(error, seL4_NoError); 100 101 uint64_t start, end, diff; 102 /* meaure revoke_func time. 103 * Note that we don't take caching and other effects (e.g. function calls vs. 104 * jmp costs into consideration, however, this should get us a better 105 * timeout value per target compared to setting a fixed timeout value that may 106 * fail on different targets. 107 */ 108 start = sel4test_timestamp(env); 109 revoke_func(env->cspace_root, ep, seL4_WordBits); 110 end = sel4test_timestamp(env); 111 112 test_geq(end, start); 113 114 diff = end - start; 115 116 /* Set a timeout value to a third of the revoke operation time, 117 * to allow preemption to occur at least twice, as the test expects. 118 */ 119 timeout_val = diff / 3; 120 121 /* Now, actually create a ctable that is gonna be used in the revoke 122 * test. 123 */ 124 error = create_cnode_table(env, num_cnode_bits, ep); 125 test_error_eq(error, seL4_NoError); 126 sel4test_periodic_start(env, timeout_val); 127 128 /* Last thread to start runs first. */ 129 revoking = 0; 130 preempt_count = 0; 131 start_helper(env, &preempt_thread, (helper_fn_t) preempt_count_func, (seL4_Word) env, 0, 0, 0); 132 start_helper(env, &revoke_thread, (helper_fn_t) revoke_func, env->cspace_root, 133 ep, seL4_WordBits, 0); 134 135 wait_for_helper(&revoke_thread); 136 137 cleanup_helper(env, &preempt_thread); 138 cleanup_helper(env, &revoke_thread); 139 140 ZF_LOGD(" %d preemptions\n", preempt_count); 141 142 return preempt_count; 143} 144 145static int test_preempt_revoke(env_t env) 146{ 147 for (int num_cnode_bits = 1; num_cnode_bits < 32; num_cnode_bits++) { 148 int result = test_preempt_revoke_actual(env, num_cnode_bits); 149 if (result > 1) { 150 return sel4test_get_result(); 151 } else if (result == -1) { 152 /* At this point we assume this error is a problem where the time of 153 * revoc_func < timer_resolution, which will cause set timeout to faule 154 * return an error. Skip such small cnodes and only work with cnode sizes where 155 * revoc_func > timer_resolution 156 */ 157 continue; 158 } 159 } 160 161 ZF_LOGD("Couldn't trigger preemption point with millions of caps!\n"); 162 test_assert(0); 163} 164DEFINE_TEST(PREEMPT_REVOKE, "Test preemption path in revoke", test_preempt_revoke, config_set(CONFIG_HAVE_TIMER)) 165