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:
35
crates/adapter-riscv/src/kernel_cell.rs
Normal file
35
crates/adapter-riscv/src/kernel_cell.rs
Normal 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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,49 +261,65 @@ 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);
|
||||||
match cause {
|
|
||||||
CAUSE_S_TIMER => {
|
|
||||||
crate::hw::timer::handle_interrupt();
|
|
||||||
}
|
|
||||||
CAUSE_S_EXTERNAL => {
|
|
||||||
uart.write_str("[EXT]");
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
match scause {
|
handle_exception(frame, scause);
|
||||||
CAUSE_ECALL_U => {
|
}
|
||||||
frame.sepc += 4;
|
}
|
||||||
|
|
||||||
if let Some(syscall) = decode_syscall(frame) {
|
fn handle_interrupt(cause: usize) {
|
||||||
let kern = crate::kernel_mut();
|
match cause {
|
||||||
let outcome = kern.handle_syscall(syscall);
|
CAUSE_S_TIMER => {
|
||||||
write_result(frame, &outcome);
|
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 => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match &outcome {
|
fn handle_exception(frame: &mut TrapFrame, scause: usize) {
|
||||||
kernel::SyscallOutcome::TerminateCaller => {
|
match scause {
|
||||||
uart.write_str("k-os: process exited\n");
|
CAUSE_ECALL_U => {
|
||||||
}
|
frame.sepc += 4;
|
||||||
_ => {}
|
|
||||||
|
if let Some(syscall) = decode_syscall(frame) {
|
||||||
|
let kern = crate::kernel_ref();
|
||||||
|
let outcome = kern.handle_syscall(syscall);
|
||||||
|
write_result(frame, &outcome);
|
||||||
|
|
||||||
|
match &outcome {
|
||||||
|
kernel::SyscallOutcome::TerminateCaller => {
|
||||||
|
let uart = crate::hw::uart::Uart::new();
|
||||||
|
uart.write_str("k-os: process exited\n");
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
}
|
||||||
uart.write_str("FAULT: scause=");
|
_ => {
|
||||||
uart.write_hex(scause);
|
let uart = crate::hw::uart::Uart::new();
|
||||||
let stval: usize;
|
uart.write_str("FAULT: scause=");
|
||||||
unsafe { core::arch::asm!("csrr {0}, stval", out(reg) stval) };
|
uart.write_hex(scause);
|
||||||
uart.write_str(" stval=");
|
let stval: usize;
|
||||||
uart.write_hex(stval);
|
unsafe { core::arch::asm!("csrr {0}, stval", out(reg) stval) };
|
||||||
uart.write_str(" sepc=");
|
uart.write_str(" stval=");
|
||||||
uart.write_hex(frame.sepc);
|
uart.write_hex(stval);
|
||||||
uart.put_char(b'\n');
|
uart.write_str(" sepc=");
|
||||||
loop { unsafe { core::arch::asm!("wfi") }; }
|
uart.write_hex(frame.sepc);
|
||||||
}
|
uart.put_char(b'\n');
|
||||||
|
loop { unsafe { core::arch::asm!("wfi") }; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user