feat: implement context switching, SavedContext with pc/status fields, wire all syscall outcomes and timer preemption

This commit is contained in:
2026-10-18 02:34:36 +02:00
parent 5a45c14cf5
commit 7724a5bd0a
7 changed files with 195 additions and 27 deletions

View File

@@ -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);
}
}
_ => {}
}
}
}