diff --git a/README.md b/README.md index 3a75310..5641f71 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,46 @@ The following functions are used to access syscall parameters safely: 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); +## Event driven operation + +A useful pattern for code running in the VM is to be event-driven. In this setup the program requests blocks until woken up with a reason. This requires some support in the host, but can be implemented as follows. + +In the VM code: + +```c +while(1) { + // ask host to suspend running until one of the following events + uint32_t wakeReason = yield(KEYPRESS_EVENT_MASK | MOUSE_EVENT_MASK | NETWORK_EVENT_MASK); + switch(wakeReason) { + ... + } +} +``` + +In the host code: + +```c +uvm32_run(&vmst, &evt, 1000); +switch(evt.typ) { + case UVM32_EVT_SYSCALL: + switch(evt.data.syscall.code) { + case UVM32_SYSCALL_YIELD: + uint32_t events = uvm32_getval(&vmst, &evt, ARG0); + // do not call uvm32_run() again until something in the events set is triggered + break; + ... + } + break; + ... +} +``` + +Then, to wake the VM once an (eg. key) event has occurred + +```c +uvm32_setval(&vmst, &evt, RET, KEYPRESS_EVENT_MASK); +uvm32_run(&vmst, &evt, 1000); +``` ## Configuration diff --git a/apps/lissajous/lissajous.c b/apps/lissajous/lissajous.c index 8aa19bb..e957a96 100644 --- a/apps/lissajous/lissajous.c +++ b/apps/lissajous/lissajous.c @@ -13,13 +13,6 @@ void movecursor(int x, int y) { print("f"); } -void sleep(uint32_t ms) { - uint32_t start = millis(); - while (millis() < start + ms) { - yield(); - } -} - // Absolute value function for doubles double abs_c(double x) { if (x < 0) { @@ -116,7 +109,7 @@ void main(void) { // wait for next frame while (millis() < framestart + (1000 / 30)) { - yield(); + yield(0); } beta += 0.05; freq1 += 0.01; diff --git a/apps/self/self.c b/apps/self/self.c index 5821e1a..fa98ab9 100644 --- a/apps/self/self.c +++ b/apps/self/self.c @@ -45,6 +45,8 @@ void main(void) { break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL switch(evt.data.syscall.code) { + case UVM32_SYSCALL_YIELD: + break; case UVM32_SYSCALL_PUTC: putc(uvm32_getval(&vmst, &evt, ARG0)); break; diff --git a/apps/sketch/sketch.c b/apps/sketch/sketch.c index 27c6a59..67d23fd 100644 --- a/apps/sketch/sketch.c +++ b/apps/sketch/sketch.c @@ -25,7 +25,7 @@ void setup(void) { void main(void) { setup(); while(loop()) { - yield(); + yield(0); } } diff --git a/common/uvm32_target.h b/common/uvm32_target.h index 4439804..15144a1 100644 --- a/common/uvm32_target.h +++ b/common/uvm32_target.h @@ -56,7 +56,7 @@ static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) { #define millis() syscall_cast(UVM32_SYSCALL_MILLIS, 0, 0) #define putc(x) syscall_cast(UVM32_SYSCALL_PUTC, x, 0) #define getc() syscall_cast(UVM32_SYSCALL_GETC, 0, 0) -#define yield() syscall_cast(UVM32_SYSCALL_YIELD, 0, 0) +#define yield(x) syscall_cast(UVM32_SYSCALL_YIELD, x, 0) #define printbuf(x, y) syscall_cast(UVM32_SYSCALL_PRINTBUF, x, y) extern char _estack; diff --git a/host-arduino/host-arduino.ino b/host-arduino/host-arduino.ino index 6487c1f..9abc23c 100644 --- a/host-arduino/host-arduino.ino +++ b/host-arduino/host-arduino.ino @@ -57,6 +57,8 @@ void loop(void) { break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL switch(evt.data.syscall.code) { + case UVM32_SYSCALL_YIELD: + break; case UVM32_SYSCALL_PUTC: Serial.print((char)uvm32_getval(&vmst, &evt, ARG0)); break; diff --git a/host-arduino/uvm32.cpp b/host-arduino/uvm32.cpp index dd9097c..f93ab92 100644 --- a/host-arduino/uvm32.cpp +++ b/host-arduino/uvm32.cpp @@ -1,4 +1,5 @@ #include "config.h" + #define MINIRV32_IMPLEMENTATION #include "uvm32.h" @@ -11,6 +12,9 @@ // On an invalid operation, an error is set in uvm32_state_t, but a valid pointer still needs to be temporarily used static uint32_t garbage; +// magic value for stack canary +#define STACK_CANARY_VALUE 0x42 + #ifndef UVM32_MEMCPY #define UVM32_MEMCPY uvm32_memcpy void uvm32_memcpy(void *dst, const void *src, int len) { @@ -85,6 +89,8 @@ bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) { } UVM32_MEMCPY(vmst->memory, rom, len); + vmst->stack_canary = (uint8_t *)UVM32_NULL; + return true; } @@ -136,6 +142,12 @@ 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; + if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { + setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); + setup_err_evt(vmst, evt); + return num_instr; + } + if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); setup_err_evt(vmst, evt); @@ -160,10 +172,25 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) case UVM32_SYSCALL_HALT: setStatus(vmst, UVM32_STATUS_ENDED); break; - case UVM32_SYSCALL_YIELD: - vmst->ioevt.typ = UVM32_EVT_YIELD; - setStatus(vmst, UVM32_STATUS_PAUSED); - 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; + + // check data fits in ram + if (mem_offset > UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); + 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; + } + } + } break; default: // user defined syscalls vmst->ioevt.typ = UVM32_EVT_SYSCALL; diff --git a/host-mini/host-mini.c b/host-mini/host-mini.c index e32ad0d..a9eb539 100644 --- a/host-mini/host-mini.c +++ b/host-mini/host-mini.c @@ -47,6 +47,8 @@ int main(int argc, char *argv[]) { break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL switch(evt.data.syscall.code) { + case UVM32_SYSCALL_YIELD: + break; case UVM32_SYSCALL_PUTC: printf("%c", uvm32_getval(&vmst, &evt, ARG0)); break; diff --git a/host-parallel/host-parallel.c b/host-parallel/host-parallel.c index 13cd69b..d2d3fd7 100644 --- a/host-parallel/host-parallel.c +++ b/host-parallel/host-parallel.c @@ -81,6 +81,8 @@ int main(int argc, char *argv[]) { break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL switch(evt.data.syscall.code) { + case UVM32_SYSCALL_YIELD: + break; case UVM32_SYSCALL_PRINTLN: { const char *str = uvm32_getcstr(&vmst[scheduler_index], &evt, ARG0); if (str[0] != 0) { diff --git a/host/host.c b/host/host.c index 2762881..27860bc 100644 --- a/host/host.c +++ b/host/host.c @@ -167,10 +167,6 @@ int main(int argc, char *argv[]) { printf("UVM32_EVT_END\n"); isrunning = false; break; - case UVM32_EVT_YIELD: - //printf("UVM32_EVT_YIELD\n"); - // program has paused, but no syscall - break; case UVM32_EVT_ERR: printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode); if (evt.data.err.errcode == UVM32_ERR_HUNG) { @@ -187,6 +183,11 @@ int main(int argc, char *argv[]) { printf("%02x", *buf.ptr++); } } break; + case UVM32_SYSCALL_YIELD: { + // uint32_t yield_typ = uvm32_getval(&vmst, &evt, ARG0); + // printf("YIELD type=%d\n", yield_typ); + // uvm32_setval(&vmst, &evt, RET, 123); + } break; case UVM32_SYSCALL_PRINT: { const char *str = uvm32_getcstr(&vmst, &evt, ARG0); printf("%s", str); diff --git a/precompiled/fib.bin b/precompiled/fib.bin index 9d95c6d..a92bdfd 100755 Binary files a/precompiled/fib.bin and b/precompiled/fib.bin differ diff --git a/precompiled/self.bin b/precompiled/self.bin index 15a686a..87d3517 100755 Binary files a/precompiled/self.bin and b/precompiled/self.bin differ diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index 6215f09..2cb61db 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -170,14 +170,10 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) case UVM32_SYSCALL_HALT: setStatus(vmst, UVM32_STATUS_ENDED); break; - case UVM32_SYSCALL_YIELD: - vmst->ioevt.typ = UVM32_EVT_YIELD; - setStatus(vmst, UVM32_STATUS_PAUSED); - 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]; + uint32_t param0 = vmst->core.regs[10]; // a0 uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; // check data fits in ram diff --git a/uvm32/uvm32.h b/uvm32/uvm32.h index 588e761..6fd44c5 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -26,7 +26,6 @@ typedef enum { typedef enum { UVM32_EVT_ERR, UVM32_EVT_SYSCALL, - UVM32_EVT_YIELD, UVM32_EVT_END, } uvm32_evt_typ_t;