From 76fba39a21e6c67f058f72151df052f2609a659a Mon Sep 17 00:00:00 2001 From: Toby Jaffey Date: Tue, 9 Dec 2025 21:51:35 +0000 Subject: [PATCH] Rework syscall ABI. Syscalls now accept two parameters, allowing for things like "int count = read(buf, len)" Rather than providing safe signatures for syscalls, the user is now given helper functions to safely parse incoming values, c-strings and slices. --- README.md | 104 ++++++------ apps/helloworld/helloworld.c | 4 +- apps/rust-hello/src/main.rs | 11 +- apps/self/self.c | 74 ++++----- apps/sketch/sketch.c | 1 + apps/zig-mandel/src/uvm.zig | 10 +- apps/zigtris/src/uvm.zig | 18 +- common/uvm32_common_custom.h | 1 + common/uvm32_target.h | 32 ++-- host-arduino/common/uvm32_common_custom.h | 7 +- host-arduino/host-arduino.ino | 78 ++++----- host-arduino/uvm32.cpp | 193 ++++++++++++++-------- host-arduino/uvm32.h | 63 ++++--- host-mini/host-mini.c | 72 ++++---- host-parallel/host-parallel.c | 105 ++++++------ host/Makefile | 2 +- host/host.c | 99 ++++------- precompiled/conio.bin | Bin 150 -> 150 bytes precompiled/fib.bin | Bin 464 -> 480 bytes precompiled/helloworld.bin | Bin 48 -> 60 bytes precompiled/lissajous.bin | Bin 14576 -> 14592 bytes precompiled/mandel.bin | Bin 236 -> 268 bytes precompiled/rust-hello.bin | Bin 78 -> 127 bytes precompiled/self.bin | Bin 4964 -> 5044 bytes precompiled/sketch.bin | Bin 112 -> 152 bytes precompiled/zigtris.bin | Bin 16364 -> 16396 bytes uvm32/uvm32.c | 145 +++++++++------- uvm32/uvm32.h | 61 +++---- 28 files changed, 543 insertions(+), 537 deletions(-) diff --git a/README.md b/README.md index 76bdd58..b787486 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ uint8_t bytecode[] = { /* ... */ }; // some compiled bytecode uvm32_state_t vmst; // execution state of the vm uvm32_evt_t evt; // events passed from vm to host -uvm32_init(&vmst, NULL, 0); // setup vm and pass in handlers for host functions +uvm32_init(&vmst); // setup vm uvm32_load(&vmst, bytecode, sizeof(bytecode)); // load the bytecode uvm32_run(&vmst, &evt, 100); // run up to 100 instructions @@ -86,7 +86,7 @@ If the bytecode attempts to execute more instructions than the the passed value ## Internals -uvm32 emulates a RISC-V 32bit CPU using [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima). All IO from vm bytecode to the host is performed using `ecall` syscalls. Each syscall provided by the host requires a unique syscall value. A syscall passes a single `uint32_t` from bytecode to the host and may receive a returned `uint32_t`. The host may treat the value as a pointer and modify memory. +uvm32 emulates a RISC-V 32bit CPU using [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima). All IO from vm bytecode to the host is performed using `ecall` syscalls. Each syscall provided by the host requires a unique syscall value. A syscall passes two values and receives one on return. uvm32 is always in one of 4 states, paused, running, ended or error. @@ -101,42 +101,48 @@ stateDiagram ## Boot -At boot, the whole memory is zeroed. The user program is placed at the start. The stack pointer is set to the start of the CPU registers and grows downwards. No heap region is setup and all code is in RAM. +At boot, the whole memory is zeroed. The user program is placed at the start. The stack pointer is set to the end of memory and grows downwards. No heap region is setup and all code is in RAM. ## syscall ABI All communication between bytecode and the vm host is performed via syscalls. -To make a syscall, register `a7` is set with the syscall number (a `UVM32_SYSCALL_x`) and `a0` is set with the syscall parameter. The response is returned in `a1`. +To make a syscall, register `a7` is set with the syscall number (a `UVM32_SYSCALL_x`) and `a0`, `a1` are set with the syscall parameters. The response is returned in `a2`. [target.h](common/uvm32_target.h#L12) ```c -static uint32_t syscall(uint32_t id, uint32_t param) { - register uint32_t a0 asm("a0") = (uint32_t)(param); - register uint32_t a1 asm("a1"); +static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) { + register uint32_t a0 asm("a0") = (uint32_t)(param1); + register uint32_t a1 asm("a1") = (uint32_t)(param2); + register uint32_t a2 asm("a2"); register uint32_t a7 asm("a7") = (uint32_t)(id); asm volatile ( "ecall" - : "=r"(a1) // output - : "r"(a0), "r"(a7) // input + : "=r"(a2) // output + : "r"(a7), "r"(a0), "r"(a1) // input : "memory" ); - return a1; + return a2; } ``` - The [RISC-V SBI](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc) is not followed, a simpler approach is taken. ## syscalls There are two system syscalls used by uvm32, `halt()` and `yield()`. -`halt()` tells the host that the program has ended normally. `yield()` tells the host that the program requires more instructions to be executed. +`halt()` tells the host that the program has ended normally. `yield()` tells the host that the program requires more instructions to be executed. These are handled internally to uvm32. -New syscalls can be added to the host via `uvm32_init()`. -Each syscall maps a syscall number to a value understood by the host (`F_PRINTD` below) and has an associated type which tells the host how to interpret the data passed to the syscall. +Syscalls are handled in the host by reading the syscall identifier, then using the provided functions to get arguments and set a return response. Direct access to the VM's memory space is not allowed, to avoid memory corruption issues. + +The following functions are used to access syscall parameters safely: + + 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); Here is a full example of a working VM host from [apps/host-mini](apps/host-mini) @@ -151,38 +157,28 @@ Here is a full example of a working VM host from [apps/host-mini](apps/host-mini uint8_t rom[] = { // mandel.bin 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, - 0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, - 0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, - 0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, - 0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, - 0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, - 0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, - 0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, - 0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, - 0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, - 0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, - 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, - 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, - 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, - 0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, - 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 -}; - -// Create an identifier for our host handler -// syscalls exposed to vm environement -typedef enum { - F_PUTC, - F_PRINTLN, -} f_code_t; - -// Map exposed syscalls to syscalls -const uvm32_mapping_t env[] = { - { .syscall = UVM32_SYSCALL_PRINTLN, .typ = UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, - { .syscall = UVM32_SYSCALL_PUTC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PUTC }, + 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, + 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, + 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, + 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, + 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, + 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, + 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, + 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, + 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, + 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, + 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x10, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, + 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x00 }; int main(int argc, char *argv[]) { @@ -190,7 +186,7 @@ int main(int argc, char *argv[]) { uvm32_evt_t evt; bool isrunning = true; - uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); + uvm32_init(&vmst); uvm32_load(&vmst, rom, sizeof(rom)); while(isrunning) { @@ -201,12 +197,16 @@ int main(int argc, char *argv[]) { isrunning = false; break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL - switch((f_code_t)evt.data.syscall.code) { - case F_PUTC: - printf("%c", evt.data.syscall.val.u32); + switch(evt.data.syscall.code) { + case UVM32_SYSCALL_PUTC: + printf("%c", uvm32_getval(&vmst, &evt, ARG0)); break; - case F_PRINTLN: - printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); + case UVM32_SYSCALL_PRINTLN: { + const char *str = uvm32_getcstr(&vmst, &evt, ARG0); + printf("%s\n", str); + } break; + default: + printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code); break; } break; diff --git a/apps/helloworld/helloworld.c b/apps/helloworld/helloworld.c index 91ec45e..072ab1d 100644 --- a/apps/helloworld/helloworld.c +++ b/apps/helloworld/helloworld.c @@ -1,8 +1,6 @@ #include "uvm32_target.h" void main(void) { - for (int i=0;i<10;i++) { - printdec(i); - } + println("Hello world"); } diff --git a/apps/rust-hello/src/main.rs b/apps/rust-hello/src/main.rs index 1cb1e89..493c9db 100644 --- a/apps/rust-hello/src/main.rs +++ b/apps/rust-hello/src/main.rs @@ -11,13 +11,13 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); // startup code global_asm!(include_str!("../../crt0.S"), UVM32_SYSCALL_HALT = const UVM32_SYSCALL_HALT); -fn syscall(id: u32, n: u32) -> u32 { +fn syscall(id: u32, param1: u32, param2: u32) -> u32 { let mut value; unsafe { asm!("ecall", - in("a0") n, + in("a0") param1, in("a1") param2, in("a7") id, - lateout("a1") value, + lateout("a2") value, ); } return value; @@ -25,17 +25,18 @@ fn syscall(id: u32, n: u32) -> u32 { fn println(message: &str) { let addr_value = message.as_ptr() as u32; - syscall(UVM32_SYSCALL_PRINTLN, addr_value); + syscall(UVM32_SYSCALL_PRINTLN, addr_value, 0); } fn printdec(n: u32) { - syscall(UVM32_SYSCALL_PRINTDEC, n); + syscall(UVM32_SYSCALL_PRINTDEC, n, 0); } #[no_mangle] pub extern "C" fn main() { for i in 0..10 { printdec(i); + println("\0"); } println("Hello, world!\0"); } diff --git a/apps/self/self.c b/apps/self/self.c index bbc8bbd..5821e1a 100644 --- a/apps/self/self.c +++ b/apps/self/self.c @@ -4,38 +4,28 @@ uint8_t rom[] = { // mandel.bin 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, - 0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, - 0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, - 0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, - 0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, - 0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, - 0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, - 0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, - 0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, - 0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, - 0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, - 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, - 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, - 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, - 0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, - 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 -}; - -// Create an identifier for our host handler -// syscalls exposed to vm environement -typedef enum { - F_PUTC, - F_PRINTLN, -} f_code_t; - -// Map exposed syscalls to syscalls -const uvm32_mapping_t env[] = { - { .syscall = UVM32_SYSCALL_PRINTLN, .typ = UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, - { .syscall = UVM32_SYSCALL_PUTC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PUTC }, + 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, + 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, + 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, + 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, + 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, + 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, + 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, + 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, + 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, + 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, + 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x10, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, + 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x00 }; void main(void) { @@ -43,7 +33,7 @@ void main(void) { uvm32_evt_t evt; bool isrunning = true; - uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); + uvm32_init(&vmst); uvm32_load(&vmst, rom, sizeof(rom)); while(isrunning) { @@ -54,15 +44,16 @@ void main(void) { isrunning = false; break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL - switch((f_code_t)evt.data.syscall.code) { - case F_PUTC: - putc(evt.data.syscall.val.u32); + switch(evt.data.syscall.code) { + case UVM32_SYSCALL_PUTC: + putc(uvm32_getval(&vmst, &evt, ARG0)); break; - case F_PRINTLN: - for (int i=0;i= 10) { return false; } else { diff --git a/apps/zig-mandel/src/uvm.zig b/apps/zig-mandel/src/uvm.zig index adfa6a2..14a2014 100644 --- a/apps/zig-mandel/src/uvm.zig +++ b/apps/zig-mandel/src/uvm.zig @@ -4,25 +4,25 @@ const uvm32 = @cImport({ }); const std = @import("std"); -pub inline fn syscall(id: u32, param: u32) u32 { +pub inline fn syscall(id: u32, param1: u32, param2: u32) u32 { var val: u32 = undefined; asm volatile ("ecall" : [val] "={a1}" (val), - : [param] "{a0}" (param), + : [param1] "{a0}" (param1), [param2] "{a1}" (param2), [id] "{a7}" (id), : .{ .memory = true }); return val; } pub inline fn println(val: [:0]const u8) void { - _ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(val.ptr)); + _ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(val.ptr), 0); } pub inline fn yield() void { - _ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0); + _ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0, 0); } pub inline fn putc(c:u8) void { - _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c); + _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c, 0); } diff --git a/apps/zigtris/src/uvm.zig b/apps/zigtris/src/uvm.zig index 344a3f4..cd73386 100644 --- a/apps/zigtris/src/uvm.zig +++ b/apps/zigtris/src/uvm.zig @@ -4,18 +4,18 @@ const uvm32 = @cImport({ }); const std = @import("std"); -pub inline fn syscall(id: u32, param: u32) u32 { +pub inline fn syscall(id: u32, param1: u32, param2: u32) u32 { var val: u32 = undefined; asm volatile ("ecall" - : [val] "={a1}" (val), - : [param] "{a0}" (param), + : [val] "={a2}" (val), + : [param1] "{a0}" (param1), [param2] "{a1}" (param2), [id] "{a7}" (id), : .{ .memory = true }); return val; } pub inline fn getc() ?u8 { - const key = syscall(uvm32.UVM32_SYSCALL_GETC, 0); + const key = syscall(uvm32.UVM32_SYSCALL_GETC, 0, 0); if (key == 0xFFFFFFFF) { return null; } else { @@ -24,7 +24,7 @@ pub inline fn getc() ?u8 { } pub inline fn millis() u32 { - return syscall(uvm32.UVM32_SYSCALL_MILLIS, 0); + return syscall(uvm32.UVM32_SYSCALL_MILLIS, 0, 0); } // dupeZ would be better, but want to avoid using an allocator @@ -35,21 +35,21 @@ pub inline fn print(m: []const u8) void { @memcpy(termination_buf[0..m.len], m); termination_buf[m.len] = 0; const s = termination_buf[0..m.len :0]; - _ = syscall(uvm32.UVM32_SYSCALL_PRINT, @intFromPtr(s.ptr)); + _ = syscall(uvm32.UVM32_SYSCALL_PRINT, @intFromPtr(s.ptr), 0); } pub inline fn println(m: []const u8) void { @memcpy(termination_buf[0..m.len], m); termination_buf[m.len] = 0; const s = termination_buf[0..m.len :0]; - _ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(s.ptr)); + _ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(s.ptr), 0); } pub inline fn yield() void { - _ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0); + _ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0, 0); } pub inline fn putc(c:u8) void { - _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c); + _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c, 0); } diff --git a/common/uvm32_common_custom.h b/common/uvm32_common_custom.h index 8ad60d7..69760cc 100644 --- a/common/uvm32_common_custom.h +++ b/common/uvm32_common_custom.h @@ -8,4 +8,5 @@ #define UVM32_SYSCALL_PRINTDEC 0x00000004 #define UVM32_SYSCALL_PRINTHEX 0x00000005 #define UVM32_SYSCALL_MILLIS 0x00000006 +#define UVM32_SYSCALL_PRINTBUF 0x00000007 diff --git a/common/uvm32_target.h b/common/uvm32_target.h index 276a8e5..ecd6345 100644 --- a/common/uvm32_target.h +++ b/common/uvm32_target.h @@ -32,30 +32,32 @@ size_assert(int32_t, 4); size_assert(int16_t, 2); size_assert(int8_t, 1); -static uint32_t syscall(uint32_t id, uint32_t param) { - register uint32_t a0 asm("a0") = (uint32_t)(param); - register uint32_t a1 asm("a1"); +static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) { + register uint32_t a0 asm("a0") = (uint32_t)(param1); + register uint32_t a1 asm("a1") = (uint32_t)(param2); + register uint32_t a2 asm("a2"); register uint32_t a7 asm("a7") = (uint32_t)(id); asm volatile ( "ecall" - : "=r"(a1) // output - : "r"(a0), "r"(a7) // input + : "=r"(a2) // output + : "r"(a7), "r"(a0), "r"(a1) // input : "memory" ); - return a1; + return a2; } -#define syscall_cast(id, x) syscall((uint32_t)id, (uint32_t)x) +#define syscall_cast(id, p1, p2) syscall((uint32_t)id, (uint32_t)p1, (uint32_t)p2) -#define println(x) syscall_cast(UVM32_SYSCALL_PRINTLN, x) -#define print(x) syscall_cast(UVM32_SYSCALL_PRINT, x) -#define printdec(x) syscall_cast(UVM32_SYSCALL_PRINTDEC, x) -#define printhex(x) syscall_cast(UVM32_SYSCALL_PRINTHEX, x) -#define millis() syscall_cast(UVM32_SYSCALL_MILLIS, 0) -#define putc(x) syscall_cast(UVM32_SYSCALL_PUTC, x) -#define getc() syscall_cast(UVM32_SYSCALL_GETC, 0) -#define yield() syscall_cast(UVM32_SYSCALL_YIELD, 0) +#define println(x) syscall_cast(UVM32_SYSCALL_PRINTLN, x, 0) +#define print(x) syscall_cast(UVM32_SYSCALL_PRINT, x, 0) +#define printdec(x) syscall_cast(UVM32_SYSCALL_PRINTDEC, x, 0) +#define printhex(x) syscall_cast(UVM32_SYSCALL_PRINTHEX, x, 0) +#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 printbuf(x, y) syscall_cast(UVM32_SYSCALL_PRINTBUF, x, y) #include "uvm32_common_custom.h" diff --git a/host-arduino/common/uvm32_common_custom.h b/host-arduino/common/uvm32_common_custom.h index 5e58ce9..69760cc 100644 --- a/host-arduino/common/uvm32_common_custom.h +++ b/host-arduino/common/uvm32_common_custom.h @@ -1,11 +1,12 @@ // Definitions needed by both host and target // syscalls for exposed host functions, start at 0 -#define UVM32_SYSCALL_PRINTC 0x00000000 +#define UVM32_SYSCALL_PUTC 0x00000000 #define UVM32_SYSCALL_GETC 0x00000001 #define UVM32_SYSCALL_PRINT 0x00000002 #define UVM32_SYSCALL_PRINTLN 0x00000003 -#define UVM32_SYSCALL_PRINTD 0x00000004 -#define UVM32_SYSCALL_PRINTX 0x00000005 +#define UVM32_SYSCALL_PRINTDEC 0x00000004 +#define UVM32_SYSCALL_PRINTHEX 0x00000005 #define UVM32_SYSCALL_MILLIS 0x00000006 +#define UVM32_SYSCALL_PRINTBUF 0x00000007 diff --git a/host-arduino/host-arduino.ino b/host-arduino/host-arduino.ino index 65d99b0..1dc7cf4 100644 --- a/host-arduino/host-arduino.ino +++ b/host-arduino/host-arduino.ino @@ -2,43 +2,30 @@ #include "uvm32.h" #include "common/uvm32_common_custom.h" -// Precompiled binary program to print integers -// This code expects to print via syscall 0x13C (UVM32_SYSCALL_PRINTDEC in common/uvm32_common_custom.h) uint8_t rom[] = { 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, - 0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, - 0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, - 0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, - 0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, - 0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, - 0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, - 0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, - 0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, - 0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, - 0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, - 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, - 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, - 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, - 0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, - 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 -}; - -// Create an identifier for our host handler -typedef enum { - F_PRINTDEC, - F_PRINTLN, - F_PUTC, -} f_code_t; - -// Map VM syscall UVM32_SYSCALL_PRINTDEC to F_PRINTDEC, tell VM to expect write of a U32 -const uvm32_mapping_t env[] = { - { UVM32_SYSCALL_PRINTDEC, F_PRINTDEC, UVM32_SYSCALL_TYP_U32_WR }, - { UVM32_SYSCALL_PUTC, F_PUTC, UVM32_SYSCALL_TYP_U32_WR }, - { UVM32_SYSCALL_PRINTLN, F_PRINTLN, UVM32_SYSCALL_TYP_BUF_TERMINATED_WR }, + 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, + 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, + 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, + 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, + 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, + 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, + 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, + 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, + 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, + 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, + 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x10, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, + 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x00 }; uvm32_state_t vmst; @@ -69,18 +56,17 @@ void loop(void) { isrunning = false; break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL - switch((f_code_t)evt.data.syscall.code) { - case F_PRINTDEC: - Serial.println(evt.data.syscall.val.u32); + switch(evt.data.syscall.code) { + case UVM32_SYSCALL_PUTC: + Serial.print((char)uvm32_getval(&vmst, &evt, ARG0)); break; - case F_PUTC: - Serial.print((char)evt.data.syscall.val.u32); - break; - case F_PRINTLN: - for (int i=0;i -#include #ifndef UVM32_MEMORY_SIZE #error Define UVM32_MEMORY_SIZE #endif +#define UVM32_NULL (void *)0 + +// 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; + +#ifndef UVM32_MEMCPY +#define UVM32_MEMCPY uvm32_memcpy +void uvm32_memcpy(void *dst, const void *src, int len) { + uint8_t *d = (uint8_t *)dst; + const uint8_t *s = (const uint8_t *)src; + while(len--) { + *(d++) = *(s++); + } +} +#endif + +#ifndef UVM32_MEMSET +#define UVM32_MEMSET uvm32_memset +void uvm32_memset(void *buf, int c, int len) { + uint8_t *b = (uint8_t *)buf; + while(len--) { + *(b++) = c; + } +} +#endif + #include "mini-rv32ima.h" #define X(name) #name, @@ -32,79 +56,85 @@ static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) { } static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) { - setStatus(vmst, UVM32_STATUS_ERROR); - vmst->err = err; + // if already in error state, stay in the first error state + if (vmst->status != UVM32_STATUS_ERROR) { + setStatus(vmst, UVM32_STATUS_ERROR); + vmst->err = err; + } } -void uvm32_init(uvm32_state_t *vmst, const uvm32_mapping_t *mappings, uint32_t numMappings) { +void uvm32_init(uvm32_state_t *vmst) { + UVM32_MEMSET(vmst, 0x00, sizeof(uvm32_state_t)); vmst->status = UVM32_STATUS_PAUSED; - memset(vmst->memory, 0x00, UVM32_MEMORY_SIZE); - // The core lives at the end of RAM. - vmst->core = (struct MiniRV32IMAState*)(vmst->memory + UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)); - vmst->core->pc = MINIRV32_RAM_IMAGE_OFFSET; + vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET; // https://projectf.io/posts/riscv-cheat-sheet/ // setup stack pointer // la sp, _sstack // addi sp,sp,-16 - vmst->core->regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)) - 16; - vmst->core->regs[10] = 0x00; //hart ID - vmst->core->regs[11] = 0; - vmst->core->extraflags |= 3; // Machine-mode. - - vmst->mappings = mappings; - vmst->numMappings = numMappings; + vmst->core.regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) - 16; + vmst->core.regs[10] = 0x00; //hart ID + vmst->core.regs[11] = 0; + vmst->core.extraflags |= 3; // Machine-mode. } bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { - // RAM needs at least image then MiniRV32IMAState (core) - if (len > UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)) { + if (len > UVM32_MEMORY_SIZE) { // too big return false; } - memcpy(vmst->memory, rom, len); + UVM32_MEMCPY(vmst->memory, rom, len); return true; } // Read C-string up to terminator and return len,ptr -static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_syscall_buf_t *buf) { +bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_syscall_buf_t *buf) { uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; uint32_t p = ptrstart; if (p >= UVM32_MEMORY_SIZE) { setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; + buf->ptr = (uint8_t *)UVM32_NULL; buf->len = 0; - return; + return false; } - while(vmst->memory[p] != terminator) { + while(vmst->memory[p] != '\0') { p++; if (p >= UVM32_MEMORY_SIZE) { setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; + buf->ptr = (uint8_t *)UVM32_NULL; buf->len = 0; - return; + return false; } } buf->ptr = &vmst->memory[ptrstart]; buf->len = p - ptrstart; + return true; } -#if 0 -static void 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_evt_syscall_buf_t *buf) { uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - if (ptrstart + len >= UVM32_MEMORY_SIZE) { + if ((ptrstart > UVM32_MEMORY_SIZE) || (ptrstart + len >= UVM32_MEMORY_SIZE)) { setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = (uint8_t *)UVM32_NULL; + buf->len = 0; + return false; } buf->ptr = &vmst->memory[ptrstart]; buf->len = len; + return true; } -#endif +void uvm32_clearError(uvm32_state_t *vmst) { + if (vmst->status == UVM32_STATUS_ERROR) { + // vm is in an error state, but user wants to continue + // most likely after UVM32_ERR_HUNG + vmst->status = UVM32_STATUS_PAUSED; + } +} uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { uint32_t num_instr = 0; -// uvm32_evt_syscall_buf_t b; if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); @@ -118,59 +148,30 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) while(vmst->status == UVM32_STATUS_RUNNING) { uint64_t elapsedUs = 1; uint32_t ret; - ret = MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1); + ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); if (3 == ret) { - const uint32_t syscall = vmst->core->regs[17]; // a7 - uint32_t value = vmst->core->regs[10]; // a0 - bool syscall_valid = false; + // 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; + vmst->core.pc += 4; switch(syscall) { // inbuilt syscalls case UVM32_SYSCALL_HALT: setStatus(vmst, UVM32_STATUS_ENDED); - syscall_valid = true; break; case UVM32_SYSCALL_YIELD: vmst->ioevt.typ = UVM32_EVT_YIELD; setStatus(vmst, UVM32_STATUS_PAUSED); - syscall_valid = true; break; - - // user defined syscalls default: - // search in mappings - for (int i=0;inumMappings;i++) { - if (syscall == vmst->mappings[i].syscall) { - // setup ioevt.data according to mapping typ - switch(vmst->mappings[i].typ) { - case UVM32_SYSCALL_TYP_VOID: - break; - case UVM32_SYSCALL_TYP_U32_WR: - vmst->ioevt.data.syscall.val.u32 = value; - break; - case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR: - get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.syscall.val.buf); - break; - case UVM32_SYSCALL_TYP_U32_RD: - // pass link to r1 for user function to update - vmst->ioevt.data.syscall.val.u32p = &vmst->core->regs[11]; - break; - } - vmst->ioevt.typ = UVM32_EVT_SYSCALL; - vmst->ioevt.data.syscall.code = vmst->mappings[i].code; - vmst->ioevt.data.syscall.typ = vmst->mappings[i].typ; - setStatus(vmst, UVM32_STATUS_PAUSED); - syscall_valid = true; - break; // stop searching - } - } - // no mapping found - if (!syscall_valid) { -printf("BADSYS %08x halt=%08x YIELD=%08x\n", syscall, UVM32_SYSCALL_HALT, UVM32_SYSCALL_YIELD); - setStatusErr(vmst, UVM32_ERR_BAD_SYSCALL); - } + // 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) { @@ -197,7 +198,7 @@ printf("BADSYS %08x halt=%08x YIELD=%08x\n", syscall, UVM32_SYSCALL_HALT, UVM32_ // an event is ready if (vmst->status == UVM32_STATUS_PAUSED) { // send back the built up event - memcpy(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); + UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); return num_instr; } else { if (vmst->status == UVM32_STATUS_ERROR) { @@ -214,4 +215,54 @@ bool uvm32_hasEnded(const uvm32_state_t *vmst) { return vmst->status == UVM32_STATUS_ENDED; } +static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + switch(arg) { + case ARG0: + return evt->data.syscall.params[0]; + break; + case ARG1: + return evt->data.syscall.params[1]; + break; + case RET: + return evt->data.syscall.ret; + break; + default: + // if something invalid is passed to arg, we should never crash + // return a pointer to something readable + setStatusErr(vmst, UVM32_ERR_ARGS); + garbage = 0; + return &garbage; + break; + } +} + +void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t val) { + *arg_to_ptr(vmst, evt, arg) = val; +} + +uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + return *arg_to_ptr(vmst, evt, arg); +} + +const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + uvm32_evt_syscall_buf_t scb; + if (get_safeptr_null_terminated(vmst, uvm32_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 + } else { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + garbage = 0; + return (const char *)&garbage; + } +} + +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 scb; + if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), uvm32_getval(vmst, evt, argLen), &scb)) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + garbage = 0; + scb.ptr = (uint8_t *)&garbage; + scb.len = 0; + } + return scb; +} diff --git a/host-arduino/uvm32.h b/host-arduino/uvm32.h index 40a5992..f12afb8 100644 --- a/host-arduino/uvm32.h +++ b/host-arduino/uvm32.h @@ -14,6 +14,7 @@ X(UVM32_ERR_HUNG) \ X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_STATE) \ + X(UVM32_ERR_ARGS) \ #define X(name) name, typedef enum { @@ -28,42 +29,17 @@ typedef enum { UVM32_EVT_END, } uvm32_evt_typ_t; -typedef enum { - UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, // data write from vm, NULL terminated string of bytes, in uvm32_evt_syscall_t.val.buf - UVM32_SYSCALL_TYP_VOID, // no data - UVM32_SYSCALL_TYP_U32_WR, // data write from vm, in uvm32_evt_syscall_t.val.u32 - UVM32_SYSCALL_TYP_U32_RD, // data read from vm, expects response in uvm32_evt_syscall_t.val.u32p -} uvm32_syscall_typ_t; - -typedef uint32_t uvm32_user_syscall_code_t; - -// user supplied mapping from syscall to typed syscall -typedef struct { - uint32_t syscall; - uvm32_user_syscall_code_t code; - uvm32_syscall_typ_t typ; -} uvm32_mapping_t; - -typedef struct { - uint8_t *ptr; - uint32_t len; -} uvm32_evt_syscall_buf_t; - -typedef struct { - uvm32_syscall_typ_t typ; - uvm32_user_syscall_code_t code; - union { - uvm32_evt_syscall_buf_t buf; - uint32_t u32; - uint32_t *u32p; - } val; -} uvm32_evt_syscall_t; - typedef struct { uvm32_err_t errcode; const char *errstr; } uvm32_evt_err_t; +typedef struct { + uint32_t code; // syscall number + uint32_t *ret; + uint32_t *params[2]; +} uvm32_evt_syscall_t; + typedef struct { uvm32_evt_typ_t typ; union { @@ -90,16 +66,33 @@ typedef enum { typedef struct { uvm32_status_t status; uvm32_err_t err; - struct MiniRV32IMAState* core; // points at end of memory + struct MiniRV32IMAState core; uint8_t memory[UVM32_MEMORY_SIZE]; uvm32_evt_t ioevt; // for building up in callbacks - const uvm32_mapping_t *mappings; - uint32_t numMappings; } uvm32_state_t; -void uvm32_init(uvm32_state_t *vmst, const uvm32_mapping_t *mappings, uint32_t numMappings); +void uvm32_init(uvm32_state_t *vmst); bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len); bool uvm32_hasEnded(const uvm32_state_t *vmst); uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); +// convenience for getptr +typedef struct { + uint8_t *ptr; + uint32_t len; +} uvm32_evt_syscall_buf_t; + +typedef enum { + ARG0, + ARG1, + RET +} uvm32_arg_t; + +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); +void uvm32_clearError(uvm32_state_t *vmst); + + #endif diff --git a/host-mini/host-mini.c b/host-mini/host-mini.c index d762504..e32ad0d 100644 --- a/host-mini/host-mini.c +++ b/host-mini/host-mini.c @@ -4,40 +4,30 @@ #include "uvm32.h" #include "../common/uvm32_common_custom.h" -uint8_t rom[] = { +uint8_t rom[] = { // mandel.bin 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, - 0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, - 0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, - 0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, - 0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, - 0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, - 0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, - 0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, - 0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, - 0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, - 0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, - 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, - 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, - 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, - 0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, - 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 -}; - -// Create an identifier for our host handler -// syscalls exposed to vm environement -typedef enum { - F_PUTC, - F_PRINTLN, -} f_code_t; - -// Map exposed syscalls to syscalls -const uvm32_mapping_t env[] = { - { .syscall = UVM32_SYSCALL_PRINTLN, .typ = UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, - { .syscall = UVM32_SYSCALL_PUTC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PUTC }, + 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, + 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, + 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, + 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, + 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, + 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, + 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, + 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, + 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, + 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, + 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x10, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, + 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x00 }; int main(int argc, char *argv[]) { @@ -45,7 +35,7 @@ int main(int argc, char *argv[]) { uvm32_evt_t evt; bool isrunning = true; - uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); + uvm32_init(&vmst); uvm32_load(&vmst, rom, sizeof(rom)); while(isrunning) { @@ -56,12 +46,16 @@ int main(int argc, char *argv[]) { isrunning = false; break; case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL - switch((f_code_t)evt.data.syscall.code) { - case F_PUTC: - printf("%c", evt.data.syscall.val.u32); + switch(evt.data.syscall.code) { + case UVM32_SYSCALL_PUTC: + printf("%c", uvm32_getval(&vmst, &evt, ARG0)); break; - case F_PRINTLN: - printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); + case UVM32_SYSCALL_PRINTLN: { + const char *str = uvm32_getcstr(&vmst, &evt, ARG0); + printf("%s\n", str); + } break; + default: + printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code); break; } break; diff --git a/host-parallel/host-parallel.c b/host-parallel/host-parallel.c index 539eed2..13cd69b 100644 --- a/host-parallel/host-parallel.c +++ b/host-parallel/host-parallel.c @@ -13,59 +13,46 @@ #define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM uint8_t rom[] = { // fib.bin - 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x40, 0x17, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, 0x05, 0x00, - 0x13, 0x85, 0x05, 0x00, 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, - 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00, 0x13, 0x04, 0x10, 0x00, - 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x26, 0x31, 0x01, - 0x23, 0x22, 0x51, 0x01, 0x23, 0x20, 0x61, 0x01, 0x23, 0x2e, 0x11, 0x00, - 0x23, 0x24, 0x41, 0x01, 0x13, 0x09, 0x05, 0x00, 0x93, 0x09, 0x00, 0x00, - 0x93, 0x04, 0x04, 0x00, 0x93, 0x0a, 0x20, 0x00, 0x13, 0x0b, 0x04, 0x00, - 0x63, 0x56, 0x89, 0x02, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, - 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, - 0x03, 0x2a, 0x81, 0x00, 0x83, 0x2a, 0x41, 0x00, 0x03, 0x2b, 0x01, 0x00, - 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x63, 0xd2, 0x8a, 0x02, - 0x33, 0x8a, 0x34, 0x01, 0x93, 0x05, 0x0a, 0x00, 0x13, 0x05, 0x40, 0x00, - 0xef, 0xf0, 0x9f, 0xf7, 0x93, 0x89, 0x04, 0x00, 0x13, 0x04, 0x14, 0x00, - 0x93, 0x04, 0x0a, 0x00, 0x6f, 0xf0, 0x5f, 0xfb, 0x93, 0x85, 0x09, 0x00, - 0x63, 0x04, 0x64, 0x01, 0x93, 0x85, 0x04, 0x00, 0x13, 0x05, 0x40, 0x00, - 0xef, 0xf0, 0x5f, 0xf5, 0x13, 0x8a, 0x04, 0x00, 0x6f, 0xf0, 0xdf, 0xfd, - 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, - 0x23, 0x26, 0x31, 0x01, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x28, 0x21, 0x01, - 0x93, 0x04, 0x05, 0x00, 0x93, 0x87, 0x05, 0x00, 0x13, 0x04, 0x06, 0x00, - 0x93, 0x09, 0x20, 0x00, 0x63, 0xd2, 0x99, 0x02, 0x33, 0x89, 0x87, 0x00, - 0x93, 0x05, 0x09, 0x00, 0x13, 0x05, 0x40, 0x00, 0xef, 0xf0, 0x1f, 0xf1, - 0x93, 0x07, 0x04, 0x00, 0x93, 0x84, 0xf4, 0xff, 0x13, 0x04, 0x09, 0x00, - 0x6f, 0xf0, 0x1f, 0xfe, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, + 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x16, 0xb7, 0x08, 0x00, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x93, 0x08, 0x40, 0x00, 0x73, 0x00, 0x00, 0x00, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x1c, 0x93, 0x08, 0x30, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00, + 0x13, 0x04, 0x10, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, + 0x23, 0x26, 0x31, 0x01, 0x23, 0x22, 0x51, 0x01, 0x23, 0x20, 0x61, 0x01, + 0x23, 0x2e, 0x11, 0x00, 0x23, 0x24, 0x41, 0x01, 0x13, 0x09, 0x05, 0x00, + 0x93, 0x09, 0x00, 0x00, 0x93, 0x04, 0x04, 0x00, 0x93, 0x0a, 0x20, 0x00, + 0x13, 0x0b, 0x04, 0x00, 0x63, 0x56, 0x89, 0x02, 0x83, 0x20, 0xc1, 0x01, + 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, + 0x83, 0x29, 0xc1, 0x00, 0x03, 0x2a, 0x81, 0x00, 0x83, 0x2a, 0x41, 0x00, + 0x03, 0x2b, 0x01, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, + 0x63, 0xd0, 0x8a, 0x02, 0x33, 0x8a, 0x34, 0x01, 0x13, 0x05, 0x0a, 0x00, + 0xef, 0xf0, 0xdf, 0xf6, 0x93, 0x89, 0x04, 0x00, 0x13, 0x04, 0x14, 0x00, + 0x93, 0x04, 0x0a, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x09, 0x00, + 0x63, 0x04, 0x64, 0x01, 0x13, 0x85, 0x04, 0x00, 0xef, 0xf0, 0xdf, 0xf4, + 0x13, 0x8a, 0x04, 0x00, 0x6f, 0xf0, 0x1f, 0xfe, 0x13, 0x01, 0x01, 0xfe, + 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x26, 0x31, 0x01, + 0x23, 0x2e, 0x11, 0x00, 0x23, 0x28, 0x21, 0x01, 0x93, 0x04, 0x05, 0x00, + 0x13, 0x04, 0x06, 0x00, 0x93, 0x09, 0x20, 0x00, 0x63, 0xd0, 0x99, 0x02, + 0x33, 0x89, 0x85, 0x00, 0x13, 0x05, 0x09, 0x00, 0xef, 0xf0, 0x1f, 0xf1, + 0x93, 0x05, 0x04, 0x00, 0x93, 0x84, 0xf4, 0xff, 0x13, 0x04, 0x09, 0x00, + 0x6f, 0xf0, 0x5f, 0xfe, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, - 0x23, 0x24, 0x81, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x04, 0x05, 0x00, - 0x13, 0x05, 0x40, 0x00, 0x23, 0x26, 0x11, 0x00, 0xef, 0xf0, 0x9f, 0xec, - 0x93, 0x05, 0x10, 0x00, 0x13, 0x05, 0x40, 0x00, 0xef, 0xf0, 0xdf, 0xeb, - 0x13, 0x05, 0x04, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, 0x20, 0xc1, 0x00, - 0x13, 0x06, 0x10, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x01, 0x01, 0x01, - 0x6f, 0xf0, 0x9f, 0xf5, 0xb7, 0x05, 0x00, 0x80, 0x13, 0x01, 0x01, 0xff, - 0x93, 0x85, 0x05, 0x1c, 0x13, 0x05, 0x30, 0x00, 0x23, 0x26, 0x11, 0x00, - 0xef, 0xf0, 0x9f, 0xe8, 0x13, 0x05, 0x80, 0x02, 0xef, 0xf0, 0x1f, 0xe9, - 0xb7, 0x05, 0x00, 0x80, 0x13, 0x05, 0x30, 0x00, 0x93, 0x85, 0xc5, 0x1c, - 0xef, 0xf0, 0x1f, 0xe7, 0x83, 0x20, 0xc1, 0x00, 0x13, 0x05, 0x80, 0x02, + 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x05, 0x00, 0x13, 0x05, 0x00, 0x00, + 0x23, 0x26, 0x11, 0x00, 0xef, 0xf0, 0xdf, 0xec, 0x13, 0x05, 0x10, 0x00, + 0xef, 0xf0, 0x5f, 0xec, 0x13, 0x05, 0x04, 0x00, 0x03, 0x24, 0x81, 0x00, + 0x83, 0x20, 0xc1, 0x00, 0x13, 0x06, 0x10, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x13, 0x01, 0x01, 0x01, 0x6f, 0xf0, 0x9f, 0xf6, 0x13, 0x01, 0x01, 0xff, + 0x37, 0x05, 0x00, 0x80, 0x23, 0x26, 0x11, 0x00, 0x13, 0x05, 0x45, 0x1c, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x30, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x05, 0x80, 0x02, 0xef, 0xf0, 0x5f, 0xea, 0x37, 0x05, 0x00, 0x80, + 0x13, 0x05, 0x05, 0x1d, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x30, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x13, 0x05, 0x80, 0x02, 0x13, 0x01, 0x01, 0x01, 0x6f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, 0x29, 0x20, 0x6c, 0x6f, - 0x6f, 0x70, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, 0x29, 0x20, 0x72, 0x65, - 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x00 -}; - -// Create an identifier for our host handler -// syscalls exposed to vm environement -typedef enum { - F_PRINTDEC, - F_PRINTLN, -} f_code_t; - -// Map exposed syscalls to syscalls -const uvm32_mapping_t env[] = { - { .syscall = UVM32_SYSCALL_PRINTLN, .typ = UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, - { .syscall = UVM32_SYSCALL_PRINTDEC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTDEC }, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, + 0x29, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, + 0x29, 0x20, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x00 }; int main(int argc, char *argv[]) { @@ -75,7 +62,7 @@ int main(int argc, char *argv[]) { int scheduler_index = 0; for (int i=0;i4iW$W delta 61 zcmbQnIE`_FtSW~ALoovbgE0Gt|H7;c43jwo!2HSWtPIIhSQ*kA7#JS6O!P9~n*ST9 K3=AgLNC5z|8xAS} diff --git a/precompiled/fib.bin b/precompiled/fib.bin index bdb49acffad70546f3f02fdb299692a981c6d026..a92bdfd9e535fa7c70320fa7342fac22de2ace47 100755 GIT binary patch delta 146 zcmcb>{D4_QSxu1PJ;MR9?Hmk@#S9QInU#TIGKT{Lh;Poy&>+mpDl<`~iRu2gi7Vuo z?thth!irITGBcwy=lwUrtO5-0Kg3V=XVkHkfoL=UYZPW}U;;|Kf*K%;EZ3}XkO3qw S%*e=?|3Ur-$lA$k7*znLktD|e delta 113 zcmaFBe1Ta-Sxu1PJwt=ob`A!{Vg?A9%;CTQVw9{iVv_$pag7|4{MU(R ztQg}bvokt#%D)w66<~P(VgBS`Mjc7%$s7h?O~R}VOhCa`P|Y%v7ch!3&HpiZ52FeI DCX^ml diff --git a/precompiled/helloworld.bin b/precompiled/helloworld.bin index 0b14b655ff17d9c8feadb36f06e90551044f64db..9fb626fbce2d58ea8e368e19dbdd1dfc5d52e539 100755 GIT binary patch delta 45 xcmXr8nINKJ&dSgr%*x6}F#`ibdIOLE0*}<3oP34y{Gyx`1^~AQ2@U`N delta 33 pcmcC9m>?n}%*wzpnSBAnWDW<0Vg?2VVOCLw$5LPar8h7z004622hjik diff --git a/precompiled/lissajous.bin b/precompiled/lissajous.bin index 4c2997d3ba7d3a313b5a88c2a9972ac6bc5158e2..8d2e57060631b13b65f111e2d67dac2363123e6c 100755 GIT binary patch delta 1408 zcmZ8h&u<$=6rSEZvk-A2LZLgI-M@lSZ&5P8z z`CxnLY7kB~rKOV~-1_LyF7J$Z@w&)v)>+G}i`GPa{d`Wa=j-C;W_>#R{a$+lNxp>Z z=0siobL#D4uM8Wawiqhdszk8s-8)PdY;Qf74ySH8ls@LUXKkJ>+dOl{=I%=|o;nrd z$wG_|z7^w%3o)LXvEaAhx464(@zk0n&5h?A{Tf;zL`DgDpWs{jySw+X`6NF^N{(K< zTanR3&(SyNRRvNs^OK|`KAWhs&p|isdF?l0J4;I3?GVwr?`MNX> zwJ%~r4JEk~ClN5$WX&SdMvxUJQiHu|l4&xkt7C>ULWA9b?I1k1(KwomGKUuQGyUVL$1>o-COhW;3;Vfa&DA!ZfBFaB@(UImUvhtqeUg6cTf zT?%1`tlpoBh^*^mIvn^_B>WglmSX}h2s=q*;-qi0OxYHhg3X5U8V&mv!)qe41&fVP xON{tYyh6{sSAs0P->ZbY{#&>E?!#{P5|;Y9NA}96ma-}TYe$y5{z|@0{{ypG&`SUS delta 1418 zcmZWpO>7%Q6rS1HKYMMputIRdc#@6lIF@=q=2)w0+)9Ngf_obEz%dGN1Ie}~{!8qb zXh7mZLlmhA5J*cvJ|SgAL#Py*w3l*At8l0g(yoOf;8OFAP1GP(8tu;e-uvG7-kaId zJK^lD_ig%Q>jPU3(N%eNY}9Con}vqFFgEcjzIGdKMJLkGNpR3!%UvNQ=0)a(cV#{} zSiTX26D=7rND%ISbYhox#@g|^$n4hHnpGETh5FXDtY9zH#l~)ZCOo{`DS+fFKpTa+ z{OkOi#eNz#Orv5rA5|%WNA0^Obiu*?gPCyZTbI&5xc6L)50zs)xfJ7xSK>T--rzOIiiLXa&+YK_iS z_4Tv*9ST;nchvRW_>0CU7H$quE}9|6UL-*WDOwEr>HBu%Ef3#Jff@j?Kgm(gN65Sl0Rn z#lAq?igjA~GdlhIwV!d=_jUevLcw0Ed%u?>2?6 z4qqL@iCcsURG|Vk5dgr~5%47f_EjqfqVoOZJT*yqdPyZSKwuy_#jT;T@pmuSUS3DF zd#Xwl^u0tUg;N%zyb72sZye|NScLzx{XqD(wm>F*78eOJ)3eK%)iXb@&)6`0In0G0=7Vpcf_az0~v0|SFcYEDkRLV12sP6`76O~X1D delta 200 zcmeBSdc(-CtR~3tp5Xw)b`A!{Vg`nZ;(GNbm^g*`m>H5!C9_TDV_=xf%D^DZ52Qhu zje$wHmF-`0(sCB#)~k%k@0K$O1LY@kurV0-zh~auf7KCa3WIR`euvHd=NXOp`58C2 z{%4+i^(cce>wD(OaYq>@w}~<2f4Kh}Xbw;x0|Qt+pB(4oTZexNvo2sTfD3ZWkbHcH j>AN{ALxV6YE8k=e1F+)s1_lO?)SR4rh4TEOoD>ECw1hao diff --git a/precompiled/rust-hello.bin b/precompiled/rust-hello.bin index b759e3d1e3bbcd79d2a7f47bc468863cf435319d..36a3e90ec3e52cad892b3ff4ed56f558b08b6d69 100755 GIT binary patch literal 127 zcmY#Z6J&VLaDZVu2Lod<0|UckHXvqSz+leK&>+nIoISaXooO<{9 literal 78 zcmY#Z6J&VLaDZVu2Lod<0|SFFD-g3SV3^F|z>v(r#sCo$Wq2%e^q)B^LxV6YE6ZdK Z1F(2{0|SFcYEDkRjzW2UQBH~?0{}?J5Jvz2 diff --git a/precompiled/self.bin b/precompiled/self.bin index 5d6602d9e070fb02d47d709b9fd02bcf2d61b9c0..cfb0bc0e051dbb1c501ab099b03263f6672ea48a 100755 GIT binary patch literal 5044 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 ZmRgVhHn;F&%l`iU1GTXO|K8tA{|7sT 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 au>8+T>X!dYZ1~!a9Xp!_cYbTfll1>kwvjUc diff --git a/precompiled/sketch.bin b/precompiled/sketch.bin index cc1a4df779256e71f3431c06dfa1a8dc7672ed28..b950f6f3c73a0117076a2ddf49bfec84606a8344 100755 GIT binary patch delta 118 zcmXSz!RVr_Cdlxfp@C&P2Lod<0|abmXJ}wv>drZtm4RV0hXVsh+?lAMXE|m>|jBApzEs|Ka{$0LVEQX#fBK diff --git a/precompiled/zigtris.bin b/precompiled/zigtris.bin index 9167e01722e14cc14420ae1d64cc269a89e4741d..a648f9542a49cbec3552023abc329d90ee08fd35 100755 GIT binary patch delta 2735 zcmZ`*e{2)?6@TA5e=qjgP8@$7Lri~UzBrH+I@SzaI#}H`20~~T4K}h)6HH#$4I!o5 zl!}(EKIGV0NI8gJ1XZcpLUCxjRb}!jBk|8=EKDmc1Jp|0s;vb#&8D$7(%Nd$x_Iw< zKBwulSvoy`-@DIy-|u^$_kDLg`vwIr-Q#EOOuL3R=E@rhT-bP7XeJ2mfun5zv!;0lTR6)c;VaPt zknAI8em_gL(Em<=axY;GQ{3huI^F9y-^5)p=M;+T?vS}eF zN^Q$&7i({G?Kgai4Y*j?hbd)=&wUg6ao3{Ny=W@2toe`wpWu^|-|X5$g0_u3SP%U}NB=l){NH}<9>N>@q!0l?^st^88IxXkTBU}-j(s#eC z7iajYnqg1kYR@LdaIYtOqm^s#RdWfSio+4w=cu|Uh1)Q~Gt9!hyi2jS;7R`NEV)AO zF8DD}RffPcfLp@PEn~%0)aYVS!PyeGHd}><0}gX?TGyregpYKii*>L?=%hQaSZ8k* znx7dfZ(+u*Ut&gEpK8RvrItIu^kCuqSv0kqv-2(kPvfYM32hh1Jk~;kE?o*DQzgcG zB!SSM{&hJaGjRVc$sQt>$HU6)Ky*1d{3!w9w9HhAPe76gs&;I=CK^J3T0b-LGs9v> zf#D8~iGc!SJa|*XKqtI_{J2tngd88xEs@u$XeqxDKd#gmZ5NMHo`~7_bi)HmDu!O~ zZ82(zp*4_+a4`HmRgtd~)@;G;-md1Fo-b z`xXuu#YW$%EK6>sLj?j?<)KiY1EB*K!NY)o5W~ih$2h?)HC?=FtT+xO&k;y|mG<1! z<|(I02I-K6Uah=|{*R2z3vWN5lJba;ZiI@$yJS*uQQgs}frIhNzLK_Pa0nsBa`PGG zdtNd0F`o0aXJ;^}PJzd?Qy3s_NuDs_{&aHc5{a45$gFxB>nsH&hRj@yebFfjpRD7A=_((yguehap*; zT;eW8G^QYfe+s-p9sgYL6uXH1jYjA1-H-=mpJZZmkMaFs_lmqM3ibQQ{1rag7}L%B zl2kXrr!S&S;7cyJ;DEz)SuFyq9aHu5yoiA&0&iOYuWpjyT2o}*$l7PYch$j@_!9}< zx&VHr$>_b}g7-RLFOSQ*%Zw!H5q#uYEhh6mMmuq*PA3L(~LV#)#AuJ?g=exwkn~-w8IRiDeb%Q@lY27 zJQrH7KnSmg;tI8Bs5!2{IDWA?eyeRcrK8| z4`56_!f>n?L_mj(qI+`13)S2Ue&%VD6!(?)UheU{adkMUpzn zzkj*59-=ih6#UFxmq<3z`W#6j41AaDbV9hz#r=--G+$wYWSC?d>1i5(MUvL{f#SY+ zhNPDaQzQcp{dL0!EV6n7m~Rcx Jxg^H@{Revc2%G=_ delta 2771 zcmZ`*4QyLi6+ZVpU&p_R?Q1)2)Ry2EyJ<*ZP_3q0x4yABok_b*rA37e5oD_U2rbiC z#=2?qE3Olzaap=k+CisDY3gocRnRcO6BWW8GE8ksI;2UUO-fd;3eq~LgfbN^lR5YK zy(B|m$vW?zbI*6rJLfy+zJkH7Wyky3FOTkQQ;gQ54}R{;pO#Z#V7@vhoI11$fCu0Y z4<|_{c;2$@oe|D^Et~q#(KoiIEWPP79o7*NItXPM)?8yx`2 zeggA)>&XZ`ZvrT%$!9b@i`&@b_b#3Ym>%CWJznA)Bnsx;AchIewCT^7wsEpK1Li4> zkW-egzT2{|Tk-W-zQzI6l~%X4)3_T4R;_lTp|oXT$aFu(!=HtJ$8xINW!Xc*`RJVbdj%(GeijKHP?eF>M|D^MXx$`(z1gG@vm-XUx zoKZ9E=XjH22dm?gj_mqEt|P7H;%*fWg(=QabyW&?W0+@H0iWbuik`;P{5x6K0Q?ZB zB12$kzzf353tH#vQLT$b1!t{R?)!y<10Lc8XzSqX!cDr-)w;@E!b7^lt98Z>;q)@C z`wL9_;;l?;?^m_heQG&jGUm5gt>YqSiFZNk90RTEBxoJ4DO%#3(xAualEup@9Cb6{ z8~~Z!Mo<-(JWLS9YEj-RX@%a@o8`F7%l=KtUZR`BQ7?A@k?8cH6Z^aU6;2OQcdUQkoTIjwU7N{-`@yoX|LX?K)$ zl74EA;k2{7gPuo5mW1nGKt*?m#r05Y=AH80(JHzup4^Wv*-&5-L5#U`OPL?mnLmDZrfpL-wqbMNQQq(FT+ z54mC$w9}JIT8t{)@gY_G9AqBZ!qn=YKnx8*P2r2u5B&hOC$`ij9o2ZMAb2+AEI&i9 za>QTA{QcFAG8sgk7jiu*pLXizBi7uwv6)Y^<$cfI%uJe?U+vUwezkhZ zZ>J^$yj>n7xWwZ=i|hZg?IvcEb$IE9=P{OmeVE_n7yO`N75EmUXIWM?evnzb+>7JB z4L$nbG?4lWt1`rh@PK{!0D(YyZn^2Jdgyc3rZt3XNcuev^ zrZ7o=!AItEapF@VPyDJvF5zYB#*z8P1bGeAGsB?8&JoWRmT*lVk)1o%0M9R}sT9#{ zks&vrHeY;8MB>mG6nrC9ncz5Gc-%ZnEmG4&ra|nThFlLLpFmR$ra!jXRSU$cCc7FP z-YctIbMiOUqTin@Uh;`Z1SF=0zY7Fe1!n^r6}<&-1Y(NuJgy7I6nz}^V5}K_ViLsS z7d{%?py*>b9*p(Cs7cv%JZ#fb>zq~xL8t281k{Trm2B$F1zYdd>uUTwljNd1p`e{D zDzq!GlJCp*K#I#gPd-7}bUF*U_#d0_rzt4$-_T{tT{I7+zK*@2;~|)nMW_Y%Wf_E; z;sv}GitVm^WG7<*u7&k0Un1WvF0Mez1!{t%;Eiw0Ob zrNoUNhBt1lye+%{+L@;qj-^5PJdgp8BUijw&;7zHchPwCSO{=Yu1ow|Ofxv%6N#q7 zz9rFi3v51|X1Prl>T1{Fddahuq1Qp}>Y$rf-t8WOSiXg-n&GhnFefOEeNwNzh`)*) zU{lx~eVR>`UWtBJUH>UMBx(I^ zxIzYbzzd{sk(?muBiTt(0{@=u^1uwO<-RGB@_>&?Ph^4hmj0_GWx$VQ?n_wD~= R;lOQRoZCn5C4H>le*k_sCTaix diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index e865c53..6c0f8ab 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -7,6 +7,9 @@ #define UVM32_NULL (void *)0 +// 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; + #ifndef UVM32_MEMCPY #define UVM32_MEMCPY uvm32_memcpy void uvm32_memcpy(void *dst, const void *src, int len) { @@ -52,26 +55,26 @@ static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) { } static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) { - setStatus(vmst, UVM32_STATUS_ERROR); - vmst->err = err; + // if already in error state, stay in the first error state + if (vmst->status != UVM32_STATUS_ERROR) { + setStatus(vmst, UVM32_STATUS_ERROR); + vmst->err = err; + } } -void uvm32_init(uvm32_state_t *vmst, const uvm32_mapping_t *mappings, uint32_t numMappings) { +void uvm32_init(uvm32_state_t *vmst) { + UVM32_MEMSET(vmst, 0x00, sizeof(uvm32_state_t)); vmst->status = UVM32_STATUS_PAUSED; - UVM32_MEMSET(vmst->memory, 0x00, UVM32_MEMORY_SIZE); vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET; // https://projectf.io/posts/riscv-cheat-sheet/ // setup stack pointer // la sp, _sstack // addi sp,sp,-16 - vmst->core.regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)) - 16; + vmst->core.regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) - 16; vmst->core.regs[10] = 0x00; //hart ID vmst->core.regs[11] = 0; vmst->core.extraflags |= 3; // Machine-mode. - - vmst->mappings = mappings; - vmst->numMappings = numMappings; } bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { @@ -85,48 +88,52 @@ bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { } // Read C-string up to terminator and return len,ptr -static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_syscall_buf_t *buf) { +bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_syscall_buf_t *buf) { uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; uint32_t p = ptrstart; if (p >= UVM32_MEMORY_SIZE) { setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = UVM32_NULL; + buf->ptr = (uint8_t *)UVM32_NULL; buf->len = 0; - return; + return false; } - while(vmst->memory[p] != terminator) { + while(vmst->memory[p] != '\0') { p++; if (p >= UVM32_MEMORY_SIZE) { setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = UVM32_NULL; + buf->ptr = (uint8_t *)UVM32_NULL; buf->len = 0; - return; + return false; } } buf->ptr = &vmst->memory[ptrstart]; buf->len = p - ptrstart; + return true; } -#if 0 -static void 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_evt_syscall_buf_t *buf) { uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - if (ptrstart + len >= UVM32_MEMORY_SIZE) { + if ((ptrstart > UVM32_MEMORY_SIZE) || (ptrstart + len >= UVM32_MEMORY_SIZE)) { setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = (uint8_t *)UVM32_NULL; + buf->len = 0; + return false; } buf->ptr = &vmst->memory[ptrstart]; buf->len = len; + return true; } -#endif - - -uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { - uint32_t num_instr = 0; +void uvm32_clearError(uvm32_state_t *vmst) { if (vmst->status == UVM32_STATUS_ERROR) { // vm is in an error state, but user wants to continue // most likely after UVM32_ERR_HUNG vmst->status = UVM32_STATUS_PAUSED; } +} + +uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { + uint32_t num_instr = 0; if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); @@ -142,9 +149,8 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) 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 - uint32_t value = vmst->core.regs[10]; // a0 - bool syscall_valid = false; // on exception we should jump to mtvec, but we handle directly // and skip over the ecall instruction vmst->core.pc += 4; @@ -152,46 +158,19 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) // inbuilt syscalls case UVM32_SYSCALL_HALT: setStatus(vmst, UVM32_STATUS_ENDED); - syscall_valid = true; break; case UVM32_SYSCALL_YIELD: vmst->ioevt.typ = UVM32_EVT_YIELD; setStatus(vmst, UVM32_STATUS_PAUSED); - syscall_valid = true; break; - - // user defined syscalls default: - // search in mappings - for (int i=0;inumMappings;i++) { - if (syscall == vmst->mappings[i].syscall) { - // setup ioevt.data according to mapping typ - switch(vmst->mappings[i].typ) { - case UVM32_SYSCALL_TYP_VOID: - break; - case UVM32_SYSCALL_TYP_U32_WR: - vmst->ioevt.data.syscall.val.u32 = value; - break; - case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR: - get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.syscall.val.buf); - break; - case UVM32_SYSCALL_TYP_U32_RD: - // pass link to r1 for user function to update - vmst->ioevt.data.syscall.val.u32p = &vmst->core.regs[11]; - break; - } - vmst->ioevt.typ = UVM32_EVT_SYSCALL; - vmst->ioevt.data.syscall.code = vmst->mappings[i].code; - vmst->ioevt.data.syscall.typ = vmst->mappings[i].typ; - setStatus(vmst, UVM32_STATUS_PAUSED); - syscall_valid = true; - break; // stop searching - } - } - // no mapping found - if (!syscall_valid) { - setStatusErr(vmst, UVM32_ERR_BAD_SYSCALL); - } + // 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) { @@ -235,4 +214,54 @@ bool uvm32_hasEnded(const uvm32_state_t *vmst) { return vmst->status == UVM32_STATUS_ENDED; } +static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + switch(arg) { + case ARG0: + return evt->data.syscall.params[0]; + break; + case ARG1: + return evt->data.syscall.params[1]; + break; + case RET: + return evt->data.syscall.ret; + break; + default: + // if something invalid is passed to arg, we should never crash + // return a pointer to something readable + setStatusErr(vmst, UVM32_ERR_ARGS); + garbage = 0; + return &garbage; + break; + } +} + +void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t val) { + *arg_to_ptr(vmst, evt, arg) = val; +} + +uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + return *arg_to_ptr(vmst, evt, arg); +} + +const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { + uvm32_evt_syscall_buf_t scb; + if (get_safeptr_null_terminated(vmst, uvm32_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 + } else { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + garbage = 0; + return (const char *)&garbage; + } +} + +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 scb; + if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), uvm32_getval(vmst, evt, argLen), &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 e6747fb..3e76d68 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -14,6 +14,7 @@ X(UVM32_ERR_HUNG) \ X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_STATE) \ + X(UVM32_ERR_ARGS) \ #define X(name) name, typedef enum { @@ -28,42 +29,17 @@ typedef enum { UVM32_EVT_END, } uvm32_evt_typ_t; -typedef enum { - UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, // data write from vm, NULL terminated string of bytes, in uvm32_evt_syscall_t.val.buf - UVM32_SYSCALL_TYP_VOID, // no data - UVM32_SYSCALL_TYP_U32_WR, // data write from vm, in uvm32_evt_syscall_t.val.u32 - UVM32_SYSCALL_TYP_U32_RD, // data read from vm, expects response in uvm32_evt_syscall_t.val.u32p -} uvm32_syscall_typ_t; - -typedef uint32_t uvm32_user_syscall_code_t; - -// user supplied mapping from syscall to typed syscall -typedef struct { - uint32_t syscall; - uvm32_user_syscall_code_t code; - uvm32_syscall_typ_t typ; -} uvm32_mapping_t; - -typedef struct { - uint8_t *ptr; - uint32_t len; -} uvm32_evt_syscall_buf_t; - -typedef struct { - uvm32_syscall_typ_t typ; - uvm32_user_syscall_code_t code; - union { - uvm32_evt_syscall_buf_t buf; - uint32_t u32; - uint32_t *u32p; - } val; -} uvm32_evt_syscall_t; - typedef struct { uvm32_err_t errcode; const char *errstr; } uvm32_evt_err_t; +typedef struct { + uint32_t code; // syscall number + uint32_t *ret; + uint32_t *params[2]; +} uvm32_evt_syscall_t; + typedef struct { uvm32_evt_typ_t typ; union { @@ -93,13 +69,30 @@ typedef struct { struct MiniRV32IMAState core; uint8_t memory[UVM32_MEMORY_SIZE]; uvm32_evt_t ioevt; // for building up in callbacks - const uvm32_mapping_t *mappings; - uint32_t numMappings; } uvm32_state_t; -void uvm32_init(uvm32_state_t *vmst, const uvm32_mapping_t *mappings, uint32_t numMappings); +void uvm32_init(uvm32_state_t *vmst); bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len); bool uvm32_hasEnded(const uvm32_state_t *vmst); uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); +// convenience for getptr +typedef struct { + uint8_t *ptr; + uint32_t len; +} uvm32_evt_syscall_buf_t; + +typedef enum { + ARG0, + ARG1, + RET +} uvm32_arg_t; + +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); +void uvm32_clearError(uvm32_state_t *vmst); + + #endif