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