2 Commits

Author SHA1 Message Date
aneuhaeuser 778b3ed80c feat: move EL0 stack to virtual space 2026-03-19 10:43:45 +01:00
aneuhaeuser cba7073ae5 refactor: organize code 2026-03-19 08:57:39 +01:00
30 changed files with 653 additions and 1281 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
KERNEL_NAME=kernel8.img KERNEL_NAME=kernel8.img
BUILD_PATH=target/aarch64-unknown-none/release BUILD_PATH=target/aarch64-unknown-none/release
BINARY_NAME=nova BINARY_NAME=BINARY_NAME
TFTP_PATH=/srv/tftp TFTP_PATH=/srv/tftp
REMOTE_USER=TFTP_HOST_USER REMOTE_USER=TFTP_HOST_USER
REMOTE_HOST=TFTP_HOST_IP REMOTE_HOST=TFTP_HOST_IP
-6
View File
@@ -5,9 +5,3 @@ sd.img
settings.json settings.json
.DS_Store .DS_Store
.venv .venv
.nvimlog
__pycache__
.pytest_cache
build/
+4 -13
View File
@@ -3,7 +3,7 @@
"compounds": [ "compounds": [
{ {
"name": "Run QEMU + Attach LLDB", "name": "Run QEMU + Attach LLDB",
"configurations": ["LLDB"], "configurations": ["Attach LLDB"],
"preLaunchTask": "Run QEMU" "preLaunchTask": "Run QEMU"
} }
], ],
@@ -58,22 +58,13 @@
], ],
"preLaunchTask": "Run QEMU wo window" "preLaunchTask": "Run QEMU wo window"
}, },
{
"name": "LLDB",
"type": "lldb",
"request": "attach",
"program": "${workspaceFolder}/target/aarch64-unknown-none/debug/nova",
"preLaunchTask": "Run QEMU",
"stopOnEntry": true,
"processCreateCommands": ["gdb-remote localhost:1234"]
},
{ {
"name": "NVIM LLDB", "name": "Attach LLDB",
"type": "codelldb", "type": "lldb",
"request": "attach", "request": "attach",
"debugServer": 1234,
"program": "${workspaceFolder}/target/aarch64-unknown-none/debug/nova", "program": "${workspaceFolder}/target/aarch64-unknown-none/debug/nova",
"preLaunchTask": "Run QEMU",
"stopOnEntry": true, "stopOnEntry": true,
"processCreateCommands": ["gdb-remote localhost:1234"] "processCreateCommands": ["gdb-remote localhost:1234"]
} }
Generated
-32
View File
@@ -40,31 +40,14 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]] [[package]]
name = "nova" name = "nova"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"heap", "heap",
"libm", "libm",
"log",
"nova_error", "nova_error",
"paste", "paste",
"spin",
] ]
[[package]] [[package]]
@@ -139,21 +122,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "spin"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.111" version = "2.0.111"
-2
View File
@@ -17,8 +17,6 @@ libm = "0.2.15"
heap = {path = "workspace/heap"} heap = {path = "workspace/heap"}
nova_error = {path = "workspace/nova_error"} nova_error = {path = "workspace/nova_error"}
paste = "1.0.15" paste = "1.0.15"
log = "0.4.29"
spin = "0.10.0"
[workspace] [workspace]
-26
View File
@@ -1,26 +0,0 @@
MIT NON-AI License
Copyright (c) 2026, Alexander Neuhäuser
Permission is hereby granted, free of charge, to any person obtaining a copy of the software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
In addition, the following restrictions apply:
1. The Software and any modifications made to it may not be used for the purpose of training or improving machine learning algorithms,
including but not limited to artificial intelligence, natural language processing, or data mining. This condition applies to any derivatives,
modifications, or updates based on the Software code. Any usage of the Software in an AI-training dataset is considered a breach of this License.
2. The Software may not be included in any dataset used for training or improving machine learning algorithms,
including but not limited to artificial intelligence, natural language processing, or data mining.
3. Any person or organization found to be in violation of these restrictions will be subject to legal action and may be held liable
for any damages resulting from such use.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+18 -115
View File
@@ -1,120 +1,23 @@
# NovaOS # NovaOS
![NovaOS banner](docs/banner.png) NovaOS is a expository project where I build a kernel from scratch for a Raspberry PI 3 B+.
NovaOS is a hobby operating system kernel written in Rust for the Raspberry Pi 3 B+. [Technical write-up](https://leafnova.net/projects/pi3_kernel/)
It is built as a learning project for low-level systems programming, bare-metal boot flow, and kernel development.
## At A Glance ## Features
NovaOS currently includes: - Delay and sleep ✓
- UART ✓
- UART initialization and logging - Switching ELs ✓
- Delay and sleep primitives - GPIOs ✓
- Exception level transitions across EL2, EL1, and EL0 - GPIO Interrupts ✓
- GPIO control and interrupt handling - Communicate with peripherals via mailboxes ✓
- Peripheral mailbox communication - Frame Buffer ✓
- Framebuffer drawing primitives - Heap Memory allocation ✓
- Heap memory allocation - MMU ✓
- MMU initialization and translation table setup - SVC instructions
- Kernel Independent Applications
Work in progress: - Multi Core
- Dynamic clock speed
- SVC instruction handling - Multiprocessing
- Basic UART console improvements - Basic Terminal over UART
- Multi-application management
Planned next:
- Multi-core support
- Dynamic clock speed management
- Kernel-independent applications
- Multiprocessing improvements
## Project Structure
- `src/` - kernel source, architecture code, peripherals, interrupts, and runtime
- `workspace/` - supporting crates such as `heap` and `nova_error`
- `tools/` - build, simulation, SD image generation, and deployment scripts
- `firmware_files/` - Raspberry Pi firmware files copied to SD or TFTP
- `link.ld` - linker script for the kernel image
## Requirements
You will need:
- Rust nightly toolchain (`rust-toolchain.toml` pins `nightly`)
- Rust target `aarch64-unknown-none`
- `llvm-objcopy` for generating `kernel8.img`
- `qemu-system-aarch64` for emulation
- `mtools` (`mformat`, `mcopy`) for SD image generation
Install the Rust target if needed:
```bash
rustup target add aarch64-unknown-none
```
## Build
Debug image:
```bash
cd tools
./build_debug.sh
```
Release image:
```bash
cd tools
./build_release.sh
```
Both scripts produce a `kernel8.img` under `target/aarch64-unknown-none/<profile>/`.
## Run In QEMU
1. Generate an SD image with firmware files:
```bash
cd tools
./generate_sd_card.sh
```
2. Start the emulator:
```bash
cd tools
./start_simulator.sh
```
For debug mode with the GDB stub enabled (`-S -s`):
```bash
cd tools
./start_simulator_debug.sh
```
## Deploy To Hardware
Use the TFTP workflow to deploy to a Raspberry Pi:
1. Copy `.env.example` to `.env` and fill in your values:
- `REMOTE_USER`
- `REMOTE_HOST`
- `TFTP_PATH`
- `BUILD_PATH`
- `BINARY_NAME`
- `KERNEL_NAME`
2. Run:
```bash
cd tools
./deply_to_hw.sh
```
## Notes
- This is an educational kernel project and is actively evolving.
- Interfaces and boot flow may change as features are added.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

+5 -5
View File
@@ -4,13 +4,9 @@ SECTIONS {
.text ALIGN(4) : { .text ALIGN(4) : {
KEEP(*(.text._start)) KEEP(*(.text._start))
*(.text .text.*) *(.text .text.*)
}
.vector_table ALIGN(2K) : {
KEEP(*(.vector_t))
}
. = ALIGN(4K); . = ALIGN(4K);
__text_end = .; __text_end = .;
}
.rodata : { .rodata : {
*(.rodata .rodata.*) *(.rodata .rodata.*)
@@ -30,6 +26,10 @@ SECTIONS {
__share_end = .; __share_end = .;
.vector_table ALIGN(2K) : {
KEEP(*(.vector_table))
}
# EL2 Stack # EL2 Stack
.stack ALIGN(16): { .stack ALIGN(16): {
__stack_start = .; __stack_start = .;
+54 -127
View File
@@ -43,61 +43,14 @@ const L2_BLOCK_BITMAP_WORDS: usize = LEVEL2_BLOCK_SIZE / (64 * GRANULARITY);
const MAX_PAGE_COUNT: usize = 1024 * 1024 * 1024 / GRANULARITY; const MAX_PAGE_COUNT: usize = 1024 * 1024 * 1024 / GRANULARITY;
const TRANSLATION_TABLE_BASE_ADDR: usize = 0xFFFF_FF82_0000_0000; const TRANSLATION_TABLE_BASE_ADDR: usize = 0xFFFF_FF82_0000_0000;
#[no_mangle] pub const KERNEL_VIRTUAL_MEM_SPACE: usize = 0xFFFF_FF80_0000_0000;
pub static KERNEL_VIRTUAL_MEM_SPACE: usize = 0xFFFF_FF80_0000_0000;
pub const STACK_START_ADDR: usize = !KERNEL_VIRTUAL_MEM_SPACE & (!0xF); pub const STACK_START_ADDR: usize = !KERNEL_VIRTUAL_MEM_SPACE & (!0xF);
pub mod physical_mapping; mod physical_mapping;
pub type VirtAddr = usize; type VirtAddr = usize;
pub type PhysAddr = usize; type PhysAddr = usize;
#[derive(Clone, Copy)]
pub struct TableEntry {
value: u64,
}
impl TableEntry {
pub fn invalid() -> Self {
Self { value: 0 }
}
fn table_descriptor(addr: PhysAddr) -> Self {
Self {
value: (addr as u64 & 0x0000_FFFF_FFFF_F000) | TABLE,
}
}
fn block_descriptor(physical_address: usize, additional_flags: u64) -> Self {
Self {
value: (physical_address as u64 & 0x0000_FFFF_FFFF_F000)
| BLOCK
| ACCESS_FLAG
| INNER_SHAREABILITY
| additional_flags,
}
}
fn page_descriptor(physical_address: usize, additional_flags: u64) -> Self {
Self {
value: (physical_address as u64 & 0x0000_FFFF_FFFF_F000)
| PAGE
| ACCESS_FLAG
| INNER_SHAREABILITY
| additional_flags,
}
}
fn is_invalid(self) -> bool {
self.value & 0b11 == 0
}
#[inline]
fn address(self) -> PhysAddr {
self.value as usize & 0x0000_FFFF_FFFF_F000
}
}
pub enum PhysSource { pub enum PhysSource {
Any, Any,
@@ -105,24 +58,12 @@ pub enum PhysSource {
} }
#[repr(align(4096))] #[repr(align(4096))]
pub struct PageTable(pub [TableEntry; TABLE_ENTRY_COUNT]); pub struct PageTable([u64; TABLE_ENTRY_COUNT]);
impl Iterator for PageTable {
type Item = VirtAddr;
fn next(&mut self) -> Option<Self::Item> {
for (offset, entity) in self.0.iter().enumerate() {
if entity.is_invalid() {
return Some(offset);
}
}
None
}
}
#[no_mangle] #[no_mangle]
pub static mut TRANSLATIONTABLE_TTBR0: PageTable = PageTable([TableEntry { value: 0 }; 512]); pub static mut TRANSLATIONTABLE_TTBR0: PageTable = PageTable([0; 512]);
#[no_mangle] #[no_mangle]
pub static mut TRANSLATIONTABLE_TTBR1: PageTable = PageTable([TableEntry { value: 0 }; 512]); pub static mut TRANSLATIONTABLE_TTBR1: PageTable = PageTable([0; 512]);
/// Allocate a memory block of `size` starting at `virtual_address`. /// Allocate a memory block of `size` starting at `virtual_address`.
pub fn allocate_memory( pub fn allocate_memory(
@@ -161,7 +102,7 @@ fn map_range_explicit(
) -> Result<(), NovaError> { ) -> Result<(), NovaError> {
let mut remaining = size_bytes; let mut remaining = size_bytes;
while !virt.is_multiple_of(LEVEL2_BLOCK_SIZE) && remaining > 0 { while virt % LEVEL2_BLOCK_SIZE != 0 {
map_page(virt, phys, base, flags)?; map_page(virt, phys, base, flags)?;
(virt, _) = virt.overflowing_add(GRANULARITY); (virt, _) = virt.overflowing_add(GRANULARITY);
phys += GRANULARITY; phys += GRANULARITY;
@@ -204,12 +145,13 @@ fn map_range_dynamic(
(virt, _) = virt.overflowing_add(GRANULARITY); (virt, _) = virt.overflowing_add(GRANULARITY);
remaining -= GRANULARITY; remaining -= GRANULARITY;
} }
Ok(()) Ok(())
} }
/// Allocate a singe page. /// Allocate a singe page.
pub fn alloc_page( pub fn alloc_page(
virtual_address: VirtAddr, virtual_address: usize,
base_table: *mut PageTable, base_table: *mut PageTable,
additional_flags: u64, additional_flags: u64,
) -> Result<(), NovaError> { ) -> Result<(), NovaError> {
@@ -221,28 +163,6 @@ pub fn alloc_page(
) )
} }
/// Allocate a singe page in one block.
pub fn find_free_kerne_page_in_block(start: VirtAddr) -> Result<VirtAddr, NovaError> {
if !start.is_multiple_of(LEVEL2_BLOCK_SIZE) {
return Err(NovaError::Misalignment);
}
let (off1, off2, _) = virtual_address_to_table_offset(start);
let offsets = [off1, off2];
let table = unsafe {
&mut *navigate_table(
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1),
&offsets,
true,
)?
};
if let Some(offset) = table.next() {
return Ok(start + (offset * GRANULARITY));
}
Err(NovaError::OutOfMeomory)
}
/// Allocate a single page at an explicit `physical_address`. /// Allocate a single page at an explicit `physical_address`.
pub fn alloc_page_explicit( pub fn alloc_page_explicit(
virtual_address: usize, virtual_address: usize,
@@ -269,14 +189,14 @@ pub fn map_page(
let offsets = [l1_off, l2_off]; let offsets = [l1_off, l2_off];
let table_ptr = navigate_table(base_table_ptr, &offsets, true)?; let table_ptr = navigate_table(base_table_ptr, &offsets)?;
let table = unsafe { &mut *table_ptr }; let table = unsafe { &mut *table_ptr };
if !table.0[l3_off].is_invalid() { if table.0[l3_off] & 0b11 > 0 {
return Err(NovaError::Paging("Page already occupied.")); return Err(NovaError::Paging);
} }
table.0[l3_off] = TableEntry::page_descriptor(physical_address, additional_flags); table.0[l3_off] = create_page_descriptor_entry(physical_address, additional_flags);
Ok(()) Ok(())
} }
@@ -309,16 +229,16 @@ pub fn map_l2_block(
) -> Result<(), NovaError> { ) -> Result<(), NovaError> {
let (l1_off, l2_off, _) = virtual_address_to_table_offset(virtual_addr); let (l1_off, l2_off, _) = virtual_address_to_table_offset(virtual_addr);
let offsets = [l1_off]; let offsets = [l1_off];
let table_ptr = navigate_table(base_table_ptr, &offsets, true)?; let table_ptr = navigate_table(base_table_ptr, &offsets)?;
let table = unsafe { &mut *table_ptr }; let table = unsafe { &mut *table_ptr };
// Verify virtual address is available. // Verify virtual address is available.
if !table.0[l2_off].is_invalid() { if table.0[l2_off] & 0b11 != 0 {
return Err(NovaError::Paging("Block already occupied.")); return Err(NovaError::Paging);
} }
let new_entry = TableEntry::block_descriptor(physical_address, additional_flags); let new_entry = create_block_descriptor_entry(physical_address, additional_flags);
table.0[l2_off] = new_entry; table.0[l2_off] = new_entry;
@@ -358,6 +278,26 @@ pub fn reserve_range(
Ok(start_physical_address) Ok(start_physical_address)
} }
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) { fn virtual_address_to_table_offset(virtual_addr: usize) -> (usize, usize, usize) {
let absolute_page_off = (virtual_addr & !KERNEL_VIRTUAL_MEM_SPACE) / GRANULARITY; let absolute_page_off = (virtual_addr & !KERNEL_VIRTUAL_MEM_SPACE) / GRANULARITY;
let l3_off = absolute_page_off % TABLE_ENTRY_COUNT; let l3_off = absolute_page_off % TABLE_ENTRY_COUNT;
@@ -371,11 +311,10 @@ fn virtual_address_to_table_offset(virtual_addr: usize) -> (usize, usize, usize)
fn navigate_table( fn navigate_table(
initial_table_ptr: *mut PageTable, initial_table_ptr: *mut PageTable,
offsets: &[usize], offsets: &[usize],
create_missing: bool,
) -> Result<*mut PageTable, NovaError> { ) -> Result<*mut PageTable, NovaError> {
let mut table = initial_table_ptr; let mut table = initial_table_ptr;
for offset in offsets { for offset in offsets {
table = next_table(table, *offset, create_missing)?; table = next_table(table, *offset)?;
} }
Ok(table) Ok(table)
} }
@@ -383,20 +322,13 @@ fn navigate_table(
/// Get the next table one level down. /// Get the next table one level down.
/// ///
/// If table doesn't exit a page will be allocated for it. /// If table doesn't exit a page will be allocated for it.
fn next_table( fn next_table(table_ptr: *mut PageTable, offset: usize) -> Result<*mut PageTable, NovaError> {
table_ptr: *mut PageTable,
offset: usize,
create_missing: bool,
) -> Result<*mut PageTable, NovaError> {
let table = unsafe { &mut *table_ptr }; let table = unsafe { &mut *table_ptr };
match table.0[offset].value & 0b11 { match table.0[offset] & 0b11 {
0 => { 0 => {
if !create_missing {
return Err(NovaError::Paging("No table defined."));
}
let new_phys_page_table_address = reserve_page(); let new_phys_page_table_address = reserve_page();
table.0[offset] = TableEntry::table_descriptor(new_phys_page_table_address); table.0[offset] = create_table_descriptor_entry(new_phys_page_table_address);
map_page( map_page(
phys_table_to_kernel_space(new_phys_page_table_address), phys_table_to_kernel_space(new_phys_page_table_address),
new_phys_page_table_address, new_phys_page_table_address,
@@ -404,31 +336,26 @@ fn next_table(
NORMAL_MEM | WRITABLE | PXN | UXN, NORMAL_MEM | WRITABLE | PXN | UXN,
)?; )?;
Ok(resolve_table_addr(table.0[offset].address()) as *mut PageTable) Ok(entry_table_addr(table.0[offset] as usize) as *mut PageTable)
} }
1 => Err(NovaError::Paging( 1 => Err(NovaError::Paging),
"Can't navigate table due to block mapping.", 3 => Ok(entry_table_addr(table.0[offset] as usize) as *mut PageTable),
)),
3 => Ok(resolve_table_addr(table.0[offset].address()) as *mut PageTable),
_ => unreachable!(), _ => unreachable!(),
} }
} }
/// Converts a physical table address and returns the corresponding virtual address depending on EL. /// Extracts the physical address out of an table entry.
///
/// - `== EL0` -> panic
/// - `== EL1` -> 0xFFFFFF82XXXXXXXX
/// - `>= EL2` -> physical address
#[inline] #[inline]
fn resolve_table_addr(physical_address: PhysAddr) -> VirtAddr { fn entry_phys(entry: usize) -> PhysAddr {
let current_el = get_current_el(); entry & 0x0000_FFFF_FFFF_F000
}
if current_el >= 2 { #[inline]
physical_address fn entry_table_addr(entry: usize) -> VirtAddr {
} else if get_current_el() == 1 { if get_current_el() == 1 {
phys_table_to_kernel_space(physical_address) phys_table_to_kernel_space(entry_phys(entry))
} else { } else {
panic!("Access to table entries is forbidden in EL0.") entry_phys(entry)
} }
} }
+11 -17
View File
@@ -1,19 +1,13 @@
use crate::aarch64::mmu::{PhysAddr, GRANULARITY, L2_BLOCK_BITMAP_WORDS, MAX_PAGE_COUNT}; use crate::aarch64::mmu::{PhysAddr, GRANULARITY, L2_BLOCK_BITMAP_WORDS, MAX_PAGE_COUNT};
use nova_error::NovaError; use nova_error::NovaError;
struct PagingMap { static mut PAGING_BITMAP: [u64; MAX_PAGE_COUNT / 64] = [0; MAX_PAGE_COUNT / 64];
bitmap: [u64; MAX_PAGE_COUNT / 64],
}
static mut PAGING_BITMAP: PagingMap = PagingMap {
bitmap: [0; MAX_PAGE_COUNT / 64],
};
pub fn reserve_page() -> PhysAddr { pub fn reserve_page() -> PhysAddr {
if let Some(address) = find_unallocated_page() { if let Some(address) = find_unallocated_page() {
let page = address / GRANULARITY; let page = address / GRANULARITY;
let word_index = page / 64; let word_index = page / 64;
unsafe { PAGING_BITMAP.bitmap[word_index] |= 1 << (page % 64) }; unsafe { PAGING_BITMAP[word_index] |= 1 << (page % 64) };
return address; return address;
} }
panic!("Out of Memory!"); panic!("Out of Memory!");
@@ -23,18 +17,18 @@ pub fn reserve_page_explicit(physical_address: usize) -> Result<PhysAddr, NovaEr
let page = physical_address / GRANULARITY; let page = physical_address / GRANULARITY;
let word_index = page / 64; let word_index = page / 64;
if unsafe { PAGING_BITMAP.bitmap[word_index] } & (1 << (page % 64)) > 0 { if unsafe { PAGING_BITMAP[word_index] } & (1 << (page % 64)) > 0 {
return Err(NovaError::Paging("Page PA already taken.")); return Err(NovaError::Paging);
} }
unsafe { PAGING_BITMAP.bitmap[word_index] |= 1 << (page % 64) }; unsafe { PAGING_BITMAP[word_index] |= 1 << (page % 64) };
Ok(physical_address) Ok(physical_address)
} }
pub fn reserve_block() -> usize { pub fn reserve_block() -> usize {
if let Some(start) = find_contiguous_free_bitmap_words(L2_BLOCK_BITMAP_WORDS) { if let Some(start) = find_contiguous_free_bitmap_words(L2_BLOCK_BITMAP_WORDS) {
for j in 0..L2_BLOCK_BITMAP_WORDS { for j in 0..L2_BLOCK_BITMAP_WORDS {
unsafe { PAGING_BITMAP.bitmap[start + j] = u64::MAX }; unsafe { PAGING_BITMAP[start + j] = u64::MAX };
} }
return start * 64 * GRANULARITY; return start * 64 * GRANULARITY;
} }
@@ -46,21 +40,21 @@ pub fn reserve_block_explicit(physical_address: usize) -> Result<(), NovaError>
let page = physical_address / GRANULARITY; let page = physical_address / GRANULARITY;
for i in 0..L2_BLOCK_BITMAP_WORDS { for i in 0..L2_BLOCK_BITMAP_WORDS {
unsafe { unsafe {
if PAGING_BITMAP.bitmap[(page / 64) + i] != 0 { if PAGING_BITMAP[(page / 64) + i] != 0 {
return Err(NovaError::Paging("Block PA already taken.")); return Err(NovaError::Paging);
} }
}; };
} }
for i in 0..L2_BLOCK_BITMAP_WORDS { for i in 0..L2_BLOCK_BITMAP_WORDS {
unsafe { unsafe {
PAGING_BITMAP.bitmap[(page / 64) + i] = u64::MAX; PAGING_BITMAP[(page / 64) + i] = u64::MAX;
}; };
} }
Ok(()) Ok(())
} }
fn find_unallocated_page() -> Option<usize> { fn find_unallocated_page() -> Option<usize> {
for (i, entry) in unsafe { PAGING_BITMAP.bitmap }.iter().enumerate() { for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() {
if *entry != u64::MAX { if *entry != u64::MAX {
for offset in 0..64 { for offset in 0..64 {
if entry >> offset & 0b1 == 0 { if entry >> offset & 0b1 == 0 {
@@ -76,7 +70,7 @@ fn find_contiguous_free_bitmap_words(required_words: usize) -> Option<usize> {
let mut run_start = 0; let mut run_start = 0;
let mut run_len = 0; let mut run_len = 0;
for (i, entry) in unsafe { PAGING_BITMAP.bitmap }.iter().enumerate() { for (i, entry) in unsafe { PAGING_BITMAP }.iter().enumerate() {
if *entry == 0 { if *entry == 0 {
if run_len == 0 { if run_len == 0 {
run_start = i; run_start = i;
-145
View File
@@ -1,145 +0,0 @@
use crate::{
aarch64::mmu::{
find_free_kerne_page_in_block, map_page, physical_mapping::reserve_page, PageTable,
TableEntry, VirtAddr, NORMAL_MEM, TRANSLATIONTABLE_TTBR0, TRANSLATIONTABLE_TTBR1, WRITABLE,
},
configuration::memory_mapping::{APPLICATION_TRANSLATION_TABLE_VA, EL0_STACK_TOP},
};
use alloc::vec::Vec;
use core::{arch::asm, mem, ptr::write_volatile};
use log::error;
use nova_error::NovaError;
use spin::Mutex;
struct AppManager {
apps: Option<Vec<Application>>,
}
impl AppManager {
const fn new() -> Self {
Self { apps: None }
}
}
unsafe impl Send for AppManager {}
pub struct Application {
pub table_ptr: *mut TableEntry,
pub start_addr: usize,
pub stack_pointer: usize,
}
impl Application {
pub fn new(start_addr: VirtAddr) -> Self {
let physical_addr = reserve_page();
let virtual_address =
find_free_kerne_page_in_block(APPLICATION_TRANSLATION_TABLE_VA).unwrap();
map_page(
virtual_address,
physical_addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1),
NORMAL_MEM | WRITABLE,
)
.unwrap();
// TODO: Temporary solution, while kernel and app share some memory regions
#[allow(static_mut_refs)]
unsafe {
let table = &mut *(virtual_address as *mut PageTable);
table.0 = TRANSLATIONTABLE_TTBR0.0;
}
Self {
table_ptr: physical_addr as *mut TableEntry,
start_addr,
stack_pointer: EL0_STACK_TOP,
}
}
pub unsafe fn configure_registers(&self) {
asm!("msr ELR_EL1, {}", in(reg) self.start_addr);
asm!("msr SPSR_EL1, {0:x}", in(reg) 0);
asm!("msr SP_EL0, {0:x}", in(reg) self.stack_pointer);
asm!("msr TTBR0_EL1, {}", in(reg) self.table_ptr as usize);
}
/// Starts an application.
///
/// `ELR_EL1` -> Exception Link Register (starting virtual address)
/// `SPSR_EL1` -> Saved Program State Register (settings for `eret` behaviour)
/// `SP_EL0` -> Stack Pointer Register (virtual_address of stack Pointer)
/// `TTBR0_EL1` -> Translation Table base Register Register
pub fn start(&mut self, args: Vec<&str>) {
let size = args.len();
let argv = self.construct_inital_stack(args);
unsafe {
self.configure_registers();
asm!("", in("x0") size, in("x1") argv);
asm!("eret");
}
}
/// Initializes the stack based on the System V ABI
fn construct_inital_stack(&mut self, args: Vec<&str>) -> usize {
let size = args.len();
let mut arg_addresses = Vec::with_capacity(size);
// Write strings into stack
for value in args {
self.stack_pointer -= value.len() * mem::size_of::<u8>();
let pointer = self.stack_pointer as *mut u8;
unsafe { core::ptr::copy(value.as_ptr(), pointer, value.len()) };
arg_addresses.push(pointer);
}
self.stack_pointer = align_down(self.stack_pointer, 16);
// TODO: Auxiliry vector entry
// TODO: Environment pointers
let argv = self.stack_pointer;
// Write argument pointers into stack
for addr in arg_addresses {
unsafe { write_volatile(self.stack_pointer as *mut *const u8, addr) };
self.stack_pointer -= mem::size_of::<*const u8>();
}
argv
}
}
fn align_down(sp: usize, align: usize) -> usize {
sp & !(align - 1)
}
static APP_MANAGER: Mutex<AppManager> = Mutex::new(AppManager::new());
pub fn initialize_app_manager() {
let mut guard = APP_MANAGER.lock();
guard.apps = Some(Vec::new());
}
pub fn add_app(app: Application) -> Result<(), NovaError> {
if let Some(app_list) = APP_MANAGER.lock().apps.as_mut() {
app_list.push(app);
Ok(())
} else {
Err(NovaError::General("AppManager not initalized."))
}
}
pub fn start_app(index: usize, args: Vec<&str>) -> Result<(), NovaError> {
if let Some(app) = APP_MANAGER
.lock()
.apps
.as_mut()
.and_then(|am| am.get_mut(index))
{
app.start(args);
unreachable!()
} else {
error!("Unable to start app due to invalid App ID.");
Err(NovaError::General("Invalid app id."))
}
}
+118 -4
View File
@@ -10,12 +10,8 @@ el2_to_el1:
msr SPSR_EL2, x0 msr SPSR_EL2, x0
// Set return address to kernel_main // Set return address to kernel_main
adrp x0, KERNEL_VIRTUAL_MEM_SPACE
ldr x1, [x0, :lo12:KERNEL_VIRTUAL_MEM_SPACE]
adrp x0, kernel_main adrp x0, kernel_main
add x0, x0, :lo12:kernel_main add x0, x0, :lo12:kernel_main
orr x0, x0, x1
msr ELR_EL2, x0 msr ELR_EL2, x0
// Set SP_EL1 to stack base // Set SP_EL1 to stack base
@@ -78,3 +74,121 @@ configure_mmu_el1:
ret 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
adrp x0, EL0_STACK_TOP
ldr x1, [x0, :lo12:EL0_STACK_TOP]
msr SP_EL0, x1
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
+96 -1
View File
@@ -31,4 +31,99 @@ const AS: u64 = 0b1 << 36; // configure an ASID size of 16 bits
#[no_mangle] #[no_mangle]
pub static TCR_EL1_CONF: u64 = IPS | TG0 | TG1 | T0SZ | T1SZ | SH0 | SH1 | AS; pub static TCR_EL1_CONF: u64 = IPS | TG0 | TG1 | T0SZ | T1SZ | SH0 | SH1 | AS;
pub mod memory_mapping; pub mod mmu {
use crate::{
aarch64::mmu::{
alloc_block_l2_explicit, allocate_memory, map_l2_block, map_page, reserve_range,
PhysSource, DEVICE_MEM, EL0_ACCESSIBLE, GRANULARITY, KERNEL_VIRTUAL_MEM_SPACE,
LEVEL1_BLOCK_SIZE, LEVEL2_BLOCK_SIZE, NORMAL_MEM, PXN, READ_ONLY, STACK_START_ADDR,
TRANSLATIONTABLE_TTBR0, UXN, WRITABLE,
},
PERIPHERAL_BASE,
};
#[no_mangle]
static EL1_STACK_TOP: usize = STACK_START_ADDR | KERNEL_VIRTUAL_MEM_SPACE;
const EL1_STACK_SIZE: usize = LEVEL2_BLOCK_SIZE * 2;
#[no_mangle]
static EL0_STACK_TOP: usize = STACK_START_ADDR;
const EL0_STACK_SIZE: usize = LEVEL2_BLOCK_SIZE * 2;
extern "C" {
static __text_end: u64;
static __share_end: u64;
static __kernel_end: u64;
}
pub fn initialize_mmu_translation_tables() {
let text_end = unsafe { &__text_end } as *const _ as usize;
let shared_segment_end = unsafe { &__share_end } as *const _ as usize;
let kernel_end = unsafe { &__kernel_end } as *const _ as usize;
reserve_range(0x0, kernel_end).unwrap();
for addr in (0..text_end).step_by(GRANULARITY) {
map_page(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | READ_ONLY | NORMAL_MEM,
)
.unwrap();
}
for addr in (text_end..shared_segment_end).step_by(GRANULARITY) {
map_page(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | WRITABLE | NORMAL_MEM,
)
.unwrap();
}
for addr in (shared_segment_end..kernel_end).step_by(LEVEL2_BLOCK_SIZE) {
map_l2_block(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
WRITABLE | UXN | NORMAL_MEM,
)
.unwrap();
}
for addr in (PERIPHERAL_BASE..LEVEL1_BLOCK_SIZE).step_by(LEVEL2_BLOCK_SIZE) {
alloc_block_l2_explicit(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | WRITABLE | UXN | PXN | DEVICE_MEM,
)
.unwrap();
}
// Frame Buffer memory range
allocate_memory(
0x3c100000,
1080 * 1920 * 4,
PhysSource::Explicit(0x3c100000),
NORMAL_MEM | PXN | UXN | WRITABLE | EL0_ACCESSIBLE,
)
.unwrap();
allocate_memory(
EL1_STACK_TOP - EL1_STACK_SIZE + 0x10,
EL1_STACK_SIZE,
PhysSource::Any,
WRITABLE | NORMAL_MEM,
)
.unwrap();
allocate_memory(
EL0_STACK_TOP - EL0_STACK_SIZE + 0x10,
EL0_STACK_SIZE,
PhysSource::Any,
WRITABLE | EL0_ACCESSIBLE | NORMAL_MEM,
)
.unwrap();
}
}
-127
View File
@@ -1,127 +0,0 @@
use crate::{
aarch64::mmu::{
alloc_block_l2_explicit, allocate_memory, map_page, physical_mapping::reserve_page,
reserve_range, PhysAddr, PhysSource, VirtAddr, DEVICE_MEM, EL0_ACCESSIBLE, GRANULARITY,
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_SIZE: usize = LEVEL2_BLOCK_SIZE * 2;
#[no_mangle]
pub static EL0_STACK_TOP: usize = STACK_START_ADDR;
pub const EL0_STACK_SIZE: usize = LEVEL2_BLOCK_SIZE * 2;
pub const MAILBOX_VIRTUAL_ADDRESS: VirtAddr = 0xFFFF_FF81_FFFF_E000;
pub static mut MAILBOX_PHYSICAL_ADDRESS: Option<PhysAddr> = None;
// TODO: Currently limited to 512 applications, more than enough, but has to be kept
// in mind
pub const APPLICATION_TRANSLATION_TABLE_VA: VirtAddr = 0xFFFF_FF81_FE00_0000;
extern "C" {
static __text_end: u64;
static __share_end: u64;
static __kernel_end: u64;
}
pub fn initialize_mmu_translation_tables() {
let text_end = unsafe { &__text_end } as *const _ as usize;
let shared_segment_end = unsafe { &__share_end } as *const _ as usize;
let kernel_end = unsafe { &__kernel_end } as *const _ as usize;
reserve_range(0x0, kernel_end).unwrap();
for addr in (0..text_end).step_by(GRANULARITY) {
map_page(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | READ_ONLY | NORMAL_MEM,
)
.unwrap();
}
for addr in (0..text_end).step_by(GRANULARITY) {
map_page(
addr | KERNEL_VIRTUAL_MEM_SPACE,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1),
READ_ONLY | NORMAL_MEM,
)
.unwrap();
}
for addr in (text_end..shared_segment_end).step_by(GRANULARITY) {
map_page(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | WRITABLE | NORMAL_MEM,
)
.unwrap();
}
for addr in (text_end..shared_segment_end).step_by(GRANULARITY) {
map_page(
addr | KERNEL_VIRTUAL_MEM_SPACE,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR1),
EL0_ACCESSIBLE | WRITABLE | NORMAL_MEM,
)
.unwrap();
}
for addr in (PERIPHERAL_BASE..LEVEL1_BLOCK_SIZE).step_by(LEVEL2_BLOCK_SIZE) {
alloc_block_l2_explicit(
addr,
addr,
core::ptr::addr_of_mut!(TRANSLATIONTABLE_TTBR0),
EL0_ACCESSIBLE | WRITABLE | UXN | PXN | DEVICE_MEM,
)
.unwrap();
}
// Frame Buffer memory range
allocate_memory(
0x3c100000,
1080 * 1920 * 4,
PhysSource::Explicit(0x3c100000),
NORMAL_MEM | PXN | UXN | WRITABLE | EL0_ACCESSIBLE,
)
.unwrap();
// Allocate EL1 stack
allocate_memory(
EL1_STACK_TOP - EL1_STACK_SIZE + 0x10,
EL1_STACK_SIZE,
PhysSource::Any,
WRITABLE | NORMAL_MEM,
)
.unwrap();
// Allocate EL0 stack
allocate_memory(
EL0_STACK_TOP - EL0_STACK_SIZE + 0x10,
EL0_STACK_SIZE,
PhysSource::Any,
WRITABLE | EL0_ACCESSIBLE | NORMAL_MEM,
)
.unwrap();
// Allocate Mailbox buffer
{
let addr = reserve_page();
unsafe { MAILBOX_PHYSICAL_ADDRESS = Some(addr) };
allocate_memory(
MAILBOX_VIRTUAL_ADDRESS,
GRANULARITY,
PhysSource::Explicit(addr),
WRITABLE | NORMAL_MEM,
)
.unwrap();
}
}
-92
View File
@@ -1,92 +0,0 @@
use alloc::string::String;
use crate::{
application_manager::start_app,
interrupt_handlers::irq::{register_interrupt_handler, IRQSource},
peripherals::uart::read_uart_data,
pi3::mailbox::read_soc_temp,
print, println,
};
pub static mut TERMINAL: Option<Terminal> = None;
pub struct Terminal {
input: String,
}
impl Default for Terminal {
fn default() -> Self {
Self::new()
}
}
impl Terminal {
pub fn new() -> Self {
Self {
input: String::new(),
}
}
fn flush(&mut self) {
print!("\n> {}", self.input);
}
fn exec(&mut self) {
print!("\n");
let val = self.input.clone();
self.input.clear();
let mut parts = val.split(" ");
match parts.next().unwrap() {
"temp" => {
println!("{}", read_soc_temp([0]).unwrap()[1]);
}
"app" => {
if let Some(app_id) = parts.next().and_then(|a| a.parse::<usize>().ok()) {
let args = parts.collect();
let _ = start_app(app_id, args);
} else {
println!("App ID not set.");
}
}
_ => {
println!("Unknown command: \"{}\"", self.input);
}
}
self.input.clear();
}
}
pub fn init_terminal() {
unsafe { TERMINAL = Some(Terminal::new()) };
register_terminal_interrupt_handler();
}
fn terminal_uart_rx_interrupt_handler() {
let input = read_uart_data();
#[allow(static_mut_refs)]
if let Some(term) = unsafe { TERMINAL.as_mut() } {
match input {
'\r' => {
term.exec();
term.flush();
}
_ => {
term.input.push(input);
print!("{}", input);
}
}
}
}
pub fn flush_terminal() {
#[allow(static_mut_refs)]
if let Some(term) = unsafe { TERMINAL.as_mut() } {
term.flush();
}
}
fn register_terminal_interrupt_handler() {
register_interrupt_handler(IRQSource::UartInt, terminal_uart_rx_interrupt_handler);
}
+5 -3
View File
@@ -4,8 +4,10 @@ mod bitmaps;
use bitmaps::BASIC_LEGACY; use bitmaps::BASIC_LEGACY;
use crate::pi3::mailbox::{read_mailbox, write_mailbox}; use crate::{
use log::error; pi3::mailbox::{read_mailbox, write_mailbox},
println,
};
#[repr(align(16))] #[repr(align(16))]
struct Mailbox([u32; 36]); struct Mailbox([u32; 36]);
@@ -235,7 +237,7 @@ impl Default for FrameBuffer {
let _ = read_mailbox(8); let _ = read_mailbox(8);
if mailbox.0[1] == 0 { if mailbox.0[1] == 0 {
error!("Mailbox request was not processed!"); println!("Failed");
} }
mailbox.0[28] &= 0x3FFFFFFF; mailbox.0[28] &= 0x3FFFFFFF;
+180 -35
View File
@@ -1,10 +1,19 @@
use core::arch::asm; use core::arch::asm;
use alloc::vec::Vec;
use crate::{ use crate::{
aarch64::registers::{daif::mask_all, read_esr_el1, read_exception_source_el}, aarch64::registers::{
daif::{mask_all, unmask_irq},
read_elr_el1, read_esr_el1, read_exception_source_el,
},
get_current_el, get_current_el,
peripherals::{
gpio::{read_gpio_event_detect_status, reset_gpio_event_detect_status},
uart::clear_uart_interrupt_state,
},
println, read_address, write_address,
}; };
use log::debug;
const INTERRUPT_BASE: u32 = 0x3F00_B000; const INTERRUPT_BASE: u32 = 0x3F00_B000;
const IRQ_PENDING_BASE: u32 = INTERRUPT_BASE + 0x204; const IRQ_PENDING_BASE: u32 = INTERRUPT_BASE + 0x204;
@@ -13,29 +22,30 @@ const DISABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x21C;
const GPIO_PENDING_BIT_OFFSET: u64 = 0b1111 << 49; const GPIO_PENDING_BIT_OFFSET: u64 = 0b1111 << 49;
#[repr(C)] struct InterruptHandlers {
pub struct TrapFrame { source: IRQSource,
pub x0: u64, function: fn(),
pub x1: u64, }
pub x2: u64,
pub x3: u64, // TODO: replace with hashmap and check for better alternatives for option
pub x4: u64, static mut INTERRUPT_HANDLERS: Option<Vec<InterruptHandlers>> = None;
pub x5: u64,
pub x6: u64, #[derive(Clone)]
pub x7: u64, #[repr(u32)]
pub x8: u64, pub enum IRQSource {
pub x9: u64, AuxInt = 29,
pub x10: u64, I2cSpiSlvInt = 44,
pub x11: u64, Pwa0 = 45,
pub x12: u64, Pwa1 = 46,
pub x13: u64, Smi = 48,
pub x14: u64, GpioInt0 = 49,
pub x15: u64, GpioInt1 = 50,
pub x16: u64, GpioInt2 = 51,
pub x17: u64, GpioInt3 = 52,
pub x18: u64, I2cInt = 53,
pub x29: u64, SpiInt = 54,
pub x30: u64, PcmInt = 55,
UartInt = 57,
} }
/// Representation of the ESR_ELx registers /// Representation of the ESR_ELx registers
@@ -59,25 +69,160 @@ impl From<u32> for EsrElX {
} }
} }
pub mod irq; #[no_mangle]
pub mod synchronous; unsafe extern "C" fn rust_irq_handler() {
mask_all();
let pending_irqs = get_irq_pending_sources();
if pending_irqs & GPIO_PENDING_BIT_OFFSET != 0 {
handle_gpio_interrupt();
let source_el = read_exception_source_el() >> 2;
println!("Source EL: {}", source_el);
println!("Current EL: {}", get_current_el());
println!("Return register address: {:#x}", read_esr_el1());
}
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)();
clear_interrupt_for_source(handler.source.clone());
}
}
}
}
#[no_mangle] #[no_mangle]
unsafe extern "C" fn rust_synchronous_interrupt_no_el_change() { unsafe extern "C" fn rust_synchronous_interrupt_no_el_change() {
mask_all(); mask_all();
let source_el = read_exception_source_el() >> 2; let source_el = read_exception_source_el() >> 2;
debug!("--------Sync Exception in EL{}--------", source_el); println!("--------Sync Exception in EL{}--------", source_el);
debug!("No EL change"); println!("No EL change");
debug!("Current EL: {}", get_current_el()); println!("Current EL: {}", get_current_el());
debug!("{:?}", EsrElX::from(read_esr_el1())); println!("{:?}", EsrElX::from(read_esr_el1()));
debug!("Return register address: {:#x}", read_esr_el1()); println!("Return register address: {:#x}", read_esr_el1());
debug!("-------------------------------------"); println!("-------------------------------------");
} }
fn set_return_to_kernel_loop() { /// Synchronous Exception Handler
///
/// Lower Exception level, where the implemented level
/// immediately lower than the target level is using
/// AArch64.
#[no_mangle]
unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64() {
mask_all();
let source_el = read_exception_source_el() >> 2;
println!("--------Sync Exception in EL{}--------", source_el);
println!("Exception escalated to EL {}", get_current_el());
println!("Current EL: {}", get_current_el());
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!("-------------------------------------");
set_return_to_kernel_main();
}
fn clear_interrupt_for_source(source: IRQSource) {
match source {
IRQSource::UartInt => clear_uart_interrupt_state(),
_ => {
todo!()
}
}
}
fn set_return_to_kernel_main() {
unsafe { unsafe {
asm!("ldr x0, =kernel_loop", "msr ELR_EL1, x0"); asm!("ldr x0, =kernel_main", "msr ELR_EL1, x0");
asm!("mov x0, #(0b0101)", "msr SPSR_EL1, x0"); asm!("mov x0, #(0b0101)", "msr SPSR_EL1, x0");
} }
} }
fn handle_gpio_interrupt() {
println!("Interrupt");
for i in 0..=53u32 {
let val = read_gpio_event_detect_status(i);
if val {
#[allow(clippy::single_match)]
match i {
26 => {
println!("Button Pressed");
}
_ => {}
}
// Reset GPIO Interrupt handler by writing a 1
reset_gpio_event_detect_status(i);
}
}
unmask_irq();
}
/// Enables IRQ Source
pub fn enable_irq_source(state: IRQSource) {
let nr = state as u32;
let register = ENABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
let current = unsafe { read_address(register) };
let mask = 0b1 << register_offset;
let new_val = current | mask;
unsafe { write_address(register, new_val) };
}
/// Disable IRQ Source
pub fn disable_irq_source(state: IRQSource) {
let nr = state as u32;
let register = DISABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
let current = unsafe { read_address(register) };
let mask = 0b1 << register_offset;
let new_val = current | mask;
unsafe { write_address(register, new_val) };
}
/// Read current IRQ Source status
pub fn read_irq_source_status(state: IRQSource) -> u32 {
let nr = state as u32;
let register = ENABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
(unsafe { read_address(register) } >> register_offset) & 0b1
}
/// Status if a IRQ Source is pending
pub fn is_irq_source_pending(state: IRQSource) -> bool {
let nr = state as u32;
let register = IRQ_PENDING_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
((unsafe { read_address(register) } >> register_offset) & 0b1) != 0
}
/// Status if a IRQ Source is pending
pub fn get_irq_pending_sources() -> u64 {
let mut pending = unsafe { read_address(IRQ_PENDING_BASE + 4) as u64 } << 32;
pending |= unsafe { read_address(IRQ_PENDING_BASE) as 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 { &mut *core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } {
handler_vec.push(InterruptHandlers { source, function });
}
}
-152
View File
@@ -1,152 +0,0 @@
use crate::aarch64::registers::read_esr_el1;
use crate::{
aarch64::registers::{
daif::{mask_all, unmask_irq},
read_exception_source_el,
},
get_current_el,
interrupt_handlers::{
DISABLE_IRQ_BASE, ENABLE_IRQ_BASE, GPIO_PENDING_BIT_OFFSET, IRQ_PENDING_BASE,
},
peripherals::{
gpio::{read_gpio_event_detect_status, reset_gpio_event_detect_status},
uart::clear_uart_interrupt_state,
},
read_address, write_address,
};
use alloc::vec::Vec;
use log::{debug, info};
struct InterruptHandlers {
source: IRQSource,
function: fn(),
}
// TODO: replace with hashmap and check for better alternatives for option
static mut INTERRUPT_HANDLERS: Option<Vec<InterruptHandlers>> = None;
#[derive(Clone)]
#[repr(u32)]
pub enum IRQSource {
AuxInt = 29,
I2cSpiSlvInt = 44,
Pwa0 = 45,
Pwa1 = 46,
Smi = 48,
GpioInt0 = 49,
GpioInt1 = 50,
GpioInt2 = 51,
GpioInt3 = 52,
I2cInt = 53,
SpiInt = 54,
PcmInt = 55,
UartInt = 57,
}
#[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 { &mut *core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } {
handler_vec.push(InterruptHandlers { source, function });
}
}
#[no_mangle]
unsafe extern "C" fn rust_irq_handler() {
mask_all();
let pending_irqs = get_irq_pending_sources();
if pending_irqs & GPIO_PENDING_BIT_OFFSET != 0 {
handle_gpio_interrupt();
let source_el = read_exception_source_el() >> 2;
debug!("Source EL: {}", source_el);
debug!("Current EL: {}", get_current_el());
debug!("Return register address: {:#x}", read_esr_el1());
}
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)();
clear_interrupt_for_source(handler.source.clone());
}
}
}
}
fn handle_gpio_interrupt() {
debug!("GPIO interrupt triggered");
for i in 0..=53u32 {
let val = read_gpio_event_detect_status(i);
if val {
#[allow(clippy::single_match)]
match i {
26 => {
info!("Button Pressed");
}
_ => {}
}
// Reset GPIO Interrupt handler by writing a 1
reset_gpio_event_detect_status(i);
}
}
unmask_irq();
}
/// Enables IRQ Source
pub fn enable_irq_source(state: IRQSource) {
let nr = state as u32;
let register = ENABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
let current = unsafe { read_address(register) };
let mask = 0b1 << register_offset;
let new_val = current | mask;
unsafe { write_address(register, new_val) };
}
/// Disable IRQ Source
pub fn disable_irq_source(state: IRQSource) {
let nr = state as u32;
let register = DISABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
let current = unsafe { read_address(register) };
let mask = 0b1 << register_offset;
let new_val = current | mask;
unsafe { write_address(register, new_val) };
}
/// Read current IRQ Source status
pub fn read_irq_source_status(state: IRQSource) -> u32 {
let nr = state as u32;
let register = ENABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
(unsafe { read_address(register) } >> register_offset) & 0b1
}
/// Status if a IRQ Source is pending
pub fn is_irq_source_pending(state: IRQSource) -> bool {
let nr = state as u32;
let register = IRQ_PENDING_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
((unsafe { read_address(register) } >> register_offset) & 0b1) != 0
}
/// Status if a IRQ Source is pending
pub fn get_irq_pending_sources() -> u64 {
let mut pending = unsafe { read_address(IRQ_PENDING_BASE + 4) as u64 } << 32;
pending |= unsafe { read_address(IRQ_PENDING_BASE) as u64 };
pending
}
fn clear_interrupt_for_source(source: IRQSource) {
match source {
IRQSource::UartInt => clear_uart_interrupt_state(),
_ => {
todo!()
}
}
}
-116
View File
@@ -1,116 +0,0 @@
use crate::{
aarch64::registers::{daif::mask_all, read_elr_el1, read_esr_el1, read_exception_source_el},
get_current_el,
interrupt_handlers::{set_return_to_kernel_loop, EsrElX, TrapFrame},
pi3::mailbox,
};
use log::{debug, error, warn};
/// Synchronous Exception Handler
///
/// Source is a lower Exception level, where the implemented level
/// immediately lower than the target level is using
/// AArch64.
#[no_mangle]
unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64(frame: &mut TrapFrame) -> usize {
mask_all();
let esr: EsrElX = EsrElX::from(read_esr_el1());
debug!("Synchronous interrupt from lower EL triggered");
log_sync_exception();
match esr.ec {
0b100100 => {
error!("Data Abort from a lower Exception level");
error!("Cause: {}", decode_data_abort(esr.iss as usize));
}
0b010101 => {
debug!("SVC instruction execution in AArch64");
return handle_svc(frame);
}
0b100010 => {
error!("PC alignment fault.");
}
_ => {
error!("Synchronous interrupt: Unknown Error Code: {:b}", esr.ec);
}
}
warn!("UnhandledException -> Returning to kernel...");
set_return_to_kernel_loop();
0
}
fn decode_data_abort(iss: usize) -> &'static str {
match iss & 0b111111 {
0b000000 => "Address size fault, level 0",
0b000001 => "Address size fault, level 1",
0b000010 => "Address size fault, level 2",
0b000011 => "Address size fault, level 3",
0b000100 => "Translation fault, level 0",
0b000101 => "Translation fault, level 1",
0b000110 => "Translation fault, level 2",
0b000111 => "Translation fault, level 3",
0b001001 => "Access flag fault, level 1",
0b001010 => "Access flag fault, level 2",
0b001011 => "Access flag fault, level 3",
0b001101 => "Permission fault, level 1",
0b001110 => "Permission fault, level 2",
0b001111 => "Permission fault, level 3",
0b010000 => "Synchronous External abort, not on translation table walk",
0b011000 => {
"Synchronous parity or ECC error on memory access, not on translation table walk"
}
0b010100 => "Synchronous External abort, on translation table walk, level 0",
0b010101 => "Synchronous External abort, on translation table walk, level 1",
0b010110 => "Synchronous External abort, on translation table walk, level 2",
0b010111 => "Synchronous External abort, on translation table walk, level 3",
0b011100 => "Synchronous parity or ECC error on translation table walk, level 0",
0b011101 => "Synchronous parity or ECC error on translation table walk, level 1",
0b011110 => "Synchronous parity or ECC error on translation table walk, level 2",
0b011111 => "Synchronous parity or ECC error on translation table walk, level 3",
0b100001 => "Alignment fault",
0b110000 => "TLB conflict abort",
0b110001 => "Unsupported atomic hardware update fault",
0b110100 => "IMPLEMENTATION DEFINED fault (Lockdown)",
0b110101 => "IMPLEMENTATION DEFINED fault (Unsupported Exclusive or Atomic access)",
0b111101 => "Section Domain Fault",
0b111110 => "Page Domain Fault",
_ => "Reserved / Unknown",
}
}
fn handle_svc(frame: &mut TrapFrame) -> usize {
match frame.x8 {
0 => {
debug!("Program exited!");
set_return_to_kernel_loop();
0
}
67 => {
let response = mailbox::read_soc_temp([0]).unwrap();
response[1] as usize
}
_ => 0,
}
}
fn log_sync_exception() {
let source_el = read_exception_source_el() >> 2;
debug!("--------Sync Exception in EL{}--------", source_el);
debug!("Exception escalated to EL {}", get_current_el());
debug!("Current EL: {}", get_current_el());
let esr: EsrElX = EsrElX::from(read_esr_el1());
debug!("{:?}", esr);
debug!("Return address: {:#x}", read_elr_el1());
debug!("-------------------------------------");
}
+7 -39
View File
@@ -3,13 +3,12 @@
extern crate alloc; extern crate alloc;
use alloc::boxed::Box;
use core::{ use core::{
arch::asm, arch::asm,
panic::PanicInfo, panic::PanicInfo,
ptr::{read_volatile, write_volatile}, ptr::{read_volatile, write_volatile},
}; };
use log::LevelFilter;
use log::{Level, Metadata, Record};
use heap::Heap; use heap::Heap;
@@ -18,13 +17,10 @@ use crate::{
allocate_memory, PhysSource, KERNEL_VIRTUAL_MEM_SPACE, LEVEL2_BLOCK_SIZE, NORMAL_MEM, UXN, allocate_memory, PhysSource, KERNEL_VIRTUAL_MEM_SPACE, LEVEL2_BLOCK_SIZE, NORMAL_MEM, UXN,
WRITABLE, WRITABLE,
}, },
application_manager::initialize_app_manager, interrupt_handlers::initialize_interrupt_handler,
console::{flush_terminal, init_terminal}, logger::DefaultLogger,
interrupt_handlers::irq::initialize_interrupt_handler,
pi3::timer::sleep_s,
}; };
static LOGGER: UartLogger = UartLogger;
static PERIPHERAL_BASE: usize = 0x3F00_0000; static PERIPHERAL_BASE: usize = 0x3F00_0000;
unsafe extern "C" { unsafe extern "C" {
@@ -34,7 +30,7 @@ unsafe extern "C" {
#[global_allocator] #[global_allocator]
pub static mut GLOBAL_ALLOCATOR: Heap = Heap::empty(); pub static mut GLOBAL_ALLOCATOR: Heap = Heap::empty();
pub unsafe fn initialize_kernel_heap() { pub unsafe fn init_kernel_heap() {
let start = core::ptr::addr_of_mut!(__kernel_end) as usize | KERNEL_VIRTUAL_MEM_SPACE; let start = core::ptr::addr_of_mut!(__kernel_end) as usize | KERNEL_VIRTUAL_MEM_SPACE;
let size = LEVEL2_BLOCK_SIZE * 2; let size = LEVEL2_BLOCK_SIZE * 2;
@@ -47,7 +43,6 @@ pub unsafe fn initialize_kernel_heap() {
fn panic(_panic: &PanicInfo) -> ! { fn panic(_panic: &PanicInfo) -> ! {
loop { loop {
println!("Panic: {}", _panic.message()); println!("Panic: {}", _panic.message());
sleep_s(1);
} }
} }
@@ -57,9 +52,8 @@ pub mod aarch64;
pub mod configuration; pub mod configuration;
pub mod framebuffer; pub mod framebuffer;
pub mod interrupt_handlers; pub mod interrupt_handlers;
pub mod logger;
pub mod application_manager;
pub mod console;
pub mod pi3; pub mod pi3;
#[inline(always)] #[inline(always)]
@@ -85,33 +79,7 @@ pub fn get_current_el() -> u64 {
} }
pub fn initialize_kernel() { pub fn initialize_kernel() {
unsafe { initialize_kernel_heap() }; unsafe { init_kernel_heap() };
logger::set_logger(Box::new(DefaultLogger));
initialize_interrupt_handler(); initialize_interrupt_handler();
initialize_app_manager();
init_terminal();
}
struct UartLogger;
impl log::Log for UartLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
if record.level() <= Level::Info {
flush_terminal();
}
}
}
fn flush(&self) {}
}
pub fn init_logger() {
log::set_logger(&LOGGER)
.map(|()| log::set_max_level(LevelFilter::Debug))
.unwrap();
} }
+45
View File
@@ -0,0 +1,45 @@
use core::fmt::Write;
use alloc::{boxed::Box, fmt};
use crate::peripherals::uart;
static mut LOGGER: Option<Box<dyn Logger>> = None;
pub trait Logger: Write + Sync {
fn flush(&mut self);
}
pub struct DefaultLogger;
impl Logger for DefaultLogger {
fn flush(&mut self) {}
}
impl Write for DefaultLogger {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
uart::Uart.write_str(s)
}
}
#[macro_export]
macro_rules! log {
() => {};
($($arg:tt)*) => {
$crate::logger::log(format_args!($($arg)*))
};
}
pub fn log(args: fmt::Arguments) {
if let Some(logger) = unsafe { &mut *core::ptr::addr_of_mut!(LOGGER) } {
logger.write_str("\n").unwrap();
logger.write_fmt(args).unwrap();
logger.flush();
}
}
pub fn set_logger(logger: Box<dyn Logger>) {
unsafe {
LOGGER = Some(logger);
}
}
+34 -85
View File
@@ -6,18 +6,16 @@ use core::{
arch::{asm, global_asm}, arch::{asm, global_asm},
ptr::write_volatile, ptr::write_volatile,
}; };
use log::{debug, info};
extern crate alloc; extern crate alloc;
use alloc::{slice, vec::Vec}; use alloc::vec::Vec;
use nova::{ use nova::{
aarch64::registers::{daif, read_id_aa64mmfr0_el1}, aarch64::registers::{daif, read_id_aa64mmfr0_el1},
application_manager::{add_app, Application}, configuration::mmu::initialize_mmu_translation_tables,
configuration::memory_mapping::initialize_mmu_translation_tables,
framebuffer::{FrameBuffer, BLUE, GREEN, RED}, framebuffer::{FrameBuffer, BLUE, GREEN, RED},
get_current_el, init_logger, get_current_el,
interrupt_handlers::irq::{enable_irq_source, IRQSource}, interrupt_handlers::{enable_irq_source, IRQSource},
peripherals::{ peripherals::{
gpio::{ gpio::{
blink_gpio, gpio_pull_up, set_falling_edge_detect, set_gpio_function, GPIOFunction, blink_gpio, gpio_pull_up, set_falling_edge_detect, set_gpio_function, GPIOFunction,
@@ -25,8 +23,7 @@ use nova::{
}, },
uart::uart_init, uart::uart_init,
}, },
pi3::timer::sleep_s, println,
print, println,
}; };
global_asm!(include_str!("vector.S")); global_asm!(include_str!("vector.S"));
@@ -36,6 +33,7 @@ static mut FRAMEBUFFER: Option<FrameBuffer> = None;
extern "C" { extern "C" {
fn el2_to_el1(); fn el2_to_el1();
fn el1_to_el0();
fn configure_mmu_el1(); fn configure_mmu_el1();
static mut __bss_start: u32; static mut __bss_start: u32;
static mut __bss_end: u32; static mut __bss_end: u32;
@@ -62,23 +60,24 @@ pub extern "C" fn main() -> ! {
// Set ACT Led to Outout // Set ACT Led to Outout
let _ = set_gpio_function(21, GPIOFunction::Output); let _ = set_gpio_function(21, GPIOFunction::Output);
init_logger();
info!("Hello World!"); println!("Hello World!");
info!("Current exception level: {}", get_current_el()); println!("Exception level: {}", get_current_el());
info!("initializing MMU..."); unsafe {
initialize_mmu_translation_tables(); initialize_mmu_translation_tables();
unsafe { configure_mmu_el1() }; configure_mmu_el1();
info!("MMU configured!"); println!("MMU initialized...");
};
debug!("Register: AA64MMFR0_EL1: {:064b}", read_id_aa64mmfr0_el1()); println!("Register: AA64MMFR0_EL1: {:064b}", read_id_aa64mmfr0_el1());
info!("Moving El2->EL1"); println!("Moving El2->EL1");
unsafe { FRAMEBUFFER = Some(FrameBuffer::default()) }; unsafe { FRAMEBUFFER = Some(FrameBuffer::default()) };
unsafe { unsafe {
el2_to_el1(); el2_to_el1();
} }
#[allow(clippy::empty_loop)] #[allow(clippy::empty_loop)]
loop {} loop {}
} }
@@ -92,50 +91,29 @@ unsafe fn zero_bss() {
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn kernel_main() { pub extern "C" fn kernel_main() -> ! {
println!("Kernel Start...");
nova::initialize_kernel(); nova::initialize_kernel();
info!("Kernel Initialized...");
info!("Current exception Level: {}", get_current_el());
let mut test_vector = Vec::new(); let mut test_vector = Vec::new();
for i in 0..20 { for i in 0..20 {
test_vector.push(i); test_vector.push(i);
} }
debug!("heap allocation test: {:?}", test_vector); println!("heap allocation test: {:?}", test_vector);
enable_irq_source(IRQSource::UartInt);
let app = Application::new(el0 as *const () as usize); println!("Exception Level: {}", get_current_el());
add_app(app).unwrap();
kernel_loop();
}
#[no_mangle]
pub extern "C" fn kernel_loop() {
daif::unmask_all(); daif::unmask_all();
unsafe {
el1_to_el0();
};
#[allow(clippy::empty_loop)] #[allow(clippy::empty_loop)]
loop {} loop {}
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn el0(argc: usize, argv: *const *const u8) { pub extern "C" fn el0() -> ! {
println!("Jumped into EL0"); println!("Jumped into EL0");
println!("num: {}", argc);
println!("argv: {:?}", argv);
let raw_args = unsafe { slice::from_raw_parts(argv, argc) };
let first_arg = raw_args
.iter()
.map(|&arg_ptr| {
if arg_ptr.is_null() {
return "";
}
let c_str = unsafe { core::ffi::CStr::from_ptr(arg_ptr) };
let str_slice = c_str.to_str().unwrap();
str_slice
})
.next();
sleep_s(1);
// Set GPIO 26 to Input // Set GPIO 26 to Input
enable_irq_source(IRQSource::GpioInt0); //26 is on the first GPIO bank enable_irq_source(IRQSource::GpioInt0); //26 is on the first GPIO bank
@@ -143,6 +121,8 @@ pub unsafe extern "C" fn el0(argc: usize, argv: *const *const u8) {
gpio_pull_up(26); gpio_pull_up(26);
set_falling_edge_detect(26, true); set_falling_edge_detect(26, true);
enable_irq_source(IRQSource::UartInt);
if let Some(fb) = unsafe { FRAMEBUFFER.as_mut() } { if let Some(fb) = unsafe { FRAMEBUFFER.as_mut() } {
for i in 0..1080 { for i in 0..1080 {
fb.draw_pixel(50, i, BLUE); fb.draw_pixel(50, i, BLUE);
@@ -156,32 +136,15 @@ pub unsafe extern "C" fn el0(argc: usize, argv: *const *const u8) {
fb.draw_function(cos, 0, 101, RED); fb.draw_function(cos, 0, 101, RED);
} }
let _temp = syscall(67); loop {
// TODO: Mailbox requires a physical address. The stack is now in VA space causing an issue.
// Fix with SVCs ?
if let Some(num) = first_arg.and_then(|val| val.parse::<usize>().ok()) { // let temp = mailbox::read_soc_temp([0]).unwrap();
println!("Calculting prime to: {}", num); // println!("{} °C", temp[1] / 1000);
for i in 3..num {
let mut is_prime = true;
for j in 3..i {
if i == j {
continue;
}
if i % j == 0 {
is_prime = false;
}
}
if is_prime {
print!("{} ", i);
}
}
println!("");
} else {
println!("Input NaN");
}
blink_gpio(SpecificGpio::OnboardLed as u8, 500); blink_gpio(SpecificGpio::OnboardLed as u8, 500);
syscall(0); }
} }
fn cos(x: u32) -> f64 { fn cos(x: u32) -> f64 {
@@ -194,17 +157,3 @@ fn enable_uart() {
let _ = set_gpio_function(15, GPIOFunction::Alternative0); let _ = set_gpio_function(15, GPIOFunction::Alternative0);
uart_init(); uart_init();
} }
pub fn syscall(nr: u64) -> u64 {
let ret: u64;
unsafe {
asm!(
"svc #0",
in("x8") nr,
lateout("x0") ret,
);
}
ret
}
+5 -11
View File
@@ -1,9 +1,4 @@
use core::slice; use crate::{read_address, write_address};
use crate::{
aarch64::mmu::GRANULARITY, configuration::memory_mapping::MAILBOX_PHYSICAL_ADDRESS,
configuration::memory_mapping::MAILBOX_VIRTUAL_ADDRESS, read_address, write_address,
};
use nova_error::NovaError; use nova_error::NovaError;
const MBOX_BASE: u32 = 0x3F00_0000 + 0xB880; const MBOX_BASE: u32 = 0x3F00_0000 + 0xB880;
@@ -36,9 +31,8 @@ macro_rules! mailbox_command {
pub fn $name( pub fn $name(
request_data: [u32; $request_len / 4], request_data: [u32; $request_len / 4],
) -> Result<[u32; $response_len / 4], NovaError> { ) -> Result<[u32; $response_len / 4], NovaError> {
let mailbox = unsafe { let mut mailbox =
slice::from_raw_parts_mut(MAILBOX_VIRTUAL_ADDRESS as *mut u32, GRANULARITY / 4) [0u32; (HEADER_LENGTH + max!($request_len, $response_len) + FOOTER_LENGTH) / 4];
};
mailbox[0] = (HEADER_LENGTH + max!($request_len, $response_len) + FOOTER_LENGTH) as u32; // Total length in Bytes mailbox[0] = (HEADER_LENGTH + max!($request_len, $response_len) + FOOTER_LENGTH) as u32; // Total length in Bytes
mailbox[1] = 0; // Request mailbox[1] = 0; // Request
mailbox[2] = $tag; // Command Tag mailbox[2] = $tag; // Command Tag
@@ -48,9 +42,9 @@ macro_rules! mailbox_command {
mailbox[5..(5 + ($request_len / 4))].copy_from_slice(&request_data); mailbox[5..(5 + ($request_len / 4))].copy_from_slice(&request_data);
mailbox[(5 + ($request_len / 4))..].fill(0); mailbox[(5 + ($request_len / 4))..].fill(0);
//let addr = core::ptr::addr_of!(mailbox[0]) as u32; let addr = core::ptr::addr_of!(mailbox[0]) as u32;
write_mailbox(8, unsafe { MAILBOX_PHYSICAL_ADDRESS.unwrap() } as u32); write_mailbox(8, addr);
let _ = read_mailbox(8); let _ = read_mailbox(8);
+53
View File
@@ -0,0 +1,53 @@
use core::fmt::Write;
use alloc::string::String;
use nova::{
interrupt_handlers::register_interrupt_handler, logger::Logger,
peripherals::uart::read_uart_data, print, println,
};
pub struct Terminal {
buffer: String,
input: String,
}
impl Terminal {
pub fn new() -> Self {
Self {
buffer: String::new(),
input: String::new(),
}
}
fn flush(&mut self) {
println!("{}", self.buffer);
print!("> {}", self.input);
self.buffer.clear();
}
}
impl Write for Terminal {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.buffer.push_str(s);
Ok(())
}
}
impl Logger for Terminal {
fn flush(&mut self) {
println!("{}", self.buffer);
print!("> {}", self.input);
self.buffer.clear();
}
}
fn terminal_uart_rx_interrupt_handler() {
print!("{}", read_uart_data());
}
pub fn register_terminal_interrupt_handler() {
register_interrupt_handler(
nova::interrupt_handlers::IRQSource::UartInt,
terminal_uart_rx_interrupt_handler,
);
}
+8 -112
View File
@@ -1,133 +1,29 @@
.section .vector_t , "ax" .section .vector_table , "ax"
.extern irq_handler .extern irq_handler
.macro ventry label .macro ventry label
.align 7 .align 11
b \label b \label
.endm .endm
.global vector_table .global vector_table
vector_table: vector_table:
// Exceptions from current EL using SP_EL0
ventry . ventry .
ventry . ventry .
ventry . ventry .
ventry . ventry .
// Exceptions from the current EL using SP_ELx
ventry synchronous_interrupt_no_el_change // Synchronous Exception 0x200 ventry synchronous_interrupt_no_el_change // Synchronous Exception 0x200
ventry irq_handler // IRQ(Interrupt Request) 0x280 ventry irq_handler // IRQ(Interrupt Request) 0x280
ventry . // FIQ(Fast Interrupt Request) 0x300 ventry .
ventry . // SError 0x580 ventry .
// Exceptions from lower EL AArch64 ventry synchronous_interrupt_imm_lower_aarch64
ventry synchronous_interrupt_imm_lower_aarch64 // Synchronous Exception 0x400 ventry irq_handler
ventry irq_handler // IRQ(Interrupt Request) 0x480 ventry .
ventry . // FIQ(Fast Interrupt Request) 0x500 ventry .
ventry . // SError 0x580
// Exceptions from lower EL AArch32
ventry . ventry .
ventry . ventry .
ventry . ventry .
ventry . ventry .
.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]
mov x0, sp
bl rust_synchronous_interrupt_imm_lower_aarch64
str x0, [sp, #0]
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]
mov x0, sp
bl rust_synchronous_interrupt_no_el_change
str x0, [sp, #0]
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
+1 -1
View File
@@ -11,4 +11,4 @@ qemu-system-aarch64 \
-cpu cortex-a53 \ -cpu cortex-a53 \
-serial stdio \ -serial stdio \
-sd ../sd.img \ -sd ../sd.img \
-kernel ../target/aarch64-unknown-none/debug/kernel8.img -S -s -kernel ../target/aarch64-unknown-none/debug/kernel8.img \
-4
View File
@@ -39,10 +39,6 @@ impl Heap {
} }
} }
pub fn size(self) -> usize {
self.raw_size
}
pub fn init(&mut self, heap_start: usize, heap_end: usize) { pub fn init(&mut self, heap_start: usize, heap_end: usize) {
self.start_address = heap_start as *mut HeapHeader; self.start_address = heap_start as *mut HeapHeader;
self.end_address = heap_end as *mut HeapHeader; self.end_address = heap_end as *mut HeapHeader;
+3 -3
View File
@@ -29,7 +29,7 @@ fn test_heap_allocation() {
assert_eq!(actual_alloc_size % MIN_BLOCK_SIZE, 0); assert_eq!(actual_alloc_size % MIN_BLOCK_SIZE, 0);
// Verify section is occupied // Verify section is occupied
assert!(!(*malloc_header).free); assert!((*malloc_header).free == false);
// Verify next header has been created // Verify next header has been created
let next = (*malloc_header).next.unwrap(); let next = (*malloc_header).next.unwrap();
@@ -55,7 +55,7 @@ fn test_full_heap() {
let malloc = heap.malloc(malloc_size).unwrap(); let malloc = heap.malloc(malloc_size).unwrap();
let malloc_header = Heap::get_header_ref_from_data_pointer(malloc); let malloc_header = Heap::get_header_ref_from_data_pointer(malloc);
unsafe { unsafe {
assert!(!(*malloc_header).free); assert_eq!((*malloc_header).free, false);
assert!((*malloc_header).next.is_none()); assert!((*malloc_header).next.is_none());
} }
@@ -79,7 +79,7 @@ fn test_freeing_root() {
let malloc = heap.malloc(malloc_size).unwrap(); let malloc = heap.malloc(malloc_size).unwrap();
let malloc_header = Heap::get_header_ref_from_data_pointer(malloc); let malloc_header = Heap::get_header_ref_from_data_pointer(malloc);
unsafe { unsafe {
assert!(!(*malloc_header).free); assert_eq!((*malloc_header).free, false);
assert!((*malloc_header).size >= malloc_size); assert!((*malloc_header).size >= malloc_size);
assert!((*root_header).next.is_some()); assert!((*root_header).next.is_some());
+1 -3
View File
@@ -5,12 +5,10 @@ use core::prelude::rust_2024::derive;
#[derive(Debug)] #[derive(Debug)]
pub enum NovaError { pub enum NovaError {
General(&'static str),
Mailbox, Mailbox,
HeapFull, HeapFull,
EmptyHeapSegmentNotAllowed, EmptyHeapSegmentNotAllowed,
Misalignment, Misalignment,
InvalidGranularity, InvalidGranularity,
Paging(&'static str), Paging,
OutOfMeomory,
} }