refactor(adapter-riscv): restructure into hw/, mmu/, trap/ modules with board constants

Add Priority::DEFAULT and TimeSlice::DEFAULT to types crate.
All QEMU virt addresses consolidated in board.rs.
Structure mirrors future adapter-arm/ layout.
This commit is contained in:
2026-10-18 02:05:57 +02:00
parent 66a730b2fe
commit d327794d33
13 changed files with 100 additions and 109 deletions

View File

@@ -0,0 +1,8 @@
pub const RAM_BASE: usize = 0x80000000;
pub const RAM_SIZE: usize = 128 * 1024 * 1024;
pub const UART_BASE: usize = 0x10000000;
pub const PLIC_BASE: usize = 0x0C000000;
pub const CLINT_BASE: usize = 0x02000000;
pub const USER_POOL_BASE: usize = 0x84000000;
pub const USER_POOL_PAGES: usize = 256;
pub const TICK_INTERVAL: u64 = 10_000_000;

View File

@@ -0,0 +1,3 @@
pub mod plic;
pub mod timer;
pub mod uart;

View File

@@ -1,14 +1,14 @@
const PLIC_BASE: usize = 0x0C000000;
const PLIC_PRIORITY: usize = PLIC_BASE;
const PLIC_S_ENABLE: usize = PLIC_BASE + 0x2080; // S-mode hart 0 context = 1
const PLIC_S_THRESHOLD: usize = PLIC_BASE + 0x201000;
const PLIC_S_CLAIM: usize = PLIC_BASE + 0x201004;
use crate::board;
const PLIC_PRIORITY: usize = board::PLIC_BASE;
const PLIC_S_ENABLE: usize = board::PLIC_BASE + 0x2080;
const PLIC_S_THRESHOLD: usize = board::PLIC_BASE + 0x201000;
const PLIC_S_CLAIM: usize = board::PLIC_BASE + 0x201004;
pub struct Plic;
impl Plic {
pub fn init() {
// set threshold to 0 (accept all priorities)
unsafe { core::ptr::write_volatile(PLIC_S_THRESHOLD as *mut u32, 0) };
}
@@ -20,7 +20,6 @@ impl Plic {
let current = core::ptr::read_volatile(addr as *const u32);
core::ptr::write_volatile(addr as *mut u32, current | (1 << bit));
}
// set priority > 0
let prio_addr = PLIC_PRIORITY + (irq as usize) * 4;
unsafe { core::ptr::write_volatile(prio_addr as *mut u32, 1) };
}

View File

