feat(adapter-riscv): add Sv39 page tables with identity mapping and MMU activation
This commit is contained in:
@@ -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") };
|
||||||
}
|
}
|
||||||
|
|||||||
169
crates/adapter-riscv/src/page_table.rs
Normal file
169
crates/adapter-riscv/src/page_table.rs
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user