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