From 34a73f0095fad268ce4aac6cf359579dc5835f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Neuh=C3=A4user?= Date: Wed, 11 Mar 2026 21:21:30 +0100 Subject: [PATCH] feat: Enhance MMU by separating sections and configuring permissions --- link.ld | 25 ++--- src/aarch64/mmu.rs | 172 ++++++++++++++++++++++++-------- src/lib.rs | 2 +- src/main.rs | 5 +- src/pi3/power_management.rs | 2 +- src/vector.S | 11 +- workspace/nova_error/src/lib.rs | 1 + 7 files changed, 151 insertions(+), 67 deletions(-) diff --git a/link.ld b/link.ld index 0aab0ed..0ec435e 100644 --- a/link.ld +++ b/link.ld @@ -27,34 +27,23 @@ SECTIONS { KEEP(*(.vector_table)) } - .translation_table_l1 ALIGN(4096) : { - __translation_table_l1_start = .; - . += 4096; - __translation_table_l1_end = .; - } - - .translation_table_l2 ALIGN(4096) : { - __translation_table_l2_start = .; - . += 4096; - __translation_table_l2_end = .; - } - - .heap : ALIGN(16) - { + .heap ALIGN(16): { __heap_start = .; . += 100K; #100kB __heap_end = .; } - .stack : ALIGN(16) - { + .stack ALIGN(16): { __stack_start = .; . += 10K; #10kB stack __stack_end = .; } - .stack_el0 : ALIGN(2M) - { + . = ALIGN(2M); + + __kernel_end = .; + + .stack_el0 : { __stack_start_el0 = .; . += 10K; #10kB stack __stack_end_el0 = .; diff --git a/src/aarch64/mmu.rs b/src/aarch64/mmu.rs index 0466475..df224dc 100644 --- a/src/aarch64/mmu.rs +++ b/src/aarch64/mmu.rs @@ -1,60 +1,154 @@ -use core::ptr::write_volatile; +use core::u64::MAX; + +use nova_error::NovaError; use crate::{println, PERIPHERAL_BASE}; unsafe extern "C" { - static mut __translation_table_l1_start: u64; static mut __translation_table_l2_start: u64; static __stack_start_el0: u64; + static __kernel_end: u64; static _data: u64; } -pub fn init_translation_table() { - unsafe { - write_volatile( - &raw mut __translation_table_l1_start, - table_descriptor_entry(&raw mut __translation_table_l2_start as u64), - ); - println!("{}", &raw mut __translation_table_l2_start as u64); - - for i in 0..512 { - let addr = 0x0 + (i as u64 * 2 * 1024 * 1024); - - let descriptor = if addr < &_data as *const _ as u64 { - block_descriptor_entry(addr, NORMAL_MEM, USER_AP | DISALLOW_KERNEL_AP) - } else if addr < PERIPHERAL_BASE as u64 { - block_descriptor_entry(addr, NORMAL_MEM, KERNEL_AP) - } else { - block_descriptor_entry(addr, DEVICE_MEM, USER_AP) - }; - - write_volatile( - (&raw mut __translation_table_l2_start).byte_add(8 * i), - descriptor, - ); - } - } -} - const BLOCK: u64 = 0b01; const TABLE: u64 = 0b11; -const USER_AP: u64 = 1 << 6; -const KERNEL_AP: u64 = 0 << 7; -const DISALLOW_KERNEL_AP: u64 = 1 << 7; +const EL0_ACCESSIBLE: u64 = 1 << 6; + +const WRITABLE: u64 = 0 << 7; +const READ_ONLY: u64 = 1 << 7; + const ACCESS_FLAG: u64 = 1 << 10; const INNER_SHAREABILITY: u64 = 0b11 << 8; const NORMAL_MEM: u64 = 0 << 2; const DEVICE_MEM: u64 = 1 << 2; -pub fn block_descriptor_entry(addr: u64, mair_index: u64, additional_flags: u64) -> u64 { - let pxn = 0 << 53; // allow EL1 execution - let uxn = 0 << 54; // allow EL0 execution +/// Disallow EL1 Execution. +const PXN: u64 = 1 << 53; - (addr & 0x0000_FFFF_FFE0_0000) +/// Disallow EL0 Execution. +const UXN: u64 = 1 << 54; + +const GRANULARITY: usize = 4 * 1024; +const TABLE_ENTRY_COUNT: usize = GRANULARITY / size_of::(); // 2MiB +const LEVEL2_BLOCK_SIZE: usize = TABLE_ENTRY_COUNT * GRANULARITY; + +const MAX_PAGE_COUNT: usize = 1 * 1024 * 1024 * 1024 / GRANULARITY; +#[repr(align(4096))] +pub struct PageTable([u64; TABLE_ENTRY_COUNT]); + +#[no_mangle] +pub static mut TRANSLATIONTABLE_TTBR0: PageTable = PageTable([0; 512]); +pub static mut TRANSLATIONTABLE_TTBR0_L2_0: PageTable = PageTable([0; 512]); + +static mut PAGING_BITMAP: [u64; MAX_PAGE_COUNT / 64] = [0; MAX_PAGE_COUNT / 64]; + +pub fn init_translation_table() { + unsafe { + TRANSLATIONTABLE_TTBR0.0[0] = + table_descriptor_entry(&raw mut TRANSLATIONTABLE_TTBR0_L2_0 as usize); + println!("{}", &raw mut TRANSLATIONTABLE_TTBR0_L2_0 as u64); + println!("{}", TRANSLATIONTABLE_TTBR0.0[0] & 0x0000_FFFF_FFFF_F000); + + for i in 0..512 { + let addr = 0x0 + (i * LEVEL2_BLOCK_SIZE); + + if addr < &_data as *const _ as usize { + let _ = alloc_block_l2( + addr, + &TRANSLATIONTABLE_TTBR0, + EL0_ACCESSIBLE | READ_ONLY | NORMAL_MEM, + ); + } else if addr < &__kernel_end as *const _ as usize { + let _ = alloc_block_l2(addr, &TRANSLATIONTABLE_TTBR0, WRITABLE | UXN | NORMAL_MEM); + } else if addr < PERIPHERAL_BASE { + let _ = alloc_block_l2( + addr, + &TRANSLATIONTABLE_TTBR0, + EL0_ACCESSIBLE | WRITABLE | PXN | NORMAL_MEM, + ); + } else { + let _ = alloc_block_l2( + addr, + &TRANSLATIONTABLE_TTBR0, + EL0_ACCESSIBLE | WRITABLE | UXN | PXN | DEVICE_MEM, + ); + }; + } + println!("Done"); + } +} + +pub fn alloc_page() -> Result { + find_unallocated_page() +} + +fn find_unallocated_page() -> Result { + for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() { + if *entry != u64::MAX { + for offset in 0..64 { + if entry >> offset & 0b1 == 0 { + return Ok((i * 64 + offset) * GRANULARITY); + } + } + } + } + Err(NovaError::Paging) +} + +pub fn alloc_block_l2( + virtual_addr: usize, + base_table: &PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + let physical_address = find_unallocated_block_l2()?; + + let l2_off = virtual_addr / GRANULARITY / TABLE_ENTRY_COUNT; + let l1_off = l2_off / TABLE_ENTRY_COUNT; + + let l2_table = + unsafe { &mut *((base_table.0[l1_off] & 0x0000_FFFF_FFFF_F000) as *mut PageTable) }; + + let new_entry = create_block_descriptor_entry(physical_address, additional_flags); + + l2_table.0[l2_off] = new_entry; + + allocate_block_l2(physical_address); + + Ok(()) +} + +fn find_unallocated_block_l2() -> Result { + let mut count = 0; + for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() { + if *entry == 0 { + count += 1; + } else { + count = 0; + } + + if count == 8 { + return Ok((i - 7) * 64 * GRANULARITY); + } + } + Err(NovaError::Paging) +} + +fn allocate_block_l2(physical_address: usize) { + let page = physical_address / GRANULARITY; + for i in 0..8 { + unsafe { PAGING_BITMAP[(page / 64) + i] = MAX }; + } +} + +fn create_block_descriptor_entry(addr: usize, additional_flags: u64) -> u64 { + let pxn = 0 << 53; // Privileged execute never + let uxn = 0 << 54; // Unprivileged execute never + + (addr as u64 & 0x0000_FFFF_FFE0_0000) | BLOCK - | mair_index | ACCESS_FLAG | pxn | uxn @@ -62,6 +156,6 @@ pub fn block_descriptor_entry(addr: u64, mair_index: u64, additional_flags: u64) | additional_flags } -pub fn table_descriptor_entry(addr: u64) -> u64 { - 0 | (addr & 0x0000_FFFF_FFFF_F000) | TABLE +pub fn table_descriptor_entry(addr: usize) -> u64 { + 0 | (addr as u64 & 0x0000_FFFF_FFFF_F000) | TABLE } diff --git a/src/lib.rs b/src/lib.rs index b61f16d..3ab469d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ use heap::Heap; use crate::{interrupt_handlers::initialize_interrupt_handler, logger::DefaultLogger}; -static PERIPHERAL_BASE: u32 = 0x3F00_0000; +static PERIPHERAL_BASE: usize = 0x3F00_0000; unsafe extern "C" { unsafe static mut __heap_start: u8; diff --git a/src/main.rs b/src/main.rs index babfa8b..3a8a7bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,6 @@ use nova::{ framebuffer::{FrameBuffer, BLUE, GREEN, RED}, get_current_el, init_heap, interrupt_handlers::{enable_irq_source, IRQSource}, - log, peripherals::{ gpio::{ blink_gpio, gpio_pull_up, set_falling_edge_detect, set_gpio_function, GPIOFunction, @@ -128,12 +127,12 @@ pub extern "C" fn el0() -> ! { loop { let temp = mailbox::read_soc_temp([0]).unwrap(); - log!("{} °C", temp[1] / 1000); + println!("{} °C", temp[1] / 1000); blink_gpio(SpecificGpio::OnboardLed as u8, 500); let b = Box::new([1, 2, 3, 4]); - log!("{:?}", b); + println!("{:?}", b); } } diff --git a/src/pi3/power_management.rs b/src/pi3/power_management.rs index 93e92a9..746e58f 100644 --- a/src/pi3/power_management.rs +++ b/src/pi3/power_management.rs @@ -3,7 +3,7 @@ use core::ptr::{read_volatile, write_volatile}; use crate::PERIPHERAL_BASE; /// Power Management Base -static PM_BASE: u32 = PERIPHERAL_BASE + 0x10_0000; +static PM_BASE: u32 = PERIPHERAL_BASE as u32 + 0x10_0000; static PM_RSTC: u32 = PM_BASE + 0x1c; static PM_WDOG: u32 = PM_BASE + 0x24; diff --git a/src/vector.S b/src/vector.S index b810e71..8e31098 100644 --- a/src/vector.S +++ b/src/vector.S @@ -56,7 +56,7 @@ el2_to_el1: adrp x0, SCTLR_EL1_CONF ldr x1, [x0, :lo12:SCTLR_EL1_CONF] - msr SCTLR_EL1, x0 + msr SCTLR_EL1, x1 isb @@ -77,7 +77,7 @@ configure_mmu_el1: // Configure MMU adrp x0, TCR_EL1_CONF ldr x1, [x0, :lo12:TCR_EL1_CONF] - msr TCR_EL1, x0 + msr TCR_EL1, x1 isb // MAIR0: Normal Mem. @@ -87,9 +87,10 @@ configure_mmu_el1: isb // Configure translation table - ldr x0, =__translation_table_l1_start - msr TTBR0_EL1, x0 - msr TTBR1_EL1, x0 + adrp x0, TRANSLATIONTABLE_TTBR0 + add x1, x0, :lo12:TRANSLATIONTABLE_TTBR0 + msr TTBR0_EL1, x1 + msr TTBR1_EL1, x1 tlbi vmalle1 dsb ish diff --git a/workspace/nova_error/src/lib.rs b/workspace/nova_error/src/lib.rs index 931c10a..1410c7b 100644 --- a/workspace/nova_error/src/lib.rs +++ b/workspace/nova_error/src/lib.rs @@ -8,4 +8,5 @@ pub enum NovaError { Mailbox, HeapFull, EmptyHeapSegmentNotAllowed, + Paging, }