diff --git a/0E_global_println/.cargo/config b/0E_global_println/.cargo/config new file mode 100644 index 00000000..d7fb2ba3 --- /dev/null +++ b/0E_global_println/.cargo/config @@ -0,0 +1,6 @@ +[target.aarch64-unknown-none] +rustflags = [ + "-C", "link-arg=-Tlink.ld", + "-C", "target-feature=-fp-armv8", + "-C", "target-cpu=cortex-a53", +] diff --git a/0E_global_println/Cargo.lock b/0E_global_println/Cargo.lock new file mode 100644 index 00000000..db059c6f --- /dev/null +++ b/0E_global_println/Cargo.lock @@ -0,0 +1,55 @@ +[[package]] +name = "cortex-a" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel8" +version = "0.1.0" +dependencies = [ + "cortex-a 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raspi3_boot 0.1.0", + "register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "panic-abort" +version = "0.3.1" +source = "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 = "raspi3_boot" +version = "0.1.0" +dependencies = [ + "cortex-a 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "register" +version = "0.3.2" +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.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97867bac786c0a2912f7df981bdb8b6ea109a2422c22b37faf651d558a054453" +"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0f44a6dc9a98359515541a0c46ef4e3630a30879c1d7a4038f31dd533570bfb" +"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0E_global_println/Cargo.toml b/0E_global_println/Cargo.toml new file mode 100644 index 00000000..6d1bfce0 --- /dev/null +++ b/0E_global_println/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "kernel8" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[dependencies] +raspi3_boot = { path = "raspi3_boot" } +cortex-a = "2.4.0" +register = "0.3.2" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" diff --git a/0E_global_println/Makefile b/0E_global_println/Makefile new file mode 100644 index 00000000..750ad650 --- /dev/null +++ b/0E_global_println/Makefile @@ -0,0 +1,65 @@ +# +# MIT License +# +# Copyright (c) 2018-2019 Andre Richter +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this 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. +# +# 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. +# + +TARGET = aarch64-unknown-none + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld + +OBJCOPY = cargo objcopy -- +OBJCOPY_PARAMS = --strip-all -O binary + +UTILS_CONTAINER = andrerichter/raspi3-utils +DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work +DOCKER_TTY = --privileged -v /dev:/dev +QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img +RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img + +.PHONY: all qemu raspboot clippy clean objdump nm + +all: clean kernel8.img + +target/$(TARGET)/release/kernel8: $(SOURCES) + cargo xbuild --target=$(TARGET) --release + +kernel8.img: target/$(TARGET)/release/kernel8 + cp $< . + $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img + +qemu: all + $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio + +raspboot: all + $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) + +clippy: + cargo xclippy --target=$(TARGET) + +clean: + cargo clean + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel8 + +nm: + cargo nm --target $(TARGET) -- kernel8 | sort diff --git a/0E_global_println/README.md b/0E_global_println/README.md new file mode 100644 index 00000000..5dc02a5c --- /dev/null +++ b/0E_global_println/README.md @@ -0,0 +1,28 @@ +# Tutorial 0E - Global `println!` + +Coming soon! + +This lesson will teach about: +- Restructuring the current codebase. +- Realizing global println! and print! macros by reusing macros from the Rust + standard library. +- The NullLock, a wrapper that allows using global static variables without + explicit need for `unsafe {}` code. It is a teaching concept that is only + valid in single-threaded IRQ-disabled environments. However, it already lays + the groundwork for the introduction of proper locking mechanisms, e.g. real + Spinlocks. + +```console +ferris@box:~$ make raspboot + +[0] UART is live! +[1] Press a key to continue booting... Greetings fellow Rustacean! +[2] Switching MMU on now... MMU online. +[i] Memory layout: + 0x00000000 - 0x0007FFFF | 512 KiB | Kernel stack + 0x00080000 - 0x00082FFF | 12 KiB | Kernel code and RO data + 0x00083000 - 0x00085007 | 8 KiB | Kernel data and BSS + 0x3F000000 - 0x3FFFFFFF | 16 MiB | Device MMIO + +$> +``` diff --git a/0E_global_println/kernel8 b/0E_global_println/kernel8 new file mode 100755 index 00000000..b759b2a1 Binary files /dev/null and b/0E_global_println/kernel8 differ diff --git a/0E_global_println/kernel8.img b/0E_global_println/kernel8.img new file mode 100755 index 00000000..773ab0f4 Binary files /dev/null and b/0E_global_println/kernel8.img differ diff --git a/0E_global_println/link.ld b/0E_global_println/link.ld new file mode 100644 index 00000000..cb6f21a6 --- /dev/null +++ b/0E_global_println/link.ld @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +ENTRY(_boot_cores); + +SECTIONS +{ + . = 0x80000; /* This is already 4KiB aligned */ + __ro_start = .; + .text : + { + KEEP(*(.text.boot)) *(.text .text.*) + } + + .rodata : + { + *(.rodata .rodata.*) + } + . = ALIGN(4096); /* Fill up to 4KiB */ + __ro_end = .; + + .data : + { + *(.data .data.*) + } + + .bss ALIGN(8): + { + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} diff --git a/0E_global_println/raspi3_boot/Cargo.toml b/0E_global_println/raspi3_boot/Cargo.toml new file mode 100644 index 00000000..09732955 --- /dev/null +++ b/0E_global_println/raspi3_boot/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "raspi3_boot" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[dependencies] +cortex-a = "2.3.1" +panic-abort = "0.3.1" +r0 = "0.2.2" diff --git a/0E_global_println/raspi3_boot/src/lib.rs b/0E_global_println/raspi3_boot/src/lib.rs new file mode 100644 index 00000000..bc9be005 --- /dev/null +++ b/0E_global_println/raspi3_boot/src/lib.rs @@ -0,0 +1,130 @@ +/* + * MIT License + * + * Copyright (c) 2018 Jorge Aparicio + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +#![deny(missing_docs)] +#![deny(warnings)] +#![no_std] + +//! Low-level boot of the Raspberry's processor + +extern crate panic_abort; + +/// Type check the user-supplied entry function. +#[macro_export] +macro_rules! entry { + ($path:path) => { + #[export_name = "main"] + pub unsafe fn __main() -> ! { + // type check the given path + let f: fn() -> ! = $path; + + f() + } + }; +} + +/// Reset function. +/// +/// Initializes the bss section before calling into the user's `main()`. +unsafe fn reset() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zeroes the .bss section + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + extern "Rust" { + fn main() -> !; + } + + main() +} + +/// Prepare and execute transition from EL2 to EL1. +#[inline] +fn setup_and_enter_el1_from_el2() -> ! { + use cortex_a::{asm, regs::*}; + + const STACK_START: u64 = 0x80_000; + + // 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 + // TODO: Explain the SWIO bit + HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::SWIO::SET); + + // 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 reset(). + ELR_EL2.set(reset as *const () as u64); + + // Set up SP_EL1 (stack pointer), which will be used by EL1 once + // we "return" to it. + SP_EL1.set(STACK_START); + + // Use `eret` to "return" to EL1. This will result in execution of + // `reset()` in EL1. + asm::eret() +} + +/// Entrypoint of the processor. +/// +/// Parks all cores except core0 and checks if we started in EL2. If +/// so, proceeds with setting up EL1. +#[link_section = ".text.boot"] +#[no_mangle] +pub unsafe extern "C" fn _boot_cores() -> ! { + use cortex_a::{asm, regs::*}; + + const CORE_0: u64 = 0; + const CORE_MASK: u64 = 0x3; + const EL2: u32 = CurrentEL::EL::EL2.value; + + if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) { + setup_and_enter_el1_from_el2() + } + + // if not core0 or EL != 2, infinitely wait for events + loop { + asm::wfe(); + } +} diff --git a/0E_global_println/src/delays.rs b/0E_global_println/src/delays.rs new file mode 100644 index 00000000..b1c1fa0f --- /dev/null +++ b/0E_global_println/src/delays.rs @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use cortex_a::asm; + +/* + * + * Using the CPU's cycles + * + */ +/// Wait N CPU cycles (ARM CPU only) +pub fn wait_cycles(cyc: u32) { + for _ in 0..cyc { + asm::nop(); + } +} diff --git a/0E_global_println/src/devices.rs b/0E_global_println/src/devices.rs new file mode 100644 index 00000000..227b92c2 --- /dev/null +++ b/0E_global_println/src/devices.rs @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +pub mod hw; +pub mod virt; diff --git a/0E_global_println/src/devices/hw.rs b/0E_global_println/src/devices/hw.rs new file mode 100644 index 00000000..877622ed --- /dev/null +++ b/0E_global_println/src/devices/hw.rs @@ -0,0 +1,31 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +mod gpio; +mod pl011_uart; +mod videocore_mbox; + +pub use gpio::GPIO; +pub use pl011_uart::PL011Uart; +pub use videocore_mbox::VideocoreMbox; diff --git a/0E_global_println/src/devices/hw/gpio.rs b/0E_global_println/src/devices/hw/gpio.rs new file mode 100644 index 00000000..0d06dfa3 --- /dev/null +++ b/0E_global_println/src/devices/hw/gpio.rs @@ -0,0 +1,120 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use core::ops; +use register::{mmio::ReadWrite, register_bitfields}; + +// 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, + RXD0 = 0b100, // UART0 - Alternate function 0 + RXD1 = 0b010 // Mini UART - Alternate function 5 + + ], + + /// Pin 14 + FSEL14 OFFSET(12) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + TXD0 = 0b100, // UART0 - Alternate function 0 + TXD1 = 0b010 // Mini UART - Alternate function 5 + ] + ], + + /// 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 +} + +/// Public interface to the GPIO MMIO area +pub struct GPIO { + base_addr: u32, +} + +impl ops::Deref for GPIO { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl GPIO { + pub fn new(base_addr: u32) -> GPIO { + GPIO { base_addr } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } +} diff --git a/0E_global_println/src/devices/hw/pl011_uart.rs b/0E_global_println/src/devices/hw/pl011_uart.rs new file mode 100644 index 00000000..f98534e4 --- /dev/null +++ b/0E_global_println/src/devices/hw/pl011_uart.rs @@ -0,0 +1,276 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use super::gpio; +use super::videocore_mbox; +use crate::delays; +use crate::devices::virt::ConsoleOps; +use core::{ + ops, + sync::atomic::{compiler_fence, Ordering}, +}; +use cortex_a::asm; +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 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 + ] + ], + + /// 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 + ] + ], + + /// Interupt 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 +} + +pub enum PL011UartError { + MailboxError, +} +pub type Result = ::core::result::Result; + +pub struct PL011Uart { + base_addr: u32, +} + +impl ops::Deref for PL011Uart { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011Uart { + pub fn new(base_addr: u32) -> PL011Uart { + PL011Uart { base_addr } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + ///Set baud rate and characteristics (115200 8N1) and map to GPIO + pub fn init( + &self, + v_mbox: &mut videocore_mbox::VideocoreMbox, + gpio: &gpio::GPIO, + ) -> Result<()> { + // turn off UART0 + self.CR.set(0); + + // set up clock for consistent divisor values + v_mbox.buffer[0] = 9 * 4; + v_mbox.buffer[1] = videocore_mbox::REQUEST; + v_mbox.buffer[2] = videocore_mbox::tag::SETCLKRATE; + v_mbox.buffer[3] = 12; + v_mbox.buffer[4] = 8; + v_mbox.buffer[5] = videocore_mbox::clock::UART; // UART clock + v_mbox.buffer[6] = 4_000_000; // 4Mhz + v_mbox.buffer[7] = 0; // skip turbo setting + v_mbox.buffer[8] = videocore_mbox::tag::LAST; + + // Insert a compiler fence that ensures that all stores to the + // mbox buffer are finished before the GPU is signaled (which + // is done by a store operation as well). + compiler_fence(Ordering::Release); + + if v_mbox.call(videocore_mbox::channel::PROP).is_err() { + return Err(PL011UartError::MailboxError); // Abort if UART clocks couldn't be set + }; + + // map UART0 to GPIO pins + gpio.GPFSEL1 + .modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); + + gpio.GPPUD.set(0); // enable pins 14 and 15 + delays::wait_cycles(150); + + gpio.GPPUDCLK0.modify( + gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, + ); + delays::wait_cycles(150); + + gpio.GPPUDCLK0.set(0); + + self.ICR.write(ICR::ALL::CLEAR); + self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud + self.FBRD.write(FBRD::FBRD.val(0xB)); + self.LCRH.write(LCRH::WLEN::EightBit); // 8N1 + + self.CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + + Ok(()) + } +} + +impl Drop for PL011Uart { + fn drop(&mut self) { + self.CR + .write(CR::UARTEN::Disabled + CR::TXE::Disabled + CR::RXE::Disabled); + } +} + +impl ConsoleOps for PL011Uart { + /// Send a character + fn putc(&self, c: char) { + // wait until we can send + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + asm::nop(); + } + + // write the character to the buffer + self.DR.set(c as u32); + } + + /// Display a string + fn puts(&self, string: &str) { + for c in string.chars() { + // convert newline to carrige return + newline + if c == '\n' { + self.putc('\r') + } + + self.putc(c); + } + } + + /// Receive a character + fn getc(&self) -> char { + // wait until something is in the buffer + loop { + if !self.FR.is_set(FR::RXFE) { + break; + } + + asm::nop(); + } + + // read it and return + let mut ret = self.DR.get() as u8 as char; + + // convert carrige return to newline + if ret == '\r' { + ret = '\n' + } + + ret + } +} diff --git a/0E_global_println/src/devices/hw/videocore_mbox.rs b/0E_global_println/src/devices/hw/videocore_mbox.rs new file mode 100644 index 00000000..1409f54a --- /dev/null +++ b/0E_global_println/src/devices/hw/videocore_mbox.rs @@ -0,0 +1,164 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use core::ops; +use cortex_a::asm; +use register::{ + mmio::{ReadOnly, WriteOnly}, + register_bitfields, +}; + +register_bitfields! { + u32, + + STATUS [ + FULL OFFSET(31) NUMBITS(1) [], + EMPTY OFFSET(30) NUMBITS(1) [] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct RegisterBlock { + READ: ReadOnly, // 0x00 + __reserved_0: [u32; 5], // 0x04 + STATUS: ReadOnly, // 0x18 + __reserved_1: u32, // 0x1C + WRITE: WriteOnly, // 0x20 +} + +// Custom errors +pub enum VideocoreMboxError { + ResponseError, + UnknownError, +} +pub type Result = ::core::result::Result; + +// Channels +pub mod channel { + pub const PROP: u32 = 8; +} + +// Tags +pub mod tag { + pub const SETCLKRATE: u32 = 0x38002; + pub const LAST: u32 = 0; +} + +// Clocks +pub mod clock { + pub const UART: u32 = 0x0_0000_0002; +} + +// Responses +mod response { + pub const SUCCESS: u32 = 0x8000_0000; + pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response) +} + +pub const REQUEST: u32 = 0; + +// Public interface to the mailbox +#[repr(C)] +#[repr(align(16))] +pub struct VideocoreMbox { + // The address for buffer needs to be 16-byte aligned so that the Videcore + // can handle it properly. Hence, put it at the start of the struct so that + // the align attribute is effective. + pub buffer: [u32; 36], + base_addr: u32, +} + +/// Deref to RegisterBlock +/// +/// Allows writing +/// ``` +/// self.STATUS.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*Mbox::ptr()).STATUS.read() } +/// ``` +impl ops::Deref for VideocoreMbox { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl VideocoreMbox { + pub fn new(base_addr: u32) -> VideocoreMbox { + VideocoreMbox { + buffer: [0; 36], + base_addr, + } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success + pub fn call(&self, channel: u32) -> Result<()> { + // wait until we can write to the mailbox + loop { + if !self.STATUS.is_set(STATUS::FULL) { + break; + } + + asm::nop(); + } + + let buf_ptr = self.buffer.as_ptr() as u32; + + // write the address of our message to the mailbox with channel identifier + self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF)); + + // now wait for the response + loop { + // is there a response? + loop { + if !self.STATUS.is_set(STATUS::EMPTY) { + break; + } + + asm::nop(); + } + + let resp: u32 = self.READ.get(); + + // is it a response to our message? + if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) { + // is it a valid successful response? + return match self.buffer[1] { + response::SUCCESS => Ok(()), + response::ERROR => Err(VideocoreMboxError::ResponseError), + _ => Err(VideocoreMboxError::UnknownError), + }; + } + } + } +} diff --git a/0E_global_println/src/devices/virt.rs b/0E_global_println/src/devices/virt.rs new file mode 100644 index 00000000..30ed5469 --- /dev/null +++ b/0E_global_println/src/devices/virt.rs @@ -0,0 +1,27 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +mod console; + +pub use console::{Console, ConsoleOps}; diff --git a/0E_global_println/src/devices/virt/console.rs b/0E_global_println/src/devices/virt/console.rs new file mode 100644 index 00000000..088971c6 --- /dev/null +++ b/0E_global_println/src/devices/virt/console.rs @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use crate::devices::hw; +use core::fmt; + +/// A trait that must be implemented by devices that are candidates for the +/// global console. +#[allow(unused_variables)] +pub trait ConsoleOps: Drop { + fn putc(&self, c: char) {} + fn puts(&self, string: &str) {} + fn getc(&self) -> char { + ' ' + } + fn flush(&self) {} +} + +/// A dummy console that just ignores its inputs. +pub struct NullConsole; +impl Drop for NullConsole { + fn drop(&mut self) {} +} +impl ConsoleOps for NullConsole {} + +/// Possible outputs which the console can store. +pub enum Output { + None(NullConsole), + PL011Uart(hw::PL011Uart), +} + +impl From for Output { + fn from(instance: hw::PL011Uart) -> Self { + Output::PL011Uart(instance) + } +} + +pub struct Console { + output: Output, +} + +impl Console { + pub const fn new() -> Console { + Console { + output: Output::None(NullConsole {}), + } + } + + #[inline(always)] + fn current_ptr(&self) -> &dyn ConsoleOps { + match &self.output { + Output::None(i) => i, + Output::PL011Uart(i) => i, + } + } + + /// Overwrite the current output. The old output will go out of scope and + /// it's Drop function will be called. + pub fn replace_with(&mut self, x: Output) { + self.current_ptr().flush(); + + self.output = x; + } + + /// A command prompt. Currently does nothing. + pub fn command_prompt(&self) -> ! { + self.puts("\n$> "); + + let mut input; + loop { + input = self.getc(); + + if input == '\n' { + self.puts("\n$> ") + } else { + self.putc(input); + } + } + } +} + +impl Drop for Console { + fn drop(&mut self) {} +} + +/// Dispatch the respective function to the currently stored output device. +impl ConsoleOps for Console { + fn putc(&self, c: char) { + self.current_ptr().putc(c); + } + + fn puts(&self, string: &str) { + self.current_ptr().puts(string); + } + + fn getc(&self) -> char { + self.current_ptr().getc() + } + + fn flush(&self) { + self.current_ptr().flush() + } +} + +/// Implementing this trait enables usage of the format_args! macros, which in +/// turn are used to implement the kernel's print! and println! macros. +/// +/// See src/macros.rs. +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.current_ptr().puts(s); + + Ok(()) + } +} diff --git a/0E_global_println/src/macros.rs b/0E_global_println/src/macros.rs new file mode 100644 index 00000000..28280be9 --- /dev/null +++ b/0E_global_println/src/macros.rs @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use core::fmt; + +// https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::macros::_print(format_args!($($arg)*))); +} + +// https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => (print!("\n")); + ($($arg:tt)*) => ({ + $crate::macros::_print(format_args_nl!($($arg)*)); + }) +} + +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + use core::fmt::Write; + + crate::CONSOLE.lock(|c| { + c.write_fmt(args).unwrap(); + }) +} diff --git a/0E_global_println/src/main.rs b/0E_global_println/src/main.rs new file mode 100644 index 00000000..bd9fb8e3 --- /dev/null +++ b/0E_global_println/src/main.rs @@ -0,0 +1,106 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +#![no_std] +#![no_main] +#![feature(const_fn)] +#![feature(custom_attribute)] +#![feature(format_args_nl)] +#![feature(range_contains)] + +mod delays; +mod devices; +mod macros; +mod memory; +mod sync; + +/// The global console. Output of the print! and println! macros. +static CONSOLE: sync::NullLock = + sync::NullLock::new(devices::virt::Console::new()); + +fn kernel_entry() -> ! { + use devices::hw; + use devices::virt::ConsoleOps; + + // This will be invisible, because CONSOLE is dispatching to the NullConsole + // at this point in time. + println!("Is there anybody out there?"); + + //------------------------------------------------------------ + // Instantiate GPIO device + //------------------------------------------------------------ + let gpio = hw::GPIO::new(memory::map::GPIO_BASE); + + //------------------------------------------------------------ + // Instantiate Videocore Mailbox + //------------------------------------------------------------ + let mut v_mbox = hw::VideocoreMbox::new(memory::map::VIDEOCORE_MBOX_BASE); + + //------------------------------------------------------------ + // Instantiate PL011 UART and put it in CONSOLE + //------------------------------------------------------------ + let uart = hw::PL011Uart::new(memory::map::PL011_UART_BASE); + + match uart.init(&mut v_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()); + }); + + println!("\n[0] UART is live!"); + } + Err(_) => loop { + cortex_a::asm::wfe() // If UART fails, abort early + }, + } + + //------------------------------------------------------------ + // Greet the user + //------------------------------------------------------------ + print!("[1] Press a key to continue booting... "); + CONSOLE.lock(|c| { + c.getc(); + }); + println!("Greetings fellow Rustacean!"); + + //------------------------------------------------------------ + // Bring up memory subsystem + //------------------------------------------------------------ + print!("[2] Switching MMU on now... "); + unsafe { memory::mmu::init() }; + println!("MMU online."); + + memory::print_layout(); + + //------------------------------------------------------------ + // Start a command prompt + //------------------------------------------------------------ + CONSOLE.lock(|c| { + c.command_prompt(); + }) +} + +raspi3_boot::entry!(kernel_entry); diff --git a/0E_global_println/src/memory.rs b/0E_global_println/src/memory.rs new file mode 100644 index 00000000..ee828c9d --- /dev/null +++ b/0E_global_println/src/memory.rs @@ -0,0 +1,113 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use crate::println; + +pub mod mmu; + +/// The system memory map. +#[rustfmt::skip] +pub mod map { + pub const KERN_STACK_BOT: u32 = 0x0000_0000; + pub const KERN_STACK_TOP: u32 = 0x0007_FFFF; + + pub const MMIO_BASE: u32 = 0x3F00_0000; + pub const VIDEOCORE_MBOX_BASE: u32 = MMIO_BASE + 0x0000_B880; + pub const GPIO_BASE: u32 = MMIO_BASE + 0x0020_0000; + pub const PL011_UART_BASE: u32 = MMIO_BASE + 0x0020_1000; + + pub const PHYS_ADDR_MAX: u32 = 0x3FFF_FFFF; +} + +const PAGESIZE: u64 = 4096; + +fn get_ro_start_end() -> (u64, u64) { + // Using the linker script, we ensure that the RO area is consecutive and 4 + // KiB aligned, and we export the boundaries via symbols. + extern "C" { + // The inclusive start of the read-only area, aka the address of the + // first byte of the area. + static __ro_start: u64; + + // The non-inclusive end of the read-only area, aka the address of the + // first byte _after_ the RO area. + static __ro_end: u64; + } + + unsafe { + // Notice the subtraction to calculate the last page index of the RO + // area and not the first page index after the RO area. + ( + &__ro_start as *const _ as u64, + &__ro_end as *const _ as u64 - 1, + ) + } +} + +pub fn print_layout() { + use crate::memory::map::*; + + // log2(1024) + const KIB_RSHIFT: u32 = 10; + + // log2(1024 * 1024) + const MIB_RSHIFT: u32 = 20; + + println!("[i] Memory layout:"); + + println!( + " {:#010X} - {:#010X} | {: >4} KiB | Kernel stack", + KERN_STACK_BOT, + KERN_STACK_TOP, + (KERN_STACK_TOP - KERN_STACK_BOT + 1) >> KIB_RSHIFT + ); + + let (ro_start, ro_end) = get_ro_start_end(); + println!( + " {:#010X} - {:#010X} | {: >4} KiB | Kernel code and RO data", + ro_start, + ro_end, + (ro_end - ro_start + 1) >> KIB_RSHIFT + ); + + extern "C" { + static __bss_end: u64; + } + + let start = ro_end + 1; + let end = unsafe { &__bss_end as *const _ as u64 } - 1; + println!( + " {:#010X} - {:#010X} | {: >4} KiB | Kernel data and BSS", + start, + end, + (end - start + 1) >> KIB_RSHIFT + ); + + println!( + " {:#010X} - {:#010X} | {: >4} MiB | Device MMIO", + MMIO_BASE, + PHYS_ADDR_MAX, + (PHYS_ADDR_MAX - MMIO_BASE + 1) >> MIB_RSHIFT + ); +} diff --git a/0E_global_println/src/memory/mmu.rs b/0E_global_println/src/memory/mmu.rs new file mode 100644 index 00000000..4c92a0aa --- /dev/null +++ b/0E_global_println/src/memory/mmu.rs @@ -0,0 +1,229 @@ +/* + * MIT License + * + * Copyright (c) 2018-2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use cortex_a::{barrier, regs::*}; +use register::register_bitfields; + +register_bitfields! {u64, + // AArch64 Reference Manual page 2150 + STAGE1_DESCRIPTOR [ + /// Execute-never + XN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Various address fields, depending on use case + LVL2_OUTPUT_ADDR_4KiB OFFSET(21) NUMBITS(27) [], // [47:21] + NEXT_LVL_TABLE_ADDR_4KiB OFFSET(12) NUMBITS(36) [], // [47:12] + + /// Access flag + AF OFFSET(10) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Shareability field + SH OFFSET(8) NUMBITS(2) [ + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + + /// Access Permissions + AP OFFSET(6) NUMBITS(2) [ + RW_EL1 = 0b00, + RW_EL1_EL0 = 0b01, + RO_EL1 = 0b10, + RO_EL1_EL0 = 0b11 + ], + + /// Memory attributes index into the MAIR_EL1 register + AttrIndx OFFSET(2) NUMBITS(3) [], + + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Table = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +trait BaseAddr { + fn base_addr(&self) -> u64; +} + +impl BaseAddr for [u64; 512] { + fn base_addr(&self) -> u64 { + self as *const u64 as u64 + } +} + +const NUM_ENTRIES_4KIB: usize = 512; + +// We need a wrapper struct here so that we can make use of the align attribute. +#[repr(C)] +#[repr(align(4096))] +struct PageTable { + entries: [u64; NUM_ENTRIES_4KIB], +} + +static mut LVL2_TABLE: PageTable = PageTable { + entries: [0; NUM_ENTRIES_4KIB], +}; +static mut SINGLE_LVL3_TABLE: PageTable = PageTable { + entries: [0; NUM_ENTRIES_4KIB], +}; + +/// Set up identity mapped page tables for the first 1 GiB of address space. +pub unsafe fn init() { + // First, define the three memory types that we will map. Cacheable and + // non-cacheable normal DRAM, and device. + MAIR_EL1.write( + // Attribute 2 + MAIR_EL1::Attr2_HIGH::Memory_OuterNonCacheable + + MAIR_EL1::Attr2_LOW_MEMORY::InnerNonCacheable + + // Attribute 1 + + MAIR_EL1::Attr1_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc + + MAIR_EL1::Attr1_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc + + // Attribute 0 + + MAIR_EL1::Attr0_HIGH::Device + + MAIR_EL1::Attr0_LOW_DEVICE::Device_nGnRE, + ); + + // Descriptive consts for indexing into the correct MAIR_EL1 attributes. + #[allow(dead_code)] + mod mair { + pub const DEVICE: u64 = 0; + pub const NORMAL: u64 = 1; + pub const NORMAL_NON_CACHEABLE: u64 = 2; + } + + // The first 2 MiB. + // + // Set up the first LVL2 entry, pointing to the base address of a follow-up + // table containing 4 KiB pages. + // + // 0x0000_0000_0000_0000 | + // |> 2 MiB + // 0x0000_0000_001F_FFFF | + let lvl3_base: u64 = SINGLE_LVL3_TABLE.entries.base_addr() >> 12; + LVL2_TABLE.entries[0] = (STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Table + + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(lvl3_base)) + .value; + + // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. + // + // Differentiate between + // - cacheable DRAM + // - device memory + // + // Ranges are stored in memory.rs. + // + // 0x0000_0000_0020_0000 | + // |> 1006 MiB cacheable DRAM + // 0x0000_0000_3EFF_FFFF | + // 0x0000_0000_3F00_0000 | + // |> 16 MiB device (MMIO) + // 0x0000_0000_4000_0000 | + let mmio_first_block_index: u64 = (crate::memory::map::MMIO_BASE >> 21).into(); + + let common = STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Block + + STAGE1_DESCRIPTOR::AP::RW_EL1 + + STAGE1_DESCRIPTOR::AF::True + + STAGE1_DESCRIPTOR::XN::True; + + // Notice the skip(1) which makes the iteration start at the second 2 MiB + // block (0x20_0000). + for (i, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { + let j: u64 = i as u64; + + let mem_attr = if j >= mmio_first_block_index { + STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) + } else { + STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) + }; + + *entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(j)).value; + } + + // Finally, fill the single LVL3 table (4 KiB granule). Differentiate + // between code+RO and RW pages. + let (ro_start_addr, ro_end_addr) = super::get_ro_start_end(); + + let ro_first_page_index = ro_start_addr / super::PAGESIZE; + let ro_last_page_index = ro_end_addr / super::PAGESIZE; + + let common = STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Table + + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) + + STAGE1_DESCRIPTOR::SH::InnerShareable + + STAGE1_DESCRIPTOR::AF::True; + + for (i, entry) in SINGLE_LVL3_TABLE.entries.iter_mut().enumerate() { + let j: u64 = i as u64; + + let mem_attr = if (ro_first_page_index..=ro_last_page_index).contains(&j) { + STAGE1_DESCRIPTOR::AP::RO_EL1 + STAGE1_DESCRIPTOR::XN::False + } else { + STAGE1_DESCRIPTOR::AP::RW_EL1 + STAGE1_DESCRIPTOR::XN::True + }; + + *entry = (common + mem_attr + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(j)).value; + } + + // Point to the LVL2 table base address in TTBR0. + TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr()); + + // Configure various settings of stage 1 of the EL1 translation regime. + let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + TCR_EL1.write( + TCR_EL1::TBI0::Ignored + + TCR_EL1::IPS.val(ips) + + TCR_EL1::TG0::KiB_4 // 4 KiB granule + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::T0SZ.val(34), // Start walks at level 2 + ); + + // Switch the MMU on. + // + // First, force all previous changes to be seen before the MMU is enabled. + barrier::isb(barrier::SY); + + // Enable the MMU and turn on data and instruction caching. + SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); + + // Force MMU init to complete before next instruction + barrier::isb(barrier::SY); +} diff --git a/0E_global_println/src/sync.rs b/0E_global_println/src/sync.rs new file mode 100644 index 00000000..55a45e31 --- /dev/null +++ b/0E_global_println/src/sync.rs @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2019 Andre Richter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + * + * 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. + */ + +use core::cell::UnsafeCell; + +pub struct NullLock { + data: UnsafeCell, +} + +// Since we are instantiating this struct as a static variable, which could +// potentially be shared between different threads, we need to tell the compiler +// that sharing of this struct is safe by marking it with the Sync trait. +// +// At this point in time, we can do so without worrying, because the kernel +// anyways runs on a single core and interrupts are disabled. In short, multiple +// threads don't exist yet in our code. +// +// Literature: +// https://doc.rust-lang.org/beta/nomicon/send-and-sync.html +// https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl NullLock { + pub fn lock(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + // In a real lock, there would be code around this line that ensures + // that this mutable reference will ever only be given out to one thread + // at a time. + f(unsafe { &mut *self.data.get() }) + } +}