Rename ioreq to syscall

This commit is contained in:
Toby Jaffey 2025-12-08 12:26:47 +00:00
parent b79a107a3d
commit 61fe0e8647
20 changed files with 187 additions and 190 deletions

View file

@ -15,7 +15,7 @@ Although based on a fully fledged CPU emulator, uvm32 is intended for executing
## Samples
* [host](host) vm host which loads a binary and runs to completion, handling multiple ioreq types
* [host](host) vm host which loads a binary and runs to completion, handling multiple syscall types
* [host-mini](host-mini) minimal vm host (shown above), with baked in bytecode
* [host-parallel](host-parallel) parallel vm host running multiple vm instances concurrently, with baked in bytecode
* [host-arduino](host-arduino) vm host as Arduino sketch (tested on Arduino Uno ATmega328P, uses 9950 bytes of flash/1254 bytes RAM)
@ -70,7 +70,7 @@ Once loaded with bytecode, uvm32's state is advanced by calling `uvm32_run()`.
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
`uvm32_run()` will execute until the bytecode requests some IO activity from the host.
These IO activities are called "ioreqs" and are the only way for bytecode to communicate with the host.
These IO activities are called "syscalls" and are the only way for bytecode to communicate with the host.
If the bytecode attempts to execute more instructions than the the passed value of `instr_meter` it is assumed to have crashed and an error is reported.
(As with a watchdog on an embedded system, the `yield()` bytecode function tells the host that the code requires more time to complete and has not hung)
@ -80,7 +80,7 @@ If the bytecode attempts to execute more instructions than the the passed value
* `UVM32_EVT_END` the program has ended
* `UVM32_EVT_ERR` the program has encountered an error
* `UVM32_EVT_YIELD` the program has called `yield()` signifying that it requires more instructions to be executed, but has not crashed/hung
* `UVM32_EVT_IOREQ` the program requests some IO via the host
* `UVM32_EVT_UVM32_SYSCALL` the program requests some IO via the host
## Internals
@ -92,7 +92,7 @@ uvm32 is always in one of 4 states, paused, running, ended or error.
stateDiagram
[*] --> UVM32_STATUS_PAUSED : uvm32_init()
UVM32_STATUS_PAUSED-->UVM32_STATUS_RUNNING : uvm32_run()
UVM32_STATUS_RUNNING --> UVM32_STATUS_PAUSED : ioreq event
UVM32_STATUS_RUNNING --> UVM32_STATUS_PAUSED : syscall event
UVM32_STATUS_RUNNING --> UVM32_STATUS_ENDED : halt()
UVM32_STATUS_RUNNING --> UVM32_STATUS_ERROR
```
@ -105,7 +105,7 @@ At boot, the whole memory is zeroed. The user program is placed at the start, th
All communication between bytecode and the vm host is performed via syscalls.
To make a syscall, register `a7` is set with the syscall number (an `IOREQ_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` is set with the syscall parameter. The response is returned in `a1`.
[target.h](common/uvm32_target.h#L12)
@ -125,14 +125,14 @@ static uint32_t syscall(uint32_t id, uint32_t param) {
}
```
## ioreqs
## syscalls
There are two system ioreqs 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.
New ioreqs can be added to the host via `uvm32_init()`.
Each ioreq 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.
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.
Here is a full example of a working VM host from [apps/host-mini](apps/host-mini)
@ -146,7 +146,7 @@ Here is a full example of a working VM host from [apps/host-mini](apps/host-mini
#include "../common/uvm32_common_custom.h"
// Precompiled binary program to print integers
// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h)
// This code expects to print via syscall 0x13C (UVM32_SYSCALL_PRINTD in common/uvm32_common_custom.h)
uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00,
@ -159,9 +159,9 @@ typedef enum {
F_PRINTD,
} f_code_t;
// Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
// Map VM syscall UVM32_SYSCALL_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = {
{ .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD },
{ .syscall = UVM32_SYSCALL_PRINTD, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTD },
};
int main(int argc, char *argv[]) {
@ -179,11 +179,11 @@ int main(int argc, char *argv[]) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_IOREQ: // vm has paused to handle IOREQ
switch((f_code_t)evt.data.ioreq.code) {
case UVM32_EVT_UVM32_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) {
case F_PRINTD:
// Type of F_PRINTD is IOREQ_TYP_U32_WR, so expect value in evt.data.ioreq.val.u32
printf("%d\n", evt.data.ioreq.val.u32);
// Type of F_PRINTD is UVM32_SYSCALL_TYP_U32_WR, so expect value in evt.data.syscall.val.u32
printf("%d\n", evt.data.syscall.val.u32);
break;
}
break;

View file

@ -12,7 +12,7 @@ jal ra, main
#include "non-rust-crt0-hack.S"
#else
// only rust will see this
li a7, {IOREQ_HALT}
li a7, {UVM32_SYSCALL_HALT}
#endif
ecall
.section .data

View file

@ -4,9 +4,9 @@
.global _start
_start:
la a0, str
li a7, IOREQ_PRINTLN
li a7, UVM32_SYSCALL_PRINTLN
ecall
li a7, IOREQ_HALT
li a7, UVM32_SYSCALL_HALT
ecall
str:
.ascii "Hi\0"

View file

@ -1 +1 @@
li a7,IOREQ_HALT
li a7,UVM32_SYSCALL_HALT

View file

@ -5,11 +5,11 @@ use core::arch::global_asm;
use core::arch::asm;
use core::panic::PanicInfo;
// fetch IOREQ definitions from C header
// fetch UVM32_SYSCALL definitions from C header
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
// startup code
global_asm!(include_str!("../../crt0.S"), IOREQ_HALT = const IOREQ_HALT);
global_asm!(include_str!("../../crt0.S"), UVM32_SYSCALL_HALT = const UVM32_SYSCALL_HALT);
fn syscall(id: u32, n: u32) -> u32 {
let mut value;
@ -25,11 +25,11 @@ fn syscall(id: u32, n: u32) -> u32 {
fn println(message: &str) {
let addr_value = message.as_ptr() as u32;
syscall(IOREQ_PRINTLN, addr_value);
syscall(UVM32_SYSCALL_PRINTLN, addr_value);
}
fn printd(n: u32) {
syscall(IOREQ_PRINTD, n);
syscall(UVM32_SYSCALL_PRINTD, n);
}
#[no_mangle]

View file

@ -15,14 +15,14 @@ pub inline fn syscall(id: u32, param: u32) u32 {
}
pub inline fn println(val: [:0]const u8) void {
_ = syscall(uvm32.IOREQ_PRINTLN, @intFromPtr(val.ptr));
_ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(val.ptr));
}
pub inline fn yield() void {
_ = syscall(uvm32.IOREQ_YIELD, 0);
_ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0);
}
pub inline fn printc(c:u8) void {
_ = syscall(uvm32.IOREQ_PRINTC, c);
_ = syscall(uvm32.UVM32_SYSCALL_PRINTC, c);
}

View file

@ -15,7 +15,7 @@ pub inline fn syscall(id: u32, param: u32) u32 {
}
pub inline fn getch() ?u8 {
const key = syscall(uvm32.IOREQ_GETC, 0);
const key = syscall(uvm32.UVM32_SYSCALL_GETC, 0);
if (key == 0xFFFFFFFF) {
return null;
} else {
@ -24,7 +24,7 @@ pub inline fn getch() ?u8 {
}
pub inline fn millis() u32 {
return syscall(uvm32.IOREQ_MILLIS, 0);
return syscall(uvm32.UVM32_SYSCALL_MILLIS, 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.IOREQ_PRINT, @intFromPtr(s.ptr));
_ = syscall(uvm32.UVM32_SYSCALL_PRINT, @intFromPtr(s.ptr));
}
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.IOREQ_PRINTLN, @intFromPtr(s.ptr));
_ = syscall(uvm32.UVM32_SYSCALL_PRINTLN, @intFromPtr(s.ptr));
}
pub inline fn yield() void {
_ = syscall(uvm32.IOREQ_YIELD, 0);
_ = syscall(uvm32.UVM32_SYSCALL_YIELD, 0);
}
pub inline fn printc(c:u8) void {
_ = syscall(uvm32.IOREQ_PRINTC, c);
_ = syscall(uvm32.UVM32_SYSCALL_PRINTC, c);
}

View file

@ -1,11 +1,11 @@
// Definitions needed by both host and target
// syscalls for exposed host functions
#define IOREQ_PRINT 0x13A
#define IOREQ_PRINTLN 0x13B
#define IOREQ_PRINTD 0x13C
#define IOREQ_PRINTX 0x13D
#define IOREQ_MILLIS 0x13F
#define IOREQ_PRINTC 0x140
#define IOREQ_GETC 0x141
#define UVM32_SYSCALL_PRINT 0x13A
#define UVM32_SYSCALL_PRINTLN 0x13B
#define UVM32_SYSCALL_PRINTD 0x13C
#define UVM32_SYSCALL_PRINTX 0x13D
#define UVM32_SYSCALL_MILLIS 0x13F
#define UVM32_SYSCALL_PRINTC 0x140
#define UVM32_SYSCALL_GETC 0x141

View file

@ -1,5 +1,5 @@
// System provided IOREQs
#define IOREQ_HALT 0x138
#define IOREQ_YIELD 0x139
// System provided UVM32_SYSCALLs
#define UVM32_SYSCALL_HALT 0x138
#define UVM32_SYSCALL_YIELD 0x139
#include "uvm32_common_custom.h"

View file

@ -24,14 +24,14 @@ static uint32_t syscall(uint32_t id, uint32_t param) {
}
#define syscall_cast(id, x) syscall((uint32_t)id, (uint32_t)x)
#define println(x) syscall_cast(IOREQ_PRINTLN, x)
#define print(x) syscall_cast(IOREQ_PRINT, x)
#define printd(x) syscall_cast(IOREQ_PRINTD, x)
#define printx(x) syscall_cast(IOREQ_PRINTX, x)
#define millis() syscall_cast(IOREQ_MILLIS, 0)
#define printc() syscall_cast(IOREQ_PRINTC, 0)
#define getc() syscall_cast(IOREQ_GETC, 0)
#define yield() syscall_cast(IOREQ_YIELD, 0)
#define println(x) syscall_cast(UVM32_SYSCALL_PRINTLN, x)
#define print(x) syscall_cast(UVM32_SYSCALL_PRINT, x)
#define printd(x) syscall_cast(UVM32_SYSCALL_PRINTD, x)
#define printx(x) syscall_cast(UVM32_SYSCALL_PRINTX, x)
#define millis() syscall_cast(UVM32_SYSCALL_MILLIS, 0)
#define printc() syscall_cast(UVM32_SYSCALL_PRINTC, 0)
#define getc() syscall_cast(UVM32_SYSCALL_GETC, 0)
#define yield() syscall_cast(UVM32_SYSCALL_YIELD, 0)
#include "uvm32_common_custom.h"

View file

@ -1,10 +1,10 @@
// Definitions needed by both host and target
// CSRs for exposed host functions
#define IOREQ_PRINT 0x13A
#define IOREQ_PRINTLN 0x13B
#define IOREQ_PRINTD 0x13C
#define IOREQ_PRINTX 0x13D
#define IOREQ_MILLIS 0x13F
#define IOREQ_PRINTC 0x140
#define UVM32_SYSCALL_PRINT 0x13A
#define UVM32_SYSCALL_PRINTLN 0x13B
#define UVM32_SYSCALL_PRINTD 0x13C
#define UVM32_SYSCALL_PRINTX 0x13D
#define UVM32_SYSCALL_MILLIS 0x13F
#define UVM32_SYSCALL_PRINTC 0x140

View file

@ -1,5 +1,5 @@
// System provided IOREQs
#define IOREQ_HALT 0x138
#define IOREQ_YIELD 0x139
// System provided UVM32_SYSCALLs
#define UVM32_SYSCALL_HALT 0x138
#define UVM32_SYSCALL_YIELD 0x139
#include "uvm32_common_custom.h"

View file

@ -3,7 +3,7 @@
#include "common/uvm32_common_custom.h"
// Precompiled binary program to print integers
// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h)
// This code expects to print via syscall 0x13C (UVM32_SYSCALL_PRINTD in common/uvm32_common_custom.h)
uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0x93, 0x08, 0x80, 0x13,
0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00,
@ -34,11 +34,11 @@ typedef enum {
F_PRINTC,
} f_code_t;
// Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
// Map VM syscall UVM32_SYSCALL_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = {
{ IOREQ_PRINTD, F_PRINTD, IOREQ_TYP_U32_WR },
{ IOREQ_PRINTC, F_PRINTC, IOREQ_TYP_U32_WR },
{ IOREQ_PRINTLN, F_PRINTLN, IOREQ_TYP_BUF_TERMINATED_WR },
{ UVM32_SYSCALL_PRINTD, F_PRINTD, UVM32_SYSCALL_TYP_U32_WR },
{ UVM32_SYSCALL_PRINTC, F_PRINTC, UVM32_SYSCALL_TYP_U32_WR },
{ UVM32_SYSCALL_PRINTLN, F_PRINTLN, UVM32_SYSCALL_TYP_BUF_TERMINATED_WR },
};
uvm32_state_t vmst;
@ -68,17 +68,17 @@ void loop(void) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_IOREQ: // vm has paused to handle IOREQ
switch((f_code_t)evt.data.ioreq.code) {
case UVM32_EVT_UVM32_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) {
case F_PRINTD:
Serial.println(evt.data.ioreq.val.u32);
Serial.println(evt.data.syscall.val.u32);
break;
case F_PRINTC:
Serial.print((char)evt.data.ioreq.val.u32);
Serial.print((char)evt.data.syscall.val.u32);
break;
case F_PRINTLN:
for (int i=0;i<evt.data.ioreq.val.buf.len;i++) {
Serial.print((char)evt.data.ioreq.val.buf.ptr[i]);
for (int i=0;i<evt.data.syscall.val.buf.len;i++) {
Serial.print((char)evt.data.syscall.val.buf.ptr[i]);
}
Serial.println("");
break;

View file

@ -68,7 +68,7 @@ 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_ioreq_buf_t *buf) {
static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_syscall_buf_t *buf) {
uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t p = ptrstart;
if (p >= UVM32_MEMORY_SIZE) {
@ -91,7 +91,7 @@ static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t t
}
#if 0
static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_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;
if (ptrstart + len >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD);
@ -104,7 +104,7 @@ static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0;
// uvm32_evt_ioreq_buf_t b;
// uvm32_evt_syscall_buf_t b;
if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY);
@ -128,11 +128,11 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
vmst->core->pc += 4;
switch(syscall) {
// inbuilt syscalls
case IOREQ_HALT:
case UVM32_SYSCALL_HALT:
setStatus(vmst, UVM32_STATUS_ENDED);
syscall_valid = true;
break;
case IOREQ_YIELD:
case UVM32_SYSCALL_YIELD:
vmst->ioevt.typ = UVM32_EVT_YIELD;
setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true;
@ -145,24 +145,21 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
if (syscall == vmst->mappings[i].syscall) {
// setup ioevt.data according to mapping typ
switch(vmst->mappings[i].typ) {
case IOREQ_TYP_VOID:
case UVM32_SYSCALL_TYP_VOID:
break;
case IOREQ_TYP_U32_WR:
vmst->ioevt.data.ioreq.val.u32 = value;
case UVM32_SYSCALL_TYP_U32_WR:
vmst->ioevt.data.syscall.val.u32 = value;
break;
case IOREQ_TYP_BUF_TERMINATED_WR:
get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf);
case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR:
get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.syscall.val.buf);
break;
case IOREQ_TYP_U32_RD:
// get_safeptr(vmst, value, 4, &b);
vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11]; // r1, //(uint32_t *)b.ptr;
case UVM32_SYSCALL_TYP_U32_RD:
vmst->ioevt.data.syscall.val.u32p = &vmst->core->regs[11];
break;
}
vmst->ioevt.typ = UVM32_EVT_IOREQ;
vmst->ioevt.data.ioreq.code = vmst->mappings[i].code;
vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ;
//#warning FIXME, retval
// vmst->core->regs[11] = 456; // r1
vmst->ioevt.typ = UVM32_EVT_UVM32_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

View file

@ -4,9 +4,9 @@
#include <stdint.h>
#include <stdbool.h>
// "well-known" system IOREQ functions
#define IOREQ_HALT 0x138
#define IOREQ_YIELD 0x139
// "well-known" system UVM32_SYSCALL functions
#define UVM32_SYSCALL_HALT 0x138
#define UVM32_SYSCALL_YIELD 0x139
#define LIST_OF_UVM32_ERRS \
X(UVM32_ERR_NONE) \
@ -26,41 +26,41 @@ typedef enum {
typedef enum {
UVM32_EVT_ERR,
UVM32_EVT_IOREQ,
UVM32_EVT_UVM32_SYSCALL,
UVM32_EVT_YIELD,
UVM32_EVT_END,
} uvm32_evt_typ_t;
typedef enum {
IOREQ_TYP_BUF_TERMINATED_WR, // data write from vm, NULL terminated string of bytes, in uvm32_evt_ioreq_t.val.buf
IOREQ_TYP_VOID, // no data
IOREQ_TYP_U32_WR, // data write from vm, in uvm32_evt_ioreq_t.val.u32
IOREQ_TYP_U32_RD, // data read from vm, expects response in uvm32_evt_ioreq_t.val.u32p
} uvm32_ioreq_typ_t;
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_ioreq_code_t;
typedef uint32_t uvm32_user_syscall_code_t;
// user supplied mapping from syscall to typed ioreq
// user supplied mapping from syscall to typed syscall
typedef struct {
uint32_t syscall;
uvm32_user_ioreq_code_t code;
uvm32_ioreq_typ_t typ;
uvm32_user_syscall_code_t code;
uvm32_syscall_typ_t typ;
} uvm32_mapping_t;
typedef struct {
uint8_t *ptr;
uint32_t len;
} uvm32_evt_ioreq_buf_t;
} uvm32_evt_syscall_buf_t;
typedef struct {
uvm32_ioreq_typ_t typ;
uvm32_user_ioreq_code_t code;
uvm32_syscall_typ_t typ;
uvm32_user_syscall_code_t code;
union {
uvm32_evt_ioreq_buf_t buf;
uvm32_evt_syscall_buf_t buf;
uint32_t u32;
uint32_t *u32p;
} val;
} uvm32_evt_ioreq_t;
} uvm32_evt_syscall_t;
typedef struct {
uvm32_err_t errcode;
@ -70,7 +70,7 @@ typedef struct {
typedef struct {
uvm32_evt_typ_t typ;
union {
uvm32_evt_ioreq_t ioreq;
uvm32_evt_syscall_t syscall;
uvm32_evt_err_t err;
} data;
} uvm32_evt_t;

View file

@ -5,7 +5,7 @@
#include "../common/uvm32_common_custom.h"
// Precompiled binary program to print integers
// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h)
// This code expects to print via syscall 0x13C (UVM32_SYSCALL_PRINTD in common/uvm32_common_custom.h)
uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13,
0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00,
@ -18,9 +18,9 @@ typedef enum {
F_PRINTD,
} f_code_t;
// Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
// Map VM syscall UVM32_SYSCALL_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = {
{ .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD },
{ .syscall = UVM32_SYSCALL_PRINTD, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTD },
};
int main(int argc, char *argv[]) {
@ -38,11 +38,11 @@ int main(int argc, char *argv[]) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_IOREQ: // vm has paused to handle IOREQ
switch((f_code_t)evt.data.ioreq.code) {
case UVM32_EVT_UVM32_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) {
case F_PRINTD:
// Type of F_PRINTD is IOREQ_TYP_U32_WR, so expect value in evt.data.ioreq.val.u32
printf("%d\n", evt.data.ioreq.val.u32);
// Type of F_PRINTD is UVM32_SYSCALL_TYP_U32_WR, so expect value in evt.data.syscall.val.u32
printf("%d\n", evt.data.syscall.val.u32);
break;
}
break;

View file

@ -13,7 +13,7 @@
#define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM
// Precompiled binary program to print integers
// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h)
// This code expects to print via syscall 0x13C (UVM32_SYSCALL_PRINTD in common/uvm32_common_custom.h)
uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13,
0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00,
@ -26,9 +26,9 @@ typedef enum {
F_PRINTD,
} f_code_t;
// Map VM ioreq IOREQ_PRINTD to F_PRINTD, tell VM to expect write of a U32
// Map VM syscall UVM32_SYSCALL_PRINTD to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = {
{ .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD },
{ .syscall = UVM32_SYSCALL_PRINTD, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTD },
};
int main(int argc, char *argv[]) {
@ -55,11 +55,11 @@ int main(int argc, char *argv[]) {
printf("[VM %d ended]\n", scheduler_index);
numVmRunning--;
break;
case UVM32_EVT_IOREQ: // vm has paused to handle IOREQ
switch((f_code_t)evt.data.ioreq.code) {
case UVM32_EVT_UVM32_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch((f_code_t)evt.data.syscall.code) {
case F_PRINTD:
// Type of F_PRINTD is IOREQ_TYP_U32_WR, so expect value in evt.data.ioreq.val.u32
printf("[VM %d]: %d\n", scheduler_index, evt.data.ioreq.val.u32);
// Type of F_PRINTD is UVM32_SYSCALL_TYP_U32_WR, so expect value in evt.data.syscall.val.u32
printf("[VM %d]: %d\n", scheduler_index, evt.data.syscall.val.u32);
break;
}
break;

View file

@ -12,7 +12,7 @@
// stash terminal settings on startup
static struct termios orig_termios;
// ioreqs exposed to vm environement
// syscalls exposed to vm environement
typedef enum {
F_PRINT,
F_PRINTD,
@ -23,15 +23,15 @@ typedef enum {
F_GETC,
} f_code_t;
// Map exposed ioreqs to syscalls
// Map exposed syscalls to syscalls
const uvm32_mapping_t env[] = {
{ .syscall = IOREQ_PRINTLN, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN },
{ .syscall = IOREQ_PRINT, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINT },
{ .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD },
{ .syscall = IOREQ_PRINTX, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTX },
{ .syscall = IOREQ_PRINTC, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTC },
{ .syscall = IOREQ_MILLIS, .typ = IOREQ_TYP_U32_RD, .code = F_MILLIS },
{ .syscall = IOREQ_GETC, .typ = IOREQ_TYP_U32_RD, .code = F_GETC },
{ .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_PRINTD, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTD },
{ .syscall = UVM32_SYSCALL_PRINTX, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTX },
{ .syscall = UVM32_SYSCALL_PRINTC, .typ = UVM32_SYSCALL_TYP_U32_WR, .code = F_PRINTC },
{ .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) {
@ -159,14 +159,14 @@ int main(int argc, char *argv[]) {
uvm32_evt_t evt;
bool isrunning = true;
uint32_t total_instrs = 0;
uint32_t num_ioreqs = 0;
uint32_t num_syscalls = 0;
// setup terminal for getch()
enableRawMode();
while(isrunning) {
total_instrs += uvm32_run(&vmst, &evt, 1000000); // num instructions before vm considered hung
num_ioreqs++;
num_syscalls++;
switch(evt.typ) {
case UVM32_EVT_END:
@ -175,57 +175,57 @@ int main(int argc, char *argv[]) {
break;
case UVM32_EVT_YIELD:
//printf("UVM32_EVT_YIELD\n");
// program has paused, but no ioreq
// program has paused, but no syscall
break;
case UVM32_EVT_ERR:
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
isrunning = false;
break;
case UVM32_EVT_IOREQ:
switch((f_code_t)evt.data.ioreq.code) {
case UVM32_EVT_UVM32_SYSCALL:
switch((f_code_t)evt.data.syscall.code) {
case F_PRINT:
printf("%.*s", evt.data.ioreq.val.buf.len, evt.data.ioreq.val.buf.ptr);
printf("%.*s", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr);
break;
case F_PRINTLN:
printf("%.*s\n", evt.data.ioreq.val.buf.len, evt.data.ioreq.val.buf.ptr);
printf("%.*s\n", evt.data.syscall.val.buf.len, evt.data.syscall.val.buf.ptr);
break;
case F_PRINTD:
printf("%d\n", evt.data.ioreq.val.u32);
printf("%d\n", evt.data.syscall.val.u32);
break;
case F_PRINTC:
printf("%c", evt.data.ioreq.val.u32);
printf("%c", evt.data.syscall.val.u32);
break;
case F_PRINTX:
printf("%08x", evt.data.ioreq.val.u32);
printf("%08x", evt.data.syscall.val.u32);
break;
case F_GETC: {
uint8_t ch;
if (poll_getch(&ch)) {
*evt.data.ioreq.val.u32p = ch;
*evt.data.syscall.val.u32p = ch;
} else {
*evt.data.ioreq.val.u32p = 0xFFFFFFFF; // nothing
*evt.data.syscall.val.u32p = 0xFFFFFFFF; // nothing
}
} break;
case F_MILLIS: {
clock_t now = clock() / (CLOCKS_PER_SEC / 1000);
*evt.data.ioreq.val.u32p = now - start_time;
*evt.data.syscall.val.u32p = now - start_time;
} break;
default: // catch any others
switch(evt.data.ioreq.typ) {
case IOREQ_TYP_BUF_TERMINATED_WR:
printf("IOREQ_TYP_BUF_TERMINATED_WR code=%d val=", evt.data.ioreq.code);
hexdump(evt.data.ioreq.val.buf.ptr, evt.data.ioreq.val.buf.len);
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 IOREQ_TYP_VOID:
printf("IOREQ_TYP_VOID code=%d\n", evt.data.ioreq.code);
case UVM32_SYSCALL_TYP_VOID:
printf("UVM32_SYSCALL_TYP_VOID code=%d\n", evt.data.syscall.code);
break;
case IOREQ_TYP_U32_WR:
printf("IOREQ_TYP_U32_WR code=%d val=%d (0x%08x)\n", evt.data.ioreq.code, evt.data.ioreq.val.u32, evt.data.ioreq.val.u32);
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 IOREQ_TYP_U32_RD:
printf("IOREQ_TYP_U32_RD code=%d\n", evt.data.ioreq.code);
*evt.data.ioreq.val.u32p = 123456;
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;
@ -239,7 +239,7 @@ int main(int argc, char *argv[]) {
fflush(stdout);
}
printf("Executed total of %d instructions and %d ioreqs\n", (int)total_instrs, (int)num_ioreqs);
printf("Executed total of %d instructions and %d syscalls\n", (int)total_instrs, (int)num_syscalls);
free(rom);

View file

@ -67,7 +67,7 @@ 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_ioreq_buf_t *buf) {
static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_syscall_buf_t *buf) {
uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t p = ptrstart;
if (p >= UVM32_MEMORY_SIZE) {
@ -90,7 +90,7 @@ static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t t
}
#if 0
static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_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;
if (ptrstart + len >= UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_MEM_RD);
@ -103,7 +103,7 @@ static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0;
// uvm32_evt_ioreq_buf_t b;
// uvm32_evt_syscall_buf_t b;
if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY);
@ -127,11 +127,11 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
vmst->core->pc += 4;
switch(syscall) {
// inbuilt syscalls
case IOREQ_HALT:
case UVM32_SYSCALL_HALT:
setStatus(vmst, UVM32_STATUS_ENDED);
syscall_valid = true;
break;
case IOREQ_YIELD:
case UVM32_SYSCALL_YIELD:
vmst->ioevt.typ = UVM32_EVT_YIELD;
setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true;
@ -144,22 +144,22 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
if (syscall == vmst->mappings[i].syscall) {
// setup ioevt.data according to mapping typ
switch(vmst->mappings[i].typ) {
case IOREQ_TYP_VOID:
case UVM32_SYSCALL_TYP_VOID:
break;
case IOREQ_TYP_U32_WR:
vmst->ioevt.data.ioreq.val.u32 = value;
case UVM32_SYSCALL_TYP_U32_WR:
vmst->ioevt.data.syscall.val.u32 = value;
break;
case IOREQ_TYP_BUF_TERMINATED_WR:
get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf);
case UVM32_SYSCALL_TYP_BUF_TERMINATED_WR:
get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.syscall.val.buf);
break;
case IOREQ_TYP_U32_RD:
case UVM32_SYSCALL_TYP_U32_RD:
// pass link to r1 for user function to update
vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11];
vmst->ioevt.data.syscall.val.u32p = &vmst->core->regs[11];
break;
}
vmst->ioevt.typ = UVM32_EVT_IOREQ;
vmst->ioevt.data.ioreq.code = vmst->mappings[i].code;
vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ;
vmst->ioevt.typ = UVM32_EVT_UVM32_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

View file

@ -4,9 +4,9 @@
#include <stdint.h>
#include <stdbool.h>
// "well-known" system IOREQ functions
#define IOREQ_HALT 0x138
#define IOREQ_YIELD 0x139
// "well-known" system UVM32_SYSCALL functions
#define UVM32_SYSCALL_HALT 0x138
#define UVM32_SYSCALL_YIELD 0x139
#define LIST_OF_UVM32_ERRS \
X(UVM32_ERR_NONE) \
@ -26,41 +26,41 @@ typedef enum {
typedef enum {
UVM32_EVT_ERR,
UVM32_EVT_IOREQ,
UVM32_EVT_UVM32_SYSCALL,
UVM32_EVT_YIELD,
UVM32_EVT_END,
} uvm32_evt_typ_t;
typedef enum {
IOREQ_TYP_BUF_TERMINATED_WR, // data write from vm, NULL terminated string of bytes, in uvm32_evt_ioreq_t.val.buf
IOREQ_TYP_VOID, // no data
IOREQ_TYP_U32_WR, // data write from vm, in uvm32_evt_ioreq_t.val.u32
IOREQ_TYP_U32_RD, // data read from vm, expects response in uvm32_evt_ioreq_t.val.u32p
} uvm32_ioreq_typ_t;
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_ioreq_code_t;
typedef uint32_t uvm32_user_syscall_code_t;
// user supplied mapping from syscall to typed ioreq
// user supplied mapping from syscall to typed syscall
typedef struct {
uint32_t syscall;
uvm32_user_ioreq_code_t code;
uvm32_ioreq_typ_t typ;
uvm32_user_syscall_code_t code;
uvm32_syscall_typ_t typ;
} uvm32_mapping_t;
typedef struct {
uint8_t *ptr;
uint32_t len;
} uvm32_evt_ioreq_buf_t;
} uvm32_evt_syscall_buf_t;
typedef struct {
uvm32_ioreq_typ_t typ;
uvm32_user_ioreq_code_t code;
uvm32_syscall_typ_t typ;
uvm32_user_syscall_code_t code;
union {
uvm32_evt_ioreq_buf_t buf;
uvm32_evt_syscall_buf_t buf;
uint32_t u32;
uint32_t *u32p;
} val;
} uvm32_evt_ioreq_t;
} uvm32_evt_syscall_t;
typedef struct {
uvm32_err_t errcode;
@ -70,7 +70,7 @@ typedef struct {
typedef struct {
uvm32_evt_typ_t typ;
union {
uvm32_evt_ioreq_t ioreq;
uvm32_evt_syscall_t syscall;
uvm32_evt_err_t err;
} data;
} uvm32_evt_t;