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
16/* This file contains tests related to multicore. */
17
18#include <stdio.h>
19#include <sel4/sel4.h>
20
21#include "../helpers.h"
22
23static int counter_func(volatile seL4_Word *counter)
24{
25    while (1) {
26        (*counter)++;
27    }
28    return 0;
29}
30
31int smp_test_tcb_resume(env_t env)
32{
33    helper_thread_t t1;
34    volatile seL4_Word counter;
35    ZF_LOGD("smp_test_tcb_resume\n");
36    create_helper_thread(env, &t1);
37
38    set_helper_priority(env, &t1, 100);
39    start_helper(env, &t1, (helper_fn_t) counter_func, (seL4_Word) &counter, 0, 0, 0);
40
41    seL4_Word old_counter;
42
43    /* Let the counter thread run. */
44    sel4test_sleep(env, 10 * NS_IN_MS);
45
46    old_counter = counter;
47
48    /* Let it run again on the current core. */
49    sel4test_sleep(env, 10 * NS_IN_MS);
50
51    /* Now, counter should have moved. */
52    test_check(counter != old_counter);
53
54    /* Suspend the thread, and move it to new core. */
55    seL4_TCB_Suspend(get_helper_tcb(&t1));
56    set_helper_affinity(env, &t1, 1);
57
58    old_counter = counter;
59
60    /* Check if the thread is running. */
61    sel4test_sleep(env, 10 * NS_IN_MS);
62
63    /* Counter should not have moved. */
64    test_check(counter == old_counter);
65    old_counter = counter;
66
67    /* Resume the thread and check it does move. */
68    seL4_TCB_Resume(get_helper_tcb(&t1));
69    sel4test_sleep(env, 10 * NS_IN_MS);
70    test_check(counter != old_counter);
71
72    /* Suspend the thread. */
73    seL4_TCB_Suspend(get_helper_tcb(&t1));
74
75    old_counter = counter;
76
77    /* Check if the thread is running. */
78    sel4test_sleep(env, 10 * NS_IN_MS);
79
80    /* Counter should not have moved. */
81    test_check(counter == old_counter);
82
83    /* Done. */
84    cleanup_helper(env, &t1);
85
86    return sel4test_get_result();
87}
88DEFINE_TEST(MULTICORE0001, "Test suspending and resuming a thread on different core", smp_test_tcb_resume,
89            config_set(CONFIG_HAVE_TIMER) &&CONFIG_MAX_NUM_NODES > 1)
90
91int smp_test_tcb_move(env_t env)
92{
93    helper_thread_t t1;
94    volatile seL4_Word counter;
95    ZF_LOGD("smp_test_tcb_move\n");
96    create_helper_thread(env, &t1);
97
98    set_helper_priority(env, &t1, 100);
99    start_helper(env, &t1, (helper_fn_t) counter_func, (seL4_Word) &counter, 0, 0, 0);
100
101    seL4_Word old_counter;
102
103    old_counter = counter;
104
105    /* Let it run on the current core. */
106    sleep_busy(env, 10 * NS_IN_MS);
107
108    /* Now, counter should not have moved. */
109    test_check(counter == old_counter);
110
111    for (int i = 1; i < env->cores; i++) {
112        set_helper_affinity(env, &t1, i);
113
114        old_counter = counter;
115
116        /* Check if the thread is running. */
117        sleep_busy(env, 10 * NS_IN_MS);
118
119        /* Counter should have moved. */
120        test_check(counter != old_counter);
121    }
122
123    /* Done. */
124    cleanup_helper(env, &t1);
125
126    return sel4test_get_result();
127}
128DEFINE_TEST(MULTICORE0002, "Test thread is runnable on all available cores (0 + other)", smp_test_tcb_move,
129            config_set(CONFIG_HAVE_TIMER) &&CONFIG_MAX_NUM_NODES > 1)
130
131int smp_test_tcb_delete(env_t env)
132{
133    helper_thread_t t1;
134    volatile seL4_Word counter;
135    ZF_LOGD("smp_test_tcb_delete\n");
136    create_helper_thread(env, &t1);
137
138    set_helper_priority(env, &t1, 100);
139    start_helper(env, &t1, (helper_fn_t) counter_func, (seL4_Word) &counter, 0, 0, 0);
140
141    seL4_Word old_counter;
142
143    old_counter = counter;
144
145    /* Let it run on the current core. */
146    sleep_busy(env, 10 * NS_IN_MS);
147
148    /* Now, counter should not have moved. */
149    test_check(counter == old_counter);
150
151    set_helper_affinity(env, &t1, 1);
152
153    old_counter = counter;
154
155    /* Check if the thread is running. */
156    sleep_busy(env, 10 * NS_IN_MS);
157
158    /* Counter should have moved. */
159    test_check(counter != old_counter);
160
161    /* Now delete the helper thread running on another core */
162    cleanup_helper(env, &t1);
163
164    old_counter = counter;
165
166    /* Check if the thread is running. */
167    sleep_busy(env, 10 * NS_IN_MS);
168
169    /* Now, counter should not have moved. */
170    test_check(counter == old_counter);
171
172    /* Done. */
173    return sel4test_get_result();
174}
175
176DEFINE_TEST(MULTICORE0005, "Test remote delete thread running on other cores", smp_test_tcb_delete,
177            config_set(CONFIG_HAVE_TIMER) &&CONFIG_MAX_NUM_NODES > 1)
178
179static int
180faulter_func(volatile seL4_Word shared_mem)
181{
182    volatile seL4_Word *page;
183
184    page = (volatile seL4_Word *)shared_mem;
185
186    /* Accessing to the new page... */
187    while (1) {
188        *page = 1;
189    }
190
191    return 0;
192}
193
194static int handler_func(seL4_CPtr fault_ep, volatile seL4_Word *pf)
195{
196    seL4_MessageInfo_t tag;
197    seL4_Word sender_badge = 0;
198
199    /* Waiting for fault from faulter */
200    tag = api_wait(fault_ep, &sender_badge);
201    *pf = seL4_MessageInfo_get_label(tag);
202    return 0;
203}
204
205static int smp_test_tlb_instance(env_t env, bool inter_as)
206{
207    int error;
208    volatile seL4_Word tag;
209    volatile seL4_Word shared_mem = 0;
210    ZF_LOGD("smp_test_tlb\n");
211
212    helper_thread_t handler_thread;
213    helper_thread_t faulter_thread;
214    vspace_t *vspace;
215    seL4_CPtr faulter_vspace, faulter_cspace;
216    seL4_CPtr fault_ep = vka_alloc_endpoint_leaky(&env->vka);
217    create_helper_thread(env, &handler_thread);
218    set_helper_priority(env, &handler_thread, 100);
219
220    seL4_CPtr fault_ep_faulter = fault_ep;
221    if (inter_as) {
222        create_helper_process(env, &faulter_thread);
223
224        /* copy the fault endpoint to the faulter */
225        cspacepath_t path;
226        vka_cspace_make_path(&env->vka,  fault_ep, &path);
227        seL4_CPtr remote_fault_ep = sel4utils_copy_path_to_process(&faulter_thread.process, path);
228        assert(remote_fault_ep != -1);
229
230        if (!config_set(CONFIG_KERNEL_MCS)) {
231            fault_ep_faulter = remote_fault_ep;
232        }
233
234        faulter_cspace = faulter_thread.process.cspace.cptr;
235        faulter_vspace = faulter_thread.process.pd.cptr;
236        vspace = &faulter_thread.process.vspace;
237    } else {
238        create_helper_thread(env, &faulter_thread);
239        faulter_cspace = env->cspace_root;
240        faulter_vspace = env->page_directory;
241        vspace = &env->vspace;
242    }
243
244    error = api_tcb_set_space(get_helper_tcb(&faulter_thread),
245                              fault_ep_faulter,
246                              faulter_cspace,
247                              api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits),
248                              faulter_vspace, seL4_NilData);
249    test_error_eq(error, seL4_NoError);
250
251    /* Move handler to core 1 and faulter to the last available core */
252    set_helper_affinity(env, &handler_thread, 1);
253    set_helper_affinity(env, &faulter_thread, env->cores - 1);
254
255    /* Map new page to shared address space */
256    shared_mem = (seL4_Word) vspace_new_pages(vspace, seL4_AllRights, 1, seL4_PageBits);
257
258    start_helper(env, &handler_thread, (helper_fn_t) handler_func, fault_ep, (seL4_Word) &tag, 0, 0);
259    start_helper(env, &faulter_thread, (helper_fn_t) faulter_func, (seL4_Word) shared_mem, 0, 0, 0);
260
261    /* Wait for some access... */
262    sel4test_sleep(env, 10 * NS_IN_MS);
263
264    /* Unmap the page */
265    vspace_unmap_pages(vspace, (void *) shared_mem, 1, seL4_PageBits, &env->vka);
266
267    /* Wait for some access... */
268    sel4test_sleep(env, CONFIG_TIMER_TICK_MS * NS_IN_MS / 10);
269
270    /* We should see page fault */
271    test_check(tag == seL4_Fault_VMFault);
272
273    /* Done. */
274    cleanup_helper(env, &faulter_thread);
275    cleanup_helper(env, &handler_thread);
276    return sel4test_get_result();
277}
278
279int smp_test_tlb(env_t env)
280{
281    test_result_t result;
282    /* Test unmapping a frame from the same VSpace and different VSpace. */
283    for (int i = 0; i < 20; i++) {
284        bool inter_as = (i % 2 == 0) ? true : false;
285        result = smp_test_tlb_instance(env, inter_as);
286        if (result != SUCCESS) {
287            return result;
288        }
289    }
290    return result;
291
292}
293DEFINE_TEST(MULTICORE0003, "Test TLB invalidated cross cores", smp_test_tlb,
294            config_set(CONFIG_HAVE_TIMER) &&CONFIG_MAX_NUM_NODES > 1)
295
296static int
297kernel_entry_func(seL4_Word *unused)
298{
299    while (1) {
300        seL4_Yield();
301    }
302    return 0;
303}
304
305int smp_test_tcb_clh(env_t env)
306{
307    helper_thread_t t[env->cores];
308    ZF_LOGD("smp_test_tcb_move\n");
309
310    for (int i = 1; i < env->cores; i++) {
311        create_helper_thread(env, &t[i]);
312
313        set_helper_affinity(env, &t[i], i);
314        start_helper(env, &t[i], (helper_fn_t) kernel_entry_func, (seL4_Word) NULL, 0, 0, 0);
315    }
316
317    /* All threads start calling 'seL4_Yield', which results in a queue to be generated in CLH lock.
318     * By the time we are trying to clean up threads, they should be already in CLH queue which
319     * result the delay release of the lock and stalling of the core.
320     *
321     * There is no failing here.
322     * If something is worng with IPI or lock handling we would stuck or possibly crash! */
323
324    /* We should be able to cleanup all threads */
325    for (int i = 1; i < env->cores; i++) {
326        cleanup_helper(env, &t[i]);
327    }
328
329    /* Do this again... */
330    for (int i = 1; i < env->cores; i++) {
331        create_helper_thread(env, &t[i]);
332
333        set_helper_affinity(env, &t[i], i);
334        start_helper(env, &t[i], (helper_fn_t) kernel_entry_func, (seL4_Word) NULL, 0, 0, 0);
335    }
336
337    /* We should be able to cleanup all threads */
338    for (int i = 1; i < env->cores; i++) {
339        cleanup_helper(env, &t[i]);
340    }
341
342    return sel4test_get_result();
343}
344DEFINE_TEST(MULTICORE0004, "Test core stalling is behaving properly (flaky)", smp_test_tcb_clh,
345            CONFIG_MAX_NUM_NODES > 1)
346