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; use crate::board;
const PLIC_PRIORITY: usize = PLIC_BASE;
const PLIC_S_ENABLE: usize = PLIC_BASE + 0x2080; // S-mode hart 0 context = 1 const PLIC_PRIORITY: usize = board::PLIC_BASE;
const PLIC_S_THRESHOLD: usize = PLIC_BASE + 0x201000; const PLIC_S_ENABLE: usize = board::PLIC_BASE + 0x2080;
const PLIC_S_CLAIM: usize = PLIC_BASE + 0x201004; const PLIC_S_THRESHOLD: usize = board::PLIC_BASE + 0x201000;
const PLIC_S_CLAIM: usize = board::PLIC_BASE + 0x201004;
pub struct Plic; pub struct Plic;
impl Plic { impl Plic {
pub fn init() { pub fn init() {
// set threshold to 0 (accept all priorities)
unsafe { core::ptr::write_volatile(PLIC_S_THRESHOLD as *mut u32, 0) }; 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); let current = core::ptr::read_volatile(addr as *const u32);
core::ptr::write_volatile(addr as *mut u32, current | (1 << bit)); core::ptr::write_volatile(addr as *mut u32, current | (1 << bit));
} }
// set priority > 0
let prio_addr = PLIC_PRIORITY + (irq as usize) * 4; let prio_addr = PLIC_PRIORITY + (irq as usize) * 4;
unsafe { core::ptr::write_volatile(prio_addr as *mut u32, 1) }; 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 { pub fn read_time() -> u64 {
let time: u64; let time: u64;
@@ -12,21 +12,21 @@ fn sbi_set_timer(stime: u64) {
"ecall", "ecall",
in("a0") stime, in("a0") stime,
in("a6") 0usize, in("a6") 0usize,
in("a7") 0x54494D45usize, // SBI Timer extension ID in("a7") 0x54494D45usize,
); );
} }
} }
pub fn init_timer() { pub fn init() {
let now = read_time(); 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); sbi_set_timer(deadline);
} }
pub fn handle_timer_interrupt() { pub fn handle_interrupt() {
let now = read_time(); 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 { pub struct Uart {
base: usize, base: usize,
@@ -6,7 +6,7 @@ pub struct Uart {
impl Uart { impl Uart {
pub fn new() -> Self { pub fn new() -> Self {
Self { base: UART_BASE } Self { base: board::UART_BASE }
} }
pub fn put_char(&self, byte: u8) { pub fn put_char(&self, byte: u8) {

View File

@@ -1,15 +1,14 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
mod page_table; mod board;
mod hw;
mod mmu;
mod platform; mod platform;
mod plic;
mod timer;
mod trap; mod trap;
mod uart;
use core::arch::global_asm; use core::arch::global_asm;
use uart::Uart; use hw::uart::Uart;
global_asm!( global_asm!(
r#" r#"
@@ -47,27 +46,24 @@ extern "C" fn kmain() -> ! {
uart.write_str("k-os: booted on RISC-V\n"); uart.write_str("k-os: booted on RISC-V\n");
let heap_start = unsafe { &_heap_start as *const u8 as usize }; let heap_start = unsafe { &_heap_start as *const u8 as usize };
page_table::init_page_allocator(heap_start); mmu::init_allocator(heap_start);
let kernel_pt = page_table::init_kernel_page_table(); let kernel_pt = mmu::init_kernel_page_table();
page_table::activate(kernel_pt); mmu::activate(kernel_pt);
uart.write_str("k-os: MMU enabled (Sv39)\n"); uart.write_str("k-os: MMU enabled (Sv39)\n");
trap::init_trap(); trap::init();
timer::init_timer(); hw::timer::init();
plic::Plic::init(); hw::plic::Plic::init();
uart.write_str("k-os: traps and timer initialized\n"); uart.write_str("k-os: traps and timer initialized\n");
let riscv_platform = platform::RiscVPlatform::new(); let riscv_platform = platform::RiscVPlatform::new();
let pool = domain::memory::page_pool::PagePool::new( let pool = domain::memory::page_pool::PagePool::new(
types::address::PhysicalAddress::from_raw(0x84000000), types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE),
256, board::USER_POOL_PAGES,
); );
let mut kern = kernel::Kernel::new(riscv_platform, pool); let mut kern = kernel::Kernel::new(riscv_platform, pool);
let (pid, tid) = kern let (pid, tid) = kern
.boot( .boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT)
types::bounded::Priority::new(16).unwrap_or_else(|| unreachable!()),
types::time::TimeSlice::new(100),
)
.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 (pid="); uart.write_str("k-os: kernel started (pid=");
@@ -100,8 +96,7 @@ fn ipc_demo(
uart.write_str("k-os: --- IPC demo ---\n"); 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!()), create_cap: CapSlot::new(0).unwrap_or_else(|| unreachable!()),
image: DataRef::empty(), image: DataRef::empty(),
}) { }) {
@@ -115,10 +110,8 @@ fn ipc_demo(
let tid_b = ThreadId::from_raw(1); let tid_b = ThreadId::from_raw(1);
uart.write_str("k-os: process B created\n"); uart.write_str("k-os: process B created\n");
// Create an endpoint
let ep_id = kern.create_endpoint().unwrap_or_else(|_| unreachable!()); let ep_id = kern.create_endpoint().unwrap_or_else(|_| unreachable!());
// Grant endpoint caps to both processes
let ep_cap_a = kern.grant_capability( let ep_cap_a = kern.grant_capability(
init_pid, init_pid,
Resource::Endpoint(ep_id), Resource::Endpoint(ep_id),
@@ -137,10 +130,8 @@ fn ipc_demo(
uart.write_str("k-os: endpoint created, caps granted\n"); uart.write_str("k-os: endpoint created, caps granted\n");
// Process B calls Receive (blocks — no sender yet)
kern.switch_to_thread(tid_b); kern.switch_to_thread(tid_b);
let outcome = kern.handle_syscall(Syscall::Receive { endpoint: ep_cap_b }); match kern.handle_syscall(Syscall::Receive { endpoint: ep_cap_b }) {
match &outcome {
SyscallOutcome::BlockCaller(_) => { SyscallOutcome::BlockCaller(_) => {
uart.write_str("k-os: process B blocked on Receive\n"); 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); let tid_a = ThreadId::from_raw(0);
kern.switch_to_thread(tid_a); kern.switch_to_thread(tid_a);
let msg = Message::from_words([42, 7, 0, 0]); 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, endpoint: ep_cap_a,
message: msg, message: msg,
}); }) {
match &outcome {
SyscallOutcome::SwitchTo(target) => { SyscallOutcome::SwitchTo(target) => {
uart.write_str("k-os: Send delivered, switching to thread "); uart.write_str("k-os: Send delivered, switching to thread ");
uart.write_dec(target.raw() as usize); 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) { match kern.pending_result(tid_b) {
Some(SyscallResult::Message(m)) => { Some(SyscallResult::Message(m)) => {
uart.write_str("k-os: IPC complete — received: ["); 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; pub const PAGE_SIZE: usize = 4096;
const PTE_V: u64 = 1 << 0; const PTE_V: u64 = 1 << 0;
const PTE_R: u64 = 1 << 1; const PTE_R: u64 = 1 << 1;
const PTE_W: u64 = 1 << 2; 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 { fn vpn(va: usize, level: usize) -> usize {
(va >> (12 + 9 * level)) & 0x1FF (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 { pub fn init_kernel_page_table() -> &'static mut PageTable {
let root = alloc_page_table(); let root = alloc_page_table();
let ram_start = 0x80000000usize; let ram_pages = board::RAM_SIZE / PAGE_SIZE;
let ram_pages = 128 * 1024 * 1024 / PAGE_SIZE; // 128 MiB map_range(root, board::RAM_BASE, board::RAM_BASE, ram_pages, true, true, true, false, true);
map_range(root, ram_start, ram_start, ram_pages, true, true, true, false, true);
let uart_start = 0x10000000usize; map_range(root, board::UART_BASE, board::UART_BASE, 1, true, true, false, false, true);
map_range(root, uart_start, uart_start, 1, true, true, false, false, true);
let plic_start = 0x0C000000usize;
let plic_pages = 0x400000 / PAGE_SIZE; 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; 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 root
} }
pub fn activate(root: &PageTable) { pub fn activate(root: &PageTable) {
let ppn = (root as *const PageTable as u64) >> 12; 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 { unsafe {
core::arch::asm!( core::arch::asm!(
"csrw satp, {0}", "csrw satp, {0}",
@@ -148,18 +132,14 @@ pub fn create_user_page_table(
) -> &'static mut PageTable { ) -> &'static mut PageTable {
let root = alloc_page_table(); 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 { for i in 0..512 {
if kernel_root.entries[i].is_valid() { if kernel_root.entries[i].is_valid() {
root.entries[i] = kernel_root.entries[i]; 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_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_va = user_va.wrapping_sub(PAGE_SIZE);
let stack_table = alloc_page_table(); let stack_table = alloc_page_table();
let stack_pa = stack_table as *const PageTable as usize; let stack_pa = stack_table as *const PageTable as usize;

View File

@@ -9,8 +9,8 @@ use types::{
time::{Ticks, TimerDuration}, time::{Ticks, TimerDuration},
}; };
use crate::plic::Plic; use crate::board;
use crate::timer; use crate::hw::{plic::Plic, timer};
pub struct RiscVPlatform { pub struct RiscVPlatform {
num_cores: u8, num_cores: u8,
@@ -23,22 +23,17 @@ impl RiscVPlatform {
} }
impl Platform for RiscVPlatform { impl Platform for RiscVPlatform {
fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) { fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {}
// page table activation happens separately per context switch
}
fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) { fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {}
// context switch is handled by the trap vector assembly
}
fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! { 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") }; } loop { unsafe { core::arch::asm!("wfi") }; }
} }
fn set_timer(&mut self, _duration: TimerDuration) { fn set_timer(&mut self, _duration: TimerDuration) {
let now = timer::read_time(); 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 { fn current_ticks(&self) -> Ticks {
@@ -53,9 +48,7 @@ impl Platform for RiscVPlatform {
Plic::enable(irq.raw() as u32); Plic::enable(irq.raw() as u32);
} }
fn send_ipi(&mut self, _target: CoreId) { fn send_ipi(&mut self, _target: CoreId) {}
// single core — no-op
}
fn core_id(&self) -> CoreId { fn core_id(&self) -> CoreId {
CoreId::from_raw(0) CoreId::from_raw(0)
@@ -72,6 +65,5 @@ impl Platform for RiscVPlatform {
_size: PageCount, _size: PageCount,
_permissions: PermissionSet, _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)] #[repr(C)]
pub struct TrapFrame { pub struct TrapFrame {
pub regs: [usize; 32], // x0-x31 pub regs: [usize; 32],
pub sepc: usize, pub sepc: usize,
pub sstatus: 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). // On trap: swap sp/sscratch. If sp==0 after swap, we came from S-mode (undo swap).
global_asm!(r#" global_asm!(r#"
.align 4 .align 4
@@ -25,10 +25,8 @@ global_asm!(r#"
trap_vector: trap_vector:
csrrw sp, sscratch, sp csrrw sp, sscratch, sp
bnez sp, _from_user bnez sp, _from_user
# came from S-mode: sscratch had 0, sp is now 0, old sp is in sscratch csrrw sp, sscratch, sp
csrrw sp, sscratch, sp # undo: sp = old kernel sp, sscratch = 0
_from_user: _from_user:
# sp is now a valid kernel stack (either kept or swapped in)
addi sp, sp, -272 addi sp, sp, -272
sd x1, 8(sp) sd x1, 8(sp)
@@ -62,7 +60,6 @@ _from_user:
sd x30, 240(sp) sd x30, 240(sp)
sd x31, 248(sp) sd x31, 248(sp)
# save user sp from sscratch (0 if from S-mode, user sp if from U-mode)
csrr t0, sscratch csrr t0, sscratch
sd t0, 16(sp) sd t0, 16(sp)
@@ -79,12 +76,9 @@ _from_user:
ld t0, 264(sp) ld t0, 264(sp)
csrw sstatus, t0 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 andi t1, t0, 0x100
bnez t1, _restore_smode bnez t1, _restore_smode
# returning to U-mode: put user sp in sscratch
ld t0, 16(sp) ld t0, 16(sp)
csrw sscratch, t0 csrw sscratch, t0
@@ -122,11 +116,10 @@ _restore_smode:
addi sp, sp, 272 addi sp, sp, 272
# if returning to U-mode, swap sp/sscratch to restore user sp
csrr t0, sstatus csrr t0, sstatus
andi t0, t0, 0x100 andi t0, t0, 0x100
bnez t0, _sret_smode bnez t0, _sret_smode
csrrw sp, sscratch, sp # sp = user sp, sscratch = kernel sp (for next trap) csrrw sp, sscratch, sp
_sret_smode: _sret_smode:
sret sret
"#); "#);
@@ -136,21 +129,15 @@ const CAUSE_S_TIMER: usize = 5;
const CAUSE_S_EXTERNAL: usize = 9; const CAUSE_S_EXTERNAL: usize = 9;
const CAUSE_ECALL_U: usize = 8; const CAUSE_ECALL_U: usize = 8;
pub fn init_trap() { pub fn init() {
unsafe extern "C" { unsafe extern "C" {
fn trap_vector(); fn trap_vector();
} }
unsafe { unsafe {
let addr = trap_vector as usize; let addr = trap_vector as usize;
core::arch::asm!("csrw stvec, {0}", in(reg) addr); core::arch::asm!("csrw stvec, {0}", in(reg) addr);
// sscratch = 0 means "currently in S-mode"
core::arch::asm!("csrw sscratch, zero"); core::arch::asm!("csrw sscratch, zero");
// enable supervisor interrupts
core::arch::asm!("csrs sstatus, {0}", in(reg) 1usize << 1); 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); let sie_bits = (1usize << 5) | (1usize << 9) | (1usize << 1);
core::arch::asm!("csrs sie, {0}", in(reg) sie_bits); 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; 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::uart::Uart::new(); 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; let cause = scause & !CAUSE_INTERRUPT_BIT;
match cause { match cause {
CAUSE_S_TIMER => { CAUSE_S_TIMER => {
crate::timer::handle_timer_interrupt(); crate::hw::timer::handle_interrupt();
} }
CAUSE_S_EXTERNAL => { CAUSE_S_EXTERNAL => {
uart.write_str("[EXT]"); uart.write_str("[EXT]");

View File

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

View File

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