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#include <stdio.h> 15#include <sel4/sel4.h> 16#include <vka/object.h> 17 18#include "../helpers.h" 19 20#define NUM_RUNS 10 21#define ASYNC 1 22#define SYNC 2 23 24static seL4_CPtr badge_endpoint(env_t env, seL4_Word badge, seL4_CPtr ep) 25{ 26 27 seL4_CPtr slot = get_free_slot(env); 28 int error = cnode_mint(env, ep, slot, seL4_AllRights, badge); 29 test_error_eq(error, seL4_NoError); 30 return slot; 31} 32 33static int sender(seL4_Word ep, seL4_Word id, seL4_Word runs, seL4_Word arg3) 34{ 35 assert(runs > 0); 36 for (seL4_Word i = 0; i < runs; i++) { 37 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0); 38 seL4_Send((seL4_CPtr) ep, info); 39 } 40 41 return 0; 42} 43 44static int test_notification_binding(env_t env) 45{ 46 helper_thread_t sync, notification; 47 48 /* create endpoints */ 49 seL4_CPtr sync_ep = vka_alloc_endpoint_leaky(&env->vka); 50 seL4_CPtr notification_ep = vka_alloc_notification_leaky(&env->vka); 51 seL4_CPtr badged_notification_ep = badge_endpoint(env, ASYNC, notification_ep); 52 seL4_CPtr badged_sync_ep = badge_endpoint(env, SYNC, sync_ep); 53 54 assert(notification_ep); 55 assert(sync_ep); 56 57 /* badge endpoints so we can tell them apart */ 58 create_helper_thread(env, &sync); 59 create_helper_thread(env, ¬ification); 60 61 /* bind the endpoint */ 62 int error = seL4_TCB_BindNotification(env->tcb, notification_ep); 63 test_error_eq(error, seL4_NoError); 64 65 start_helper(env, ¬ification, sender, badged_notification_ep, ASYNC, NUM_RUNS, 0); 66 start_helper(env, &sync, sender, badged_sync_ep, SYNC, NUM_RUNS, 0); 67 68 int num_notification_messages = 0; 69 int num_sync_messages = 0; 70 for (int i = 0; i < NUM_RUNS * 2; i++) { 71 seL4_Word badge = 0; 72 seL4_Wait(sync_ep, &badge); 73 74 switch (badge) { 75 case ASYNC: 76 num_notification_messages++; 77 break; 78 case SYNC: 79 num_sync_messages++; 80 break; 81 } 82 } 83 84 test_check(num_notification_messages == NUM_RUNS); 85 test_check(num_sync_messages == NUM_RUNS); 86 87 error = seL4_TCB_UnbindNotification(env->tcb); 88 test_error_eq(error, seL4_NoError); 89 90 cleanup_helper(env, &sync); 91 cleanup_helper(env, ¬ification); 92 93 return sel4test_get_result(); 94} 95DEFINE_TEST(BIND0001, 96 "Test that a bound tcb waiting on a sync endpoint receives normal sync ipc and notification notifications.", 97 test_notification_binding, true) 98 99static int 100test_notification_binding_2(env_t env) 101{ 102 helper_thread_t notification; 103 104 /* create endpoints */ 105 seL4_CPtr notification_ep = vka_alloc_notification_leaky(&env->vka); 106 seL4_CPtr badged_notification_ep = badge_endpoint(env, ASYNC, notification_ep); 107 108 test_assert(notification_ep); 109 test_assert(badged_notification_ep); 110 111 /* badge endpoints so we can tell them apart */ 112 create_helper_thread(env, ¬ification); 113 114 /* bind the endpoint */ 115 int error = seL4_TCB_BindNotification(env->tcb, notification_ep); 116 test_error_eq(error, seL4_NoError); 117 118 start_helper(env, ¬ification, sender, badged_notification_ep, ASYNC, NUM_RUNS, 0); 119 120 int num_notification_messages = 0; 121 for (int i = 0; i < NUM_RUNS; i++) { 122 seL4_Word badge = 0; 123 seL4_Wait(notification_ep, &badge); 124 125 switch (badge) { 126 case ASYNC: 127 num_notification_messages++; 128 break; 129 } 130 } 131 132 test_check(num_notification_messages == NUM_RUNS); 133 134 error = seL4_TCB_UnbindNotification(env->tcb); 135 test_error_eq(error, seL4_NoError); 136 137 cleanup_helper(env, ¬ification); 138 139 return sel4test_get_result(); 140} 141DEFINE_TEST(BIND0002, "Test that a bound tcb waiting on its bound notification recieves notifications", 142 test_notification_binding_2, true) 143 144/* helper thread for testing the ordering of bound notification endpoint operations */ 145static int 146waiter(seL4_Word bound_ep, seL4_Word arg1, seL4_Word arg2, seL4_Word arg3) 147{ 148 seL4_Word badge; 149 seL4_Wait(bound_ep, &badge); 150 return 0; 151} 152 153static int test_notification_binding_prio(env_t env, uint8_t waiter_prio, uint8_t sender_prio) 154{ 155 helper_thread_t waiter_thread; 156 helper_thread_t sender_thread; 157 158 seL4_CPtr notification_ep = vka_alloc_notification_leaky(&env->vka); 159 seL4_CPtr sync_ep = vka_alloc_endpoint_leaky(&env->vka); 160 161 test_assert(notification_ep); 162 test_assert(sync_ep); 163 164 create_helper_thread(env, &waiter_thread); 165 set_helper_priority(env, &waiter_thread, waiter_prio); 166 167 create_helper_thread(env, &sender_thread); 168 set_helper_priority(env, &sender_thread, sender_prio); 169 170 int error = seL4_TCB_BindNotification(get_helper_tcb(&waiter_thread), notification_ep); 171 test_error_eq(error, seL4_NoError); 172 173 start_helper(env, &waiter_thread, waiter, notification_ep, 0, 0, 0); 174 start_helper(env, &sender_thread, sender, notification_ep, 0, 1, 0); 175 176 wait_for_helper(&waiter_thread); 177 wait_for_helper(&sender_thread); 178 179 cleanup_helper(env, &waiter_thread); 180 cleanup_helper(env, &sender_thread); 181 182 return sel4test_get_result(); 183} 184 185static int test_notification_binding_3(env_t env) 186{ 187 test_notification_binding_prio(env, 10, 9); 188 return sel4test_get_result(); 189} 190DEFINE_TEST(BIND0003, "Test IPC ordering 1) bound tcb waits on bound notification 2, true) another tcb sends a message", 191 test_notification_binding_3, true) 192 193static int 194test_notification_binding_4(env_t env) 195{ 196 test_notification_binding_prio(env, 9, 10); 197 return sel4test_get_result(); 198} 199DEFINE_TEST(BIND0004, "Test IPC ordering 2) bound tcb waits on bound notification 1, true) another tcb sends a message", 200 test_notification_binding_4, true) 201 202static void 203bind0005_helper(seL4_CPtr endpoint, volatile int *state) 204{ 205 *state = 1; 206 seL4_Wait(endpoint, NULL); 207 *state = 2; 208} 209 210static int test_notification_binding_no_sc(env_t env) 211{ 212 seL4_CPtr endpoint, notification; 213 int error; 214 helper_thread_t helper; 215 volatile int state = 0; 216 217 endpoint = vka_alloc_endpoint_leaky(&env->vka); 218 notification = vka_alloc_notification_leaky(&env->vka); 219 220 create_helper_thread(env, &helper); 221 222 /* set our prio lower so the helper thread runs when we start it */ 223 set_helper_priority(env, &helper, 10); 224 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 225 test_eq(error, seL4_NoError); 226 227 error = seL4_TCB_BindNotification(helper.thread.tcb.cptr, notification); 228 test_eq(error, seL4_NoError); 229 230 /* start the helper so it is waiting on the endpoint */ 231 start_helper(env, &helper, (helper_fn_t) bind0005_helper, endpoint, 232 (seL4_Word) &state, 0, 0); 233 test_eq(state, 1); 234 235 /* clear its sc */ 236 error = api_sc_unbind(helper.thread.sched_context.cptr); 237 test_eq(error, seL4_NoError); 238 239 /* signal it */ 240 seL4_Signal(notification); 241 242 /* it should not have run */ 243 test_eq(state, 1); 244 245 /* now give back the scheduling context */ 246 error = api_sc_bind(helper.thread.sched_context.cptr, 247 helper.thread.tcb.cptr); 248 test_eq(error, seL4_NoError); 249 250 /* now it should have got the signal */ 251 test_eq(state, 2); 252 253 return sel4test_get_result(); 254} 255DEFINE_TEST(BIND005, "Test passing thread notification binding with no scheduling context", 256 test_notification_binding_no_sc, config_set(CONFIG_KERNEL_MCS)) 257 258static int 259test_notification_binding_with_sc(env_t env) 260{ 261 seL4_CPtr endpoint, notification; 262 int error; 263 helper_thread_t helper; 264 volatile int state = 0; 265 266 endpoint = vka_alloc_endpoint_leaky(&env->vka); 267 notification = vka_alloc_notification_leaky(&env->vka); 268 269 create_helper_thread(env, &helper); 270 271 /* set our prio lower so the helper thread runs when we start it */ 272 set_helper_priority(env, &helper, 10); 273 error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9); 274 test_eq(error, seL4_NoError); 275 276 error = seL4_TCB_BindNotification(helper.thread.tcb.cptr, notification); 277 test_eq(error, seL4_NoError); 278 279 /* start the helper so it is waiting on the endpoint */ 280 start_helper(env, &helper, (helper_fn_t) bind0005_helper, endpoint, 281 (seL4_Word) &state, 0, 0); 282 test_eq(state, 1); 283 284 /* clear its sc */ 285 error = api_sc_unbind(helper.thread.sched_context.cptr); 286 test_eq(error, seL4_NoError); 287 288 error = api_sc_bind(helper.thread.sched_context.cptr, notification); 289 test_eq(error, seL4_NoError); 290 291 /* signal it */ 292 seL4_Signal(notification); 293 294 /* now it should have got the signal */ 295 test_eq(state, 2); 296 297 return sel4test_get_result(); 298} 299DEFINE_TEST(BIND006, "Test passing thread notification binding with a scheduling context", 300 test_notification_binding_with_sc, config_set(CONFIG_KERNEL_MCS)) 301