@@ -1,4 +1,4 @@
const TICK_INTERVAL: u64 = 10_000_000; // 1 second at 10MHz
use crate::board;
pub fn read_time() -> u64 {
let time: u64;
@@ -12,21 +12,21 @@ fn sbi_set_timer(stime: u64) {
"ecall",
in("a0") stime,
in("a6") 0usize,
in("a7") 0x54494D45usize, // SBI Timer extension ID
in("a7") 0x54494D45usize,
);
}
}
pub fn init_timer() {
pub fn init() {
let now = read_time();
sbi_set_timer(now + TICK_INTERVAL);
sbi_set_timer(now + board::TICK_INTERVAL);
}
pub fn set_next_timer(deadline: u64) {
pub fn set_next(deadline: u64) {
sbi_set_timer(deadline);
}
pub fn handle_timer_interrupt() {
pub fn handle_interrupt() {
let now = read_time();
sbi_set_timer(now + TICK_INTERVAL);
sbi_set_timer(now + board::TICK_INTERVAL);
}

View File

@@ -1,4 +1,4 @@
const UART_BASE: usize = 0x10000000;
use crate::board;
pub struct Uart {
base: usize,
@@ -6,7 +6,7 @@ pub struct Uart {
impl Uart {
pub fn new() -> Self {
Self { base: UART_BASE }
Self { base: board::UART_BASE }
}
pub fn put_char(&self, byte: u8) {

View File

@@ -1,15 +1,14 @@
#![no_std]
#![no_main]
mod page_table;
mod board;
mod hw;
mod mmu;
mod platform;
mod plic;
mod timer;
mod trap;
mod uart;
use core::arch::global_asm;
use uart::Uart;
use hw::uart::Uart;
global_asm!(
r#"
@@ -47,27 +46,24 @@ extern "C" fn kmain() -> ! {
uart.write_str("k-os: booted on RISC-V\n");
let heap_start = unsafe { &_heap_start as *const u8 as usize };
page_table::init_page_allocator(heap_start);
let kernel_pt = page_table::init_kernel_page_table();
page_table::activate(kernel_pt);
mmu::init_allocator(heap_start);
let kernel_pt = mmu::init_kernel_page_table();
mmu::activate(kernel_pt);
uart.write_str("k-os: MMU enabled (Sv39)\n");
trap::init_trap();
timer::init_timer();
plic::Plic::init();
trap::init();
hw::timer::init();
hw::plic::Plic::init();
uart.write_str("k-os: traps and timer initialized\n");
let riscv_platform = platform::RiscVPlatform::new();
let pool = domain::memory::page_pool::PagePool::new(
types::address::PhysicalAddress::from_raw(0x84000000),
256,
types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE),
board::USER_POOL_PAGES,
);
let mut kern = kernel::Kernel::new(riscv_platform, pool);
let (pid, tid) = kern
.boot(
types::bounded::Priority::new(16).unwrap_or_else(|| unreachable!()),
types::time::TimeSlice::new(100),
)
.boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT)
.unwrap_or_else(|_| loop { unsafe { core::arch::asm!("wfi") }; });
uart.write_str("k-os: kernel started (pid=");
@@ -100,8 +96,7 @@ fn ipc_demo(
uart.write_str("k-os: --- IPC demo ---\n");
// Create process B using init's ProcessCreate cap (slot 0)
let proc_b_slot = match kern.handle_syscall(Syscall::CreateProcess {
let _proc_b_slot = match kern.handle_syscall(Syscall::CreateProcess {
create_cap: CapSlot::new(0).unwrap_or_else(|| unreachable!()),
image: DataRef::empty(),
}) {
@@ -115,10 +110,8 @@ fn ipc_demo(
let tid_b = ThreadId::from_raw(1);
uart.write_str("k-os: process B created\n");
// Create an endpoint
let ep_id = kern.create_endpoint().unwrap_or_else(|_| unreachable!());
// Grant endpoint caps to both processes
let ep_cap_a = kern.grant_capability(
init_pid,
Resource::Endpoint(ep_id),
@@ -137,10 +130,8 @@ fn ipc_demo(
uart.write_str("k-os: endpoint created, caps granted\n");
// Process B calls Receive (blocks — no sender yet)
kern.switch_to_thread(tid_b);
let outcome = kern.handle_syscall(Syscall::Receive { endpoint: ep_cap_b });
match &outcome {
match kern.handle_syscall(Syscall::Receive { endpoint: ep_cap_b }) {
SyscallOutcome::BlockCaller(_) => {
uart.write_str("k-os: process B blocked on Receive\n");
}
@@ -150,15 +141,13 @@ fn ipc_demo(
}
}
// Process A calls Send (finds B waiting → delivers message)
let tid_a = ThreadId::from_raw(0);
kern.switch_to_thread(tid_a);
let msg = Message::from_words([42, 7, 0, 0]);
let outcome = kern.handle_syscall(Syscall::Send {
match kern.handle_syscall(Syscall::Send {
endpoint: ep_cap_a,
message: msg,
});
match &outcome {
}) {
SyscallOutcome::SwitchTo(target) => {
uart.write_str("k-os: Send delivered, switching to thread ");
uart.write_dec(target.raw() as usize);
@@ -170,7 +159,6 @@ fn ipc_demo(
}
}
// Check B's pending result
match kern.pending_result(tid_b) {
Some(SyscallResult::Message(m)) => {
uart.write_str("k-os: IPC complete — received: [");

View File

@@ -0,0 +1,19 @@
use super::page_table::{PageTable, PAGE_SIZE};
// Boot-time bump allocator for page tables.
// Only used during init before the scheduler starts.
static mut NEXT_FREE: usize = 0;
pub fn init_allocator(start: usize) {
unsafe { NEXT_FREE = start; }
}
pub fn alloc_page_table() -> &'static mut PageTable {
unsafe {
let addr = NEXT_FREE;
NEXT_FREE += PAGE_SIZE;
let table = &mut *(addr as *mut PageTable);
table.zero();
table
}
}

View File

@@ -0,0 +1,5 @@
pub mod allocator;
pub mod page_table;
pub use allocator::init_allocator;
pub use page_table::{activate, init_kernel_page_table, create_user_page_table, PageTable, PAGE_SIZE};

View File

@@ -1,4 +1,8 @@
use crate::board;
use super::allocator::alloc_page_table;
pub const PAGE_SIZE: usize = 4096;
const PTE_V: u64 = 1 << 0;
const PTE_R: u64 = 1 << 1;
const PTE_W: u64 = 1 << 2;
@@ -53,22 +57,6 @@ impl PageTable {
}
}
static mut PAGE_ALLOC_NEXT: usize = 0;
pub fn init_page_allocator(start: usize) {
unsafe { PAGE_ALLOC_NEXT = start; }
}
pub fn alloc_page_table() -> &'static mut PageTable {
unsafe {
let addr = PAGE_ALLOC_NEXT;
PAGE_ALLOC_NEXT += PAGE_SIZE;
let table = &mut *(addr as *mut PageTable);
table.zero();
table
}
}
fn vpn(va: usize, level: usize) -> usize {
(va >> (12 + 9 * level)) & 0x1FF
}
@@ -110,27 +98,23 @@ fn map_range(root: &mut PageTable, va_start: usize, pa_start: usize, pages: usiz
pub fn init_kernel_page_table() -> &'static mut PageTable {
let root = alloc_page_table();
let ram_start = 0x80000000usize;
let ram_pages = 128 * 1024 * 1024 / PAGE_SIZE; // 128 MiB
map_range(root, ram_start, ram_start, ram_pages, true, true, true, false, true);
let ram_pages = board::RAM_SIZE / PAGE_SIZE;
map_range(root, board::RAM_BASE, board::RAM_BASE, ram_pages, true, true, true, false, true);
let uart_start = 0x10000000usize;
map_range(root, uart_start, uart_start, 1, true, true, false, false, true);
map_range(root, board::UART_BASE, board::UART_BASE, 1, true, true, false, false, true);
let plic_start = 0x0C000000usize;
let plic_pages = 0x400000 / PAGE_SIZE;
map_range(root, plic_start, plic_start, plic_pages, true, true, false, false, true);
map_range(root, board::PLIC_BASE, board::PLIC_BASE, plic_pages, true, true, false, false, true);
let clint_start = 0x02000000usize;
let clint_pages = 0x10000 / PAGE_SIZE;
map_range(root, clint_start, clint_start, clint_pages, true, true, false, false, true);
map_range(root, board::CLINT_BASE, board::CLINT_BASE, clint_pages, true, true, false, false, true);
root
}
pub fn activate(root: &PageTable) {
let ppn = (root as *const PageTable as u64) >> 12;
let satp = (8u64 << 60) | ppn; // mode=8 (Sv39), ASID=0
let satp = (8u64 << 60) | ppn;
unsafe {
core::arch::asm!(
"csrw satp, {0}",
@@ -148,18 +132,14 @@ pub fn create_user_page_table(
) -> &'static mut PageTable {
let root = alloc_page_table();
// Copy kernel mappings (upper half — entries 256-511 cover high VA space)
// For identity mapping, copy all valid entries from kernel root
for i in 0..512 {
if kernel_root.entries[i].is_valid() {
root.entries[i] = kernel_root.entries[i];
}
}
// Map user program pages
map_range(root, user_va, program_pa, program_pages, true, false, true, true, false);
// Map user stack (1 page at user_va - PAGE_SIZE)
let stack_va = user_va.wrapping_sub(PAGE_SIZE);
let stack_table = alloc_page_table();
let stack_pa = stack_table as *const PageTable as usize;

View File

@@ -9,8 +9,8 @@ use types::{
time::{Ticks, TimerDuration},
};
use crate::plic::Plic;
use crate::timer;
use crate::board;
use crate::hw::{plic::Plic, timer};
pub struct RiscVPlatform {
num_cores: u8,
@@ -23,22 +23,17 @@ impl RiscVPlatform {
}
impl Platform for RiscVPlatform {
fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {
// page table activation happens separately per context switch
}
fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {}
fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {
// context switch is handled by the trap vector assembly
}
fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {}
fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! {
// handled by setting up TrapFrame and sret in the main loop
loop { unsafe { core::arch::asm!("wfi") }; }
}
fn set_timer(&mut self, _duration: TimerDuration) {
let now = timer::read_time();
timer::set_next_timer(now + 10_000_000);
timer::set_next(now + board::TICK_INTERVAL);
}
fn current_ticks(&self) -> Ticks {
@@ -53,9 +48,7 @@ impl Platform for RiscVPlatform {
Plic::enable(irq.raw() as u32);
}
fn send_ipi(&mut self, _target: CoreId) {
// single core — no-op
}
fn send_ipi(&mut self, _target: CoreId) {}
fn core_id(&self) -> CoreId {
CoreId::from_raw(0)
@@ -72,6 +65,5 @@ impl Platform for RiscVPlatform {
_size: PageCount,
_permissions: PermissionSet,
) {
// page table mapping for shared memory — deferred to full MMU integration
}
}

View File

@@ -2,7 +2,7 @@ use core::arch::global_asm;
#[repr(C)]
pub struct TrapFrame {
pub regs: [usize; 32], // x0-x31
pub regs: [usize; 32],
pub sepc: usize,
pub sstatus: usize,
}
@@ -17,7 +17,7 @@ impl TrapFrame {
}
}
// sscratch = 0 when in S-mode, = kernel_stack_top when in U-mode
// sscratch = 0 when in S-mode, = kernel_stack_top when in U-mode.
// On trap: swap sp/sscratch. If sp==0 after swap, we came from S-mode (undo swap).
global_asm!(r#"
.align 4
@@ -25,10 +25,8 @@ global_asm!(r#"
trap_vector:
csrrw sp, sscratch, sp
bnez sp, _from_user
# came from S-mode: sscratch had 0, sp is now 0, old sp is in sscratch
csrrw sp, sscratch, sp # undo: sp = old kernel sp, sscratch = 0
csrrw sp, sscratch, sp
_from_user:
# sp is now a valid kernel stack (either kept or swapped in)
addi sp, sp, -272
sd x1, 8(sp)
@@ -62,7 +60,6 @@ _from_user:
sd x30, 240(sp)
sd x31, 248(sp)
# save user sp from sscratch (0 if from S-mode, user sp if from U-mode)
csrr t0, sscratch
sd t0, 16(sp)
@@ -79,12 +76,9 @@ _from_user:
ld t0, 264(sp)
csrw sstatus, t0
# check if returning to U-mode: sstatus.SPP (bit 8)
# if SPP=0 (user mode), restore sscratch with user sp
andi t1, t0, 0x100
bnez t1, _restore_smode
# returning to U-mode: put user sp in sscratch
ld t0, 16(sp)
csrw sscratch, t0
@@ -122,11 +116,10 @@ _restore_smode:
addi sp, sp, 272
# if returning to U-mode, swap sp/sscratch to restore user sp
csrr t0, sstatus
andi t0, t0, 0x100
bnez t0, _sret_smode
csrrw sp, sscratch, sp # sp = user sp, sscratch = kernel sp (for next trap)
csrrw sp, sscratch, sp
_sret_smode:
sret
"#);
@@ -136,21 +129,15 @@ const CAUSE_S_TIMER: usize = 5;
const CAUSE_S_EXTERNAL: usize = 9;
const CAUSE_ECALL_U: usize = 8;
pub fn init_trap() {
pub fn init() {
unsafe extern "C" {
fn trap_vector();
}
unsafe {
let addr = trap_vector as usize;
core::arch::asm!("csrw stvec, {0}", in(reg) addr);
// sscratch = 0 means "currently in S-mode"
core::arch::asm!("csrw sscratch, zero");
// enable supervisor interrupts
core::arch::asm!("csrs sstatus, {0}", in(reg) 1usize << 1);
// enable timer + external + software interrupts in sie
let sie_bits = (1usize << 5) | (1usize << 9) | (1usize << 1);
core::arch::asm!("csrs sie, {0}", in(reg) sie_bits);
}
@@ -167,13 +154,13 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
let scause: usize;
unsafe { core::arch::asm!("csrr {0}, scause", out(reg) scause) };
let uart = crate::uart::Uart::new();
let uart = crate::hw::uart::Uart::new();
if scause & CAUSE_INTERRUPT_BIT != 0 {
let cause = scause & !CAUSE_INTERRUPT_BIT;
match cause {
CAUSE_S_TIMER => {
crate::timer::handle_timer_interrupt();
crate::hw::timer::handle_interrupt();
}
CAUSE_S_EXTERNAL => {
uart.write_str("[EXT]");

View File

@@ -3,3 +3,8 @@ use crate::macros::bounded;
bounded!(CapSlot, u8, 0, 63);
bounded!(Priority, u8, 0, 31);
bounded!(PageCount, u16, 1, 1024);
impl Priority {
pub const DEFAULT: Self = Self(16);
}

View File

@@ -25,6 +25,11 @@ pub struct TimeSlice {
}
impl TimeSlice {
pub const DEFAULT: Self = Self {
remaining: 100,
quantum: 100,
};
pub fn new(quantum: u32) -> Self {
Self {
remaining: quantum,