feat(adapter-riscv): add Sv39 page tables with identity mapping and MMU activation

This commit is contained in:
2026-10-18 01:48:00 +02:00
parent 44a0ead7f2
commit d9de828420
2 changed files with 177 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
mod page_table;
mod uart; mod uart;
use core::arch::global_asm; use core::arch::global_asm;
@@ -22,6 +23,7 @@ spin:
unsafe extern "C" { unsafe extern "C" {
static _bss_start: u8; static _bss_start: u8;
static _bss_end: u8; static _bss_end: u8;
static _heap_start: u8;
} }
fn zero_bss() { fn zero_bss() {
@@ -40,6 +42,12 @@ extern "C" fn kmain() -> ! {
let uart = Uart::new(); let uart = Uart::new();
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 };
page_table::init_page_allocator(heap_start);
let kernel_pt = page_table::init_kernel_page_table();
page_table::activate(kernel_pt);
uart.write_str("k-os: MMU enabled (Sv39)\n");
loop { loop {
unsafe { core::arch::asm!("wfi") }; unsafe { core::arch::asm!("wfi") };
} }

View File

@@ -0,0 +1,169 @@
pub const PAGE_SIZE: usize = 4096;
const PTE_V: u64 = 1 << 0;
const PTE_R: u64 = 1 << 1;
const PTE_W: u64 = 1 << 2;
const PTE_X: u64 = 1 << 3;
const PTE_U: u64 = 1 << 4;
const PTE_G: u64 = 1 << 5;
const PTE_A: u64 = 1 << 6;
const PTE_D: u64 = 1 << 7;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct PageTableEntry(u64);
impl PageTableEntry {
pub fn empty() -> Self {
Self(0)
}
pub fn new_leaf(ppn: u64, read: bool, write: bool, exec: bool, user: bool, global: bool) -> Self {
let mut flags = PTE_V | PTE_A | PTE_D;
if read { flags |= PTE_R; }
if write { flags |= PTE_W; }
if exec { flags |= PTE_X; }
if user { flags |= PTE_U; }
if global { flags |= PTE_G; }
Self(flags | (ppn << 10))
}
pub fn new_branch(ppn: u64) -> Self {
Self(PTE_V | (ppn << 10))
}
pub fn is_valid(&self) -> bool {
self.0 & PTE_V != 0
}
pub fn ppn(&self) -> u64 {
(self.0 >> 10) & 0xFFF_FFFF_FFFF
}
}
#[repr(C, align(4096))]
pub struct PageTable {
pub entries: [PageTableEntry; 512],
}
impl PageTable {
pub fn zero(&mut self) {
for entry in &mut self.entries {
*entry = PageTableEntry::empty();
}
}
}
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
}
fn map_page(root: &mut PageTable, va: usize, pa: usize, read: bool, write: bool, exec: bool, user: bool, global: bool) {
let vpn2 = vpn(va, 2);
let vpn1 = vpn(va, 1);
let vpn0 = vpn(va, 0);
if !root.entries[vpn2].is_valid() {
let table = alloc_page_table();
let ppn = (table as *const PageTable as u64) >> 12;
root.entries[vpn2] = PageTableEntry::new_branch(ppn);
}
let l1_addr = (root.entries[vpn2].ppn() << 12) as *mut PageTable;
let l1 = unsafe { &mut *l1_addr };
if !l1.entries[vpn1].is_valid() {
let table = alloc_page_table();
let ppn = (table as *const PageTable as u64) >> 12;
l1.entries[vpn1] = PageTableEntry::new_branch(ppn);
}
let l0_addr = (l1.entries[vpn1].ppn() << 12) as *mut PageTable;
let l0 = unsafe { &mut *l0_addr };
let ppn = (pa as u64) >> 12;
l0.entries[vpn0] = PageTableEntry::new_leaf(ppn, read, write, exec, user, global);
}
fn map_range(root: &mut PageTable, va_start: usize, pa_start: usize, pages: usize, read: bool, write: bool, exec: bool, user: bool, global: bool) {
for i in 0..pages {
let offset = i * PAGE_SIZE;
map_page(root, va_start + offset, pa_start + offset, read, write, exec, user, global);
}
}
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 uart_start = 0x10000000usize;
map_range(root, uart_start, uart_start, 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);
let clint_start = 0x02000000usize;
let clint_pages = 0x10000 / PAGE_SIZE;
map_range(root, clint_start, clint_start, 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
unsafe {
core::arch::asm!(
"csrw satp, {0}",
"sfence.vma",
in(reg) satp,
);
}
}
pub fn create_user_page_table(
kernel_root: &PageTable,
program_pa: usize,
program_pages: usize,
user_va: usize,
) -> &'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;
map_range(root, stack_va, stack_pa, 1, true, true, false, true, false);
root
}