From f78388ee2c70b0ad9700841a053f20436bc4e4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Neuh=C3=A4user?= <27020492+iceHtwoO@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:30:45 +0100 Subject: [PATCH] feat: implement MMU core functionality * feat: Implement a basic MMU configuration * feat: Enhance MMU by separating sections and configuring permissions * feat: Update MMU configuration and memory allocation functions * fix: Level 3 translation fault * docs: add code documentation * fix: linter * feat: map translation tables to kernel space * feat: move el1 stack to kernel VA space * feat: use virtual memory for heap allocation * docs: update Readme --- .vscode/launch.json | 25 ++ .vscode/tasks.json | 33 +- README.md | 4 +- link.ld | 33 +- src/aarch64/mmu.rs | 517 +++++++++++++++++++++++++++++++- src/aarch64/registers.rs | 4 +- src/config.S | 193 ++++++++++++ src/configuration.rs | 110 ++++++- src/framebuffer.rs | 7 +- src/interrupt_handlers.rs | 21 +- src/lib.rs | 26 +- src/logger.rs | 10 +- src/main.rs | 53 +++- src/peripherals/gpio.rs | 2 +- src/peripherals/uart.rs | 4 + src/pi3/mod.rs | 1 + src/pi3/power_management.rs | 3 +- src/{ => pi3}/timer.rs | 0 src/vector.S | 165 +--------- tools/start_simulator.sh | 2 + tools/start_simulator_debug.sh | 4 +- workspace/heap/src/tests.rs | 36 ++- workspace/nova_error/src/lib.rs | 3 + 23 files changed, 992 insertions(+), 264 deletions(-) create mode 100644 src/config.S rename src/{ => pi3}/timer.rs (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index bffd237..77dba6e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,6 +33,31 @@ ], "preLaunchTask": "Run QEMU" }, + { + "name": "Attach to QEMU (AArch64) wo. window", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/target/aarch64-unknown-none/debug/nova", + "miDebuggerServerAddress": "localhost:1234", + "miDebuggerPath": "gdb", + "cwd": "${workspaceFolder}", + "stopAtEntry": true, + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Show assembly on stop", + "text": "set disassemble-next-line on", + "ignoreFailures": true + } + ], + "preLaunchTask": "Run QEMU wo window" + }, { "name": "Attach LLDB", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1cff419..cbd7ee3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -14,9 +14,38 @@ { "label": "Run QEMU", "type": "shell", - "command": "llvm-objcopy -O binary target/aarch64-unknown-none/debug/nova target/aarch64-unknown-none/debug/kernel8.img && qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -serial stdio -sd sd.img -kernel ${workspaceFolder}/target/aarch64-unknown-none/debug/kernel8.img -S -s -m 1024", + "command": "llvm-objcopy -O binary target/aarch64-unknown-none/debug/nova target/aarch64-unknown-none/debug/kernel8.img && echo Starting QEMU&qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -serial stdio -sd sd.img -kernel ${workspaceFolder}/target/aarch64-unknown-none/debug/kernel8.img -S -s -m 1024", "isBackground": true, - "dependsOn": ["Build"] + "dependsOn": ["Build"], + "problemMatcher": { + "pattern": { + "regexp": "^(Starting QEMU)", + "line": 1, + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^(Starting QEMU)", + "endsPattern": "^(Starting QEMU)" + } + } + }, + { + "label": "Run QEMU wo window", + "type": "shell", + "command": "llvm-objcopy -O binary target/aarch64-unknown-none/debug/nova target/aarch64-unknown-none/debug/kernel8.img && echo Starting QEMU&qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -display none -serial stdio -sd sd.img -kernel ${workspaceFolder}/target/aarch64-unknown-none/debug/kernel8.img -S -s -m 1024", + "isBackground": true, + "dependsOn": ["Build"], + "problemMatcher": { + "pattern": { + "regexp": "^(Starting QEMU)", + "line": 1, + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^(Starting QEMU)", + "endsPattern": "^(Starting QEMU)" + } + } } ] } diff --git a/README.md b/README.md index f0b6e0a..225db46 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,10 @@ NovaOS is a expository project where I build a kernel from scratch for a Raspber - Communicate with peripherals via mailboxes ✓ - Frame Buffer ✓ - Heap Memory allocation ✓ +- MMU ✓ +- SVC instructions +- Kernel Independent Applications - Multi Core - Dynamic clock speed -- MMU - Multiprocessing - Basic Terminal over UART diff --git a/link.ld b/link.ld index 3a85ba0..548260b 100644 --- a/link.ld +++ b/link.ld @@ -10,45 +10,40 @@ SECTIONS { *(.rodata .rodata.*) } - .data : { + .data ALIGN(2M) : { _data = .; *(.data .data.*) } - .bss (NOLOAD) : { - . = ALIGN(16); + .bss ALIGN(16) (NOLOAD) : { __bss_start = .; *(.bss .bss.*) - *(COMMON) __bss_end = .; } - .vector_table ALIGN(2048) : { + .vector_table ALIGN(2K) : { KEEP(*(.vector_table)) } - .heap : ALIGN(16) - { - __heap_start = .; - . += 0x10000; #10kB - __heap_end = .; - } - - .stack : ALIGN(16) - { + # EL2 Stack + .stack ALIGN(16): { __stack_start = .; - . += 0x10000; #10kB stack + . += 100K; #100kB stack + . = ALIGN(16); __stack_end = .; } - .stack_el0 : ALIGN(16) - { + . = ALIGN(2M); + + __kernel_end = .; + + .stack_el0 : { __stack_start_el0 = .; - . += 0x10000; #10kB stack + . += 10K; #10kB stack __stack_end_el0 = .; } - + . = ALIGN(2M); _end = .; } diff --git a/src/aarch64/mmu.rs b/src/aarch64/mmu.rs index 428c1b6..7f2566d 100644 --- a/src/aarch64/mmu.rs +++ b/src/aarch64/mmu.rs @@ -1,17 +1,508 @@ -use core::arch::asm; +use core::panic; -pub fn init_mmu() { - let ips = 0b000 << 32; +use core::mem::size_of; +use nova_error::NovaError; - // 4KB granularity - let tg0 = 0b00 << 14; - let tg1 = 0b00 << 30; +use crate::get_current_el; - //64-25 = 29 bits of VA - // FFFF_FF80_0000_0000 start address - let t0sz = 25; - - let tcr_el1: u64 = ips | tg0 | tg1 | t0sz; - - unsafe { asm!("msr TCR_EL1, {0:x}", in(reg) tcr_el1) }; +unsafe extern "C" { + static mut __translation_table_l2_start: u64; + static __stack_start_el0: u64; + static __kernel_end: u64; + static _data: u64; +} + +const BLOCK: u64 = 0b01; +const TABLE: u64 = 0b11; +const PAGE: u64 = 0b11; + +/// Allow EL0 to access this section +pub const EL0_ACCESSIBLE: u64 = 1 << 6; + +/// Allow a page or block to be written. +pub const WRITABLE: u64 = 0 << 7; +/// Disallow a page or block to be written. +pub const READ_ONLY: u64 = 1 << 7; + +const ACCESS_FLAG: u64 = 1 << 10; +const INNER_SHAREABILITY: u64 = 0b11 << 8; + +pub const NORMAL_MEM: u64 = 0 << 2; +pub const DEVICE_MEM: u64 = 1 << 2; + +/// Disallow EL1 Execution. +pub const PXN: u64 = 1 << 53; + +/// Disallow EL0 Execution. +pub const UXN: u64 = 1 << 54; + +pub const GRANULARITY: usize = 4 * 1024; +const TABLE_ENTRY_COUNT: usize = GRANULARITY / size_of::(); // 2MiB + +pub const LEVEL1_BLOCK_SIZE: usize = TABLE_ENTRY_COUNT * TABLE_ENTRY_COUNT * GRANULARITY; +pub const LEVEL2_BLOCK_SIZE: usize = TABLE_ENTRY_COUNT * GRANULARITY; + +const L2_BLOCK_BITMAP_WORDS: usize = LEVEL2_BLOCK_SIZE / (64 * GRANULARITY); + +const MAX_PAGE_COUNT: usize = 1024 * 1024 * 1024 / GRANULARITY; + +const TRANSLATION_TABLE_BASE_ADDR: usize = 0xFFFF_FF82_0000_0000; +pub const KERNEL_VIRTUAL_MEM_SPACE: usize = 0xFFFF_FF80_0000_0000; + +pub const STACK_START_ADDR: usize = !KERNEL_VIRTUAL_MEM_SPACE & (!0xF); + +#[repr(align(4096))] +pub struct PageTable([u64; TABLE_ENTRY_COUNT]); + +#[no_mangle] +pub static mut TRANSLATIONTABLE_TTBR0: PageTable = PageTable([0; 512]); +#[no_mangle] +pub static mut TRANSLATIONTABLE_TTBR1: PageTable = PageTable([0; 512]); + +static mut PAGING_BITMAP: [u64; MAX_PAGE_COUNT / 64] = [0; MAX_PAGE_COUNT / 64]; + +/// Allocate a memory block of `size` starting at `virtual_address`. +pub fn allocate_memory( + mut virtual_address: usize, + mut size: usize, + additional_flags: u64, +) -> Result<(), NovaError> { + if !virtual_address.is_multiple_of(GRANULARITY) { + return Err(NovaError::Misalignment); + } + + let level1_blocks = size / LEVEL1_BLOCK_SIZE; + size %= LEVEL1_BLOCK_SIZE; + let level2_blocks = size / LEVEL2_BLOCK_SIZE; + size %= LEVEL2_BLOCK_SIZE; + let level3_pages = size / GRANULARITY; + if !size.is_multiple_of(GRANULARITY) { + return Err(NovaError::InvalidGranularity); + } + + if level1_blocks > 0 { + todo!("Currently not supported"); + } + + let base_table = if virtual_address & KERNEL_VIRTUAL_MEM_SPACE > 0 { + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1) + } else { + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0) + }; + + for _ in 0..level2_blocks { + alloc_block_l2(virtual_address, base_table, additional_flags)?; + virtual_address += LEVEL2_BLOCK_SIZE; + } + for _ in 0..level3_pages { + alloc_page(virtual_address, base_table, additional_flags)?; + virtual_address += GRANULARITY; + } + + Ok(()) +} + +/// Allocate a memory block of `size` starting at `virtual_address`, +/// with explicit physical_address. +/// +/// Note: This can be used when mapping predefined regions. +pub fn allocate_memory_explicit( + mut virtual_address: usize, + mut size: usize, + mut physical_address: usize, + additional_flags: u64, +) -> Result<(), NovaError> { + if !virtual_address.is_multiple_of(GRANULARITY) { + return Err(NovaError::Misalignment); + } + if !physical_address.is_multiple_of(GRANULARITY) { + return Err(NovaError::Misalignment); + } + + let level1_blocks = size / LEVEL1_BLOCK_SIZE; + size %= LEVEL1_BLOCK_SIZE; + let mut level2_blocks = size / LEVEL2_BLOCK_SIZE; + size %= LEVEL2_BLOCK_SIZE; + let mut level3_pages = size / GRANULARITY; + if !size.is_multiple_of(GRANULARITY) { + return Err(NovaError::InvalidGranularity); + } + + if level1_blocks > 0 { + todo!("Currently not supported"); + } + + let l2_alignment = (physical_address % LEVEL2_BLOCK_SIZE) / GRANULARITY; + if l2_alignment != 0 { + let l3_diff = LEVEL2_BLOCK_SIZE / GRANULARITY - l2_alignment; + if l3_diff > level3_pages { + level2_blocks -= 1; + level3_pages += TABLE_ENTRY_COUNT; + } + + level3_pages -= l3_diff; + + for _ in 0..l3_diff { + alloc_page_explicit( + virtual_address, + physical_address, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + additional_flags, + )?; + + virtual_address += GRANULARITY; + physical_address += GRANULARITY; + } + } + + for _ in 0..level2_blocks { + alloc_block_l2_explicit( + virtual_address, + physical_address, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + additional_flags, + )?; + virtual_address += LEVEL2_BLOCK_SIZE; + physical_address += LEVEL2_BLOCK_SIZE; + } + + for _ in 0..level3_pages { + alloc_page_explicit( + virtual_address, + physical_address, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + additional_flags, + )?; + virtual_address += GRANULARITY; + physical_address += GRANULARITY; + } + + Ok(()) +} + +/// Allocate a singe page. +pub fn alloc_page( + virtual_address: usize, + base_table: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + map_page( + virtual_address, + reserve_page(), + base_table, + additional_flags, + ) +} + +/// Allocate a single page at an explicit `physical_address`. +pub fn alloc_page_explicit( + virtual_address: usize, + physical_address: usize, + base_table: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + reserve_page_explicit(physical_address)?; + map_page( + virtual_address, + physical_address, + base_table, + additional_flags, + ) +} + +fn map_page( + virtual_address: usize, + physical_address: usize, + base_table_ptr: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + let (l1_off, l2_off, l3_off) = virtual_address_to_table_offset(virtual_address); + + let offsets = [l1_off, l2_off]; + + let table_ptr = navigate_table(base_table_ptr, &offsets)?; + let table = unsafe { &mut *table_ptr }; + + if table.0[l3_off] & 0b11 > 0 { + return Err(NovaError::Paging); + } + + table.0[l3_off] = create_page_descriptor_entry(physical_address, additional_flags); + + Ok(()) +} + +// Allocate a level 2 block. +pub fn alloc_block_l2( + virtual_addr: usize, + base_table_ptr: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + map_l2_block( + virtual_addr, + reserve_block(), + base_table_ptr, + additional_flags, + ) +} + +// Allocate a level 2 block, at a explicit `physical_address`. +pub fn alloc_block_l2_explicit( + virtual_addr: usize, + physical_address: usize, + base_table_ptr: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + if !physical_address.is_multiple_of(LEVEL2_BLOCK_SIZE) { + return Err(NovaError::Misalignment); + } + + reserve_block_explicit(physical_address)?; + map_l2_block( + virtual_addr, + physical_address, + base_table_ptr, + additional_flags, + ) +} + +pub fn map_l2_block( + virtual_addr: usize, + physical_address: usize, + base_table_ptr: *mut PageTable, + additional_flags: u64, +) -> Result<(), NovaError> { + let (l1_off, l2_off, _) = virtual_address_to_table_offset(virtual_addr); + let offsets = [l1_off]; + let table_ptr = navigate_table(base_table_ptr, &offsets)?; + + let table = unsafe { &mut *table_ptr }; + + // Verify virtual address is available. + if table.0[l2_off] & 0b11 != 0 { + return Err(NovaError::Paging); + } + + let new_entry = create_block_descriptor_entry(physical_address, additional_flags); + + table.0[l2_off] = new_entry; + + Ok(()) +} + +pub fn reserve_range_explicit( + start_physical_address: usize, + end_physical_address: usize, +) -> Result<(), NovaError> { + let mut size = end_physical_address - start_physical_address; + let l1_blocks = size / LEVEL1_BLOCK_SIZE; + size %= LEVEL1_BLOCK_SIZE; + let l2_blocks = size / LEVEL2_BLOCK_SIZE; + size %= LEVEL2_BLOCK_SIZE; + let l3_pages = size / GRANULARITY; + + if !size.is_multiple_of(GRANULARITY) { + return Err(NovaError::Misalignment); + } + + if l1_blocks > 0 { + todo!(); + } + + let mut addr = start_physical_address; + for _ in 0..l2_blocks { + reserve_block_explicit(addr)?; + addr += LEVEL2_BLOCK_SIZE; + } + + for _ in 0..l3_pages { + reserve_page_explicit(addr)?; + addr += GRANULARITY; + } + + Ok(()) +} + +fn reserve_page() -> usize { + if let Some(address) = find_unallocated_page() { + let page = address / GRANULARITY; + let word_index = page / 64; + unsafe { PAGING_BITMAP[word_index] |= 1 << (page % 64) }; + return address; + } + panic!("Out of Memory!"); +} + +fn reserve_page_explicit(physical_address: usize) -> Result<(), NovaError> { + let page = physical_address / GRANULARITY; + let word_index = page / 64; + + if unsafe { PAGING_BITMAP[word_index] } & (1 << (page % 64)) > 0 { + return Err(NovaError::Paging); + } + + unsafe { PAGING_BITMAP[word_index] |= 1 << (page % 64) }; + Ok(()) +} + +fn reserve_block() -> usize { + if let Some(start) = find_contiguous_free_bitmap_words(L2_BLOCK_BITMAP_WORDS) { + for j in 0..L2_BLOCK_BITMAP_WORDS { + unsafe { PAGING_BITMAP[start + j] = u64::MAX }; + } + return start * 64 * GRANULARITY; + } + + panic!("Out of Memory!"); +} + +fn reserve_block_explicit(physical_address: usize) -> Result<(), NovaError> { + let page = physical_address / GRANULARITY; + for i in 0..L2_BLOCK_BITMAP_WORDS { + unsafe { + if PAGING_BITMAP[(page / 64) + i] != 0 { + return Err(NovaError::Paging); + } + }; + } + for i in 0..L2_BLOCK_BITMAP_WORDS { + unsafe { + PAGING_BITMAP[(page / 64) + i] = u64::MAX; + }; + } + Ok(()) +} + +fn create_block_descriptor_entry(physical_address: usize, additional_flags: u64) -> u64 { + (physical_address as u64 & 0x0000_FFFF_FFFF_F000) + | BLOCK + | ACCESS_FLAG + | INNER_SHAREABILITY + | additional_flags +} + +fn create_page_descriptor_entry(physical_address: usize, additional_flags: u64) -> u64 { + (physical_address as u64 & 0x0000_FFFF_FFFF_F000) + | PAGE + | ACCESS_FLAG + | INNER_SHAREABILITY + | additional_flags +} + +fn create_table_descriptor_entry(addr: usize) -> u64 { + (addr as u64 & 0x0000_FFFF_FFFF_F000) | TABLE +} + +fn virtual_address_to_table_offset(virtual_addr: usize) -> (usize, usize, usize) { + let absolute_page_off = (virtual_addr & !KERNEL_VIRTUAL_MEM_SPACE) / GRANULARITY; + let l3_off = absolute_page_off % TABLE_ENTRY_COUNT; + let l2_off = (absolute_page_off / TABLE_ENTRY_COUNT) % TABLE_ENTRY_COUNT; + let l1_off = (absolute_page_off / TABLE_ENTRY_COUNT / TABLE_ENTRY_COUNT) % TABLE_ENTRY_COUNT; + (l1_off, l2_off, l3_off) +} + +/// Debugging function to navigate the translation tables. +#[allow(unused_variables)] +pub fn sim_l3_access(addr: usize) { + unsafe { + let entry1 = TRANSLATIONTABLE_TTBR0.0[addr / LEVEL1_BLOCK_SIZE]; + let table2 = &mut *(entry_phys(entry1 as usize) as *mut PageTable); + let entry2 = table2.0[(addr % LEVEL1_BLOCK_SIZE) / LEVEL2_BLOCK_SIZE]; + let table3 = &mut *(entry_phys(entry2 as usize) as *mut PageTable); + let _entry3 = table3.0[(addr % LEVEL2_BLOCK_SIZE) / GRANULARITY]; + } +} + +/// Navigate the table tree, by following given offsets. This function +/// allocates new tables if required. +fn navigate_table( + initial_table_ptr: *mut PageTable, + offsets: &[usize], +) -> Result<*mut PageTable, NovaError> { + let mut table = initial_table_ptr; + for offset in offsets { + table = next_table(table, *offset)?; + } + Ok(table) +} + +/// Get the next table one level down. +/// +/// If table doesn't exit a page will be allocated for it. +fn next_table(table_ptr: *mut PageTable, offset: usize) -> Result<*mut PageTable, NovaError> { + let table = unsafe { &mut *table_ptr }; + match table.0[offset] & 0b11 { + 0 => { + let new_phys_page_table_address = reserve_page(); + + table.0[offset] = create_table_descriptor_entry(new_phys_page_table_address); + map_page( + phys_table_to_kernel_space(new_phys_page_table_address), + new_phys_page_table_address, + &raw mut TRANSLATIONTABLE_TTBR1, + NORMAL_MEM | WRITABLE | PXN | UXN, + )?; + + Ok(entry_table_addr(table.0[offset] as usize) as *mut PageTable) + } + 1 => Err(NovaError::Paging), + 3 => Ok(entry_table_addr(table.0[offset] as usize) as *mut PageTable), + _ => unreachable!(), + } +} + +fn find_unallocated_page() -> Option { + for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() { + if *entry != u64::MAX { + for offset in 0..64 { + if entry >> offset & 0b1 == 0 { + return Some((i * 64 + offset) * GRANULARITY); + } + } + } + } + None +} + +fn find_contiguous_free_bitmap_words(required_words: usize) -> Option { + let mut run_start = 0; + let mut run_len = 0; + + for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() { + if *entry == 0 { + if run_len == 0 { + run_start = i; + } + run_len += 1; + + if run_len == required_words { + return Some(run_start); + } + } else { + run_len = 0; + } + } + + None +} + +/// Extracts the physical address out of an table entry. +#[inline] +fn entry_phys(entry: usize) -> usize { + entry & 0x0000_FFFF_FFFF_F000 +} + +#[inline] +fn entry_table_addr(entry: usize) -> usize { + if get_current_el() == 1 { + phys_table_to_kernel_space(entry_phys(entry)) + } else { + entry_phys(entry) + } +} + +/// Extracts the physical address out of an table entry. +#[inline] +fn phys_table_to_kernel_space(entry: usize) -> usize { + entry | TRANSLATION_TABLE_BASE_ADDR } diff --git a/src/aarch64/registers.rs b/src/aarch64/registers.rs index c39ed66..95a22d8 100644 --- a/src/aarch64/registers.rs +++ b/src/aarch64/registers.rs @@ -50,7 +50,9 @@ psr!(ESR_EL1, u32); psr!(SPSR_EL1, u32); -psr!(ELR_EL1, u32); +psr!(ELR_EL1, u64); + +psr!(SCTLR_EL1, u64); pub fn read_exception_source_el() -> u32 { read_spsr_el1() & 0b1111 diff --git a/src/config.S b/src/config.S new file mode 100644 index 0000000..05f69b1 --- /dev/null +++ b/src/config.S @@ -0,0 +1,193 @@ +.section .text.config +.align 4 +.global el2_to_el1 +el2_to_el1: + mov x0, #(1 << 31) + msr HCR_EL2, x0 + + // Set SPSR_EL2: return to EL1h + mov x0, #(0b0101) + msr SPSR_EL2, x0 + + // Set return address to kernel_main + adrp x0, kernel_main + add x0, x0, :lo12:kernel_main + msr ELR_EL2, x0 + + // Set SP_EL1 to stack base + adrp x0, EL1_STACK_TOP + ldr x1, [x0, :lo12:EL1_STACK_TOP] + msr SP_EL1, x1 + + // Set VBAR_EL1 to vector table + adrp x0, vector_table + add x0, x0, :lo12:vector_table + msr VBAR_EL1, x0 + + isb + + adrp x0, SCTLR_EL1_CONF + ldr x1, [x0, :lo12:SCTLR_EL1_CONF] + msr SCTLR_EL1, x1 + + isb + + // SIMD should not be trapped + mrs x0, CPACR_EL1 + mov x1, #(0b11<<20) + orr x0,x0, x1 + msr CPACR_EL1,x0 + + isb + + // Return to EL1 + eret + +.section .text.config +.align 4 +.global configure_mmu_el1 +configure_mmu_el1: + // Configure MMU + adrp x0, TCR_EL1_CONF + ldr x1, [x0, :lo12:TCR_EL1_CONF] + msr TCR_EL1, x1 + isb + + // MAIR0: Normal Mem. + // MAIR1: Device Mem. + mov x0, #0x04FF + msr MAIR_EL1, x0 + isb + + // Configure translation table + adrp x0, TRANSLATIONTABLE_TTBR0 + add x1, x0, :lo12:TRANSLATIONTABLE_TTBR0 + msr TTBR0_EL1, x1 + + adrp x0, TRANSLATIONTABLE_TTBR1 + add x1, x0, :lo12:TRANSLATIONTABLE_TTBR1 + msr TTBR1_EL1, x1 + + tlbi vmalle1 + dsb ish + isb + + ret + +.align 4 +.global el1_to_el0 +el1_to_el0: + + // Set SPSR_EL1: return to EL0t + mov x0, #(0b0000) + msr SPSR_EL1, x0 + + // Set return address to el0 + ldr x0, =el0 + msr ELR_EL1, x0 + + // Set SP_EL1 to stack base + ldr x0, =__stack_end_el0 + msr SP_EL0, x0 + + isb + + // Return to EL0 + eret + + +.align 4 +irq_handler: + sub sp, sp, #176 + stp x0, x1, [sp, #0] + stp x2, x3, [sp, #16] + stp x4, x5, [sp, #32] + stp x6, x7, [sp, #48] + stp x8, x9, [sp, #64] + stp x10, x11, [sp, #80] + stp x12, x13, [sp, #96] + stp x14, x15, [sp, #112] + stp x16, x17, [sp, #128] + stp x18, x29, [sp, #144] + stp x30, xzr, [sp, #160] + + bl rust_irq_handler + + ldp x0, x1, [sp, #0] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp, #32] + ldp x6, x7, [sp, #48] + ldp x8, x9, [sp, #64] + ldp x10, x11, [sp, #80] + ldp x12, x13, [sp, #96] + ldp x14, x15, [sp, #112] + ldp x16, x17, [sp, #128] + ldp x18, x29, [sp, #144] + ldp x30, xzr, [sp, #160] + add sp, sp, #176 + + eret + +.align 4 +synchronous_interrupt_imm_lower_aarch64: + sub sp, sp, #176 + stp x0, x1, [sp, #0] + stp x2, x3, [sp, #16] + stp x4, x5, [sp, #32] + stp x6, x7, [sp, #48] + stp x8, x9, [sp, #64] + stp x10, x11, [sp, #80] + stp x12, x13, [sp, #96] + stp x14, x15, [sp, #112] + stp x16, x17, [sp, #128] + stp x18, x29, [sp, #144] + stp x30, xzr, [sp, #160] + + bl rust_synchronous_interrupt_imm_lower_aarch64 + + ldp x0, x1, [sp, #0] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp, #32] + ldp x6, x7, [sp, #48] + ldp x8, x9, [sp, #64] + ldp x10, x11, [sp, #80] + ldp x12, x13, [sp, #96] + ldp x14, x15, [sp, #112] + ldp x16, x17, [sp, #128] + ldp x18, x29, [sp, #144] + ldp x30, xzr, [sp, #160] + add sp, sp, #176 + + eret + +.align 4 +synchronous_interrupt_no_el_change: + sub sp, sp, #176 + stp x0, x1, [sp, #0] + stp x2, x3, [sp, #16] + stp x4, x5, [sp, #32] + stp x6, x7, [sp, #48] + stp x8, x9, [sp, #64] + stp x10, x11, [sp, #80] + stp x12, x13, [sp, #96] + stp x14, x15, [sp, #112] + stp x16, x17, [sp, #128] + stp x18, x29, [sp, #144] + stp x30, xzr, [sp, #160] + + bl rust_synchronous_interrupt_no_el_change + + ldp x0, x1, [sp, #0] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp, #32] + ldp x6, x7, [sp, #48] + ldp x8, x9, [sp, #64] + ldp x10, x11, [sp, #80] + ldp x12, x13, [sp, #96] + ldp x14, x15, [sp, #112] + ldp x16, x17, [sp, #128] + ldp x18, x29, [sp, #144] + ldp x30, xzr, [sp, #160] + add sp, sp, #176 + + eret diff --git a/src/configuration.rs b/src/configuration.rs index 53ca5d0..9496034 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,16 +1,110 @@ -static SCTLR_EL1_MMU_DISABLED: u64 = 0; //M -static SCTLR_EL1_DATA_CACHE_DISABLED: u64 = 0 << 2; //C -static SCTLR_EL1_INSTRUCTION_CACHE_DISABLED: u64 = 0 << 12; //I -static SCTLR_EL1_LITTLE_ENDIAN_EL0: u64 = 0 << 24; //E0E -static SCTLR_EL1_LITTLE_ENDIAN_EL1: u64 = 0 << 25; //EE +const SCTLR_EL1_MMU_ENABLED: u64 = 1; //M +const SCTLR_EL1_DATA_CACHE_DISABLED: u64 = 0 << 2; //C +const SCTLR_EL1_INSTRUCTION_CACHE_DISABLED: u64 = 0 << 12; //I +const SCTLR_EL1_LITTLE_ENDIAN_EL0: u64 = 0 << 24; //E0E +const SCTLR_EL1_LITTLE_ENDIAN_EL1: u64 = 0 << 25; //EE +const SCTLR_EL1_SPAN: u64 = 1 << 23; //SPAN #[allow(clippy::identity_op)] -static SCTLR_EL1_RES: u64 = (0 << 6) | (1 << 11) | (0 << 17) | (1 << 20) | (1 << 22); //Res0 & Res1 +const SCTLR_EL1_RES: u64 = (0 << 6) | (1 << 11) | (0 << 17) | (1 << 20) | (1 << 22); //Res0 & Res1 #[no_mangle] -pub static SCTLR_EL1_CONF: u64 = SCTLR_EL1_MMU_DISABLED +pub static SCTLR_EL1_CONF: u64 = SCTLR_EL1_MMU_ENABLED | SCTLR_EL1_DATA_CACHE_DISABLED | SCTLR_EL1_INSTRUCTION_CACHE_DISABLED | SCTLR_EL1_LITTLE_ENDIAN_EL0 | SCTLR_EL1_LITTLE_ENDIAN_EL1 - | SCTLR_EL1_RES; + | SCTLR_EL1_RES + | SCTLR_EL1_SPAN; + +const TG0: u64 = 0b00 << 14; // 4KB granularity EL0 +const T0SZ: u64 = 25; // 25 Bits of TTBR select -> 39 Bits of VA +const SH0: u64 = 0b11 << 12; // Inner shareable + +const TG1: u64 = 0b10 << 30; // 4KB granularity EL1 +const T1SZ: u64 = 25 << 16; // 25 Bits of TTBR select -> 39 Bits of VA +const SH1: u64 = 0b11 << 28; // Inner sharable + +const IPS: u64 = 0b000 << 32; // 32 bits of PA space -> up to 4GiB +const AS: u64 = 0b1 << 36; // configure an ASID size of 16 bits + +#[no_mangle] +pub static TCR_EL1_CONF: u64 = IPS | TG0 | TG1 | T0SZ | T1SZ | SH0 | SH1 | AS; + +pub mod mmu { + + use crate::{ + aarch64::mmu::{ + alloc_block_l2, alloc_block_l2_explicit, map_l2_block, reserve_range_explicit, + DEVICE_MEM, EL0_ACCESSIBLE, KERNEL_VIRTUAL_MEM_SPACE, LEVEL1_BLOCK_SIZE, + LEVEL2_BLOCK_SIZE, NORMAL_MEM, PXN, READ_ONLY, STACK_START_ADDR, + TRANSLATIONTABLE_TTBR0, TRANSLATIONTABLE_TTBR1, UXN, WRITABLE, + }, + PERIPHERAL_BASE, + }; + + #[no_mangle] + static EL1_STACK_TOP: usize = STACK_START_ADDR | KERNEL_VIRTUAL_MEM_SPACE; + const EL1_STACK_BOTTOM: usize = EL1_STACK_TOP - LEVEL2_BLOCK_SIZE * 2; + + extern "C" { + static _data: u64; + static _end: u64; + static __kernel_end: u64; + } + + pub fn initialize_mmu_translation_tables() { + let shared_segment_end = unsafe { &_data } as *const _ as usize; + let kernel_end = unsafe { &__kernel_end } as *const _ as usize; + let user_space_end = unsafe { &_end } as *const _ as usize; + + reserve_range_explicit(0x0, user_space_end).unwrap(); + + for addr in (0..shared_segment_end).step_by(LEVEL2_BLOCK_SIZE) { + let _ = map_l2_block( + addr, + addr, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + EL0_ACCESSIBLE | READ_ONLY | NORMAL_MEM, + ); + } + + for addr in (shared_segment_end..kernel_end).step_by(LEVEL2_BLOCK_SIZE) { + let _ = map_l2_block( + addr, + addr, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + WRITABLE | UXN | NORMAL_MEM, + ); + } + + for addr in (kernel_end..user_space_end).step_by(LEVEL2_BLOCK_SIZE) { + let _ = map_l2_block( + addr, + addr, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + EL0_ACCESSIBLE | WRITABLE | PXN | NORMAL_MEM, + ); + } + + for addr in (PERIPHERAL_BASE..LEVEL1_BLOCK_SIZE).step_by(LEVEL2_BLOCK_SIZE) { + let _ = alloc_block_l2_explicit( + addr, + addr, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0), + EL0_ACCESSIBLE | WRITABLE | UXN | PXN | DEVICE_MEM, + ); + } + + for addr in (EL1_STACK_BOTTOM..EL1_STACK_TOP) + .rev() + .step_by(LEVEL2_BLOCK_SIZE) + { + let _ = alloc_block_l2( + addr, + core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1), + WRITABLE | NORMAL_MEM, + ); + } + } +} diff --git a/src/framebuffer.rs b/src/framebuffer.rs index 8c14e34..0102927 100644 --- a/src/framebuffer.rs +++ b/src/framebuffer.rs @@ -24,8 +24,8 @@ pub struct FrameBuffer { pixel_depth: u32, // Bits per pixel pitch: u32, // Pixel per row rows: u32, // Rows - start_addr: *mut u32, - size: u32, //Bytes + pub start_addr: *mut u32, + pub size: u32, //Bytes } pub const RED: u32 = 0x00FF0000; @@ -37,6 +37,9 @@ pub const YELLOW: u32 = 0x00FFFF00; impl FrameBuffer { pub fn draw_pixel(&self, x: u32, y: u32, color: u32) { let offset = x + y * self.pitch; + if x >= self.pitch || y >= self.rows { + return; + } unsafe { write_volatile(self.start_addr.add(offset as usize), color); } diff --git a/src/interrupt_handlers.rs b/src/interrupt_handlers.rs index 9423eea..0f38215 100644 --- a/src/interrupt_handlers.rs +++ b/src/interrupt_handlers.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use crate::{ aarch64::registers::{ daif::{mask_all, unmask_irq}, - read_esr_el1, read_exception_source_el, + read_elr_el1, read_esr_el1, read_exception_source_el, }, get_current_el, peripherals::{ @@ -82,7 +82,7 @@ unsafe extern "C" fn rust_irq_handler() { println!("Return register address: {:#x}", read_esr_el1()); } - if let Some(handler_vec) = unsafe { INTERRUPT_HANDLERS.as_ref() } { + if let Some(handler_vec) = unsafe { &*core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } { for handler in handler_vec { if (pending_irqs & (1 << (handler.source.clone() as u32))) != 0 { (handler.function)(); @@ -118,15 +118,17 @@ unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64() { println!("--------Sync Exception in EL{}--------", source_el); println!("Exception escalated to EL {}", get_current_el()); println!("Current EL: {}", get_current_el()); - let esr = EsrElX::from(read_esr_el1()); - println!("{:?}", EsrElX::from(esr)); - println!("Return register address: {:#x}", read_esr_el1()); + let esr: EsrElX = EsrElX::from(read_esr_el1()); + println!("{:?}", esr); + println!("Return address: {:#x}", read_elr_el1()); match esr.ec { 0b100100 => { println!("Cause: Data Abort from a lower Exception level"); } - _ => {} + _ => { + println!("Unknown Error Code: {:b}", esr.ec); + } } println!("-------------------------------------"); @@ -136,7 +138,9 @@ unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64() { fn clear_interrupt_for_source(source: IRQSource) { match source { IRQSource::UartInt => clear_uart_interrupt_state(), - _ => {} + _ => { + todo!() + } } } @@ -212,12 +216,13 @@ pub fn get_irq_pending_sources() -> u64 { pending } +#[inline(always)] pub fn initialize_interrupt_handler() { unsafe { INTERRUPT_HANDLERS = Some(Vec::new()) }; } pub fn register_interrupt_handler(source: IRQSource, function: fn()) { - if let Some(handler_vec) = unsafe { INTERRUPT_HANDLERS.as_mut() } { + if let Some(handler_vec) = unsafe { &mut *core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } { handler_vec.push(InterruptHandlers { source, function }); } } diff --git a/src/lib.rs b/src/lib.rs index b61f16d..057fea7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,30 +12,36 @@ use core::{ use heap::Heap; -use crate::{interrupt_handlers::initialize_interrupt_handler, logger::DefaultLogger}; +use crate::{ + aarch64::mmu::{ + allocate_memory, KERNEL_VIRTUAL_MEM_SPACE, LEVEL2_BLOCK_SIZE, NORMAL_MEM, UXN, WRITABLE, + }, + 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; - unsafe static mut __heap_end: u8; + unsafe static mut __kernel_end: u8; } #[global_allocator] pub static mut GLOBAL_ALLOCATOR: Heap = Heap::empty(); -pub unsafe fn init_heap() { - let start = core::ptr::addr_of_mut!(__heap_start) as usize; - let end = core::ptr::addr_of_mut!(__heap_end) as usize; +pub unsafe fn init_kernel_heap() { + let start = core::ptr::addr_of_mut!(__kernel_end) as usize | KERNEL_VIRTUAL_MEM_SPACE; + let size = LEVEL2_BLOCK_SIZE * 2; + allocate_memory(start, size, NORMAL_MEM | UXN | WRITABLE).unwrap(); let heap = core::ptr::addr_of_mut!(GLOBAL_ALLOCATOR); - (*heap).init(start, end); + (*heap).init(start, start + size); } #[panic_handler] fn panic(_panic: &PanicInfo) -> ! { loop { - println!("Panic"); + println!("Panic: {}", _panic.message()); } } @@ -46,7 +52,6 @@ pub mod configuration; pub mod framebuffer; pub mod interrupt_handlers; pub mod logger; -pub mod timer; pub mod pi3; @@ -73,6 +78,7 @@ pub fn get_current_el() -> u64 { } pub fn initialize_kernel() { + unsafe { init_kernel_heap() }; logger::set_logger(Box::new(DefaultLogger)); initialize_interrupt_handler(); } diff --git a/src/logger.rs b/src/logger.rs index 5ecd9c6..c769551 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -31,12 +31,10 @@ macro_rules! log { } pub fn log(args: fmt::Arguments) { - unsafe { - if let Some(logger) = LOGGER.as_mut() { - logger.write_str("\n").unwrap(); - logger.write_fmt(args).unwrap(); - logger.flush(); - } + if let Some(logger) = unsafe { &mut *core::ptr::addr_of_mut!(LOGGER) } { + logger.write_str("\n").unwrap(); + logger.write_fmt(args).unwrap(); + logger.flush(); } } diff --git a/src/main.rs b/src/main.rs index 6a33913..169deb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ #![no_main] #![no_std] -#![feature(asm_experimental_arch)] #![allow(static_mut_refs)] #![allow(clippy::missing_safety_doc)] use core::{ @@ -10,13 +9,16 @@ use core::{ extern crate alloc; -use alloc::boxed::Box; +use alloc::vec::Vec; use nova::{ - aarch64::registers::{daif, read_id_aa64mmfr0_el1, read_tcr_el1}, + aarch64::{ + mmu::{allocate_memory_explicit, EL0_ACCESSIBLE, NORMAL_MEM, PXN, UXN, WRITABLE}, + registers::{daif, read_id_aa64mmfr0_el1}, + }, + configuration::mmu::initialize_mmu_translation_tables, framebuffer::{FrameBuffer, BLUE, GREEN, RED}, - get_current_el, init_heap, + get_current_el, interrupt_handlers::{enable_irq_source, IRQSource}, - log, peripherals::{ gpio::{ blink_gpio, gpio_pull_up, set_falling_edge_detect, set_gpio_function, GPIOFunction, @@ -29,10 +31,12 @@ use nova::{ }; global_asm!(include_str!("vector.S")); +global_asm!(include_str!("config.S")); extern "C" { fn el2_to_el1(); fn el1_to_el0(); + fn configure_mmu_el1(); static mut __bss_start: u32; static mut __bss_end: u32; } @@ -63,7 +67,14 @@ pub extern "C" fn main() -> ! { println!("Exception level: {}", get_current_el()); unsafe { - asm!("mrs x0, SCTLR_EL1",); + initialize_mmu_translation_tables(); + configure_mmu_el1(); + println!("MMU initialized..."); + }; + + println!("Register: AA64MMFR0_EL1: {:064b}", read_id_aa64mmfr0_el1()); + println!("Moving El2->EL1"); + unsafe { el2_to_el1(); } @@ -81,15 +92,27 @@ unsafe fn zero_bss() { #[no_mangle] pub extern "C" fn kernel_main() -> ! { + println!("Kernel Start..."); nova::initialize_kernel(); - println!("Kernel Main"); + let mut test_vector = Vec::new(); + for i in 0..20 { + test_vector.push(i); + } + println!("heap allocation test: {:?}", test_vector); + + // Frame Buffer memory range + // TODO: this is just temporary + allocate_memory_explicit( + 0x3c100000, + 1080 * 1920 * 4, + 0x3c100000, + NORMAL_MEM | PXN | UXN | WRITABLE | EL0_ACCESSIBLE, + ) + .unwrap(); println!("Exception Level: {}", get_current_el()); daif::unmask_all(); unsafe { - init_heap(); - println!("{:b}", read_id_aa64mmfr0_el1()); - println!("{:b}", read_tcr_el1()); el1_to_el0(); }; @@ -111,22 +134,22 @@ pub extern "C" fn el0() -> ! { let fb = FrameBuffer::default(); + for i in 0..1080 { + fb.draw_pixel(50, i, BLUE); + } fb.draw_square(500, 500, 600, 700, RED); fb.draw_square_fill(800, 800, 900, 900, GREEN); fb.draw_square_fill(1000, 800, 1200, 700, BLUE); fb.draw_square_fill(900, 100, 800, 150, RED | BLUE); fb.draw_string("Hello World! :D\nTest next Line", 500, 5, 3, BLUE); - fb.draw_function(cos, 100, 101, RED); + fb.draw_function(cos, 0, 101, RED); 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); } } diff --git a/src/peripherals/gpio.rs b/src/peripherals/gpio.rs index f9f5303..1e39f75 100644 --- a/src/peripherals/gpio.rs +++ b/src/peripherals/gpio.rs @@ -2,7 +2,7 @@ use core::result::Result; use core::result::Result::Ok; use core::sync::atomic::{compiler_fence, Ordering}; -use crate::timer::{delay_nops, sleep_ms}; +use crate::pi3::timer::{delay_nops, sleep_ms}; use crate::{read_address, write_address}; const GPFSEL_BASE: u32 = 0x3F20_0000; diff --git a/src/peripherals/uart.rs b/src/peripherals/uart.rs index 57e2843..8c234b4 100644 --- a/src/peripherals/uart.rs +++ b/src/peripherals/uart.rs @@ -118,11 +118,13 @@ fn uart_fifo_enable(enable: bool) { unsafe { write_address(UART0_LCRH, lcrh) }; } +#[inline(always)] fn uart_enable_rx_interrupt() { unsafe { write_address(UART0_IMSC, UART0_IMSC_RXIM) }; } /// Set UART word length and set FIFO status +#[inline(always)] fn uart_set_lcrh(wlen: u32, enable_fifo: bool) { let mut value = (wlen & 0b11) << 5; if enable_fifo { @@ -131,10 +133,12 @@ fn uart_set_lcrh(wlen: u32, enable_fifo: bool) { unsafe { write_address(UART0_LCRH, value) }; } +#[inline(always)] pub fn read_uart_data() -> char { (unsafe { read_address(UART0_DR) } & 0xFF) as u8 as char } +#[inline(always)] pub fn clear_uart_interrupt_state() { unsafe { write_address(UART0_ICR, 1 << 4); diff --git a/src/pi3/mod.rs b/src/pi3/mod.rs index e23f6e4..915b500 100644 --- a/src/pi3/mod.rs +++ b/src/pi3/mod.rs @@ -1,2 +1,3 @@ pub mod mailbox; pub mod power_management; +pub mod timer; diff --git a/src/pi3/power_management.rs b/src/pi3/power_management.rs index 93e92a9..8feb0b1 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; @@ -23,5 +23,6 @@ pub fn reboot_system() { PM_PASSWORD | (pm_rstc_val & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET, ); } + #[allow(clippy::empty_loop)] loop {} } diff --git a/src/timer.rs b/src/pi3/timer.rs similarity index 100% rename from src/timer.rs rename to src/pi3/timer.rs diff --git a/src/vector.S b/src/vector.S index a4a2d35..5732b8f 100644 --- a/src/vector.S +++ b/src/vector.S @@ -1,13 +1,12 @@ - -.global vector_table +.section .vector_table , "ax" .extern irq_handler .macro ventry label -.align 7 +.align 11 b \label .endm -.section .vector_table, "ax" +.global vector_table vector_table: ventry . ventry . @@ -21,156 +20,10 @@ vector_table: ventry synchronous_interrupt_imm_lower_aarch64 ventry irq_handler + ventry . + ventry . - -.align 4 -.global el2_to_el1 -el2_to_el1: - - mov x0, #(1 << 31) - msr HCR_EL2, x0 - - // Set SPSR_EL2: return to EL1h - mov x0, #(0b0101) - msr SPSR_EL2, x0 - - // Set return address to kernel_main - ldr x0, =kernel_main - msr ELR_EL2, x0 - - // Set SP_EL1 to stack base - ldr x0, =__stack_end - msr SP_EL1, x0 - - // Set VBAR_EL1 to vector table - adr x0, vector_table - msr VBAR_EL1, x0 - - // Disable MMU - ldr x0, =SCTLR_EL1_CONF - msr sctlr_el1, x0 - - // SIMD should not be trapped - mrs x0, CPACR_EL1 - mov x1, #(0b11<<20) - orr x0,x0, x1 - msr CPACR_EL1,x0 - - // Return to EL1 - eret - -.align 4 -.global el1_to_el0 -el1_to_el0: - - // Set SPSR_EL1: return to EL0t - mov x0, #(0b0000) - msr SPSR_EL1, x0 - - // Set return address to el0 - ldr x0, =el0 - msr ELR_EL1, x0 - - // Set SP_EL1 to stack base - ldr x0, =__stack_end_el0 - msr SP_EL0, x0 - - // Return to EL0 - eret - - -.align 4 -irq_handler: - sub sp, sp, #176 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - stp x4, x5, [sp, #32] - stp x6, x7, [sp, #48] - stp x8, x9, [sp, #64] - stp x10, x11, [sp, #80] - stp x12, x13, [sp, #96] - stp x14, x15, [sp, #112] - stp x16, x17, [sp, #128] - stp x18, x29, [sp, #144] - stp x30, xzr, [sp, #160] - - bl rust_irq_handler - - ldp x0, x1, [sp, #0] - ldp x2, x3, [sp, #16] - ldp x4, x5, [sp, #32] - ldp x6, x7, [sp, #48] - ldp x8, x9, [sp, #64] - ldp x10, x11, [sp, #80] - ldp x12, x13, [sp, #96] - ldp x14, x15, [sp, #112] - ldp x16, x17, [sp, #128] - ldp x18, x29, [sp, #144] - ldp x30, xzr, [sp, #160] - add sp, sp, #176 - - eret - -.align 4 -synchronous_interrupt_imm_lower_aarch64: - sub sp, sp, #176 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - stp x4, x5, [sp, #32] - stp x6, x7, [sp, #48] - stp x8, x9, [sp, #64] - stp x10, x11, [sp, #80] - stp x12, x13, [sp, #96] - stp x14, x15, [sp, #112] - stp x16, x17, [sp, #128] - stp x18, x29, [sp, #144] - stp x30, xzr, [sp, #160] - - bl rust_synchronous_interrupt_imm_lower_aarch64 - - ldp x0, x1, [sp, #0] - ldp x2, x3, [sp, #16] - ldp x4, x5, [sp, #32] - ldp x6, x7, [sp, #48] - ldp x8, x9, [sp, #64] - ldp x10, x11, [sp, #80] - ldp x12, x13, [sp, #96] - ldp x14, x15, [sp, #112] - ldp x16, x17, [sp, #128] - ldp x18, x29, [sp, #144] - ldp x30, xzr, [sp, #160] - add sp, sp, #176 - - eret - -.align 4 -synchronous_interrupt_no_el_change: - sub sp, sp, #176 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - stp x4, x5, [sp, #32] - stp x6, x7, [sp, #48] - stp x8, x9, [sp, #64] - stp x10, x11, [sp, #80] - stp x12, x13, [sp, #96] - stp x14, x15, [sp, #112] - stp x16, x17, [sp, #128] - stp x18, x29, [sp, #144] - stp x30, xzr, [sp, #160] - - bl rust_synchronous_interrupt_no_el_change - - ldp x0, x1, [sp, #0] - ldp x2, x3, [sp, #16] - ldp x4, x5, [sp, #32] - ldp x6, x7, [sp, #48] - ldp x8, x9, [sp, #64] - ldp x10, x11, [sp, #80] - ldp x12, x13, [sp, #96] - ldp x14, x15, [sp, #112] - ldp x16, x17, [sp, #128] - ldp x18, x29, [sp, #144] - ldp x30, xzr, [sp, #160] - add sp, sp, #176 - - eret + ventry . + ventry . + ventry . + ventry . diff --git a/tools/start_simulator.sh b/tools/start_simulator.sh index 0955560..225184d 100755 --- a/tools/start_simulator.sh +++ b/tools/start_simulator.sh @@ -1,3 +1,5 @@ +set -e + cargo build --target aarch64-unknown-none --release cd "$(dirname "$0")" diff --git a/tools/start_simulator_debug.sh b/tools/start_simulator_debug.sh index 17e9660..9f52332 100755 --- a/tools/start_simulator_debug.sh +++ b/tools/start_simulator_debug.sh @@ -1,3 +1,5 @@ +set -e + cargo build --target aarch64-unknown-none cd "$(dirname "$0")" @@ -9,6 +11,4 @@ qemu-system-aarch64 \ -cpu cortex-a53 \ -serial stdio \ -sd ../sd.img \ - -display none \ -kernel ../target/aarch64-unknown-none/debug/kernel8.img \ - -s -S diff --git a/workspace/heap/src/tests.rs b/workspace/heap/src/tests.rs index 13f3326..cad2fd2 100644 --- a/workspace/heap/src/tests.rs +++ b/workspace/heap/src/tests.rs @@ -100,7 +100,7 @@ fn test_merging_free_sections() { ); let root_header = heap.start_address; - let root_header_start_size = unsafe { (*root_header).size }; + let _root_header_start_size = unsafe { (*root_header).size }; let malloc1 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); let malloc_header_before = unsafe { *Heap::get_header_ref_from_data_pointer(malloc1) }; @@ -135,31 +135,29 @@ fn test_first_fit() { ); let root_header = heap.start_address; - let root_header_start_size = unsafe { (*root_header).size }; + let _root_header_start_size = unsafe { (*root_header).size }; let malloc1 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); - let malloc2 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); + let _malloc2 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); let malloc3 = heap.malloc(MIN_BLOCK_SIZE * 3).unwrap(); let malloc4 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); - unsafe { - assert!(heap.free(malloc1).is_ok()); - assert!(heap.free(malloc3).is_ok()); - let malloc5 = heap.malloc(MIN_BLOCK_SIZE * 2).unwrap(); - let malloc1_header = unsafe { *Heap::get_header_ref_from_data_pointer(malloc1) }; + assert!(heap.free(malloc1).is_ok()); + assert!(heap.free(malloc3).is_ok()); + let malloc5 = heap.malloc(MIN_BLOCK_SIZE * 2).unwrap(); + let malloc1_header = unsafe { *Heap::get_header_ref_from_data_pointer(malloc1) }; - // First free block stays empty - assert!(malloc1_header.free); + // First free block stays empty + assert!(malloc1_header.free); - // New allocation takes the first fit aka. malloc3 - assert_eq!(malloc5, malloc3); + // New allocation takes the first fit aka. malloc3 + assert_eq!(malloc5, malloc3); - // If no free slot could be found, append to the end - let malloc6 = heap.malloc(MIN_BLOCK_SIZE * 2).unwrap(); - assert!(malloc6 > malloc4); + // If no free slot could be found, append to the end + let malloc6 = heap.malloc(MIN_BLOCK_SIZE * 2).unwrap(); + assert!(malloc6 > malloc4); - // Malloc7 takes slot of Malloc1 - let malloc7 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); - assert_eq!(malloc1, malloc7); - } + // Malloc7 takes slot of Malloc1 + let malloc7 = heap.malloc(MIN_BLOCK_SIZE).unwrap(); + assert_eq!(malloc1, malloc7); } diff --git a/workspace/nova_error/src/lib.rs b/workspace/nova_error/src/lib.rs index 931c10a..bc32817 100644 --- a/workspace/nova_error/src/lib.rs +++ b/workspace/nova_error/src/lib.rs @@ -8,4 +8,7 @@ pub enum NovaError { Mailbox, HeapFull, EmptyHeapSegmentNotAllowed, + Misalignment, + InvalidGranularity, + Paging, }