# Tutorial 03 - Hacky Hello World ## tl;dr Introducing global `print!()` macros to enable "printf debugging" at the earliest; To keep tutorial length reasonable, printing functions for now "abuse" a QEMU property that lets us use the RPi's `UART` without setting it up properly; Using the real hardware `UART` is enabled step-by-step in following tutorials. - `interface.rs` is introduced: - Provides `Traits` for abstracting `kernel` from `BSP` and `arch` code. - Panic handler `print!()`s supplied error messages. - This is showcased in `main()`. ### Give it a try QEMU is no longer run in assembly mode. It will from now on show the output of `UART0`. ```console make qemu [...] Hello from Rust! Kernel panic: Stopping here. ``` ## Diff to previous ```diff diff -uNr 02_runtime_init/Makefile 03_hacky_hello_world/Makefile --- 02_runtime_init/Makefile +++ 03_hacky_hello_world/Makefile @@ -13,7 +13,7 @@ OUTPUT = kernel8.img QEMU_BINARY = qemu-system-aarch64 QEMU_MACHINE_TYPE = raspi3 - QEMU_MISC_ARGS = -d in_asm + QEMU_MISC_ARGS = -serial stdio LINKER_FILE = src/bsp/rpi/link.ld RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 else ifeq ($(BSP),rpi4) @@ -21,7 +21,7 @@ OUTPUT = kernel8.img # QEMU_BINARY = qemu-system-aarch64 # QEMU_MACHINE_TYPE = -# QEMU_MISC_ARGS = -d in_asm +# QEMU_MISC_ARGS = -serial stdio LINKER_FILE = src/bsp/rpi/link.ld RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 endif diff -uNr 02_runtime_init/src/bsp/rpi.rs 03_hacky_hello_world/src/bsp/rpi.rs --- 02_runtime_init/src/bsp/rpi.rs +++ 03_hacky_hello_world/src/bsp/rpi.rs @@ -4,4 +4,35 @@ //! Board Support Package for the Raspberry Pi. -// Coming soon. +use crate::interface; +use core::fmt; + +/// A mystical, magical device for generating QEMU output out of the void. +struct QEMUOutput; + +/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used +/// to implement the `kernel`'s `print!` and `println!` macros. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl interface::console::Write for QEMUOutput { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + unsafe { + core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); + } + } + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Returns a ready-to-use `console::Write` implementation. +pub fn console() -> impl interface::console::Write { + QEMUOutput {} +} diff -uNr 02_runtime_init/src/interface.rs 03_hacky_hello_world/src/interface.rs --- 02_runtime_init/src/interface.rs +++ 03_hacky_hello_world/src/interface.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + /// Console write functions. + /// + /// `core::fmt::Write` is exactly what we need for now. Re-export it here because + /// implementing `console::Write` gives a better hint to the reader about the + /// intention. + pub use core::fmt::Write; + + /// Console read functions. + pub trait Read { + fn read_char(&mut self) -> char { + ' ' + } + } +} diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs --- 02_runtime_init/src/main.rs +++ 03_hacky_hello_world/src/main.rs @@ -6,9 +6,23 @@ #![doc(html_logo_url = "https://git.io/JeGIp")] //! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html #![feature(asm)] +#![feature(format_args_nl)] #![feature(global_asm)] +#![feature(panic_info_message)] #![no_main] #![no_std] @@ -22,7 +36,9 @@ // Conditionally includes the selected `BSP` code. mod bsp; +mod interface; mod panic_wait; +mod print; /// Early init code. /// @@ -30,5 +46,7 @@ /// /// - Only a single core must be active and running this function. unsafe fn kernel_init() -> ! { - panic!() + println!("Hello from Rust!"); + + panic!("Stopping here.") } diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.rs --- 02_runtime_init/src/panic_wait.rs +++ 03_hacky_hello_world/src/panic_wait.rs @@ -4,9 +4,16 @@ //! A panic handler that infinitely waits. +use crate::{arch, println}; use core::panic::PanicInfo; #[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - crate::arch::wait_forever() +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() } diff -uNr 02_runtime_init/src/print.rs 03_hacky_hello_world/src/print.rs --- 02_runtime_init/src/print.rs +++ 03_hacky_hello_world/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} ```