547 lines
16 KiB
C
547 lines
16 KiB
C
#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(0);
|
|
printf("I should never be showed\n");
|
|
assert(1);
|
|
}
|
|
|
|
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();
|
|
}
|