vesper/nucleus/src/main.rs

144 lines
3.8 KiB
Rust

/*
* SPDX-License-Identifier: BlueOak-1.0.0
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
//! Vesper single-address-space exokernel.
//!
//! This crate implements the kernel binary proper.
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(global_asm)]
#![feature(decl_macro)]
#![feature(allocator_api)]
#![feature(ptr_internals)]
#![feature(format_args_nl)]
#![feature(nonnull_slice_from_raw_parts)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::tests::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![deny(missing_docs)]
#![deny(warnings)]
#[cfg(not(target_arch = "aarch64"))]
use architecture_not_supported_sorry;
extern crate rlibc; // To enable linking memory intrinsics.
/// Architecture-specific code.
#[macro_use]
pub mod arch;
pub use arch::*;
mod devices;
mod macros;
mod mm;
mod panic;
mod platform;
#[cfg(feature = "qemu")]
mod qemu;
mod sync;
#[cfg(test)]
mod tests;
mod write_to;
entry!(kmain);
/// The global console. Output of the kernel print! and println! macros goes here.
static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
/// The global allocator for DMA-able memory. That is, memory which is tagged
/// non-cacheable in the page tables.
static DMA_ALLOCATOR: sync::NullLock<mm::BumpAllocator> =
sync::NullLock::new(mm::BumpAllocator::new(
// @todo Init this after we loaded boot memory map
memory::map::virt::DMA_HEAP_START as usize,
memory::map::virt::DMA_HEAP_END as usize,
"Global DMA Allocator",
// Try the following arguments instead to see all mailbox operations
// fail. It will cause the allocator to use memory that are marked
// cacheable and therefore not DMA-safe. The answer from the VideoCore
// won't be received by the CPU because it reads an old cached value
// that resembles an error case instead.
// 0x00600000 as usize,
// 0x007FFFFF as usize,
// "Global Non-DMA Allocator",
));
fn print_mmu_state_and_features() {
memory::mmu::print_features();
}
fn init_mmu() {
print_mmu_state_and_features();
unsafe {
memory::mmu::init().unwrap();
}
println!("MMU initialised");
}
fn init_exception_traps() {
extern "C" {
static __exception_vectors_start: u64;
}
unsafe {
let exception_vectors_start: u64 = &__exception_vectors_start as *const _ as u64;
arch::traps::set_vbar_el1_checked(exception_vectors_start)
.expect("Vector table properly aligned!");
}
println!("Exception traps set up");
}
#[cfg(not(feature = "noserial"))]
fn init_uart_serial() {
use crate::platform::rpi3::{gpio::GPIO, mini_uart::MiniUart};
let gpio = GPIO::default();
let uart = MiniUart::default();
let uart = uart.prepare(&gpio);
CONSOLE.lock(|c| {
// Move uart into the global CONSOLE.
c.replace_with(uart.into());
});
println!("[0] MiniUART is live!");
}
/// Kernel entry point.
/// `arch` crate is responsible for calling it.
#[inline]
pub fn kmain() -> ! {
#[cfg(not(feature = "noserial"))]
init_uart_serial();
init_exception_traps();
init_mmu();
#[cfg(test)]
test_main();
println!("Bye, hanging forever...");
endless_sleep()
}
#[cfg(test)]
mod main_tests {
use super::*;
#[test_case]
fn check_data_abort_trap() {
// Cause an exception by accessing a virtual address for which no
// address translations have been set up.
//
// This line of code accesses the address 3 GiB, but page tables are
// only set up for the range [0..1) GiB.
let big_addr: u64 = 3 * 1024 * 1024 * 1024;
unsafe { core::ptr::read_volatile(big_addr as *mut u64) };
println!("[i] Whoa! We recovered from an exception.");
}
}