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..e2e45ab 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ 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 - Multi Core - Dynamic clock speed -- MMU - Multiprocessing - Basic Terminal over UART diff --git a/link.ld b/link.ld index 3a85ba0..0aab0ed 100644 --- a/link.ld +++ b/link.ld @@ -10,7 +10,7 @@ SECTIONS { *(.rodata .rodata.*) } - .data : { + .data ALIGN(2M) : { _data = .; *(.data .data.*) } @@ -27,28 +27,39 @@ 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_start = .; - . += 0x10000; #10kB + . += 100K; #100kB __heap_end = .; } .stack : ALIGN(16) { __stack_start = .; - . += 0x10000; #10kB stack + . += 10K; #10kB stack __stack_end = .; } - .stack_el0 : ALIGN(16) + .stack_el0 : ALIGN(2M) { __stack_start_el0 = .; - . += 0x10000; #10kB stack + . += 10K; #10kB stack __stack_end_el0 = .; } - _end = .; } diff --git a/src/aarch64/mmu.rs b/src/aarch64/mmu.rs index 428c1b6..0466475 100644 --- a/src/aarch64/mmu.rs +++ b/src/aarch64/mmu.rs @@ -1,17 +1,67 @@ -use core::arch::asm; +use core::ptr::write_volatile; -pub fn init_mmu() { - let ips = 0b000 << 32; +use crate::{println, PERIPHERAL_BASE}; - // 4KB granularity - let tg0 = 0b00 << 14; - let tg1 = 0b00 << 30; - - //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_l1_start: u64; + static mut __translation_table_l2_start: u64; + static __stack_start_el0: 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 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 + + (addr & 0x0000_FFFF_FFE0_0000) + | BLOCK + | mair_index + | ACCESS_FLAG + | pxn + | uxn + | INNER_SHAREABILITY + | additional_flags +} + +pub fn table_descriptor_entry(addr: u64) -> u64 { + 0 | (addr & 0x0000_FFFF_FFFF_F000) | TABLE } diff --git a/src/aarch64/registers.rs b/src/aarch64/registers.rs index c39ed66..00ec0cc 100644 --- a/src/aarch64/registers.rs +++ b/src/aarch64/registers.rs @@ -52,6 +52,8 @@ psr!(SPSR_EL1, u32); psr!(ELR_EL1, u32); +psr!(SCTLR_EL1, u32); + pub fn read_exception_source_el() -> u32 { read_spsr_el1() & 0b1111 } diff --git a/src/configuration.rs b/src/configuration.rs index 53ca5d0..81ccf97 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,16 +1,33 @@ -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 EPD1: u64 = 0b1 << 23; // Trigger translation fault when using TTBR1_EL1 +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 | EPD1 | AS; diff --git a/src/interrupt_handlers.rs b/src/interrupt_handlers.rs index 9423eea..ac4abb4 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::{ @@ -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,6 +216,7 @@ pub fn get_irq_pending_sources() -> u64 { pending } +#[inline(always)] pub fn initialize_interrupt_handler() { unsafe { INTERRUPT_HANDLERS = Some(Vec::new()) }; } diff --git a/src/main.rs b/src/main.rs index 6a33913..babfa8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,10 @@ extern crate alloc; use alloc::boxed::Box; use nova::{ - aarch64::registers::{daif, read_id_aa64mmfr0_el1, read_tcr_el1}, + aarch64::{ + mmu::init_translation_table, + registers::{daif, read_id_aa64mmfr0_el1}, + }, framebuffer::{FrameBuffer, BLUE, GREEN, RED}, get_current_el, init_heap, interrupt_handlers::{enable_irq_source, IRQSource}, @@ -33,6 +36,7 @@ global_asm!(include_str!("vector.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",); + init_heap(); + init_translation_table(); + configure_mmu_el1(); + }; + + println!("AA64 {:064b}", read_id_aa64mmfr0_el1()); + + unsafe { el2_to_el1(); } @@ -82,14 +93,10 @@ unsafe fn zero_bss() { #[no_mangle] pub extern "C" fn kernel_main() -> ! { nova::initialize_kernel(); - println!("Kernel Main"); 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(); }; 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/vector.S b/src/vector.S index a4a2d35..b810e71 100644 --- a/src/vector.S +++ b/src/vector.S @@ -1,5 +1,5 @@ -.global vector_table +.global v_table .extern irq_handler .macro ventry label @@ -7,7 +7,7 @@ b \label .endm -.section .vector_table, "ax" +.section .vector_table , "ax" vector_table: ventry . ventry . @@ -21,12 +21,18 @@ vector_table: ventry synchronous_interrupt_imm_lower_aarch64 ventry irq_handler + ventry . + ventry . + + ventry . + ventry . + ventry . + ventry . .align 4 .global el2_to_el1 el2_to_el1: - mov x0, #(1 << 31) msr HCR_EL2, x0 @@ -46,9 +52,13 @@ el2_to_el1: adr x0, vector_table msr VBAR_EL1, x0 - // Disable MMU - ldr x0, =SCTLR_EL1_CONF - msr sctlr_el1, x0 + isb + + adrp x0, SCTLR_EL1_CONF + ldr x1, [x0, :lo12:SCTLR_EL1_CONF] + msr SCTLR_EL1, x0 + + isb // SIMD should not be trapped mrs x0, CPACR_EL1 @@ -56,9 +66,37 @@ el2_to_el1: orr x0,x0, x1 msr CPACR_EL1,x0 + isb + // Return to EL1 eret +.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, x0 + isb + + // MAIR0: Normal Mem. + // MAIR1: Device Mem. + mov x0, #0x04FF + msr MAIR_EL1, x0 + isb + + // Configure translation table + ldr x0, =__translation_table_l1_start + msr TTBR0_EL1, x0 + msr TTBR1_EL1, x0 + + tlbi vmalle1 + dsb ish + isb + + ret + .align 4 .global el1_to_el0 el1_to_el0: @@ -75,6 +113,8 @@ el1_to_el0: ldr x0, =__stack_end_el0 msr SP_EL0, x0 + isb + // Return to EL0 eret 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..d7063e8 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")"