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

@@ -126,4 +126,17 @@ impl Platform for RiscVPlatform {
), ),
}); });
} }
fn halt(&self) -> ! {
unsafe {
core::arch::asm!(
"ecall",
in("a0") 0usize,
in("a1") 0usize,
in("a7") 0x53525354usize,
in("a6") 0usize,
);
}
loop { unsafe { core::arch::asm!("wfi") }; }
}
} }

View File

@@ -161,6 +161,7 @@ fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
match nr { match nr {
SYS_EXIT => Some(Syscall::Exit), SYS_EXIT => Some(Syscall::Exit),
SYS_HALT => Some(Syscall::Halt),
SYS_EXIT_THREAD => Some(Syscall::ExitThread), SYS_EXIT_THREAD => Some(Syscall::ExitThread),
SYS_YIELD => Some(Syscall::Yield), SYS_YIELD => Some(Syscall::Yield),
SYS_GET_TIME => Some(Syscall::GetTime), SYS_GET_TIME => Some(Syscall::GetTime),
@@ -408,7 +409,21 @@ fn handle_exception(frame: &mut TrapFrame, scause: usize) {
uart.write_str(" sepc="); uart.write_str(" sepc=");
uart.write_hex(frame.sepc); uart.write_hex(frame.sepc);
uart.put_char(b'\n'); uart.put_char(b'\n');
loop { unsafe { core::arch::asm!("wfi") }; }
let kern = crate::kernel_ref();
if let Some(current_tid) = kern.current_thread() {
let fault = match scause {
2 => domain::fault::Fault::IllegalInstruction,
5 | 7 | 13 | 15 => domain::fault::Fault::MemoryFault {
address: types::address::VirtualAddress::from_raw(stval),
},
_ => domain::fault::Fault::InvalidSyscall,
};
kern.handle_fault(current_tid, fault, frame.sepc);
switch_to_next(frame, current_tid);
} else {
loop { unsafe { core::arch::asm!("wfi") }; }
}
} }
} }
} }

View File

@@ -8,3 +8,23 @@ pub enum Fault {
MemoryFault { address: VirtualAddress }, MemoryFault { address: VirtualAddress },
IllegalInstruction, IllegalInstruction,
} }
impl Fault {
pub fn code(&self) -> usize {
match self {
Fault::CapabilityViolation { .. } => 0,
Fault::InvalidCapability { .. } => 1,
Fault::RevokedCapability { .. } => 2,
Fault::InvalidSyscall => 3,
Fault::MemoryFault { .. } => 4,
Fault::IllegalInstruction => 5,
}
}
pub fn address(&self) -> usize {
match self {
Fault::MemoryFault { address } => address.raw(),
_ => 0,
}
}
}

View File

@@ -86,4 +86,5 @@ pub enum Syscall {
GetProcessId, GetProcessId,
Yield, Yield,
Exit, Exit,
Halt,
} }

View File

@@ -1,3 +1,25 @@
pub fn shutdown() -> ! { const SHUTDOWN_TIMEOUT_TICKS: usize = 50_000_000;
userspace::syscall::exit();
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; use crate::manifest::BootManifest;
pub fn supervise(_manifest: &BootManifest) -> ! { const MAX_SERVERS: usize = 16;
loop { const MAX_RESTARTS: u8 = 5;
userspace::syscall::yield_now();
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;
}
}
}
} }
} }

View File

@@ -0,0 +1,66 @@
use platform::Platform;
use crate::Kernel;
impl<P: Platform> Kernel<P> {
pub fn handle_fault(
&mut self,
tid: types::id::ThreadId,
fault: domain::fault::Fault,
fault_pc: usize,
) {
let pid = match self.threads.get(tid) {
Ok(t) => t.process(),
Err(_) => return,
};
if let Ok(process) = self.processes.get(pid) {
if let Some(handler_slot) = process.fault_handler() {
if let Ok(cap) = process.capabilities().get(handler_slot) {
if let domain::resource::Resource::Endpoint(ep_id) = cap.resource() {
let msg = domain::message::Message::from_words([
pid.raw() as usize,
fault.code(),
fault.address(),
fault_pc,
]);
if let Some(ep) = self.endpoints.get_mut(*ep_id) {
if ep.has_waiting_receiver() {
if let Some(receiver_tid) = ep.dequeue_receiver() {
self.pending_results[receiver_tid.raw() as usize] =
Some(crate::SyscallResult::Message(msg));
if let Ok(receiver) = self.threads.get_mut(receiver_tid) {
let priority = receiver.priority();
let _ = receiver.unblock();
let core = self.platform.core_id();
use domain::scheduler::Scheduler;
self.scheduler.add(receiver_tid, priority, core);
}
}
}
}
}
}
}
}
let thread_ids: [Option<types::id::ThreadId>; 4] = {
match self.processes.get(pid) {
Ok(p) => {
let mut ids = [None; 4];
for (i, t) in p.thread_ids().enumerate() {
if i < 4 { ids[i] = Some(t); }
}
ids
}
Err(_) => return,
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(pid);
self.process_memory[pid.raw() as usize] = None;
}
}

View File

@@ -1,4 +1,5 @@
mod capability; mod capability;
mod fault;
mod ipc; mod ipc;
mod lifecycle; mod lifecycle;
mod memory; mod memory;
@@ -53,6 +54,7 @@ impl<P: Platform> Kernel<P> {
Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq), Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq),
Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq), Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq),
Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources), Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources),
Syscall::Halt => self.handle_halt(pid),
} }
} }
} }

View File

@@ -92,4 +92,16 @@ impl<P: Platform> Kernel<P> {
SyscallOutcome::TerminateCaller SyscallOutcome::TerminateCaller
} }
pub(crate) fn handle_halt(
&mut self,
pid: types::id::ProcessId,
) -> SyscallOutcome {
if pid.raw() != 0 {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::ProcessCreateDenied,
));
}
self.platform.halt();
}
} }

View File

@@ -54,4 +54,7 @@ impl Platform for MockPlatform {
_permissions: PermissionSet, _permissions: PermissionSet,
) { ) {
} }
fn halt(&self) -> ! {
loop {}
}
} }

View File

@@ -26,4 +26,5 @@ pub trait Platform {
size: PageCount, size: PageCount,
permissions: PermissionSet, permissions: PermissionSet,
); );
fn halt(&self) -> !;
} }

View File

@@ -15,3 +15,4 @@ pub const SYS_GET_TIME: usize = 13;
pub const SYS_GET_PROCESS_ID: usize = 14; pub const SYS_GET_PROCESS_ID: usize = 14;
pub const SYS_YIELD: usize = 15; pub const SYS_YIELD: usize = 15;
pub const SYS_EXIT: usize = 16; pub const SYS_EXIT: usize = 16;
pub const SYS_HALT: usize = 17;

View File

@@ -47,6 +47,11 @@ pub fn revoke(slot: usize) -> usize {
arch::syscall1(SYS_REVOKE, slot) arch::syscall1(SYS_REVOKE, slot)
} }
pub fn halt() -> ! {
arch::syscall0(SYS_HALT);
loop {}
}
pub fn send(endpoint: usize, w0: usize, w1: usize, w2: usize, w3: usize) -> usize { pub fn send(endpoint: usize, w0: usize, w1: usize, w2: usize, w3: usize) -> usize {
arch::syscall5(SYS_SEND, endpoint, w0, w1, w2, w3) arch::syscall5(SYS_SEND, endpoint, w0, w1, w2, w3)
} }