diff --git a/crates/adapter-riscv/src/platform.rs b/crates/adapter-riscv/src/platform.rs index 7e11b0c..0c5f76a 100644 --- a/crates/adapter-riscv/src/platform.rs +++ b/crates/adapter-riscv/src/platform.rs @@ -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") }; } + } } diff --git a/crates/adapter-riscv/src/trap/mod.rs b/crates/adapter-riscv/src/trap/mod.rs index 067ff76..e2fc98c 100644 --- a/crates/adapter-riscv/src/trap/mod.rs +++ b/crates/adapter-riscv/src/trap/mod.rs @@ -161,6 +161,7 @@ fn decode_syscall(frame: &TrapFrame) -> Option { match nr { SYS_EXIT => Some(Syscall::Exit), + SYS_HALT => Some(Syscall::Halt), SYS_EXIT_THREAD => Some(Syscall::ExitThread), SYS_YIELD => Some(Syscall::Yield), SYS_GET_TIME => Some(Syscall::GetTime), @@ -408,7 +409,21 @@ fn handle_exception(frame: &mut TrapFrame, scause: usize) { uart.write_str(" sepc="); uart.write_hex(frame.sepc); 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") }; } + } } } } diff --git a/crates/domain/src/fault.rs b/crates/domain/src/fault.rs index 30e35dd..58e0e90 100644 --- a/crates/domain/src/fault.rs +++ b/crates/domain/src/fault.rs @@ -8,3 +8,23 @@ pub enum Fault { MemoryFault { address: VirtualAddress }, 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, + } + } +} diff --git a/crates/domain/src/syscall.rs b/crates/domain/src/syscall.rs index 1a541e0..757e655 100644 --- a/crates/domain/src/syscall.rs +++ b/crates/domain/src/syscall.rs @@ -86,4 +86,5 @@ pub enum Syscall { GetProcessId, Yield, Exit, + Halt, } diff --git a/crates/init/src/shutdown.rs b/crates/init/src/shutdown.rs index fb3aa46..2b4833d 100644 --- a/crates/init/src/shutdown.rs +++ b/crates/init/src/shutdown.rs @@ -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(); } diff --git a/crates/init/src/supervisor.rs b/crates/init/src/supervisor.rs index 081bad5..38bf8eb 100644 --- a/crates/init/src/supervisor.rs +++ b/crates/init/src/supervisor.rs @@ -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; 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; + } + } + } } } diff --git a/crates/kernel/src/syscall/fault.rs b/crates/kernel/src/syscall/fault.rs new file mode 100644 index 0000000..fb9229d --- /dev/null +++ b/crates/kernel/src/syscall/fault.rs @@ -0,0 +1,66 @@ +use platform::Platform; + +use crate::Kernel; + +impl Kernel

{ + 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; 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; + } +} diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs index c6d78ce..20bd1df 100644 --- a/crates/kernel/src/syscall/mod.rs +++ b/crates/kernel/src/syscall/mod.rs @@ -1,4 +1,5 @@ mod capability; +mod fault; mod ipc; mod lifecycle; mod memory; @@ -53,6 +54,7 @@ impl Kernel

{ Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq), Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq), Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources), + Syscall::Halt => self.handle_halt(pid), } } } diff --git a/crates/kernel/src/syscall/simple.rs b/crates/kernel/src/syscall/simple.rs index de453aa..8b5cc9d 100644 --- a/crates/kernel/src/syscall/simple.rs +++ b/crates/kernel/src/syscall/simple.rs @@ -92,4 +92,16 @@ impl Kernel

{ 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(); + } } diff --git a/crates/kernel/src/tests/mock_platform.rs b/crates/kernel/src/tests/mock_platform.rs index f7f3a79..8a4d6a9 100644 --- a/crates/kernel/src/tests/mock_platform.rs +++ b/crates/kernel/src/tests/mock_platform.rs @@ -54,4 +54,7 @@ impl Platform for MockPlatform { _permissions: PermissionSet, ) { } + fn halt(&self) -> ! { + loop {} + } } diff --git a/crates/platform/src/lib.rs b/crates/platform/src/lib.rs index 6d0e215..8b72f16 100644 --- a/crates/platform/src/lib.rs +++ b/crates/platform/src/lib.rs @@ -26,4 +26,5 @@ pub trait Platform { size: PageCount, permissions: PermissionSet, ); + fn halt(&self) -> !; } diff --git a/crates/types/src/syscall_abi.rs b/crates/types/src/syscall_abi.rs index 70f0079..469eabc 100644 --- a/crates/types/src/syscall_abi.rs +++ b/crates/types/src/syscall_abi.rs @@ -15,3 +15,4 @@ pub const SYS_GET_TIME: usize = 13; pub const SYS_GET_PROCESS_ID: usize = 14; pub const SYS_YIELD: usize = 15; pub const SYS_EXIT: usize = 16; +pub const SYS_HALT: usize = 17; diff --git a/crates/userspace/src/syscall.rs b/crates/userspace/src/syscall.rs index fcc6213..d340b13 100644 --- a/crates/userspace/src/syscall.rs +++ b/crates/userspace/src/syscall.rs @@ -47,6 +47,11 @@ pub fn revoke(slot: usize) -> usize { 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 { arch::syscall5(SYS_SEND, endpoint, w0, w1, w2, w3) }