Renaming and documentating header

This commit is contained in:
Toby Jaffey 2025-12-12 20:42:26 +00:00
parent 9880eadf4f
commit 8158ac647c
19 changed files with 305 additions and 225 deletions

View file

@ -23,10 +23,10 @@ void main(void) {
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
break; break;
case UVM32_SYSCALL_PUTC: case UVM32_SYSCALL_PUTC:
putc(uvm32_getval(&vmst, &evt, ARG0)); putc(uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
println(str); println(str);
} break; } break;
default: default:
@ -35,7 +35,9 @@ void main(void) {
} }
break; break;
case UVM32_EVT_ERR: case UVM32_EVT_ERR:
println(evt.data.err.errstr); println("error: ");
printdec(evt.data.err.errcode);
println("");
break; break;
default: default:
break; break;

View file

@ -181,6 +181,8 @@ uvm32_run(&vmst, &evt, 1000);
The uvm32 memory size is set at compile time with `-DUVM32_MEMORY_SIZE=X` (in bytes). A memory of 512 bytes will be sufficient for trivial programs. The uvm32 memory size is set at compile time with `-DUVM32_MEMORY_SIZE=X` (in bytes). A memory of 512 bytes will be sufficient for trivial programs.
Define `UVM32_ERROR_STRINGS` to add an `errstr` field to `uvm32_evt_err_t` giving a printable error string.
## Debugging ## Debugging
Binaries can be disassembled with Binaries can be disassembled with

View file

@ -36,19 +36,19 @@ void loop(void) {
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
break; break;
case UVM32_SYSCALL_PUTC: case UVM32_SYSCALL_PUTC:
Serial.print((char)uvm32_getval(&vmst, &evt, ARG0)); Serial.print((char)uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
Serial.print(str); Serial.print(str);
Serial.println(""); Serial.println("");
} break; } break;
case UVM32_SYSCALL_PRINT: { case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
Serial.print(str); Serial.print(str);
} break; } break;
case UVM32_SYSCALL_MILLIS: { case UVM32_SYSCALL_MILLIS: {
uvm32_setval(&vmst, &evt, RET, millis()); uvm32_arg_setval(&vmst, &evt, RET, millis());
} break; } break;
default: default:
Serial.print("Unhandled syscall: "); Serial.print("Unhandled syscall: ");
@ -59,7 +59,7 @@ void loop(void) {
break; break;
case UVM32_EVT_ERR: case UVM32_EVT_ERR:
Serial.print("Error: "); Serial.print("Error: ");
Serial.println(evt.data.err.errstr); Serial.println(evt.data.err.errcode);
isrunning = false; isrunning = false;
break; break;
} }

View file

@ -2,7 +2,7 @@ TOPDIR=../..
all: all:
xxd -n mandel -i ${TOPDIR}/precompiled/mandel.bin | sed -e "s/unsigned char/const unsigned char/" > mandel.h xxd -n mandel -i ${TOPDIR}/precompiled/mandel.bin | sed -e "s/unsigned char/const unsigned char/" > mandel.h
gcc -Wall -DUVM32_MEMORY_SIZE=512 -O2 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-mini ${TOPDIR}/uvm32/uvm32.c host-mini.c gcc -Wall -DUVM32_ERROR_STRINGS -DUVM32_MEMORY_SIZE=512 -O2 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-mini ${TOPDIR}/uvm32/uvm32.c host-mini.c
clean: clean:
rm -f host-mini rm -f host-mini

View file

@ -25,10 +25,10 @@ int main(int argc, char *argv[]) {
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
break; break;
case UVM32_SYSCALL_PUTC: case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0)); printf("%c", uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str); printf("%s\n", str);
} break; } break;
default: default:

View file

@ -1,7 +1,7 @@
TOPDIR = ../.. TOPDIR = ../..
all: all:
xxd -n fib -i ${TOPDIR}/precompiled/fib.bin | sed -e "s/unsigned char/const unsigned char/" > fib.h xxd -n fib -i ${TOPDIR}/precompiled/fib.bin | sed -e "s/unsigned char/const unsigned char/" > fib.h
gcc -Wall -DUVM32_MEMORY_SIZE=16386 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-parallel ${TOPDIR}/uvm32/uvm32.c host-parallel.c gcc -Wall -DUVM32_ERROR_STRINGS -DUVM32_MEMORY_SIZE=16386 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-parallel ${TOPDIR}/uvm32/uvm32.c host-parallel.c
clean: clean:
rm -f host-parallel rm -f host-parallel

View file

@ -43,13 +43,13 @@ int main(int argc, char *argv[]) {
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
break; break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst[scheduler_index], &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst[scheduler_index], &evt, ARG0);
if (str[0] != 0) { if (str[0] != 0) {
printf("[VM %d] %s\n", scheduler_index, str); printf("[VM %d] %s\n", scheduler_index, str);
} }
} break; } break;
case UVM32_SYSCALL_PRINTDEC: case UVM32_SYSCALL_PRINTDEC:
printf("[VM %d] %d\n", scheduler_index, uvm32_getval(&vmst[scheduler_index], &evt, ARG0)); printf("[VM %d] %d\n", scheduler_index, uvm32_arg_getval(&vmst[scheduler_index], &evt, ARG0));
break; break;
default: default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code); printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);

View file

@ -11,7 +11,7 @@ endif
CFLAGS += -Wall -Werror CFLAGS += -Wall -Werror
CFLAGS += -pedantic -std=c99 -O3 CFLAGS += -pedantic -std=c99 -O3
CFLAGS += -DUVM32_MEMORY_SIZE=$(shell echo "1024 * 1024 * 8" | bc) CFLAGS += -DUVM32_ERROR_STRINGS -DUVM32_MEMORY_SIZE=$(shell echo "1024 * 1024 * 8" | bc)
all: all:
gcc ${CFLAGS} -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-sdl ${TOPDIR}/uvm32/uvm32.c host-sdl.c ${LIBS} gcc ${CFLAGS} -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-sdl ${TOPDIR}/uvm32/uvm32.c host-sdl.c ${LIBS}

View file

