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 <sel4/sel4.h> 16#include <vka/object.h> 17 18#include "../helpers.h" 19 20#include <utils/util.h> 21 22static void interrupt_helper(env_t env, volatile int *state, int runs, seL4_CPtr endpoint) 23{ 24 while (*state < runs) { 25 *state = *state + 1; 26 ZF_LOGD("Tick"); 27 sel4test_ntfn_timer_wait(env); 28 } 29 ZF_LOGD("Boom"); 30 sel4test_ntfn_timer_wait(env); 31 32} 33 34/* test an interrupt handling thread that inherits the scheduling context of the notification 35 * object */ 36static int test_interrupt_notification_sc(env_t env) 37{ 38 helper_thread_t helper; 39 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 40 volatile seL4_Word state = 0; 41 seL4_Word runs = 10; 42 int error; 43 44 /* set up helper */ 45 create_helper_thread(env, &helper); 46 start_helper(env, &helper, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state, 47 runs, endpoint); 48 set_helper_priority(env, &helper, 10); 49 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 50 test_eq(error, seL4_NoError); 51 52 /* helper should not have finished */ 53 test_leq(state, runs); 54 55 /* take away scheduling context and give it to notification object */ 56 error = api_sc_unbind(helper.thread.sched_context.cptr); 57 test_eq(error, seL4_NoError); 58 59 error = api_sc_bind(helper.thread.sched_context.cptr, 60 env->timer_notification.cptr); 61 test_eq(error, seL4_NoError); 62 63 sel4test_periodic_start(env, 10 * NS_IN_MS); 64 65 /* wait for the helper */ 66 wait_for_helper(&helper); 67 test_eq(state, runs); 68 69 sel4test_timer_reset(env); 70 71 return sel4test_get_result(); 72} 73DEFINE_TEST(INTERRUPT0002, "Test interrupts with scheduling context donation from notification object", 74 test_interrupt_notification_sc, config_set(CONFIG_HAVE_TIMER) &&config_set(CONFIG_KERNEL_MCS)); 75 76/* test an interrupt handling thread with a scheduling context doesn't inherit the notification objects scheduling context */ 77static int test_interrupt_notification_and_tcb_sc(env_t env) 78{ 79 helper_thread_t helper_with_sc, helper_without_sc; 80 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 81 volatile seL4_Word state_with_sc = 0; 82 volatile seL4_Word state_without_sc = 0; 83 seL4_Word runs = 10; 84 int error; 85 86 /* set up helpers */ 87 create_helper_thread(env, &helper_without_sc); 88 start_helper(env, &helper_without_sc, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state_without_sc, 89 runs, endpoint); 90 set_helper_priority(env, &helper_without_sc, 10); 91 92 93 create_helper_thread(env, &helper_with_sc); 94 start_helper(env, &helper_with_sc, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state_with_sc, 95 runs, endpoint); 96 set_helper_priority(env, &helper_with_sc, 10); 97 98 /* helper_with_sc will run first */ 99 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 100 test_eq(error, seL4_NoError); 101 102 /* both helpers should run and wait for irq */ 103 test_leq(state_with_sc, (seL4_Word) runs); 104 test_leq(state_without_sc, (seL4_Word) runs); 105 106 /* take away scheduling context from helper_without_sc and give it to notification object */ 107 error = api_sc_unbind(helper_without_sc.thread.sched_context.cptr); 108 test_eq(error, seL4_NoError); 109 110 sel4test_periodic_start(env, 10 * NS_IN_MS); 111 112 error = api_sc_bind(helper_without_sc.thread.sched_context.cptr, 113 env->timer_notification.cptr); 114 test_eq(error, seL4_NoError); 115 116 /* wait for the helper */ 117 wait_for_helper(&helper_with_sc); 118 test_eq(state_with_sc, runs); 119 120 wait_for_helper(&helper_without_sc); 121 test_eq(state_without_sc, runs); 122 123 sel4test_timer_reset(env); 124 125 return sel4test_get_result(); 126} 127DEFINE_TEST(INTERRUPT0003, 128 "Test interrupts with scheduling context donation from notification object and without (two clients)", 129 test_interrupt_notification_and_tcb_sc, config_set(CONFIG_HAVE_TIMER) &&config_set(CONFIG_KERNEL_MCS)); 130 131/* test that if niether the thread or notification object have a scheduling context, nothing happens */ 132static int test_interrupt_no_sc(env_t env) 133{ 134 helper_thread_t helper; 135 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 136 volatile seL4_Word state = 0; 137 seL4_Word runs = 10; 138 int error; 139 140 /* set up helper */ 141 create_helper_thread(env, &helper); 142 start_helper(env, &helper, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state, 143 runs, endpoint); 144 set_helper_priority(env, &helper, 10); 145 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 146 test_eq(error, seL4_NoError); 147 148 /* helper should run and wait for irq */ 149 test_leq(state, (seL4_Word) runs); 150 seL4_Word prev_state = state; 151 152 /* take away scheduling context */ 153 error = api_sc_unbind(helper.thread.sched_context.cptr); 154 test_eq(error, seL4_NoError); 155 156 sel4test_periodic_start(env, 10 * NS_IN_MS); 157 158 test_eq(state, (seL4_Word) prev_state); 159 160 sel4test_timer_reset(env); 161 162 return sel4test_get_result(); 163} 164DEFINE_TEST(INTERRUPT0004, "Test interrupts with no scheduling context at all", test_interrupt_no_sc, 165 config_set(CONFIG_HAVE_TIMER) &&config_set(CONFIG_KERNEL_MCS)); 166 167/* test that a second interrupt handling thread on the same endpoint works */ 168int test_interrupt_notification_sc_two_clients(env_t env) 169{ 170 helper_thread_t helper_first, helper_second; 171 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 172 volatile seL4_Word state_first = 0; 173 volatile seL4_Word state_second = 0; 174 seL4_Word runs = 10; 175 int error; 176 177 /* set up helpers */ 178 create_helper_thread(env, &helper_second); 179 start_helper(env, &helper_second, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state_first, 180 runs, endpoint); 181 set_helper_priority(env, &helper_second, 10); 182 183 create_helper_thread(env, &helper_first); 184 start_helper(env, &helper_first, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state_second, 185 runs, endpoint); 186 set_helper_priority(env, &helper_first, 10); 187 188 /* helper_with_sc will run first */ 189 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 190 test_eq(error, seL4_NoError); 191 192 /* both helpers should run and wait for irq */ 193 test_leq(state_first, (seL4_Word) runs); 194 test_leq(state_second, (seL4_Word) runs); 195 196 /* take away scheduling context from both, give one to a notification object */ 197 error = api_sc_unbind(helper_first.thread.sched_context.cptr); 198 test_eq(error, seL4_NoError); 199 200 error = api_sc_unbind(helper_second.thread.sched_context.cptr); 201 test_eq(error, seL4_NoError); 202 203 error = api_sc_bind(helper_first.thread.sched_context.cptr, 204 env->timer_notification.cptr); 205 test_eq(error, seL4_NoError); 206 207 sel4test_periodic_start(env, 10 * NS_IN_MS); 208 209 /* wait for the helper */ 210 wait_for_helper(&helper_first); 211 /* second will not exit as first stole the scheduling context when it exited */ 212 213 test_eq(state_first, runs); 214 test_eq(state_second, runs); 215 216 sel4test_timer_reset(env); 217 218 return sel4test_get_result(); 219} 220DEFINE_TEST(INTERRUPT0005, "Test the same scheduling context cannot be loaned to different threads", 221 test_interrupt_notification_sc_two_clients, config_set(CONFIG_HAVE_TIMER) &&config_set(CONFIG_KERNEL_MCS)); 222 223/* test deleting the scheduling context stops the notification from donating it */ 224static int test_interrupt_delete_sc(env_t env) 225{ 226 helper_thread_t helper; 227 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 228 volatile seL4_Word state = 0; 229 seL4_Word runs = 10; 230 int error; 231 232 /* set up helper */ 233 create_helper_thread(env, &helper); 234 start_helper(env, &helper, (helper_fn_t) interrupt_helper, (seL4_Word) env, (seL4_Word) &state, 235 runs, endpoint); 236 set_helper_priority(env, &helper, 10); 237 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 238 test_eq(error, seL4_NoError); 239 240 /* helper should run and wait for irq */ 241 test_leq(state, (seL4_Word) runs); 242 seL4_Word prev_state = state; 243 244 /* take away scheduling context and give it to notification object */ 245 error = api_sc_unbind(helper.thread.sched_context.cptr); 246 test_eq(error, seL4_NoError); 247 248 error = api_sc_bind(helper.thread.sched_context.cptr, 249 env->timer_notification.cptr); 250 test_eq(error, seL4_NoError); 251 252 /* now delete it */ 253 vka_free_object(&env->vka, &helper.thread.sched_context); 254 255 sel4test_periodic_start(env, 10 * NS_IN_MS); 256 257 test_eq(state, prev_state); 258 259 sel4test_timer_reset(env); 260 return sel4test_get_result(); 261} 262DEFINE_TEST(INTERRUPT0006, "Test interrupts after deleting scheduling context bound to notification", 263 test_interrupt_delete_sc, config_set(CONFIG_HAVE_TIMER) &&config_set(CONFIG_KERNEL_MCS)); 264