#include "alloc.h" #include "allocArea.h" #include "ata.h" #include "assert.h" #include "cpu_context.h" #include "kernel.h" #include "klibc.h" #include "thread.h" #include "list.h" #include "mem.h" #include "mmuContext.h" #include "paging.h" #include "process.h" #include "serial.h" #include "stack.h" #include "synchro.h" #include "syscall.h" #include "swintr.h" #include "ringbuffer.h" #include "time.h" void testMemcpyPerf() { struct test_struct { char data[4096]; }; // instantiate 2 structs. for our purposes, we don't care what data is in // there. set them to `volatile` so the compiler won't optimize away what we // do with them volatile struct test_struct dest, source; printf("Test Memcpy perf\n"); // run through powers-of-two memcpy's, printing stats for each test for (size_t len = 1; len <= sizeof(dest); len <<= 1) { uint32_t start = read_cycle_counter(); // << Start count memcpy((void *)&dest, (void *)&source, len); uint32_t stop = read_cycle_counter(); // << Stop count // print out the cycles consumed printf("len = %d, %d %d cyccnt = %d, cycles/byte = %d\n", (uint32_t)len, stop, start, stop - start, (stop - start) / len); } } void testPhymem(void) { printf("Testing memory PHY\n"); struct phyMemDesc *allocated_page_list; struct phyMemDesc *page; // Cast in mem_desc to use it. In fact it's the addr of 4K free memory list_init(allocated_page_list); int allocCount = 0; int freeCount = 0; uint freePageStatBegin, usedPageStatBegin; uint freePageStatAlloc, usedPageStatAlloc; uint freePageStatFree, usedPageStatFree; memGetStat(&freePageStatBegin, &usedPageStatBegin); while ((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL) { page->phy_addr = allocCount; allocCount++; list_add_tail(allocated_page_list, page); } printf("%d pages allocated\n", allocCount); memGetStat(&freePageStatAlloc, &usedPageStatAlloc); assert(freePageStatAlloc == 0); assert((usedPageStatAlloc - usedPageStatBegin) == (uint)allocCount); while ((allocated_page_list != NULL) && (page = list_pop_head(allocated_page_list)) != NULL) { assertmsg(page->phy_addr == (ulong)freeCount, "page %p modified", page); assertmsg(unrefPhyPage((ulong)page) >= 0, "Failed to free page %p\n", page); freeCount++; } printf("%d pages freed\n", freeCount); memGetStat(&freePageStatFree, &usedPageStatFree); assert(freePageStatFree == freePageStatBegin); assert(usedPageStatFree == usedPageStatBegin); assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL, "Cannot allocate memory\n"); unrefPhyPage((ulong)page); } static void *testAllocNSet(size_t size) { void *allocated = malloc(size); assert(allocated); memset(allocated, size, size); return allocated; } void testAllocArea(){ vaddr_t area = areaAlloc(1, 0); vaddr_t area2 = areaAlloc(1, AREA_PHY_MAP); assert(area != area2); areaFree(area); areaFree(area2); } void testAlloc(void) { assert(malloc(1410065407) == NULL); for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) { assert(malloc(sizeof(struct slabEntry)) != NULL); } for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) { assert(malloc(sizeof(struct slabDesc)) != NULL); } assert(malloc(1)); assert(malloc(2)); assert(malloc(3)); assert(malloc(4)); void *malloc1 = malloc(sizeof(void *)); void *malloc2 = malloc(sizeof(void *)); assert((char *)malloc2 == ((char *)malloc1 + sizeof(void *))); free(malloc2); void *malloc3 = malloc(sizeof(void *)); assertmsg((char *)malloc2 == (char *)malloc3, " %p %p\n", malloc2, malloc3); free(malloc1); free(malloc3); void *alloc1 = testAllocNSet(1024); void *alloc2 = testAllocNSet(1024); void *alloc3 = testAllocNSet(1024); void *alloc4 = testAllocNSet(1024); void *alloc5 = testAllocNSet(1024); void *alloc6 = testAllocNSet(1024); void *alloc7 = testAllocNSet(4096); void *alloc8 = testAllocNSet(8192); free(alloc1); free(alloc2); free(alloc3); free(alloc4); free(alloc5); free(alloc6); free(alloc7); free(alloc8); void *alloc11 = testAllocNSet(1024); void *alloc12 = testAllocNSet(1024); void *alloc13 = testAllocNSet(1024); void *alloc14 = testAllocNSet(1024); void *alloc15 = testAllocNSet(1024); void *alloc16 = testAllocNSet(1024); free(alloc11); free(alloc12); free(alloc13); free(alloc14); free(alloc15); free(alloc16); } void testPaging(void) { printf("Testing paging\n"); struct phyMemDesc *allocated_page_list; struct phyMemDesc *page; // Cast in mem_desc to use it. In fact it's the addr of 4K free memory list_init(allocated_page_list); int allocCount = 0; int freeCount = 0; while ((page = (struct phyMemDesc *)areaAlloc(1, AREA_PHY_MAP)) != NULL) { memset(page, allocCount, PAGE_SIZE); allocCount++; list_add_tail(allocated_page_list, page); } printf("%d pages allocated\n", allocCount); while (!list_is_empty(allocated_page_list) && (page = list_pop_head(allocated_page_list)) != NULL) { assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %p\n", freeCount, (void *)page->phy_addr); areaFree((vaddr_t)page); freeCount++; } printf("%d pages freed\n", freeCount); assert(freeCount == allocCount); assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL, "Cannot allocate memory\n"); unrefPhyPage((ulong)page); } static void test_backtrace_2(int a, int b) { printStackTrace(a + b); } static void test_backtrace_1(int a) { test_backtrace_2(a, 3); } void test_backtrace() { test_backtrace_1(2); } /* ====================================================================== * Demonstrate the use of the CPU kernet context management API: * - A coroutine prints "Hlowrd" and switches to the other after each * letter * - A coroutine prints "el ol\n" and switches back to the other after * each letter. * The first to reach the '\n' returns back to main. */ struct cpu_state *ctxt_hello1; struct cpu_state *ctxt_hello2; struct cpu_state *ctxt_main; vaddr_t hello1_stack, hello2_stack; static void reclaim_stack(void *stack_vaddr) { free(stack_vaddr); } static void exit_hello12(void *stack_vaddr) { printf("Stopping hello\n"); cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack, (vaddr_t)stack_vaddr); } static void hello1(void *strIn) { char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("hello1: %c\n", *str); cpu_context_switch(&ctxt_hello1, ctxt_hello2); } /* You can uncomment this in case you explicitly want to exit now. But returning from the function will do the same */ /* cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t*) reclaim_stack, hello1_stack); */ } static void hello2(void *strIn) { char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("hello2: %c\n", *str); cpu_context_switch(&ctxt_hello2, ctxt_hello1); } /* You can uncomment this in case you explicitly want to exit now. But returning from the function will do the same */ /* cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t*) reclaim_stack, hello2_stack); */ } void testCoroutine() { #define DEMO_STACK_SIZE 1024 /* Allocate the stacks */ hello1_stack = (vaddr_t)malloc(DEMO_STACK_SIZE); hello2_stack = (vaddr_t)malloc(DEMO_STACK_SIZE); /* Initialize the coroutines' contexts */ cpu_kstate_init(&ctxt_hello1, (cpu_kstate_function_arg1_t *)hello1, (uint32_t) "Hlowrd", (vaddr_t)hello1_stack, DEMO_STACK_SIZE, (cpu_kstate_function_arg1_t *)exit_hello12, (uint32_t)hello1_stack); cpu_kstate_init(&ctxt_hello2, (cpu_kstate_function_arg1_t *)hello2, (uint32_t) "el ol\n", (vaddr_t)hello2_stack, DEMO_STACK_SIZE, (cpu_kstate_function_arg1_t *)exit_hello12, (uint32_t)hello2_stack); /* Go to first coroutine */ printf("Printing Hello World\\n...\n"); cpu_context_switch(&ctxt_main, ctxt_hello1); /* The first coroutine to reach the '\n' switched back to us */ printf("Back in main !\n"); } static void kthread1(void *strIn) { char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("kth1: %c\n", *str); threadYield(); } } static void kthread2(void *strIn) { char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("kth2: %c\n", *str); threadYield(); } } static int initialJiffies = 0; void sleepThread(void *arg) { (void)arg; int secSleep = 0; initialJiffies = jiffies; while (secSleep < 5) { // printf("Sleeping loop %d\n", secSleep); secSleep++; threadMsleep(100); } unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies); assertmsg(ellapsedTime >= 500 && ellapsedTime < 510, "ellapsedTime %lu\n", ellapsedTime); threadMsleep(ULONG_MAX); assert(0); } struct mutex mutexTest; void mutThread(void *arg) { (void)arg; printf("%s started\n", (char *)arg); int test = 5; while (test > 0) { mutexLock(&mutexTest); printf("%s sleep\n", (char *)arg); threadMsleep(100); printf("%s up\n", (char *)arg); mutexUnlock(&mutexTest); test--; } } static int haveTimeout = 0; void wqThread(void *arg) { (void)arg; DECLARE_WAITQUEUE(test); waitQueueInit(&test); assert(waitTimeout(&test, 100) == 1); waitQueueFree(&test); haveTimeout = 1; } void testKthread() { mutexInit(&mutexTest); // It is not expected to have necessarily "Hello world\n" properly written threadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n"); threadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n"); threadMsleep(100); threadCreate("wq timeout", wqThread, NULL); threadMsleep(200); assert(haveTimeout); threadCreate("sleep", sleepThread, NULL); threadMsleep(500); threadCreate("mtest1", mutThread, "mut1"); threadCreate("mtest2", mutThread, "mut2"); threadCreate("mtest3", mutThread, "mut3"); threadMsleep(2000); } void testATAThread(){ uint16_t buf[DISK_SECTOR_SIZE/2]; struct ata_device *dev = ATAGetDevice(0, 0); if(dev != NULL){ ATAReadSector(dev, 0, 1, buf); printf("Reading from disk 0x%x 0x%x 0x%x 0x%x\n", buf[0], buf[1], buf[2], buf[3]); memset(buf, 0, sizeof(buf)); buf[0]= 0x1; buf[1]= 0x2; buf[2]= 0x3; buf[3]= 0x4; //ATAWriteSector(dev, 0, 1, buf); } } static void testATA(){ threadCreate("ATA_TEST", testATAThread, NULL); //testATAThread(); } static void testMMUContext() { printf("Testing mmu\n"); struct mmu_context *current = mmuContextGetCurrent(); assert(current != NULL); struct mmu_context *new = mmuContextCreate(); assert(new != NULL); mmuContextSwitch(new); mmuContextSwitch(current); mmuContextUnref(new); } static void userProgramm() { int ret; asm volatile("movl %1,%%eax \n" "movl %2,%%ebx \n" "movl %3,%%ecx \n" "movl %4,%%edx \n" "int %5\n" "movl %%eax, %0" : "=g"(ret) : "g"(SYSCALL_ID_HELO), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) : "eax", "ebx", "ecx", "edx"); asm volatile("movl %1,%%eax \n" "movl %2,%%ebx \n" "movl %3,%%ecx \n" "movl %4,%%edx \n" "int %5\n" "movl %%eax, %0" : "=g"(ret) : "g"(SYSCALL_ID_EXIT), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) : "eax", "ebx", "ecx", "edx"); } static void testProcess(){ struct process *proc= processCreate("TESTPROCESS"); struct thread *th1 = threadCreate("th1", sleepThread, NULL); struct thread *th2 = threadCreate("th2", sleepThread, NULL); processAddThread(proc, th1); processAddThread(proc, th2); processListPrint(); threadMsleep(600); processRemoveThread(th1); processRemoveThread(th2); processUnref(proc); printf("Next process list should be empty\n"); processListPrint(); printf("Running user space app\n"); struct process *proc2= processCreate("TESTPROCESS2"); threadChangeCurrentContext(processGetMMUContext(proc2)); uaddr_t stackTop = 0xfffffffc; uaddr_t stackBottom = ALIGN_DOWN(stackTop, PAGE_SIZE); paddr_t stackPhy = allocPhyPage(1); assert(pageMap(stackBottom, stackPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); unrefPhyPage(stackPhy); uaddr_t mem = PAGING_BASE_USER_ADDRESS; paddr_t memPhy = allocPhyPage(1); assert(pageMap(mem, memPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); unrefPhyPage(memPhy); memcpy((void *)mem, userProgramm, PAGE_SIZE); threadCreateUser("UserProg", proc2, mem, 0, 0, stackTop); threadYield(); processUnref(proc2); threadChangeCurrentContext(NULL); printf("Running user space app DONE\n"); } void testRingBuffer() { printf("Testing ring buffer\n"); ringbuffer_t inst = ringbufferCreate(4); ringbufferDebug(inst); uint8_t item; assert(ringbufferEnqueue(inst, 1)); ringbufferDebug(inst); assert(ringbufferEnqueue(inst, 2)); ringbufferDebug(inst); assert(ringbufferEnqueue(inst, 3)); ringbufferDebug(inst); assert(ringbufferEnqueue(inst, 4)); ringbufferDebug(inst); assert(ringbufferEnqueue(inst, 5) == FALSE); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item)); assert(item == 1); ringbufferDebug(inst); assert(ringbufferEnqueue(inst, 5)); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item)); assert(item == 2); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item)); assert(item == 3); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item)); assert(item == 4); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item)); assert(item == 5); ringbufferDebug(inst); assert(ringbufferDequeue(inst, &item) == FALSE); ringbufferDebug(inst); ringbufferDestroy(inst); } void run_test(void) { // Example of checking thx to access attributs //int a[4] = {0}; //int b[3] = {0}; //memcpy(b, a, sizeof(a)); uint freemem, usedmem; uint afterFreemem, afterUsedmem; memGetStat(&freemem, &usedmem); testATA(); testMemcpyPerf(); { int test = 1000; long long int test64 = 0x100000000; assert(printf("hello") == 5); assert(printf("hello\n") == 6); assert(printf("hello %d\n", test) == 11); assert(printf("hello %llx\n", test64) == 16); assert(printf("hello %c\n", 'a') == 8); assert(printf("hello %s\n", "world") == 12); } { char *strAlloc; int ret = asprintf(&strAlloc, "hello %s\n", "world"); printf("asprint ret %d %s\n", ret, strAlloc); assert(ret == 13); // include the '\0' free(strAlloc); } testPaging(); printf("Testing Serial\n"); serialPutc('h'); serialPutc('e'); serialPutc('l'); serialPutc('l'); serialPutc('o'); testAlloc(); testAllocArea(); printf("Testing backtrace\n"); test_backtrace(); testCoroutine(); //int nbThread = threadCount(); testKthread(); //assert(nbThread + 1 == threadCount());//For sleep Thread testMMUContext(); testProcess(); memGetStat(&afterFreemem, &afterUsedmem); printf("free %d -> %d\n", freemem, afterFreemem); testRingBuffer(); }