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 <stdio.h>
14#include <sel4/sel4.h>
15#include <stdlib.h>
16#include <string.h>
17#include <vka/object.h>
18#include <sel4test/macros.h>
19
20#include "../helpers.h"
21
22/* declare some per thread variables for our tests. both bss and data */
23static __thread seL4_Word bss_array[6] = {0};
24static __thread seL4_Word data_array[6] = {1, 2, 3, 4, 5, 6};
25
26static int test_root_tls(env_t env)
27{
28    seL4_Word i;
29    for (i = 0; i < ARRAY_SIZE(bss_array); i++) {
30        test_eq(bss_array[i], (seL4_Word)0);
31    }
32
33    for (i = 0; i < ARRAY_SIZE(data_array); i++) {
34        test_eq(data_array[i], i + 1);
35    }
36    /* very the bss and data arrays containg the correct thing */
37    return sel4test_get_result();
38}
39DEFINE_TEST(
40    TLS0001,
41    "Test root thread accessing __thread variables",
42    test_root_tls,
43    true
44)
45
46static int
47tls_helper(seL4_Word helper, seL4_Word done_ep, seL4_Word start_ep, seL4_Word arg4)
48{
49    seL4_Word i;
50    /* first verify all our initial data */
51    for (i = 0; i < ARRAY_SIZE(bss_array); i++) {
52        test_eq(bss_array[i], (seL4_Word)0);
53    }
54
55    for (i = 0; i < ARRAY_SIZE(data_array); i++) {
56        test_eq(data_array[i], i + 1);
57    }
58    /* now update based on our thread */
59    for (i = 0; i < ARRAY_SIZE(bss_array); i++) {
60        bss_array[i] = i + helper;
61    }
62    for (i = 0; i < ARRAY_SIZE(data_array); i++) {
63        data_array[i] = helper * ARRAY_SIZE(data_array) + i;
64    }
65    /* signal we are ready */
66    seL4_Signal(done_ep);
67    /* wait for all threads are done and we are signaled to start */
68    seL4_Wait(start_ep, NULL);
69    /* verify our arrays are still the same */
70    for (i = 0; i < ARRAY_SIZE(bss_array); i++) {
71        test_eq(bss_array[i], i + helper);
72    }
73    for (i = 0; i < ARRAY_SIZE(data_array); i++) {
74        test_eq(data_array[i], helper * ARRAY_SIZE(data_array) + i);
75    }
76    return sel4test_get_result();
77}
78
79static int test_threads_tls(env_t env)
80{
81    /* create endpoints for synchronization */
82    seL4_CPtr done_ep = vka_alloc_endpoint_leaky(&env->vka);
83    seL4_CPtr start_ep = vka_alloc_endpoint_leaky(&env->vka);
84    /* spawn some helper threads for manipulating the __thread variables */
85    helper_thread_t helper_threads[4];
86    int i;
87    for (i = 0; i < ARRAY_SIZE(helper_threads); i++) {
88        create_helper_thread(env, &helper_threads[i]);
89        start_helper(env, &helper_threads[i], tls_helper, i, done_ep, start_ep, 0);
90    }
91    /* wait for all threads to be done */
92    for (i = 0; i < ARRAY_SIZE(helper_threads); i++) {
93        seL4_Wait(done_ep, NULL);
94    }
95    /* signal all threads to do the verification step */
96    for (i = 0; i < ARRAY_SIZE(helper_threads); i++) {
97        seL4_Signal(start_ep);
98    }
99    /* wait for them all to complete */
100    for (i = 0; i < ARRAY_SIZE(helper_threads); i++) {
101        wait_for_helper(&helper_threads[i]);
102        cleanup_helper(env, &helper_threads[i]);
103    }
104    return sel4test_get_result();
105}
106DEFINE_TEST(
107    TLS0002,
108    "Test multiple threads using __thread variables",
109    test_threads_tls,
110    true
111)
112
113// Thread local storage value.
114#define INITIAL_TLS_VALUE 42
115#define TLS_INCREMENT_ITERATIONS 10000
116#define NUM_PARALLEL_THREADS 8
117
118static __thread int tls_value = INITIAL_TLS_VALUE;
119
120// Thread that competes.
121static int simple_tls_test_thread(
122    UNUSED seL4_Word arg1,
123    UNUSED seL4_Word arg2,
124    UNUSED seL4_Word arg3,
125    UNUSED seL4_Word arg4
126)
127{
128    // Each thread should start with the same value.
129    if (tls_value != INITIAL_TLS_VALUE) {
130        sel4test_failure("TLS started with incorrect value");
131        return -1;
132    }
133
134    // First try increment atomically.
135    int initial = tls_value;
136    int last = initial;
137    for (int i = 0; i < TLS_INCREMENT_ITERATIONS; i++) {
138        int next = __sync_add_and_fetch(&tls_value, 1);
139        if (next != last + 1) {
140            sel4test_failure("TLS did not increment atomically");
141            return -1;
142        }
143        last = next;
144    }
145
146    if (tls_value != initial + TLS_INCREMENT_ITERATIONS) {
147        sel4test_failure("TLS did not increment atomically");
148        return -1;
149    }
150
151    // Then try non-atomic.
152    // First try increment atomically.
153    initial = tls_value;
154    last = initial;
155    for (int i = 0; i < TLS_INCREMENT_ITERATIONS; i++) {
156        int next = ++tls_value;
157        if (next != last + 1) {
158            sel4test_failure("TLS did not increment correctly.");
159            return -1;
160        }
161        last = next;
162    }
163
164    if (tls_value != initial + TLS_INCREMENT_ITERATIONS) {
165        sel4test_failure("TLS did not increment correctly");
166        return -1;
167    }
168
169    return 0;
170}
171
172int test_sel4utils_thread_tls(env_t env)
173{
174    helper_thread_t threads[NUM_PARALLEL_THREADS];
175
176    for (int t = 0; t < NUM_PARALLEL_THREADS; t++) {
177        create_helper_thread(env, &threads[t]);
178        start_helper(
179            env, &threads[t],
180            simple_tls_test_thread,
181            0, 0, 0, 0
182        );
183    }
184
185    for (int t = 0; t < NUM_PARALLEL_THREADS; t++) {
186        wait_for_helper(&threads[t]);
187        cleanup_helper(env, &threads[t]);
188    }
189
190    return sel4test_get_result();
191}
192DEFINE_TEST(
193    TLS0006,
194    "sel4utils_thread with distinct TLS should not interfere",
195    test_sel4utils_thread_tls,
196    true
197)
198