diff --git a/tests/am-tests/Makefile b/tests/am-tests/Makefile new file mode 100644 index 0000000..907ff4f --- /dev/null +++ b/tests/am-tests/Makefile @@ -0,0 +1,3 @@ +NAME = amtest +SRCS = $(shell find src/ -name "*.c") +include $(AM_HOME)/Makefile diff --git a/tests/am-tests/include/amtest.h b/tests/am-tests/include/amtest.h new file mode 100644 index 0000000..de172ae --- /dev/null +++ b/tests/am-tests/include/amtest.h @@ -0,0 +1,24 @@ +#ifndef __AMUNIT_H__ +#define __AMUNIT_H__ + +#include +#include +#include + +#define IOE ({ ioe_init(); }) +#define CTE(h) ({ Context *h(Event, Context *); cte_init(h); }) +#define VME(f1, f2) ({ void *f1(int); void f2(void *); vme_init(f1, f2); }) +#define MPE ({ mpe_init(entry); }) + +extern void (*entry)(); + +#define CASE(id, entry_, ...) \ + case id: { \ + void entry_(); \ + entry = entry_; \ + __VA_ARGS__; \ + entry_(); \ + break; \ + } + +#endif diff --git a/tests/am-tests/src/main.c b/tests/am-tests/src/main.c new file mode 100644 index 0000000..0c7b300 --- /dev/null +++ b/tests/am-tests/src/main.c @@ -0,0 +1,37 @@ +#include + +void (*entry)() = NULL; // mp entry + +static const char *tests[256] = { + ['h'] = "hello", + ['H'] = "display this help message", + ['i'] = "interrupt/yield test", + ['d'] = "scan devices", + ['m'] = "multiprocessor test", + ['t'] = "real-time clock test", + ['k'] = "readkey test", + ['v'] = "display test", + ['p'] = "x86 virtual memory test", +}; + +int main(const char *args) { + switch (args[0]) { + CASE('h', hello); + CASE('i', hello_intr, IOE, CTE(simple_trap)); + CASE('d', devscan, IOE); + CASE('m', mp_print, MPE); + CASE('t', rtc_test, IOE); + CASE('k', keyboard_test, IOE); + CASE('v', video_test, IOE); + CASE('p', vm_test, CTE(vm_handler), VME(simple_pgalloc, simple_pgfree)); + case 'H': + default: + printf("Usage: make run mainargs=*\n"); + for (int ch = 0; ch < 256; ch++) { + if (tests[ch]) { + printf(" %c: %s\n", ch, tests[ch]); + } + } + } + return 0; +} diff --git a/tests/am-tests/src/tests/devscan.c b/tests/am-tests/src/tests/devscan.c new file mode 100644 index 0000000..d2c1a54 --- /dev/null +++ b/tests/am-tests/src/tests/devscan.c @@ -0,0 +1,69 @@ +#include + +static void input_test() { + printf("Input device test skipped.\n"); +} + +static void timer_test() { + AM_TIMER_UPTIME_T uptime; + uint32_t t0, t1; + + uptime = io_read(AM_TIMER_UPTIME); + t0 = uptime.us / 1000; + + for (int volatile i = 0; i < 10000000; i ++) ; + + uptime = io_read(AM_TIMER_UPTIME); + t1 = uptime.us / 1000; + + printf("Loop 10^7 time elapse: %d ms\n", t1 - t0); +} + +static uint8_t vmem[512 << 10]; +static inline gpuptr_t to_guest(void *ptr) { return ptr ? (uint8_t *)ptr - vmem : AM_GPU_NULL; } + +static void video_test() { + AM_GPU_CONFIG_T info = io_read(AM_GPU_CONFIG); + int w = info.width, h = info.height; + printf("Screen size: %d x %d\n", w, h); + + struct gpu_canvas *cv = (void *)vmem; + + for (uint8_t *p = (void *)&cv[8]; p != vmem + sizeof(vmem); p++) + *p = rand() & 0xff; + + cv[0] = (struct gpu_canvas) { + .w = -1, .h = -1, .x1 = w / 4, .y1 = 0, .w1 = w / 2, .h1 = h - 100, + .type = AM_GPU_TEXTURE, + .texture = (struct gpu_texturedesc) { + .w = 37, .h = 10, + .pixels = to_guest(&cv[8]), + }, + .sibling = to_guest(NULL), + }; + + io_write(AM_GPU_MEMCPY, 0, vmem, sizeof(vmem)); + io_write(AM_GPU_RENDER, 0); +} + +static void storage_test() { + #define nbytes 512 + static char buf[nbytes]; + AM_DISK_CONFIG_T info = io_read(AM_DISK_CONFIG); + printf("Storage: %d blocks of %d size. Show first 512 bytes\n", info.blkcnt, info.blksz); + io_write(AM_DISK_BLKIO, false, buf, 0, nbytes / info.blksz); + for (uint32_t i = 0; i < nbytes; i += 2) { + printf("%02x%02x ", buf[i] & 0xff, buf[i+1] & 0xff); + if ((i+2) % 32 == 0) printf("\n"); + } +} + +void devscan() { + printf("heap = [%08x, %08x)\n", heap.start, heap.end); + input_test(); + timer_test(); + video_test(); + storage_test(); + printf("Test End!\n"); + while (1); +} diff --git a/tests/am-tests/src/tests/hello.c b/tests/am-tests/src/tests/hello.c new file mode 100644 index 0000000..7672018 --- /dev/null +++ b/tests/am-tests/src/tests/hello.c @@ -0,0 +1,7 @@ +#include + +void hello() { + for (int i = 0; i < 10; i ++) { + putstr("Hello, AM World @ " __ISA__ "\n"); + } +} diff --git a/tests/am-tests/src/tests/intr.c b/tests/am-tests/src/tests/intr.c new file mode 100644 index 0000000..e03a0a7 --- /dev/null +++ b/tests/am-tests/src/tests/intr.c @@ -0,0 +1,26 @@ +#include + +Context *simple_trap(Event ev, Context *ctx) { + switch(ev.event) { + case EVENT_IRQ_TIMER: + printf("t"); break; + case EVENT_IRQ_IODEV: + printf("d"); break; + case EVENT_YIELD: + printf("y"); break; + default: + break; + } + return ctx; +} + +void hello_intr() { + printf("Hello, AM World @ " __ISA__ "\n"); + printf(" t = timer, d = device, y = yield\n"); + io_read(AM_INPUT_CONFIG); + iset(1); + while (1) { + for (volatile int i = 0; i < 10000000; i++) ; + yield(); + } +} diff --git a/tests/am-tests/src/tests/keyboard.c b/tests/am-tests/src/tests/keyboard.c new file mode 100644 index 0000000..3439333 --- /dev/null +++ b/tests/am-tests/src/tests/keyboard.c @@ -0,0 +1,35 @@ +#include + +#define NAMEINIT(key) [ AM_KEY_##key ] = #key, +static const char *names[] = { + AM_KEYS(NAMEINIT) +}; + +static bool has_uart, has_kbd; + +static void drain_keys() { + if (has_uart) { + while (1) { + char ch = io_read(AM_UART_RX).data; + if (ch == -1) break; + printf("Got (uart): %c (%d)\n", ch, ch & 0xff); + } + } + + if (has_kbd) { + while (1) { + AM_INPUT_KEYBRD_T ev = io_read(AM_INPUT_KEYBRD); + if (ev.keycode == AM_KEY_NONE) break; + printf("Got (kbd): %s (%d) %s\n", names[ev.keycode], ev.keycode, ev.keydown ? "DOWN" : "UP"); + } + } +} + +void keyboard_test() { + printf("Try to press any key (uart or keyboard)...\n"); + has_uart = io_read(AM_UART_CONFIG).present; + has_kbd = io_read(AM_INPUT_CONFIG).present; + while (1) { + drain_keys(); + } +} diff --git a/tests/am-tests/src/tests/mp.c b/tests/am-tests/src/tests/mp.c new file mode 100644 index 0000000..787de79 --- /dev/null +++ b/tests/am-tests/src/tests/mp.c @@ -0,0 +1,7 @@ +#include + +void mp_print() { + while (1) { + printf("%d", cpu_current()); + } +} diff --git a/tests/am-tests/src/tests/rtc.c b/tests/am-tests/src/tests/rtc.c new file mode 100644 index 0000000..5db6f74 --- /dev/null +++ b/tests/am-tests/src/tests/rtc.c @@ -0,0 +1,17 @@ +#include + +void rtc_test() { + AM_TIMER_RTC_T rtc; + int sec = 1; + while (1) { + while(io_read(AM_TIMER_UPTIME).us / 1000000 < sec) ; + rtc = io_read(AM_TIMER_RTC); + printf("%d-%d-%d %02d:%02d:%02d GMT (", rtc.year, rtc.month, rtc.day, rtc.hour, rtc.minute, rtc.second); + if (sec == 1) { + printf("%d second).\n", sec); + } else { + printf("%d seconds).\n", sec); + } + sec ++; + } +} diff --git a/tests/am-tests/src/tests/video.c b/tests/am-tests/src/tests/video.c new file mode 100644 index 0000000..21b4d72 --- /dev/null +++ b/tests/am-tests/src/tests/video.c @@ -0,0 +1,90 @@ +#include + +#define FPS 30 +#define N 32 + +static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) { + return (r << 16) | (g << 8) | b; +} +static inline uint8_t R(uint32_t p) { return p >> 16; } +static inline uint8_t G(uint32_t p) { return p >> 8; } +static inline uint8_t B(uint32_t p) { return p; } + +static uint32_t canvas[N][N]; +static int used[N][N]; + +static uint32_t color_buf[32 * 32]; + +void redraw() { + int w = io_read(AM_GPU_CONFIG).width / N; + int h = io_read(AM_GPU_CONFIG).height / N; + int block_size = w * h; + assert((uint32_t)block_size <= LENGTH(color_buf)); + + int x, y, k; + for (y = 0; y < N; y ++) { + for (x = 0; x < N; x ++) { + for (k = 0; k < block_size; k ++) { + color_buf[k] = canvas[y][x]; + } + io_write(AM_GPU_FBDRAW, x * w, y * h, color_buf, w, h, false); + } + } + io_write(AM_GPU_FBDRAW, 0, 0, NULL, 0, 0, true); +} + +static uint32_t p(int tsc) { + int b = tsc & 0xff; + return pixel(b * 6, b * 7, b); +} + +void update() { + static int tsc = 0; + static int dx[4] = {0, 1, 0, -1}; + static int dy[4] = {1, 0, -1, 0}; + + tsc ++; + + for (int i = 0; i < N; i ++) + for (int j = 0; j < N; j ++) { + used[i][j] = 0; + } + + int init = tsc * 1; + canvas[0][0] = p(init); used[0][0] = 1; + int x = 0, y = 0, d = 0; + for (int step = 1; step < N * N; step ++) { + for (int t = 0; t < 4; t ++) { + int x1 = x + dx[d], y1 = y + dy[d]; + if (x1 >= 0 && x1 < N && y1 >= 0 && y1 < N && !used[x1][y1]) { + x = x1; y = y1; + used[x][y] = 1; + canvas[x][y] = p(init + step / 2); + break; + } + d = (d + 1) % 4; + } + } +} + +void video_test() { + unsigned long last = 0; + unsigned long fps_last = 0; + int fps = 0; + + while (1) { + unsigned long upt = io_read(AM_TIMER_UPTIME).us / 1000; + if (upt - last > 1000 / FPS) { + update(); + redraw(); + last = upt; + fps ++; + } + if (upt - fps_last > 1000) { + // display fps every 1s + printf("%d: FPS = %d\n", upt, fps); + fps_last = upt; + fps = 0; + } + } +} diff --git a/tests/am-tests/src/tests/vm.c b/tests/am-tests/src/tests/vm.c new file mode 100644 index 0000000..c89079f --- /dev/null +++ b/tests/am-tests/src/tests/vm.c @@ -0,0 +1,90 @@ +#include + +static Context *uctx; +static AddrSpace prot; +static uintptr_t st = 0; +static int first_trap = 1; + +void *simple_pgalloc(int size) { + if (st == 0) { st = (uintptr_t)heap.start; } + while (st % size != 0) st++; + void *ret = (void *)st; + st += size; + return ret; +} + +void simple_pgfree(void *ptr) { +} + +Context* vm_handler(Event ev, Context *ctx) { + switch (ev.event) { + case EVENT_YIELD: + break; + case EVENT_IRQ_TIMER: + case EVENT_IRQ_IODEV: + printf("==== interrupt (%s) ====\n", ev.msg); + break; + case EVENT_PAGEFAULT: + printf("PF: %x %s%s%s\n", + ev.ref, + (ev.cause & MMAP_NONE) ? "[not present]" : "", + (ev.cause & MMAP_READ) ? "[read fail]" : "", + (ev.cause & MMAP_WRITE) ? "[write fail]" : ""); + break; + case EVENT_SYSCALL: + iset(1); + for (int volatile i = 0; i < 1000000; i++) ; + printf("%d ", ctx->GPRx); + break; + default: + assert(0); + } + if (first_trap) { + first_trap = 0; + return uctx; + } else { + return ctx; + } +} + +uint8_t code[] = { +#ifdef __ARCH_NATIVE + 0x31, 0xc0, // xor %eax, %eax + 0x83, 0xc0, 0x01, // add $1, %eax + 0xff, 0x14, 0x25, 0x00, 0x00, 0x10, 0x00, // call *0x100000 + 0xeb, 0xf4, // jmp 2 +#else + 0x31, 0xc0, // xor %eax, %eax + 0x8d, 0xb6, // lea 0(%esi), %esi + 0x00, 0x00, 0x00, 0x00, + 0x83, 0xc0, 0x01, // add $1, %eax + 0xcd, 0x80, // int $0x80 + 0xeb, 0xf9, // jmp 8 +#endif + +}; + +void vm_test() { + if (!strncmp(__ISA__, "x86", 3) == 0 && + !strcmp(__ISA__, "native") == 0) { + printf("Not supported architecture.\n"); + return; + } + protect(&prot); + printf("Protected address space: [%p, %p)\n", prot.area.start, prot.area.end); + + uint8_t *ptr = (void*)((uintptr_t)(prot.area.start) + + ((uintptr_t)(prot.area.end) - (uintptr_t)(prot.area.start)) / 2); + + void *pg = simple_pgalloc(prot.pgsize); + memcpy(pg, code, sizeof(code)); + + map(&prot, ptr, pg, MMAP_WRITE | MMAP_READ); + printf("Code copied to %p (physical %p) execute\n", ptr, pg); + + static uint8_t stack[4096]; + uctx = ucontext(&prot, RANGE(stack, stack + sizeof(stack)), ptr); + + iset(1); + yield(); +}