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.
This commit is contained in:
Toby Jaffey 2025-12-09 21:51:35 +00:00
parent f046a590c0
commit 76fba39a21
28 changed files with 543 additions and 537 deletions

104
README.md
View file

@ -56,7 +56,7 @@ uint8_t bytecode[] = { /* ... */ }; // some compiled bytecode
uvm32_state_t vmst; // execution state of the vm uvm32_state_t vmst; // execution state of the vm
uvm32_evt_t evt; // events passed from vm to host 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_load(&vmst, bytecode, sizeof(bytecode)); // load the bytecode
uvm32_run(&vmst, &evt, 100); // run up to 100 instructions 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 ## 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. uvm32 is always in one of 4 states, paused, running, ended or error.
@ -101,42 +101,48 @@ stateDiagram
## Boot ## 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 ## syscall ABI
All communication between bytecode and the vm host is performed via syscalls. 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) [target.h](common/uvm32_target.h#L12)
```c ```c
static uint32_t syscall(uint32_t id, uint32_t param) { static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) {
register uint32_t a0 asm("a0") = (uint32_t)(param); register uint32_t a0 asm("a0") = (uint32_t)(param1);
register uint32_t a1 asm("a1"); register uint32_t a1 asm("a1") = (uint32_t)(param2);
register uint32_t a2 asm("a2");
register uint32_t a7 asm("a7") = (uint32_t)(id); register uint32_t a7 asm("a7") = (uint32_t)(id);
asm volatile ( asm volatile (
"ecall" "ecall"
: "=r"(a1) // output : "=r"(a2) // output
: "r"(a0), "r"(a7) // input : "r"(a7), "r"(a0), "r"(a1) // input
: "memory" : "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. 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 ## syscalls
There are two system syscalls used by uvm32, `halt()` and `yield()`. 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()`. 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.
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.
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) 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 uint8_t rom[] = { // mandel.bin
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01,
0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00,
0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff,
0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01,
0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6,
0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09,
0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00,
0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00,
0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04,
0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00,
0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02,
0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01,
0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03,
0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00,
0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00,
0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80,
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 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,
// Create an identifier for our host handler 0x72, 0x6c, 0x64, 0x00
// 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 },
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -190,7 +186,7 @@ int main(int argc, char *argv[]) {
uvm32_evt_t evt; uvm32_evt_t evt;
bool isrunning = true; bool isrunning = true;
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst);
uvm32_load(&vmst, rom, sizeof(rom)); uvm32_load(&vmst, rom, sizeof(rom));
while(isrunning) { while(isrunning) {
@ -201,12 +197,16 @@ int main(int argc, char *argv[]) {
isrunning = false; isrunning = false;
break; break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PUTC: case UVM32_SYSCALL_PUTC:
printf("%c", evt.data.syscall.val.u32); printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break; break;
case F_PRINTLN: case UVM32_SYSCALL_PRINTLN: {
printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); 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;
} }
break; break;

View file

@ -1,8 +1,6 @@
#include "uvm32_target.h" #include "uvm32_target.h"
void main(void) { void main(void) {
for (int i=0;i<10;i++) { println("Hello world");
printdec(i);
}
} }

View file

@ -11,13 +11,13 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
// startup code // startup code
global_asm!(include_str!("../../crt0.S"), UVM32_SYSCALL_HALT = const UVM32_SYSCALL_HALT); 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; let mut value;
unsafe { unsafe {
asm!("ecall", asm!("ecall",
in("a0") n, in("a0") param1, in("a1") param2,
in("a7") id, in("a7") id,
lateout("a1") value, lateout("a2") value,
); );
} }
return value; return value;
@ -25,17 +25,18 @@ fn syscall(id: u32, n: u32) -> u32 {
fn println(message: &str) { fn println(message: &str) {
let addr_value = message.as_ptr() as u32; 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) { fn printdec(n: u32) {
syscall(UVM32_SYSCALL_PRINTDEC, n); syscall(UVM32_SYSCALL_PRINTDEC, n, 0);
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn main() { pub extern "C" fn main() {
for i in 0..10 { for i in 0..10 {
printdec(i); printdec(i);
println("\0");
} }
println("Hello, world!\0"); println("Hello, world!\0");
} }

View file

@ -4,38 +4,28 @@
uint8_t rom[] = { // mandel.bin uint8_t rom[] = { // mandel.bin
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01,
0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00,
0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff,
0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01,
0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6,
0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09,
0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00,
0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00,
0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04,
0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00,
0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02,
0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01,
0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03,
0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00,
0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00,
0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80,
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 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,
// Create an identifier for our host handler 0x72, 0x6c, 0x64, 0x00
// 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 },
}; };
void main(void) { void main(void) {
@ -43,7 +33,7 @@ void main(void) {
uvm32_evt_t evt; uvm32_evt_t evt;
bool isrunning = true; bool isrunning = true;
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst);
uvm32_load(&vmst, rom, sizeof(rom)); uvm32_load(&vmst, rom, sizeof(rom));
while(isrunning) { while(isrunning) {
@ -54,15 +44,16 @@ void main(void) {
isrunning = false; isrunning = false;
break; break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PUTC: case UVM32_SYSCALL_PUTC:
putc(evt.data.syscall.val.u32); putc(uvm32_getval(&vmst, &evt, ARG0));
break; break;
case F_PRINTLN: case UVM32_SYSCALL_PRINTLN: {
for (int i=0;i<evt.data.syscall.val.buf.len;i++) { const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
putc(evt.data.syscall.val.buf.ptr[i]); println(str);
} } break;
putc('\n'); default:
println("Unhandled syscall");
break; break;
} }
break; break;
@ -74,3 +65,4 @@ void main(void) {
} }
} }
} }

View file

@ -9,6 +9,7 @@ uint32_t count;
bool loop(void) { bool loop(void) {
printdec(count); printdec(count);
println("");
if (count++ >= 10) { if (count++ >= 10) {
return false; return false;
} else { } else {

View file

@ -4,25 +4,25 @@ const uvm32 = @cImport({
}); });
const std = @import("std"); 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; var val: u32 = undefined;
asm volatile ("ecall" asm volatile ("ecall"
: [val] "={a1}" (val), : [val] "={a1}" (val),
: [param] "{a0}" (param), : [param1] "{a0}" (param1), [param2] "{a1}" (param2),
[id] "{a7}" (id), [id] "{a7}" (id),
: .{ .memory = true }); : .{ .memory = true });
return val; return val;
} }
pub inline fn println(val: [:0]const u8) void { 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 { 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 { pub inline fn putc(c:u8) void {
_ = syscall(uvm32.UVM32_SYSCALL_PUTC, c); _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c, 0);
} }

View file

@ -4,18 +4,18 @@ const uvm32 = @cImport({
}); });
const std = @import("std"); 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; var val: u32 = undefined;
asm volatile ("ecall" asm volatile ("ecall"
: [val] "={a1}" (val), : [val] "={a2}" (val),
: [param] "{a0}" (param), : [param1] "{a0}" (param1), [param2] "{a1}" (param2),
[id] "{a7}" (id), [id] "{a7}" (id),
: .{ .memory = true }); : .{ .memory = true });
return val; return val;
} }
pub inline fn getc() ?u8 { 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) { if (key == 0xFFFFFFFF) {
return null; return null;
} else { } else {
@ -24,7 +24,7 @@ pub inline fn getc() ?u8 {
} }
pub inline fn millis() u32 { 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 // 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); @memcpy(termination_buf[0..m.len], m);
termination_buf[m.len] = 0; termination_buf[m.len] = 0;
const s = termination_buf[0..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 { pub inline fn println(m: []const u8) void {
@memcpy(termination_buf[0..m.len], m); @memcpy(termination_buf[0..m.len], m);
termination_buf[m.len] = 0; termination_buf[m.len] = 0;
const s = termination_buf[0..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 { 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 { pub inline fn putc(c:u8) void {
_ = syscall(uvm32.UVM32_SYSCALL_PUTC, c); _ = syscall(uvm32.UVM32_SYSCALL_PUTC, c, 0);
} }

View file

@ -8,4 +8,5 @@
#define UVM32_SYSCALL_PRINTDEC 0x00000004 #define UVM32_SYSCALL_PRINTDEC 0x00000004
#define UVM32_SYSCALL_PRINTHEX 0x00000005 #define UVM32_SYSCALL_PRINTHEX 0x00000005
#define UVM32_SYSCALL_MILLIS 0x00000006 #define UVM32_SYSCALL_MILLIS 0x00000006
#define UVM32_SYSCALL_PRINTBUF 0x00000007

View file

@ -32,30 +32,32 @@ size_assert(int32_t, 4);
size_assert(int16_t, 2); size_assert(int16_t, 2);
size_assert(int8_t, 1); size_assert(int8_t, 1);
static uint32_t syscall(uint32_t id, uint32_t param) { static uint32_t syscall(uint32_t id, uint32_t param1, uint32_t param2) {
register uint32_t a0 asm("a0") = (uint32_t)(param); register uint32_t a0 asm("a0") = (uint32_t)(param1);
register uint32_t a1 asm("a1"); register uint32_t a1 asm("a1") = (uint32_t)(param2);
register uint32_t a2 asm("a2");
register uint32_t a7 asm("a7") = (uint32_t)(id); register uint32_t a7 asm("a7") = (uint32_t)(id);
asm volatile ( asm volatile (
"ecall" "ecall"
: "=r"(a1) // output : "=r"(a2) // output
: "r"(a0), "r"(a7) // input : "r"(a7), "r"(a0), "r"(a1) // input
: "memory" : "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 println(x) syscall_cast(UVM32_SYSCALL_PRINTLN, x, 0)
#define print(x) syscall_cast(UVM32_SYSCALL_PRINT, x) #define print(x) syscall_cast(UVM32_SYSCALL_PRINT, x, 0)
#define printdec(x) syscall_cast(UVM32_SYSCALL_PRINTDEC, x) #define printdec(x) syscall_cast(UVM32_SYSCALL_PRINTDEC, x, 0)
#define printhex(x) syscall_cast(UVM32_SYSCALL_PRINTHEX, x) #define printhex(x) syscall_cast(UVM32_SYSCALL_PRINTHEX, x, 0)
#define millis() syscall_cast(UVM32_SYSCALL_MILLIS, 0) #define millis() syscall_cast(UVM32_SYSCALL_MILLIS, 0, 0)
#define putc(x) syscall_cast(UVM32_SYSCALL_PUTC, x) #define putc(x) syscall_cast(UVM32_SYSCALL_PUTC, x, 0)
#define getc() syscall_cast(UVM32_SYSCALL_GETC, 0) #define getc() syscall_cast(UVM32_SYSCALL_GETC, 0, 0)
#define yield() syscall_cast(UVM32_SYSCALL_YIELD, 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" #include "uvm32_common_custom.h"

View file

@ -1,11 +1,12 @@
// Definitions needed by both host and target // Definitions needed by both host and target
// syscalls for exposed host functions, start at 0 // 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_GETC 0x00000001
#define UVM32_SYSCALL_PRINT 0x00000002 #define UVM32_SYSCALL_PRINT 0x00000002
#define UVM32_SYSCALL_PRINTLN 0x00000003 #define UVM32_SYSCALL_PRINTLN 0x00000003
#define UVM32_SYSCALL_PRINTD 0x00000004 #define UVM32_SYSCALL_PRINTDEC 0x00000004
#define UVM32_SYSCALL_PRINTX 0x00000005 #define UVM32_SYSCALL_PRINTHEX 0x00000005
#define UVM32_SYSCALL_MILLIS 0x00000006 #define UVM32_SYSCALL_MILLIS 0x00000006
#define UVM32_SYSCALL_PRINTBUF 0x00000007

View file

@ -2,43 +2,30 @@
#include "uvm32.h" #include "uvm32.h"
#include "common/uvm32_common_custom.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[] = { uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01,
0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00,
0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff,
0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01,
0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6,
0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09,
0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00,
0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00,
0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04,
0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00,
0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02,
0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01,
0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03,
0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00,
0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00,
0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80,
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 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,
// Create an identifier for our host handler 0x72, 0x6c, 0x64, 0x00
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 },
}; };
uvm32_state_t vmst; uvm32_state_t vmst;
@ -69,18 +56,17 @@ void loop(void) {
isrunning = false; isrunning = false;
break; break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PRINTDEC: case UVM32_SYSCALL_PUTC:
Serial.println(evt.data.syscall.val.u32); Serial.print((char)uvm32_getval(&vmst, &evt, ARG0));
break; break;
case F_PUTC: case UVM32_SYSCALL_PRINTLN: {
Serial.print((char)evt.data.syscall.val.u32); const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
break; Serial.print(str);
case F_PRINTLN:
for (int i=0;i<evt.data.syscall.val.buf.len;i++) {
Serial.print((char)evt.data.syscall.val.buf.ptr[i]);
}
Serial.println(""); Serial.println("");
} break;
default:
Serial.println("Unhandled syscall");
break; break;
} }
break; break;
@ -95,7 +81,7 @@ void loop(void) {
} else { } else {
Serial.println("Starting VM"); Serial.println("Starting VM");
// setup vm // setup vm
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst);
uvm32_load(&vmst, rom, sizeof(rom)); uvm32_load(&vmst, rom, sizeof(rom));
isrunning = true; isrunning = true;
delay(2000); delay(2000);

View file

@ -1,13 +1,37 @@
#include "config.h" #include "config.h"
#define MINIRV32_IMPLEMENTATION #define MINIRV32_IMPLEMENTATION
#include "uvm32.h" #include "uvm32.h"
#include <stdio.h>
#include <string.h>
#ifndef UVM32_MEMORY_SIZE #ifndef UVM32_MEMORY_SIZE
#error Define UVM32_MEMORY_SIZE #error Define UVM32_MEMORY_SIZE
#endif #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" #include "mini-rv32ima.h"
#define X(name) #name, #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) { static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) {
// if already in error state, stay in the first error state
if (vmst->status != UVM32_STATUS_ERROR) {
setStatus(vmst, UVM32_STATUS_ERROR); setStatus(vmst, UVM32_STATUS_ERROR);
vmst->err = err; vmst->err = err;
}
} }
void uvm32_init(uvm32_state_t *vmst, 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; vmst->status = UVM32_STATUS_PAUSED;
memset(vmst->memory, 0x00, UVM32_MEMORY_SIZE); vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET;
// 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;
// https://projectf.io/posts/riscv-cheat-sheet/ // https://projectf.io/posts/riscv-cheat-sheet/
// setup stack pointer // setup stack pointer
// la sp, _sstack // la sp, _sstack
// addi sp,sp,-16 // addi sp,sp,-16
vmst->core->regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE - 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[10] = 0x00; //hart ID
vmst->core->regs[11] = 0; vmst->core.regs[11] = 0;
vmst->core->extraflags |= 3; // Machine-mode. vmst->core.extraflags |= 3; // Machine-mode.
vmst->mappings = mappings;
vmst->numMappings = numMappings;
} }
bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { 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) {
if (len > UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)) {
// too big // too big
return false; return false;
} }
memcpy(vmst->memory, rom, len); UVM32_MEMCPY(vmst->memory, rom, len);
return true; return true;
} }
// Read C-string up to terminator and return len,ptr // 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 ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t p = ptrstart; uint32_t p = ptrstart;
if (p >= UVM32_MEMORY_SIZE) { if (p >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = NULL; buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0; buf->len = 0;
return; return false;
} }
while(vmst->memory[p] != terminator) { while(vmst->memory[p] != '\0') {
p++; p++;
if (p >= UVM32_MEMORY_SIZE) { if (p >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = NULL; buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0; buf->len = 0;
return; return false;
} }
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->memory[ptrstart];
buf->len = p - ptrstart; buf->len = p - ptrstart;
return true;
} }
#if 0 bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_syscall_buf_t *buf) {
static void 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; 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); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0;
return false;
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->memory[ptrstart];
buf->len = len; 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 uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0; uint32_t num_instr = 0;
// uvm32_evt_syscall_buf_t b;
if (vmst->status != UVM32_STATUS_PAUSED) { if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY); 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) { while(vmst->status == UVM32_STATUS_RUNNING) {
uint64_t elapsedUs = 1; uint64_t elapsedUs = 1;
uint32_t ret; uint32_t ret;
ret = MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1); ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1);
if (3 == ret) { if (3 == ret) {
const uint32_t syscall = vmst->core->regs[17]; // a7 // Fetch registers used by syscall
uint32_t value = vmst->core->regs[10]; // a0 const uint32_t syscall = vmst->core.regs[17]; // a7
bool syscall_valid = false;
// on exception we should jump to mtvec, but we handle directly // on exception we should jump to mtvec, but we handle directly
// and skip over the ecall instruction // and skip over the ecall instruction
vmst->core->pc += 4; vmst->core.pc += 4;
switch(syscall) { switch(syscall) {
// inbuilt syscalls // inbuilt syscalls
case UVM32_SYSCALL_HALT: case UVM32_SYSCALL_HALT:
setStatus(vmst, UVM32_STATUS_ENDED); setStatus(vmst, UVM32_STATUS_ENDED);
syscall_valid = true;
break; break;
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
vmst->ioevt.typ = UVM32_EVT_YIELD; vmst->ioevt.typ = UVM32_EVT_YIELD;
setStatus(vmst, UVM32_STATUS_PAUSED); setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true;
break; break;
// user defined syscalls
default: default:
// search in mappings // user defined syscalls
for (int i=0;i<vmst->numMappings;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.typ = UVM32_EVT_SYSCALL;
vmst->ioevt.data.syscall.code = vmst->mappings[i].code; vmst->ioevt.data.syscall.code = syscall;
vmst->ioevt.data.syscall.typ = vmst->mappings[i].typ; 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); 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);
}
break; break;
} }
} else if (ret != 0) { } 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 // an event is ready
if (vmst->status == UVM32_STATUS_PAUSED) { if (vmst->status == UVM32_STATUS_PAUSED) {
// send back the built up event // send back the built up event
memcpy(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t));
return num_instr; return num_instr;
} else { } else {
if (vmst->status == UVM32_STATUS_ERROR) { if (vmst->status == UVM32_STATUS_ERROR) {
@ -214,4 +215,54 @@ bool uvm32_hasEnded(const uvm32_state_t *vmst) {
return vmst->status == UVM32_STATUS_ENDED; return vmst->status == UVM32_STATUS_ENDED;
} }
static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) {
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;
}

View file

@ -14,6 +14,7 @@
X(UVM32_ERR_HUNG) \ X(UVM32_ERR_HUNG) \
X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_INTERNAL_STATE) \
X(UVM32_ERR_ARGS) \
#define X(name) name, #define X(name) name,
typedef enum { typedef enum {
@ -28,42 +29,17 @@ typedef enum {
UVM32_EVT_END, UVM32_EVT_END,
} uvm32_evt_typ_t; } 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 { typedef struct {
uvm32_err_t errcode; uvm32_err_t errcode;
const char *errstr; const char *errstr;
} uvm32_evt_err_t; } uvm32_evt_err_t;
typedef struct {
uint32_t code; // syscall number
uint32_t *ret;
uint32_t *params[2];
} uvm32_evt_syscall_t;
typedef struct { typedef struct {
uvm32_evt_typ_t typ; uvm32_evt_typ_t typ;
union { union {
@ -90,16 +66,33 @@ typedef enum {
typedef struct { typedef struct {
uvm32_status_t status; uvm32_status_t status;
uvm32_err_t err; uvm32_err_t err;
struct MiniRV32IMAState* core; // points at end of memory struct MiniRV32IMAState core;
uint8_t memory[UVM32_MEMORY_SIZE]; uint8_t memory[UVM32_MEMORY_SIZE];
uvm32_evt_t ioevt; // for building up in callbacks uvm32_evt_t ioevt; // for building up in callbacks
const uvm32_mapping_t *mappings;
uint32_t numMappings;
} uvm32_state_t; } 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_load(uvm32_state_t *vmst, uint8_t *rom, int len);
bool uvm32_hasEnded(const uvm32_state_t *vmst); bool uvm32_hasEnded(const uvm32_state_t *vmst);
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter);
// convenience for getptr
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 #endif

View file

@ -4,40 +4,30 @@
#include "uvm32.h" #include "uvm32.h"
#include "../common/uvm32_common_custom.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, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01,
0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00,
0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff,
0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01,
0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6,
0x63, 0xc8, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xca, 0x63, 0x06, 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09,
0x93, 0x0e, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00,
0x93, 0x0f, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00,
0x63, 0x62, 0xa7, 0x04, 0x33, 0x85, 0xd5, 0x01, 0x63, 0xee, 0xa7, 0x02, 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04,
0x13, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0x33, 0x8f, 0xef, 0x03, 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00,
0xb3, 0x8f, 0xd5, 0x41, 0x73, 0x00, 0x00, 0x00, 0x13, 0x5f, 0xbf, 0x40, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02,
0xb3, 0x8f, 0xcf, 0x01, 0x33, 0x0f, 0x0f, 0x01, 0xb3, 0x85, 0xff, 0x03, 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01,
0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xef, 0x03, 0x93, 0x5e, 0xc5, 0x00, 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03,
0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, 0x13, 0x85, 0x06, 0x00, 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09, 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00,
0xe3, 0xda, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xdc, 0x02, 0xf7, 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00,
0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80,
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 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,
// Create an identifier for our host handler 0x72, 0x6c, 0x64, 0x00
// 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 },
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -45,7 +35,7 @@ int main(int argc, char *argv[]) {
uvm32_evt_t evt; uvm32_evt_t evt;
bool isrunning = true; bool isrunning = true;
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst);
uvm32_load(&vmst, rom, sizeof(rom)); uvm32_load(&vmst, rom, sizeof(rom));
while(isrunning) { while(isrunning) {
@ -56,12 +46,16 @@ int main(int argc, char *argv[]) {
isrunning = false; isrunning = false;
break; break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PUTC: case UVM32_SYSCALL_PUTC:
printf("%c", evt.data.syscall.val.u32); printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break; break;
case F_PRINTLN: case UVM32_SYSCALL_PRINTLN: {
printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); 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;
} }
break; break;

View file

@ -13,59 +13,46 @@
#define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM #define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM
uint8_t rom[] = { // fib.bin uint8_t rom[] = { // fib.bin
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x40, 0x17, 0xb7, 0x08, 0x00, 0x01, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x16, 0xb7, 0x08, 0x00, 0x01,
0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, 0x05, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00,
0x13, 0x85, 0x05, 0x00, 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x93, 0x08, 0x40, 0x00, 0x73, 0x00, 0x00, 0x00, 0x37, 0x05, 0x00, 0x80,
0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00, 0x13, 0x04, 0x10, 0x00, 0x13, 0x05, 0x05, 0x1c, 0x93, 0x08, 0x30, 0x00, 0x73, 0x00, 0x00, 0x00,
0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x26, 0x31, 0x01, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00,
0x23, 0x22, 0x51, 0x01, 0x23, 0x20, 0x61, 0x01, 0x23, 0x2e, 0x11, 0x00, 0x13, 0x04, 0x10, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01,
0x23, 0x24, 0x41, 0x01, 0x13, 0x09, 0x05, 0x00, 0x93, 0x09, 0x00, 0x00, 0x23, 0x26, 0x31, 0x01, 0x23, 0x22, 0x51, 0x01, 0x23, 0x20, 0x61, 0x01,
0x93, 0x04, 0x04, 0x00, 0x93, 0x0a, 0x20, 0x00, 0x13, 0x0b, 0x04, 0x00, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x24, 0x41, 0x01, 0x13, 0x09, 0x05, 0x00,
0x63, 0x56, 0x89, 0x02, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x93, 0x09, 0x00, 0x00, 0x93, 0x04, 0x04, 0x00, 0x93, 0x0a, 0x20, 0x00,
0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, 0x13, 0x0b, 0x04, 0x00, 0x63, 0x56, 0x89, 0x02, 0x83, 0x20, 0xc1, 0x01,
0x03, 0x2a, 0x81, 0x00, 0x83, 0x2a, 0x41, 0x00, 0x03, 0x2b, 0x01, 0x00, 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01,
0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x63, 0xd2, 0x8a, 0x02, 0x83, 0x29, 0xc1, 0x00, 0x03, 0x2a, 0x81, 0x00, 0x83, 0x2a, 0x41, 0x00,
0x33, 0x8a, 0x34, 0x01, 0x93, 0x05, 0x0a, 0x00, 0x13, 0x05, 0x40, 0x00, 0x03, 0x2b, 0x01, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00,
0xef, 0xf0, 0x9f, 0xf7, 0x93, 0x89, 0x04, 0x00, 0x13, 0x04, 0x14, 0x00, 0x63, 0xd0, 0x8a, 0x02, 0x33, 0x8a, 0x34, 0x01, 0x13, 0x05, 0x0a, 0x00,
0x93, 0x04, 0x0a, 0x00, 0x6f, 0xf0, 0x5f, 0xfb, 0x93, 0x85, 0x09, 0x00, 0xef, 0xf0, 0xdf, 0xf6, 0x93, 0x89, 0x04, 0x00, 0x13, 0x04, 0x14, 0x00,
0x63, 0x04, 0x64, 0x01, 0x93, 0x85, 0x04, 0x00, 0x13, 0x05, 0x40, 0x00, 0x93, 0x04, 0x0a, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x09, 0x00,
0xef, 0xf0, 0x5f, 0xf5, 0x13, 0x8a, 0x04, 0x00, 0x6f, 0xf0, 0xdf, 0xfd, 0x63, 0x04, 0x64, 0x01, 0x13, 0x85, 0x04, 0x00, 0xef, 0xf0, 0xdf, 0xf4,
0x13, 0x01, 0x01, 0xfe, 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x13, 0x8a, 0x04, 0x00, 0x6f, 0xf0, 0x1f, 0xfe, 0x13, 0x01, 0x01, 0xfe,
0x23, 0x26, 0x31, 0x01, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x26, 0x31, 0x01,
0x93, 0x04, 0x05, 0x00, 0x93, 0x87, 0x05, 0x00, 0x13, 0x04, 0x06, 0x00, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x28, 0x21, 0x01, 0x93, 0x04, 0x05, 0x00,
0x93, 0x09, 0x20, 0x00, 0x63, 0xd2, 0x99, 0x02, 0x33, 0x89, 0x87, 0x00, 0x13, 0x04, 0x06, 0x00, 0x93, 0x09, 0x20, 0x00, 0x63, 0xd0, 0x99, 0x02,
0x93, 0x05, 0x09, 0x00, 0x13, 0x05, 0x40, 0x00, 0xef, 0xf0, 0x1f, 0xf1, 0x33, 0x89, 0x85, 0x00, 0x13, 0x05, 0x09, 0x00, 0xef, 0xf0, 0x1f, 0xf1,
0x93, 0x07, 0x04, 0x00, 0x93, 0x84, 0xf4, 0xff, 0x13, 0x04, 0x09, 0x00, 0x93, 0x05, 0x04, 0x00, 0x93, 0x84, 0xf4, 0xff, 0x13, 0x04, 0x09, 0x00,
0x6f, 0xf0, 0x1f, 0xfe, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x6f, 0xf0, 0x5f, 0xfe, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01,
0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00,
0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff,
0x23, 0x24, 0x81, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x04, 0x05, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x05, 0x00, 0x13, 0x05, 0x00, 0x00,
0x13, 0x05, 0x40, 0x00, 0x23, 0x26, 0x11, 0x00, 0xef, 0xf0, 0x9f, 0xec, 0x23, 0x26, 0x11, 0x00, 0xef, 0xf0, 0xdf, 0xec, 0x13, 0x05, 0x10, 0x00,
0x93, 0x05, 0x10, 0x00, 0x13, 0x05, 0x40, 0x00, 0xef, 0xf0, 0xdf, 0xeb, 0xef, 0xf0, 0x5f, 0xec, 0x13, 0x05, 0x04, 0x00, 0x03, 0x24, 0x81, 0x00,
0x13, 0x05, 0x04, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x13, 0x06, 0x10, 0x00, 0x93, 0x05, 0x00, 0x00,
0x13, 0x06, 0x10, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x01, 0x01, 0x01, 0x13, 0x01, 0x01, 0x01, 0x6f, 0xf0, 0x9f, 0xf6, 0x13, 0x01, 0x01, 0xff,
0x6f, 0xf0, 0x9f, 0xf5, 0xb7, 0x05, 0x00, 0x80, 0x13, 0x01, 0x01, 0xff, 0x37, 0x05, 0x00, 0x80, 0x23, 0x26, 0x11, 0x00, 0x13, 0x05, 0x45, 0x1c,
0x93, 0x85, 0x05, 0x1c, 0x13, 0x05, 0x30, 0x00, 0x23, 0x26, 0x11, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x30, 0x00, 0x73, 0x00, 0x00, 0x00,
0xef, 0xf0, 0x9f, 0xe8, 0x13, 0x05, 0x80, 0x02, 0xef, 0xf0, 0x1f, 0xe9, 0x13, 0x05, 0x80, 0x02, 0xef, 0xf0, 0x5f, 0xea, 0x37, 0x05, 0x00, 0x80,
0xb7, 0x05, 0x00, 0x80, 0x13, 0x05, 0x30, 0x00, 0x93, 0x85, 0xc5, 0x1c, 0x13, 0x05, 0x05, 0x1d, 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x30, 0x00,
0xef, 0xf0, 0x1f, 0xe7, 0x83, 0x20, 0xc1, 0x00, 0x13, 0x05, 0x80, 0x02, 0x73, 0x00, 0x00, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x13, 0x05, 0x80, 0x02,
0x13, 0x01, 0x01, 0x01, 0x6f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0x01, 0x6f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, 0x29, 0x20, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28,
0x6f, 0x70, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28, 0x29, 0x20, 0x72, 0x65, 0x29, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x66, 0x69, 0x62, 0x28,
0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x00 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 },
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -75,7 +62,7 @@ int main(int argc, char *argv[]) {
int scheduler_index = 0; int scheduler_index = 0;
for (int i=0;i<NUM_VM;i++) { for (int i=0;i<NUM_VM;i++) {
uvm32_init(&vmst[i], env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst[i]);
uvm32_load(&vmst[i], rom, sizeof(rom)); uvm32_load(&vmst[i], rom, sizeof(rom));
} }
@ -93,12 +80,18 @@ int main(int argc, char *argv[]) {
numVmRunning--; numVmRunning--;
break; break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PRINTDEC: case UVM32_SYSCALL_PRINTLN: {
printf("[VM %d] %d\n", scheduler_index, evt.data.syscall.val.u32); const char *str = uvm32_getcstr(&vmst[scheduler_index], &evt, ARG0);
if (str[0] != 0) {
printf("[VM %d] %s\n", scheduler_index, str);
}
} break;
case UVM32_SYSCALL_PRINTDEC:
printf("[VM %d] %d\n", scheduler_index, uvm32_getval(&vmst[scheduler_index], &evt, ARG0));
break; break;
case F_PRINTLN: default:
printf("[VM %d] %.*s\n", scheduler_index, evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break; break;
} }
break; break;

View file

@ -1,5 +1,5 @@
all: all:
gcc -Wall -Werror -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=32768 -I../uvm32 -I../common -o host ../uvm32/uvm32.c host.c gcc -Wall -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=32768 -I../uvm32 -I../common -o host ../uvm32/uvm32.c host.c
clean: clean:
rm -f host rm -f host

View file

@ -13,28 +13,6 @@
// stash terminal settings on startup // stash terminal settings on startup
static struct termios orig_termios; static struct termios orig_termios;
// syscalls exposed to vm environement
typedef enum {
F_PRINT,
F_PRINTDEC,
F_PRINTHEX,
F_PUTC,
F_PRINTLN,
F_MILLIS,
F_GETC,
} 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_PRINT, .typ = UVM32_SYSCALL_TYP_BUF_TERMINATED_WR, .code = F_PRINT },
{ .syscall = UVM32_SYSCALL_PRINTDEC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTDEC },
{ .syscall = UVM32_SYSCALL_PRINTHEX, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTHEX },
{ .syscall = UVM32_SYSCALL_PUTC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PUTC },
{ .syscall = UVM32_SYSCALL_MILLIS, .typ = UVM32_SYSCALL_TYP_U32_RD, .code = F_MILLIS },
{ .syscall = UVM32_SYSCALL_GETC, .typ = UVM32_SYSCALL_TYP_U32_RD, .code = F_GETC },
};
void disableRawMode(void) { void disableRawMode(void) {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
printf("\033[?25h"); // show cursor printf("\033[?25h"); // show cursor
@ -165,7 +143,8 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0])); uvm32_init(&vmst);
if (!uvm32_load(&vmst, rom, romlen)) { if (!uvm32_load(&vmst, rom, romlen)) {
printf("load failed!\n"); printf("load failed!\n");
return 1; return 1;
@ -201,52 +180,44 @@ int main(int argc, char *argv[]) {
} }
break; break;
case UVM32_EVT_SYSCALL: case UVM32_EVT_SYSCALL:
switch((f_code_t)evt.data.syscall.code) { switch(evt.data.syscall.code) {
case F_PRINT: case UVM32_SYSCALL_PRINTBUF: {
printf("%.*s", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr); uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
break; while(buf.len--) {
case F_PRINTLN: printf("%02x", *buf.ptr++);
printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr);
break;
case F_PRINTDEC:
printf("%d", evt.data.syscall.val.u32);
break;
case F_PUTC:
printf("%c", evt.data.syscall.val.u32);
break;
case F_PRINTHEX:
printf("%08x", evt.data.syscall.val.u32);
break;
case F_GETC: {
uint8_t ch;
if (poll_getch(&ch)) {
*evt.data.syscall.val.u32p = ch;
} else {
*evt.data.syscall.val.u32p = 0xFFFFFFFF; // nothing
} }
} break; } break;
case F_MILLIS: { case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s", str);
} break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str);
} break;
case UVM32_SYSCALL_PRINTDEC:
printf("%d", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTHEX:
printf("%d", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_MILLIS: {
clock_t now = clock() / (CLOCKS_PER_SEC / 1000); clock_t now = clock() / (CLOCKS_PER_SEC / 1000);
*evt.data.syscall.val.u32p = now - start_time; uvm32_setval(&vmst, &evt, RET, now - start_time);
} break; } break;
default: // catch any others case UVM32_SYSCALL_GETC: {
switch(evt.data.syscall.typ) { uint8_t c;
case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR: if (poll_getch(&c)) {
printf("UVM32_SYSCALL_TYP_BUF_TERMINATED_WR code=%d val=", evt.data.syscall.code); uvm32_setval(&vmst, &evt, RET, c);
hexdump(evt.data.syscall.val.buf.ptr, evt.data.syscall.val.buf.len); } else {
printf("\n"); uvm32_setval(&vmst, &evt, RET, 0xFFFFFFFF);
break;
case UVM32_SYSCALL_TYP_VOID:
printf("UVM32_SYSCALL_TYP_VOID code=%d\n", evt.data.syscall.code);
break;
case UVM32_SYSCALL_TYP_U32_WR:
printf("UVM32_SYSCALL_TYP_U32_WR code=%d val=%d (0x%08x)\n", evt.data.syscall.code, evt.data.syscall.val.u32, evt.data.syscall.val.u32);
break;
case UVM32_SYSCALL_TYP_U32_RD:
printf("UVM32_SYSCALL_TYP_U32_RD code=%d\n", evt.data.syscall.code);
*evt.data.syscall.val.u32p = 123456;
break;
} }
} break;
default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break; break;
} }
break; break;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -7,6 +7,9 @@
#define UVM32_NULL (void *)0 #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 #ifndef UVM32_MEMCPY
#define UVM32_MEMCPY uvm32_memcpy #define UVM32_MEMCPY uvm32_memcpy
void uvm32_memcpy(void *dst, const void *src, int len) { 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) { static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) {
// if already in error state, stay in the first error state
if (vmst->status != UVM32_STATUS_ERROR) {
setStatus(vmst, UVM32_STATUS_ERROR); setStatus(vmst, UVM32_STATUS_ERROR);
vmst->err = err; vmst->err = err;
}
} }
void uvm32_init(uvm32_state_t *vmst, 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; vmst->status = UVM32_STATUS_PAUSED;
UVM32_MEMSET(vmst->memory, 0x00, UVM32_MEMORY_SIZE);
vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET; vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET;
// https://projectf.io/posts/riscv-cheat-sheet/ // https://projectf.io/posts/riscv-cheat-sheet/
// setup stack pointer // setup stack pointer
// la sp, _sstack // la sp, _sstack
// addi sp,sp,-16 // addi sp,sp,-16
vmst->core.regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE - 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[10] = 0x00; //hart ID
vmst->core.regs[11] = 0; vmst->core.regs[11] = 0;
vmst->core.extraflags |= 3; // Machine-mode. vmst->core.extraflags |= 3; // Machine-mode.
vmst->mappings = mappings;
vmst->numMappings = numMappings;
} }
bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { 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 // 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 ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t p = ptrstart; uint32_t p = ptrstart;
if (p >= UVM32_MEMORY_SIZE) { if (p >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = UVM32_NULL; buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0; buf->len = 0;
return; return false;
} }
while(vmst->memory[p] != terminator) { while(vmst->memory[p] != '\0') {
p++; p++;
if (p >= UVM32_MEMORY_SIZE) { if (p >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = UVM32_NULL; buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0; buf->len = 0;
return; return false;
} }
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->memory[ptrstart];
buf->len = p - ptrstart; buf->len = p - ptrstart;
return true;
} }
#if 0 bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_syscall_buf_t *buf) {
static void 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; 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); setStatusErr(vmst, UVM32_ERR_MEM_RD);
buf->ptr = (uint8_t *)UVM32_NULL;
buf->len = 0;
return false;
} }
buf->ptr = &vmst->memory[ptrstart]; buf->ptr = &vmst->memory[ptrstart];
buf->len = len; 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) { if (vmst->status == UVM32_STATUS_ERROR) {
// vm is in an error state, but user wants to continue // vm is in an error state, but user wants to continue
// most likely after UVM32_ERR_HUNG // most likely after UVM32_ERR_HUNG
vmst->status = UVM32_STATUS_PAUSED; vmst->status = UVM32_STATUS_PAUSED;
} }
}
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) { if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY); 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; 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) { if (3 == ret) {
// Fetch registers used by syscall
const uint32_t syscall = vmst->core.regs[17]; // a7 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 // on exception we should jump to mtvec, but we handle directly
// and skip over the ecall instruction // and skip over the ecall instruction
vmst->core.pc += 4; vmst->core.pc += 4;
@ -152,46 +158,19 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
// inbuilt syscalls // inbuilt syscalls
case UVM32_SYSCALL_HALT: case UVM32_SYSCALL_HALT:
setStatus(vmst, UVM32_STATUS_ENDED); setStatus(vmst, UVM32_STATUS_ENDED);
syscall_valid = true;
break; break;
case UVM32_SYSCALL_YIELD: case UVM32_SYSCALL_YIELD:
vmst->ioevt.typ = UVM32_EVT_YIELD; vmst->ioevt.typ = UVM32_EVT_YIELD;
setStatus(vmst, UVM32_STATUS_PAUSED); setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true;
break; break;
// user defined syscalls
default: default:
// search in mappings // user defined syscalls
for (int i=0;i<vmst->numMappings;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.typ = UVM32_EVT_SYSCALL;
vmst->ioevt.data.syscall.code = vmst->mappings[i].code; vmst->ioevt.data.syscall.code = syscall;
vmst->ioevt.data.syscall.typ = vmst->mappings[i].typ; 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); setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true;
break; // stop searching
}
}
// no mapping found
if (!syscall_valid) {
setStatusErr(vmst, UVM32_ERR_BAD_SYSCALL);
}
break; break;
} }
} else if (ret != 0) { } else if (ret != 0) {
@ -235,4 +214,54 @@ bool uvm32_hasEnded(const uvm32_state_t *vmst) {
return vmst->status == UVM32_STATUS_ENDED; return vmst->status == UVM32_STATUS_ENDED;
} }
static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) {
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;
}

View file

@ -14,6 +14,7 @@
X(UVM32_ERR_HUNG) \ X(UVM32_ERR_HUNG) \
X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_INTERNAL_STATE) \
X(UVM32_ERR_ARGS) \
#define X(name) name, #define X(name) name,
typedef enum { typedef enum {
@ -28,42 +29,17 @@ typedef enum {
UVM32_EVT_END, UVM32_EVT_END,
} uvm32_evt_typ_t; } 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 { typedef struct {
uvm32_err_t errcode; uvm32_err_t errcode;
const char *errstr; const char *errstr;
} uvm32_evt_err_t; } uvm32_evt_err_t;
typedef struct {
uint32_t code; // syscall number
uint32_t *ret;
uint32_t *params[2];
} uvm32_evt_syscall_t;
typedef struct { typedef struct {
uvm32_evt_typ_t typ; uvm32_evt_typ_t typ;
union { union {
@ -93,13 +69,30 @@ typedef struct {
struct MiniRV32IMAState core; struct MiniRV32IMAState core;
uint8_t memory[UVM32_MEMORY_SIZE]; uint8_t memory[UVM32_MEMORY_SIZE];
uvm32_evt_t ioevt; // for building up in callbacks uvm32_evt_t ioevt; // for building up in callbacks
const uvm32_mapping_t *mappings;
uint32_t numMappings;
} uvm32_state_t; } 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_load(uvm32_state_t *vmst, uint8_t *rom, int len);
bool uvm32_hasEnded(const uvm32_state_t *vmst); bool uvm32_hasEnded(const uvm32_state_t *vmst);
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter);
// convenience for getptr
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 #endif