From d5cd571ce56cec684efc0016f555b0c0d92d71e6 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Fri, 1 Nov 2019 23:03:29 +0100 Subject: [PATCH] Add code for tutorial 10 --- 10_privilege_level/.vscode/settings.json | 10 + 10_privilege_level/Cargo.lock | 42 ++ 10_privilege_level/Cargo.toml | 21 + 10_privilege_level/Makefile | 127 ++++++ 10_privilege_level/README.md | 406 ++++++++++++++++++ 10_privilege_level/kernel | Bin 0 -> 94720 bytes 10_privilege_level/kernel8.img | Bin 0 -> 16480 bytes 10_privilege_level/src/arch.rs | 11 + 10_privilege_level/src/arch/aarch64.rs | 133 ++++++ .../src/arch/aarch64/exception.rs | 44 ++ 10_privilege_level/src/arch/aarch64/sync.rs | 44 ++ 10_privilege_level/src/arch/aarch64/time.rs | 77 ++++ 10_privilege_level/src/bsp.rs | 13 + 10_privilege_level/src/bsp/driver.rs | 11 + 10_privilege_level/src/bsp/driver/bcm.rs | 11 + .../src/bsp/driver/bcm/bcm2xxx_gpio.rs | 157 +++++++ .../src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs | 332 ++++++++++++++ 10_privilege_level/src/bsp/rpi.rs | 58 +++ 10_privilege_level/src/bsp/rpi/link.ld | 35 ++ 10_privilege_level/src/bsp/rpi/memory_map.rs | 18 + 10_privilege_level/src/interface.rs | 129 ++++++ 10_privilege_level/src/main.rs | 97 +++++ 10_privilege_level/src/panic_wait.rs | 19 + 10_privilege_level/src/print.rs | 91 ++++ 10_privilege_level/src/runtime_init.rs | 24 ++ 25 files changed, 1910 insertions(+) create mode 100644 10_privilege_level/.vscode/settings.json create mode 100644 10_privilege_level/Cargo.lock create mode 100644 10_privilege_level/Cargo.toml create mode 100644 10_privilege_level/Makefile create mode 100644 10_privilege_level/README.md create mode 100755 10_privilege_level/kernel create mode 100755 10_privilege_level/kernel8.img create mode 100644 10_privilege_level/src/arch.rs create mode 100644 10_privilege_level/src/arch/aarch64.rs create mode 100644 10_privilege_level/src/arch/aarch64/exception.rs create mode 100644 10_privilege_level/src/arch/aarch64/sync.rs create mode 100644 10_privilege_level/src/arch/aarch64/time.rs create mode 100644 10_privilege_level/src/bsp.rs create mode 100644 10_privilege_level/src/bsp/driver.rs create mode 100644 10_privilege_level/src/bsp/driver/bcm.rs create mode 100644 10_privilege_level/src/bsp/driver/bcm/bcm2xxx_gpio.rs create mode 100644 10_privilege_level/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs create mode 100644 10_privilege_level/src/bsp/rpi.rs create mode 100644 10_privilege_level/src/bsp/rpi/link.ld create mode 100644 10_privilege_level/src/bsp/rpi/memory_map.rs create mode 100644 10_privilege_level/src/interface.rs create mode 100644 10_privilege_level/src/main.rs create mode 100644 10_privilege_level/src/panic_wait.rs create mode 100644 10_privilege_level/src/print.rs create mode 100644 10_privilege_level/src/runtime_init.rs diff --git a/10_privilege_level/.vscode/settings.json b/10_privilege_level/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/10_privilege_level/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/10_privilege_level/Cargo.lock b/10_privilege_level/Cargo.lock new file mode 100644 index 00000000..9a17339b --- /dev/null +++ b/10_privilege_level/Cargo.lock @@ -0,0 +1,42 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tock-registers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" +"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/10_privilege_level/Cargo.toml b/10_privilege_level/Cargo.toml new file mode 100644 index 00000000..cf0f0636 --- /dev/null +++ b/10_privilege_level/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/10_privilege_level/Makefile b/10_privilege_level/Makefile new file mode 100644 index 00000000..4cf5e508 --- /dev/null +++ b/10_privilege_level/Makefile @@ -0,0 +1,127 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi3.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi3.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi4.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev +DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag +DOCKER_ARG_NET = --network host + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu chainboot clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +endif + +chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) + +jtagboot: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + /jtag/$(JTAG_BOOT_IMAGE) + +openocd: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + openocd $(OPENOCD_ARG) + +define gen_gdb + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) $1 + cp $(CARGO_OUTPUT) kernel_for_jtag + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + gdb-multiarch -q kernel_for_jtag +endef + +gdb: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2) + +gdb-opt0: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2 -C opt-level=0) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/10_privilege_level/README.md b/10_privilege_level/README.md new file mode 100644 index 00000000..68172fc3 --- /dev/null +++ b/10_privilege_level/README.md @@ -0,0 +1,406 @@ +# Tutorial 10 - Privilege Level + +## tl;dr + +In early boot code, we transition from the `Hypervisor` privilege level (`EL2` in AArch64) to the +`Kernel` (`EL1`) privilege level. + +## Introduction + +Application-grade CPUs have so-called `privilege levels`, which have different purposes: + +| Typically used for | AArch64 | RISC-V | x86 | +| ------------- | ------------- | ------------- | ------------- | +| Userspace applications | EL0 | U/VU | Ring 3 | +| OS Kernel | EL1 | S/VS | Ring 0 | +| Hypervisor | EL2 | HS | Ring -1 | +| Low-Level Firmware | EL3 | M | | + +`EL` in AArch64 stands for `Exception Level`. If you want more information regarding the other +architectures, please have a look at the following links: +- [x86 privilege rings](https://en.wikipedia.org/wiki/Protection_ring). +- [RISC-V privilege modes](https://content.riscv.org/wp-content/uploads/2017/12/Tue0942-riscv-hypervisor-waterman.pdf). + +At this point, I strongly recommend that you glimpse over `Chapter 3` of the [Programmer’s Guide for +ARMv8-A](http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf) +before you continue. It gives a concise overview about the topic. + +## Scope of this tutorial + +If you set up your SD Card exactly like mentioned in [tutorial 06], the Rpi will always start +executing in `EL2`. Since we are writing a traditional `Kernel`, we have to transition into the more +appropriate `EL1`. + +[tutorial 06]: https://github.com/rust-embedded/rust-raspi3-OS-tutorials/tree/master/06_drivers_gpio_uart#boot-it-from-sd-card + +## Checking for EL2 in the entrypoint + +First of all, we need to ensure that we actually execute in `EL2` before we can call respective code +to transition to `EL1`: + +```rust +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + // Expect the boot core to start in EL2. + if (bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK) + && (CurrentEL.get() == CurrentEL::EL::EL2.value) + { + el2_to_el1_transition() + } else { + // If not core0, infinitely wait for events. + wait_forever() + } +} +``` + +If this is the case, we continue with preparing the `EL2` -> `EL1` transition in +`el2_to_el1_transition()`. + +## Transition preparation + +Since `EL2` is more privileged than `EL1`, it has control over various processor features and can +allow or disallow `EL1` code to use them. One such example is access to timer and counter registers. +We are already using them since [tutorial 08](../08_timestamps/), so of course we want to keep them. +Therefore we set the respective flags in the [Counter-timer Hypervisor Control register] and +additionally set the virtual offset to zero so that we get the real physical value everytime: + +[Counter-timer Hypervisor Control register]: https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/cnthctl_el2.rs.html + +```rust +// Enable timer counter registers for EL1. +CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); + +// No offset for reading the counters. +CNTVOFF_EL2.set(0); +``` + +Next, we configure the [Hypervisor Configuration Register] such that `EL1` should actually run in +`AArch64` mode, and not in `AArch32`, which would also be possible. + +[Hypervisor Configuration Register]: https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/hcr_el2.rs.html + +```rust +// Set EL1 execution state to AArch64. +HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); +``` + +## Returning from an exception that never happened + +There is actually only one way to transition from a higher EL to a lower EL, which is by way of +executing the [ERET] instruction. + +[ERET]: https://docs.rs/cortex-a/2.4.0/src/cortex_a/asm.rs.html#49-62 + +This instruction will copy the contents of the [Saved Program Status Register - EL2] to `Current +Program Status Register - EL1` and jump to the instruction address that is stored in the [Exception +Link Register - EL2]. + +This is basically the reverse of what is happening when an exception is taken. You'll learn about it +in an upcoming tutorial. + +[Saved Program Status Register - EL2]: https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/spsr_el2.rs.html +[Exception Link Register - EL2]: https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/elr_el2.rs.html + +```rust +// Set up a simulated exception return. +// +// First, fake a saved program status, where all interrupts were masked and SP_EL1 was used as a +// stack pointer. +SPSR_EL2.write( + SPSR_EL2::D::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::M::EL1h, +); + +// Second, let the link register point to init(). +ELR_EL2.set(crate::runtime_init::init as *const () as u64); +``` + +As you can see, we are populating `ELR_EL2` with the address of the [init()] function that we +earlier used to call directly from the entrypoint. + +Finally, we set the stack pointer for `SP_EL1` and call `ERET`: + +[init()]: src/runtime_init.rs + +```rust +// Set up SP_EL1 (stack pointer), which will be used by EL1 once we "return" to it. +SP_EL1.set(bsp::BOOT_CORE_STACK_START); + +// Use `eret` to "return" to EL1. This will result in execution of `reset()` in EL1. +asm::eret() +``` + +## Are we stackless? + +We just wrote a big inline rust function, `el2_to_el1_transition()`, that is executed in a context +where we do not have a stack yet. We should double-check the generated machine code: + +```console +make objdump +[...] +Disassembly of section .text: + +0000000000080000 _start: + 80000: a8 00 38 d5 mrs x8, MPIDR_EL1 + 80004: 1f 05 40 f2 tst x8, #0x3 + 80008: 81 00 00 54 b.ne #0x10 <_start+0x18> + 8000c: 48 42 38 d5 mrs x8, CurrentEL + 80010: 1f 21 00 71 cmp w8, #0x8 + 80014: 60 00 00 54 b.eq #0xc <_start+0x20> + 80018: 5f 20 03 d5 wfe + 8001c: ff ff ff 17 b #-0x4 <_start+0x18> + 80020: e8 03 1f aa mov x8, xzr + 80024: 69 00 80 52 mov w9, #0x3 + 80028: 09 e1 1c d5 msr CNTHCTL_EL2, x9 + 8002c: 68 e0 1c d5 msr CNTVOFF_EL2, x8 + 80030: 08 00 00 90 adrp x8, #0x0 + 80034: 0a 00 b0 52 mov w10, #-0x80000000 + 80038: ab 78 80 52 mov w11, #0x3c5 + 8003c: 0c 01 a0 52 mov w12, #0x80000 + 80040: 0a 11 1c d5 msr HCR_EL2, x10 + 80044: 0b 40 1c d5 msr SPSR_EL2, x11 + 80048: 08 91 2f 91 add x8, x8, #0xbe4 + 8004c: 28 40 1c d5 msr ELR_EL2, x8 + 80050: 0c 41 1c d5 msr SP_EL1, x12 + 80054: e0 03 9f d6 eret +``` + +Looks good! Thanks zero-overhead abstractions in the +[cortex-a](https://github.com/rust-embedded/cortex-a) crate! :heart_eyes: + +## Testing + +In `main.rs`, we additionally inspect if the mask bits in `SPSR_EL2` made it to `EL1` as well: + +```console +make chainbot +[...] +### Listening on /dev/ttyUSB0 + __ __ _ _ _ _ +| \/ (_)_ _ (_) | ___ __ _ __| | +| |\/| | | ' \| | |__/ _ \/ _` / _` | +|_| |_|_|_||_|_|____\___/\__,_\__,_| + + Raspberry Pi 3 + +[ML] Requesting binary +### sending kernel kernel8.img [16480 byte] +### finished sending +[ML] Loaded! Executing the payload now + +[ 1.459973] Booting on: Raspberry Pi 3 +[ 1.462256] Current privilege level: EL1 +[ 1.466163] Exception handling state: +[ 1.469810] Debug: Masked +[ 1.473023] SError: Masked +[ 1.476235] IRQ: Masked +[ 1.479447] FIQ: Masked +[ 1.482661] Architectural timer resolution: 52 ns +[ 1.487349] Drivers loaded: +[ 1.490127] 1. GPIO +[ 1.492731] 2. PL011Uart +[ 1.495770] Timer test, spinning for 1 second +[ 2.500114] Echoing input now +``` + +## Diff to previous +```diff + +diff -uNr 09_hw_debug_JTAG/src/arch/aarch64/exception.rs 10_privilege_level/src/arch/aarch64/exception.rs +--- 09_hw_debug_JTAG/src/arch/aarch64/exception.rs ++++ 10_privilege_level/src/arch/aarch64/exception.rs +@@ -0,0 +1,44 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Exception handling. ++ ++use cortex_a::regs::*; ++ ++pub trait DaifField { ++ fn daif_field() -> register::Field; ++} ++ ++pub struct Debug; ++pub struct SError; ++pub struct IRQ; ++pub struct FIQ; ++ ++impl DaifField for Debug { ++ fn daif_field() -> register::Field { ++ DAIF::D ++ } ++} ++ ++impl DaifField for SError { ++ fn daif_field() -> register::Field { ++ DAIF::A ++ } ++} ++ ++impl DaifField for IRQ { ++ fn daif_field() -> register::Field { ++ DAIF::I ++ } ++} ++ ++impl DaifField for FIQ { ++ fn daif_field() -> register::Field { ++ DAIF::F ++ } ++} ++ ++pub fn is_masked() -> bool { ++ DAIF.is_set(T::daif_field()) ++} + +diff -uNr 09_hw_debug_JTAG/src/arch/aarch64.rs 10_privilege_level/src/arch/aarch64.rs +--- 09_hw_debug_JTAG/src/arch/aarch64.rs ++++ 10_privilege_level/src/arch/aarch64.rs +@@ -4,6 +4,7 @@ + + //! AArch64. + ++mod exception; + pub mod sync; + mod time; + +@@ -21,15 +22,51 @@ + pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + +- if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { +- SP.set(bsp::BOOT_CORE_STACK_START); +- crate::runtime_init::init() ++ // Expect the boot core to start in EL2. ++ if (bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK) ++ && (CurrentEL.get() == CurrentEL::EL::EL2.value) ++ { ++ el2_to_el1_transition() + } else { + // If not core0, infinitely wait for events. + wait_forever() + } + } + ++/// Transition from EL2 to EL1. ++#[inline(always)] ++fn el2_to_el1_transition() -> ! { ++ // Enable timer counter registers for EL1. ++ CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); ++ ++ // No offset for reading the counters. ++ CNTVOFF_EL2.set(0); ++ ++ // Set EL1 execution state to AArch64. ++ HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); ++ ++ // Set up a simulated exception return. ++ // ++ // First, fake a saved program status, where all interrupts were masked and SP_EL1 was used as a ++ // stack pointer. ++ SPSR_EL2.write( ++ SPSR_EL2::D::Masked ++ + SPSR_EL2::A::Masked ++ + SPSR_EL2::I::Masked ++ + SPSR_EL2::F::Masked ++ + SPSR_EL2::M::EL1h, ++ ); ++ ++ // Second, let the link register point to init(). ++ ELR_EL2.set(crate::runtime_init::init as *const () as u64); ++ ++ // Set up SP_EL1 (stack pointer), which will be used by EL1 once we "return" to it. ++ SP_EL1.set(bsp::BOOT_CORE_STACK_START); ++ ++ // Use `eret` to "return" to EL1. This will result in execution of `reset()` in EL1. ++ asm::eret() ++} ++ + //-------------------------------------------------------------------------------------------------- + // Global instances + //-------------------------------------------------------------------------------------------------- +@@ -61,3 +98,36 @@ + asm::wfe() + } + } ++ ++/// Information about the HW state. ++pub mod state { ++ use cortex_a::regs::*; ++ ++ /// The current privilege level. ++ pub fn current_privilege_level() -> &'static str { ++ let el = CurrentEL.read_as_enum(CurrentEL::EL); ++ match el { ++ Some(CurrentEL::EL::Value::EL2) => "EL2", ++ Some(CurrentEL::EL::Value::EL1) => "EL1", ++ _ => "Unknown", ++ } ++ } ++ ++ #[rustfmt::skip] ++ pub fn print_exception_state() { ++ use super::{ ++ exception, ++ exception::{Debug, SError, FIQ, IRQ}, ++ }; ++ use crate::println; ++ ++ let to_mask_str = |x: bool| -> &'static str { ++ if x { "Masked" } else { "Unmasked" } ++ }; ++ ++ println!(" Debug: {}", to_mask_str(exception::is_masked::())); ++ println!(" SError: {}", to_mask_str(exception::is_masked::())); ++ println!(" IRQ: {}", to_mask_str(exception::is_masked::())); ++ println!(" FIQ: {}", to_mask_str(exception::is_masked::())); ++ } ++} + +diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs +--- 09_hw_debug_JTAG/src/main.rs ++++ 10_privilege_level/src/main.rs +@@ -65,9 +65,17 @@ + /// The main function running after the early init. + fn kernel_main() -> ! { + use core::time::Duration; +- use interface::time::Timer; ++ use interface::{console::All, time::Timer}; + + println!("Booting on: {}", bsp::board_name()); ++ ++ println!( ++ "Current privilege level: {}", ++ arch::state::current_privilege_level() ++ ); ++ println!("Exception handling state:"); ++ arch::state::print_exception_state(); ++ + println!( + "Architectural timer resolution: {} ns", + arch::timer().resolution().as_nanos() +@@ -78,11 +86,12 @@ + println!(" {}. {}", i + 1, driver.compatible()); + } + +- // Test a failing timer case. +- arch::timer().spin_for(Duration::from_nanos(1)); ++ println!("Timer test, spinning for 1 second"); ++ arch::timer().spin_for(Duration::from_secs(1)); + ++ println!("Echoing input now"); + loop { +- println!("Spinning for 1 second"); +- arch::timer().spin_for(Duration::from_secs(1)); ++ let c = bsp::console().read_char(); ++ bsp::console().write_char(c); + } + } + +``` diff --git a/10_privilege_level/kernel b/10_privilege_level/kernel new file mode 100755 index 0000000000000000000000000000000000000000..eb7c2e42253f02c75eb457706029204d15c03d47 GIT binary patch literal 94720 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 literal 0 HcmV?d00001 diff --git a/10_privilege_level/kernel8.img b/10_privilege_level/kernel8.img new file mode 100755 index 0000000000000000000000000000000000000000..0268a52c000f8b846d083c2e6f635199c6f46f62 GIT binary patch literal 16480 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 + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/10_privilege_level/src/arch/aarch64.rs b/10_privilege_level/src/arch/aarch64.rs new file mode 100644 index 00000000..7905a3ff --- /dev/null +++ b/10_privilege_level/src/arch/aarch64.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +mod exception; +pub mod sync; +mod time; + +use crate::{bsp, interface}; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + // Expect the boot core to start in EL2. + if (bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK) + && (CurrentEL.get() == CurrentEL::EL::EL2.value) + { + el2_to_el1_transition() + } else { + // If not core0, infinitely wait for events. + wait_forever() + } +} + +/// Transition from EL2 to EL1. +#[inline(always)] +fn el2_to_el1_transition() -> ! { + // Enable timer counter registers for EL1. + CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); + + // No offset for reading the counters. + CNTVOFF_EL2.set(0); + + // Set EL1 execution state to AArch64. + HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); + + // Set up a simulated exception return. + // + // First, fake a saved program status, where all interrupts were masked and SP_EL1 was used as a + // stack pointer. + SPSR_EL2.write( + SPSR_EL2::D::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::M::EL1h, + ); + + // Second, let the link register point to init(). + ELR_EL2.set(crate::runtime_init::init as *const () as u64); + + // Set up SP_EL1 (stack pointer), which will be used by EL1 once we "return" to it. + SP_EL1.set(bsp::BOOT_CORE_STACK_START); + + // Use `eret` to "return" to EL1. This will result in execution of `reset()` in EL1. + asm::eret() +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static TIMER: time::Timer = time::Timer; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Return a reference to a `interface::time::TimeKeeper` implementation. +pub fn timer() -> &'static impl interface::time::Timer { + &TIMER +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} + +/// Information about the HW state. +pub mod state { + use cortex_a::regs::*; + + /// The current privilege level. + pub fn current_privilege_level() -> &'static str { + let el = CurrentEL.read_as_enum(CurrentEL::EL); + match el { + Some(CurrentEL::EL::Value::EL2) => "EL2", + Some(CurrentEL::EL::Value::EL1) => "EL1", + _ => "Unknown", + } + } + + #[rustfmt::skip] + pub fn print_exception_state() { + use super::{ + exception, + exception::{Debug, SError, FIQ, IRQ}, + }; + use crate::println; + + let to_mask_str = |x: bool| -> &'static str { + if x { "Masked" } else { "Unmasked" } + }; + + println!(" Debug: {}", to_mask_str(exception::is_masked::())); + println!(" SError: {}", to_mask_str(exception::is_masked::())); + println!(" IRQ: {}", to_mask_str(exception::is_masked::())); + println!(" FIQ: {}", to_mask_str(exception::is_masked::())); + } +} diff --git a/10_privilege_level/src/arch/aarch64/exception.rs b/10_privilege_level/src/arch/aarch64/exception.rs new file mode 100644 index 00000000..27939b84 --- /dev/null +++ b/10_privilege_level/src/arch/aarch64/exception.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Exception handling. + +use cortex_a::regs::*; + +pub trait DaifField { + fn daif_field() -> register::Field; +} + +pub struct Debug; +pub struct SError; +pub struct IRQ; +pub struct FIQ; + +impl DaifField for Debug { + fn daif_field() -> register::Field { + DAIF::D + } +} + +impl DaifField for SError { + fn daif_field() -> register::Field { + DAIF::A + } +} + +impl DaifField for IRQ { + fn daif_field() -> register::Field { + DAIF::I + } +} + +impl DaifField for FIQ { + fn daif_field() -> register::Field { + DAIF::F + } +} + +pub fn is_masked() -> bool { + DAIF.is_set(T::daif_field()) +} diff --git a/10_privilege_level/src/arch/aarch64/sync.rs b/10_privilege_level/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/10_privilege_level/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/10_privilege_level/src/arch/aarch64/time.rs b/10_privilege_level/src/arch/aarch64/time.rs new file mode 100644 index 00000000..a6c9d50c --- /dev/null +++ b/10_privilege_level/src/arch/aarch64/time.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Timer primitives. + +use crate::{interface, warn}; +use core::time::Duration; +use cortex_a::regs::*; + +const NS_PER_S: u64 = 1_000_000_000; + +pub struct Timer; + +impl interface::time::Timer for Timer { + fn resolution(&self) -> Duration { + Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + } + + fn uptime(&self) -> Duration { + let frq: u64 = CNTFRQ_EL0.get() as u64; + let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; + + Duration::from_nanos(current_count / frq) + } + + fn spin_for(&self, duration: Duration) { + // Instantly return on zero. + if duration.as_nanos() == 0 { + return; + } + + // Calculate the register compare value. + let frq = CNTFRQ_EL0.get() as u64; + let x = match frq.checked_mul(duration.as_nanos() as u64) { + None => { + warn!("Spin duration too long, skipping"); + return; + } + Some(val) => val, + }; + let tval = x / NS_PER_S; + + // Check if it is within supported bounds. + let warn: Option<&str> = if tval == 0 { + Some("smaller") + } else if tval > u32::max_value().into() { + Some("bigger") + } else { + None + }; + + if let Some(w) = warn { + warn!( + "Spin duration {} than architecturally supported, skipping", + w + ); + return; + } + + // Set the compare value register. + CNTP_TVAL_EL0.set(tval as u32); + + // Kick off the counting. // Disable timer interrupt. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + + loop { + // ISTATUS will be '1' when cval ticks have passed. Busy-check it. + if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { + break; + } + } + + // Disable counting again. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); + } +} diff --git a/10_privilege_level/src/bsp.rs b/10_privilege_level/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/10_privilege_level/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/10_privilege_level/src/bsp/driver.rs b/10_privilege_level/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/10_privilege_level/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/10_privilege_level/src/bsp/driver/bcm.rs b/10_privilege_level/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/10_privilege_level/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_gpio.rs b/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_gpio.rs new file mode 100644 index 00000000..a9ceda61 --- /dev/null +++ b/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::ops; +use register::{mmio::ReadWrite, register_bitfields}; + +// GPIO registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// GPIO Function Select 1 + GPFSEL1 [ + /// Pin 15 + FSEL15 OFFSET(15) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART RX + + ], + + /// Pin 14 + FSEL14 OFFSET(12) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART TX + ] + ], + + /// GPIO Pull-up/down Clock Register 0 + GPPUDCLK0 [ + /// Pin 15 + PUDCLK15 OFFSET(15) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 14 + PUDCLK14 OFFSET(14) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct RegisterBlock { + pub GPFSEL0: ReadWrite, // 0x00 + pub GPFSEL1: ReadWrite, // 0x04 + pub GPFSEL2: ReadWrite, // 0x08 + pub GPFSEL3: ReadWrite, // 0x0C + pub GPFSEL4: ReadWrite, // 0x10 + pub GPFSEL5: ReadWrite, // 0x14 + __reserved_0: u32, // 0x18 + GPSET0: ReadWrite, // 0x1C + GPSET1: ReadWrite, // 0x20 + __reserved_1: u32, // + GPCLR0: ReadWrite, // 0x28 + __reserved_2: [u32; 2], // + GPLEV0: ReadWrite, // 0x34 + GPLEV1: ReadWrite, // 0x38 + __reserved_3: u32, // + GPEDS0: ReadWrite, // 0x40 + GPEDS1: ReadWrite, // 0x44 + __reserved_4: [u32; 7], // + GPHEN0: ReadWrite, // 0x64 + GPHEN1: ReadWrite, // 0x68 + __reserved_5: [u32; 10], // + pub GPPUD: ReadWrite, // 0x94 + pub GPPUDCLK0: ReadWrite, // 0x98 + pub GPPUDCLK1: ReadWrite, // 0x9C +} + +/// The driver's private data. +struct GPIOInner { + base_addr: usize, +} + +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..78303c49 --- /dev/null +++ b/10_privilege_level/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// Interrupt Clear Register + ICR [ + /// Meta field for all pending interrupts + ALL OFFSET(0) NUMBITS(11) [] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct RegisterBlock { + DR: ReadWrite, // 0x00 + __reserved_0: [u32; 5], // 0x04 + FR: ReadOnly, // 0x18 + __reserved_1: [u32; 2], // 0x1c + IBRD: WriteOnly, // 0x24 + FBRD: WriteOnly, // 0x28 + LCRH: WriteOnly, // 0x2C + CR: WriteOnly, // 0x30 + __reserved_2: [u32; 4], // 0x34 + ICR: WriteOnly, // 0x44 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until the TX FIFO empty flag is set. + r.lock(|inner| loop { + if inner.FR.is_set(FR::TXFE) { + break; + } + + arch::nop(); + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + ret + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| loop { + // Read from the RX FIFO until the empty bit is '1'. + if !inner.FR.is_set(FR::RXFE) { + inner.DR.get(); + } else { + break; + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/10_privilege_level/src/bsp/rpi.rs b/10_privilege_level/src/bsp/rpi.rs new file mode 100644 index 00000000..c22c47bb --- /dev/null +++ b/10_privilege_level/src/bsp/rpi.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} diff --git a/10_privilege_level/src/bsp/rpi/link.ld b/10_privilege_level/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/10_privilege_level/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/10_privilege_level/src/bsp/rpi/memory_map.rs b/10_privilege_level/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/10_privilege_level/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/10_privilege_level/src/interface.rs b/10_privilege_level/src/interface.rs new file mode 100644 index 00000000..55df89cb --- /dev/null +++ b/10_privilege_level/src/interface.rs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +// +// 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 { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} + +/// Timekeeping interfaces. +pub mod time { + use core::time::Duration; + + /// Timer functions. + pub trait Timer { + /// The timer's resolution. + fn resolution(&self) -> Duration; + + /// The uptime since power-on of the device. + /// + /// This includes time consumed by firmware and bootloaders. + fn uptime(&self) -> Duration; + + /// Spin for a given duration. + fn spin_for(&self, duration: Duration); + } +} diff --git a/10_privilege_level/src/main.rs b/10_privilege_level/src/main.rs new file mode 100644 index 00000000..e89366c4 --- /dev/null +++ b/10_privilege_level/src/main.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![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(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } + + bsp::post_driver_init(); + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use core::time::Duration; + use interface::{console::All, time::Timer}; + + println!("Booting on: {}", bsp::board_name()); + + println!( + "Current privilege level: {}", + arch::state::current_privilege_level() + ); + println!("Exception handling state:"); + arch::state::print_exception_state(); + + println!( + "Architectural timer resolution: {} ns", + arch::timer().resolution().as_nanos() + ); + + println!("Drivers loaded:"); + for (i, driver) in bsp::device_drivers().iter().enumerate() { + println!(" {}. {}", i + 1, driver.compatible()); + } + + println!("Timer test, spinning for 1 second"); + arch::timer().spin_for(Duration::from_secs(1)); + + println!("Echoing input now"); + loop { + let c = bsp::console().read_char(); + bsp::console().write_char(c); + } +} diff --git a/10_privilege_level/src/panic_wait.rs b/10_privilege_level/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/10_privilege_level/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/10_privilege_level/src/print.rs b/10_privilege_level/src/print.rs new file mode 100644 index 00000000..86ab59b3 --- /dev/null +++ b/10_privilege_level/src/print.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// 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. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +/// Prints a warning, with newline. +#[macro_export] +macro_rules! warn { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/10_privilege_level/src/runtime_init.rs b/10_privilege_level/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/10_privilege_level/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +}