Rework syscall ABI.

Syscalls now accept two parameters, allowing for things like "int count = read(buf, len)"
Rather than providing safe signatures for syscalls, the user is now given helper functions to safely parse incoming values, c-strings and slices.
This commit is contained in:
Toby Jaffey 2025-12-09 21:51:35 +00:00
parent f046a590c0
commit 76fba39a21
28 changed files with 543 additions and 537 deletions

View file

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

View file

@ -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");
}

View file

@ -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) {
}
}
}

View file

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

View file

@ -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);
}

View file

@ -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);
}