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 <stdio.h>
16#include <sel4/sel4.h>
17#include <stdlib.h>
18#include <string.h>
19#include <vka/object.h>
20
21#include "../helpers.h"
22
23#define PRIORITY_FUDGE 1
24
25#define MIN_PRIO seL4_MinPrio
26#define MAX_PRIO (OUR_PRIO - 1)
27#define NUM_PRIOS (MAX_PRIO - MIN_PRIO + 1)
28
29#define CHECK_STEP(var, x) do { \
30        test_check((var) == x); \
31        ZF_LOGD(#x "..."); \
32        var = (x) + 1; \
33    } while (0)
34
35static int counter_func(volatile seL4_Word *counter)
36{
37    while (1) {
38        (*counter)++;
39    }
40    return 0;
41}
42
43/*
44 * Test that thread suspending works when idling the system.
45 * Note: This test non-deterministically fails. If you find only this test
46 * try re-running the test suite.
47 */
48static int test_thread_suspend(env_t env)
49{
50    helper_thread_t t1;
51    volatile seL4_Word counter;
52    ZF_LOGD("test_thread_suspend\n");
53    create_helper_thread(env, &t1);
54
55    set_helper_priority(env, &t1, 100);
56    start_helper(env, &t1, (helper_fn_t) counter_func, (seL4_Word) &counter, 0, 0, 0);
57
58    sel4test_periodic_start(env, 10 * NS_IN_MS);
59
60    seL4_Word old_counter;
61
62    /* Let the counter thread run. */
63    sel4test_ntfn_timer_wait(env);
64
65    old_counter = counter;
66
67    /* Let it run again. */
68    sel4test_ntfn_timer_wait(env);
69
70    /* Now, counter should have moved. */
71    test_check(counter != old_counter);
72    old_counter = counter;
73
74    /* Suspend the thread, and wait again. */
75    seL4_TCB_Suspend(get_helper_tcb(&t1));
76    sel4test_ntfn_timer_wait(env);
77
78    /* Counter should not have moved. */
79    test_check(counter == old_counter);
80    old_counter = counter;
81
82    /* Check once more for good measure. */
83    sel4test_ntfn_timer_wait(env);
84
85    /* Counter should not have moved. */
86    test_check(counter == old_counter);
87    old_counter = counter;
88
89    /* Resume the thread and check it does move. */
90    seL4_TCB_Resume(get_helper_tcb(&t1));
91    sel4test_ntfn_timer_wait(env);
92    test_check(counter != old_counter);
93
94    /* Done. */
95    cleanup_helper(env, &t1);
96
97    return sel4test_get_result();
98}
99DEFINE_TEST(SCHED0000, "Test suspending and resuming a thread (flaky)", test_thread_suspend,
100            config_set(CONFIG_HAVE_TIMER)
101            && !config_set(CONFIG_ARCH_RISCV))
102
103/*
104 * Test TCB Resume on self.
105 */
106static int
107test_resume_self(struct env *env)
108{
109    ZF_LOGD("Starting test_resume_self\n");
110    /* Ensure nothing bad happens if we resume ourselves. */
111    int error = seL4_TCB_Resume(env->tcb);
112    test_error_eq(error, seL4_NoError);
113    ZF_LOGD("Ending test_resume_self\n");
114    return sel4test_get_result();
115}
116DEFINE_TEST(SCHED0002, "Test resuming ourselves", test_resume_self, true)
117
118/*
119 * Test TCB Suspend/Resume.
120 */
121static volatile int suspend_test_step;
122static int suspend_test_helper_2a(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b)
123{
124    /* Helper function that runs at a higher priority. */
125
126    /* Start here. */
127    CHECK_STEP(suspend_test_step, 0);
128
129    /* Wait for a timer tick to make 2b run. */
130    while (suspend_test_step == 1)
131        /* spin */{
132        ;
133    }
134
135    /* Suspend helper 2b. */
136    int error = seL4_TCB_Suspend(t2b);
137    test_check(!error);
138
139    CHECK_STEP(suspend_test_step, 2);
140
141    /* Now suspend ourselves, passing control to the low priority process to
142     * resume 2b. */
143    error = seL4_TCB_Suspend(t2a);
144    test_check(!error);
145
146    return sel4test_get_result();
147}
148
149static int suspend_test_helper_2b(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b)
150{
151    /* Wait for 2a to get suspend_test_step set to 1. */
152    test_check(suspend_test_step == 0 || suspend_test_step == 1);
153    while (suspend_test_step == 0) {
154        seL4_Yield();
155    }
156
157    /* Timer tick should bring us back here. */
158    CHECK_STEP(suspend_test_step, 1);
159
160    /* Now spin and wait for us to be suspended. */
161    while (suspend_test_step == 2) {
162        seL4_Yield();
163    }
164
165    /* When we wake up suspend_test_step should be 4. */
166    CHECK_STEP(suspend_test_step, 4);
167
168    return sel4test_get_result();
169}
170
171static int suspend_test_helper_1(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b)
172{
173    CHECK_STEP(suspend_test_step, 3);
174
175    /* Our sole job is to wake up 2b. */
176    int error = seL4_TCB_Resume(t2b);
177    test_check(!error);
178
179    /* We should have been preempted immediately, so by the time we run again,
180     * the suspend_test_step should be 5. */
181
182#if 1 // WE HAVE A BROKEN SCHEDULER IN SEL4
183    /* FIXME: The seL4 scheduler is broken, and seL4_TCB_Resume will not
184     * preempt. The only way to get preempted is to force it ourselves (or wait
185     * for a timer tick). */
186    seL4_Yield();
187#endif
188
189    CHECK_STEP(suspend_test_step, 5);
190
191    return sel4test_get_result();
192}
193
194static int test_suspend(struct env *env)
195{
196    helper_thread_t thread1;
197    helper_thread_t thread2a;
198    helper_thread_t thread2b;
199
200    ZF_LOGD("Starting test_suspend\n");
201
202    create_helper_thread(env, &thread1);
203    ZF_LOGD("Show me\n");
204    create_helper_thread(env, &thread2a);
205
206    create_helper_thread(env, &thread2b);
207
208    /* First set all the helper threads to have unique priorities
209     * and then start them in order of priority. This is so when
210     * the 'start_helper' function does an IPC to the helper
211     * thread, it doesn't allow one of the already started helper
212     * threads to run at all */
213    set_helper_priority(env, &thread1, 0);
214    set_helper_priority(env, &thread2a, 1);
215    set_helper_priority(env, &thread2b, 2);
216
217    start_helper(env, &thread1, (helper_fn_t) suspend_test_helper_1,
218                 (seL4_Word) get_helper_tcb(&thread1),
219                 (seL4_Word) get_helper_tcb(&thread2a),
220                 (seL4_Word) get_helper_tcb(&thread2b), 0);
221
222    start_helper(env, &thread2a, (helper_fn_t) suspend_test_helper_2a,
223                 (seL4_Word) get_helper_tcb(&thread1),
224                 (seL4_Word) get_helper_tcb(&thread2a),
225                 (seL4_Word) get_helper_tcb(&thread2b), 0);
226
227    start_helper(env, &thread2b, (helper_fn_t) suspend_test_helper_2b,
228                 (seL4_Word) get_helper_tcb(&thread1),
229                 (seL4_Word) get_helper_tcb(&thread2a),
230                 (seL4_Word) get_helper_tcb(&thread2b), 0);
231
232    /* Now set their priorities to what we want */
233    set_helper_priority(env, &thread1, 100);
234    set_helper_priority(env, &thread2a, 101);
235    set_helper_priority(env, &thread2b, 101);
236
237    suspend_test_step = 0;
238    ZF_LOGD("      ");
239
240    wait_for_helper(&thread1);
241    wait_for_helper(&thread2b);
242
243    CHECK_STEP(suspend_test_step, 6);
244    ZF_LOGD("\n");
245
246    cleanup_helper(env, &thread1);
247    cleanup_helper(env, &thread2a);
248    cleanup_helper(env, &thread2b);
249
250    return sel4test_get_result();
251}
252DEFINE_TEST(SCHED0003, "Test TCB suspend/resume", test_suspend, !config_set(CONFIG_FT))
253
254/*
255 * Test threads at all possible priorities, and that they get scheduled in the
256 * correct order.
257 */
258static int
259prio_test_func(seL4_Word my_prio, volatile seL4_Word *last_prio, seL4_CPtr ep)
260{
261    COMPILER_MEMORY_FENCE();
262    test_check(*last_prio - 1 == my_prio);
263
264    *last_prio = my_prio;
265    COMPILER_MEMORY_FENCE();
266
267    /* Unsuspend the top thread if we are the last one. */
268    if (my_prio == MIN_PRIO) {
269        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
270        seL4_Send(ep, tag);
271    }
272    return 0;
273}
274
275static int test_all_priorities(struct env *env)
276{
277    int i;
278
279    helper_thread_t **threads = (helper_thread_t **) malloc(sizeof(helper_thread_t *) * NUM_PRIOS);
280    assert(threads != NULL);
281
282    for (i = 0; i < NUM_PRIOS; i++) {
283        threads[i] = (helper_thread_t *) malloc(sizeof(helper_thread_t));
284        assert(threads[i]);
285    }
286
287    vka_t *vka = &env->vka;
288    ZF_LOGD("Testing all thread priorities");
289    volatile seL4_Word last_prio = MAX_PRIO + 1;
290
291    seL4_CPtr ep = vka_alloc_endpoint_leaky(vka);
292
293    for (int prio = MIN_PRIO; prio <= MAX_PRIO; prio++) {
294        int idx = prio - MIN_PRIO;
295        test_check(idx >= 0 && idx < NUM_PRIOS);
296        create_helper_thread(env, threads[idx]);
297        set_helper_priority(env, threads[idx], prio);
298
299        start_helper(env, threads[idx], (helper_fn_t) prio_test_func,
300                     prio, (seL4_Word) &last_prio, ep, 0);
301    }
302
303    /* Now block. */
304    seL4_Word sender_badge = 0;
305    seL4_Wait(ep, &sender_badge);
306
307    /* When we get woken up, last_prio should be MIN_PRIO. */
308    test_check(last_prio == MIN_PRIO);
309
310    for (int prio = MIN_PRIO; prio <= MAX_PRIO; prio++) {
311        int idx = prio - MIN_PRIO;
312        cleanup_helper(env, threads[idx]);
313        free(threads[idx]);
314    }
315    free(threads);
316
317    return sel4test_get_result();
318}
319DEFINE_TEST(SCHED0004, "Test threads at all priorities", test_all_priorities, true)
320
321#define SCHED0005_HIGHEST_PRIO (seL4_MaxPrio - 2)
322/*
323 * Test setting the priority of a runnable thread.
324 */
325static volatile int set_priority_step;
326static int set_priority_helper_1(seL4_CPtr t1, seL4_CPtr t2)
327{
328    test_check(set_priority_step == 0);
329    ZF_LOGD("0...");
330    set_priority_step = 1;
331
332    /*
333     * Down our priority. This should force a reschedule and make thread 2 run.
334     */
335    int error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 4);
336    test_check(!error);
337
338    test_check(set_priority_step == 2);
339    ZF_LOGD("2...");
340    set_priority_step = 3;
341
342    /* set our priority back up - this should work as we did not down our max priority */
343    error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO);
344    test_check(error == seL4_NoError);
345
346    /* now down our max_priority */
347    error = seL4_TCB_SetMCPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 4);
348    test_check(error == seL4_NoError);
349
350    /* try to set our prio higher than our max prio, but lower than our prio */
351    error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 3);
352    test_check(error == seL4_RangeError);
353
354    /* try to set our max prio back up */
355    error = seL4_TCB_SetMCPriority(t1, t1, SCHED0005_HIGHEST_PRIO);
356    test_check(error == seL4_RangeError);
357
358    return sel4test_get_result();
359}
360
361static int set_priority_helper_2(seL4_CPtr t1, seL4_CPtr t2)
362{
363    test_check(set_priority_step == 1);
364    ZF_LOGD("1...");
365
366    /* Raise thread 1 to equal to ours, which should fail. */
367    int error = seL4_TCB_SetPriority(t1, t2, SCHED0005_HIGHEST_PRIO - 1 + PRIORITY_FUDGE);
368    test_check(error == seL4_RangeError);
369
370    /* Raise thread 1 to just below us. */
371    error = seL4_TCB_SetPriority(t1, t2, SCHED0005_HIGHEST_PRIO - 2);
372    test_check(!error);
373
374    /* Drop ours to below thread 1. Thread 1 should run. */
375    set_priority_step = 2;
376    error = seL4_TCB_SetPriority(t2, t2, SCHED0005_HIGHEST_PRIO - 3);
377    test_check(!error);
378
379    /* Once thread 1 exits, we should run. */
380    test_check(set_priority_step == 3);
381    ZF_LOGD("3...");
382    set_priority_step = 4;
383
384    return sel4test_get_result();
385}
386
387#if CONFIG_NUM_PRIORITIES >= 7
388/* The enclosed test relies on the current thread being able to create two
389 * threads of unequal priority, both less than the caller's own priority. For
390 * this we need at least 3 priority levels, assuming that the current thread is
391 * running at the highest priority.
392 */
393static int test_set_priority(struct env *env)
394{
395    helper_thread_t thread1;
396    helper_thread_t thread2;
397    ZF_LOGD("test_set_priority starting\n");
398    create_helper_thread(env, &thread1);
399    set_helper_priority(env, &thread1, SCHED0005_HIGHEST_PRIO);
400    set_helper_mcp(env, &thread1, SCHED0005_HIGHEST_PRIO);
401
402    create_helper_thread(env, &thread2);
403    /* thread2 needs to start at a lower prio than thread1, so that when thread1 sets
404     * its own prio down, this thread runs, but not before. */
405    set_helper_priority(env, &thread2, SCHED0005_HIGHEST_PRIO - 1);
406    /* thread2 needs mcp SCHED0005_HIGHEST_PRIO - 2 so that it can raise thread1's
407     * priority to that value */
408    set_helper_mcp(env, &thread2, SCHED0005_HIGHEST_PRIO - 2);
409
410    set_priority_step = 0;
411    ZF_LOGD("      ");
412
413    start_helper(env, &thread1, (helper_fn_t) set_priority_helper_1,
414                 (seL4_Word) get_helper_tcb(&thread1),
415                 (seL4_Word) get_helper_tcb(&thread2), 0, 0);
416
417    start_helper(env, &thread2, (helper_fn_t) set_priority_helper_2,
418                 (seL4_Word) get_helper_tcb(&thread1),
419                 (seL4_Word) get_helper_tcb(&thread2), 0, 0);
420
421    wait_for_helper(&thread1);
422    wait_for_helper(&thread2);
423
424    test_check(set_priority_step == 4);
425
426    ZF_LOGD("\n");
427    cleanup_helper(env, &thread1);
428    cleanup_helper(env, &thread2);
429    return sel4test_get_result();
430}
431DEFINE_TEST(SCHED0005, "Test set priority", test_set_priority, true)
432#endif
433
434/*
435 * Perform IPC Send operations across priorities and ensure that strict
436 * priority-based scheduling is still observed.
437 */
438static volatile int ipc_test_step;
439typedef struct ipc_test_data {
440    volatile seL4_CPtr ep0, ep1, ep2, ep3, reply;
441    volatile seL4_Word bounces;
442    volatile seL4_Word spins;
443    seL4_CPtr tcb0, tcb1, tcb2, tcb3;
444} ipc_test_data_t;
445
446static int ipc_test_helper_0(ipc_test_data_t *data)
447{
448    /* We are a "bouncer" thread. Each time a high priority process actually
449     * wants to wait for a low priority process to execute and block, it does a
450     * call to us. We are the lowest priority process and therefore will run
451     * only after all other higher priority threads are done.
452     */
453    while (1) {
454        seL4_MessageInfo_t tag;
455        seL4_Word sender_badge = 0;
456        tag = api_recv(data->ep0, &sender_badge, data->reply);
457        data->bounces++;
458        api_reply(data->reply, tag);
459    }
460
461    return sel4test_get_result();
462}
463
464static int ipc_test_helper_1(ipc_test_data_t *data)
465{
466    seL4_Word sender_badge = 0;
467    seL4_MessageInfo_t tag;
468
469    /* TEST PART 1 */
470    /* Receive a pending send. */
471    CHECK_STEP(ipc_test_step, 1);
472    tag = api_recv(data->ep1, &sender_badge, data->reply);
473
474    /* As soon as the wait is performed, we should be preempted. */
475
476    /* Thread 3 will give us a chance to check our message. */
477    CHECK_STEP(ipc_test_step, 3);
478    test_check(seL4_MessageInfo_get_length(tag) == 20);
479    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
480        test_check(seL4_GetMR(i) == i);
481    }
482
483    /* Now we bounce to allow thread 3 control again. */
484    seL4_MessageInfo_ptr_set_length(&tag, 0);
485    seL4_Call(data->ep0, tag);
486
487    /* TEST PART 2 */
488    /* Receive a send that is not yet pending. */
489    CHECK_STEP(ipc_test_step, 5);
490    tag = api_recv(data->ep1, &sender_badge, data->reply);
491
492    CHECK_STEP(ipc_test_step, 8);
493    test_check(seL4_MessageInfo_get_length(tag) == 19);
494    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
495        test_check(seL4_GetMR(i) == i);
496    }
497
498    return sel4test_get_result();
499}
500
501static int ipc_test_helper_2(ipc_test_data_t *data)
502{
503    /* We are a spinner thread. Our job is to do spin, and occasionally bounce
504     * to thread 0 to let other code execute. */
505    while (1) {
506        /* Ensure nothing happens whilst we are busy. */
507        int last_step = ipc_test_step;
508        for (int i = 0; i < 100000; i++) {
509            asm volatile("");
510        }
511        test_check(last_step == ipc_test_step);
512
513        data->spins++;
514
515        /* Bounce. */
516        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
517        seL4_Call(data->ep0, tag);
518    }
519    return sel4test_get_result();
520}
521
522static int ipc_test_helper_3(ipc_test_data_t *data)
523{
524    seL4_MessageInfo_t tag;
525    int last_spins, last_bounces;
526
527    /* This test starts here. */
528
529    /* TEST PART 1 */
530    /* Perform a send to a thread 1. It is not yet waiting. */
531    CHECK_STEP(ipc_test_step, 0);
532    seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 20);
533    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
534        seL4_SetMR(i, i);
535    }
536    last_spins = data->spins;
537    last_bounces = data->bounces;
538    seL4_Send(data->ep1, tag);
539    /* We block, causing thread 2 to spin for a while, before it calls the
540     * bouncer thread 0, which finally lets thread 1 run and reply to us. */
541    CHECK_STEP(ipc_test_step, 2);
542    test_check(data->spins - last_spins == 1);
543    test_check(data->bounces - last_bounces == 0);
544
545    /* Now bounce ourselves, to ensure that thread 1 can check its stuff. */
546    seL4_MessageInfo_ptr_set_length(&tag, 0);
547    seL4_Call(data->ep0, tag);
548
549    /* Two bounces - us and thread 1. */
550    test_check(data->spins - last_spins == 2);
551    test_check(data->bounces - last_bounces == 2);
552    CHECK_STEP(ipc_test_step, 4);
553
554    /* TEST PART 2 */
555    /* Perform a send to a thread 1, which is already waiting. */
556    /* Bounce first to let thread prepare. */
557    last_spins = data->spins;
558    last_bounces = data->bounces;
559
560    seL4_MessageInfo_ptr_set_length(&tag, 0);
561    seL4_Call(data->ep0, tag);
562    CHECK_STEP(ipc_test_step, 6);
563
564    /* Do the send. */
565    seL4_MessageInfo_ptr_set_length(&tag, 19);
566    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
567        seL4_SetMR(i, i);
568    }
569    seL4_Send(data->ep1, tag);
570
571    CHECK_STEP(ipc_test_step, 7);
572
573    /* Bounce to let thread 1 check again. */
574    seL4_MessageInfo_ptr_set_length(&tag, 0);
575    seL4_Call(data->ep0, tag);
576
577    CHECK_STEP(ipc_test_step, 9);
578
579    /* Five bounces in total. */
580    test_check(data->spins - last_spins == 2);
581    test_check(data->bounces - last_bounces == 5);
582
583    return sel4test_get_result();
584}
585
586static int test_ipc_prios(struct env *env)
587{
588    vka_t *vka = &env->vka;
589    helper_thread_t thread0;
590    helper_thread_t thread1;
591    helper_thread_t thread2;
592    helper_thread_t thread3;
593
594    ipc_test_data_t data;
595    memset(&data, 0, sizeof(data));
596
597    data.ep0 = vka_alloc_endpoint_leaky(vka);
598    data.ep1 = vka_alloc_endpoint_leaky(vka);
599    data.ep2 = vka_alloc_endpoint_leaky(vka);
600    data.ep3 = vka_alloc_endpoint_leaky(vka);
601
602    create_helper_thread(env, &thread0);
603    set_helper_priority(env, &thread0, 0);
604
605    create_helper_thread(env, &thread1);
606    set_helper_priority(env, &thread1, 1);
607
608    create_helper_thread(env, &thread2);
609    set_helper_priority(env, &thread2, 2);
610
611    create_helper_thread(env, &thread3);
612    set_helper_priority(env, &thread3, 3);
613    set_helper_mcp(env, &thread3, 3);
614
615    data.tcb0 = get_helper_tcb(&thread0);
616    data.tcb1 = get_helper_tcb(&thread1);
617    data.tcb2 = get_helper_tcb(&thread2);
618    data.tcb3 = get_helper_tcb(&thread3);
619    data.reply = get_helper_reply(&thread0);
620
621    ZF_LOGD("      ");
622    ipc_test_step = 0;
623
624    start_helper(env, &thread0, (helper_fn_t) ipc_test_helper_0, (seL4_Word) &data, 0, 0, 0);
625    start_helper(env, &thread1, (helper_fn_t) ipc_test_helper_1, (seL4_Word) &data, 0, 0, 0);
626    start_helper(env, &thread2, (helper_fn_t) ipc_test_helper_2, (seL4_Word) &data, 0, 0, 0);
627    start_helper(env, &thread3, (helper_fn_t) ipc_test_helper_3, (seL4_Word) &data, 0, 0, 0);
628
629    wait_for_helper(&thread1);
630    wait_for_helper(&thread3);
631
632    CHECK_STEP(ipc_test_step, 10);
633    ZF_LOGD("\n");
634
635    cleanup_helper(env, &thread0);
636    cleanup_helper(env, &thread1);
637    cleanup_helper(env, &thread2);
638    cleanup_helper(env, &thread3);
639
640    return sel4test_get_result();
641}
642/* this test does not work on the RT kernel as it relies on FIFO IPC */
643DEFINE_TEST(SCHED0006, "Test IPC priorities for Send", test_ipc_prios, !config_set(CONFIG_KERNEL_MCS))
644
645#define SCHED0007_NUM_CLIENTS 5
646#define SCHED0007_PRIO(x) ((seL4_Word)(seL4_MaxPrio - 1 - SCHED0007_NUM_CLIENTS + (x)))
647
648static void
649sched0007_client(seL4_CPtr endpoint, int order)
650{
651    seL4_SetMR(0, order);
652    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1);
653
654    ZF_LOGD("Client %d call", order);
655    info = seL4_Call(endpoint, info);
656}
657
658static int sched0007_server(seL4_CPtr endpoint, seL4_CPtr reply)
659{
660    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
661
662    api_recv(endpoint, NULL, reply);
663
664    for (int i = SCHED0007_NUM_CLIENTS - 1; i >= 0; i--) {
665        test_eq(SCHED0007_PRIO(i), seL4_GetMR(0));
666        if (i > 0) {
667            api_reply_recv(endpoint, info, NULL, reply);
668        }
669    }
670
671    return SUCCESS;
672}
673
674static inline void sched0007_start_client(env_t env, helper_thread_t clients[], seL4_CPtr endpoint, int i)
675{
676    start_helper(env, &clients[i], (helper_fn_t) sched0007_client, endpoint, SCHED0007_PRIO(i), 0, 0);
677}
678
679int test_ipc_ordered(env_t env)
680{
681    seL4_CPtr endpoint;
682    helper_thread_t server;
683    helper_thread_t clients[SCHED0007_NUM_CLIENTS];
684
685    endpoint = vka_alloc_endpoint_leaky(&env->vka);
686    test_assert(endpoint != 0);
687
688    /* create clients, smallest prio first */
689    for (int i = 0; i < SCHED0007_NUM_CLIENTS; i++) {
690        create_helper_thread(env, &clients[i]);
691
692        set_helper_priority(env, &clients[i], SCHED0007_PRIO(i));
693    }
694
695    /* create the server */
696    create_helper_thread(env, &server);
697    set_helper_priority(env, &server, seL4_MaxPrio - 1);
698
699    compile_time_assert(sched0007_clients_correct, SCHED0007_NUM_CLIENTS == 5);
700
701    /* start the clients out of order to queue on the endpoint in order */
702    sched0007_start_client(env, clients, endpoint, 2);
703    sched0007_start_client(env, clients, endpoint, 0);
704    sched0007_start_client(env, clients, endpoint, 4);
705    sched0007_start_client(env, clients, endpoint, 1);
706    sched0007_start_client(env, clients, endpoint, 3);
707
708    /* start the server */
709    start_helper(env, &server, (helper_fn_t) sched0007_server, endpoint, clients[0].thread.reply.cptr, 0, 0);
710
711    /* server returns success if all requests are processed in order */
712    return wait_for_helper(&server);
713}
714DEFINE_TEST(SCHED0007, "Test IPC priorities", test_ipc_ordered, config_set(CONFIG_KERNEL_MCS));
715
716#define SCHED0008_NUM_CLIENTS 5
717
718static NORETURN void sched0008_client(int id, seL4_CPtr endpoint)
719{
720    while (1) {
721        ZF_LOGD("Client call %d\n", id);
722        seL4_Call(endpoint, seL4_MessageInfo_new(0, 0, 0, 0));
723    }
724}
725
726static inline int check_receive_ordered(env_t env, seL4_CPtr endpoint, int pos, seL4_CPtr replies[])
727{
728    seL4_Word badge;
729    seL4_Word expected_badge = SCHED0008_NUM_CLIENTS - 1;
730
731    /* check we receive messages in expected order */
732    for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) {
733        ZF_LOGD("Server wait\n");
734        api_recv(endpoint, &badge, replies[i]);
735
736        if (pos == i) {
737            ZF_LOGD("Server expecting %d\n", 0);
738            /* client 0 should be in here */
739            test_eq(badge, (seL4_Word)0);
740        } else {
741            ZF_LOGD("Server expecting %d\n", expected_badge);
742            test_eq(expected_badge, badge);
743            expected_badge--;
744        }
745    }
746
747    /* now reply to all callers */
748    for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) {
749        seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
750        seL4_Send(replies[i], info);
751    }
752
753    /* let everyone queue up again */
754    sel4test_sleep(env, 1 * NS_IN_S);
755    return sel4test_get_result();
756}
757
758int test_change_prio_on_endpoint(env_t env)
759{
760    int error;
761    helper_thread_t clients[SCHED0008_NUM_CLIENTS];
762    seL4_CPtr replies[SCHED0008_NUM_CLIENTS];
763    seL4_CPtr endpoint;
764    seL4_CPtr badged_endpoints[SCHED0008_NUM_CLIENTS];
765
766    endpoint = vka_alloc_endpoint_leaky(&env->vka);
767
768    int highest = seL4_MaxPrio - 1;
769    int lowest = highest - SCHED0008_NUM_CLIENTS - 2;
770    int prio = highest - SCHED0008_NUM_CLIENTS - 1;
771    int middle = highest - 3;
772
773    assert(highest > lowest && highest > middle && middle > lowest);
774
775    /* set up all the clients */
776    for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) {
777        create_helper_thread(env, &clients[i]);
778        set_helper_priority(env, &clients[i], prio);
779        badged_endpoints[i] = get_free_slot(env);
780        error = cnode_mint(env, endpoint, badged_endpoints[i], seL4_AllRights, i);
781        test_eq(error, seL4_NoError);
782        replies[i] = clients[i].thread.reply.cptr;
783        ZF_LOGD("Client %d, prio %d\n", i, prio);
784        prio++;
785    }
786
787    seL4_Word badge;
788    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
789
790    /* first test that changing prio while on an endpoint works */
791    seL4_CPtr reply = vka_alloc_reply_leaky(&env->vka);
792    test_neq(reply, (seL4_Word)seL4_CapNull);
793
794    /* start one clients so it queue on the endpoint */
795    start_helper(env, &clients[0], (helper_fn_t) sched0008_client, 0, badged_endpoints[0], 0, 0);
796    /* change its prio down */
797    set_helper_priority(env, &clients[0], lowest);
798    /* wait for a message */
799    api_recv(endpoint, &badge, reply);
800    test_eq(badge, (seL4_Word)0);
801
802    /* now send another message */
803    seL4_Send(reply, info);
804    /* change its prio up */
805    set_helper_priority(env, &clients[0], lowest + 1);
806    /* get another message */
807    api_recv(endpoint, &badge, reply);
808    test_eq(badge, (seL4_Word)0);
809    seL4_Send(reply, info);
810
811    /* Now test moving client 0 into all possible places in the endpoint queue */
812    /* first start the rest */
813    for (int i = 1; i < SCHED0008_NUM_CLIENTS; i++) {
814        start_helper(env, &clients[i], (helper_fn_t) sched0008_client, i, badged_endpoints[i], 0, 0);
815    }
816
817    /* let everyone queue on endpoint */
818    sel4test_sleep(env, 1 * US_IN_S);
819
820    ZF_LOGD("lower -> lowest");
821    ZF_LOGD("Client 0, prio %d\n", lowest);
822    /* move client 0's prio from lower -> lowest*/
823    set_helper_priority(env, &clients[0], lowest);
824    check_receive_ordered(env, endpoint, 4, replies);
825
826    ZF_LOGD("higher -> middle");
827    ZF_LOGD("Client 0, prio %d\n", middle);
828    /* higher -> to middle */
829    set_helper_priority(env, &clients[0], middle);
830    check_receive_ordered(env, endpoint, 2, replies);
831
832    ZF_LOGD("higher -> highest");
833    ZF_LOGD("Client 0, prio %d\n", highest - 1);
834    /* higher -> to highest */
835    set_helper_priority(env, &clients[0], highest - 1);
836    check_receive_ordered(env, endpoint, 0, replies);
837
838    ZF_LOGD("higher -> highest");
839    ZF_LOGD("Client 0, prio %d\n", highest);
840    /* highest -> even higher */
841    set_helper_priority(env, &clients[0], highest);
842    check_receive_ordered(env, endpoint, 0, replies);
843
844    ZF_LOGD("lower -> highest");
845    ZF_LOGD("Client 0, prio %d\n", highest - 1);
846    /* lower -> highest */
847    set_helper_priority(env, &clients[0], highest - 1);
848    check_receive_ordered(env, endpoint, 0, replies);
849
850    ZF_LOGD("lower -> middle");
851    ZF_LOGD("Client 0, prio %d\n", middle);
852    /* lower -> middle */
853    set_helper_priority(env, &clients[0], middle);
854    check_receive_ordered(env, endpoint, 2, replies);
855
856    ZF_LOGD("lower -> lowest");
857    ZF_LOGD("Client 0, prio %d\n", lowest);
858    /* lower -> lowest */
859    set_helper_priority(env, &clients[0], lowest);
860    check_receive_ordered(env, endpoint, 4, replies);
861
862    return sel4test_get_result();
863}
864DEFINE_TEST(SCHED0008, "Test changing prio while in endpoint queues results in correct message order",
865            test_change_prio_on_endpoint, config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))
866
867#define SCHED0009_SERVERS 5
868
869static NORETURN void
870sched0009_server(seL4_CPtr endpoint, int id, seL4_CPtr reply)
871{
872    /* wait to start */
873    ZF_LOGD("Server %d: awake", id);
874    seL4_Word badge;
875    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1);
876    api_recv(endpoint, &badge, reply);
877
878    while (1) {
879        ZF_LOGD("Server %d: ReplyRecv", id);
880        seL4_SetMR(0, id);
881        api_reply_recv(endpoint, info, &badge, reply);
882    }
883}
884
885static int test_ordered_ipc_fastpath(env_t env)
886{
887    helper_thread_t threads[SCHED0009_SERVERS];
888    seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka);
889
890    /* set up servers */
891    for (int i = 0; i < SCHED0009_SERVERS; i++) {
892        int prio = seL4_MaxPrio - 1 - SCHED0009_SERVERS + i;
893        ZF_LOGD("Server %d, prio %d\n", i, prio);
894        create_helper_thread(env, &threads[i]);
895        set_helper_priority(env, &threads[i], prio);
896    }
897
898    /* start the first server */
899    start_helper(env, &threads[0], (helper_fn_t) sched0009_server, endpoint, 0,
900                 get_helper_reply(&threads[0]), 0);
901
902    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0);
903    ZF_LOGD("Client Call\n");
904    seL4_Call(endpoint, info);
905    test_eq(seL4_GetMR(0), (seL4_Word)0);
906
907    /* resume all other servers */
908    for (int i = 1; i < SCHED0009_SERVERS; i++) {
909        start_helper(env, &threads[i], (helper_fn_t) sched0009_server, endpoint, i,
910                     get_helper_reply(&threads[i]), 0);
911        /* sleep and allow it to run */
912        sel4test_sleep(env, 1 * NS_IN_S);
913        /* since we resume a higher prio server each time this should work */
914        seL4_Call(endpoint, info);
915        test_eq(seL4_GetMR(0), (seL4_Word)i);
916    }
917
918    /* At this point all servers are queued on the endpoint */
919    /* now we will call and the highest prio server should reply each time */
920    for (int i = 0; i < SCHED0009_SERVERS; i++) {
921        seL4_Call(endpoint, info);
922        test_eq(seL4_GetMR(0), (seL4_Word)(SCHED0009_SERVERS - 1));
923    }
924
925    /* suspend each server in reverse prio order, should get next message from lower prio server */
926    for (int i = SCHED0009_SERVERS - 1; i >= 0; i--) {
927        seL4_TCB_Suspend(threads[i].thread.tcb.cptr);
928        /* don't call on the endpoint once all servers are suspended */
929        if (i > 0) {
930            seL4_Call(endpoint, info);
931            test_eq(seL4_GetMR(0), (seL4_Word)(i - 1));
932        }
933    }
934
935    return sel4test_get_result();
936}
937DEFINE_TEST(SCHED0009, "Test ordered ipc on reply wait fastpath", test_ordered_ipc_fastpath,
938            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))
939
940int
941sched0010_fn(volatile int *state)
942{
943    state++;
944    return 0;
945}
946
947int test_resume_empty_or_no_sched_context(env_t env)
948{
949    /* resuming a thread with empty or no scheduling context should work (it puts the thread in a runnable state)
950     * but the thread cannot run until it receives a scheduling context */
951
952    sel4utils_thread_t thread;
953    seL4_Word data = api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits);
954    sel4utils_thread_config_t config = thread_config_default(&env->simple, env->cspace_root,
955                                                             data, 0, OUR_PRIO - 1);
956
957    int error = sel4utils_configure_thread_config(&env->vka, &env->vspace, &env->vspace,
958                                                  config, &thread);
959    assert(error == 0);
960
961    error = api_sc_unbind(thread.sched_context.cptr);
962    test_eq(error, 0);
963
964    volatile int state = 0;
965    error = sel4utils_start_thread(&thread, (void *) sched0010_fn, (void *) &state, NULL, 1);
966    test_eq(error, seL4_NoError);
967
968    error = seL4_TCB_Resume(thread.tcb.cptr);
969    test_eq(error, seL4_NoError);
970
971    /* let the thread 'run' */
972    sel4test_sleep(env, 10 * NS_IN_MS);
973    test_eq(state, 0);
974
975    /* nuke the sc */
976    error = cnode_delete(env, thread.sched_context.cptr);
977    test_eq(error, seL4_NoError);
978
979    /* resume it */
980    error = seL4_TCB_Resume(thread.tcb.cptr);
981    test_eq(error, seL4_NoError);
982
983    /* let the thread 'run' */
984    sel4test_sleep(env, 10 * NS_IN_MS);
985    test_eq(state, 0);
986
987    return sel4test_get_result();
988}
989DEFINE_TEST(SCHED0010, "Test resuming a thread with empty or missing scheduling context",
990            test_resume_empty_or_no_sched_context,
991            (config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)))
992
993void
994sched0011_helper(void)
995{
996    while (1);
997}
998
999int test_scheduler_accuracy(env_t env)
1000{
1001    /*
1002     * Start a thread with a 1s timeslice at our priority, and make sure it
1003     * runs for that long
1004     */
1005    helper_thread_t helper;
1006
1007    create_helper_thread(env, &helper);
1008    uint64_t period = 100 * US_IN_MS;
1009    set_helper_sched_params(env, &helper, period, period, 0);
1010    start_helper(env, &helper, (helper_fn_t) sched0011_helper, 0, 0, 0, 0);
1011    set_helper_priority(env, &helper, OUR_PRIO);
1012    seL4_Yield();
1013    for (int i = 0; i < 11; i++) {
1014        uint64_t start = sel4test_timestamp(env);
1015        seL4_Yield();
1016        uint64_t end = sel4test_timestamp(env);
1017        /* calculate diff in ns */
1018        uint64_t diff = (end - start);
1019        uint64_t period_ns = period * NS_IN_US;
1020        if (i > 0) {
1021            test_geq(diff, period_ns - 2 * NS_IN_MS);
1022            test_leq(diff, period_ns + 2 * NS_IN_MS);
1023            if (diff > period_ns) {
1024                ZF_LOGD("Too late: by %llu us", diff - period_ns);
1025            } else if (diff < period_ns) {
1026                ZF_LOGD("Too soon: by %llu us", period_ns - diff);
1027            }
1028        }
1029    }
1030
1031    return sel4test_get_result();
1032}
1033DEFINE_TEST(SCHED0011, "Test scheduler accuracy",
1034            test_scheduler_accuracy, config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))
1035
1036/* used by sched0012, 0013, 0014 */
1037static void
1038periodic_thread(int id, volatile unsigned long *counters)
1039{
1040    counters[id] = 0;
1041
1042    while (1) {
1043        counters[id]++;
1044        test_leq(counters[id], (unsigned long) 10000);
1045        printf("Tick\n");
1046        seL4_Yield();
1047    }
1048}
1049
1050int test_one_periodic_thread(env_t env)
1051{
1052    helper_thread_t helper;
1053    volatile unsigned long counter;
1054    int error;
1055
1056    /* set priority down so we can run the helper(s) at a higher prio */
1057    error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1);
1058    test_eq(error, seL4_NoError);
1059
1060    create_helper_thread(env, &helper);
1061    set_helper_priority(env, &helper, env->priority);
1062    error = set_helper_sched_params(env, &helper, 0.2 * US_IN_S, US_IN_S, 0);
1063    test_eq(error, seL4_NoError);
1064
1065    start_helper(env, &helper, (helper_fn_t) periodic_thread, 0, (seL4_Word) &counter, 0, 0);
1066
1067    while (counter < 10) {
1068        printf("Tock %ld\n", counter);
1069        sel4test_sleep(env, NS_IN_S);
1070    }
1071
1072    return sel4test_get_result();
1073}
1074DEFINE_TEST(SCHED0012, "Test one periodic thread", test_one_periodic_thread,
1075            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))
1076
1077int
1078test_two_periodic_threads(env_t env)
1079{
1080    const int num_threads = 2;
1081    helper_thread_t helpers[num_threads];
1082    volatile unsigned long counters[num_threads];
1083
1084    /* set priority down so we can run the helper(s) at a higher prio */
1085    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1);
1086    test_eq(error, seL4_NoError);
1087
1088    for (int i = 0; i < num_threads; i++) {
1089        create_helper_thread(env, &helpers[i]);
1090        set_helper_priority(env, &helpers[i], env->priority);
1091    }
1092
1093    set_helper_sched_params(env, &helpers[0], 0.1 * US_IN_S, 2 * US_IN_S, 0);
1094    set_helper_sched_params(env, &helpers[1], 0.1 * US_IN_S, 3 * US_IN_S, 0);
1095
1096    for (int i = 0; i < num_threads; i++) {
1097        start_helper(env, &helpers[i], (helper_fn_t) periodic_thread, i, (seL4_Word) counters, 0, 0);
1098    }
1099
1100    while (counters[0] < 3 && counters[1] < 3) {
1101        sel4test_sleep(env, NS_IN_S);
1102    }
1103
1104    return sel4test_get_result();
1105}
1106DEFINE_TEST(SCHED0013, "Test two periodic threads", test_two_periodic_threads,
1107            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER));
1108
1109int test_ordering_periodic_threads(env_t env)
1110{
1111    /*
1112     * Set up 3 periodic threads with different budgets.
1113     * All 3 threads increment global counters,
1114     * check their increments are inline with their budgets.
1115     */
1116
1117    const int num_threads = 3;
1118    helper_thread_t helpers[num_threads];
1119    volatile unsigned long counters[num_threads];
1120
1121    /* set priority down so we can run the helper(s) at a higher prio */
1122    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1);
1123    test_eq(error, seL4_NoError);
1124
1125    /* sleep for a bit first - collect any waiting timer irqs */
1126    sel4test_sleep(env, 50 * NS_IN_MS);
1127
1128    for (int i = 0; i < num_threads; i++) {
1129        create_helper_thread(env, &helpers[i]);
1130        set_helper_priority(env, &helpers[i], env->priority);
1131    }
1132
1133    set_helper_sched_params(env, &helpers[0], 10 * US_IN_MS, 100 * US_IN_MS, 0);
1134    set_helper_sched_params(env, &helpers[1], 10 * US_IN_MS, 200 * US_IN_MS, 0);
1135    set_helper_sched_params(env, &helpers[2], 10 * US_IN_MS, 800 * US_IN_MS, 0);
1136
1137    for (int i = 0; i < num_threads; i++) {
1138        start_helper(env, &helpers[i], (helper_fn_t) periodic_thread, i, (seL4_Word) counters, 0, 0);
1139    }
1140
1141    /* stop once 2 reaches 11 increments */
1142    const unsigned long limit = 11u;
1143    while (counters[2] < limit) {
1144        sel4test_sleep(env, NS_IN_S);
1145    }
1146
1147    ZF_LOGD("O: %lu\n1: %lu\n2: %lu\n", counters[0], counters[1], counters[2]);
1148
1149    /* zero should have run 8 times as much as 2 */
1150    test_geq(counters[0], (limit - 1) * 8);
1151    /* one should have run 4 times as much as 2 */
1152    test_geq(counters[1], (limit - 1) * 4);
1153
1154    return sel4test_get_result();
1155}
1156DEFINE_TEST(SCHED0014, "Test periodic thread ordering", test_ordering_periodic_threads,
1157            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))
1158
1159static void
1160sched0015_helper(int id, env_t env, volatile unsigned long long *counters)
1161{
1162    counters[id] = 0;
1163
1164    uint64_t prev = 0;
1165    prev = sel4test_timestamp(env);
1166    while (1) {
1167        uint64_t now = sel4test_timestamp(env);
1168        uint64_t diff = now - prev;
1169        if (diff < 10 * NS_IN_US) {
1170            counters[id]++;
1171        }
1172        prev = now;
1173    }
1174}
1175
1176int test_budget_overrun(env_t env)
1177{
1178    /* Run two periodic threads that do not yeild but count the approximate
1179     * amount of time that they run for in 10's of nanoseconds.
1180     *
1181     * Each thread has a different share of the processor.
1182     * Both threads are higher prio than the test runner thread.
1183     * Make sure the test runner thread gets to run, and that
1184     * the two threads run roughly according to their budgets
1185     */
1186    volatile unsigned long long counters[2];
1187    helper_thread_t thirty, fifty;
1188    int error;
1189
1190    /* set priority down so we can run the helper(s) at a higher prio */
1191    error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1);
1192    test_eq(error, seL4_NoError);
1193
1194    create_helper_thread(env, &thirty);
1195    create_helper_thread(env, &fifty);
1196
1197    set_helper_priority(env, &thirty, env->priority);
1198    set_helper_priority(env, &fifty, env->priority);
1199
1200    set_helper_sched_params(env, &fifty, 0.1 * US_IN_S, 0.2 * US_IN_S, 0);
1201    set_helper_sched_params(env, &thirty, 0.1 * US_IN_S, 0.3 * US_IN_S, 0);
1202
1203    start_helper(env, &fifty, (helper_fn_t) sched0015_helper, 1, (seL4_Word)env,
1204                 (seL4_Word) counters, 0);
1205    start_helper(env, &thirty, (helper_fn_t) sched0015_helper, 0, (seL4_Word)env,
1206                 (seL4_Word) counters, 0);
1207
1208    uint64_t ticks = 0;
1209    while (counters[1] < 10000000) {
1210        sel4test_sleep(env, US_IN_S);
1211        ticks++;
1212        ZF_LOGD("Tick %llu", counters[1]);
1213    }
1214    error = seL4_TCB_Suspend(thirty.thread.tcb.cptr);
1215    test_eq(error, 0);
1216    test_geq(counters[0], 0llu);
1217
1218    error = seL4_TCB_Suspend(fifty.thread.tcb.cptr);
1219    test_eq(error, 0);
1220    test_geq(counters[1], 0llu);
1221
1222    /* we should have run in the 20% of time left by thirty and fifty threads */
1223    test_geq(ticks, 0llu);
1224    /* fifty should have run more than thirty */
1225    test_geq(counters[1], counters[0]);
1226
1227    ZF_LOGD("Result: 30%% incremented %llu, 50%% incremened %llu\n",
1228            counters[0], counters[1]);
1229
1230    return sel4test_get_result();
1231}
1232
1233static void sched0016_helper(volatile int *state)
1234{
1235    while (1) {
1236        printf("Hello\n");
1237        *state = *state + 1;
1238        seL4_Yield();
1239    }
1240
1241    ZF_LOGF("Should not get here!");
1242}
1243
1244int test_resume_no_overflow(env_t env)
1245{
1246    /* test thread cannot exceed its budget by being suspended and resumed */
1247    helper_thread_t helper;
1248    volatile int state = 0;
1249    int error = 0;
1250
1251    /* set priority down so we can run the helper(s) at a higher prio */
1252    error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority);
1253    test_eq(error, seL4_NoError);
1254
1255    create_helper_thread(env, &helper);
1256    set_helper_priority(env, &helper, env->priority);
1257
1258    /* this thread only runs for 1 second every 10 minutes */
1259    set_helper_sched_params(env, &helper, 1 * US_IN_S, 10 * SEC_IN_MINUTE * US_IN_S, 0);
1260
1261    start_helper(env, &helper, (helper_fn_t) sched0016_helper, (seL4_Word) &state,
1262                 0, 0, 0);
1263    seL4_Yield();
1264    test_eq(state, 1);
1265
1266    for (int i = 0; i < 10; i++) {
1267        error = seL4_TCB_Suspend(helper.thread.tcb.cptr);
1268        test_eq(error, 0);
1269
1270        error = seL4_TCB_Resume(helper.thread.tcb.cptr);
1271        test_eq(error, 0);
1272
1273        seL4_Yield();
1274
1275        test_eq(state, 1);
1276    }
1277
1278    return sel4test_get_result();
1279}
1280DEFINE_TEST(SCHED0016, "Test resume cannot be used to exceed budget", test_resume_no_overflow,
1281            config_set(CONFIG_KERNEL_MCS));
1282
1283#ifdef CONFIG_KERNEL_MCS
1284void sched0017_helper_fn(seL4_CPtr sc, volatile seL4_SchedContext_YieldTo_t *ret)
1285{
1286    ZF_LOGD("Yield To");
1287    *ret = seL4_SchedContext_YieldTo(sc);
1288}
1289
1290int test_yieldTo_errors(env_t env)
1291{
1292    volatile seL4_SchedContext_YieldTo_t ret;
1293
1294    /* can't yieldTo self */
1295    ret = seL4_SchedContext_YieldTo(simple_get_sc(&env->simple));
1296    test_eq(ret.error, seL4_IllegalOperation);
1297
1298    /* can't yield to unbound sched context */
1299    seL4_CPtr sched_context = vka_alloc_sched_context_leaky(&env->vka);
1300    ret = seL4_SchedContext_YieldTo(sched_context);
1301    test_eq(ret.error, seL4_IllegalOperation);
1302
1303    /* yield to unrunnable thread (permitted, but should return immediately) */
1304    helper_thread_t helper;
1305    create_helper_thread(env, &helper);
1306    ret = seL4_SchedContext_YieldTo(helper.thread.sched_context.cptr);
1307    test_eq(ret.error, seL4_NoError);
1308    test_eq(ret.consumed, 0llu);
1309
1310    /* start the thread and have it try to yield to us - but fail as
1311     * we have a higher mcp
1312     */
1313    ZF_LOGD("Yield to MCP check\n");
1314    set_helper_mcp(env, &helper, 0);
1315    start_helper(env, &helper, (helper_fn_t) sched0017_helper_fn, simple_get_sc(&env->simple),
1316                 (seL4_Word) &ret, 0, 0);
1317
1318    ZF_LOGD("Wait for helper\n");
1319    wait_for_helper(&helper);
1320    test_eq(ret.error, seL4_IllegalOperation);
1321
1322    return sel4test_get_result();
1323}
1324DEFINE_TEST(SCHED0017, "Test seL4_SchedContext_YieldTo errors", test_yieldTo_errors, config_set(CONFIG_KERNEL_MCS));
1325
1326int sched0018_to_fn(void)
1327{
1328    while (1) {
1329        ZF_LOGD("Running");
1330    }
1331}
1332
1333int test_yieldTo_cleanup(env_t env)
1334{
1335    int error;
1336    helper_thread_t to, from;
1337    volatile seL4_SchedContext_YieldTo_t ret;
1338
1339    create_helper_thread(env, &to);
1340    create_helper_thread(env, &from);
1341
1342    start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0);
1343    start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, (seL4_Word) &ret, 0, 0);
1344
1345    set_helper_mcp(env, &to, seL4_MaxPrio);
1346    set_helper_mcp(env, &from, seL4_MaxPrio);
1347    error = set_helper_sched_params(env, &to, 500 * US_IN_MS, 500 * US_IN_MS, 0);
1348    test_eq(error, seL4_NoError);
1349    error = set_helper_sched_params(env, &from, 500 * US_IN_MS, 500 * US_IN_MS, 0);
1350    test_eq(error, seL4_NoError);
1351
1352    /* wait for them to execute */
1353    ZF_LOGD("Sleep\n");
1354    sel4test_sleep(env, NS_IN_S);
1355
1356    ZF_LOGD("suspend to\n");
1357    /* suspend yielded to thread */
1358    error = seL4_TCB_Suspend(to.thread.tcb.cptr);
1359    test_eq(error, seL4_NoError);
1360
1361    ZF_LOGD("Wait for from\n");
1362    wait_for_helper(&from);
1363    test_eq(ret.error, seL4_NoError);
1364    test_gt(ret.consumed, 0llu);
1365
1366    ret.consumed = 0;
1367
1368    /* restart threads */
1369    cleanup_helper(env, &from);
1370    cleanup_helper(env, &to);
1371
1372    create_helper_thread(env, &from);
1373    create_helper_thread(env, &to);
1374    set_helper_mcp(env, &to, seL4_MaxPrio);
1375    set_helper_mcp(env, &from, seL4_MaxPrio);
1376    start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0);
1377    start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr,
1378                 (seL4_Word) &ret, 0, 0);
1379
1380    /* let them run */
1381    ZF_LOGD("Sleep\n");
1382    sel4test_sleep(env, NS_IN_S);
1383
1384    /* delete yielded to thread */
1385    ZF_LOGD("Delete yielded to\n");
1386    cleanup_helper(env, &to);
1387
1388    ZF_LOGD("Wait for from\n");
1389    wait_for_helper(&from);
1390    test_eq(ret.error, seL4_NoError);
1391    test_gt(ret.consumed, 0llu);
1392
1393    /* restart threads */
1394    cleanup_helper(env, &from);
1395
1396    create_helper_thread(env, &from);
1397    create_helper_thread(env, &to);
1398    start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0);
1399    start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr,
1400                 (seL4_Word) &ret, 0, 0);
1401    set_helper_mcp(env, &to, seL4_MaxPrio);
1402    set_helper_mcp(env, &from, seL4_MaxPrio);
1403    /* wait for them to execute */
1404    ZF_LOGD("sleep\n");
1405    sel4test_sleep(env, NS_IN_S);
1406
1407    /* delete yielded from thread */
1408    /* delete yielded from thread */
1409    ZF_LOGD("delete from\n");
1410    cleanup_helper(env, &from);
1411
1412    return sel4test_get_result();
1413}
1414DEFINE_TEST(SCHED0018, "Test clean up cases after seL4_SchedContext_YieldTo", test_yieldTo_cleanup,
1415            config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER));
1416
1417
1418int test_yieldTo(env_t env)
1419{
1420    int error;
1421    helper_thread_t to, from;
1422    volatile seL4_SchedContext_YieldTo_t ret;
1423
1424    create_helper_thread(env, &to);
1425    create_helper_thread(env, &from);
1426
1427    start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0);
1428    start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, (seL4_Word) &ret, 0, 0);
1429
1430    set_helper_mcp(env, &to, seL4_MaxPrio);
1431    set_helper_mcp(env, &from, seL4_MaxPrio);
1432    error = set_helper_sched_params(env, &to, 500 * US_IN_MS, 500 * US_IN_MS, 0);
1433    test_eq(error, seL4_NoError);
1434    error = set_helper_sched_params(env, &from, 500 * US_IN_MS, 500 * US_IN_MS, 0);
1435    test_eq(error, seL4_NoError);
1436
1437    ZF_LOGD("Wait for from\n");
1438    wait_for_helper(&from);
1439    test_eq(ret.error, seL4_NoError);
1440    test_geq(ret.consumed, 0llu);
1441
1442    return sel4test_get_result();
1443}
1444DEFINE_TEST(SCHED0019, "Test seL4_SchedContext_YieldTo", test_yieldTo, config_set(CONFIG_KERNEL_MCS));
1445
1446#endif /* CONFIG_KERNEL_MCS */
1447
1448void set_higher_prio_helper(volatile int *state)
1449{
1450    /* Yield incase the scheduler picked us before the
1451     * test set our priority higher */
1452    seL4_Yield();
1453    *state = 2;
1454}
1455
1456static int test_set_higher_prio(struct env *env)
1457{
1458    helper_thread_t thread;
1459
1460    /* set our priority down */
1461    int error = seL4_TCB_SetPriority(env->tcb, env->tcb, OUR_PRIO - 1);
1462    test_eq(error, seL4_NoError);
1463
1464    /* start helper at highest prio */
1465    volatile int state = 0;
1466
1467    /* start helper - it will run at the same prio as us */
1468    create_helper_thread(env, &thread);
1469    start_helper(env, &thread, (helper_fn_t) set_higher_prio_helper, (seL4_Word) &state, 0, 0, 0);
1470    /* check it didn't update state yet - even if the scheduler picks the helper
1471     * it will yield back to us first */
1472    test_eq(state, 0);
1473
1474    set_helper_priority(env, &thread, OUR_PRIO);
1475
1476    /* helper should run and set state to 2 */
1477    test_eq(state, 2);
1478
1479    return sel4test_get_result();
1480}
1481DEFINE_TEST(SCHED0020, "test set prio to a higher prio runs higher prio thread", test_set_higher_prio, true);
1482
1483#define PREEMPTION_THREADS 4
1484
1485#ifdef CONFIG_KERNEL_MCS
1486/* Under MCS each round-robin thread will run for a dedicated time slice
1487 * before being pre-empted at the end. This should ensure full
1488 * utilisation of the entire slice for each thread. */
1489#define MIN_THREAD_SLICE (CONFIG_BOOT_THREAD_TIME_SLICE * NS_IN_MS)
1490#define MAX_THREAD_SLICE MIN_THREAD_SLICE
1491#else
1492/* With the non-mcs scheduler timer ticks occur periodically and may
1493 * even include some amount of drift. Whichever thread is executing when
1494 * a tick occurs is charged an entire tick from its time slice. A thread
1495 * executing continually may therefore only actually execute for one
1496 * less than the allocated number of ticks */
1497#define MIN_THREAD_SLICE (CONFIG_TIMER_TICK_MS * (CONFIG_TIME_SLICE - 1) * NS_IN_MS)
1498#define MAX_THREAD_SLICE (CONFIG_TIMER_TICK_MS * CONFIG_TIME_SLICE * NS_IN_MS)
1499#endif
1500
1501#define TIME_SCALE(time, dividend, divisor) (((time) * (dividend)) / (divisor))
1502
1503/* This test should at least run through the minimal time for each
1504 * concurrent thread updating the data. Allow for 2% speedup */
1505#define MIN_TIME TIME_SCALE(MIN_THREAD_SLICE * PREEMPTION_THREADS, 98, 100)
1506/* This thread should run for no longer that tne maximum time for all
1507 * threads to execute plus the time to execute monitor thread. Allow for
1508 * 2% slowdown. */
1509#define MAX_TIME TIME_SCALE(MAX_THREAD_SLICE * (PREEMPTION_THREADS + 1), 102, 100)
1510
1511volatile unsigned int preemption_thread_data[PREEMPTION_THREADS];
1512volatile unsigned int test_simple_preempt_start = 0;
1513
1514static inline uint64_t time_now(struct env *env)
1515{
1516    if (config_set(CONFIG_HAVE_TIMER)) {
1517        return sel4test_timestamp(env);
1518    } else {
1519        return 0;
1520    }
1521}
1522
1523void test_simple_preempt_runner(size_t thread_id)
1524{
1525    ssize_t next = (thread_id - 1) % PREEMPTION_THREADS;
1526    while (test_simple_preempt_start == 0) {
1527        ZF_LOGD("#%zu", thread_id);
1528    }
1529
1530    /* Get the count for the previous thread */
1531    unsigned int next_data = preemption_thread_data[next];
1532
1533    /* We only stay in this loop until we get back to the monitor to
1534     * ensure that we don't loop again if it takes too long to check all
1535     * the threads. */
1536    while (test_simple_preempt_start) {
1537        /* Signal test thread */
1538        unsigned int data = preemption_thread_data[thread_id] + 1;
1539        preemption_thread_data[thread_id] = data;
1540
1541        ZF_LOGD("#%zu = %u", thread_id, data);
1542
1543        /* Wait for next thread to progress */
1544        while (preemption_thread_data[next] <= next_data);
1545        next_data = preemption_thread_data[next];
1546    }
1547}
1548
1549
1550/*
1551 * Checks that ticks preempt threads of equal priority, and
1552 * all threads get run RR exactly once
1553 */
1554static int test_simple_preempt(struct env *env)
1555{
1556#ifdef CONFIG_DEBUG_BUILD
1557    seL4_DebugNameThread(env->tcb, "Pre-empt monitor");
1558#endif
1559
1560    uint64_t total = 0;
1561    uint64_t prev = time_now(env);
1562    for (size_t i = 0; i < 10; i++) {
1563        uint64_t next = time_now(env);
1564        total += next - prev;
1565        prev = next;
1566    }
1567    ZF_LOGD("Average to read time %lluns", total / 10);
1568
1569    helper_thread_t threads[PREEMPTION_THREADS] = {};
1570
1571    ZF_LOGD("%zu threads, %lluus time slice", (size_t)PREEMPTION_THREADS, (uint64_t)(MAX_THREAD_SLICE / NS_IN_US));
1572
1573    /* Configure all of the threads */
1574    for (size_t thread = 0; thread < PREEMPTION_THREADS; thread += 1) {
1575        /* Ensure the thread data starts at 0 */
1576        preemption_thread_data[thread] = 0;
1577
1578        create_helper_thread(env, &threads[thread]);
1579
1580        /* Thread must run at same prio as monitor */
1581        set_helper_priority(env, &threads[thread], OUR_PRIO);
1582
1583#ifdef CONFIG_DEBUG_BUILD
1584        char name[32] = "";
1585        snprintf(name, 32, "Pre-empt #%zu", thread);
1586        seL4_DebugNameThread(threads[thread].thread.tcb.cptr, name);
1587#endif
1588
1589        /* Start the thread (will yield until we're ready) */
1590        ZF_LOGD("Start thread %u", thread);
1591        start_helper(env, &threads[thread], (helper_fn_t) test_simple_preempt_runner, thread, 0, 0, 0);
1592        ZF_LOGD("Started thread %u", thread);
1593    }
1594
1595    /* Set a timeout for the test.
1596     * Each thread should be run for one tick */
1597    uint64_t start = time_now(env);
1598    uint64_t now = start;
1599
1600    /* Start executing other threads */
1601    ZF_LOGD("Releasing Threads");
1602    test_simple_preempt_start = 1;
1603    /* Yield should cause all other threads to execute before returning
1604     * to the current thread. */
1605    seL4_Yield();
1606    test_simple_preempt_start = 0;
1607
1608    /* Get the total time taken to synchronise */
1609    now = time_now(env);
1610    uint64_t duration = now - start;
1611
1612    for (size_t thread = 0; thread < PREEMPTION_THREADS; thread += 1) {
1613        cleanup_helper(env, &threads[thread]);
1614
1615        /* Each thread should only have been run once */
1616        test_eq(preemption_thread_data[thread], 1);
1617    }
1618
1619    /* Only check duration where a user timer is available */
1620    if (config_set(CONFIG_HAVE_TIMER)) {
1621        /* Show total time */
1622        ZF_LOGI("Finished in %lluus", (now - start) / NS_IN_US);
1623
1624        /* If the timeout was exceeded this test has failed */
1625        test_geq(now, start);
1626        test_geq(duration, MIN_TIME);
1627        test_lt(duration, MAX_TIME);
1628    }
1629
1630
1631    return sel4test_get_result();
1632}
1633DEFINE_TEST(SCHED0021, "Test for pre-emption during running of many threads with equal prio", test_simple_preempt,
1634            true);
1635
1636int sched0022_to_fn(struct env *env, helper_thread_t *thread, seL4_CPtr ep)
1637{
1638    seL4_MessageInfo_t tag = {0};
1639    seL4_MessageInfo_ptr_set_length(&tag, 2);
1640
1641    /* change to core 1 */
1642    seL4_Error error = api_sched_ctrl_configure(simple_get_sched_ctrl(&env->simple, 1),
1643                                                thread->thread.sched_context.cptr,
1644                                                10000,
1645                                                10000,
1646                                                1,
1647                                                0);
1648    seL4_SetMR(0, error);
1649    /* and back to core 0 */
1650    error = api_sched_ctrl_configure(simple_get_sched_ctrl(&env->simple, 0),
1651                                     thread->thread.sched_context.cptr,
1652                                     10000,
1653                                     10000,
1654                                     1,
1655                                     0);
1656
1657    seL4_SetMR(1, error);
1658    seL4_Send(ep, tag);
1659}
1660
1661/* Test that a helper thread can move itself back from another core.
1662 * Save the return values and check them in test thread.
1663 */
1664static int test_changing_affinity(struct env *env)
1665{
1666    int error;
1667    helper_thread_t t0;
1668    seL4_MessageInfo_t tag;
1669    seL4_CPtr reply, ep;
1670    seL4_Word sender_badge = 0;
1671
1672    ep = vka_alloc_endpoint_leaky(&env->vka);
1673
1674    create_helper_thread(env, &t0);
1675
1676    reply = get_helper_reply(&t0);
1677
1678    start_helper(env, &t0, (helper_fn_t) sched0022_to_fn, (seL4_Word) env, (seL4_Word) &t0, ep, 0);
1679
1680    tag = api_recv(ep, &sender_badge, reply);
1681
1682    /* Check error codes */
1683    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
1684        test_assert(!seL4_GetMR(i));
1685    }
1686
1687    cleanup_helper(env, &t0);
1688
1689    return sel4test_get_result();
1690}
1691DEFINE_TEST(SCHED0022, "test changing a helper threads core", test_changing_affinity,
1692            (config_set(CONFIG_KERNEL_MCS) &&(CONFIG_MAX_NUM_NODES > 1)));
1693