@ -214,8 +214,8 @@ int main(int argc, char *argv[]) {
uvm32_clearError(vmst); // allow to continue uvm32_clearError(vmst); // allow to continue
} else { } else {
isrunning = false; isrunning = false;
memdump("host-ram.dump", vmst->memory, UVM32_MEMORY_SIZE); memdump("host-ram.dump", uvm32_getMemory(vmst), UVM32_MEMORY_SIZE);
printf("memory dumped to host-ram.dump, pc=0x%08x\n", vmst->core.pc); printf("memory dumped to host-ram.dump, pc=0x%08x\n", uvm32_getProgramCounter(vmst));
if (extram_buf != NULL) { if (extram_buf != NULL) {
memdump("host-extram.dump", (uint8_t *)extram_buf, extram_len); memdump("host-extram.dump", (uint8_t *)extram_buf, extram_len);
printf("extram dumped to host-extram.dump\n"); printf("extram dumped to host-extram.dump\n");
@ -225,41 +225,41 @@ int main(int argc, char *argv[]) {
case UVM32_EVT_SYSCALL: case UVM32_EVT_SYSCALL:
switch(evt.data.syscall.code) { switch(evt.data.syscall.code) {
case UVM32_SYSCALL_PRINTBUF: { case UVM32_SYSCALL_PRINTBUF: {
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(vmst, &evt, ARG0, ARG1);
while(buf.len--) { while(buf.len--) {
printf("%02x", *buf.ptr++); printf("%02x", *buf.ptr++);
} }
} break; } break;
case UVM32_SYSCALL_YIELD: { case UVM32_SYSCALL_YIELD: {
// uint32_t yield_typ = uvm32_getval(vmst, &evt, ARG0); // uint32_t yield_typ = uvm32_arg_getval(vmst, &evt, ARG0);
// printf("YIELD type=%d\n", yield_typ); // printf("YIELD type=%d\n", yield_typ);
// uvm32_setval(vmst, &evt, RET, 123); // uvm32_arg_setval(vmst, &evt, RET, 123);
} break; } break;
case UVM32_SYSCALL_PRINT: { case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(vmst, &evt, ARG0);
printf("%s", str); printf("%s", str);
} break; } break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(vmst, &evt, ARG0);
printf("%s\n", str); printf("%s\n", str);
} break; } break;
case UVM32_SYSCALL_PRINTDEC: case UVM32_SYSCALL_PRINTDEC:
printf("%d", uvm32_getval(vmst, &evt, ARG0)); printf("%d", uvm32_arg_getval(vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PUTC: case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(vmst, &evt, ARG0)); printf("%c", uvm32_arg_getval(vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PRINTHEX: case UVM32_SYSCALL_PRINTHEX:
printf("%08x", uvm32_getval(vmst, &evt, ARG0)); printf("%08x", uvm32_arg_getval(vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_MILLIS: { case UVM32_SYSCALL_MILLIS: {
uvm32_setval(vmst, &evt, RET, SDL_GetTicks()); uvm32_arg_setval(vmst, &evt, RET, SDL_GetTicks());
} break; } break;
case UVM32_SYSCALL_GETC: { case UVM32_SYSCALL_GETC: {
uvm32_setval(vmst, &evt, RET, 0xFFFFFFFF); uvm32_arg_setval(vmst, &evt, RET, 0xFFFFFFFF);
} break; } break;
case UVM32_SYSCALL_RENDER: { case UVM32_SYSCALL_RENDER: {
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(vmst, &evt, ARG0, ARG1);
// copy into texture // copy into texture
void* dst; void* dst;
@ -284,9 +284,9 @@ int main(int argc, char *argv[]) {
case UVM32_SYSCALL_GETKEY: case UVM32_SYSCALL_GETKEY:
if (last_keyvalid) { if (last_keyvalid) {
uint32_t code = (last_keypressed ? 0x80000000 : 0) | last_keyscancode; uint32_t code = (last_keypressed ? 0x80000000 : 0) | last_keyscancode;
uvm32_setval(vmst, &evt, RET, code); uvm32_arg_setval(vmst, &evt, RET, code);
} else { } else {
uvm32_setval(vmst, &evt, RET, 0xFFFFFFFF); uvm32_arg_setval(vmst, &evt, RET, 0xFFFFFFFF);
} }
last_keyvalid = false; last_keyvalid = false;
break; break;

View file

@ -1,7 +1,7 @@
TOPDIR=../.. TOPDIR=../..
all: all:
gcc -Wall -Werror -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=65536 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host ${TOPDIR}/uvm32/uvm32.c host.c gcc -Wall -Werror -pedantic -std=c99 -O2 -DUVM32_ERROR_STRINGS -DUVM32_MEMORY_SIZE=65536 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host ${TOPDIR}/uvm32/uvm32.c host.c
clean: clean:
rm -f host rm -f host

View file

@ -226,8 +226,8 @@ int main(int argc, char *argv[]) {
uvm32_clearError(&vmst); // allow to continue uvm32_clearError(&vmst); // allow to continue
} else { } else {
isrunning = false; isrunning = false;
memdump("host-ram.dump", vmst.memory, UVM32_MEMORY_SIZE); memdump("host-ram.dump", uvm32_getMemory(&vmst), UVM32_MEMORY_SIZE);
printf("memory dumped to host-ram.dump, pc=0x%08x\n", vmst.core.pc); printf("memory dumped to host-ram.dump, pc=0x%08x\n", uvm32_getProgramCounter(&vmst));
if (extram_buf != NULL) { if (extram_buf != NULL) {
memdump("host-extram.dump", (uint8_t *)extram_buf, extram_len); memdump("host-extram.dump", (uint8_t *)extram_buf, extram_len);
printf("extram dumped to host-extram.dump\n"); printf("extram dumped to host-extram.dump\n");
@ -237,43 +237,43 @@ int main(int argc, char *argv[]) {
case UVM32_EVT_SYSCALL: case UVM32_EVT_SYSCALL:
switch(evt.data.syscall.code) { switch(evt.data.syscall.code) {
case UVM32_SYSCALL_PRINTBUF: { case UVM32_SYSCALL_PRINTBUF: {
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
while(buf.len--) { while(buf.len--) {
printf("%02x", *buf.ptr++); printf("%02x", *buf.ptr++);
} }
} break; } break;
case UVM32_SYSCALL_YIELD: { case UVM32_SYSCALL_YIELD: {
// uint32_t yield_typ = uvm32_getval(&vmst, &evt, ARG0); // uint32_t yield_typ = uvm32_arg_getval(&vmst, &evt, ARG0);
// printf("YIELD type=%d\n", yield_typ); // printf("YIELD type=%d\n", yield_typ);
// uvm32_setval(&vmst, &evt, RET, 123); // uvm32_arg_setval(&vmst, &evt, RET, 123);
} break; } break;
case UVM32_SYSCALL_PRINT: { case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
printf("%s", str); printf("%s", str);
} break; } break;
case UVM32_SYSCALL_PRINTLN: { case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str); printf("%s\n", str);
} break; } break;
case UVM32_SYSCALL_PRINTDEC: case UVM32_SYSCALL_PRINTDEC:
printf("%d", uvm32_getval(&vmst, &evt, ARG0)); printf("%d", uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PUTC: case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0)); printf("%c", uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_PRINTHEX: case UVM32_SYSCALL_PRINTHEX:
printf("%08x", uvm32_getval(&vmst, &evt, ARG0)); printf("%08x", uvm32_arg_getval(&vmst, &evt, ARG0));
break; break;
case UVM32_SYSCALL_MILLIS: { case UVM32_SYSCALL_MILLIS: {
clock_t now = clock() / (CLOCKS_PER_SEC / 1000); clock_t now = clock() / (CLOCKS_PER_SEC / 1000);
uvm32_setval(&vmst, &evt, RET, now - start_time); uvm32_arg_setval(&vmst, &evt, RET, now - start_time);
} break; } break;
case UVM32_SYSCALL_GETC: { case UVM32_SYSCALL_GETC: {
uint8_t c; uint8_t c;
if (poll_getch(&c)) { if (poll_getch(&c)) {
uvm32_setval(&vmst, &evt, RET, c); uvm32_arg_setval(&vmst, &evt, RET, c);
} else { } else {
uvm32_setval(&vmst, &evt, RET, 0xFFFFFFFF); uvm32_arg_setval(&vmst, &evt, RET, 0xFFFFFFFF);
} }
} break; } break;
default: default:

Binary file not shown.

View file

@ -22,31 +22,31 @@ void test_syscalls(void) {
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world")); TEST_ASSERT_EQUAL(0, strcmp(uvm32_arg_getcstr(&vmst, &evt, ARG0), "Hello world"));
// check for print syscall // check for print syscall
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world")); TEST_ASSERT_EQUAL(0, strcmp(uvm32_arg_getcstr(&vmst, &evt, ARG0), "Hello world"));
// check for printdec syscall // check for printdec syscall
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
TEST_ASSERT_EQUAL(42, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(42, uvm32_arg_getval(&vmst, &evt, ARG0));
// check for printhex syscall // check for printhex syscall
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTHEX); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTHEX);
TEST_ASSERT_EQUAL(0xDEADBEEF, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(0xDEADBEEF, uvm32_arg_getval(&vmst, &evt, ARG0));
// check for putc syscall // check for putc syscall
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PUTC); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PUTC);
TEST_ASSERT_EQUAL('G', uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL('G', uvm32_arg_getval(&vmst, &evt, ARG0));
// run vm to completion // run vm to completion
uvm32_run(&vmst, &evt, 10000); uvm32_run(&vmst, &evt, 10000);

View file

@ -23,15 +23,15 @@ void test_custom_syscall_normal(void) {
// check for custom syscall // check for custom syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, 0xDEADBEEF); TEST_ASSERT_EQUAL(evt.data.syscall.code, 0xDEADBEEF);
TEST_ASSERT_EQUAL(0xABCD1234, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(0xABCD1234, uvm32_arg_getval(&vmst, &evt, ARG0));
TEST_ASSERT_EQUAL(0xDECAFBAD, uvm32_getval(&vmst, &evt, ARG1)); TEST_ASSERT_EQUAL(0xDECAFBAD, uvm32_arg_getval(&vmst, &evt, ARG1));
uvm32_setval(&vmst, &evt, RET, 0xAABBCCDD); uvm32_arg_setval(&vmst, &evt, RET, 0xAABBCCDD);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
// check for print syscall // check for print syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT);
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(0, strcmp(str, "ok")); TEST_ASSERT_EQUAL(0, strcmp(str, "ok"));
// run vm to completion // run vm to completion
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
@ -44,15 +44,15 @@ void test_custom_syscall_badval(void) {
// check for custom syscall // check for custom syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, 0xDEADBEEF); TEST_ASSERT_EQUAL(evt.data.syscall.code, 0xDEADBEEF);
TEST_ASSERT_EQUAL(0xABCD1234, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(0xABCD1234, uvm32_arg_getval(&vmst, &evt, ARG0));
TEST_ASSERT_EQUAL(0xDECAFBAD, uvm32_getval(&vmst, &evt, ARG1)); TEST_ASSERT_EQUAL(0xDECAFBAD, uvm32_arg_getval(&vmst, &evt, ARG1));
uvm32_setval(&vmst, &evt, RET, 0); // send value that is not being expected uvm32_arg_setval(&vmst, &evt, RET, 0); // send value that is not being expected
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
// check for print syscall // check for print syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT);
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(0, strcmp(str, "fail")); TEST_ASSERT_EQUAL(0, strcmp(str, "fail"));
// run vm to completion // run vm to completion
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);

View file

@ -33,13 +33,13 @@ void test_extram_access(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST1); uvm32_arg_setval(&vmst, &evt, RET, TEST1);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
// check for printdec of val // check for printdec of val
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
TEST_ASSERT_EQUAL(1234, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(1234, uvm32_arg_getval(&vmst, &evt, ARG0));
// run vm to completion // run vm to completion
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
@ -57,7 +57,7 @@ void test_extram_out_of_bounds_rd(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST2); uvm32_arg_setval(&vmst, &evt, RET, TEST2);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
@ -73,7 +73,7 @@ void test_extram_out_of_bounds_wr(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST3); uvm32_arg_setval(&vmst, &evt, RET, TEST3);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
@ -89,7 +89,7 @@ void test_extram_out_of_bounds_dirty_flag(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST4); uvm32_arg_setval(&vmst, &evt, RET, TEST4);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst)); TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst));
@ -105,7 +105,7 @@ void test_extram_byte_write(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST5); uvm32_arg_setval(&vmst, &evt, RET, TEST5);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst)); TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst));
@ -129,7 +129,7 @@ void test_extram_byte_read(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST6); uvm32_arg_setval(&vmst, &evt, RET, TEST6);
// set only byte 7 in extram // set only byte 7 in extram
uint8_t *p = (uint8_t *)extram; uint8_t *p = (uint8_t *)extram;
@ -140,7 +140,7 @@ void test_extram_byte_read(void) {
// check for printdec of val // check for printdec of val
TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ); TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
TEST_ASSERT_EQUAL(0xCD, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(0xCD, uvm32_arg_getval(&vmst, &evt, ARG0));
} }
void test_extram_short_write(void) { void test_extram_short_write(void) {
@ -151,7 +151,7 @@ void test_extram_short_write(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST7); uvm32_arg_setval(&vmst, &evt, RET, TEST7);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst)); TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst));
@ -175,7 +175,7 @@ void test_extram_short_read(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST8); uvm32_arg_setval(&vmst, &evt, RET, TEST8);
// set only short 7 in extram // set only short 7 in extram
uint16_t *p = (uint16_t *)extram; uint16_t *p = (uint16_t *)extram;
@ -186,7 +186,7 @@ void test_extram_short_read(void) {
// check for printdec of val // check for printdec of val
TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ); TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
TEST_ASSERT_EQUAL(0xCDEF, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(0xCDEF, uvm32_arg_getval(&vmst, &evt, ARG0));
} }
void test_extram_buf_unterminated(void) { void test_extram_buf_unterminated(void) {
@ -197,14 +197,14 @@ void test_extram_buf_unterminated(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST9); uvm32_arg_setval(&vmst, &evt, RET, TEST9);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst)); TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst));
// check for printbuf of val // check for printbuf of val
TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ); TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTBUF); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTBUF);
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(5, buf.len); TEST_ASSERT_EQUAL(5, buf.len);
TEST_ASSERT_EQUAL(0, memcmp(buf.ptr, "hello", 5)); TEST_ASSERT_EQUAL(0, memcmp(buf.ptr, "hello", 5));
} }
@ -217,14 +217,14 @@ void test_extram_buf_terminated(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, TEST9); uvm32_arg_setval(&vmst, &evt, RET, TEST9);
uvm32_run(&vmst, &evt, 100); uvm32_run(&vmst, &evt, 100);
TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst)); TEST_ASSERT_EQUAL(true, uvm32_extramDirty(&vmst));
// check for printbuf of val // check for printbuf of val
TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ); TEST_ASSERT_EQUAL(UVM32_EVT_SYSCALL, evt.typ);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTBUF); TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTBUF);
const char *str = uvm32_getcstr(&vmst, &evt, ARG0); const char *str = uvm32_arg_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_NOT_EQUAL(NULL, str); TEST_ASSERT_NOT_EQUAL(NULL, str);
TEST_ASSERT_EQUAL(0, strcmp(str, "hello")); TEST_ASSERT_EQUAL(0, strcmp(str, "hello"));
} }

