feat: implement context switching, SavedContext with pc/status fields, wire all syscall outcomes and timer preemption
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user