diff --git a/host/host.c b/host/host.c index 27860bc..9519cc6 100644 --- a/host/host.c +++ b/host/host.c @@ -171,6 +171,7 @@ int main(int argc, char *argv[]) { printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode); if (evt.data.err.errcode == UVM32_ERR_HUNG) { printf("VM may have hung, increase max_instrs_per_run\n"); + uvm32_clearError(&vmst); // allow to continue } else { isrunning = false; } diff --git a/test/Makefile b/test/Makefile index 1bf09ef..a2befe3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,4 +1,10 @@ -TESTS = example_1 stackoverflow custom_syscall +TESTS = \ + basic_syscalls \ + stackoverflow \ + custom_syscall \ + syscall_args \ + meter + RUNCMD = $(foreach TEST,${TESTS},make -C ${TEST} &&) CLEANCMD = $(foreach TEST,${TESTS},make -C ${TEST} clean &&) diff --git a/test/example_1/Makefile b/test/basic_syscalls/Makefile similarity index 100% rename from test/example_1/Makefile rename to test/basic_syscalls/Makefile diff --git a/test/example_1/rom/Makefile b/test/basic_syscalls/rom/Makefile similarity index 100% rename from test/example_1/rom/Makefile rename to test/basic_syscalls/rom/Makefile diff --git a/test/basic_syscalls/rom/rom.c b/test/basic_syscalls/rom/rom.c new file mode 100644 index 0000000..ea40b79 --- /dev/null +++ b/test/basic_syscalls/rom/rom.c @@ -0,0 +1,10 @@ +#include "uvm32_target.h" + +void main(void) { + println("Hello world"); + print("Hello world"); + printdec(42); + printhex(0xDEADBEEF); + putc('G'); +} + diff --git a/test/basic_syscalls/test/tests.c b/test/basic_syscalls/test/tests.c new file mode 100644 index 0000000..c5d2f5a --- /dev/null +++ b/test/basic_syscalls/test/tests.c @@ -0,0 +1,56 @@ +#include +#include "unity.h" +#include "uvm32.h" +#include "../common/uvm32_common_custom.h" + +#include "rom-header.h" + +static uvm32_state_t vmst; +static uvm32_evt_t evt; + +void setUp(void) { + // runs before each test + uvm32_init(&vmst); + uvm32_load(&vmst, rom_bin, rom_bin_len); +} + +void tearDown(void) { +} + +void test_syscalls(void) { + // check for println syscall + uvm32_run(&vmst, &evt, 1000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); + TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN); + TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world")); + + // check for print syscall + uvm32_run(&vmst, &evt, 1000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); + TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT); + TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world")); + + // check for printdec syscall + uvm32_run(&vmst, &evt, 1000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); + TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC); + TEST_ASSERT_EQUAL(42, uvm32_getval(&vmst, &evt, ARG0)); + + // check for printhex syscall + uvm32_run(&vmst, &evt, 1000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); + TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTHEX); + TEST_ASSERT_EQUAL(0xDEADBEEF, uvm32_getval(&vmst, &evt, ARG0)); + + // check for putc syscall + uvm32_run(&vmst, &evt, 1000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); + TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PUTC); + TEST_ASSERT_EQUAL('G', uvm32_getval(&vmst, &evt, ARG0)); + + // run vm to completion + uvm32_run(&vmst, &evt, 10000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END); +} + + diff --git a/test/example_1/rom/rom.c b/test/example_1/rom/rom.c deleted file mode 100644 index 072ab1d..0000000 --- a/test/example_1/rom/rom.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "uvm32_target.h" - -void main(void) { - println("Hello world"); -} - diff --git a/test/example_1/test/tests.c b/test/example_1/test/tests.c deleted file mode 100644 index 37585ce..0000000 --- a/test/example_1/test/tests.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include "unity.h" -#include "uvm32.h" -#include "../common/uvm32_common_custom.h" - -#include "rom-header.h" - -static uvm32_state_t vmst; -static uvm32_evt_t evt; - -void setUp(void) { - // runs before each test - uvm32_init(&vmst); - uvm32_load(&vmst, rom_bin, rom_bin_len); -} - -void tearDown(void) { -} - -void test_helloworld1(void) { - // run the vm - uvm32_run(&vmst, &evt, 10000); - // check for println syscall - TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL); - TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN); - const char *str = uvm32_getcstr(&vmst, &evt, ARG0); - TEST_ASSERT_EQUAL(0, strcmp(str, "Hello world")); - // run vm to completion - uvm32_run(&vmst, &evt, 10000); - TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END); -} - - diff --git a/test/meter/Makefile b/test/meter/Makefile new file mode 100644 index 0000000..6533b02 --- /dev/null +++ b/test/meter/Makefile @@ -0,0 +1,33 @@ +C_COMPILER=gcc + +UNITY_ROOT=../unity + +CFLAGS=-std=c99 +CFLAGS += -Wall +CFLAGS += -Werror +CFLAGS += -DUVM32_MEMORY_SIZE=16384 + +SUITE_NAME=tests + +TARGET_BASE1=test1 +TARGET1 = $(TARGET_BASE1) +SRC_FILES1=$(UNITY_ROOT)/src/unity.c test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c ../../uvm32/uvm32.c +INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom + +.PHONY: rom + +default: $(SRC_FILES1) rom + @$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1) + @ ./$(TARGET1) + +rom: + @(cd rom && make) + +test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c + @mkdir -p test/test_runners + @ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c + +clean: + rm -rf $(TARGET1) test/test_runners + (cd rom && make clean) + diff --git a/test/meter/rom/Makefile b/test/meter/rom/Makefile new file mode 100644 index 0000000..9f50d69 --- /dev/null +++ b/test/meter/rom/Makefile @@ -0,0 +1,14 @@ +TOPDIR=../../../ +PROJECT:=$(shell basename ${PWD}) +SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S +OPT=-O0 # don't let optimiser remove stall loop +all: all_common + @# Convert ROM to C file and header + @xxd -i ${PROJECT}.bin > ${PROJECT}-header.c + @echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h + +test: test_common +clean: clean_common + rm -f ${PROJECT}-header.h ${PROJECT}-header.c + +include ${TOPDIR}/apps/makefile.common diff --git a/test/meter/rom/rom.c b/test/meter/rom/rom.c new file mode 100644 index 0000000..823862e --- /dev/null +++ b/test/meter/rom/rom.c @@ -0,0 +1,15 @@ +#include "uvm32_target.h" + +void stall(void) { + volatile uint32_t i; + for (i=0;i<100;i++) { + } +} + +void main(void) { + for (int i=0;i<100;i++) { + stall(); + printdec(i); + } +} + diff --git a/test/meter/test/tests.c b/test/meter/test/tests.c new file mode 100644 index 0000000..8999e1c --- /dev/null +++ b/test/meter/test/tests.c @@ -0,0 +1,58 @@ +#include +#include "unity.h" +#include "uvm32.h" +#include "../common/uvm32_common_custom.h" + +#include "rom-header.h" + +static uvm32_state_t vmst; +static uvm32_evt_t evt; + +void setUp(void) { + // runs before each test + uvm32_init(&vmst); + uvm32_load(&vmst, rom_bin, rom_bin_len); +} + +void tearDown(void) { +} + +// run program until printdec() reaches 100 +// every time it hangs, resume +// run in batches of num_instr +void metered_run(uint32_t num_instr) { + uint32_t expected = 0; + + while(expected < 100) { + uvm32_run(&vmst, &evt, num_instr); + switch(evt.typ) { + case UVM32_EVT_SYSCALL: { + TEST_ASSERT_EQUAL(UVM32_SYSCALL_PRINTDEC, evt.data.syscall.code); + uint32_t val = uvm32_getval(&vmst, &evt, ARG0); + TEST_ASSERT_EQUAL(val, expected); + expected++; + } break; + case UVM32_EVT_ERR: + TEST_ASSERT_EQUAL(evt.data.err.errcode, UVM32_ERR_HUNG); + uvm32_clearError(&vmst); // clear the hung error so it can continue + break; + case UVM32_EVT_END: + TEST_ASSERT_EQUAL(0, 1); // trigger an assert, we didn't get to 100000 yet + break; + } + } + + // run vm to completion + uvm32_run(&vmst, &evt, 10000); + TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END); +} + +void test_meter(void) { + // test many different meter values, including 0 (which should run 1 instruction, as 0 is going to hang) + for (uint32_t i=0;i<1000;i++) { + uvm32_init(&vmst); + uvm32_load(&vmst, rom_bin, rom_bin_len); + metered_run(i); + } +} + diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index 550d302..f5ba520 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -138,27 +138,33 @@ void uvm32_clearError(uvm32_state_t *vmst) { } uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { - uint32_t num_instr = 0; + const uint32_t min_instrs = 1; + uint32_t orig_instr_meter = instr_meter; + + if (instr_meter < min_instrs) { + instr_meter = min_instrs; + } if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setup_err_evt(vmst, evt); - return num_instr; + return orig_instr_meter - instr_meter; } if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); setup_err_evt(vmst, evt); - return num_instr; + return orig_instr_meter - instr_meter; } setStatus(vmst, UVM32_STATUS_RUNNING); // run CPU until no longer in running state - while(vmst->status == UVM32_STATUS_RUNNING) { + while(vmst->status == UVM32_STATUS_RUNNING && instr_meter > 0) { uint64_t elapsedUs = 1; uint32_t ret; ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); + instr_meter--; switch(ret) { case 0: // ok @@ -213,28 +219,25 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) break; } - num_instr++; - - // check instruction meter, in case of hang/infinite loop - if (instr_meter-- == 0) { + if (vmst->status == UVM32_STATUS_RUNNING && instr_meter == 0) { + // no syscall occurred, so we've hung setStatusErr(vmst, UVM32_ERR_HUNG); setup_err_evt(vmst, evt); - return num_instr; + return orig_instr_meter - instr_meter; } - } if (vmst->status == UVM32_STATUS_ENDED) { evt->typ = UVM32_EVT_END; - return num_instr; + return orig_instr_meter - instr_meter; } // an event is ready if (vmst->status == UVM32_STATUS_PAUSED) { // send back the built up event UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); - return num_instr; + return orig_instr_meter - instr_meter; } else { if (vmst->status == UVM32_STATUS_ERROR) { setup_err_evt(vmst, evt); @@ -242,7 +245,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE); setup_err_evt(vmst, evt); } - return num_instr; + return orig_instr_meter - instr_meter; } } @@ -301,3 +304,14 @@ uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm3 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_evt_syscall_buf_t scb; + if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), len, &scb)) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + garbage = 0; + scb.ptr = (uint8_t *)&garbage; + scb.len = 0; + } + return scb; +} + diff --git a/uvm32/uvm32.h b/uvm32/uvm32.h index d72a886..6c6fa8e 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -97,6 +97,7 @@ uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t); 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);