feat(adapter-riscv): replace static mut with KernelCell, complete all 17 syscall decoders, wire timer and PLIC interrupts to kernel

This commit is contained in:
2026-10-18 02:29:46 +02:00
parent ededdb2ed9
commit 5a45c14cf5
4 changed files with 156 additions and 80 deletions

View File

@@ -0,0 +1,35 @@
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicBool, Ordering};
pub struct KernelCell<T> {
initialized: AtomicBool,
inner: UnsafeCell<MaybeUninit<T>>,
}
// Single-core: no concurrent access. Initialized once before traps are enabled.
unsafe impl<T> Sync for KernelCell<T> {}
impl<T> KernelCell<T> {
pub const fn uninit() -> Self {
Self {
initialized: AtomicBool::new(false),
inner: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn init(&self, value: T) {
unsafe { (*self.inner.get()).write(value) };
self.initialized.store(true, Ordering::Release);
}
pub fn get(&self) -> &T {
debug_assert!(self.initialized.load(Ordering::Acquire));
unsafe { (*self.inner.get()).assume_init_ref() }
}
pub fn get_mut(&self) -> &mut T {
debug_assert!(self.initialized.load(Ordering::Acquire));
unsafe { (*self.inner.get()).assume_init_mut() }
}
}

View File

@@ -3,19 +3,19 @@
mod board; mod board;
mod hw; mod hw;
mod kernel_cell;
mod mmu; mod mmu;
mod platform; mod platform;
mod trap; mod trap;
use core::arch::global_asm; use core::arch::global_asm;
use hw::uart::Uart; use hw::uart::Uart;
use kernel_cell::KernelCell;
use core::ptr::addr_of_mut; static KERNEL: KernelCell<kernel::Kernel<platform::RiscVPlatform>> = KernelCell::uninit();
static mut KERNEL: Option<kernel::Kernel<platform::RiscVPlatform>> = None; pub fn kernel_ref() -> &'static mut kernel::Kernel<platform::RiscVPlatform> {
KERNEL.get_mut()
pub fn kernel_mut() -> &'static mut kernel::Kernel<platform::RiscVPlatform> {
unsafe { (*addr_of_mut!(KERNEL)).as_mut() }.unwrap_or_else(|| unreachable!())
} }
global_asm!( global_asm!(
@@ -69,16 +69,15 @@ extern "C" fn kmain() -> ! {
types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE), types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE),
board::USER_POOL_PAGES, board::USER_POOL_PAGES,
); );
let kern = kernel::Kernel::new(riscv_platform, pool); KERNEL.init(kernel::Kernel::new(riscv_platform, pool));
unsafe { *addr_of_mut!(KERNEL) = Some(kern); } let kern = kernel_ref();
let kern = kernel_mut();
let (_pid, _tid) = kern let (_pid, _tid) = kern
.boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT) .boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT)
.unwrap_or_else(|_| loop { unsafe { core::arch::asm!("wfi") }; }); .unwrap_or_else(|_| loop { unsafe { core::arch::asm!("wfi") }; });
uart.write_str("k-os: kernel started\n"); uart.write_str("k-os: kernel started\n");
userspace_demo(kern, &uart, kernel_pt); userspace_demo(&uart, kernel_pt);
uart.write_str("k-os: demo complete\n"); uart.write_str("k-os: demo complete\n");
loop { loop {
@@ -86,28 +85,17 @@ extern "C" fn kmain() -> ! {
} }
} }
// Tiny userspace program: li a7, SYS_EXIT; ecall; j -4
// SYS_EXIT = 16 = 0x10
// RISC-V instructions (little-endian):
// li a7, 16 → addi a7, zero, 16 → 0x01000893
// ecall → 0x00000073
// j -4 → 0xffdff06f
static USER_EXIT_PROGRAM: [u8; 12] = [ static USER_EXIT_PROGRAM: [u8; 12] = [
0x93, 0x08, 0x00, 0x01, // addi a7, zero, 16 0x93, 0x08, 0x00, 0x01, // addi a7, zero, 16 (SYS_EXIT)
0x73, 0x00, 0x00, 0x00, // ecall 0x73, 0x00, 0x00, 0x00, // ecall
0x6f, 0xf0, 0xdf, 0xff, // j -4 0x6f, 0xf0, 0xdf, 0xff, // j -4
]; ];
const USER_VA: usize = 0x10000; const USER_VA: usize = 0x10000;
fn userspace_demo( fn userspace_demo(uart: &Uart, kernel_pt: &mmu::PageTable) {
_kern: &mut kernel::Kernel<platform::RiscVPlatform>,
uart: &Uart,
kernel_pt: &mmu::PageTable,
) {
uart.write_str("k-os: --- userspace demo ---\n"); uart.write_str("k-os: --- userspace demo ---\n");
// Allocate a page for the user program and copy code into it
let program_page = mmu::allocator::alloc_zeroed_page(); let program_page = mmu::allocator::alloc_zeroed_page();
unsafe { unsafe {
core::ptr::copy_nonoverlapping( core::ptr::copy_nonoverlapping(
@@ -117,32 +105,23 @@ fn userspace_demo(
); );
} }
// Create user page table with program mapped at USER_VA let user_pt = mmu::create_user_page_table(kernel_pt, program_page, 1, USER_VA);
let user_pt = mmu::create_user_page_table(
kernel_pt,
program_page,
1,
USER_VA,
);
uart.write_str("k-os: user page table created, entering U-mode\n"); uart.write_str("k-os: user page table created, entering U-mode\n");
// Activate user page table
mmu::activate(user_pt); mmu::activate(user_pt);
// Set sscratch to kernel stack so trap vector can switch stacks
let kernel_sp: usize; let kernel_sp: usize;
unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) }; unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) };
trap::set_kernel_stack(kernel_sp); trap::set_kernel_stack(kernel_sp);
// Enter user mode: set sstatus.SPP=0 (U-mode), sepc=USER_VA, sret let user_stack = USER_VA - 8;
let user_stack = USER_VA - 8; // stack below program
unsafe { unsafe {
core::arch::asm!( core::arch::asm!(
"csrc sstatus, {spp}", // clear SPP → return to U-mode "csrc sstatus, {spp}",
"csrs sstatus, {spie}", // set SPIE → enable interrupts after sret "csrs sstatus, {spie}",
"csrw sepc, {entry}", // return address = user entry "csrw sepc, {entry}",
"mv sp, {stack}", // user stack pointer (will be saved to sscratch on swap) "mv sp, {stack}",
"sret", "sret",
spp = in(reg) 1usize << 8, spp = in(reg) 1usize << 8,
spie = in(reg) 1usize << 5, spie = in(reg) 1usize << 5,

View File

@@ -149,15 +149,15 @@ pub fn set_kernel_stack(kernel_sp: usize) {
fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> { fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
use domain::message::Message; use domain::message::Message;
use domain::syscall::Syscall; use domain::syscall::{Syscall, WaitSet};
use types::syscall_abi::*; use types::syscall_abi::*;
let nr = frame.regs[17]; // a7 let nr = frame.regs[17]; // a7
let a0 = frame.regs[10]; // a0 let a0 = frame.regs[10];
let a1 = frame.regs[11]; // a1 let a1 = frame.regs[11];
let a2 = frame.regs[12]; // a2 let a2 = frame.regs[12];
let a3 = frame.regs[13]; // a3 let a3 = frame.regs[13];
let a4 = frame.regs[14]; // a4 let a4 = frame.regs[14];
match nr { match nr {
SYS_EXIT => Some(Syscall::Exit), SYS_EXIT => Some(Syscall::Exit),
@@ -174,6 +174,46 @@ fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
let slot = types::bounded::CapSlot::new(a0 as u8)?; let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::Receive { endpoint: slot }) Some(Syscall::Receive { endpoint: slot })
} }
SYS_DELEGATE => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
let to = types::bounded::CapSlot::new(a1 as u8)?;
let perms = types::permission::PermissionSet::from_bits(a2 as u8);
Some(Syscall::Delegate { slot, to_endpoint: to, permissions: perms })
}
SYS_WAIT_FOR_INTERRUPT => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::WaitForInterrupt { irq: slot })
}
SYS_ACKNOWLEDGE_INTERRUPT => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::AcknowledgeInterrupt { irq: slot })
}
SYS_WAIT_ANY => {
let ep0 = if a0 != usize::MAX { types::bounded::CapSlot::new(a0 as u8) } else { None };
let ep1 = if a1 != usize::MAX { types::bounded::CapSlot::new(a1 as u8) } else { None };
let ep2 = if a2 != usize::MAX { types::bounded::CapSlot::new(a2 as u8) } else { None };
let ep3 = if a3 != usize::MAX { types::bounded::CapSlot::new(a3 as u8) } else { None };
let sources = WaitSet::new(
[ep0, ep1, ep2, ep3],
[None, None, None, None],
);
Some(Syscall::WaitAny { sources })
}
SYS_CREATE_PROCESS => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::CreateProcess {
create_cap: slot,
image: types::address::DataRef::empty(),
})
}
SYS_KILL => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::Kill { process: slot })
}
SYS_REVOKE => {
let slot = types::bounded::CapSlot::new(a0 as u8)?;
Some(Syscall::Revoke { slot })
}
SYS_SPAWN_THREAD => Some(Syscall::SpawnThread { SYS_SPAWN_THREAD => Some(Syscall::SpawnThread {
entry: types::address::EntryPoint::from_raw(a0), entry: types::address::EntryPoint::from_raw(a0),
stack: types::address::StackPointer::from_raw(a1), stack: types::address::StackPointer::from_raw(a1),
@@ -182,6 +222,13 @@ fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
let count = types::bounded::PageCount::new(a0 as u16)?; let count = types::bounded::PageCount::new(a0 as u16)?;
Some(Syscall::RequestPages { count }) Some(Syscall::RequestPages { count })
} }
SYS_RELEASE_PAGES => {
let count = types::bounded::PageCount::new(a1 as u16)?;
Some(Syscall::ReleasePages {
addr: types::address::VirtualAddress::from_raw(a0),
count,
})
}
_ => None, _ => None,
} }
} }
@@ -214,31 +261,47 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
let scause: usize; let scause: usize;
unsafe { core::arch::asm!("csrr {0}, scause", out(reg) scause) }; unsafe { core::arch::asm!("csrr {0}, scause", out(reg) scause) };
let uart = crate::hw::uart::Uart::new();
if scause & CAUSE_INTERRUPT_BIT != 0 { if scause & CAUSE_INTERRUPT_BIT != 0 {
let cause = scause & !CAUSE_INTERRUPT_BIT; handle_interrupt(scause & !CAUSE_INTERRUPT_BIT);
} else {
handle_exception(frame, scause);
}
}
fn handle_interrupt(cause: usize) {
match cause { match cause {
CAUSE_S_TIMER => { CAUSE_S_TIMER => {
crate::hw::timer::handle_interrupt(); crate::hw::timer::handle_interrupt();
let kern = crate::kernel_ref();
let core = types::id::CoreId::from_raw(0);
let _decision = kern.handle_tick(core);
} }
CAUSE_S_EXTERNAL => { CAUSE_S_EXTERNAL => {
uart.write_str("[EXT]"); let irq_num = crate::hw::plic::Plic::claim();
if irq_num != 0 {
let kern = crate::kernel_ref();
let irq = types::id::IrqId::from_raw(irq_num as u16);
kern.handle_interrupt(irq);
crate::hw::plic::Plic::complete(irq_num);
}
} }
_ => {} _ => {}
} }
} else { }
fn handle_exception(frame: &mut TrapFrame, scause: usize) {
match scause { match scause {
CAUSE_ECALL_U => { CAUSE_ECALL_U => {
frame.sepc += 4; frame.sepc += 4;
if let Some(syscall) = decode_syscall(frame) { if let Some(syscall) = decode_syscall(frame) {
let kern = crate::kernel_mut(); let kern = crate::kernel_ref();
let outcome = kern.handle_syscall(syscall); let outcome = kern.handle_syscall(syscall);
write_result(frame, &outcome); write_result(frame, &outcome);
match &outcome { match &outcome {
kernel::SyscallOutcome::TerminateCaller => { kernel::SyscallOutcome::TerminateCaller => {
let uart = crate::hw::uart::Uart::new();
uart.write_str("k-os: process exited\n"); uart.write_str("k-os: process exited\n");
} }
_ => {} _ => {}
@@ -246,6 +309,7 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
} }
} }
_ => { _ => {
let uart = crate::hw::uart::Uart::new();
uart.write_str("FAULT: scause="); uart.write_str("FAULT: scause=");
uart.write_hex(scause); uart.write_hex(scause);
let stval: usize; let stval: usize;
@@ -259,4 +323,3 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
} }
} }
} }
}

View File

@@ -41,7 +41,6 @@ impl PermissionSet {
self.0 & other.0 == self.0 self.0 & other.0 == self.0
} }
#[cfg(kani)]
pub fn from_bits(bits: u8) -> Self { pub fn from_bits(bits: u8) -> Self {
PermissionSet(bits) PermissionSet(bits)
} }