View file

@ -29,7 +29,7 @@ uint32_t metered_run(uint32_t num_instr) {
switch(evt.typ) { switch(evt.typ) {
case UVM32_EVT_SYSCALL: { case UVM32_EVT_SYSCALL: {
TEST_ASSERT_EQUAL(UVM32_SYSCALL_PRINTDEC, evt.data.syscall.code); TEST_ASSERT_EQUAL(UVM32_SYSCALL_PRINTDEC, evt.data.syscall.code);
uint32_t val = uvm32_getval(&vmst, &evt, ARG0); uint32_t val = uvm32_arg_getval(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(val, expected); TEST_ASSERT_EQUAL(val, expected);
expected++; expected++;
} break; } break;

View file

@ -24,13 +24,13 @@ void test_syscall_args_two_strings(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_A); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_A);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_A); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_A);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), SYSCALL_A_DATA0)); TEST_ASSERT_EQUAL(0, strcmp(uvm32_arg_getcstr(&vmst, &evt, ARG0), SYSCALL_A_DATA0));
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG1), SYSCALL_A_DATA1)); TEST_ASSERT_EQUAL(0, strcmp(uvm32_arg_getcstr(&vmst, &evt, ARG1), SYSCALL_A_DATA1));
// run vm to completion // run vm to completion
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
@ -42,21 +42,21 @@ void test_syscall_args_bad_string(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_B); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_B);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_B); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_B);
// try to read C string from the first null ptr // try to read C string from the first null ptr
const char *s0 = uvm32_getcstr(&vmst, &evt, ARG0); const char *s0 = uvm32_arg_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(0, strlen(s0)); // tried to read a null pointer, get back a zero length string (not a NULL we might read) TEST_ASSERT_EQUAL(0, strlen(s0)); // tried to read a null pointer, get back a zero length string (not a NULL we might read)
// attempt to run vm, should be stuck in err // attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
// try to read C string from the second null ptr // try to read C string from the second null ptr
const char *s1 = uvm32_getcstr(&vmst, &evt, ARG1); const char *s1 = uvm32_arg_getcstr(&vmst, &evt, ARG1);
TEST_ASSERT_EQUAL(0, strlen(s1)); // tried to read a null pointer, get back a zero length string (not a NULL we might read) TEST_ASSERT_EQUAL(0, strlen(s1)); // tried to read a null pointer, get back a zero length string (not a NULL we might read)
// attempt to run vm, should be stuck in err // attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
@ -69,14 +69,14 @@ void test_syscall_args_bufrd(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_C); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_C);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_C); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_C);
// get buffer described by ptr=ARG0,len=ARG1 // get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
uint8_t expected[32]; uint8_t expected[32];
for (int i=0;i<32;i++) { for (int i=0;i<32;i++) {
expected[i] = i; expected[i] = i;
@ -91,14 +91,14 @@ void test_syscall_args_bufrd_bad_addr(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_D); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_D);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_D); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_D);
// get buffer described by ptr=ARG0,len=ARG1 // get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(0, buf.len); // it was invalid, should get a safe zero length buffer TEST_ASSERT_EQUAL(0, buf.len); // it was invalid, should get a safe zero length buffer
// attempt to run vm, should be stuck in err // attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
@ -111,20 +111,20 @@ void test_syscall_args_bufrd_ptrs(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_E); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_E);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_E); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_E);
// get uint32_t buffer described by ptr=ARG0 // get uint32_t buffer described by ptr=ARG0
uvm32_evt_syscall_buf_t buf0 = uvm32_getbuf_fixed(&vmst, &evt, ARG0, 4); uvm32_slice_t buf0 = uvm32_arg_getbuf_fixed(&vmst, &evt, ARG0, 4);
TEST_ASSERT_EQUAL(4, buf0.len); // check read ok TEST_ASSERT_EQUAL(4, buf0.len); // check read ok
uint32_t rspa = 0xABCD1234; uint32_t rspa = 0xABCD1234;
memcpy(buf0.ptr, &rspa, 4); // send it back memcpy(buf0.ptr, &rspa, 4); // send it back
// get uint32_t buffer described by ptr=ARG1 // get uint32_t buffer described by ptr=ARG1
uvm32_evt_syscall_buf_t buf1 = uvm32_getbuf_fixed(&vmst, &evt, ARG1, 4); uvm32_slice_t buf1 = uvm32_arg_getbuf_fixed(&vmst, &evt, ARG1, 4);
TEST_ASSERT_EQUAL(4, buf1.len); // check read ok TEST_ASSERT_EQUAL(4, buf1.len); // check read ok
uint32_t rspb = 0x9876DECF; uint32_t rspb = 0x9876DECF;
memcpy(buf1.ptr, &rspb, 4); // send it back memcpy(buf1.ptr, &rspb, 4); // send it back
@ -133,8 +133,8 @@ void test_syscall_args_bufrd_ptrs(void) {
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_F); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_F);
TEST_ASSERT_EQUAL(rspa, uvm32_getval(&vmst, &evt, ARG0)); TEST_ASSERT_EQUAL(rspa, uvm32_arg_getval(&vmst, &evt, ARG0));
TEST_ASSERT_EQUAL(rspb, uvm32_getval(&vmst, &evt, ARG1)); TEST_ASSERT_EQUAL(rspb, uvm32_arg_getval(&vmst, &evt, ARG1));
} }
void test_syscall_args_buf_pass(void) { void test_syscall_args_buf_pass(void) {
@ -143,14 +143,14 @@ void test_syscall_args_buf_pass(void) {
// check for picktest syscall // check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_G); uvm32_arg_setval(&vmst, &evt, RET, SYSCALL_G);
uvm32_run(&vmst, &evt, 1000); uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_G); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_G);
// get buffer described by ptr=ARG0,len=ARG1 // get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf0 = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf0 = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(32, buf0.len); TEST_ASSERT_EQUAL(32, buf0.len);
// fill with a pattern // fill with a pattern
for (int i=0;i<32;i++) { for (int i=0;i<32;i++) {
@ -162,7 +162,7 @@ void test_syscall_args_buf_pass(void) {
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_H); TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_H);
// get buffer described by ptr=ARG0,len=ARG1 // get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf1 = uvm32_getbuf(&vmst, &evt, ARG0, ARG1); uvm32_slice_t buf1 = uvm32_arg_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(32, buf1.len); TEST_ASSERT_EQUAL(32, buf1.len);
for (int i=0;i<32;i++) { for (int i=0;i<32;i++) {
TEST_ASSERT_EQUAL(i*2, buf1.ptr[i]); TEST_ASSERT_EQUAL(i*2, buf1.ptr[i]);

View file

@ -42,52 +42,56 @@ void uvm32_memset(void *buf, int c, int len) {
#include "mini-rv32ima.h" #include "mini-rv32ima.h"
#ifdef UVM32_ERROR_STRINGS
#define X(name) #name, #define X(name) #name,
static const char *errNames[] = { static const char *errNames[] = {
LIST_OF_UVM32_ERRS LIST_OF_UVM32_ERRS
}; };
#undef X #undef X
#endif
static void setup_err_evt(uvm32_state_t *vmst, uvm32_evt_t *evt) { static void setup_err_evt(uvm32_state_t *vmst, uvm32_evt_t *evt) {
evt->typ = UVM32_EVT_ERR; evt->typ = UVM32_EVT_ERR;
evt->data.err.errcode = vmst->err; evt->data.err.errcode = vmst->_err;
evt->data.err.errstr = errNames[vmst->err]; #ifdef UVM32_ERROR_STRINGS
evt->data.err.errstr = errNames[vmst->_err];
#endif
} }
static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) { static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) {
if (vmst->status == UVM32_STATUS_ERROR) { if (vmst->_status == UVM32_STATUS_ERROR) {
// always stay in error state until a uvm32_init() // always stay in error state until a uvm32_init()
return; return;
} else { } else {
vmst->status = newStatus; vmst->_status = newStatus;
} }
} }
static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) { static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) {
// if already in error state, stay in the first error state // if already in error state, stay in the first error state
if (vmst->status != UVM32_STATUS_ERROR) { if (vmst->_status != UVM32_STATUS_ERROR) {
setStatus(vmst, UVM32_STATUS_ERROR); setStatus(vmst, UVM32_STATUS_ERROR);
vmst->err = err; vmst->_err = err;
} }
} }
void uvm32_init(uvm32_state_t *vmst) { void uvm32_init(uvm32_state_t *vmst) {
UVM32_MEMSET(vmst, 0x00, sizeof(uvm32_state_t)); UVM32_MEMSET(vmst, 0x00, sizeof(uvm32_state_t));
vmst->status = UVM32_STATUS_PAUSED; vmst->_status = UVM32_STATUS_PAUSED;
vmst->extramLen = 0; vmst->_extramLen = 0;
vmst->extram = (uint32_t *)NULL; vmst->_extram = (uint32_t *)NULL;
vmst->extramDirty = false; vmst->_extramDirty = false;
vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET; vmst->_core.pc = MINIRV32_RAM_IMAGE_OFFSET;
// https://projectf.io/posts/riscv-cheat-sheet/ // https://projectf.io/posts/riscv-cheat-sheet/
// setup stack pointer // setup stack pointer
// la sp, _sstack // la sp, _sstack
// addi sp,sp,-16 // addi sp,sp,-16
vmst->core.regs[2] = ((MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) & ~0xF) - 16; // 16 byte align stack vmst->_core.regs[2] = ((MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) & ~0xF) - 16; // 16 byte align stack
vmst->core.regs[10] = 0x00; //hart ID vmst->_core.regs[10] = 0x00; //hart ID
vmst->core.regs[11] = 0; vmst->_core.regs[11] = 0;
vmst->core.extraflags |= 3; // Machine-mode. vmst->_core.extraflags |= 3; // Machine-mode.
} }
bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) { bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) {
@ -96,37 +100,37 @@ bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) {
return false; return false;
} }
UVM32_MEMCPY(vmst->memory, rom, len); UVM32_MEMCPY(vmst->_memory, rom, len);
vmst->stack_canary = (uint8_t *)NULL; vmst->_stack_canary = (uint8_t *)NULL;
return true; return true;
} }
// Read C-string up to terminator and return len,ptr // Read C-string up to terminator and return len,ptr
bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_syscall_buf_t *buf) { bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_slice_t *buf) {
if (MINIRV32_MMIO_RANGE(addr)) { if (MINIRV32_MMIO_RANGE(addr)) {
if (vmst->extram == NULL) { if (vmst->_extram == NULL) {
return false; return false;
} else { } else {
uint32_t ptrstart = addr - UVM32_EXTRAM_BASE;; uint32_t ptrstart = addr - UVM32_EXTRAM_BASE;;
uint32_t p = ptrstart; uint32_t p = ptrstart;
if (p >= vmst->extramLen) { if (p >= vmst->_extramLen) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = (uint8_t *)NULL; buf->ptr = (uint8_t *)NULL;
buf->len = 0; buf->len = 0;
return false; return false;
} }
while(vmst->memory[p] != '\0') { while(vmst->_memory[p] != '\0') {
p++; p++;
if (p >= vmst->extramLen) { if (p >= vmst->_extramLen) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = (uint8_t *)NULL; buf->ptr = (uint8_t *)NULL;
buf->len = 0; buf->len = 0;
return false; return false;
} }
} }
buf->ptr = (uint8_t *)&vmst->extram[ptrstart/4]; // extram is uint32_t* buf->ptr = (uint8_t *)&vmst->_extram[ptrstart/4]; // extram is uint32_t*
buf->len = p - ptrstart; buf->len = p - ptrstart;
return true; return true;
} }
@ -139,7 +143,7 @@ bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_s
buf->len = 0; buf->len = 0;
return false; return false;
} }
while(vmst->memory[p] != '\0') { while(vmst->_memory[p] != '\0') {
p++; p++;
if (p >= UVM32_MEMORY_SIZE) { if (p >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
@ -148,25 +152,25 @@ bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_s
return false; return false;
} }
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->_memory[ptrstart];
buf->len = p - ptrstart; buf->len = p - ptrstart;
return true; return true;
} }
} }
bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_syscall_buf_t *buf) { bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_slice_t *buf) {
if (MINIRV32_MMIO_RANGE(addr)) { if (MINIRV32_MMIO_RANGE(addr)) {
if (vmst->extram == NULL) { if (vmst->_extram == NULL) {
return false; return false;
} else { } else {
uint32_t ptrstart = addr - UVM32_EXTRAM_BASE; uint32_t ptrstart = addr - UVM32_EXTRAM_BASE;
if ((ptrstart > vmst->extramLen) || (ptrstart + len > vmst->extramLen)) { if ((ptrstart > vmst->_extramLen) || (ptrstart + len > vmst->_extramLen)) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = (uint8_t *)NULL; buf->ptr = (uint8_t *)NULL;
buf->len = 0; buf->len = 0;
return false; return false;
} }
buf->ptr = (uint8_t *)&vmst->extram[ptrstart/4]; // extram is uint32_t* buf->ptr = (uint8_t *)&vmst->_extram[ptrstart/4]; // extram is uint32_t*
buf->len = len; buf->len = len;
return true; return true;
} }
@ -178,17 +182,17 @@ bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_sys
buf->len = 0; buf->len = 0;
return false; return false;
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->_memory[ptrstart];
buf->len = len; buf->len = len;
return true; return true;
} }
} }
void uvm32_clearError(uvm32_state_t *vmst) { void uvm32_clearError(uvm32_state_t *vmst) {
if (vmst->status == UVM32_STATUS_ERROR) { if (vmst->_status == UVM32_STATUS_ERROR) {
// vm is in an error state, but user wants to continue // vm is in an error state, but user wants to continue
// most likely after UVM32_ERR_HUNG // most likely after UVM32_ERR_HUNG
vmst->status = UVM32_STATUS_PAUSED; vmst->_status = UVM32_STATUS_PAUSED;
} }
} }
@ -196,20 +200,20 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
const uint32_t min_instrs = 1; const uint32_t min_instrs = 1;
uint32_t orig_instr_meter = instr_meter; uint32_t orig_instr_meter = instr_meter;
vmst->extramDirty = false; vmst->_extramDirty = false;
if (instr_meter < min_instrs) { if (instr_meter < min_instrs) {
instr_meter = min_instrs; instr_meter = min_instrs;
orig_instr_meter = min_instrs; orig_instr_meter = min_instrs;
} }
if (vmst->stack_canary != NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { if (vmst->_stack_canary != NULL && *vmst->_stack_canary != STACK_CANARY_VALUE) {
setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
return orig_instr_meter - instr_meter; return orig_instr_meter - instr_meter;
} }
if (vmst->status != UVM32_STATUS_PAUSED) { if (vmst->_status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY); setStatusErr(vmst, UVM32_ERR_NOTREADY);
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
return orig_instr_meter - instr_meter; return orig_instr_meter - instr_meter;
@ -218,10 +222,10 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
setStatus(vmst, UVM32_STATUS_RUNNING); setStatus(vmst, UVM32_STATUS_RUNNING);
// run CPU until no longer in running state // run CPU until no longer in running state
while(vmst->status == UVM32_STATUS_RUNNING && instr_meter > 0) { while(vmst->_status == UVM32_STATUS_RUNNING && instr_meter > 0) {
uint64_t elapsedUs = 1; uint64_t elapsedUs = 1;
uint32_t ret; uint32_t ret;
ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); ret = MiniRV32IMAStep(vmst, &vmst->_core, vmst->_memory, 0, elapsedUs, 1);
instr_meter--; instr_meter--;
switch(ret) { switch(ret) {
@ -229,10 +233,10 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
break; break;
case 12: { // ecall case 12: { // ecall
// Fetch registers used by syscall // Fetch registers used by syscall
const uint32_t syscall = vmst->core.regs[17]; // a7 const uint32_t syscall = vmst->_core.regs[17]; // a7
// on exception we should jump to mtvec, but we handle directly // on exception we should jump to mtvec, but we handle directly
// and skip over the ecall instruction // and skip over the ecall instruction
vmst->core.pc += 4; vmst->_core.pc += 4;
switch(syscall) { switch(syscall) {
// inbuilt syscalls // inbuilt syscalls
case UVM32_SYSCALL_HALT: case UVM32_SYSCALL_HALT:
@ -240,8 +244,8 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
break; break;
case UVM32_SYSCALL_STACKPROTECT: { case UVM32_SYSCALL_STACKPROTECT: {
// don't allow errant code to change it once set // don't allow errant code to change it once set
if (vmst->stack_canary == (uint8_t *)NULL) { if (vmst->_stack_canary == (uint8_t *)NULL) {
uint32_t param0 = vmst->core.regs[10]; // a0 uint32_t param0 = vmst->_core.regs[10]; // a0
uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET;
mem_offset &= ~0xF; // round up by 16 bytes mem_offset &= ~0xF; // round up by 16 bytes
mem_offset += 16*4; mem_offset += 16*4;
@ -255,18 +259,18 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
// check canary is inside valid memory // check canary is inside valid memory
if (mem_offset < UVM32_MEMORY_SIZE) { if (mem_offset < UVM32_MEMORY_SIZE) {
// set canary // set canary
vmst->stack_canary = &vmst->memory[mem_offset]; vmst->_stack_canary = &vmst->_memory[mem_offset];
*vmst->stack_canary = STACK_CANARY_VALUE; *vmst->_stack_canary = STACK_CANARY_VALUE;
} }
} }
} break; } break;
default: default:
// user defined syscalls // user defined syscalls
vmst->ioevt.typ = UVM32_EVT_SYSCALL; vmst->_ioevt.typ = UVM32_EVT_SYSCALL;
vmst->ioevt.data.syscall.code = syscall; vmst->_ioevt.data.syscall.code = syscall;
vmst->ioevt.data.syscall.ret = &vmst->core.regs[12]; // a2 vmst->_ioevt.data.syscall._ret = &vmst->_core.regs[12]; // a2
vmst->ioevt.data.syscall.params[0] = &vmst->core.regs[10]; // a0 vmst->_ioevt.data.syscall._params[0] = &vmst->_core.regs[10]; // a0
vmst->ioevt.data.syscall.params[1] = &vmst->core.regs[11]; // a1 vmst->_ioevt.data.syscall._params[1] = &vmst->_core.regs[11]; // a1
setStatus(vmst, UVM32_STATUS_PAUSED); setStatus(vmst, UVM32_STATUS_PAUSED);
break; break;
} // end switch(syscall) } // end switch(syscall)
@ -282,7 +286,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
break; break;
} }
if (vmst->status == UVM32_STATUS_RUNNING && instr_meter == 0) { if (vmst->_status == UVM32_STATUS_RUNNING && instr_meter == 0) {
// no syscall occurred, so we've hung // no syscall occurred, so we've hung
setStatusErr(vmst, UVM32_ERR_HUNG); setStatusErr(vmst, UVM32_ERR_HUNG);
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
@ -291,18 +295,18 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
} }
if (vmst->status == UVM32_STATUS_ENDED) { if (vmst->_status == UVM32_STATUS_ENDED) {
evt->typ = UVM32_EVT_END; evt->typ = UVM32_EVT_END;
return orig_instr_meter - instr_meter; return orig_instr_meter - instr_meter;
} }
// an event is ready // an event is ready
if (vmst->status == UVM32_STATUS_PAUSED) { if (vmst->_status == UVM32_STATUS_PAUSED) {
// send back the built up event // send back the built up event
UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); UVM32_MEMCPY(evt, &vmst->_ioevt, sizeof(uvm32_evt_t));
return orig_instr_meter - instr_meter; return orig_instr_meter - instr_meter;
} else { } else {
if (vmst->status == UVM32_STATUS_ERROR) { if (vmst->_status == UVM32_STATUS_ERROR) {
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
} else { } else {
setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE); setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE);
@ -313,19 +317,19 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
} }
bool uvm32_hasEnded(const uvm32_state_t *vmst) { bool uvm32_hasEnded(const uvm32_state_t *vmst) {
return vmst->status == UVM32_STATUS_ENDED; return vmst->_status == UVM32_STATUS_ENDED;
} }
static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) {
switch(arg) { switch(arg) {
case ARG0: case ARG0:
return evt->data.syscall.params[0]; return evt->data.syscall._params[0];
break; break;
case ARG1: case ARG1:
return evt->data.syscall.params[1]; return evt->data.syscall._params[1];
break; break;
case RET: case RET:
return evt->data.syscall.ret; return evt->data.syscall._ret;
break; break;
default: default:
// if something invalid is passed to arg, we should never crash // if something invalid is passed to arg, we should never crash
@ -337,17 +341,17 @@ static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t a
} }
} }
void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t val) { void uvm32_arg_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t val) {
*arg_to_ptr(vmst, evt, arg) = val; *arg_to_ptr(vmst, evt, arg) = val;
} }
uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { uint32_t uvm32_arg_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) {
return *arg_to_ptr(vmst, evt, arg); return *arg_to_ptr(vmst, evt, arg);
} }
const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { const char *uvm32_arg_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) {
uvm32_evt_syscall_buf_t scb; uvm32_slice_t scb;
if (get_safeptr_null_terminated(vmst, uvm32_getval(vmst, evt, arg), &scb)) { if (get_safeptr_null_terminated(vmst, uvm32_arg_getval(vmst, evt, arg), &scb)) {
return (const char *)scb.ptr; // we know the buffer in cpu memory is null terminated, so safe to pass back return (const char *)scb.ptr; // we know the buffer in cpu memory is null terminated, so safe to pass back
} else { } else {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
@ -356,9 +360,9 @@ const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg
} }
} }
uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen) { uvm32_slice_t uvm32_arg_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen) {
uvm32_evt_syscall_buf_t scb; uvm32_slice_t scb;
if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), uvm32_getval(vmst, evt, argLen), &scb)) { if (!get_safeptr(vmst, uvm32_arg_getval(vmst, evt, argPtr), uvm32_arg_getval(vmst, evt, argLen), &scb)) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
garbage = 0; garbage = 0;
scb.ptr = (uint8_t *)&garbage; scb.ptr = (uint8_t *)&garbage;
@ -367,9 +371,9 @@ uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm3
return scb; return scb;
} }
uvm32_evt_syscall_buf_t uvm32_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uint32_t len) { uvm32_slice_t uvm32_arg_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uint32_t len) {
uvm32_evt_syscall_buf_t scb; uvm32_slice_t scb;
if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), len, &scb)) { if (!get_safeptr(vmst, uvm32_arg_getval(vmst, evt, argPtr), len, &scb)) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
garbage = 0; garbage = 0;
scb.ptr = (uint8_t *)&garbage; scb.ptr = (uint8_t *)&garbage;
@ -378,28 +382,28 @@ uvm32_evt_syscall_buf_t uvm32_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt
return scb; return scb;
} }
uint32_t uvm32_extramLoad(void *userdata, uint32_t addr, uint32_t accessTyp) { uint32_t _uvm32_extramLoad(void *userdata, uint32_t addr, uint32_t accessTyp) {
uvm32_state_t *vmst = (uvm32_state_t *)userdata; uvm32_state_t *vmst = (uvm32_state_t *)userdata;
addr -= UVM32_EXTRAM_BASE; addr -= UVM32_EXTRAM_BASE;
if (vmst->extram != NULL) { if (vmst->_extram != NULL) {
if (addr < vmst->extramLen) { if (addr < vmst->_extramLen) {
switch(accessTyp) { switch(accessTyp) {
case 0: case 0:
return ((int8_t *)vmst->extram)[addr]; return ((int8_t *)vmst->_extram)[addr];
break; break;
case 1: case 1:
return ((int16_t *)vmst->extram)[addr/2]; return ((int16_t *)vmst->_extram)[addr/2];
break; break;
case 2: case 2:
return ((uint32_t *)vmst->extram)[addr / 4]; return ((uint32_t *)vmst->_extram)[addr / 4];
break; break;
case 4: case 4:
return ((uint8_t *)vmst->extram)[addr]; return ((uint8_t *)vmst->_extram)[addr];
break; break;
case 5: case 5:
return ((uint16_t *)vmst->extram)[addr/2]; return ((uint16_t *)vmst->_extram)[addr/2];
break; break;
default: default:
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
@ -413,26 +417,26 @@ uint32_t uvm32_extramLoad(void *userdata, uint32_t addr, uint32_t accessTyp) {
return 0; return 0;
} }
uint32_t uvm32_extramStore(void *userdata, uint32_t addr, uint32_t val, uint32_t accessTyp) { uint32_t _uvm32_extramStore(void *userdata, uint32_t addr, uint32_t val, uint32_t accessTyp) {
uvm32_state_t *vmst = (uvm32_state_t *)userdata; uvm32_state_t *vmst = (uvm32_state_t *)userdata;
addr -= UVM32_EXTRAM_BASE; addr -= UVM32_EXTRAM_BASE;
if (vmst->extram != NULL) { if (vmst->_extram != NULL) {
if (addr < vmst->extramLen) { if (addr < vmst->_extramLen) {
switch(accessTyp) { switch(accessTyp) {
case 0: case 0:
((uint8_t *)vmst->extram)[addr] = val; ((uint8_t *)vmst->_extram)[addr] = val;
break; break;
case 1: case 1:
((uint16_t *)vmst->extram)[addr/2] = val; ((uint16_t *)vmst->_extram)[addr/2] = val;
break; break;
case 2: case 2:
((uint32_t *)vmst->extram)[addr/4] = val; ((uint32_t *)vmst->_extram)[addr/4] = val;
break; break;
default: default:
setStatusErr(vmst, UVM32_ERR_MEM_WR); setStatusErr(vmst, UVM32_ERR_MEM_WR);
break; break;
} }
vmst->extramDirty = true; vmst->_extramDirty = true;
} else { } else {
setStatusErr(vmst, UVM32_ERR_MEM_WR); setStatusErr(vmst, UVM32_ERR_MEM_WR);
} }
@ -441,12 +445,19 @@ uint32_t uvm32_extramStore(void *userdata, uint32_t addr, uint32_t val, uint32_t
} }
void uvm32_extram(uvm32_state_t *vmst, uint32_t *ram, uint32_t len) { void uvm32_extram(uvm32_state_t *vmst, uint32_t *ram, uint32_t len) {
vmst->extram = ram; vmst->_extram = ram;
vmst->extramLen = len; vmst->_extramLen = len;
} }
bool uvm32_extramDirty(uvm32_state_t *vmst) { bool uvm32_extramDirty(uvm32_state_t *vmst) {
return vmst->extramDirty; return vmst->_extramDirty;
} }
const uint8_t *uvm32_getMemory(const uvm32_state_t *vmst) {
return vmst->_memory;
}
uint32_t uvm32_getProgramCounter(const uvm32_state_t *vmst) {
return vmst->_core.pc;
}

View file

@ -1,6 +1,33 @@
#ifndef UVM32_H #ifndef UVM32_H
#define UVM32_H 1 #define UVM32_H 1
/*!
https://github.com/ringtailsoftware/uvm32
MIT License
Copyright (c) 2025 Toby Jaffey <toby@ringtailsoftware.co.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*! To use uvm32 in an environment without standard library headers, define CUSTOM_STDLIB_H to the name of header providing the equivalent types to stdint.h and stdbool.h */
#ifndef CUSTOM_STDLIB_H #ifndef CUSTOM_STDLIB_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -8,8 +35,23 @@
#include CUSTOM_STDLIB_H #include CUSTOM_STDLIB_H
#endif #endif
// Include definitions for required syscalls
#include "uvm32_sys.h" #include "uvm32_sys.h"
// Setup and hooks for mini-rv32ima emulator core
#define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE
#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return retval;}
uint32_t _uvm32_extramLoad(void *userdata, uint32_t addr, uint32_t accessTyp);
uint32_t _uvm32_extramStore(void *userdata, uint32_t addr, uint32_t val, uint32_t accessTyp);
#define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = _uvm32_extramLoad(userdata, addy, ( ir >> 12 ) & 0x7);
#define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( _uvm32_extramStore(userdata, addy, val, ( ir >> 12 ) & 0x7) ) return val;
#ifndef MINIRV32_IMPLEMENTATION
#define MINIRV32_STEPPROTO
#endif
#include "mini-rv32ima.h"
// Define all errors returned in a uvm32_evt_err_t
#define LIST_OF_UVM32_ERRS \ #define LIST_OF_UVM32_ERRS \
X(UVM32_ERR_NONE) \ X(UVM32_ERR_NONE) \
X(UVM32_ERR_NOTREADY) \ X(UVM32_ERR_NOTREADY) \
@ -27,44 +69,40 @@ typedef enum {
} uvm32_err_t; } uvm32_err_t;
#undef X #undef X
/*! Event types, giving the reason that the VM has stopped running */
typedef enum { typedef enum {
UVM32_EVT_ERR, UVM32_EVT_ERR, /*! Error has occurred, details in uvm32_evt_t data.err field */
UVM32_EVT_SYSCALL, UVM32_EVT_SYSCALL, /*! A syscall has been requested, details in uvm32__evt_t data.syscall field */
UVM32_EVT_END, UVM32_EVT_END, /*! The program has ended by making a UVM32_SYSCALL_HALT */
} uvm32_evt_typ_t; } uvm32_evt_typ_t;
/*! Details for an error event */
typedef struct { typedef struct {
uvm32_err_t errcode; uvm32_err_t errcode; /*! Error code */
const char *errstr; #ifdef UVM32_ERROR_STRINGS
const char *errstr; /*! Error as a string */
#endif
} uvm32_evt_err_t; } uvm32_evt_err_t;
/*! Describes a syscall made from running code to the VM host
Typically, a host will switch on `code` to decide how to parse the syscall. Then must use the functions `uvm32_arg_getval()` `uvm32_arg_getcstr()` and so on to safely access the data
*/
typedef struct { typedef struct {
uint32_t code; // syscall number uint32_t code; /*! Syscall number, eg. UVM32_SYSCALL_YIELD */
uint32_t *ret; uint32_t *_ret; /*! Value to be returned to caller, private, do not use directly */
uint32_t *params[2]; uint32_t *_params[2]; /*! The syscall's two parameters, private, do not use directly */
} uvm32_evt_syscall_t; } uvm32_evt_syscall_t;
/*! An event passed from uvm32 to host when code must be paused */
typedef struct { typedef struct {
uvm32_evt_typ_t typ; uvm32_evt_typ_t typ; /*! The type of this event */
union { union {
uvm32_evt_syscall_t syscall; uvm32_evt_syscall_t syscall; /*! Only valid when typ == UVM32_EVT_SYSCALL */
uvm32_evt_err_t err; uvm32_evt_err_t err; /*! Only valid when typ == UVM32_EVT_ERR */
} data; } data;
} uvm32_evt_t; } uvm32_evt_t;
#define MINIRV32_DECORATE static /*! Internal state of uvm32 */
#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE
#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return retval;}
uint32_t uvm32_extramLoad(void *userdata, uint32_t addr, uint32_t accessTyp);
uint32_t uvm32_extramStore(void *userdata, uint32_t addr, uint32_t val, uint32_t accessTyp);
#define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = uvm32_extramLoad(userdata, addy, ( ir >> 12 ) & 0x7);
#define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( uvm32_extramStore(userdata, addy, val, ( ir >> 12 ) & 0x7) ) return val;
#ifndef MINIRV32_IMPLEMENTATION
#define MINIRV32_STEPPROTO
#endif
#include "mini-rv32ima.h"
typedef enum { typedef enum {
UVM32_STATUS_PAUSED, UVM32_STATUS_PAUSED,
UVM32_STATUS_RUNNING, UVM32_STATUS_RUNNING,
@ -72,46 +110,73 @@ typedef enum {
UVM32_STATUS_ENDED, UVM32_STATUS_ENDED,
} uvm32_status_t; } uvm32_status_t;
/*! State of uvm32. Each VM requires an instance of uvm32_state_t. All members of the struct are private and should only be accessed through provided functions */
typedef struct { typedef struct {
uvm32_status_t status; uvm32_status_t _status; /*! Current VM running state */
uvm32_err_t err; uvm32_err_t _err; /*! Current error code */
struct MiniRV32IMAState core; struct MiniRV32IMAState _core; /*! CPU registers */
uint8_t memory[UVM32_MEMORY_SIZE]; uint8_t _memory[UVM32_MEMORY_SIZE]; /*! Memory */
uvm32_evt_t ioevt; // for building up in callbacks uvm32_evt_t _ioevt; /*! Event to be returned on next pause */
uint8_t *stack_canary; uint8_t *_stack_canary; /*! Location of stack canary */
uint32_t extramLen; uint32_t *_extram; /*! External RAM pointer, or NULL */
uint32_t *extram; // all accesses are 32bit uint32_t _extramLen; /*! Length of external RAM */
bool extramDirty; bool _extramDirty; /*! Flag to indicate VM code has modified extram since last run */
} uvm32_state_t; } uvm32_state_t;
/*! Initialise a VM instance */
void uvm32_init(uvm32_state_t *vmst); void uvm32_init(uvm32_state_t *vmst);
/*! Load compiled code into the instance for execution. `rom` is copied into the memory space of the VM so the caller may safely free it after calling */
bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len); bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len);
bool uvm32_hasEnded(const uvm32_state_t *vmst);
/*! Run the VM for a maximum on `instr_meter` instructions. The VM will pause and return to the caller after executing `instr_meter` or less instructions and give the reason for stopping in `evt`. The number of instructions executed is returned. */
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter);
// convenience for getptr /*! Check if the VM has ended execution. On ending, the VM will send `UVM32_EVT_END`, but this function may be used to repeatedly check */
bool uvm32_hasEnded(const uvm32_state_t *vmst);
/* After VM has paused due to UVM32_STATUS_ERROR, calling `uvm32_clearError()` will allow it to continue normally */
void uvm32_clearError(uvm32_state_t *vmst);
/*! Describes a slice of memory, with a pointer and known length */
typedef struct { typedef struct {
uint8_t *ptr; uint8_t *ptr;
uint32_t len; uint32_t len;
} uvm32_evt_syscall_buf_t; } uvm32_slice_t;
/*! Used for accessing the parts of a syscall */
typedef enum { typedef enum {
ARG0, ARG0, /*! The first argument of a syscall */
ARG1, ARG1, /*! The second argument of a syscall */
RET RET, /*! The return value of a syscall */
} uvm32_arg_t; } uvm32_arg_t;
// syscall parameter handling /*! Read a syscall argument as a uint32_t */
uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t); uint32_t uvm32_arg_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg);
const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t);
void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t, uint32_t val);
uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen);
uvm32_evt_syscall_buf_t uvm32_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uint32_t len);
void uvm32_clearError(uvm32_state_t *vmst);
// external RAM /*! Read a syscall argument as a C string */
const char *uvm32_arg_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg);
/*! Write a syscall argument as a uint32_t value */
void uvm32_arg_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t, uint32_t val);
/*! Read a syscall argument pair (ptr, length) as a slice */
uvm32_slice_t uvm32_arg_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen);
/*! Read a syscall argument pointer as a slice of known length */
uvm32_slice_t uvm32_arg_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t len);
/*! Setup a block of memory to act as external RAM, it will be available on in VM code at address `UVM32_EXTRAM_BASE`. The memory is not copied, so the caller must ensure it remains available until `uvm32_extram()` is called to setup a different region or the VM is ended */
void uvm32_extram(uvm32_state_t *vmst, uint32_t *ram, uint32_t len); void uvm32_extram(uvm32_state_t *vmst, uint32_t *ram, uint32_t len);
bool uvm32_extramDirty(uvm32_state_t *vmst); // cleared by uvm32_run()
/*! Check to see if the external RAM is marked as dirty. If the VM code writes to the external RAM, this flag is set. The flag is automatically cleared next time uvm32_run() is called */
bool uvm32_extramDirty(uvm32_state_t *vmst);
/*! Get const pointer to raw memory, for debugging */
const uint8_t *uvm32_getMemory(const uvm32_state_t *vmst);
/*! Get program counter for, for debugging */
uint32_t uvm32_getProgramCounter(const uvm32_state_t *vmst);
#endif #endif