feat: add Rust userspace support; fix TLS layout for p_align < 16; kill whole process on sibling-thread faults

This commit is contained in:
daniel committed 2026-10-14 07:16:42 +02:00
1 parent a81298fb24
commit f8b8f9610f
31 files changed
+1076 -38

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
//! rusthello - Phase 1 Rust smoke test for MontaukOS.
//!
//! Exercises alloc collections, formatting, argv, a #[thread_local]
//! (PT_TLS + per-thread %fs blocks), threads, and the futex mutex.
//! `rusthello panic` / `rusthello panic-thread` check that a panic on
//! either thread takes the whole process down; `rusthello fault-thread`
//! checks the same for a page fault on a worker thread.
#![no_std]
#![no_main]
#![feature(thread_local)]
extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::Cell;
use core::time::Duration;
use montauk_rt::sync::Mutex;
use montauk_rt::{entry, println, thread, time};
entry!(main);
static mut FAILURES: u32 = 0;
fn check(ok: bool, name: &str) {
println!("{} {}", if ok { "PASS" } else { "FAIL" }, name);
if !ok {
unsafe { FAILURES += 1 };
}
}
#[thread_local]
static TLS_COUNTER: Cell<u64> = Cell::new(7);
const THREADS: usize = 4;
const ITERS: u64 = 20_000;
fn main() -> i32 {
println!("rusthello: Rust on MontaukOS (no_std + alloc)");
let args: Vec<&str> = montauk_rt::env::args().collect();
println!("argv = {:?}", args);
match args.get(1).copied() {
Some("panic") => panic!("requested panic on the main thread"),
Some("fault-thread") => {
// A CPU fault (not a panic) on a worker must still kill the
// whole process, with the SIGSEGV exit code (267).
let h = thread::spawn(|| {
unsafe { core::ptr::write_volatile(8 as *mut u64, 0xdead) };
0
})
.unwrap();
let _ = h.join();
thread::yield_now();
println!("FAIL process survived a worker-thread fault");
return 1;
}
Some("panic-thread") => {
let h = thread::spawn(|| panic!("requested panic on a worker thread")).unwrap();
let _ = h.join();
println!("FAIL process survived a worker-thread panic");
return 1;
}
_ => {}
}
// --- alloc ---
let mut v: Vec<u64> = (1..=100).collect();
v.retain(|x| x % 3 == 0);
check(v.len() == 33 && v.iter().sum::<u64>() == 1683, "Vec collect/retain/sum");
let mut s = String::new();
for word in ["Montauk", "speaks", "Rust"] {
if !s.is_empty() {
s.push(' ');
}
s.push_str(word);
}
check(s == "Montauk speaks Rust", "String building");
check(format!("{:>6.2}|{:#x}|{:?}", 3.14159f64, 255, Some("x")) == " 3.14|0xff|Some(\"x\")",
"format! with floats, hex, Debug");
let mut map = BTreeMap::new();
for (i, w) in s.split(' ').enumerate() {
map.insert(w, i);
}
check(map.get("Rust") == Some(&2) && map.len() == 3, "BTreeMap");
let big: Vec<u8> = alloc::vec![0xA5; 1 << 20];
check(big.iter().all(|&b| b == 0xA5), "1 MiB allocation");
#[repr(align(4096))]
struct PageAligned([u8; 64]);
let page = alloc::boxed::Box::new(PageAligned([1; 64]));
check((&*page as *const PageAligned as usize) % 4096 == 0 && page.0[63] == 1, "over-aligned Box (4096)");
// --- thread-local storage ---
check(TLS_COUNTER.get() == 7, "#[thread_local] initial value on main thread");
TLS_COUNTER.set(1000);
let tls_seen = thread::spawn(|| {
let initial = TLS_COUNTER.get();
TLS_COUNTER.set(55);
initial as i32
})
.unwrap()
.join();
check(tls_seen == Ok(7), "worker thread gets a fresh TLS block");
check(TLS_COUNTER.get() == 1000, "main thread TLS untouched by worker");
// --- threads + futex mutex ---
let counter = Arc::new(Mutex::new(0u64));
let mut handles = Vec::new();
for t in 0..THREADS {
let counter = counter.clone();
handles.push(
thread::spawn(move || {
for i in 0..ITERS {
let mut g = counter.lock();
let v = *g;
if i % 512 == 0 {
thread::yield_now(); // widen the race window under the lock
}
*g = v + 1;
}
t as i32 + 10
})
.unwrap(),
);
}
let codes: Vec<_> = handles.into_iter().map(|h| h.join()).collect();
check(codes.iter().enumerate().all(|(t, c)| *c == Ok(t as i32 + 10)), "thread exit codes via join");
check(*counter.lock() == THREADS as u64 * ITERS, "futex Mutex keeps the counter exact");
// --- time ---
let t0 = time::uptime_ms();
time::sleep(Duration::from_millis(25));
let dt = time::uptime_ms() - t0;
check((25..1000).contains(&dt), "sleep(25 ms)");
let failures = unsafe { FAILURES };
println!("rusthello: {}", if failures == 0 { "all passed" } else { "FAILURES" });
if failures == 0 { 0 } else { 1 }
}