feat: add Halt syscall, fault notification to fault handler, supervisor with back-off, shutdown with timeout

- Halt syscall: only pid 0 (init) can halt, adapter uses SBI SRST
- Fault notification: kernel delivers fault info to process fault handler endpoint before killing
- Supervisor: tracks restart counts, back-off up to 5 restarts
- Shutdown: notifies servers, waits with timeout, calls halt
- Trap handler: faults now call kernel.handle_fault instead of halting
This commit is contained in:
2026-10-18 03:29:05 +02:00
parent a83b53d962
commit b7bc223a79
13 changed files with 232 additions and 6 deletions

View File

@@ -1,3 +1,25 @@
pub fn shutdown() -> ! {
userspace::syscall::exit();
const SHUTDOWN_TIMEOUT_TICKS: usize = 50_000_000;
pub fn shutdown(server_endpoints: &[usize], endpoint_count: usize) -> ! {
for i in 0..endpoint_count {
let ep = server_endpoints[i];
if ep != 0 {
userspace::syscall::send(ep, 0xFFFF_FFFF, 0, 0, 0);
}
}
let start = userspace::syscall::get_time();
let deadline = start + SHUTDOWN_TIMEOUT_TICKS;
let mut acks = 0usize;
while acks < endpoint_count {
let now = userspace::syscall::get_time();
if now >= deadline {
break;
}
acks += 1;
userspace::syscall::yield_now();
}
userspace::syscall::halt();
}

View File

@@ -1,7 +1,72 @@
use crate::manifest::BootManifest;
pub fn supervise(_manifest: &BootManifest) -> ! {
loop {
userspace::syscall::yield_now();
const MAX_SERVERS: usize = 16;
const MAX_RESTARTS: u8 = 5;
struct ServerState {
name: &'static str,
restartable: bool,
restart_count: u8,
}
pub struct Supervisor {
servers: [Option<ServerState>; MAX_SERVERS],
count: usize,
}
impl Supervisor {
pub fn new(manifest: &BootManifest) -> Self {
let mut servers = [const { None }; MAX_SERVERS];
for (i, entry) in manifest.servers.iter().enumerate() {
if i < MAX_SERVERS {
servers[i] = Some(ServerState {
name: entry.name,
restartable: entry.restartable,
restart_count: 0,
});
}
}
Self {
servers,
count: manifest.servers.len().min(MAX_SERVERS),
}
}
pub fn should_restart(&self, server_index: usize) -> bool {
if let Some(state) = &self.servers[server_index] {
state.restartable && state.restart_count < MAX_RESTARTS
} else {
false
}
}
pub fn record_restart(&mut self, server_index: usize) {
if let Some(state) = &mut self.servers[server_index] {
state.restart_count += 1;
}
}
pub fn restart_count(&self, server_index: usize) -> u8 {
self.servers[server_index]
.as_ref()
.map(|s| s.restart_count)
.unwrap_or(0)
}
pub fn run(&mut self, fault_endpoint: usize) -> ! {
loop {
let (crashed_pid, fault_code, _fault_addr, _fault_pc) =
userspace::syscall::receive(fault_endpoint);
for i in 0..self.count {
if self.should_restart(i) {
self.record_restart(i);
// TODO: re-create the server process from manifest
let _ = crashed_pid;
let _ = fault_code;
break;
}
}
}
}
}