feat(userspace-rt): add runtime crate with entry point, panic handler, and allocator re-export

Snake and hello apps now use extern crate userspace_rt — no more boilerplate assembly or panic handlers in app code.
This commit is contained in:
2026-10-18 04:04:52 +02:00
parent e092fa4ae5
commit 29434805a6
9 changed files with 58 additions and 70 deletions

View File

@@ -38,6 +38,7 @@ members = [
"crates/servers/user-manager", "crates/servers/user-manager",
"crates/types", "crates/types",
"crates/userspace", "crates/userspace",
"crates/userspace-rt",
] ]
resolver = "2" resolver = "2"
@@ -51,6 +52,7 @@ platform = { path = "crates/platform" }
kernel = { path = "crates/kernel" } kernel = { path = "crates/kernel" }
server-framework = { path = "crates/server-framework" } server-framework = { path = "crates/server-framework" }
userspace = { path = "crates/userspace" } userspace = { path = "crates/userspace" }
userspace-rt = { path = "crates/userspace-rt" }
# servers # servers
accessibility-server = { path = "crates/servers/accessibility-server" } accessibility-server = { path = "crates/servers/accessibility-server" }

View File

@@ -9,11 +9,4 @@ test = false
bench = false bench = false
[dependencies] [dependencies]
userspace = { workspace = true } userspace-rt = { workspace = true }
[profile.release]
panic = "abort"
opt-level = "s"
[profile.dev]
panic = "abort"

View File

@@ -1,24 +1,9 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::arch::global_asm; extern crate userspace_rt;
global_asm!(
r#"
.section .text.entry
.globl _start
_start:
call main
j _start
"#
);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn main() -> ! { extern "C" fn main() {
userspace::syscall::exit(); // The runtime handles _start, exit, and panic.
}
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
} }

View File

@@ -11,3 +11,4 @@ bench = false
[dependencies] [dependencies]
types = { workspace = true } types = { workspace = true }
userspace = { workspace = true } userspace = { workspace = true }
userspace-rt = { workspace = true }

View File

@@ -1,48 +1,31 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
extern crate userspace_rt;
use snake::game::{Direction, GameState, Snake}; use snake::game::{Direction, GameState, Snake};
use snake::renderer::{render, RenderCommand}; use snake::renderer::{render, RenderCommand};
use userspace_rt::syscall;
const DISPLAY_EP: usize = 0; const DISPLAY_EP: usize = 0;
const INPUT_EP: usize = 1; const INPUT_EP: usize = 1;
const TICK_DELAY: usize = 2_000_000; const TICK_DELAY: usize = 2_000_000;
const MAX_RENDER_COMMANDS: usize = 192; const MAX_RENDER_COMMANDS: usize = 192;
#[cfg(not(test))]
use core::arch::global_asm;
#[cfg(not(test))]
global_asm!(
r#"
.section .text.entry
.globl _start
_start:
call main
j _start
"#
);
#[cfg(not(test))]
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn main() -> ! { extern "C" fn main() {
run_game();
}
fn run_game() -> ! {
let mut snake = Snake::new(); let mut snake = Snake::new();
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_COMMANDS]; let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_COMMANDS];
loop { loop {
handle_input(&mut snake); handle_input(&mut snake);
snake.tick(); snake.tick();
let count = render(&snake, &mut commands); let count = render(&snake, &mut commands);
send_render_commands(&commands, count); send_render_commands(&commands, count);
if snake.state() == GameState::GameOver { if snake.state() == GameState::GameOver {
userspace::syscall::exit(); return;
} }
wait_tick(); wait_tick();
@@ -50,11 +33,9 @@ fn run_game() -> ! {
} }
fn handle_input(snake: &mut Snake) { fn handle_input(snake: &mut Snake) {
// PollEvent request: tag=0 let _ = syscall::send(INPUT_EP, 0, 0, 0, 0);
let _ = userspace::syscall::send(INPUT_EP, 0, 0, 0, 0); let (tag, kind, _, _) = syscall::receive(INPUT_EP);
let (tag, kind, _data, _) = userspace::syscall::receive(INPUT_EP);
// InputResponse::Event(KeyDown(key)): tag=1, key_kind in word 1
if tag == 1 { if tag == 1 {
match kind { match kind {
0 => snake.set_direction(Direction::Up), 0 => snake.set_direction(Direction::Up),
@@ -67,34 +48,25 @@ fn handle_input(snake: &mut Snake) {
} }
fn send_render_commands(commands: &[RenderCommand], count: usize) { fn send_render_commands(commands: &[RenderCommand], count: usize) {
// Clear: DisplayRequest tag=1 let _ = syscall::send(DISPLAY_EP, 1, 0, 0, 0);
let _ = userspace::syscall::send(DISPLAY_EP, 1, 0, 0, 0); let _ = syscall::receive(DISPLAY_EP);
let _ = userspace::syscall::receive(DISPLAY_EP);
for i in 0..count { for i in 0..count {
let cmd = &commands[i]; let cmd = &commands[i];
// DrawRect: DisplayRequest tag=0, xy packed, wh packed, color
let xy = cmd.x as usize | ((cmd.y as usize) << 16); let xy = cmd.x as usize | ((cmd.y as usize) << 16);
let wh = cmd.w as usize | ((cmd.h as usize) << 16); let wh = cmd.w as usize | ((cmd.h as usize) << 16);
let _ = userspace::syscall::send(DISPLAY_EP, 0, xy, wh, cmd.color as usize); let _ = syscall::send(DISPLAY_EP, 0, xy, wh, cmd.color as usize);
let _ = userspace::syscall::receive(DISPLAY_EP); let _ = syscall::receive(DISPLAY_EP);
} }
// Flush: DisplayRequest tag=2 let _ = syscall::send(DISPLAY_EP, 2, 0, 0, 0);
let _ = userspace::syscall::send(DISPLAY_EP, 2, 0, 0, 0); let _ = syscall::receive(DISPLAY_EP);
let _ = userspace::syscall::receive(DISPLAY_EP);
} }
fn wait_tick() { fn wait_tick() {
let start = userspace::syscall::get_time(); let start = syscall::get_time();
let deadline = start + TICK_DELAY; let deadline = start + TICK_DELAY;
while userspace::syscall::get_time() < deadline { while syscall::get_time() < deadline {
userspace::syscall::yield_now(); syscall::yield_now();
} }
} }
#[cfg(not(test))]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
userspace::syscall::exit();
}

View File

@@ -0,0 +1,8 @@
[package]
name = "userspace-rt"
version = "0.1.0"
edition = "2024"
[dependencies]
types = { workspace = true }
userspace = { workspace = true }

View File

@@ -0,0 +1,2 @@
#[cfg(all(target_arch = "riscv64", target_os = "none"))]
mod riscv;

View File

@@ -0,0 +1,13 @@
use core::arch::global_asm;
global_asm!(
r#"
.section .text.entry
.globl _start
_start:
call main
li a7, 16
ecall
j _start
"#
);

View File

@@ -0,0 +1,12 @@
#![no_std]
mod arch;
pub use userspace::syscall;
pub use userspace::allocator::KosAllocator;
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
userspace::syscall::exit();
}