diff --git a/apps/makefile.common b/apps/makefile.common index ee9ca71..574b48c 100644 --- a/apps/makefile.common +++ b/apps/makefile.common @@ -1,8 +1,9 @@ PREFIX:=riscv64-elf- +OPT ?= -Os CFLAGS+=-I${TOPDIR}/common CFLAGS+=-fno-stack-protector CFLAGS+=-static-libgcc -fdata-sections -ffunction-sections -CFLAGS+=-g -Os -march=rv32im -mabi=ilp32 -static +CFLAGS+=-g ${OPT} -march=rv32im -mabi=ilp32 -static LDFLAGS:= -T ${TOPDIR}/apps/linker.ld -nostdlib -Wl,--gc-sections LIBS:= -lgcc # needed for softfp @@ -21,8 +22,8 @@ else endif all_common: - ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} - $(PREFIX)objcopy ${PROJECT}.elf -O binary ${PROJECT}.bin + @${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} + @$(PREFIX)objcopy ${PROJECT}.elf -O binary ${PROJECT}.bin disasm_common: all $(PREFIX)objdump -S -d -f ${PROJECT}.elf diff --git a/test/Makefile b/test/Makefile index 18bfb77..3b7c49b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,8 @@ all: - make -C example_1 + @make -C example_1 + @make -C stackoverflow clean: make -C example_1 clean + make -C stackoverflow clean + diff --git a/test/example_1/Makefile b/test/example_1/Makefile index f03fcdc..07ba1b0 100644 --- a/test/example_1/Makefile +++ b/test/example_1/Makefile @@ -17,15 +17,15 @@ 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) + @$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1) + @- ./$(TARGET1) rom: - (cd rom && make) + @(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 + @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 diff --git a/test/example_1/rom/Makefile b/test/example_1/rom/Makefile index 2239f1e..4698f22 100644 --- a/test/example_1/rom/Makefile +++ b/test/example_1/rom/Makefile @@ -2,9 +2,9 @@ TOPDIR=../../../ PROJECT:=$(shell basename ${PWD}) SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S 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 + @# 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 diff --git a/test/stackoverflow/Makefile b/test/stackoverflow/Makefile new file mode 100644 index 0000000..0ddca7a --- /dev/null +++ b/test/stackoverflow/Makefile @@ -0,0 +1,33 @@ +C_COMPILER=gcc + +UNITY_ROOT=../unity + +CFLAGS=-std=c99 +CFLAGS += -Wall +CFLAGS += -Werror +CFLAGS += -DUVM32_MEMORY_SIZE=512 + +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/stackoverflow/rom/Makefile b/test/stackoverflow/rom/Makefile new file mode 100644 index 0000000..6efca22 --- /dev/null +++ b/test/stackoverflow/rom/Makefile @@ -0,0 +1,14 @@ +TOPDIR=../../../ +PROJECT:=$(shell basename ${PWD}) +SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S +OPT=-O0 # don't optimise +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/stackoverflow/rom/rom.c b/test/stackoverflow/rom/rom.c new file mode 100644 index 0000000..a5175c1 --- /dev/null +++ b/test/stackoverflow/rom/rom.c @@ -0,0 +1,12 @@ +#include "uvm32_target.h" + +int foo(int a) { + yield(0); + printdec(a); + return foo(a+1); +} + +void main(void) { + foo(1); +} + diff --git a/test/stackoverflow/test/tests.c b/test/stackoverflow/test/tests.c new file mode 100644 index 0000000..9fc6503 --- /dev/null +++ b/test/stackoverflow/test/tests.c @@ -0,0 +1,34 @@ +#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_stackoverflow(void) { + // run the vm + while(1) { + uvm32_run(&vmst, &evt, 100); + if (evt.typ == UVM32_EVT_SYSCALL) { + // ignore syscalls + } else { + TEST_ASSERT_EQUAL(UVM32_EVT_ERR, evt.typ); + TEST_ASSERT_EQUAL(UVM32_ERR_INTERNAL_CORE, evt.data.err.errcode); + break; + } + } +} + + diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index 2cb61db..550d302 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -141,7 +141,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) uint32_t num_instr = 0; if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { - setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); + setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setup_err_evt(vmst, evt); return num_instr; } @@ -159,50 +159,58 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) uint64_t elapsedUs = 1; uint32_t ret; ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); - if (3 == ret) { - // Fetch registers used by syscall - const uint32_t syscall = vmst->core.regs[17]; // a7 - // on exception we should jump to mtvec, but we handle directly - // and skip over the ecall instruction - vmst->core.pc += 4; - switch(syscall) { - // inbuilt syscalls - case UVM32_SYSCALL_HALT: - setStatus(vmst, UVM32_STATUS_ENDED); - break; - case UVM32_SYSCALL_STACKPROTECT: { - // don't allow errant code to change it once set - if (vmst->stack_canary == (uint8_t *)UVM32_NULL) { - uint32_t param0 = vmst->core.regs[10]; // a0 - uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; + + switch(ret) { + case 0: // ok + break; + case 12: { // ecall + // Fetch registers used by syscall + const uint32_t syscall = vmst->core.regs[17]; // a7 + // on exception we should jump to mtvec, but we handle directly + // and skip over the ecall instruction + vmst->core.pc += 4; + switch(syscall) { + // inbuilt syscalls + case UVM32_SYSCALL_HALT: + setStatus(vmst, UVM32_STATUS_ENDED); + break; + case UVM32_SYSCALL_STACKPROTECT: { + // don't allow errant code to change it once set + if (vmst->stack_canary == (uint8_t *)UVM32_NULL) { + uint32_t param0 = vmst->core.regs[10]; // a0 + uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; + mem_offset &= ~0xF; // round up by 16 bytes + mem_offset += 16*4; - // check data fits in ram - if (mem_offset > UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); - setup_err_evt(vmst, evt); + // check data fits in ram + if (mem_offset > UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); + setup_err_evt(vmst, evt); + } + // check canary is inside valid memory + if (mem_offset < UVM32_MEMORY_SIZE) { + // set canary + vmst->stack_canary = &vmst->memory[mem_offset]; + *vmst->stack_canary = STACK_CANARY_VALUE; + } } - // check canary is inside valid memory - if (mem_offset < UVM32_MEMORY_SIZE) { - // set canary - vmst->stack_canary = &vmst->memory[mem_offset]; - *vmst->stack_canary = STACK_CANARY_VALUE; - } - } - } break; - default: - // user defined syscalls - vmst->ioevt.typ = UVM32_EVT_SYSCALL; - vmst->ioevt.data.syscall.code = syscall; - 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[1] = &vmst->core.regs[11]; // a1 - setStatus(vmst, UVM32_STATUS_PAUSED); - break; - } - } else if (ret != 0) { - // unhandled exception - setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); - setup_err_evt(vmst, evt); + } break; + default: + // user defined syscalls + vmst->ioevt.typ = UVM32_EVT_SYSCALL; + vmst->ioevt.data.syscall.code = syscall; + 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[1] = &vmst->core.regs[11]; // a1 + setStatus(vmst, UVM32_STATUS_PAUSED); + break; + } // end switch(syscall) + } break; // end ecall + default: + // unhandled exception + setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); + setup_err_evt(vmst, evt); + break; } num_instr++; @@ -213,8 +221,10 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) setup_err_evt(vmst, evt); return num_instr; } + } + if (vmst->status == UVM32_STATUS_ENDED) { evt->typ = UVM32_EVT_END; return num_instr; diff --git a/uvm32/uvm32.h b/uvm32/uvm32.h index 11d5341..d72a886 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -20,7 +20,6 @@ X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_ARGS) \ - X(UVM32_ERR_STACKOVERFLOW) \ #define X(name) name, typedef enum { @@ -55,7 +54,7 @@ typedef struct { #define MINIRV32_DECORATE static #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE -#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} +#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return retval;} #ifndef MINIRV32_IMPLEMENTATION #define MINIRV32_STEPPROTO #endif