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, &notification);
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, &notification, 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, &notification);
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, &notification);
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, &notification, 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, &notification);
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