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:
8
crates/adapter-riscv/src/board.rs
Normal file
8
crates/adapter-riscv/src/board.rs
Normal 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;
|
||||
3
crates/adapter-riscv/src/hw/mod.rs
Normal file
3
crates/adapter-riscv/src/hw/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod plic;
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
@@ -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) };
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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: [");
|
||||
|
||||
19
crates/adapter-riscv/src/mmu/allocator.rs
Normal file
19
crates/adapter-riscv/src/mmu/allocator.rs
Normal 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
|
||||
}
|
||||
}
|
||||
5
crates/adapter-riscv/src/mmu/mod.rs
Normal file
5
crates/adapter-riscv/src/mmu/mod.rs
Normal 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};
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]");
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user