mirror of
https://github.com/ringtailsoftware/uvm32.git
synced 2026-06-05 22:43:39 +00:00
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:
parent
f046a590c0
commit
76fba39a21
28 changed files with 543 additions and 537 deletions
104
README.md
104
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;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include "uvm32_target.h"
|
||||
|
||||
void main(void) {
|
||||
for (int i=0;i<10;i++) {
|
||||
printdec(i);
|
||||
}
|
||||
println("Hello world");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<evt.data.syscall.val.buf.len;i++) {
|
||||
putc(evt.data.syscall.val.buf.ptr[i]);
|
||||
}
|
||||
putc('\n');
|
||||
case UVM32_SYSCALL_PRINTLN: {
|
||||
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
|
||||
println(str);
|
||||
} break;
|
||||
default:
|
||||
println("Unhandled syscall");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -74,3 +65,4 @@ void main(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ uint32_t count;
|
|||
|
||||
bool loop(void) {
|
||||
printdec(count);
|
||||
println("");
|
||||
if (count++ >= 10) {
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@
|
|||
#define UVM32_SYSCALL_PRINTDEC 0x00000004
|
||||
#define UVM32_SYSCALL_PRINTHEX 0x00000005
|
||||
#define UVM32_SYSCALL_MILLIS 0x00000006
|
||||
#define UVM32_SYSCALL_PRINTBUF 0x00000007
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<evt.data.syscall.val.buf.len;i++) {
|
||||
Serial.print((char)evt.data.syscall.val.buf.ptr[i]);
|
||||
}
|
||||
case UVM32_SYSCALL_PRINTLN: {
|
||||
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
|
||||
Serial.print(str);
|
||||
Serial.println("");
|
||||
} break;
|
||||
default:
|
||||
Serial.println("Unhandled syscall");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -95,7 +81,7 @@ void loop(void) {
|
|||
} else {
|
||||
Serial.println("Starting VM");
|
||||
// setup vm
|
||||
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0]));
|
||||
uvm32_init(&vmst);
|
||||
uvm32_load(&vmst, rom, sizeof(rom));
|
||||
isrunning = true;
|
||||
delay(2000);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,37 @@
|
|||
#include "config.h"
|
||||
#define MINIRV32_IMPLEMENTATION
|
||||
#include "uvm32.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;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.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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;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));
|
||||
}
|
||||
|
||||
|
|
@ -93,12 +80,18 @@ int main(int argc, char *argv[]) {
|
|||
numVmRunning--;
|
||||
break;
|
||||
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
|
||||
switch((f_code_t)evt.data.syscall.code) {
|
||||
case F_PRINTDEC:
|
||||
printf("[VM %d] %d\n", scheduler_index, evt.data.syscall.val.u32);
|
||||
switch(evt.data.syscall.code) {
|
||||
case UVM32_SYSCALL_PRINTLN: {
|
||||
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;
|
||||
case F_PRINTLN:
|
||||
printf("[VM %d] %.*s\n", scheduler_index, evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr);
|
||||
default:
|
||||
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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:
|
||||
rm -f host
|
||||
|
|
|
|||
99
host/host.c
99
host/host.c
|
|
@ -13,28 +13,6 @@
|
|||
// stash terminal settings on startup
|
||||
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) {
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
|
||||
printf("\033[?25h"); // show cursor
|
||||
|
|
@ -165,7 +143,8 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
uvm32_init(&vmst, env, sizeof(env) / sizeof(env[0]));
|
||||
uvm32_init(&vmst);
|
||||
|
||||
if (!uvm32_load(&vmst, rom, romlen)) {
|
||||
printf("load failed!\n");
|
||||
return 1;
|
||||
|
|
@ -201,52 +180,44 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
break;
|
||||
case UVM32_EVT_SYSCALL:
|
||||
switch((f_code_t)evt.data.syscall.code) {
|
||||
case F_PRINT:
|
||||
printf("%.*s", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr);
|
||||
break;
|
||||
case F_PRINTLN:
|
||||
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
|
||||
switch(evt.data.syscall.code) {
|
||||
case UVM32_SYSCALL_PRINTBUF: {
|
||||
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
|
||||
while(buf.len--) {
|
||||
printf("%02x", *buf.ptr++);
|
||||
}
|
||||
} 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);
|
||||
*evt.data.syscall.val.u32p = now - start_time;
|
||||
uvm32_setval(&vmst, &evt, RET, now - start_time);
|
||||
} break;
|
||||
default: // catch any others
|
||||
switch(evt.data.syscall.typ) {
|
||||
case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR:
|
||||
printf("UVM32_SYSCALL_TYP_BUF_TERMINATED_WR code=%d val=", evt.data.syscall.code);
|
||||
hexdump(evt.data.syscall.val.buf.ptr, evt.data.syscall.val.buf.len);
|
||||
printf("\n");
|
||||
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;
|
||||
case UVM32_SYSCALL_GETC: {
|
||||
uint8_t c;
|
||||
if (poll_getch(&c)) {
|
||||
uvm32_setval(&vmst, &evt, RET, c);
|
||||
} else {
|
||||
uvm32_setval(&vmst, &evt, RET, 0xFFFFFFFF);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
|
||||
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.
145
uvm32/uvm32.c
145
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;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.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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue