1/*
2 * Copyright 2016, 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(D61_BSD)
11 */
12
13#include <autoconf.h>
14#ifdef CONFIG_REFOS_RUN_TESTS
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <assert.h>
20#include <math.h>
21#include <time.h>
22#include <unistd.h>
23
24#include <refos/test.h>
25#include <refos-io/stdio.h>
26#include <refos-util/init.h>
27#include <refos/sync.h>
28
29/* Debug printing. */
30#include <refos-util/dprintf.h>
31
32#include <refos-rpc/proc_client.h>
33#include <refos-rpc/proc_client_helper.h>
34#include <refos/vmlayout.h>
35#include <data_struct/cvector.h>
36
37#define BSS_MAGIC 0xBA13DD37
38#define BSS_ARRAY_SIZE 0x20000
39#define TEST_USER_TEST_APPNAME "/fileserv/test_user"
40#define TEST_NUMTHREADS 8
41
42char bssArray[BSS_ARRAY_SIZE];
43int bssVar = BSS_MAGIC;
44int bssVar2;
45
46const char* dprintfServerName = "USER_TEST";
47int dprintfServerColour = 38;
48
49static int
50test_bss(void)
51{
52    test_start("bss zero");
53    test_assert(bssVar == BSS_MAGIC);
54    test_assert(bssVar2 == 0);
55    for (int i = 0; i < BSS_ARRAY_SIZE; i++) {
56        test_assert(bssArray[i] == 0);
57        bssArray[i] = (char)((i*7)%250);
58    }
59    test_assert(bssVar == BSS_MAGIC);
60    test_assert(bssVar2 == 0);
61    for (int i = 0; i < BSS_ARRAY_SIZE; i++) {
62        test_assert(bssArray[i] == (char)((i*7)%250));
63    }
64    return test_success();
65}
66
67static int
68test_stack(void)
69{
70    test_start("stack");
71    const size_t stackAllocSize = 0x2000;
72    char stackArray[stackAllocSize];
73    for (int i = 0; i < stackAllocSize; i++) {
74        stackArray[i] = (char)(i * 1234);
75    }
76    for (int i = 0; i < stackAllocSize; i++) {
77        test_assert( stackArray[i] == (char)(i * 1234));
78    }
79    return test_success();
80}
81
82static int
83test_heap(void)
84{
85    test_start("heap");
86    /* Test heap malloc actually works. */
87    const size_t heapAllocSize = 0x16000;
88    char *heapArray = malloc(heapAllocSize);
89    test_assert(heapArray);
90    for (int i = 0; i < heapAllocSize; i++) {
91        heapArray[i] = (i%2)?'z':'a';
92    }
93    for (int i = 0; i < heapAllocSize; i++) {
94        test_assert(heapArray[i] == (i%2)?'z':'a');
95    }
96    free(heapArray);
97    /* Test that free works by continually allocating and free-ing a large block. */
98    for (int i = 0; i < 10000; i++) {
99        char *heapArray2 = malloc(heapAllocSize);
100        test_assert(heapArray2);
101        heapArray2[4] = 123;
102        test_assert(heapArray2[4] == 123);
103        free(heapArray);
104    }
105    return test_success();
106}
107
108static int
109test_malloc_huge(void)
110{
111    test_start("huge malloc");
112    const size_t heapAllocSize = 0x4000000; // 64MB allocation.
113    char *heapArray = malloc(heapAllocSize);
114    test_assert(heapArray);
115    free(heapArray);
116    return test_success();
117}
118
119static void
120test_memory(void)
121{
122    test_bss();
123    test_stack();
124    test_heap();
125    test_malloc_huge();
126}
127
128static int
129test_param(void)
130{
131    test_start("param");
132    int paramStrMatch = !strcmp(TEST_USER_TEST_APPNAME, (char*) PROCESS_STATICPARAM_ADDR);
133    test_assert(paramStrMatch);
134    return test_success();
135}
136
137static int
138test_libc_maths(void)
139{
140    test_start("libc maths");
141    static double eps = 0.000001f;
142    test_assert(abs(sin(3.4) - 0.25554110202) < eps);
143    test_assert(abs(cosh(0.4) - 1.08107237184) < eps);
144    test_assert(abs(tan(0.75) - 0.93159645994) < eps);
145    test_assert(abs(floor(313.421326) - 313.0) < eps);
146    return test_success();
147}
148
149static int
150test_libc_string(void)
151{
152    test_start("libc string");
153    char g[] = "the quick brown fox jumped over the lazy dog.";
154    memset(g, '-', 3);
155    strcat(g, " 12345.");
156    size_t sz = strlen(g);
157    test_assert(sz == 52);
158    int c = strcmp(g,"blob.");
159    test_assert(c != 0);
160    c = strcmp(g, "--- quick brown fox jumped over the lazy dog. 12345.");
161    test_assert(c == 0);
162    return test_success();
163}
164
165static void
166test_libc(void)
167{
168    test_libc_maths();
169    test_libc_string();
170}
171
172static seL4_CPtr testThreadEP;
173static sync_mutex_t testThreadMutex;
174static uint32_t testThreadCount = 0;
175
176static int
177test_threads_func(void *arg)
178{
179    /* Thread entry point which loops and then signals parent and hangs. */
180    for (int i = 0; i < 10000; i++) {
181        sync_acquire(testThreadMutex);
182        testThreadCount++;
183        sync_release(testThreadMutex);
184    }
185    for(int i = 0; i < 20; i++) {
186        for(int delay = 0; delay < 1000000; delay++);
187    }
188    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
189    seL4_Call(testThreadEP, tag);
190    while(1);
191    return 0;
192}
193
194static int
195test_threads(void)
196{
197    test_start("threads");
198    int threadID;
199    testThreadEP = 0;
200    testThreadCount = 0;
201
202    static char test_clone_stack[TEST_NUMTHREADS][4096];
203
204    /* Create the endpoint on which threads will signal. */
205    tvprintf("test_threads creating new ep...\n");
206    testThreadEP = proc_new_endpoint();
207    test_assert(testThreadEP != 0);
208    test_assert(REFOS_GET_ERRNO() == ESUCCESS);
209
210    tvprintf("test_threads creating new mutex...\n");
211    testThreadMutex = sync_create_mutex();
212    test_assert(testThreadMutex);
213
214    /* Start the threads. */
215    for(int i = 0; i < TEST_NUMTHREADS; i++) {
216        tvprintf("test_threads cloning thread child %d...\n", i);
217        threadID = proc_clone(test_threads_func, &test_clone_stack[i][4096], 0, 0);
218        test_assert(REFOS_GET_ERRNO() == ESUCCESS);
219        test_assert(threadID == i + 1);
220    }
221
222    /* Increment shared vraiable. */
223    for (int i = 0; i < 10000; i++) {
224        sync_acquire(testThreadMutex);
225        testThreadCount++;
226        sync_release(testThreadMutex);
227    }
228
229    /* Block and wait for thread exit signal. */
230    for(int i = 0; i < TEST_NUMTHREADS; i++) {
231        tvprintf("test_threads waiting thread child %d...\n", i);
232        seL4_Word badge;
233        seL4_Recv(testThreadEP, &badge);
234        test_assert(badge == 0);
235    }
236
237    /* Test that were no race conditions on mutexed variable. */
238    test_assert(testThreadCount == (10000 * TEST_NUMTHREADS) + 10000);
239
240    tvprintf("test_threads deleting ep...\n");
241    sync_destroy_mutex(testThreadMutex);
242    proc_del_endpoint(testThreadEP);
243    return test_success();
244}
245
246static int
247test_cvector(void)
248{
249    test_start("cvector");
250    // Src: https://gist.github.com/EmilHernvall/953968
251    cvector_t v; cvector_init(&v);
252    cvector_add(&v, (cvector_item_t)1); cvector_add(&v, (cvector_item_t)2);
253    cvector_add(&v, (cvector_item_t)3); cvector_add(&v, (cvector_item_t)4);
254    cvector_add(&v, (cvector_item_t)5);
255    test_assert(cvector_count(&v) == (int)5);
256    test_assert(cvector_get(&v, 0) == (cvector_item_t)1);
257    test_assert(cvector_get(&v, 1) == (cvector_item_t)2);
258    test_assert(cvector_get(&v, 2) == (cvector_item_t)3);
259    test_assert(cvector_get(&v, 3) == (cvector_item_t)4);
260    test_assert(cvector_get(&v, 4) == (cvector_item_t)5);
261    cvector_delete(&v, 1);
262    cvector_delete(&v, 3);
263    test_assert(cvector_count(&v) == (int)3);
264    test_assert(cvector_get(&v, 0) == (cvector_item_t)1);
265    test_assert(cvector_get(&v, 1) == (cvector_item_t)3);
266    test_assert(cvector_get(&v, 2) == (cvector_item_t)4);
267    cvector_free(&v);
268    int vcStress = 10000;
269    for (int i = 0; i < vcStress; i++) {
270        int data = ((i << 2) * 0xcafebabe) ^ 0xdeadbeef;
271        cvector_add(&v, (cvector_item_t)data);
272        test_assert(cvector_count(&v) == (int)(i + 1));
273        test_assert(cvector_get(&v, i) == (cvector_item_t)data);
274        data = (data << 7) ^ 0xbaabaabb;
275        cvector_set(&v, i, (cvector_item_t)data);
276        test_assert(cvector_count(&v) == (int)(i + 1));
277        test_assert(cvector_get(&v, i) == (cvector_item_t)data);
278    }
279    cvector_free(&v);
280    return test_success();
281}
282
283static int
284test_filetable_read(void)
285{
286    test_start("filetable read");
287
288    FILE * testFile = fopen("fileserv/hello.txt", "r");
289    test_assert(testFile);
290
291    char str[128], str2[128];
292    fscanf(testFile, "%s %s", str, str2);
293    tvprintf("str [%s] str2 [%s]\n", str, str2);
294    test_assert(strcmp(str, "hello") == 0);
295    test_assert(strcmp(str2, "world!") == 0);
296    test_assert(feof(testFile));
297    fclose(testFile);
298
299    /* Future Work 5: set up muslc's errno
300       (see errno in libs/libmuslc/src/internal/syscall_ret.c)
301       Since the new muslc API implements errno, fopen() on a non-existent
302       file triggers errno which results in a segmentation fault. So, we
303       remove this part of the filetable read test until errno is set up in
304       muslc. Also note that setting up errno in RefOS would be helpful
305       in other areas of RefOS when changes are made to RefOS and seL4's external
306       API. As errno is intended to aid debugging, changes to external repos
307       will inevitably require changes to RefOS and having errno set up would
308       aid this process.
309    */
310    /*testFile = fopen("fileserv/hello_this_file_doesnt_exist.txt", "r");
311    test_assert(!testFile);*/
312
313    return test_success();
314}
315
316static int
317test_filetable_write(void)
318{
319    test_start("filetable write");
320
321    FILE * testFile = fopen("fileserv/test_file_abc", "w");
322    test_assert(testFile);
323    for (int i = 0; i < 32; i++) {
324        fprintf(testFile, "hello!\n");
325    }
326    for (int i = 0; i < 12; i++) {
327        int nw = fwrite("abc", 1, 3, testFile);
328        test_assert(nw == 3);
329    }
330
331    fprintf(testFile, "hello!\n");
332
333    /* Write a big block to file. */
334    static char temp[20480];
335    int nw = write(fileno(testFile), temp, 20480);
336    test_assert(nw == 20480);
337    fclose(testFile);
338
339    testFile = fopen("fileserv/test_file_abc", "r");
340    test_assert(testFile);
341    char str[128];
342    for (int i = 0; i < 32; i++) {
343        fscanf(testFile, "%s\n", str);
344        test_assert(!strcmp(str, "hello!"));
345    }
346    for (int i = 0; i < 12; i++) {
347        int nr = fread(str, 1, 3, testFile);
348        test_assert(nr == 3);
349        test_assert(str[0] == 'a');
350        test_assert(str[1] == 'b');
351        test_assert(str[2] == 'c');
352    }
353    fclose(testFile);
354    return test_success();
355}
356
357static int
358test_gettime(void)
359{
360    test_start("gettime");
361    time_t last = 0;
362    for (int i = 0; i < 150; i++) {
363        time_t t = time(NULL);
364        tvprintf("%d (last %d).\n", t, last);
365        test_assert(t >= last);
366        last = t;
367    }
368    return test_success();
369}
370
371#endif /* CONFIG_REFOS_RUN_TESTS */
372
373int
374main()
375{
376#ifdef CONFIG_REFOS_RUN_TESTS
377
378    /* Future Work 3:
379       How the selfloader bootstraps user processes needs to be modified further. Changes were
380       made to accomodate the new way that muslc expects process's stacks to be set up when
381       processes start, but the one part of this that still needs to changed is how user processes
382       find their system call table. Currently the selfloader sets up user processes so that
383       the selfloader's system call table is used by user processes by passing the address of the
384       selfloader's system call table to the user processes via the user process's environment
385       variables. Ideally, user processes would use their own system call table.
386    */
387
388    uintptr_t address = strtoll(getenv("SYSTABLE"), NULL, 16);
389    refos_init_selfload_child(address);
390    refos_initialise();
391    printf("USER_TEST | Hello world!\n");
392    printf("USER_TEST | Running RefOS User-level tests.\n");
393    test_title = "USER_TEST";
394
395    test_memory();
396    test_param();
397    test_libc();
398    test_threads();
399    test_cvector();
400    test_filetable_read();
401    test_filetable_write();
402    test_gettime();
403
404    test_print_log();
405#endif
406
407    return 0x1234;
408}
409