diff --git a/10_exceptions_groundwork/.cargo/config b/10_exceptions_groundwork/.cargo/config new file mode 100644 index 00000000..d7fb2ba3 --- /dev/null +++ b/10_exceptions_groundwork/.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/10_exceptions_groundwork/Cargo.lock b/10_exceptions_groundwork/Cargo.lock new file mode 100644 index 00000000..db059c6f --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/Cargo.toml b/10_exceptions_groundwork/Cargo.toml new file mode 100644 index 00000000..6d1bfce0 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/Makefile b/10_exceptions_groundwork/Makefile new file mode 100644 index 00000000..be8d37ef --- /dev/null +++ b/10_exceptions_groundwork/Makefile @@ -0,0 +1,66 @@ +# +# 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_MISC = -v $(shell pwd)/../emulation:/emulation +DOCKER_TTY = --privileged -v /dev:/dev +QEMU_CMD = bash /emulation/qemu_multi_uart.sh +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) $(DOCKER_MISC) $(UTILS_CONTAINER) $(QEMU_CMD) + +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/10_exceptions_groundwork/README.md b/10_exceptions_groundwork/README.md new file mode 100644 index 00000000..7edd68db --- /dev/null +++ b/10_exceptions_groundwork/README.md @@ -0,0 +1,286 @@ +# Tutorial 10 - Exceptions: Groundwork + + +In this tutorial, we lay the groundwork for taking exceptions, and write a very bare-bones handler for a synchronous exception that happens in `EL1`. + +More tutorials on exceptions will follow, implementing and introducing various other aspects of this rather huge topic. + +## Exception Types + +In `AArch64`, it is differentiated between four types of exceptions. These are: +- Synchronous + - For example, a `data abort` or a `system call`. They happen in direct consequence of executing a certain instruction, hence _synchronously_. +- Interrupt Request (`IRQ`) + - For example, an external device, like a timer, is asserting a physical interrupt line. IRQs happen _asynchronously_. +- Fast Interrupt Request (`FIQ`) + - These are basically interrupts that take priority over normal IRQs and have some more traits that make them suitable to implement super-fast processing. However, this is out of scope for this tutorial. For the sake of keeping these tutorials compact and concise, we will more or less ignore FIQs and only implement a dummy handler that would halt the CPU core. +- System Error (`SError`) + - Like IRQs, SErrors happen asynchronously and are technically more or less the same. They are intended to signal rather fatal errors in the system, e.g. if a transaction times out on the `SoC` interconnect. They are highly implementation specific and it is up to the SoC designer to decide which events are delivered as SErrors instead of normal IRQs. + +## Exception entry + +We recommend to read pages 1874-1876 of the [ARMv8 Architecture Reference Manual][ARMv8_Manual] to understand the mechanisms of taking an exception. + +Here's an excerpt of important features for this tutorial: +- Exception entry moves the processor to the same or a higher `Exception Level`, but never to a lower `EL`. +- The program status is saved in the `SPSR_ELx` register at the target `EL`. +- The preferred return address is saved in the `ELR_ELx` register. + - "Preferred" here means that `ELR_ELx` may hold the instruction address of the instructions that caused the exception (`synchronous case`) or the first instruction that did not complete due to an `asynchronous` exception. Details in Chapter D1.10.1 of the [ARMv8 Architecture Reference Manual][ARMv8_Manual]. +- All kinds of exceptions are turned off upon taking an exception, so that by default exception handlers can not get interrupted themselves. +- Taking an exception will select the dedicated stack pointer of the target `EL`. + - For example, if an exception in `EL0` is taken, the Stack Pointer Select register `SPSel` will switch from `0` to `1`, meaning that `SP_EL1` will be used by the exception vector code unless you explicitly change it back to `SP_EL0`. + + +### Exception Vectors + +`AArch64` has a total of 16 exception vectors. There is one for each of the four kinds that were introduced already, and additionally, it is taken into account _where_ the exception was taken from and what the circumstances were. + +Here is a copy of the decision table as shown in Chapter D1.10.2 of the [ARMv8 Architecture Reference Manual][ARMv8_Manual]: + +[ARMv8_Manual]: https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Exception taken from Offset for exception type
SynchronousIRQ or vIRQFIQ or vFIQSError or vSError
Current Exception level with SP_EL0.0x0000x0800x1000x180
Current Exception level with SP_ELx, x>0.0x2000x2800x3000x380
Lower Exception level, where the implemented level immediately lower than the target level is using AArch64.0x4000x4800x5000x580
Lower Exception level, where the implemented level immediately lower than the target level is using AArch32.0x6000x6800x7000x780
+ +Since our bare-metal Raspberry code operates in `EL1` using `SP_EL1`, if we'd cause a synchronous exception, the exception vector at offset `0x200` would be executed. But what does that even mean? + +## Handler Code and Offsets + +In many architectures, Operating Systems register their exception handlers (aka vectors) by compiling an architecturally defined data structure that stores function pointers to the different handlers. This can be as simple as an ordinary array of function pointers. The `base address` of this data structure is then stored into a special purpose register so that the CPU can branch to the respective handler function upon taking an exception. The famous `x86_64` architecture follows this principle, for example. + +In `AArch64`, it is a bit different. Here, we have the special purpose register as well, called `VBAR_EL1`: Vector Base Address Register. + +However, it does not store the base address of an array of function pointers, but the base address of a **memory location that contains code** for the 16 handlers, one handler back-to-back after the other. Each handler can take a maximum space of `0x80` bytes, aka 128 bytes. That's why the table above shows `offsets`: To indicate at which offset a certain handler starts. + +Of course, you are not obliged to cram all your handler code into only 128 bytes. You are free to branch off to any other functions at any time. Actually, that is needed in most cases anyways, because the context-saving code alone would take up most of the available space (You'll learn about what context saving is shortly). + +Additionally, there is a requirement that the `Vector Base Address` is aligned to `0x800` aka 2048 bytes. + +## Rust Implementation + +We start by adding a new section to the `link.ld` script, which will contain the exception vector code: + +```rust +SECTIONS +{ + .vectors ALIGN(2048): + { + *(.vectors) + } +``` + +### Context Save and Restore + +Exception vectors, just like any other code, use a bunch of commonly shared processor resources. Most of all, the set of `General Purpose Registers` (GPRs) that each core in `AArch64` provides (`X0`-`X30`). + +In order to not taint these registers when executing exception vector code, it is general practice to save these shared resources in memory (the stack, to be precise) as the very first action. This is commonly described as *saving the context*. Exception vector code can then use the shared resources in its own code without bothering, and as a last action before returning from exception handling code, restore the context, so that the processor can continue where it left off before taking the exception. + +Context save and restore is one of the few places in system software where it is strongly advised to to use some hand-crafted assembly. Introducing `vectors.S`: + +```asm +.macro SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE handler +.balign 0x80 + + sub sp, sp, #16 * 17 + + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + + mrs x1, SPSR_EL1 + mrs x2, ELR_EL1 + + stp x30, x1, [sp, #16 * 15] + str x2, [sp, #16 * 16] + + mov x0, sp + bl \handler + b __restore_context +.endm +``` + +First, a macro for saving the context, branching to follow-up handler code, and finally restoring the context. In advance, we reserve space on the stack for the context. That is, the 30 `GPRs` as well as the `saved program status` and the `exception link register` (holding the preferred return address). Afterwards, we store those registers, save the current stack address in `X0` and branch off to follow-up handler-code, whose function name is supplied as an argument to the macro. + +The handler code will be written in Rust, but use the platform's `C` ABI. This way, we can define a function signature that has a pointer to the context-data on the stack as its first argument, and know that this argument is expected to be in the `X0` register. We need to use the `C` ABI here because `Rust` has no stable convention ([yet](https://github.com/rust-lang/rfcs/issues/600)). + +Also note the `.balign 0x80` which ensure that the code is aligned properly according to the table shown earlier. + +### Exception Vector Code + +Next, we populate the `.vectors` section of the linker script using our macro (except for the FIQ vectors, for which we insert code that halts the CPU (via another macro): + +```asm +.section .vectors, "ax" +.global __exception_vectors_start +__exception_vectors_start: + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_synchronous // 0x000 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_irq // 0x080 + FIQ_DUMMY // 0x100 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_serror // 0x180 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_synchronous // 0x200 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_irq // 0x280 + FIQ_DUMMY // 0x300 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_serror // 0x380 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_synchronous // 0x400 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_irq // 0x480 + FIQ_DUMMY // 0x500 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_serror // 0x580 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_synchronous // 0x600 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_irq // 0x680 + FIQ_DUMMY // 0x700 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_serror // 0x780 +``` + +This part introduces various handler function names, which we can now implement using `Rust`. + +### Implementing a handler + +In `exception.rs`, we implement a handler for a synchronous exception that that will happen in `EL1` using `SP_EL1`: + +```rust +#[no_mangle] +unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { + println!("[!] A synchronous exception happened."); + println!(" ELR_EL1: {:#010X}", e.elr_el1); + println!( + " Incrementing ELR_EL1 by 4 now to continue with the first \ + instruction after the exception!" + ); + + e.elr_el1 += 4; + + println!(" ELR_EL1 modified: {:#010X}", e.elr_el1); + println!(" Returning from exception...\n"); +} +``` + +The function takes a `struct ExceptionContext` argument, which resembles what we put on the stack before branching to the handler: + +```rust +#[repr(C)] +pub struct GPR { + x: [u64; 31], +} + +#[repr(C)] +pub struct ExceptionContext { + // General Purpose Registers + gpr: GPR, + spsr_el1: u64, + elr_el1: u64, +} +``` + +Inside the function, for demo purposes, we advance the copy of the `ELR` by 4, which makes it point to the next instruction after the instruction that caused the exception. +When the function returns, execution continues in the assembly macro we introduced before. The macro has only one more line left: `b __restore_context`, which jumps to an assembly function that plays back our saved context before finally executing `eret` to return from the exception. + +#### Default handler + +In order to spare the work of implementing each and every handler, we define an `extern "C" fn default_exception_handler()`. Using the linker script, we take a shortcut and make all the other handlers point to this function code if it is not implemented explicitly anywhere else: + +```rust +PROVIDE(current_el0_synchronous = default_exception_handler); +PROVIDE(current_el0_irq = default_exception_handler); +PROVIDE(current_el0_serror = default_exception_handler); + +...(Many more omitted) +``` + +## Causing an Exception - Testing the Code + +After pointing `VBAR_EL1` to our vector code, + +```rust +exception::set_vbar_el1_checked(exception_vectors_start) +``` +which enables exception handling, we cause a data abort exception by reading from memory address `3 GiB`: + +```rust +let big_addr: u64 = 3 * 1024 * 1024 * 1024; +unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; +``` + +Finally, this triggers our exception code, because we try to read from a virtual address for which no address translations have been installed. Remember, we only installed identity-mapped page tables for the first 1 GiB of address space in lesson `0C`. +After the exception handler is finished, it returns to the first instruction after the memory read that caused the exception: + +```console +ferris@box:~$ make raspboot +[0] MiniUart online. +[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 - 0x00084FFF | 20 KiB | Kernel code and RO data + 0x00085000 - 0x00088007 | 12 KiB | Kernel data and BSS + 0x00200000 - 0x005FFFFF | 4 MiB | DMA heap pool + 0x3F000000 - 0x3FFFFFFF | 16 MiB | Device MMIO +[i] Global DMA Allocator: + Allocated Addr 0x00200000 Size 0x90 +[3] Videocore Mailbox set up (DMA mem heap allocation successful). +[4] PL011 UART online. Output switched to it. +[5] Exception vectors are set up. +[!] A synchronous exception happened. + ELR_EL1: 0x000809C0 + Incrementing ELR_EL1 by 4 now to continue with the first instruction after the exception! + ELR_EL1 modified: 0x000809C4 + Returning from exception... + +[i] Whoa! We recovered from an exception. +``` diff --git a/10_exceptions_groundwork/kernel8 b/10_exceptions_groundwork/kernel8 new file mode 100755 index 00000000..a9eceffd Binary files /dev/null and b/10_exceptions_groundwork/kernel8 differ diff --git a/10_exceptions_groundwork/kernel8.img b/10_exceptions_groundwork/kernel8.img new file mode 100755 index 00000000..aa564c34 Binary files /dev/null and b/10_exceptions_groundwork/kernel8.img differ diff --git a/10_exceptions_groundwork/link.ld b/10_exceptions_groundwork/link.ld new file mode 100644 index 00000000..00bb9b46 --- /dev/null +++ b/10_exceptions_groundwork/link.ld @@ -0,0 +1,78 @@ +/* + * 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.*) + } + + .vectors ALIGN(2048): + { + *(.vectors) + } + + .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*) } +} + +PROVIDE(current_el0_synchronous = default_exception_handler); +PROVIDE(current_el0_irq = default_exception_handler); +PROVIDE(current_el0_serror = default_exception_handler); + +PROVIDE(current_elx_synchronous = default_exception_handler); +PROVIDE(current_elx_irq = default_exception_handler); +PROVIDE(current_elx_serror = default_exception_handler); + +PROVIDE(lower_aarch64_synchronous = default_exception_handler); +PROVIDE(lower_aarch64_irq = default_exception_handler); +PROVIDE(lower_aarch64_serror = default_exception_handler); + +PROVIDE(lower_aarch32_synchronous = default_exception_handler); +PROVIDE(lower_aarch32_irq = default_exception_handler); +PROVIDE(lower_aarch32_serror = default_exception_handler); diff --git a/10_exceptions_groundwork/raspi3_boot/Cargo.lock b/10_exceptions_groundwork/raspi3_boot/Cargo.lock new file mode 100644 index 00000000..7428c6de --- /dev/null +++ b/10_exceptions_groundwork/raspi3_boot/Cargo.lock @@ -0,0 +1,46 @@ +[[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 = "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/10_exceptions_groundwork/raspi3_boot/Cargo.toml b/10_exceptions_groundwork/raspi3_boot/Cargo.toml new file mode 100644 index 00000000..09732955 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/raspi3_boot/src/lib.rs b/10_exceptions_groundwork/raspi3_boot/src/lib.rs new file mode 100644 index 00000000..bc9be005 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/delays.rs b/10_exceptions_groundwork/src/delays.rs new file mode 100644 index 00000000..b1c1fa0f --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/devices.rs b/10_exceptions_groundwork/src/devices.rs new file mode 100644 index 00000000..227b92c2 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/devices/hw.rs b/10_exceptions_groundwork/src/devices/hw.rs new file mode 100644 index 00000000..08a6c06c --- /dev/null +++ b/10_exceptions_groundwork/src/devices/hw.rs @@ -0,0 +1,33 @@ +/* + * 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 mini_uart; +mod pl011_uart; +mod videocore_mbox; + +pub use gpio::GPIO; +pub use mini_uart::MiniUart; +pub use pl011_uart::PL011Uart; +pub use videocore_mbox::VideocoreMbox; diff --git a/10_exceptions_groundwork/src/devices/hw/gpio.rs b/10_exceptions_groundwork/src/devices/hw/gpio.rs new file mode 100644 index 00000000..0d06dfa3 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/devices/hw/mini_uart.rs b/10_exceptions_groundwork/src/devices/hw/mini_uart.rs new file mode 100644 index 00000000..4f814268 --- /dev/null +++ b/10_exceptions_groundwork/src/devices/hw/mini_uart.rs @@ -0,0 +1,266 @@ +/* + * 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 crate::devices::virt::ConsoleOps; +use core::ops; +use cortex_a::asm; +use register::{mmio::*, register_bitfields}; + +/// Auxilary mini UART registers +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Auxiliary enables + AUX_ENABLES [ + /// If set the mini UART is enabled. The UART will immediately + /// start receiving data, especially if the UART1_RX line is + /// low. + /// If clear the mini UART is disabled. That also disables any + /// mini UART register access + MINI_UART_ENABLE OFFSET(0) NUMBITS(1) [] + ], + + /// Mini Uart Interrupt Identify + AUX_MU_IIR [ + /// Writing with bit 1 set will clear the receive FIFO + /// Writing with bit 2 set will clear the transmit FIFO + FIFO_CLEAR OFFSET(1) NUMBITS(2) [ + Rx = 0b01, + Tx = 0b10, + All = 0b11 + ] + ], + + /// Mini Uart Line Control + AUX_MU_LCR [ + /// Mode the UART works in + DATA_SIZE OFFSET(0) NUMBITS(2) [ + SevenBit = 0b00, + EightBit = 0b11 + ] + ], + + /// Mini Uart Line Status + AUX_MU_LSR [ + /// This bit is set if the transmit FIFO is empty and the transmitter is + /// idle. (Finished shifting out the last bit). + TX_IDLE OFFSET(6) NUMBITS(1) [], + + /// This bit is set if the transmit FIFO can accept at least + /// one byte. + TX_EMPTY OFFSET(5) NUMBITS(1) [], + + /// This bit is set if the receive FIFO holds at least 1 + /// symbol. + DATA_READY OFFSET(0) NUMBITS(1) [] + ], + + /// Mini Uart Extra Control + AUX_MU_CNTL [ + /// If this bit is set the mini UART transmitter is enabled. + /// If this bit is clear the mini UART transmitter is disabled. + TX_EN OFFSET(1) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// If this bit is set the mini UART receiver is enabled. + /// If this bit is clear the mini UART receiver is disabled. + RX_EN OFFSET(0) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ] + ], + + /// Mini Uart Baudrate + AUX_MU_BAUD [ + /// Mini UART baudrate counter + RATE OFFSET(0) NUMBITS(16) [] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct RegisterBlock { + __reserved_0: u32, // 0x00 + AUX_ENABLES: ReadWrite, // 0x04 + __reserved_1: [u32; 14], // 0x08 + AUX_MU_IO: ReadWrite, // 0x40 - Mini Uart I/O Data + AUX_MU_IER: WriteOnly, // 0x44 - Mini Uart Interrupt Enable + AUX_MU_IIR: WriteOnly, // 0x48 + AUX_MU_LCR: WriteOnly, // 0x4C + AUX_MU_MCR: WriteOnly, // 0x50 + AUX_MU_LSR: ReadOnly, // 0x54 + __reserved_2: [u32; 2], // 0x58 + AUX_MU_CNTL: WriteOnly, // 0x60 + __reserved_3: u32, // 0x64 + AUX_MU_BAUD: WriteOnly, // 0x68 +} + +pub struct MiniUart { + base_addr: u32, +} + +/// Deref to RegisterBlock +/// +/// Allows writing +/// ``` +/// self.MU_IER.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*MiniUart::ptr()).MU_IER.read() } +/// ``` +impl ops::Deref for MiniUart { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl MiniUart { + pub fn new(base_addr: u32) -> MiniUart { + MiniUart { 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, gpio: &gpio::GPIO) { + // initialize UART + self.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET); + self.AUX_MU_IER.set(0); + self.AUX_MU_CNTL.set(0); + self.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit); + self.AUX_MU_MCR.set(0); + self.AUX_MU_IER.set(0); + self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All); + self.AUX_MU_BAUD.write(AUX_MU_BAUD::RATE.val(270)); // 115200 baud + + // map UART1 to GPIO pins + gpio.GPFSEL1 + .modify(gpio::GPFSEL1::FSEL14::TXD1 + gpio::GPFSEL1::FSEL15::RXD1); + + gpio.GPPUD.set(0); // enable pins 14 and 15 + for _ in 0..150 { + asm::nop(); + } + + gpio.GPPUDCLK0 + .write(gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock); + for _ in 0..150 { + asm::nop(); + } + + gpio.GPPUDCLK0.set(0); + + self.AUX_MU_CNTL + .write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled); + + // Clear FIFOs before using the device + self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All); + } + + pub fn wait_tx_fifo_empty(&self) { + loop { + if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE) { + break; + } + + asm::nop(); + } + } +} + +impl Drop for MiniUart { + fn drop(&mut self) { + self.AUX_ENABLES + .modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR); + } +} + +impl ConsoleOps for MiniUart { + /// Send a character + fn putc(&self, c: char) { + // wait until we can send + loop { + if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) { + break; + } + + asm::nop(); + } + + // write the character to the buffer + self.AUX_MU_IO.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.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) { + break; + } + + asm::nop(); + } + + // read it and return + let mut ret = self.AUX_MU_IO.get() as u8 as char; + + // convert carrige return to newline + if ret == '\r' { + ret = '\n' + } + + ret + } + + /// Wait until the TX FIFO is empty, aka all characters have been put on the + /// line. + fn flush(&self) { + self.wait_tx_fifo_empty(); + } +} diff --git a/10_exceptions_groundwork/src/devices/hw/pl011_uart.rs b/10_exceptions_groundwork/src/devices/hw/pl011_uart.rs new file mode 100644 index 00000000..f98534e4 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/devices/hw/videocore_mbox.rs b/10_exceptions_groundwork/src/devices/hw/videocore_mbox.rs new file mode 100644 index 00000000..fad36461 --- /dev/null +++ b/10_exceptions_groundwork/src/devices/hw/videocore_mbox.rs @@ -0,0 +1,170 @@ +/* + * 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; + +// The address for buffer needs to be 16-byte aligned so that the Videcore can +// handle it properly. +const MBOX_ALIGNMENT: usize = 16; +const MBOX_SIZE: usize = 36; + +// Public interface to the mailbox +pub struct VideocoreMbox<'a> { + pub buffer: &'a mut [u32], + base_addr: u32, +} + +/// Deref to RegisterBlock +/// +/// Allows writing +/// ``` +/// self.STATUS.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*Mbox::ptr()).STATUS.read() } +/// ``` +impl<'a> ops::Deref for VideocoreMbox<'a> { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl<'a> VideocoreMbox<'a> { + pub fn new(base_addr: u32) -> ::core::result::Result, ()> { + let ret = crate::DMA_ALLOCATOR.lock(|d| d.alloc_slice_zeroed(MBOX_SIZE, MBOX_ALIGNMENT)); + + if ret.is_err() { + return Err(()); + } + + Ok(VideocoreMbox { + base_addr, + buffer: ret.unwrap(), + }) + } + + /// 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/10_exceptions_groundwork/src/devices/virt.rs b/10_exceptions_groundwork/src/devices/virt.rs new file mode 100644 index 00000000..30ed5469 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/devices/virt/console.rs b/10_exceptions_groundwork/src/devices/virt/console.rs new file mode 100644 index 00000000..6972325a --- /dev/null +++ b/10_exceptions_groundwork/src/devices/virt/console.rs @@ -0,0 +1,144 @@ +/* + * 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), + MiniUart(hw::MiniUart), + PL011Uart(hw::PL011Uart), +} + +impl From for Output { + fn from(instance: hw::MiniUart) -> Self { + Output::MiniUart(instance) + } +} + +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::MiniUart(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/10_exceptions_groundwork/src/exception.rs b/10_exceptions_groundwork/src/exception.rs new file mode 100644 index 00000000..85cba9c7 --- /dev/null +++ b/10_exceptions_groundwork/src/exception.rs @@ -0,0 +1,100 @@ +/* + * 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 crate::println; +use cortex_a::{barrier, regs::*}; + +global_asm!(include_str!("vectors.S")); + +pub unsafe fn set_vbar_el1_checked(vec_base_addr: u64) -> bool { + if vec_base_addr.trailing_zeros() < 11 { + false + } else { + cortex_a::regs::VBAR_EL1.set(vec_base_addr); + + // Force VBAR update to complete before next instruction. + barrier::isb(barrier::SY); + + true + } +} + +#[repr(C)] +pub struct GPR { + x: [u64; 31], +} + +#[repr(C)] +pub struct ExceptionContext { + // General Purpose Registers + gpr: GPR, + spsr_el1: u64, + elr_el1: u64, +} + +/// The default exception, invoked for every exception type unless the handler +/// is overwritten. +#[no_mangle] +unsafe extern "C" fn default_exception_handler() { + println!("Unexpected exception. Halting CPU."); + + loop { + cortex_a::asm::wfe() + } +} + +// To implement an exception handler, overwrite it by defining the respective +// function below. +// Don't forget the #[no_mangle] attribute. +// +// unsafe extern "C" fn current_el0_synchronous(e: &mut ExceptionContext); +// unsafe extern "C" fn current_el0_irq(e: &mut ExceptionContext); +// unsafe extern "C" fn current_el0_serror(e: &mut ExceptionContext); + +// unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext); +// unsafe extern "C" fn current_elx_irq(e: &mut ExceptionContext); +// unsafe extern "C" fn current_elx_serror(e: &mut ExceptionContext); + +// unsafe extern "C" fn lower_aarch64_synchronous(e: &mut ExceptionContext); +// unsafe extern "C" fn lower_aarch64_irq(e: &mut ExceptionContext); +// unsafe extern "C" fn lower_aarch64_serror(e: &mut ExceptionContext); + +// unsafe extern "C" fn lower_aarch32_synchronous(e: &mut ExceptionContext); +// unsafe extern "C" fn lower_aarch32_irq(e: &mut ExceptionContext); +// unsafe extern "C" fn lower_aarch32_serror(e: &mut ExceptionContext); + +#[no_mangle] +unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { + println!("[!] A synchronous exception happened."); + println!(" ELR_EL1: {:#010X}", e.elr_el1); + println!( + " Incrementing ELR_EL1 by 4 now to continue with the first \ + instruction after the exception!" + ); + + e.elr_el1 += 4; + + println!(" ELR_EL1 modified: {:#010X}", e.elr_el1); + println!(" Returning from exception...\n"); +} diff --git a/10_exceptions_groundwork/src/macros.rs b/10_exceptions_groundwork/src/macros.rs new file mode 100644 index 00000000..28280be9 --- /dev/null +++ b/10_exceptions_groundwork/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/10_exceptions_groundwork/src/main.rs b/10_exceptions_groundwork/src/main.rs new file mode 100644 index 00000000..efcdd8ee --- /dev/null +++ b/10_exceptions_groundwork/src/main.rs @@ -0,0 +1,184 @@ +/* + * 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(allocator_api)] +#![feature(const_fn)] +#![feature(custom_attribute)] +#![feature(format_args_nl)] +#![feature(global_asm)] +#![feature(label_break_value)] +#![feature(range_contains)] + +mod delays; +mod devices; +mod exception; +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()); + +/// The global allocator for DMA-able memory. That is, memory which is tagged +/// non-cacheable in the page tables. +static DMA_ALLOCATOR: sync::NullLock = + sync::NullLock::new(memory::BumpAllocator::new( + memory::map::DMA_HEAP_START as usize, + memory::map::DMA_HEAP_END as usize, + "Global DMA Allocator", + )); + +fn kernel_entry() -> ! { + use devices::hw; + use devices::virt::ConsoleOps; + + extern "C" { + static __exception_vectors_start: u64; + } + + //------------------------------------------------------------ + // Instantiate GPIO device + //------------------------------------------------------------ + let gpio = hw::GPIO::new(memory::map::GPIO_BASE); + + //------------------------------------------------------------ + // Instantiate MiniUart + //------------------------------------------------------------ + let mini_uart = hw::MiniUart::new(memory::map::MINI_UART_BASE); + mini_uart.init(&gpio); + + CONSOLE.lock(|c| { + // Moves mini_uart into the global CONSOLE. It is not accessible anymore + // for the remaining parts of kernel_entry(). + c.replace_with(mini_uart.into()); + }); + println!("\n[0] MiniUart online."); + + //------------------------------------------------------------ + // 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(); + + // We are now in a state where every next step can fail, but we can handle + // the error with feedback for the user and fall through to our UART + // loopback. + 'init: { + //------------------------------------------------------------ + // Instantiate Videocore Mailbox + //------------------------------------------------------------ + let mut v_mbox; + match hw::VideocoreMbox::new(memory::map::VIDEOCORE_MBOX_BASE) { + Ok(i) => { + println!("[3] Videocore Mailbox set up (DMA mem heap allocation successful)."); + v_mbox = i; + } + + Err(_) => { + println!("[3][Error] Could not set up Videocore Mailbox. Aborting."); + break 'init; + } + } + + //------------------------------------------------------------ + // Instantiate PL011 UART and replace MiniUart with it in CONSOLE + //------------------------------------------------------------ + let pl011_uart = hw::PL011Uart::new(memory::map::PL011_UART_BASE); + + // uart.init() will reconfigure the GPIO, which causes a race against + // the MiniUart that is still putting out characters on the physical + // line that are already buffered in its TX FIFO. + // + // To ensure the CPU doesn't rewire the GPIO before the MiniUart has put + // its last character, explicitly flush it before rewiring. + // + // If you switch to an output that happens to not use the same pair of + // physical wires (e.g. the Framebuffer), you don't need to do this, + // because flush() is anyways called implicitly by replace_with(). This + // is just a special case. + CONSOLE.lock(|c| c.flush()); + match pl011_uart.init(&mut v_mbox, &gpio) { + Ok(_) => { + CONSOLE.lock(|c| { + c.replace_with(pl011_uart.into()); + }); + + println!("[4] PL011 UART online. Output switched to it."); + } + + Err(_) => println!( + "[4][Error] PL011 UART init failed. \ + Trying to continue with MiniUart." + ), + } + + //------------------------------------------------------------ + // Set up exception vectors and cause an exception + //------------------------------------------------------------ + if unsafe { + let exception_vectors_start: u64 = &__exception_vectors_start as *const _ as u64; + + exception::set_vbar_el1_checked(exception_vectors_start) + } { + println!("[5] Exception vectors are set up."); + } else { + println!("[5][Error] Error setting exception vectors. Aborting."); + break 'init; + } + + // Cause an exception by accessing a virtual address for which no + // address translations have been set up. + // + // This line of code accesses the address 3 GiB, but page tables are + // only set up for the range [0..1] GiB. + let big_addr: u64 = 3 * 1024 * 1024 * 1024; + unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; + + println!("[i] Whoa! We recovered from an exception."); + } + + //------------------------------------------------------------ + // Start a command prompt + //------------------------------------------------------------ + CONSOLE.lock(|c| { + c.command_prompt(); + }) +} + +raspi3_boot::entry!(kernel_entry); diff --git a/10_exceptions_groundwork/src/memory.rs b/10_exceptions_groundwork/src/memory.rs new file mode 100644 index 00000000..42535974 --- /dev/null +++ b/10_exceptions_groundwork/src/memory.rs @@ -0,0 +1,133 @@ +/* + * 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; + +mod bump_allocator; +pub use bump_allocator::BumpAllocator; + +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; + + /// The second 2 MiB block. + pub const DMA_HEAP_START: u32 = 0x0020_0000; + pub const DMA_HEAP_END: u32 = 0x005F_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 MINI_UART_BASE: u32 = MMIO_BASE + 0x0021_5000; + + pub const PHYS_ADDR_MAX: u32 = 0x3FFF_FFFF; +} + +const PAGESIZE: u64 = 4096; + +#[inline] +fn aligned_addr_unchecked(addr: usize, alignment: usize) -> usize { + (addr + (alignment - 1)) & !(alignment - 1) +} + +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 | DMA heap pool", + DMA_HEAP_START, + DMA_HEAP_END, + (DMA_HEAP_END - DMA_HEAP_START + 1) >> MIB_RSHIFT + ); + + println!( + " {:#010X} - {:#010X} | {: >4} MiB | Device MMIO", + MMIO_BASE, + PHYS_ADDR_MAX, + (PHYS_ADDR_MAX - MMIO_BASE + 1) >> MIB_RSHIFT + ); +} diff --git a/10_exceptions_groundwork/src/memory/bump_allocator.rs b/10_exceptions_groundwork/src/memory/bump_allocator.rs new file mode 100644 index 00000000..23346056 --- /dev/null +++ b/10_exceptions_groundwork/src/memory/bump_allocator.rs @@ -0,0 +1,100 @@ +/* + * 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; +use core::alloc::{Alloc, AllocErr, Layout}; +use core::mem; +use core::ptr::NonNull; +use core::slice; + +pub struct BumpAllocator { + next: usize, + pool_end: usize, + name: &'static str, +} + +unsafe impl Alloc for BumpAllocator { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + let start = super::aligned_addr_unchecked(self.next, layout.align()); + let end = start + layout.size(); + + if end <= self.pool_end { + self.next = end; + + println!( + "[i] {}:\n Allocated Addr {:#010X} Size {:#X}", + self.name, + start, + layout.size() + ); + + Ok(NonNull::new_unchecked(start as *mut u8)) + } else { + Err(AllocErr) + } + } + + // A bump allocator doesn't care + unsafe fn dealloc(&mut self, _ptr: NonNull, _layout: Layout) {} +} + +impl BumpAllocator { + pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self { + Self { + next: pool_start, + pool_end, + name, + } + } + + /// Allocate a zeroed slice + pub fn alloc_slice_zeroed<'a, T>( + &mut self, + count_of_items: usize, + alignment: usize, + ) -> Result<&'a mut [T], ()> { + let l; + let size_in_byte = count_of_items * mem::size_of::(); + match Layout::from_size_align(size_in_byte, alignment) { + Ok(layout) => l = layout, + + Err(_) => { + println!("[e] Layout Error!"); + return Err(()); + } + } + + let ptr; + match unsafe { self.alloc_zeroed(l) } { + Ok(i) => ptr = i.as_ptr(), + + Err(_) => { + println!("[e] Layout Error!"); + return Err(()); + } + } + + Ok(unsafe { slice::from_raw_parts_mut(ptr as *mut T, count_of_items) }) + } +} diff --git a/10_exceptions_groundwork/src/memory/mmu.rs b/10_exceptions_groundwork/src/memory/mmu.rs new file mode 100644 index 00000000..a26a3f08 --- /dev/null +++ b/10_exceptions_groundwork/src/memory/mmu.rs @@ -0,0 +1,238 @@ +/* + * 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 + // - non-cacheable DRAM + // - cacheable DRAM + // - device memory + // + // Ranges are stored in memory.rs. + // + // 0x0000_0000_0020_0000 | + // |> 4 MiB non-cacheable DRAM + // 0x0000_0000_005F_FFFF | + // 0x0000_0000_0060_0000 | + // |> 1002 MiB cacheable DRAM + // 0x0000_0000_3EFF_FFFF | + // 0x0000_0000_3F00_0000 | + // |> 16 MiB device (MMIO) + // 0x0000_0000_4000_0000 | + let dma_first_block_index: u64 = (crate::memory::map::DMA_HEAP_START >> 21).into(); + let dma_last_block_index: u64 = (crate::memory::map::DMA_HEAP_END >> 21).into(); + 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 (dma_first_block_index..=dma_last_block_index).contains(&j) { + STAGE1_DESCRIPTOR::SH::InnerShareable + + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) + } else 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/10_exceptions_groundwork/src/sync.rs b/10_exceptions_groundwork/src/sync.rs new file mode 100644 index 00000000..55a45e31 --- /dev/null +++ b/10_exceptions_groundwork/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() }) + } +} diff --git a/10_exceptions_groundwork/src/vectors.S b/10_exceptions_groundwork/src/vectors.S new file mode 100644 index 00000000..b2f294c8 --- /dev/null +++ b/10_exceptions_groundwork/src/vectors.S @@ -0,0 +1,113 @@ +// +// 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. +// + +.macro SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE handler +.balign 0x80 + + sub sp, sp, #16 * 17 + + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + + mrs x1, SPSR_EL1 + mrs x2, ELR_EL1 + + stp x30, x1, [sp, #16 * 15] + str x2, [sp, #16 * 16] + + mov x0, sp + bl \handler + b __restore_context +.endm + +.macro FIQ_DUMMY +.balign 0x80 +1: wfe + b 1b +.endm + +// The vector definitions +.section .vectors, "ax" +.global __exception_vectors_start +__exception_vectors_start: + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_synchronous // 0x000 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_irq // 0x080 + FIQ_DUMMY // 0x100 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_serror // 0x180 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_synchronous // 0x200 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_irq // 0x280 + FIQ_DUMMY // 0x300 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_serror // 0x380 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_synchronous // 0x400 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_irq // 0x480 + FIQ_DUMMY // 0x500 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_serror // 0x580 + + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_synchronous // 0x600 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_irq // 0x680 + FIQ_DUMMY // 0x700 + SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_serror // 0x780 + +.global __restore_context +__restore_context: + ldr x19, [sp, #16 * 16] + ldp x30, x20, [sp, #16 * 15] + + msr ELR_EL1, x19 + msr SPSR_EL1, x20 + + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + + add sp, sp, #16 * 17 + + eret