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 <stdlib.h>
17#include <string.h>
18#include "test_memserv.h"
19#include "../state.h"
20#include "../system/memserv/window.h"
21#include "../system/memserv/dataspace.h"
22#include "../system/memserv/ringbuffer.h"
23#include <refos/test.h>
24
25/* -------------------------------------- Window module test ------------------------------------ */
26
27int
28test_window_list(void)
29{
30    test_start("windows");
31    struct w_list wlist;
32    w_init(&wlist);
33    int nWindowTest = W_MAX_WINDOWS / 2;
34
35    /* Spam allocate all the windows. */
36    for (int i = 1; i < nWindowTest; i++) {
37        reservation_t tempr;
38        struct w_window* w = w_create_window(&wlist, 1024, -1, 0, NULL, tempr, true);
39        test_assert(w);
40        test_assert(w->wID == i);
41        test_assert(w->magic == W_MAGIC);
42        test_assert(w->size == 1024);
43        test_assert(w->clientOwnerPID == -1);
44        test_assert(w->mode == W_MODE_EMPTY);
45        struct w_window* w_ = w_get_window(&wlist, w->wID);
46        test_assert(w_ && w_->wID == i);
47        test_assert(w_ == w);
48    }
49
50    /* Free every second window. */
51    for (int i = 1; i < nWindowTest; i+=2) {
52        int err = w_delete_window(&wlist, i);
53        test_assert(err == ESUCCESS);
54    }
55
56    /* Make sure every second window is no longer get-able. */
57    for (int i = 1; i < nWindowTest; i++) {
58        struct w_window* w_ = w_get_window(&wlist, i);
59        if (i % 2) {
60            test_assert(w_ == NULL);
61        } else {
62            test_assert(w_ != NULL);
63            test_assert(w_->magic == W_MAGIC);
64            test_assert(w_->wID == i);
65        }
66    }
67
68    w_deinit(&wlist);
69    return test_success();
70}
71
72
73int
74test_window_associations(void)
75{
76    test_start("window associations");
77
78    struct w_associated_windowlist aw;
79    w_associate_init(&aw);
80
81    w_associate(&aw, 4, 400, 10);
82    w_associate(&aw, 2, 200, 10);
83    w_associate(&aw, 3, 300, 10);
84    w_associate(&aw, 1, 100, 10);
85    w_associate(&aw, 5, 500, 10);
86
87#if REFOS_TEST_VERBOSE_PRINT
88    tvprintf("------- Unsorted window list \n");
89    w_associate_print(&aw);
90
91    tvprintf("------- Sorted window list \n");
92    w_associate_find(&aw, (vaddr_t) 0);
93    w_associate_print(&aw);
94#endif
95
96    /* Associate some windows, and test window conflict checking with icky window
97       boundary vaddr cases. */
98    int testVaddr[] = {400, 401, 300, 301, 90, 110, 105, 500, 510, 505, 499, 200, 201};
99    int expectedWinID[] = {4, 4, 3, 3, W_INVALID_WINID, W_INVALID_WINID, 1, 5, W_INVALID_WINID,
100            5, W_INVALID_WINID, 2, 2};
101    int numTestVaddr = 13;
102    int foundWinID;
103    struct w_associated_window *foundWin = NULL;
104    for (int i = 0; i < numTestVaddr; i++) {
105        foundWin = w_associate_find(&aw, testVaddr[i]);
106        foundWinID = foundWin == NULL ? W_INVALID_WINID : foundWin->winID;
107        tvprintf("------- winID for vaddr %d: %d\n", testVaddr[i], foundWinID);
108        test_assert(foundWinID == expectedWinID[i]);
109    }
110
111    /* Test w_associate_check with icky window boundary vaddr cases. */
112    int testWin[] = {100, 1, 201, 20, 210, 10, 490, 50, 0, 499};
113    bool expectedCheckResult[] = {false, false, true, false, false};
114    int numTestWin = 5;
115    for (int i = 0; i < numTestWin; i++) {
116        bool check = w_associate_check(&aw, testWin[i*2], testWin[i*2+1]);
117        tvprintf("------- check for window [%d -> %d], %d\n", testWin[i*2],
118                 testWin[i*2] + testWin[i*2+1], check);
119        test_assert(check == expectedCheckResult[i]);
120    }
121
122    /* Test w_associate_find_range with icky window boundary cases. */
123    int testRange[] = {100, 1, 201, 20, 210, 10, 490, 50, 0, 499, 500, 10, 305, 5};
124    int expectedFindRangeResult[] = {1, W_INVALID_WINID, W_INVALID_WINID, W_INVALID_WINID, W_INVALID_WINID,
125            5, 3};
126    int numTestRange = 7;
127    for (int i = 0; i < numTestRange; i++) {
128        foundWin = w_associate_find_range(&aw, testRange[i*2], testRange[i*2+1]);
129        foundWinID = foundWin == NULL ? W_INVALID_WINID : foundWin->winID;
130        tvprintf("------- check range for window [%d -> %d], %d (expected to be %d)\n",
131                testRange[i*2], testRange[i*2] + testRange[i*2+1],
132                foundWinID, expectedFindRangeResult[i]);
133        test_assert(foundWinID == expectedFindRangeResult[i]);
134    }
135
136    /* Test that clearing window association list actually clears. */
137    w_associate_clear(&aw);
138    for (int i = 0; i < numTestWin; i++) {
139        bool check = w_associate_check(&aw, testWin[i*2], testWin[i*2+1]);
140        test_assert(check == true);
141    }
142
143    return test_success();
144}
145
146/* --------------------------------- RAM dataspace module test ---------------------------------- */
147
148int
149test_ram_dspace_list(void)
150{
151    test_start("ram dataspace list");
152
153    struct ram_dspace_list rlist;
154    ram_dspace_init(&rlist);
155
156    /* Create a bunch of RAM dataspaces to test with. */
157    const int rsz[] = {3, 1, REFOS_PAGE_SIZE, REFOS_PAGE_SIZE + 1,
158            REFOS_PAGE_SIZE + 2, REFOS_PAGE_SIZE * 12 + 1};
159    const int nrsz = 6;
160    struct ram_dspace *testDSpace[nrsz];
161
162    tvprintf("Creating dataspaces...\n");
163    for (int i = 0; i < nrsz; i++) {
164        tvprintf("    Creating dataspace %d (size %d)...\n", i, rsz[i]);
165        testDSpace[i] = ram_dspace_create(&rlist, rsz[i]);
166        test_assert(testDSpace[i] != NULL);
167        test_assert(testDSpace[i]->magic == RAM_DATASPACE_MAGIC);
168        test_assert(testDSpace[i]->ID == 1 + i);
169        test_assert(testDSpace[i]->ref == 1);
170    }
171
172    /* Test that getting ID works. */
173    tvprintf("Testing dataspace get...\n");
174    for (int i = 0; i < nrsz; i++) {
175        struct ram_dspace * d = ram_dspace_get(&rlist, testDSpace[i]->ID);
176        test_assert(d);
177        test_assert(d->magic == RAM_DATASPACE_MAGIC);
178        test_assert(d->ID == testDSpace[i]->ID);
179    }
180
181    /* Delete some dataspaces and make sure get doesn't work on them any more. */
182    tvprintf("Deleting dataspaces...\n");
183    ram_dspace_ref(&rlist, 6);
184    test_assert(testDSpace[5]->ref == 2);
185    ram_dspace_unref(&rlist, 6);
186    test_assert(testDSpace[5]->ref == 1);
187    ram_dspace_unref(&rlist, 6);
188    ram_dspace_unref(&rlist, 5);
189    for (int i = 0; i < 4; i++) {
190        tvprintf("   Testing that dspace ID %d get still works...\n", testDSpace[i]->ID);
191        struct ram_dspace * d = ram_dspace_get(&rlist, testDSpace[i]->ID);
192        test_assert(d);
193        test_assert(d->magic == RAM_DATASPACE_MAGIC);
194        test_assert(d->ID == testDSpace[i]->ID);
195        test_assert(d->ID == i + 1);
196    }
197    for (int i = 4; i < 6; i++) {
198        tvprintf("   Testing that dspace ID %d get returns NULL...\n", i + 1);
199        struct ram_dspace * d = ram_dspace_get(&rlist, i + 1);
200        test_assert(!d);
201    }
202
203    /* Create new dataspaces again, which should reuse the just freed IDs. */
204    tvprintf("Reallocating dataspaces...\n");
205    for (int i = 4; i < 6; i++) {
206        testDSpace[i] = ram_dspace_create(&rlist, rsz[i]);
207        test_assert(testDSpace[i] != NULL);
208        test_assert(testDSpace[i]->magic == RAM_DATASPACE_MAGIC);
209        test_assert(testDSpace[i]->ID == 1 + i);
210    }
211
212    /* Create new dataspace should assign new ID. */
213    tvprintf("Testing new dataspace creation to assign new ID...\n");
214    struct ram_dspace *testDS_ = ram_dspace_create(&rlist, REFOS_PAGE_SIZE);
215    test_assert(testDS_ && testDS_->magic == RAM_DATASPACE_MAGIC);
216    test_assert(testDS_->ID == 7);
217
218    /* Test get-size and resize. */
219    test_assert(ram_dspace_get_size(testDS_) == REFOS_PAGE_SIZE);
220    int error = ram_dspace_expand(testDS_, REFOS_PAGE_SIZE * 34);
221    test_assert(error == ESUCCESS);
222    test_assert(ram_dspace_get_size(testDS_) == REFOS_PAGE_SIZE * 34);
223
224    ram_dspace_deinit(&rlist);
225    return test_success();
226}
227
228/* Internal declarations. */
229int ram_dspace_read_page(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset);
230int ram_dspace_write_page(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset);
231
232/*! \brief Runs a single instance of the test_ram_dspace_read_write test.
233           Internal helper function.
234*/
235static int
236test_ram_dspace_read_write_instance_helper(int testBufSize, int writeSize, int writeOffset,
237        int readSize, int readOffset, char usePage, int srcOffset, int ansOffset, int ansSize)
238{
239    struct ram_dspace_list rlist;
240    ram_dspace_init(&rlist);
241
242    /* Function pointer prototypes for read/write function, so we can easily switch between using
243       ram_dspace_write_page / ram_dspace_write and ram_dspace_read_page / ram_dspace_read tests.
244    */
245    int (*dsRead)(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset) = NULL;
246    int (*dsWrite)(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset) = NULL;
247
248    int i, error;
249
250    /* Assign the function pointers based on the type of read/write call we want to test. */
251    if (usePage) {
252        dsRead = &ram_dspace_read_page;
253        dsWrite = &ram_dspace_write_page;
254    } else {
255        dsRead = &ram_dspace_read;
256        dsWrite = &ram_dspace_write;
257    }
258
259    /* Allocate some test buffers. */
260    char *srcBuf = kmalloc(testBufSize);
261    test_assert(srcBuf != NULL);
262
263    char *testBufA = kmalloc(testBufSize);
264    test_assert(testBufA != NULL);
265
266    char *testBufB = kmalloc(testBufSize);
267    test_assert(testBufB != NULL);
268
269    /* Initialise test buffers. */
270    for (i = 0; i < testBufSize; i++) {
271        srcBuf[i] = (rand() & 0xFF);
272        testBufA[i] = 0;
273        testBufB[i] = 0;
274    }
275
276    /* Create a test RAM dataspace to play with. */
277    struct ram_dspace *testDSpace = ram_dspace_create(&rlist, testBufSize);
278    test_assert(testDSpace != NULL);
279    test_assert(testDSpace->magic == RAM_DATASPACE_MAGIC);
280
281    error = dsWrite(srcBuf + srcOffset, writeSize, testDSpace, writeOffset);
282    test_assert(!error);
283    error = dsRead(testBufB, readSize, testDSpace, readOffset);
284    test_assert(!error);
285
286    memcpy(testBufA, srcBuf + ansOffset, ansSize);
287    for (i = 0; i < ansSize; i++) {
288        test_assert(testBufA[i] == testBufB[i]);
289    }
290
291    /* Clean up. */
292    kfree(testBufA);
293    kfree(testBufB);
294    kfree(srcBuf);
295
296    ram_dspace_deinit(&rlist);
297    return ESUCCESS;
298}
299
300int
301test_ram_dspace_read_write(void)
302{
303    test_start("ram dataspace read/write");
304    int error = 0;
305
306    /* Test read/write with aligned page. */
307    error = test_ram_dspace_read_write_instance_helper (
308            REFOS_PAGE_SIZE, REFOS_PAGE_SIZE, 0, REFOS_PAGE_SIZE, 0,
309            true, 0, 0, REFOS_PAGE_SIZE
310    );
311    test_assert(error == ESUCCESS);
312
313    error = test_ram_dspace_read_write_instance_helper (
314            REFOS_PAGE_SIZE, REFOS_PAGE_SIZE, 0, REFOS_PAGE_SIZE, 0,
315            false, 0, 0, REFOS_PAGE_SIZE
316    );
317    test_assert(error == ESUCCESS);
318
319    /* Test read/write with some offsets. */
320    error = test_ram_dspace_read_write_instance_helper (
321            REFOS_PAGE_SIZE, REFOS_PAGE_SIZE / 2, REFOS_PAGE_SIZE / 4,
322            REFOS_PAGE_SIZE / 8, REFOS_PAGE_SIZE / 2, false, 0, REFOS_PAGE_SIZE / 4,
323            REFOS_PAGE_SIZE / 8
324    );
325    test_assert(error == ESUCCESS);
326
327    error = test_ram_dspace_read_write_instance_helper (
328            REFOS_PAGE_SIZE, REFOS_PAGE_SIZE / 2, REFOS_PAGE_SIZE / 4,
329            REFOS_PAGE_SIZE / 8, REFOS_PAGE_SIZE / 2, true, 0, REFOS_PAGE_SIZE / 4,
330            REFOS_PAGE_SIZE / 8
331    );
332    test_assert(error == ESUCCESS);
333
334    /* Test read/write with offsets on big dataspace. */
335    error = test_ram_dspace_read_write_instance_helper (
336            REFOS_PAGE_SIZE * 3, REFOS_PAGE_SIZE * 2, REFOS_PAGE_SIZE / 2, 578,
337            REFOS_PAGE_SIZE + 200, false, 0, (REFOS_PAGE_SIZE / 2) + 200, 578
338    );
339    test_assert(error == ESUCCESS);
340
341    error = test_ram_dspace_read_write_instance_helper (
342            REFOS_PAGE_SIZE * 3, REFOS_PAGE_SIZE * 2, REFOS_PAGE_SIZE / 2,
343            REFOS_PAGE_SIZE, REFOS_PAGE_SIZE * 2, false, 0, REFOS_PAGE_SIZE + (REFOS_PAGE_SIZE / 2),
344            REFOS_PAGE_SIZE / 2
345    );
346    test_assert(error == ESUCCESS);
347
348    return test_success();
349}
350
351int
352test_ram_dspace_content_init(void)
353{
354    test_start("ram dataspace content init");
355    struct ram_dspace_list rlist;
356    ram_dspace_init(&rlist);
357
358    /* Create a dummy EP. */
359    const int npages = 9;
360    cspacepath_t dummyEP = procserv_mint_badge(20500);
361    cspacepath_t dummyWaiter = procserv_mint_badge(20501);
362    test_assert(dummyEP.capPtr);
363    test_assert(dummyWaiter.capPtr);
364
365    /* Create a test RAM dataspace to play with. */
366    struct ram_dspace *dspace = ram_dspace_create(&rlist, npages * REFOS_PAGE_SIZE);
367    test_assert(dspace != NULL);
368    test_assert(dspace->magic == RAM_DATASPACE_MAGIC);
369
370    int error = ram_dspace_need_content_init(dspace, 0x1000);
371    test_assert(error == -EINVALID);
372
373    /* Enable content init mode, with dummy EP. */
374    error = ram_dspace_content_init(dspace, dummyEP, 0x54);
375    test_assert(error == ESUCCESS);
376
377    /* Test content-init bit. */
378    error = ram_dspace_need_content_init(dspace, npages * REFOS_PAGE_SIZE + 0x35);
379    test_assert(error == -EINVALIDPARAM);
380    for (int i = 0; i < npages; i++) {
381        int val = ram_dspace_need_content_init(dspace, i * REFOS_PAGE_SIZE);
382        test_assert(val == true);
383        ram_dspace_set_content_init_provided(dspace,i * REFOS_PAGE_SIZE);
384        val = ram_dspace_need_content_init(dspace, i * REFOS_PAGE_SIZE);
385        test_assert(val == false);
386    }
387
388    /* Test waiter. */
389    error = ram_dspace_add_content_init_waiter(dspace, 0x2000, dummyWaiter);
390    test_assert(error == ESUCCESS);
391
392    ram_dspace_unref(&rlist, dspace->ID);
393    ram_dspace_deinit(&rlist);
394    return test_success();
395}
396
397
398/* ------------------------------- Ring buffer module test ------------------------------- */
399
400int
401test_ringbuffer(void)
402{
403    test_start("ring buffer");
404
405    struct ram_dspace_list rlist;
406    ram_dspace_init(&rlist);
407
408    /* Create a RAM dataspace to attach to a ringbuffer. */
409    struct ram_dspace *ds = ram_dspace_create(&rlist,
410        (REFOS_PAGE_SIZE * 3) + RINGBUFFER_METADATA_SIZE + 1);
411    char *buf = kmalloc(REFOS_PAGE_SIZE * 3);
412    char *outbuf = kmalloc(REFOS_PAGE_SIZE * 3);
413    test_assert(ds && buf && outbuf);
414    test_assert(ds->magic == RAM_DATASPACE_MAGIC);
415
416    /* Initialise ring buffer content. */
417    for (int i = 0; i < REFOS_PAGE_SIZE * 3; i++) {
418        outbuf[i] = 0;
419    }
420    for (int i = 0; i < REFOS_PAGE_SIZE * 3; i++) {
421        buf[i] = 'a' + (i / REFOS_PAGE_SIZE);
422    }
423
424    /* Create a write ringbuffer and make sure it has reffed the dataspace. */
425    struct rb_buffer *rb =  rb_create(ds, RB_WRITEONLY);
426    test_assert(rb && rb->magic == RINGBUFFER_MAGIC);
427    test_assert(rb->dataspace->ID = ds->ID);
428    test_assert(rb->dataspace->ref == 2);
429
430    /* Test rb_write icky wrapping case works. */
431    rb->localEnd = 4096;
432    rb->localStart = 8193;
433    ram_dspace_write(((char*) &rb->localEnd), sizeof(rb->localEnd), rb->dataspace,
434            sizeof(uint32_t));
435    ram_dspace_write(((char*) &rb->localStart), sizeof(rb->localStart), rb->dataspace, 0);
436    rb_write(rb, buf + REFOS_PAGE_SIZE, REFOS_PAGE_SIZE);
437
438    ram_dspace_read_page(outbuf, REFOS_PAGE_SIZE - (sizeof(seL4_Word) * 2),
439            rb->dataspace, (sizeof(seL4_Word) * 2));
440    ram_dspace_read_page(outbuf + REFOS_PAGE_SIZE - (sizeof(seL4_Word) * 2),
441            REFOS_PAGE_SIZE, rb->dataspace, REFOS_PAGE_SIZE);
442    ram_dspace_read_page(outbuf + (REFOS_PAGE_SIZE * 2) - (sizeof(seL4_Word) * 2),
443            REFOS_PAGE_SIZE, rb->dataspace, REFOS_PAGE_SIZE * 2);
444    ram_dspace_read_page(outbuf + (REFOS_PAGE_SIZE * 3) - (sizeof(seL4_Word) * 2),
445            (sizeof(seL4_Word) * 2), rb->dataspace,
446            REFOS_PAGE_SIZE * 3 - (sizeof(seL4_Word) * 2));
447
448    for (int i = 0; i < REFOS_PAGE_SIZE; i++) {
449        test_assert(buf[i] != outbuf[i]);
450    }
451    for (int i = REFOS_PAGE_SIZE; i < REFOS_PAGE_SIZE * 2; i++) {
452        test_assert(buf[i] == outbuf[i]);
453    }
454    for (int i = REFOS_PAGE_SIZE * 2; i < REFOS_PAGE_SIZE * 3; i++) {
455        test_assert(buf[i] != outbuf[i]);
456    }
457
458    /* Create another write ringbuffer and make sure it has reffed the dataspace. */
459    struct rb_buffer *rbw =  rb_create(ds, RB_WRITEONLY);
460    test_assert(rbw && rbw->magic == RINGBUFFER_MAGIC);
461    test_assert(rbw->dataspace->ID = ds->ID);
462    test_assert(rbw->dataspace->ref == 3);
463    for (int i = 0; i < REFOS_PAGE_SIZE * 3; i++) {
464        buf[i] = '0' + (i / REFOS_PAGE_SIZE);
465        outbuf[i] = 133;
466    }
467    size_t bytesRead = 42;
468
469    /* Create read ringbuffer and make sure it has reffed the dataspace. */
470    struct rb_buffer *rbr =  rb_create(ds, RB_READONLY);
471    test_assert(rbr && rbr->magic == RINGBUFFER_MAGIC);
472    test_assert(rbr->dataspace->ID = ds->ID);
473    test_assert(rbr->dataspace->ref == 4);
474
475    /* Test rb_read icky wrapping case works, after rb_write. */
476    rbw->localStart = rbw->localEnd = 1337;
477    rb_write(rbw, buf, REFOS_PAGE_SIZE * 3);
478
479    rbr->localStart = rbr->localEnd = 1337;
480    rb_read(rbr, outbuf, REFOS_PAGE_SIZE * 3, &bytesRead);
481
482    test_assert(bytesRead == REFOS_PAGE_SIZE * 3);
483
484    for (int i = 0; i < REFOS_PAGE_SIZE * 3; i++) {
485        test_assert(buf[i] == outbuf[i]);
486    }
487
488    /* Test ring buffer deletion. */
489    rb_delete(rb);
490    test_assert(ds->ref == 3);
491    test_assert(ds->magic == RAM_DATASPACE_MAGIC);
492
493    rb_delete(rbw);
494    test_assert(ds->ref == 2);
495    test_assert(ds->magic == RAM_DATASPACE_MAGIC);
496
497    rb_delete(rbr);
498    test_assert(ds->ref == 1);
499    test_assert(ds->magic == RAM_DATASPACE_MAGIC);
500
501    kfree(buf);
502    kfree(outbuf);
503
504    ram_dspace_deinit(&rlist);
505    return test_success();
506}
507
508
509#endif /* CONFIG_REFOS_RUN_TESTS */
510