From 7724a5bd0a4877ecb5f717b03e094f0b508bf940 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 02:34:36 +0200 Subject: [PATCH] feat: implement context switching, SavedContext with pc/status fields, wire all syscall outcomes and timer preemption --- crates/adapter-riscv/src/main.rs | 2 +- crates/adapter-riscv/src/platform.rs | 49 ++++++++++++-- crates/adapter-riscv/src/trap/mod.rs | 99 ++++++++++++++++++++++++++-- crates/domain/src/thread/mod.rs | 41 +++++++++--- crates/kernel/src/lib.rs | 23 +++++++ crates/kernel/src/tests/helpers.rs | 4 -- crates/kernel/src/tests/lifecycle.rs | 4 +- 7 files changed, 195 insertions(+), 27 deletions(-) diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index 9bfff14..2ccf4b9 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -64,7 +64,7 @@ extern "C" fn kmain() -> ! { hw::plic::Plic::init(); uart.write_str("k-os: traps and timer initialized\n"); - let riscv_platform = platform::RiscVPlatform::new(); + let riscv_platform = platform::RiscVPlatform::new(kernel_pt as *const mmu::PageTable as usize); let pool = domain::memory::page_pool::PagePool::new( types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE), board::USER_POOL_PAGES, diff --git a/crates/adapter-riscv/src/platform.rs b/crates/adapter-riscv/src/platform.rs index 4b5b3fa..9c66b83 100644 --- a/crates/adapter-riscv/src/platform.rs +++ b/crates/adapter-riscv/src/platform.rs @@ -1,4 +1,5 @@ use domain::memory::MemoryRegion; +use domain::process::MAX_PROCESSES; use domain::thread::SavedContext; use platform::Platform; use types::{ @@ -11,24 +12,60 @@ use types::{ use crate::board; use crate::hw::{plic::Plic, timer}; +use crate::mmu; +use crate::trap; pub struct RiscVPlatform { num_cores: u8, + page_tables: [usize; MAX_PROCESSES], } impl RiscVPlatform { - pub fn new() -> Self { - Self { num_cores: 1 } + pub fn new(kernel_pt_addr: usize) -> Self { + let mut pt = [0usize; MAX_PROCESSES]; + pt[0] = kernel_pt_addr; + Self { num_cores: 1, page_tables: pt } + } + + pub fn page_table_for(&self, pid: ProcessId) -> usize { + self.page_tables[pid.raw() as usize] } } impl Platform for RiscVPlatform { - fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {} + fn configure_protection(&mut self, process: ProcessId, _regions: &[MemoryRegion]) { + if self.page_tables[process.raw() as usize] == 0 { + let kernel_pt_addr = self.page_tables[0]; + let kernel_pt = unsafe { &*(kernel_pt_addr as *const mmu::PageTable) }; + let user_pt = mmu::create_user_page_table(kernel_pt, 0, 0, 0x10000); + self.page_tables[process.raw() as usize] = user_pt as *const mmu::PageTable as usize; + } + } - fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {} + fn context_switch(&mut self, from: &mut SavedContext, to: &SavedContext) { + let _ = from; + let _ = to; + } - fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! { - loop { unsafe { core::arch::asm!("wfi") }; } + fn enable_user_mode(&mut self, entry: EntryPoint, stack: StackPointer) -> ! { + let kernel_sp: usize; + unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) }; + trap::set_kernel_stack(kernel_sp); + + unsafe { + core::arch::asm!( + "csrc sstatus, {spp}", + "csrs sstatus, {spie}", + "csrw sepc, {entry}", + "mv sp, {stack}", + "sret", + spp = in(reg) 1usize << 8, + spie = in(reg) 1usize << 5, + entry = in(reg) entry.raw(), + stack = in(reg) stack.raw(), + options(noreturn), + ); + } } fn set_timer(&mut self, _duration: TimerDuration) { diff --git a/crates/adapter-riscv/src/trap/mod.rs b/crates/adapter-riscv/src/trap/mod.rs index 7f7da94..067ff76 100644 --- a/crates/adapter-riscv/src/trap/mod.rs +++ b/crates/adapter-riscv/src/trap/mod.rs @@ -256,25 +256,100 @@ fn write_result(frame: &mut TrapFrame, outcome: &kernel::SyscallOutcome) { } } +fn save_frame_to_context(frame: &TrapFrame, ctx: &mut domain::thread::SavedContext) { + ctx.registers_mut().copy_from_slice(&frame.regs); + ctx.set_pc(frame.sepc); + ctx.set_status(frame.sstatus); +} + +fn load_context_to_frame(ctx: &domain::thread::SavedContext, frame: &mut TrapFrame) { + frame.regs.copy_from_slice(ctx.registers()); + frame.sepc = ctx.program_counter(); + frame.sstatus = ctx.status(); + // ensure returning to U-mode + frame.sstatus &= !(1 << 8); // clear SPP + frame.sstatus |= 1 << 5; // set SPIE +} + +fn switch_to_thread(frame: &mut TrapFrame, current_tid: types::id::ThreadId, next_tid: types::id::ThreadId) { + let kern = crate::kernel_ref(); + + if let Some(ctx) = kern.thread_context_mut(current_tid) { + save_frame_to_context(frame, ctx); + } + + let next_pid = kern.thread_process(next_tid); + let current_pid = kern.thread_process(current_tid); + + if let Some(ctx) = kern.thread_context(next_tid) { + load_context_to_frame(ctx, frame); + } + + if next_pid != current_pid { + if let Some(pid) = next_pid { + let pt_addr = kern.platform_mut().page_table_for(pid); + if pt_addr != 0 { + let pt = unsafe { &*(pt_addr as *const crate::mmu::PageTable) }; + crate::mmu::activate(pt); + } + } + } +} + +fn switch_to_next(frame: &mut TrapFrame, current_tid: types::id::ThreadId) { + let kern = crate::kernel_ref(); + + if let Some(ctx) = kern.thread_context_mut(current_tid) { + save_frame_to_context(frame, ctx); + } + + if let Some(next_tid) = kern.pick_next_thread() { + let next_pid = kern.thread_process(next_tid); + let current_pid = kern.thread_process(current_tid); + + if let Some(ctx) = kern.thread_context(next_tid) { + load_context_to_frame(ctx, frame); + } + + if next_pid != current_pid { + if let Some(pid) = next_pid { + let pt_addr = kern.platform_mut().page_table_for(pid); + if pt_addr != 0 { + let pt = unsafe { &*(pt_addr as *const crate::mmu::PageTable) }; + crate::mmu::activate(pt); + } + } + } + } else { + // no runnable thread — idle + loop { unsafe { core::arch::asm!("wfi") }; } + } +} + #[unsafe(no_mangle)] extern "C" fn trap_handler_rust(frame: &mut TrapFrame) { let scause: usize; unsafe { core::arch::asm!("csrr {0}, scause", out(reg) scause) }; if scause & CAUSE_INTERRUPT_BIT != 0 { - handle_interrupt(scause & !CAUSE_INTERRUPT_BIT); + handle_interrupt(frame, scause & !CAUSE_INTERRUPT_BIT); } else { handle_exception(frame, scause); } } -fn handle_interrupt(cause: usize) { +fn handle_interrupt(frame: &mut TrapFrame, cause: usize) { match cause { CAUSE_S_TIMER => { crate::hw::timer::handle_interrupt(); let kern = crate::kernel_ref(); let core = types::id::CoreId::from_raw(0); - let _decision = kern.handle_tick(core); + if let Some(current_tid) = kern.current_thread() { + let decision = kern.handle_tick(core); + if let domain::scheduler::SchedulerDecision::Preempt(next) = decision { + switch_to_thread(frame, current_tid, next); + } + } } CAUSE_S_EXTERNAL => { let irq_num = crate::hw::plic::Plic::claim(); @@ -296,15 +371,29 @@ fn handle_exception(frame: &mut TrapFrame, scause: usize) { if let Some(syscall) = decode_syscall(frame) { let kern = crate::kernel_ref(); + let current_tid = kern.current_thread(); let outcome = kern.handle_syscall(syscall); write_result(frame, &outcome); - match &outcome { + match outcome { + kernel::SyscallOutcome::Complete(_) => {} + kernel::SyscallOutcome::BlockCaller(_) => { + if let Some(tid) = current_tid { + switch_to_next(frame, tid); + } + } + kernel::SyscallOutcome::SwitchTo(next) => { + if let Some(tid) = current_tid { + switch_to_thread(frame, tid, next); + } + } kernel::SyscallOutcome::TerminateCaller => { let uart = crate::hw::uart::Uart::new(); uart.write_str("k-os: process exited\n"); + if let Some(tid) = current_tid { + switch_to_next(frame, tid); + } } - _ => {} } } } diff --git a/crates/domain/src/thread/mod.rs b/crates/domain/src/thread/mod.rs index 3f2c665..1c94903 100644 --- a/crates/domain/src/thread/mod.rs +++ b/crates/domain/src/thread/mod.rs @@ -29,14 +29,17 @@ pub enum ThreadState { pub struct SavedContext { registers: [usize; 32], + pc: usize, + status: usize, } -pub const PC_REGISTER: usize = 0; -pub const SP_REGISTER: usize = 1; - impl SavedContext { pub fn empty() -> Self { - Self { registers: [0; 32] } + Self { + registers: [0; 32], + pc: 0, + status: 0, + } } pub fn with_entry_and_stack( @@ -44,17 +47,33 @@ impl SavedContext { stack: types::address::StackPointer, ) -> Self { let mut ctx = Self::empty(); - ctx.registers[PC_REGISTER] = entry.raw(); - ctx.registers[SP_REGISTER] = stack.raw(); + ctx.pc = entry.raw(); + ctx.registers[2] = stack.raw(); ctx } pub fn program_counter(&self) -> usize { - self.registers[PC_REGISTER] + self.pc } - pub fn stack_pointer(&self) -> usize { - self.registers[SP_REGISTER] + pub fn set_pc(&mut self, pc: usize) { + self.pc = pc; + } + + pub fn status(&self) -> usize { + self.status + } + + pub fn set_status(&mut self, status: usize) { + self.status = status; + } + + pub fn registers(&self) -> &[usize; 32] { + &self.registers + } + + pub fn registers_mut(&mut self) -> &mut [usize; 32] { + &mut self.registers } } @@ -107,6 +126,10 @@ impl Thread { &self.saved_context } + pub fn saved_context_mut(&mut self) -> &mut SavedContext { + &mut self.saved_context + } + pub fn id(&self) -> ThreadId { self.id } diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index a578765..6465e11 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -113,6 +113,29 @@ impl Kernel

{ self.processes.get(pid).is_ok() } + pub fn thread_context(&self, tid: ThreadId) -> Option<&domain::thread::SavedContext> { + self.threads.get(tid).ok().map(|t| t.saved_context()) + } + + pub fn thread_context_mut(&mut self, tid: ThreadId) -> Option<&mut domain::thread::SavedContext> { + self.threads.get_mut(tid).ok().map(|t| t.saved_context_mut()) + } + + pub fn thread_process(&self, tid: ThreadId) -> Option { + self.threads.get(tid).ok().map(|t| t.process()) + } + + pub fn current_thread(&self) -> Option { + let core = self.platform.core_id(); + self.scheduler.current(core) + } + + pub fn pick_next_thread(&mut self) -> Option { + use domain::scheduler::Scheduler; + let core = self.platform.core_id(); + self.scheduler.next(core) + } + pub fn create_endpoint(&mut self) -> Result { self.endpoints.create().ok_or(KernelError::NoFreeSlot) } diff --git a/crates/kernel/src/tests/helpers.rs b/crates/kernel/src/tests/helpers.rs index ab22fb3..b106fbf 100644 --- a/crates/kernel/src/tests/helpers.rs +++ b/crates/kernel/src/tests/helpers.rs @@ -97,8 +97,4 @@ impl Kernel { ) -> CapSlot { self.grant_cap(pid, resource, permissions).expect("grant cap") } - - pub fn thread_context(&self, tid: ThreadId) -> &domain::thread::SavedContext { - self.threads.get(tid).expect("thread").saved_context() - } } diff --git a/crates/kernel/src/tests/lifecycle.rs b/crates/kernel/src/tests/lifecycle.rs index c766596..4bd1676 100644 --- a/crates/kernel/src/tests/lifecycle.rs +++ b/crates/kernel/src/tests/lifecycle.rs @@ -28,9 +28,9 @@ fn spawn_thread_preserves_entry_and_stack() { SyscallOutcome::Complete(SyscallResult::Value(v)) => ThreadId::from_raw(v as u16), _ => panic!("expected Value"), }; - let ctx = k.thread_context(tid); + let ctx = k.thread_context(tid).unwrap(); assert_eq!(ctx.program_counter(), 0xDEAD); - assert_eq!(ctx.stack_pointer(), 0xBEEF); + assert_eq!(ctx.registers()[2], 0xBEEF); } #[test]