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 <stdlib.h>
16#include <sel4/sel4.h>
17#include <vka/object.h>
18
19#include "../helpers.h"
20
21#define MIN_LENGTH 0
22#define MAX_LENGTH (seL4_MsgMaxLength)
23
24#define FOR_EACH_LENGTH(len_var) \
25    for(int len_var = MIN_LENGTH; len_var <= MAX_LENGTH; len_var++)
26
27typedef int (*test_func_t)(seL4_Word /* endpoint */, seL4_Word /* seed */, seL4_Word /* reply */,
28                           seL4_CPtr /* extra */);
29
30static int send_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word extra)
31{
32    FOR_EACH_LENGTH(length) {
33        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, length);
34        for (int i = 0; i < length; i++) {
35            seL4_SetMR(i, seed);
36            seed++;
37        }
38        seL4_Send(endpoint, tag);
39    }
40
41    return SUCCESS;
42}
43
44static int nbsend_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word extra)
45{
46    FOR_EACH_LENGTH(length) {
47        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, length);
48        for (int i = 0; i < length; i++) {
49            seL4_SetMR(i, seed);
50            seed++;
51        }
52        seL4_NBSend(endpoint, tag);
53    }
54
55    return SUCCESS;
56}
57
58static int call_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word extra)
59{
60    test_result_t result = SUCCESS;
61    FOR_EACH_LENGTH(length) {
62        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, length);
63
64        /* Construct a message. */
65        for (int i = 0; i < length; i++) {
66            seL4_SetMR(i, seed);
67            seed++;
68        }
69
70        tag = seL4_Call(endpoint, tag);
71
72        seL4_Word actual_len = length;
73        /* Sanity check the received message. */
74        if (actual_len <= seL4_MsgMaxLength) {
75            if (actual_len != seL4_MessageInfo_get_length(tag)) {
76                result = FAILURE;
77            }
78        } else {
79            actual_len = seL4_MsgMaxLength;
80        }
81
82        for (int i = 0; i < actual_len; i++) {
83            seL4_Word mr = seL4_GetMR(i);
84            if (mr != seed) {
85                result = FAILURE;
86            }
87            seed++;
88        }
89    }
90
91    return result;
92}
93
94static int wait_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word extra)
95{
96    test_result_t result = SUCCESS;
97    FOR_EACH_LENGTH(length) {
98        seL4_MessageInfo_t tag;
99        seL4_Word sender_badge = 0;
100
101        tag = api_recv(endpoint, &sender_badge, reply);
102        seL4_Word actual_len = length;
103        if (actual_len <= seL4_MsgMaxLength) {
104            if (actual_len != seL4_MessageInfo_get_length(tag)) {
105                result = FAILURE;
106            }
107        } else {
108            actual_len = seL4_MsgMaxLength;
109        }
110
111        for (int i = 0; i < actual_len; i++) {
112            seL4_Word mr = seL4_GetMR(i);
113            if (mr != seed) {
114                result = FAILURE;
115            }
116            seed++;
117        }
118    }
119
120    return result;
121}
122
123static int nbwait_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word nbwait_should_wait)
124{
125    if (!nbwait_should_wait) {
126        return SUCCESS;
127    }
128
129    test_result_t result = SUCCESS;
130    FOR_EACH_LENGTH(length) {
131        seL4_MessageInfo_t tag;
132        seL4_Word sender_badge = 0;
133
134        tag = api_recv(endpoint, &sender_badge, reply);
135        seL4_Word actual_len = length;
136        if (actual_len <= seL4_MsgMaxLength) {
137            if (actual_len != seL4_MessageInfo_get_length(tag)) {
138                result = FAILURE;
139            }
140        } else {
141            actual_len = seL4_MsgMaxLength;
142        }
143
144        for (int i = 0; i < actual_len; i++) {
145            seL4_Word mr = seL4_GetMR(i);
146            if (mr != seed) {
147                result = FAILURE;
148            }
149            seed++;
150        }
151    }
152
153    return result;
154}
155
156static int replywait_func(seL4_Word endpoint, seL4_Word seed, seL4_CPtr reply, seL4_Word extra)
157{
158    int first = 1;
159    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
160
161    test_result_t result = SUCCESS;
162    FOR_EACH_LENGTH(length) {
163        seL4_Word sender_badge = 0;
164
165        /* First reply/wait can't reply. */
166        if (first) {
167#ifdef CONFIG_KERNEL_MCS
168            tag = seL4_NBSendRecv(endpoint, tag, endpoint, &sender_badge, reply);
169#else
170            tag = seL4_Recv(endpoint, &sender_badge);
171#endif
172            first = 0;
173        } else {
174            tag = api_reply_recv(endpoint, tag, &sender_badge, reply);
175        }
176
177        seL4_Word actual_len = length;
178        /* Sanity check the received message. */
179        if (actual_len <= seL4_MsgMaxLength) {
180            if (actual_len != seL4_MessageInfo_get_length(tag)) {
181                result = FAILURE;
182            }
183        } else {
184            actual_len = seL4_MsgMaxLength;
185        }
186
187        for (int i = 0; i < actual_len; i++) {
188            seL4_Word mr = seL4_GetMR(i);
189            if (mr != seed) {
190                result = FAILURE;
191            }
192            seed++;
193        }
194        /* Seed will have changed more if the message was truncated. */
195        for (int i = actual_len; i < length; i++) {
196            seed++;
197        }
198
199        /* Construct a reply. */
200        for (int i = 0; i < actual_len; i++) {
201            seL4_SetMR(i, seed);
202            seed++;
203        }
204    }
205
206    /* Need to do one last reply to match call. */
207    api_reply(reply, tag);
208
209    return result;
210}
211
212static int reply_and_wait_func(seL4_Word endpoint, seL4_Word seed, seL4_CPtr reply, seL4_Word unused)
213{
214    int first = 1;
215
216    test_result_t result = SUCCESS;
217    seL4_MessageInfo_t tag;
218    FOR_EACH_LENGTH(length) {
219        seL4_Word sender_badge = 0;
220
221        /* First reply/wait can't reply. */
222        if (!first) {
223            api_reply(reply, tag);
224        } else {
225            first = 0;
226        }
227
228        tag = api_recv(endpoint, &sender_badge, reply);
229
230        seL4_Word actual_len = length;
231        /* Sanity check the received message. */
232        if (actual_len <= seL4_MsgMaxLength) {
233            if (actual_len != seL4_MessageInfo_get_length(tag)) {
234                result = FAILURE;
235            }
236        } else {
237            actual_len = seL4_MsgMaxLength;
238        }
239
240        for (int i = 0; i < actual_len; i++) {
241            seL4_Word mr = seL4_GetMR(i);
242            if (mr != seed) {
243                result = FAILURE;
244            }
245            seed++;
246        }
247        /* Seed will have changed more if the message was truncated. */
248        for (int i = actual_len; i < length; i++) {
249            seed++;
250        }
251
252        /* Construct a reply. */
253        for (int i = 0; i < actual_len; i++) {
254            seL4_SetMR(i, seed);
255            seed++;
256        }
257    }
258
259    /* Need to do one last reply to match call. */
260    api_reply(reply, tag);
261
262    return result;
263}
264
265#ifdef CONFIG_KERNEL_MCS
266/* this function is expected to talk to another version of itself, second implies
267 * that this is the second to be executed */
268static int nbsendrecv_func(seL4_Word endpoint, seL4_Word seed, seL4_Word reply, seL4_Word unused)
269{
270    FOR_EACH_LENGTH(length) {
271        api_nbsend_recv(endpoint, seL4_MessageInfo_new(0, 0, 0, MAX_LENGTH), endpoint, NULL, reply);
272    }
273
274    /* signal we're done to hanging second thread */
275    seL4_NBSend(endpoint, seL4_MessageInfo_new(0, 0, 0, MAX_LENGTH));
276
277    return SUCCESS;
278}
279#endif /* CONFIG_KERNEL_MCS */
280
281static int test_ipc_pair(env_t env, test_func_t fa, test_func_t fb, bool inter_as, seL4_Word nr_cores)
282{
283    helper_thread_t thread_a, thread_b;
284    vka_t *vka = &env->vka;
285
286    UNUSED int error;
287    seL4_CPtr ep = vka_alloc_endpoint_leaky(vka);
288    seL4_Word start_number = 0xabbacafe;
289
290    seL4_CPtr a_reply = vka_alloc_reply_leaky(vka);
291    seL4_CPtr b_reply = vka_alloc_reply_leaky(vka);
292
293    /* Test sending messages of varying lengths. */
294    /* Please excuse the awful indending here. */
295    for (int core_a = 0; core_a < nr_cores; core_a++) {
296        for (int core_b = 0; core_b < nr_cores; core_b++) {
297            for (int sender_prio = 98; sender_prio <= 102; sender_prio++) {
298                for (int waiter_prio = 100; waiter_prio <= 100; waiter_prio++) {
299                    for (int sender_first = 0; sender_first <= 1; sender_first++) {
300                        ZF_LOGD("%d %s %d\n",
301                                sender_prio, sender_first ? "->" : "<-", waiter_prio);
302                        seL4_Word thread_a_arg0, thread_b_arg0;
303                        seL4_CPtr thread_a_reply, thread_b_reply;
304
305                        if (inter_as) {
306                            create_helper_process(env, &thread_a);
307
308                            cspacepath_t path;
309                            vka_cspace_make_path(&env->vka, ep, &path);
310                            thread_a_arg0 = sel4utils_copy_path_to_process(&thread_a.process, path);
311                            assert(thread_a_arg0 != -1);
312
313                            create_helper_process(env, &thread_b);
314                            thread_b_arg0 = sel4utils_copy_path_to_process(&thread_b.process, path);
315                            assert(thread_b_arg0 != -1);
316
317                            if (config_set(CONFIG_KERNEL_MCS)) {
318                                thread_a_reply = sel4utils_copy_cap_to_process(&thread_a.process, vka, a_reply);
319                                thread_b_reply = sel4utils_copy_cap_to_process(&thread_b.process, vka, b_reply);
320                            }
321                        } else {
322                            create_helper_thread(env, &thread_a);
323                            create_helper_thread(env, &thread_b);
324                            thread_a_arg0 = ep;
325                            thread_b_arg0 = ep;
326                            thread_a_reply = a_reply;
327                            thread_b_reply = b_reply;
328                        }
329
330                        set_helper_priority(env, &thread_a, sender_prio);
331                        set_helper_priority(env, &thread_b, waiter_prio);
332
333                        set_helper_affinity(env, &thread_a, core_a);
334                        set_helper_affinity(env, &thread_b, core_b);
335
336                        /* Set the flag for nbwait_func that tells it whether or not it really
337                         * should wait. */
338                        int nbwait_should_wait;
339                        nbwait_should_wait =
340                            (sender_prio < waiter_prio);
341
342                        /* Threads are enqueued at the head of the scheduling queue, so the
343                         * thread enqueued last will be run first, for a given priority. */
344                        if (sender_first) {
345                            start_helper(env, &thread_b, (helper_fn_t) fb, thread_b_arg0, start_number,
346                                         thread_b_reply, nbwait_should_wait);
347                            start_helper(env, &thread_a, (helper_fn_t) fa, thread_a_arg0, start_number,
348                                         thread_a_reply, nbwait_should_wait);
349                        } else {
350                            start_helper(env, &thread_a, (helper_fn_t) fa, thread_a_arg0, start_number,
351                                         thread_a_reply, nbwait_should_wait);
352                            start_helper(env, &thread_b, (helper_fn_t) fb, thread_b_arg0, start_number,
353                                         thread_b_reply, nbwait_should_wait);
354                        }
355
356                        test_result_t res = wait_for_helper(&thread_a);
357                        test_eq(res, SUCCESS);
358                        res = wait_for_helper(&thread_b);
359                        test_eq(res, SUCCESS);
360
361                        cleanup_helper(env, &thread_a);
362                        cleanup_helper(env, &thread_b);
363
364                        start_number += 0x71717171;
365                    }
366                }
367            }
368        }
369    }
370
371    error = cnode_delete(env, ep);
372    test_error_eq(error, seL4_NoError);
373    return sel4test_get_result();
374}
375
376static int test_send_wait(env_t env)
377{
378    return test_ipc_pair(env, send_func, wait_func, false, env->cores);
379}
380DEFINE_TEST(IPC0001, "Test SMP seL4_Send + seL4_Recv", test_send_wait, true)
381
382static int
383test_call_replywait(env_t env)
384{
385    return test_ipc_pair(env, call_func, replywait_func, false, env->cores);
386}
387DEFINE_TEST(IPC0002, "Test SMP seL4_Call + seL4_ReplyRecv", test_call_replywait, true)
388
389static int
390test_call_reply_and_wait(env_t env)
391{
392    return test_ipc_pair(env, call_func, reply_and_wait_func, false, env->cores);
393}
394DEFINE_TEST(IPC0003, "Test SMP seL4_Send + seL4_Reply + seL4_Recv", test_call_reply_and_wait, true)
395
396static int
397test_nbsend_wait(env_t env)
398{
399    return test_ipc_pair(env, nbsend_func, nbwait_func, false, 1);
400}
401DEFINE_TEST(IPC0004, "Test seL4_NBSend + seL4_Recv", test_nbsend_wait, true)
402
403static int
404test_send_wait_interas(env_t env)
405{
406    return test_ipc_pair(env, send_func, wait_func, true, env->cores);
407}
408DEFINE_TEST(IPC1001, "Test SMP inter-AS seL4_Send + seL4_Recv", test_send_wait_interas, true)
409
410static int
411test_call_replywait_interas(env_t env)
412{
413    return test_ipc_pair(env, call_func, replywait_func, true, env->cores);
414}
415DEFINE_TEST(IPC1002, "Test SMP inter-AS seL4_Call + seL4_ReplyRecv", test_call_replywait_interas, true)
416
417static int
418test_call_reply_and_wait_interas(env_t env)
419{
420    return test_ipc_pair(env, call_func, reply_and_wait_func, true, env->cores);
421}
422DEFINE_TEST(IPC1003, "Test SMP inter-AS seL4_Send + seL4_Reply + seL4_Recv", test_call_reply_and_wait_interas, true)
423
424static int
425test_nbsend_wait_interas(env_t env)
426{
427    return test_ipc_pair(env, nbsend_func, nbwait_func, true, 1);
428}
429DEFINE_TEST(IPC1004, "Test inter-AS seL4_NBSend + seL4_Recv", test_nbsend_wait_interas, true)
430
431static int
432test_ipc_abort_in_call(env_t env)
433{
434    helper_thread_t thread_a;
435    vka_t *vka = &env->vka;
436
437    seL4_CPtr ep = vka_alloc_endpoint_leaky(vka);
438    seL4_CPtr reply = vka_alloc_reply_leaky(vka);
439
440    seL4_Word start_number = 0xabbacafe;
441
442    create_helper_thread(env, &thread_a);
443    set_helper_priority(env, &thread_a, 100);
444
445    start_helper(env, &thread_a, (helper_fn_t) call_func, ep, start_number, 0, 0);
446
447    /* Wait for the endpoint that it's going to call. */
448    seL4_Word sender_badge = 0;
449
450    api_recv(ep, &sender_badge, reply);
451
452    /* Now suspend the thread. */
453    seL4_TCB_Suspend(get_helper_tcb(&thread_a));
454
455    /* Now resume the thread. */
456    seL4_TCB_Resume(get_helper_tcb(&thread_a));
457
458    /* Now suspend it again for good measure. */
459    seL4_TCB_Suspend(get_helper_tcb(&thread_a));
460
461    /* And delete it. */
462    cleanup_helper(env, &thread_a);
463
464    return sel4test_get_result();
465}
466DEFINE_TEST(IPC0010, "Test suspending an IPC mid-Call()", test_ipc_abort_in_call, true)
467
468#ifdef CONFIG_KERNEL_MCS
469#define RUNS 10
470static void
471server_fn(seL4_CPtr endpoint, seL4_CPtr reply, volatile int *state)
472{
473    /* signal the intialiser that we are done */
474    ZF_LOGD("Server call");
475    *state = *state + 1;
476    seL4_MessageInfo_t info = api_nbsend_recv(endpoint, info, endpoint, NULL, reply);
477    /* from here on we are running on borrowed time */
478    ZF_LOGD("Server awake!\n");
479    int i = 0;
480    while (i < RUNS) {
481        test_eq(seL4_GetMR(0), (seL4_Word) 12345678);
482        seL4_SetMR(0, 0xdeadbeef);
483        *state = *state + 1;
484        ZF_LOGD("Server replyRecv\n");
485        info = seL4_ReplyRecv(endpoint, info, NULL, reply);
486        i++;
487    }
488}
489
490static void proxy_fn(seL4_CPtr receive_endpoint, seL4_CPtr call_endpoint, seL4_Word reply, volatile int *state)
491{
492    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
493
494    /* signal the initialiser that we are awake */
495    ZF_LOGD("Proxy nbsendrecv, sending on ep %lu, receiving on ep %lu, reply is %lu\n", call_endpoint, receive_endpoint,
496            reply);
497    *state = *state + 1;
498    info = api_nbsend_recv(call_endpoint, info, receive_endpoint, NULL, reply);
499    /* when we get here we are running on a donated scheduling context,
500       as the initialiser has taken ours away */
501
502    int i = 0;
503    while (i < RUNS) {
504        test_eq(seL4_GetMR(0), (seL4_Word) 12345678);
505        seL4_SetMR(0, 12345678);
506
507        ZF_LOGD("Proxy call\n");
508        seL4_Call(call_endpoint, info);
509
510        test_eq(seL4_GetMR(0), (seL4_Word) 0xdeadbeef);
511
512        seL4_SetMR(0, 0xdeadbeef);
513        ZF_LOGD("Proxy replyRecv\n");
514        *state = *state + 1;
515        info = seL4_ReplyRecv(receive_endpoint, info, NULL, reply);
516        i++;
517    }
518}
519
520static void client_fn(seL4_CPtr endpoint, bool fastpath, int unused, volatile int *state)
521{
522
523    /* make the message greater than 4 in size if we do not want to hit the fastpath */
524    uint32_t length = fastpath ? 1 : 8;
525
526    int i = 0;
527    while (i < RUNS) {
528        seL4_SetMR(0, 12345678);
529        seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, length);
530
531        ZF_LOGD("Client calling on ep %lu\n", endpoint);
532        info = seL4_Call(endpoint, info);
533
534        test_eq(seL4_GetMR(0), (seL4_Word) 0xdeadbeef);
535        i++;
536        *state = *state + 1;
537    }
538}
539
540static int single_client_server_chain_test(env_t env, int fastpath, int prio_diff)
541{
542    const int num_proxies = 5;
543    int client_prio = 10;
544    int server_prio = client_prio + (prio_diff * num_proxies);
545    helper_thread_t client, server;
546    helper_thread_t proxies[num_proxies];
547    volatile int client_state = 0;
548    volatile int server_state = 0;
549    volatile int proxy_state[num_proxies];
550
551    create_helper_thread(env, &client);
552    set_helper_priority(env, &client, client_prio);
553
554    seL4_CPtr receive_endpoint = vka_alloc_endpoint_leaky(&env->vka);
555    seL4_CPtr first_endpoint = receive_endpoint;
556
557    /* create proxies */
558    for (int i = 0; i < num_proxies; i++) {
559        int prio = server_prio + (prio_diff * i);
560        proxy_state[i] = 0;
561        seL4_CPtr call_endpoint = vka_alloc_endpoint_leaky(&env->vka);
562        create_helper_thread(env, &proxies[i]);
563        set_helper_priority(env, &proxies[i], prio);
564        ZF_LOGD("Start proxy\n");
565        start_helper(env, &proxies[i], (helper_fn_t) proxy_fn, receive_endpoint,
566                     call_endpoint, proxies[i].thread.reply.cptr, (seL4_Word) &proxy_state[i]);
567
568        /* wait for proxy to initialise */
569        ZF_LOGD("Recv for proxy\n");
570        seL4_Wait(call_endpoint, NULL);
571        test_eq(proxy_state[i], 1);
572        /* now take away its scheduling context */
573        int error = api_sc_unbind(proxies[i].thread.sched_context.cptr);
574        test_eq(error, seL4_NoError);
575        receive_endpoint = call_endpoint;
576    }
577
578    /* create the server */
579    create_helper_thread(env, &server);
580    set_helper_priority(env, &server, server_prio);
581    ZF_LOGD("Start server");
582    start_helper(env, &server, (helper_fn_t) server_fn, receive_endpoint, server.thread.reply.cptr,
583                 (seL4_Word) &server_state, 0);
584    /* wait for server to initialise on our time */
585    ZF_LOGD("Recv for server");
586    seL4_Wait(receive_endpoint, NULL);
587    test_eq(server_state, 1);
588
589    /* now take it's scheduling context away */
590    int error = api_sc_unbind(server.thread.sched_context.cptr);
591    test_eq(error, seL4_NoError);
592
593    ZF_LOGD("Start client");
594    start_helper(env, &client, (helper_fn_t) client_fn, first_endpoint,
595                 fastpath, RUNS, (seL4_Word) &client_state);
596
597    /* sleep and let the testrun */
598    ZF_LOGD("wait_for_helper() for client");
599    wait_for_helper(&client);
600
601    test_eq(server_state, RUNS + 1);
602    test_eq(client_state, RUNS);
603    for (int i = 0; i < num_proxies; i++) {
604        test_eq(proxy_state[i], RUNS + 1);
605    }
606
607    return sel4test_get_result();
608}
609
610int test_single_client_slowpath_same_prio(env_t env)
611{
612    return single_client_server_chain_test(env, 0, 0);
613}
614DEFINE_TEST(IPC0011, "Client-server inheritance: slowpath, same prio", test_single_client_slowpath_same_prio,
615            config_set(CONFIG_KERNEL_MCS))
616
617int test_single_client_slowpath_higher_prio(env_t env)
618{
619    return single_client_server_chain_test(env, 0, 1);
620}
621DEFINE_TEST(IPC0012, "Client-server inheritance: slowpath, client higher prio",
622            test_single_client_slowpath_higher_prio, config_set(CONFIG_KERNEL_MCS))
623
624int test_single_client_slowpath_lower_prio(env_t env)
625{
626    return single_client_server_chain_test(env, 0, -1);
627}
628DEFINE_TEST(IPC0013, "Client-server inheritance: slowpath, client lower prio",
629            test_single_client_slowpath_lower_prio, config_set(CONFIG_KERNEL_MCS))
630
631int test_single_client_fastpath_higher_prio(env_t env)
632{
633    return single_client_server_chain_test(env, 1, 1);
634}
635DEFINE_TEST(IPC0014, "Client-server inheritance: fastpath, client higher prio", test_single_client_fastpath_higher_prio,
636            config_set(CONFIG_KERNEL_MCS))
637
638int
639test_single_client_fastpath_same_prio(env_t env)
640{
641    return single_client_server_chain_test(env, 1, 0);
642}
643DEFINE_TEST(IPC0015, "Client-server inheritance: fastpath, client same prio", test_single_client_fastpath_same_prio,
644            config_set(CONFIG_KERNEL_MCS))
645
646static void
647ipc0016_call_once_fn(seL4_CPtr endpoint, volatile int *state)
648{
649    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
650    *state = *state + 1;
651    ZF_LOGD("Call %d\n", *state);
652    seL4_Call(endpoint, info);
653    ZF_LOGD("Resumed with reply\n");
654    *state = *state + 1;
655}
656
657static void ipc0016_reply_once_fn(seL4_CPtr endpoint, seL4_CPtr reply)
658{
659    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
660
661    /* send initialisation context back */
662    ZF_LOGD("seL4_nbsendrecv\n");
663    api_nbsend_recv(endpoint, info, endpoint, NULL, reply);
664
665    /* reply */
666    ZF_LOGD("Reply\n");
667    seL4_Send(reply, info);
668
669    /* wait (keeping sc) */
670    ZF_LOGD("Recv\n");
671    seL4_Wait(endpoint, NULL);
672
673    test_check(!"should not get here");
674}
675
676static int test_transfer_on_reply(env_t env)
677{
678    volatile int state = 1;
679    helper_thread_t client, server;
680
681    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
682    create_helper_thread(env, &client);
683    create_helper_thread(env, &server);
684
685    set_helper_priority(env, &client, 10);
686    set_helper_priority(env, &server, 11);
687
688    start_helper(env, &server, (helper_fn_t) ipc0016_reply_once_fn, endpoint, server.thread.reply.cptr, 0, 0);
689
690    /* wait for server to initialise */
691    seL4_Wait(endpoint, NULL);
692    /* now remove the schedluing context */
693    int error = api_sc_unbind(server.thread.sched_context.cptr);
694    test_eq(error, seL4_NoError);
695
696    /* start the client */
697    start_helper(env, &client, (helper_fn_t) ipc0016_call_once_fn, endpoint,
698                 (seL4_Word) &state, 0, 0);
699    /* the server will attempt to steal the clients scheduling context
700     * by using seL4_Send instead of seL4_ReplyWait. However,
701     * a reply cap is a guarantee that a scheduling context will be returned,
702     * so it does return to the client and the server hangs.
703     */
704    wait_for_helper(&client);
705
706    test_eq(state, 3);
707
708    return sel4test_get_result();
709}
710DEFINE_TEST(IPC0016, "Test reply returns scheduling context",
711            test_transfer_on_reply, config_set(CONFIG_KERNEL_MCS));
712
713/* used by ipc0017 and ipc0019 */
714static void sender(seL4_CPtr endpoint, volatile int *state)
715{
716    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
717    ZF_LOGD("Client send\n");
718    *state = 1;
719    seL4_Send(endpoint, info);
720    *state = 2;
721}
722
723static void wait_server(seL4_CPtr endpoint, int messages)
724{
725    /* signal test that we are initialised */
726    seL4_Send(endpoint, seL4_MessageInfo_new(0, 0, 0, 0));
727    int i = 0;
728    while (i < messages) {
729        ZF_LOGD("Server wait\n");
730        seL4_Wait(endpoint, NULL);
731        i++;
732    }
733}
734
735static int test_send_to_no_sc(env_t env)
736{
737    /* sends should block until the server gets a scheduling context.
738     * nb sends should not block */
739    helper_thread_t server, client1, client2;
740
741    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
742
743    create_helper_thread(env, &server);
744    create_helper_thread(env, &client1);
745    create_helper_thread(env, &client2);
746
747    set_helper_priority(env, &server, 10);
748    set_helper_priority(env, &client1, 9);
749    set_helper_priority(env, &client2, 9);
750
751    const int num_server_messages = 4;
752    start_helper(env, &server, (helper_fn_t) wait_server, endpoint, num_server_messages, 0, 0);
753    seL4_Wait(endpoint, NULL);
754
755    int error = api_sc_unbind(server.thread.sched_context.cptr);
756    test_eq(error, seL4_NoError);
757
758    /* this message should not result in the server being scheduled */
759    ZF_LOGD("NBSend");
760    seL4_SetMR(0, 12345678);
761    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1);
762    seL4_NBSend(endpoint, info);
763    test_eq(seL4_GetMR(0), (seL4_Word)12345678);
764
765    /* start clients */
766    volatile int state1 = 0;
767    volatile int state2 = 0;
768    start_helper(env, &client1, (helper_fn_t) sender, endpoint, (seL4_Word) &state1, 0, 0);
769    start_helper(env, &client2, (helper_fn_t) sender, endpoint, (seL4_Word) &state2, 0, 0);
770
771    /* set our prio down, both clients should block as the server cannot
772     * run without a schedluing context */
773    error = seL4_TCB_SetPriority(env->tcb, env->tcb, 8);
774    test_eq(error, seL4_NoError);
775    test_eq(state1, 1);
776    test_eq(state2, 1);
777
778    /* restore the servers schedluing context */
779    error = api_sc_bind(server.thread.sched_context.cptr, server.thread.tcb.cptr);
780    test_eq(error, seL4_NoError);
781
782    /* now the clients should be unblocked */
783    test_eq(state1, 2);
784    test_eq(state2, 2);
785
786    /* this should work */
787    seL4_NBSend(endpoint, info);
788
789    /* and so should this */
790    seL4_Send(endpoint, info);
791
792    /* if the server received the correct number of messages it should now be done */
793    wait_for_helper(&server);
794
795    return sel4test_get_result();
796}
797DEFINE_TEST(IPC0017, "Test seL4_Send/seL4_NBSend to a server with no scheduling context", test_send_to_no_sc,
798            config_set(CONFIG_KERNEL_MCS))
799
800static void
801ipc0018_helper(seL4_CPtr endpoint, volatile int *state)
802{
803    *state = 1;
804
805    while (1) {
806        ZF_LOGD("Send");
807        seL4_Send(endpoint, seL4_MessageInfo_new(0, 0, 0, 0));
808        *state = *state + 1;
809    }
810}
811
812static int test_receive_no_sc(env_t env)
813{
814    helper_thread_t client;
815    volatile int state = 0;
816    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
817    create_helper_thread(env, &client);
818    set_helper_priority(env, &client, 10);
819    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9);
820    test_eq(error, seL4_NoError);
821
822    /* start the client, it will increment state and send a message */
823    start_helper(env, &client, (helper_fn_t) ipc0018_helper, endpoint,
824                 (seL4_Word) &state, 0, 0);
825
826    test_eq(state, 1);
827
828    /* clear the clients scheduling context */
829    ZF_LOGD("Unbind scheduling context");
830    error = api_sc_unbind(client.thread.sched_context.cptr);
831    test_eq(error, seL4_NoError);
832
833    /* now we should be able to receive the message, but since the client
834     * no longer has a schedluing context it should not run
835     */
836    ZF_LOGD("Recv");
837    seL4_Wait(endpoint, NULL);
838
839    /* check thread has not run */
840    test_eq(state, 1);
841
842    /* now set the schedluing context again */
843    error = api_sc_bind(client.thread.sched_context.cptr,
844                        client.thread.tcb.cptr);
845    test_eq(error, seL4_NoError);
846    test_eq(state, 2);
847
848    /* now get another message */
849    seL4_Wait(endpoint, NULL);
850    test_eq(state, 3);
851
852    /* and another, to check client is well and truly running */
853    seL4_Wait(endpoint, NULL);
854    test_eq(state, 4);
855
856    return sel4test_get_result();
857}
858DEFINE_TEST(IPC0018, "Test receive from a client with no scheduling context",
859            test_receive_no_sc, config_set(CONFIG_KERNEL_MCS));
860
861static int delete_sc_client_sending_on_endpoint(env_t env)
862{
863    helper_thread_t client;
864    volatile int state = 0;
865
866    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
867
868    create_helper_thread(env, &client);
869    set_helper_priority(env, &client, 10);
870
871    /* set our prio below the helper */
872    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9);
873    test_eq(error, seL4_NoError);
874
875    start_helper(env, &client, (helper_fn_t) sender, endpoint, (seL4_Word) &state, 0, 0);
876
877    /* the client will run and send on the endpoint */
878    test_eq(state, 1);
879
880    /* now delete the scheduling context - this should unbind the client
881     * but not remove the message */
882
883    ZF_LOGD("Destroying schedluing context");
884    vka_free_object(&env->vka, &client.thread.sched_context);
885
886    ZF_LOGD("seL4_Wait");
887    seL4_Wait(endpoint, NULL);
888    test_eq(state, 1);
889
890    return sel4test_get_result();
891}
892DEFINE_TEST(IPC0019, "Test deleteing the scheduling context while a client is sending on an endpoint",
893            delete_sc_client_sending_on_endpoint, config_set(CONFIG_KERNEL_MCS));
894
895static void ipc0020_helper(seL4_CPtr endpoint, volatile int *state)
896{
897    *state = 1;
898    while (1) {
899        ZF_LOGD("Recv");
900        seL4_Wait(endpoint, NULL);
901        *state = *state + 1;
902    }
903}
904
905static int delete_sc_client_waiting_on_endpoint(env_t env)
906{
907    helper_thread_t waiter;
908    volatile int state = 0;
909    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
910
911    create_helper_thread(env, &waiter);
912    set_helper_priority(env, &waiter, 10);
913    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, 9);
914    start_helper(env, &waiter, (helper_fn_t) ipc0020_helper, endpoint, (seL4_Word) &state, 0, 0);
915
916    /* helper should run and block receiving on endpoint */
917    test_eq(state, 1);
918
919    /* destroy scheduling context */
920    vka_free_object(&env->vka, &waiter.thread.sched_context);
921
922    /* send message */
923    seL4_Send(endpoint, seL4_MessageInfo_new(0, 0, 0, 0));
924
925    /* thread should not have moved */
926    test_eq(state, 1);
927
928    /* now create a new scheduling context and give it to the thread */
929    seL4_CPtr sched_context = vka_alloc_sched_context_leaky(&env->vka);
930    error = api_sched_ctrl_configure(simple_get_sched_ctrl(&env->simple, 0), sched_context,
931                                     1000 * US_IN_S, 1000 * US_IN_S, 0, 0);
932    test_eq(error, seL4_NoError);
933
934    error = api_sc_bind(sched_context, waiter.thread.tcb.cptr);
935    test_eq(error, seL4_NoError);
936
937    /* now the thread should run and receive the message */
938    test_eq(state, 2);
939
940    return sel4test_get_result();
941}
942DEFINE_TEST(IPC0020, "test deleting a scheduling context while the client is waiting on an endpoint",
943            delete_sc_client_waiting_on_endpoint, config_set(CONFIG_KERNEL_MCS));
944
945static void ipc21_faulter_fn(int *addr)
946{
947    ZF_LOGD("Fault at %p\n", addr);
948    *addr = 0xdeadbeef;
949    ZF_LOGD("Resumed\n");
950}
951
952static void ipc21_fault_handler_fn(seL4_CPtr endpoint, vspace_t *vspace, reservation_t *res, seL4_CPtr reply)
953{
954    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
955    info = api_nbsend_recv(endpoint, info, endpoint, NULL, reply);
956
957    while (1) {
958        test_check(seL4_isVMFault_tag(info));
959        void *addr = (void *) seL4_GetMR(seL4_VMFault_Addr);
960        ZF_LOGD("Handling fault at %p\n", addr);
961        int error = vspace_new_pages_at_vaddr(vspace, addr, 1, seL4_PageBits, *res);
962        test_eq(error, seL4_NoError);
963        seL4_ReplyRecv(endpoint, info, NULL, reply);
964    }
965}
966
967static int test_fault_handler_donated_sc(env_t env)
968{
969    helper_thread_t handler, faulter;
970    void *vaddr = NULL;
971
972    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
973    reservation_t res = vspace_reserve_range(&env->vspace, PAGE_SIZE_4K, seL4_AllRights, 1, &vaddr);
974    test_check(vaddr != NULL);
975
976    create_helper_thread(env, &handler);
977    create_helper_thread(env, &faulter);
978
979    /* start fault handler */
980    start_helper(env, &handler, (helper_fn_t) ipc21_fault_handler_fn,
981                 endpoint, (seL4_Word) &env->vspace, (seL4_Word) &res,
982                 handler.thread.reply.cptr);
983
984    /* wait for it to initialise */
985    seL4_Wait(endpoint, NULL);
986
987    /* now remove its scheduling context */
988    int error = api_sc_unbind(handler.thread.sched_context.cptr);
989    test_eq(error, seL4_NoError);
990
991    /* set fault handler */
992    seL4_Word data = api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits);
993    error = api_tcb_set_space(faulter.thread.tcb.cptr, endpoint,
994                              env->cspace_root, data, env->page_directory, seL4_NilData);
995    test_eq(error, seL4_NoError);
996
997    /* start the fault handler */
998    start_helper(env, &faulter, (helper_fn_t) ipc21_faulter_fn, (seL4_Word) vaddr, 0, 0, 0);
999    /* the faulter handler will restore the faulter and we should not block here */
1000    wait_for_helper(&faulter);
1001
1002    return sel4test_get_result();
1003}
1004DEFINE_TEST(IPC0021, "Test fault handler on donated scheduling context",
1005            test_fault_handler_donated_sc, config_set(CONFIG_KERNEL_MCS));
1006
1007static void ipc22_client_fn(seL4_CPtr endpoint, volatile int *state)
1008{
1009    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
1010    ZF_LOGD("Client init");
1011    seL4_Call(endpoint, info);
1012    *state = *state + 1;
1013    ZF_LOGD("Client receive reply");
1014}
1015
1016static seL4_CPtr ipc22_go;
1017
1018static void ipc22_server_fn(seL4_CPtr init_ep, seL4_CPtr reply_cap)
1019{
1020    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
1021    ZF_LOGD("Server init\n");
1022
1023    /* wait for the signal to go from the test runner -
1024     * we have to block here to wait for all the clients to
1025     * start and queue up - otherwise they will all be served
1026     * by the same server and the point is to test stack spawning  */
1027    api_nbsend_wait(init_ep, info, init_ep, NULL);
1028
1029    ZF_LOGD("Server reply to fwded cap\n");
1030    seL4_Send(reply_cap, info);
1031}
1032
1033static void ipc22_stack_spawner_fn(env_t env, seL4_CPtr endpoint, int server_prio, seL4_Word unused)
1034{
1035    helper_thread_t servers[RUNS];
1036    seL4_CPtr init_ep = vka_alloc_endpoint_leaky(&env->vka);
1037
1038    /* first we signal to endpoint to tell the test runner we are ready */
1039    seL4_CPtr first_ep = endpoint;
1040    ZF_LOGD("Stack spawner init");
1041
1042    for (int i = 0; i < RUNS; i++) {
1043        create_helper_thread(env, &servers[i]);
1044        set_helper_priority(env, &servers[i], server_prio);
1045
1046        api_nbsend_recv(first_ep, seL4_MessageInfo_new(0, 0, 0, 0), endpoint, NULL,
1047                        servers[i].thread.reply.cptr);
1048
1049        /* after the first nbsend, we want to signal the clients via init_ep */
1050        first_ep = init_ep;
1051
1052        ZF_LOGD("Got another client\n");
1053        /* start helper and allow to initialise */
1054        ZF_LOGD("Spawn server\n");
1055        start_helper(env, &servers[i], (helper_fn_t) ipc22_server_fn, init_ep,
1056                     servers[i].thread.reply.cptr, 0, 0);
1057        /* wait for it to block */
1058        seL4_Wait(init_ep, NULL);
1059
1060        /*  now remove the schedling context */
1061        int error =  api_sc_unbind(servers[i].thread.sched_context.cptr);
1062        test_eq(error, seL4_NoError);
1063    }
1064
1065    /* signal the last client */
1066    api_nbsend_wait(first_ep, seL4_MessageInfo_new(0, 0, 0, 0), endpoint, NULL);
1067}
1068
1069static int test_stack_spawning_server(env_t env)
1070{
1071    helper_thread_t clients[RUNS];
1072    helper_thread_t stack_spawner;
1073    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
1074    ipc22_go = vka_alloc_endpoint_leaky(&env->vka);
1075    volatile int state = 0;
1076    int our_prio = 10;
1077
1078    create_helper_thread(env, &stack_spawner);
1079    set_helper_mcp(env, &stack_spawner, seL4_MaxPrio);
1080    start_helper(env, &stack_spawner, (helper_fn_t) ipc22_stack_spawner_fn,
1081                 (seL4_Word) env, endpoint, our_prio + 1, RUNS);
1082
1083    /* wait for stack spawner to init */
1084    ZF_LOGD("Wait for stack spawner to init");
1085    seL4_Wait(endpoint, NULL);
1086
1087    /* take away scheduling context */
1088    int error = api_sc_unbind(stack_spawner.thread.sched_context.cptr);
1089    test_eq(error, seL4_NoError);
1090
1091    set_helper_priority(env, &stack_spawner, our_prio + 2);
1092
1093    error = seL4_TCB_SetPriority(env->tcb, env->tcb, our_prio);
1094    test_eq(error, seL4_NoError);
1095
1096    set_helper_priority(env, &stack_spawner, our_prio + 2);
1097    set_helper_mcp(env, &stack_spawner, seL4_MaxPrio - 1);
1098
1099    ZF_LOGD("Starting clients");
1100    /* create and start clients */
1101    for (int i = 0; i < RUNS; i++) {
1102        create_helper_thread(env, &clients[i]);
1103        set_helper_priority(env, &clients[i], our_prio + 1);
1104        start_helper(env, &clients[i], (helper_fn_t) ipc22_client_fn, endpoint, (seL4_Word) &state, i, 0);
1105    }
1106
1107    /* set our priority down so servers can run */
1108    error = seL4_TCB_SetPriority(env->tcb, env->tcb, our_prio - 2);
1109    test_eq(error, seL4_NoError);
1110
1111    for (int i = 0; i < RUNS; i++) {
1112        wait_for_helper(&clients[i]);
1113    }
1114
1115    ZF_LOGD("Done");
1116
1117    /* make sure all the clients got served */
1118    test_eq(state, RUNS);
1119
1120    return sel4test_get_result();
1121}
1122DEFINE_TEST(IPC0022, "Test stack spawning server with scheduling context donation",
1123            test_stack_spawning_server, config_set(CONFIG_KERNEL_MCS));
1124
1125static void ipc23_client_fn(seL4_CPtr ep, volatile int *state)
1126{
1127    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
1128
1129    *state = 1;
1130
1131    ZF_LOGD("Call");
1132    seL4_Call(ep, info);
1133    /* should not get here */
1134    *state = 2;
1135}
1136
1137/* used by ipc0023 and 0024 */
1138static void ipc23_server_fn(seL4_CPtr client_ep, seL4_CPtr wait_ep, seL4_CPtr reply)
1139{
1140    /* send to the wait_ep to tell the test we are initialised,
1141     * then wait on the client_ep to receive a scheduling context */
1142    api_nbsend_recv(wait_ep, seL4_MessageInfo_new(0, 0, 0, 0), client_ep, NULL, reply);
1143
1144    /* now block */
1145    seL4_Wait(wait_ep, NULL);
1146}
1147
1148static int test_delete_reply_cap_sc(env_t env)
1149{
1150    helper_thread_t client, server;
1151    volatile int state = 0;
1152    seL4_CPtr client_ep = vka_alloc_endpoint_leaky(&env->vka);
1153    seL4_CPtr server_ep = vka_alloc_endpoint_leaky(&env->vka);
1154
1155    create_helper_thread(env, &client);
1156    create_helper_thread(env, &server);
1157
1158    start_helper(env, &client, (helper_fn_t) ipc23_client_fn, client_ep,
1159                 (seL4_Word) &state, 0, 0);
1160    start_helper(env, &server, (helper_fn_t) ipc23_server_fn, client_ep,
1161                 server_ep, server.thread.reply.cptr, 0);
1162
1163    /* wait for server to init */
1164    seL4_Wait(server_ep, NULL);
1165    /* take its scheduling context away */
1166    int error = api_sc_unbind(server.thread.sched_context.cptr);
1167    test_eq(error, seL4_NoError);
1168
1169    /* set the client and server prio higher than ours so they can run */
1170    error = seL4_TCB_SetPriority(env->tcb, env->tcb, 10);
1171    test_eq(error, seL4_NoError);
1172
1173    set_helper_priority(env, &client, 11);
1174    set_helper_priority(env, &server, 11);
1175
1176    /* now the client should have run and called the server*/
1177    test_eq(state, 1);
1178
1179    /* delete scheduling context */
1180    vka_free_object(&env->vka, & client.thread.sched_context);
1181
1182    /* try to reply, the client should not run as it has no scheduling context */
1183    seL4_Signal(server.thread.reply.cptr);
1184    test_eq(state, 1);
1185
1186    return sel4test_get_result();
1187}
1188DEFINE_TEST(IPC0023, "Test deleting the scheduling context tracked in a reply cap",
1189            test_delete_reply_cap_sc, config_set(CONFIG_KERNEL_MCS))
1190
1191static int test_delete_reply_cap_then_sc(env_t env)
1192{
1193    helper_thread_t client, server;
1194    volatile int state = 0;
1195
1196    seL4_CPtr client_ep = vka_alloc_endpoint_leaky(&env->vka);
1197    seL4_CPtr server_ep = vka_alloc_endpoint_leaky(&env->vka);
1198
1199    create_helper_thread(env, &client);
1200    create_helper_thread(env, &server);
1201
1202    set_helper_priority(env, &client, 11);
1203    set_helper_priority(env, &server, 11);
1204    /* start server */
1205    start_helper(env, &server, (helper_fn_t) ipc23_server_fn, client_ep,
1206                 server_ep, server.thread.reply.cptr, 0);
1207
1208    ZF_LOGD("Waiting for server");
1209    /* wait for server to init */
1210    seL4_Wait(server_ep, NULL);
1211
1212    /* set our prio down so client can run */
1213    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, 10);
1214    test_eq(error, 0);
1215
1216    ZF_LOGD("Removed sc\n");
1217    /* remove schedluing context */
1218    error = api_sc_unbind(server.thread.sched_context.cptr);
1219    test_eq(error, seL4_NoError);
1220
1221    ZF_LOGD("Start client");
1222    /* start client */
1223    start_helper(env, &client, (helper_fn_t) ipc23_client_fn, client_ep,
1224                 (seL4_Word) &state, 0, 0);
1225    /* client should have started */
1226    test_eq(state, 1);
1227
1228    ZF_LOGD("Steal reply cap ");
1229    /* nuke the reply cap */
1230    vka_free_object(&env->vka, &server.thread.reply);
1231    /* nuke the sc */
1232    vka_free_object(&env->vka, &client.thread.sched_context);
1233
1234    ZF_LOGD("Done");
1235    /* caller should not run */
1236    test_eq(1, state);
1237
1238    return sel4test_get_result();
1239}
1240DEFINE_TEST(IPC0024, "Test deleting the reply cap in the scheduling context",
1241            test_delete_reply_cap_then_sc, config_set(CONFIG_KERNEL_MCS));
1242
1243static int test_nbsendrecv(env_t env)
1244{
1245    return test_ipc_pair(env, (test_func_t) nbsendrecv_func, (test_func_t) nbsendrecv_func, false, env->cores);
1246}
1247DEFINE_TEST(IPC0025, "Test seL4_nbsendrecv + seL4_nbsendrecv", test_nbsendrecv, config_set(CONFIG_KERNEL_MCS))
1248
1249static int test_nbsendrecv_interas(env_t env)
1250{
1251    return test_ipc_pair(env, (test_func_t) nbsendrecv_func, (test_func_t) nbsendrecv_func, false, env->cores);
1252}
1253DEFINE_TEST(IPC0026, "Test interas seL4_nbsendrecv + seL4_nbsendrecv", test_nbsendrecv_interas,
1254            config_set(CONFIG_KERNEL_MCS))
1255
1256static int
1257test_sched_donation_low_prio_server(env_t env)
1258{
1259    helper_thread_t client, server, server2;
1260    seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka);
1261
1262    create_helper_thread(env, &client);
1263    create_helper_thread(env, &server);
1264    create_helper_thread(env, &server2);
1265
1266    int error = create_passive_thread(env, &server, (helper_fn_t) replywait_func, ep, 0, server.thread.reply.cptr, 0);
1267    test_eq(error, seL4_NoError);
1268
1269    /* make client higher prio than server */
1270    set_helper_priority(env, &server, 1);
1271    set_helper_priority(env, &client, 2);
1272
1273    ZF_LOGD("Start client");
1274    start_helper(env, &client, (helper_fn_t) call_func, ep, 0, 0, 0);
1275
1276    ZF_LOGD("Wait for helper");
1277    wait_for_helper(&client);
1278
1279    /* give server a sc to finish on */
1280    error = api_sc_bind(server.thread.sched_context.cptr,
1281                        server.thread.tcb.cptr);
1282    test_eq(error, 0);
1283    wait_for_helper(&server);
1284
1285    /* now try again, but start the client first */
1286    start_helper(env, &client, (helper_fn_t) call_func, ep, 0, 0, 0);
1287    error = create_passive_thread(env, &server2, (helper_fn_t) replywait_func, ep, 0,
1288                                  server2.thread.reply.cptr, 0);
1289
1290    test_eq(error, seL4_NoError);
1291
1292    ZF_LOGD("Wait for helper");
1293    wait_for_helper(&client);
1294    error = api_sc_bind(server2.thread.sched_context.cptr,
1295                        server2.thread.tcb.cptr);
1296    test_eq(error, 0);
1297    wait_for_helper(&server2);
1298
1299    return sel4test_get_result();
1300}
1301DEFINE_TEST(IPC0027, "Test sched donation to low prio server", test_sched_donation_low_prio_server,
1302            config_set(CONFIG_KERNEL_MCS))
1303
1304static void
1305ipc28_server_fn(seL4_CPtr ep, seL4_CPtr reply, volatile int *state)
1306{
1307    api_nbsend_recv(ep, seL4_MessageInfo_new(0, 0, 0, 0), ep, NULL, reply);
1308    while (1) {
1309        *state = *state + 1;
1310        seL4_ReplyRecv(ep, seL4_MessageInfo_new(0, 0, 0, 0), NULL, reply);
1311    }
1312}
1313
1314static int ipc28_client_fn(seL4_CPtr ep, volatile int *state)
1315{
1316
1317    while (*state < RUNS) {
1318        seL4_Call(ep, seL4_MessageInfo_new(0, 0, 0, 0));
1319        *state = *state + 1;
1320    }
1321
1322    return 0;
1323}
1324
1325static int test_sched_donation_cross_core(env_t env)
1326{
1327    seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka);
1328    helper_thread_t clients[env->cores - 1];
1329    helper_thread_t server;
1330    volatile int states[env->cores - 1];
1331    volatile seL4_Word server_state = 0;
1332
1333    /* start server on core 0 */
1334    create_helper_thread(env, &server);
1335
1336    /* start a client on each other core */
1337    for (int i = 0; i < env->cores - 1; i++) {
1338        states[i] = 0;
1339        create_helper_thread(env, &clients[i]);
1340        set_helper_affinity(env, &clients[i], i + 1);
1341    }
1342
1343    /* start server */
1344    start_helper(env, &server, (helper_fn_t) ipc28_server_fn, ep, get_helper_reply(&server),
1345                 (seL4_Word) &server_state, 0);
1346
1347    /* wait for server to init */
1348    seL4_Wait(ep, NULL);
1349
1350    /* convert to passive */
1351    int error = api_sc_unbind(get_helper_sched_context(&server));
1352    test_eq(error, seL4_NoError);
1353
1354    /* start clients */
1355    for (int i = 0; i < env->cores - 1; i++) {
1356        start_helper(env, &clients[i], (helper_fn_t) ipc28_client_fn, ep, (seL4_Word) &states[i], 0, 0);
1357    }
1358
1359    /* wait for the clients */
1360    for (int i = 0; i < env->cores - 1; i++) {
1361        error = wait_for_helper(&clients[i]);
1362        test_eq(error, 0);
1363        test_eq(states[i], RUNS);
1364    }
1365
1366    test_eq(server_state, RUNS * (env->cores - 1));
1367
1368    return sel4test_get_result();
1369}
1370DEFINE_TEST(IPC0028, "Cross core sched donation", test_sched_donation_cross_core,
1371            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_MAX_NUM_NODES) &&CONFIG_MAX_NUM_NODES > 1);
1372#endif /* CONFIG_KERNEL_MCS */
1373