Skip to content

Writing a program

A program for EDOS is a normal Rust binary. There is no #![no_std], no entry macro, and no custom allocator to wire up.

Terminal window
mkdir -p programs/hello2/src

programs/hello2/Cargo.toml:

[package]
name = "hello2"
version = "0.1.0"
edition = "2024"

Then add "hello2" to the members list in programs/Cargo.toml. That is the only registration step; make programs picks it up and drops the binary in filesystem/bin/.

programs/hello2/src/main.rs
use std::fs;
fn main() {
let args: Vec<String> = std::env::args().collect();
let path = args.get(1).map(String::as_str).unwrap_or("/proc/meminfo");
match fs::read_to_string(path) {
Ok(text) => print!("{text}"),
Err(err) => {
eprintln!("hello2: {path}: {err}");
std::process::exit(1);
}
}
}

std::fs, std::io, std::thread, std::net, std::process and std::time are all available. The toolchain is pinned by programs/rust-toolchain.toml:

[toolchain]
channel = "edos"
Terminal window
make programs # builds userspace into filesystem/bin/
make sata-disk.img # rebuilds the root filesystem image
make run

Inside the guest:

/ $ hello2 /proc/meminfo

edos_render gives you a window, a framebuffer and a widget toolkit:

use edos_render::graphics::{Color, Screen};
use edos_render::window::Window;
fn main() {
let mut window = Window::new(100, 100, 480, 320).expect("create window");
let _ = window.set_title("hello2");
// draw into the window's shared buffer, then present it
}

programs/wintest is the complete example: buttons, a text input, checkboxes, sliders and layout. programs/hello is the smallest one that touches the screen.

Three places, in order of preference:

  1. edos_lib, for syscalls that are EDOS-specific and have no std equivalent: shared memory, window management, keymaps, process control extras. Add a wrapper here.
  2. edos_render, for anything graphical.
  3. The std fork and edos_rt, only when it must be reachable through a standard std API. This is the slow path: patch edos_rt, publish it, move the fork’s pin, reinstall the toolchain. The build guide has the full loop.

Adding the syscall itself means a number and a dispatch arm in kernel/src/syscalls/mod.rs plus the implementation in the matching syscalls/*.rs; the syscall reference has the calling convention and the full table.