tests: add am-tests
This commit is contained in:
parent
8fbf7f9425
commit
0a123cc9e6
11 changed files with 405 additions and 0 deletions
3
tests/am-tests/Makefile
Normal file
3
tests/am-tests/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
NAME = amtest
|
||||||
|
SRCS = $(shell find src/ -name "*.c")
|
||||||
|
include $(AM_HOME)/Makefile
|
24
tests/am-tests/include/amtest.h
Normal file
24
tests/am-tests/include/amtest.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef __AMUNIT_H__
|
||||||
|
#define __AMUNIT_H__
|
||||||
|
|
||||||
|
#include <am.h>
|
||||||
|
#include <klib.h>
|
||||||
|
#include <klib-macros.h>
|
||||||
|
|
||||||
|
#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
|
37
tests/am-tests/src/main.c
Normal file
37
tests/am-tests/src/main.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
69
tests/am-tests/src/tests/devscan.c
Normal file
69
tests/am-tests/src/tests/devscan.c
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
7
tests/am-tests/src/tests/hello.c
Normal file
7
tests/am-tests/src/tests/hello.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
void hello() {
|
||||||
|
for (int i = 0; i < 10; i ++) {
|
||||||
|
putstr("Hello, AM World @ " __ISA__ "\n");
|
||||||
|
}
|
||||||
|
}
|
26
tests/am-tests/src/tests/intr.c
Normal file
26
tests/am-tests/src/tests/intr.c
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
35
tests/am-tests/src/tests/keyboard.c
Normal file
35
tests/am-tests/src/tests/keyboard.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
7
tests/am-tests/src/tests/mp.c
Normal file
7
tests/am-tests/src/tests/mp.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
void mp_print() {
|
||||||
|
while (1) {
|
||||||
|
printf("%d", cpu_current());
|
||||||
|
}
|
||||||
|
}
|
17
tests/am-tests/src/tests/rtc.c
Normal file
17
tests/am-tests/src/tests/rtc.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
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 ++;
|
||||||
|
}
|
||||||
|
}
|
90
tests/am-tests/src/tests/video.c
Normal file
90
tests/am-tests/src/tests/video.c
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
tests/am-tests/src/tests/vm.c
Normal file
90
tests/am-tests/src/tests/vm.c
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#include <amtest.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
Loading…
Reference in a new issue