vesper/src/main.rs

210 lines
6.2 KiB
Rust

#![no_std]
#![no_main]
#![feature(asm)]
#![feature(global_asm)]
#![feature(const_fn)]
#![feature(format_args_nl)]
#![feature(lang_items)]
#![feature(ptr_internals)] // until we mark with PhantomData instead?
#![feature(core_intrinsics)]
#![feature(range_contains)]
#![feature(try_from)]
#![doc(html_root_url = "https://docs.metta.systems/")]
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_must_use)]
//any(target_arch = "aarch64", target_arch = "x86_64")
#[cfg(not(target_arch = "aarch64"))]
use architecture_not_supported_sorry;
extern crate bitflags;
#[macro_use]
extern crate register;
extern crate cortex_a;
extern crate rlibc;
#[macro_use]
pub mod arch;
pub use arch::*;
mod devices;
mod macros;
pub mod platform;
mod sync;
mod write_to;
use core::fmt::Write;
#[cfg(feature = "jlink")]
use jlink_rtt::Output;
use platform::{
display::{Color, Size2d},
gpio::GPIO,
// uart::MiniUart,
vc::VC,
};
// User-facing kernel parts - syscalls and capability invocations.
// pub mod vesper; -- no mod exported, because available through syscall interface
// Actual interfaces to call these syscalls are in vesper-user (similar to libsel4)
// pub mod vesper; -- exported from vesper-user
/// The global console. Output of the print! and println! macros.
static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
// Kernel entry point
// arch crate is responsible for calling this
fn kmain() -> ! {
// let gpio = GPIO::new_default();
// let uart = platform::MiniUart::new_default();
// uart.init(&gpio);
// CONSOLE.lock(|c| {
// // Moves uart into the global CONSOLE. It is not accessible
// // anymore for the remaining parts of kernel_entry().
// c.replace_with(uart.into());
// });
// let uart = platform::PL011Uart::new_default();
// let mut mbox = platform::mailbox::Mailbox::new();
// match uart.init(&mut mbox, &gpio) {
// Ok(_) => {
// CONSOLE.lock(|c| {
// // Moves uart into the global CONSOLE. It is not accessible
// // anymore for the remaining parts of kernel_entry().
// c.replace_with(uart.into());
// });
// }
// Err(_) => endless_sleep(),
// }
// jtag_dbg_wait();
let mut out = Output::new();
writeln!(out, "JLink RTT is working!"); // @todo RttConsole
println!("\n[0] UART is live!");
extern "C" {
static __exception_vectors_start: u64;
}
//==============================================
// Since formatted output doesn't work, lets do some other preparatory steps:
// 1. Initialize MMU
// 2. Set up exception handlers
// Obviously, things should keep working after that...
//==============================================
unsafe {
let exception_vectors_start: u64 = &__exception_vectors_start as *const _ as u64;
arch::traps::set_vbar_el1_checked(exception_vectors_start);
println!("Exception traps set up");
}
// unsafe {
// mmu::init();
// }
// println!("MMU initialised");
// jtag_dbg_wait();
if let Some(mut display) = VC::init_fb(Size2d { x: 800, y: 600 }, 32) {
println!("Display created");
display.clear(Color::black()); // Takes A LOONG time, check caching opts?
println!("Display cleared");
display.rect(10, 10, 250, 250, Color::rgb(32, 96, 64));
display.draw_text(50, 50, "Hello there!", Color::rgb(128, 192, 255));
// let mut buf = [0u8; 64];
// Crashes if uncommenting next line: vvv
// let s = write_to::show(&mut buf, format_args!("Display width {}", display.width));
// So, some rust runtime things are breaking it, why?
// if s.is_err() {
// display.draw_text(50, 150, "Error displaying", Color::red())
// } else {
// display.draw_text(50, 150, s.unwrap(), Color::white());
// }
display.draw_text(150, 50, "RED", Color::red());
display.draw_text(160, 60, "GREEN", Color::green());
display.draw_text(170, 70, "BLUE", Color::blue());
}
// jtag_dbg_wait();
//------------------------------------------------------------
// Start a command prompt
//------------------------------------------------------------
'cmd_loop: loop {
let mut buf = [0u8; 64];
match CONSOLE.lock(|c| c.command_prompt(&mut buf)) {
b"trap" => check_data_abort_trap(),
b"map" => arch::memory::print_layout(),
b"help" => print_help(),
b"end" => break 'cmd_loop,
x => println!("Unknown command {:?}, try 'help'", x),
}
}
println!("Bye, going to sleep now");
// qemu_aarch64_exit()
endless_sleep()
}
fn print_help() {
println!("Supported console commands:");
println!(" trap - cause and recover from a data abort exception");
println!(" map - show kernel memory layout");
println!(" end - leave console and lock up");
}
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.");
}
entry!(kmain);
// From https://stackoverflow.com/a/49930361/895245
// @todo specify exit value depending on tests result?
// fn qemu_aarch64_exit() -> ! {
// unsafe {
// asm!("
// /* 0x20026 == ADP_Stopped_ApplicationExit */
// mov x1, #0x26
// movk x1, #2, lsl #16
// str x1, [sp,#0]
// /* Exit status code. Host QEMU process exits with that status. */
// mov x0, #0
// str x0, [sp,#8]
// /* x1 contains the address of parameter block.
// * Any memory address could be used. */
// mov x1, sp
// /* SYS_EXIT */
// mov w0, #0x18
// /* Do the semihosting call on A64. */
// hlt 0xf000"
// :::: "volatile");
// }
// unreachable!();
// }