From 4ecddb20deb50cd6e9c338dd6dfdeb6a6974d9ed Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Thu, 27 Dec 2018 21:33:45 +0100 Subject: [PATCH] Add tutorial 0E - Exceptions: Groundwork --- 0E_exceptions_groundwork/.cargo/config | 6 + 0E_exceptions_groundwork/Cargo.lock | 55 ++++ 0E_exceptions_groundwork/Cargo.toml | 13 + 0E_exceptions_groundwork/Makefile | 65 ++++ 0E_exceptions_groundwork/README.md | 301 ++++++++++++++++++ 0E_exceptions_groundwork/kernel8 | Bin 0 -> 76072 bytes 0E_exceptions_groundwork/kernel8.img | Bin 0 -> 6622 bytes 0E_exceptions_groundwork/link.ld | 78 +++++ .../raspi3_boot/Cargo.toml | 10 + .../raspi3_boot/src/lib.rs | 129 ++++++++ 0E_exceptions_groundwork/src/delays.rs | 37 +++ 0E_exceptions_groundwork/src/exception.rs | 119 +++++++ 0E_exceptions_groundwork/src/gpio.rs | 121 +++++++ 0E_exceptions_groundwork/src/main.rs | 95 ++++++ 0E_exceptions_groundwork/src/mbox.rs | 162 ++++++++++ 0E_exceptions_groundwork/src/mmu.rs | 226 +++++++++++++ 0E_exceptions_groundwork/src/uart.rs | 284 +++++++++++++++++ 0E_exceptions_groundwork/src/vectors.S | 109 +++++++ 18 files changed, 1810 insertions(+) create mode 100644 0E_exceptions_groundwork/.cargo/config create mode 100644 0E_exceptions_groundwork/Cargo.lock create mode 100644 0E_exceptions_groundwork/Cargo.toml create mode 100644 0E_exceptions_groundwork/Makefile create mode 100644 0E_exceptions_groundwork/README.md create mode 100755 0E_exceptions_groundwork/kernel8 create mode 100755 0E_exceptions_groundwork/kernel8.img create mode 100644 0E_exceptions_groundwork/link.ld create mode 100644 0E_exceptions_groundwork/raspi3_boot/Cargo.toml create mode 100644 0E_exceptions_groundwork/raspi3_boot/src/lib.rs create mode 100644 0E_exceptions_groundwork/src/delays.rs create mode 100644 0E_exceptions_groundwork/src/exception.rs create mode 100644 0E_exceptions_groundwork/src/gpio.rs create mode 100644 0E_exceptions_groundwork/src/main.rs create mode 100644 0E_exceptions_groundwork/src/mbox.rs create mode 100644 0E_exceptions_groundwork/src/mmu.rs create mode 100644 0E_exceptions_groundwork/src/uart.rs create mode 100644 0E_exceptions_groundwork/src/vectors.S diff --git a/0E_exceptions_groundwork/.cargo/config b/0E_exceptions_groundwork/.cargo/config new file mode 100644 index 00000000..d7fb2ba3 --- /dev/null +++ b/0E_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/0E_exceptions_groundwork/Cargo.lock b/0E_exceptions_groundwork/Cargo.lock new file mode 100644 index 00000000..db059c6f --- /dev/null +++ b/0E_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/0E_exceptions_groundwork/Cargo.toml b/0E_exceptions_groundwork/Cargo.toml new file mode 100644 index 00000000..6d1bfce0 --- /dev/null +++ b/0E_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/0E_exceptions_groundwork/Makefile b/0E_exceptions_groundwork/Makefile new file mode 100644 index 00000000..b3e446c7 --- /dev/null +++ b/0E_exceptions_groundwork/Makefile @@ -0,0 +1,65 @@ +# +# 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. +# + +TARGET = aarch64-unknown-none + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld + +OBJCOPY = cargo objcopy -- +OBJCOPY_PARAMS = --strip-all -O binary + +UTILS_CONTAINER = andrerichter/raspi3-utils +DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work +DOCKER_TTY = --privileged -v /dev:/dev +QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img +RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img + +.PHONY: all qemu raspboot clippy clean objdump nm + +all: clean kernel8.img + +target/$(TARGET)/release/kernel8: $(SOURCES) + cargo xbuild --target=$(TARGET) --release + +kernel8.img: target/$(TARGET)/release/kernel8 + cp $< . + $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img + +qemu: all + $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio + +raspboot: all + $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) + +clippy: + cargo xclippy --target=$(TARGET) + +clean: + cargo clean + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel8 + +nm: + cargo nm --target $(TARGET) -- kernel8 | sort diff --git a/0E_exceptions_groundwork/README.md b/0E_exceptions_groundwork/README.md new file mode 100644 index 00000000..6544dd68 --- /dev/null +++ b/0E_exceptions_groundwork/README.md @@ -0,0 +1,301 @@ +# Tutorial 0E - 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_AND_CALL_HANDLER 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 + b \handler +.endm +``` + +First, a macro for saving the context and branching to follow-up handler code. 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_AND_CALL_HANDLER current_el0_synchronous // 0x000 + SAVE_CONTEXT_AND_CALL_HANDLER current_el0_irq // 0x080 + FIQ_DUMMY // 0x100 + SAVE_CONTEXT_AND_CALL_HANDLER current_el0_serror // 0x180 + + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_synchronous // 0x200 + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_irq // 0x280 + FIQ_DUMMY // 0x300 + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_serror // 0x380 + + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_synchronous // 0x400 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_irq // 0x480 + FIQ_DUMMY // 0x500 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_serror // 0x580 + + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch32_synchronous // 0x600 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch32_irq // 0x680 + FIQ_DUMMY // 0x700 + SAVE_CONTEXT_AND_CALL_HANDLER 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 +#[naked] +#[no_mangle] +unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { + UART.puts("A synchronous exception happened.\n"); + UART.puts(" ELR_EL1: 0x"); + UART.hex(e.elr_el1); + UART.puts( + "\n Incrementing ELR_EL1 by 4 now to continue with the first \ + instruction after the exception!\n", + ); + + e.elr_el1 += 4; + + UART.puts(" ELR_EL1 modified: 0x"); + UART.hex(e.elr_el1); + UART.puts("\n"); + + UART.puts(" Returning from exception...\n\n"); + + exception_return!(); +} +``` + +First of all, we make use of the `#[naked]` attribute to define a [naked function](https://github.com/nox/rust-rfcs/blob/master/tex), which prevents emission of a function prologue and epilogue, which would potentially further modify our already set-up stack. + +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. +Finally, we call the `exception_return!()` Rust macro, which 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 + +In `mmu.rs`, we set the `Access Flag` of the second 2 MiB block to `False`: + +```rust +// The second 2 MiB block. +LVL2_TABLE[1] = (STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Block + + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) + + STAGE1_DESCRIPTOR::AP::RW_EL1 + + STAGE1_DESCRIPTOR::SH::OuterShareable + // Set the Access Flag to false. This will cause a synchronous exception + // when code tries to read from this virtual address. + + STAGE1_DESCRIPTOR::AF::False + + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(1) + + STAGE1_DESCRIPTOR::XN::True) + .value; +``` + +After pointing `VBAR_EL1` to our vector code, + +```rust +exception::set_vbar_el1_checked(exception_vectors_start) +``` + +we cause a data abort exception by reading from said 2 MiB block: + +```rusta +unsafe { core::ptr::read_volatile((2 * 1024 * 1024) as *const u64) }; +``` + +Finally, this triggers our exception code, and returns to the first instruction afterwards: + +```console +ferris@box:~$ make raspboot +[0] UART is live! +[1] Press a key to continue booting... Greetings fellow Rustacean! +[2] Switching MMU on now... MMU is live \o/ +[3] Exception vectors are set up. + +A synchronous exception happened. + ELR_EL1: 0x00000000000807A4 + Incrementing ELR_EL1 by 4 now to continue with the first instruction after the exception! + ELR_EL1 modified: 0x00000000000807A8 + Returning from exception... + +Whoa! We recovered from an exception. +``` diff --git a/0E_exceptions_groundwork/kernel8 b/0E_exceptions_groundwork/kernel8 new file mode 100755 index 0000000000000000000000000000000000000000..5ce5c26eeffbfad2b417bef8b4c9be37432d3398 GIT binary patch literal 76072 zcmeI2e{37)dB>mQNft?2wj@jPk0|EJmQ!0p81YDo5kJfsEeb{U#un$_sC*7{?coB&0N0fFsQ?a;2VB6OQw1xYN& zwdAhhp7(esk+cZZ+l&CgbHM%jzR&Z%&-;0w_wHUw$2z*cXyQ09HWU01a(8A;^6Leg zzp!ZI2QTp82Qw_wY|AwpqdeyaSjx8d)@rAPGw8K5>t$NG?kDJe#yRH(AQ%@pyQlsm z6=8m~5dk7V1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&Ih`|3Z zfmgvhX)E{79tY^zwGFSEVCWED2L*mIo6WASH^CVJgrAsU;^*d?iJ#jZg`p6ZPw}>v z2|oB!m{>D8#e;ZlD?h2-16<&62mi)&)0izCDjN$N=HQG`Umcd6*|sswY;N(8X<9C% zx1r4xUza(Ec1&+NBc!KSg2=Q#LQOj1pJ&(qY?NQd^2(ggUetQAT=VPNke;r1So1U2 zK)Uw?%9@XhzGg*iGRE1xiUfHhx zYV|htKW*F9>tEQR{`#>F^#jir)epDsR6pLiOP$@*seZD5xB8plm(;n#UFzM$6Y76I z{iK@RUY4HX&;68fv;cg(g@bgcF&_OYvb%KhKB0$i>sJ3qS{^&0ck?DeU!t1nKS z3z+fxMe}*3>f383_jG`|5`AZQTjpclCN5uFcBRS!S3cyenUBy9IsD@)fVyMvSKL%$ zeea%kyzp|s1k5(mR}N{>1oML&ym6CT@vpWOPtCl^LC=+{>et5bTs!;KcLS!ce*Yb= z_Ee>*^3+E-?$@}LGv{%fFE>H@Cch%{Azz)jfqE6j`Z~6UvAp#9@4DV)@qED7i7bZK z%Q2ph#{wUrevGS{Ih5XWcL>{Vt*Sg#>-ejex0Cy?gWPf2+Sq|RrwyN**kiK=aQk@Q)(%Ecky<=(1H9e*NF_p9$H-$Q#X|Gv2~J+0-? zs!Rqo6OM-s=g~=%e|%LX-@oq-?f_)p_p3pIlt1*V~Lay&iLNRpuK;eXlNR ze~sEuHhWfU$G-{lw^6fw33b+A+|#SEe2Rm%f_TR^X>*X(`yAR{=PO06j^&S}ZJdAp zO+DUa7%QuXm1%SLl}FOwUN#mOL7#4J3{z>bFZI|Lz`p#8-j|cukLR(E=7*uy=7ynR z)5&#h_2u1n(DxeJOko}w92!u+sq2ix*H5g!ID>h08e?bkCpvSywEMUgm_;Kx6zrN zg7FxRiFUmOZ7LqpY%GtW{~=vRPPdM3Q*U>-t9K5(^(vbi>^WVm-Cg<6+aC+?>YJ7D2II@vy1{hn2iVqr+^^&F(o#R+_2M{pn&v#8@aBx|4`67l9LIP2jv=r@ z&nU)cFx>o*e|#N|_b@UH^0~bC@-TkRR6p~r-(`Dtu$#Serw7ta-ZA6Wgv9|vb%XVW~3)c5*@o>v0@$tZ0AOb{y2oM1x@P8nHYtt)J6@Y774%fCOT-)-vwk^Z8Z8@%Om*LvB0@t=?T-#c3 zZEMA~Z6&U4m*d*D3fHzPRL<|6+<^P16)V;0XJH<{G1J-KW>?$r`}X(QA4L1t?xX#n z{yWF-6YGPpc@X_ScOU(K5bgiPXWV}9KHC30j=$|b`u`xtKYRBxp8xmJ{^xQ0&)rA= zAH?_{`;6NU-beeN$MLse|BX+AeQ|kYP!6Z$h#-%I<-t@e5w{Dwr2bSaepuMPcc0zb zE+j|e;a(*XPYfp)t%P1_a8QoR5xZ5ut)nXt?C5g*q0l;_-FL>riaa34wR-eIAv7wu zgm~gjB3>VXq=5 zlY%7l$)g4Fg%SzY39LcbsmL>-RBIR7LkT5UCMo@+b`8j4Z24FCJ1>;M&1}BLuGf^% zA1k5nWKO`tch}%AIC+BFYTs|)pjuJc9q`*nR&k^U83cNOW9u79makLvnsMS7p6 z<1drdh4Bn(ItQDfP{-daapPcbk^YRX4;JY@9>8Q;tY@=k2XG9}OL0D1LdU-)XeA&N z#`A`uw-)NTesM6!73$+?&tc08^;ByWzzst;>e2PTLsJ&NQO_ZkAh-#5UMhZ13EjvC zb!}lhx%hdwTr@6EYVGjwlOo;7pZ_Y-jr>_rRKJlwEk(MKKYNRGN$VXChl+F~f1WMU z4bGQMh5a(}=VV!--i`e-0SAWtMXjFzKg1N|Op6>4yS5$2@})TcO$q&bCG;Pa(2e+C zDvHy{XINGk|1Yr~mO-yn6zczu@v}TJ^fS5e!K&xKQO_%yKfvSVIeVjB{2QdUO_zw1 z{ULWL4%OK1Qo1ouz2?F=?Ir9D4*nv0V;oaOx=}wA&G$~m0r?pAex1V$x^C3(()Cf~ zQ;iDY;)=j6|H`{MDMMxFJ`$P*)5{uJ|*>Bjsu^n|V(^=#HV zd=&L+%tY`TmbrrG4815Gu&3ma6xj7QKiHK-L`q3u4<(aePmT_xq!6Ba@tY`jl{-V9 zti;Z-8nEY>}c)!a+etFY2Vh>;qdf2+ahks=Z-`po;FvT z+}3fAy{EhL$(>ytL3DF2x_QFxHmBR+aJEXGP+0C*IzVx0*~ubv=C|Y!b0G%t^Hmwe zxIDhLP&guorHB*5i(*_$@^>vQyO);RmX?wIh`mHj-lbi9kV=I6f{J`Nmc$R{q&;#Z za-=URi+WZlQgSfn46+Ydd&oO5;z{(!v?2%VwZoBDE_(6vadV5pg&pt`IgCT<{9&b2f-e z03`+CVMRe=h^aLgQ-%uelQR1iKWpC8sl|wip zE|1uTvo#o05}0j~+(iy2I=j8W=B}RRVW-$UEVedFN$pgW>h{H~w|B-ls-|uo3jNIWPm=k_x%W{f z&3d%#Di^NtzWd*v+3S_<_zL}C|GzPVylpJ+ znq9Kbd}fJ%|JOEST{g8k=l^N$q?hLG7jE!^tqWO*_{Pm``g`uZwYsBV6y(lD-GPTu z#;>1ywkh3zOpl+P9>?AAXYI0W6YdLbYfA2Ls43UJCr@LSMco0meqGA{tnQ!2_jmqQ G?EgP92otIR literal 0 HcmV?d00001 diff --git a/0E_exceptions_groundwork/kernel8.img b/0E_exceptions_groundwork/kernel8.img new file mode 100755 index 0000000000000000000000000000000000000000..4f1f179b0c349de557fe246b2a4f334a1542b916 GIT binary patch literal 6622 zcmeHLZ%kC#6~FI2#(DE##Nq!U`tImzl}1*Eb(gju7(lA9Slj_i6KZi7pE5yb;4vtK zZWuA!YFQhfVQXTWz-~5K-)wEocH1}~+JYvHntkXd&$hs1?PX0IR9(N0W;I-OwcZ_)7Hgtd25_MTLQnSR5^c+~3R*E&o*DOkrv8dX35XzC_41ThicU73elG zIyn{%UOyEbaEaKT5(ic5+c}Y%3Zkdt?6@F@?hCRb<+-F0tA&i*6{OHToI@ObpH4_d z&n@$W>TS6(Sop>{7f+aMsKrDy2Tx{NIr6~-xAL#@%2&rPbEIy>y6U|kzSAaOyW!$r zd;f1-T3<4s+;_nRoR)+2ht3tOyw*u=0*aMc+7rv~%!SLJ>G8_!U z1rzYx54!Gw-oshP_h~h=ZLq!7n%tLG`15lmymg$OshQPG&XHGnTZqwId`jpIi&JZ{ z5Uqz3Cu~(jw?8)OD&P6Z^_}6KGa}82SdxY&MRRe)V%hh*N<6b#6IT^Sbkge*IZM8{ zYL3S0&k%$Wf*LYD5p`CB4{Ig;mO!+r?qq$`!)u0N)(v9?mI*5fOTgl>I4q(Y0-0$f zq#r%-Vxndd*Ng4(nzipQcLa!_1(|6V1LOxc1zgn-2#%d@%6atM$K4kRK z3Emk_wu^wc4Ux)0NknD)*NZF{#oO9-K9PbKXWqKGd*1PX) z)ZShZbamoPEf+*7J@F+Iz7Y8G=eRE?;Kxb$D6S!C@;1`e!Jo)|DbrN@2xo6XCW<;r z#32jxi8!6b;mzK)m&Q?7XMmmc$D@Wn>LPK5E6M5W4{JHyO9m&Wx_MpVg4eUs&${G7 z5*Fz(*8_nIl~599cgUOY;UqUSSw)5>&zlF;tmwPyw&#hSPdWV`z!iZ#`N#DwLvb98 zb}ddj1`H>Bd1)fI#&K05;e?-8A(P#;w)QWOGva<;g`XBHNfQs}rIp~GyoEZIxr~;# z@O@&1Gkja%95QEV8BQW~VtUXeqBcHE3%aD??_UaKWfV!nS(oHt`O;<+X=CvihHVSy z4CkBD95FeLgctH;Jl{sIHv@ZMq#e*iv+q~m*SP?r2#n7GV>K|A#4%<8D0XGhqb&uhO-&yjfbggkw@eTIeOH<>AEDove~*e zEz3k~%^N>FcQO~fXmy^E{z+`fraXtdflSmN>&q>MpTOEL!_WDiglrDzDRDaM$uS`- z|13|!H_J$y9eJASE(fkM;40Op&D&#`67T;_cn^rU`|)18#@=fsoH%|NeK!C6I+_c;6r-8K`4r%)ph5rMK@P8@n|MV%_Z(M}^XEFcsBK%*9{2Q}Rx&Ieo|5?ocz#{x# ziv0IHW&4ebu>UOPUxxpQp9Fh+N@uI$2`Kxi(&Q(f&k=HoIF}6`!)dK%)3mRJ$50stdnOw|2&!D}5eSIjs0%dg4sl)J0#U zKL1e~@KcYU@j4WG)Ej7~fo6ps@T%h-k;)D8~=Lfr=fiW=k1gR?KRMi2YjC6@~5F=>g@?A^73yXb-qOirWVjj=i)NzId9M^ + * + * 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/0E_exceptions_groundwork/raspi3_boot/Cargo.toml b/0E_exceptions_groundwork/raspi3_boot/Cargo.toml new file mode 100644 index 00000000..d7009347 --- /dev/null +++ b/0E_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.4.0" +panic-abort = "0.3.1" +r0 = "0.2.2" diff --git a/0E_exceptions_groundwork/raspi3_boot/src/lib.rs b/0E_exceptions_groundwork/raspi3_boot/src/lib.rs new file mode 100644 index 00000000..4c2de2a7 --- /dev/null +++ b/0E_exceptions_groundwork/raspi3_boot/src/lib.rs @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2018 Jorge Aparicio + * 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. + */ + +#![deny(missing_docs)] +#![deny(warnings)] +#![no_std] + +//! Low-level boot of the Raspberry's processor + +extern crate panic_abort; + +#[macro_export] +macro_rules! entry { + ($path:path) => { + #[export_name = "main"] + pub unsafe fn __main() -> ! { + // type check the given path + let f: fn() -> ! = $path; + + f() + } + }; +} + +/// Reset function. +/// +/// Initializes the bss section before calling into the user's `main()`. +unsafe fn reset() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zeroes the .bss section + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + extern "Rust" { + fn main() -> !; + } + + main() +} + +/// Prepare and execute transition from EL2 to EL1. +#[inline] +fn setup_and_enter_el1_from_el2() -> ! { + use cortex_a::{asm, regs::*}; + + const STACK_START: u64 = 0x80_000; + + // Enable timer counter registers for EL1 + CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); + + // No offset for reading the counters + CNTVOFF_EL2.set(0); + + // Set EL1 execution state to AArch64 + // TODO: Explain the SWIO bit + HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::SWIO::SET); + + // Set up a simulated exception return. + // + // First, fake a saved program status, where all interrupts were + // masked and SP_EL1 was used as a stack pointer. + SPSR_EL2.write( + SPSR_EL2::D::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::M::EL1h, + ); + + // Second, let the link register point to reset(). + ELR_EL2.set(reset as *const () as u64); + + // Set up SP_EL1 (stack pointer), which will be used by EL1 once + // we "return" to it. + SP_EL1.set(STACK_START); + + // Use `eret` to "return" to EL1. This will result in execution of + // `reset()` in EL1. + asm::eret() +} + +/// Entrypoint of the processor. +/// +/// Parks all cores except core0 and checks if we started in EL2. If +/// so, proceeds with setting up EL1. +#[link_section = ".text.boot"] +#[no_mangle] +pub unsafe extern "C" fn _boot_cores() -> ! { + use cortex_a::{asm, regs::*}; + + const CORE_0: u64 = 0; + const CORE_MASK: u64 = 0x3; + const EL2: u32 = CurrentEL::EL::EL2.value; + + if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) { + setup_and_enter_el1_from_el2() + } + + // if not core0 or EL != 2, infinitely wait for events + loop { + asm::wfe(); + } +} diff --git a/0E_exceptions_groundwork/src/delays.rs b/0E_exceptions_groundwork/src/delays.rs new file mode 100644 index 00000000..63414e4f --- /dev/null +++ b/0E_exceptions_groundwork/src/delays.rs @@ -0,0 +1,37 @@ +/* + * 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. + */ + +use cortex_a::asm; + +/* + * + * Using the CPU's cycles + * + */ +/// Wait N CPU cycles (ARM CPU only) +pub fn wait_cycles(cyc: u32) { + for _ in 0..cyc { + asm::nop(); + } +} diff --git a/0E_exceptions_groundwork/src/exception.rs b/0E_exceptions_groundwork/src/exception.rs new file mode 100644 index 00000000..574d49de --- /dev/null +++ b/0E_exceptions_groundwork/src/exception.rs @@ -0,0 +1,119 @@ +/* + * 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. + */ + +use super::UART; +use cortex_a::{asm, 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, +} + +macro_rules! exception_return { + () => { + asm! {"RESTORE_CONTEXT"} + + asm::eret(); + }; +} + +/// The default exception, invoked for every exception type unless the handler +/// is overwritten. +#[naked] +#[no_mangle] +extern "C" fn default_exception_handler() { + UART.puts("Unexpected exception. Halting CPU.\n"); + + loop { + cortex_a::asm::wfe() + } +} + +// To implement an exception handler, overwrite it by defining the respective +// function below. +// Don't forget: +// - The #[naked] attribute +// - The #[no_mangle] attribute +// - The exception_return! macro if suitable. +// +// 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); + +#[naked] +#[no_mangle] +unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { + UART.puts("A synchronous exception happened.\n"); + UART.puts(" ELR_EL1: 0x"); + UART.hex(e.elr_el1); + UART.puts( + "\n Incrementing ELR_EL1 by 4 now to continue with the first \ + instruction after the exception!\n", + ); + + e.elr_el1 += 4; + + UART.puts(" ELR_EL1 modified: 0x"); + UART.hex(e.elr_el1); + UART.puts("\n"); + + UART.puts(" Returning from exception...\n\n"); + + exception_return!(); +} diff --git a/0E_exceptions_groundwork/src/gpio.rs b/0E_exceptions_groundwork/src/gpio.rs new file mode 100644 index 00000000..608ba532 --- /dev/null +++ b/0E_exceptions_groundwork/src/gpio.rs @@ -0,0 +1,121 @@ +/* + * 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. + */ + +use super::MMIO_BASE; +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 + ] + ] +} + +const GPIO_BASE: u32 = MMIO_BASE + 0x200_000; + +#[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; + +impl ops::Deref for GPIO { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*Self::ptr() } + } +} + +impl GPIO { + pub fn new() -> GPIO { + GPIO + } + + /// Returns a pointer to the register block + fn ptr() -> *const RegisterBlock { + GPIO_BASE as *const _ + } +} diff --git a/0E_exceptions_groundwork/src/main.rs b/0E_exceptions_groundwork/src/main.rs new file mode 100644 index 00000000..ea954a0e --- /dev/null +++ b/0E_exceptions_groundwork/src/main.rs @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#![no_std] +#![no_main] +#![feature(asm)] +#![feature(const_fn)] +#![feature(global_asm)] +#![feature(label_break_value)] +#![feature(naked_functions)] + +const MMIO_BASE: u32 = 0x3F00_0000; + +mod delays; +mod exception; +mod gpio; +mod mbox; +mod mmu; +mod uart; + +static UART: uart::Uart = uart::Uart::new(uart::UART_PHYS_BASE); + +fn kernel_entry() -> ! { + extern "C" { + static __exception_vectors_start: u64; + } + + let gpio = gpio::GPIO::new(); + let mut mbox = mbox::Mbox::new(); + + // set up serial console + match UART.init(&mut mbox, &gpio) { + Ok(_) => UART.puts("\n[0] UART is live!\n"), + Err(_) => loop { + cortex_a::asm::wfe() // If UART fails, abort early + }, + } + + UART.puts("[1] Press a key to continue booting... "); + UART.getc(); + UART.puts("Greetings fellow Rustacean!\n"); + + UART.puts("[2] Switching MMU on now... "); + + unsafe { mmu::init() }; + + UART.puts("MMU is live \\o/\n"); + + 'init: { + if unsafe { + let exception_vectors_start: u64 = &__exception_vectors_start as *const _ as u64; + + exception::set_vbar_el1_checked(exception_vectors_start) + } { + UART.puts("[3] Exception vectors are set up.\n\n"); + } else { + UART.puts("[3] Error setting exception vectors. Aborting early.\n"); + break 'init; + } + + // Cause an exception by accessing a virtual address for which we set + // the "Access Flag" to zero in the page tables. + unsafe { core::ptr::read_volatile((2 * 1024 * 1024) as *const u64) }; + + UART.puts("Whoa! We recovered from an exception.\n") + } + + // echo everything back + loop { + UART.send(UART.getc()); + } +} + +raspi3_boot::entry!(kernel_entry); diff --git a/0E_exceptions_groundwork/src/mbox.rs b/0E_exceptions_groundwork/src/mbox.rs new file mode 100644 index 00000000..aeae88bb --- /dev/null +++ b/0E_exceptions_groundwork/src/mbox.rs @@ -0,0 +1,162 @@ +/* + * 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. + */ + +use super::MMIO_BASE; +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) [] + ] +} + +const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; + +#[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 MboxError { + ResponseError, + UnknownError, +} +pub type Result = ::core::result::Result; + +// Channels +pub mod channel { + pub const PROP: u32 = 8; +} + +// Tags +pub mod tag { + pub const SETCLKRATE: u32 = 0x38002; + pub const LAST: u32 = 0; +} + +// Clocks +pub mod clock { + pub const UART: u32 = 0x0_0000_0002; +} + +// Responses +mod response { + pub const SUCCESS: u32 = 0x8000_0000; + pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response) +} + +pub const REQUEST: u32 = 0; + +// Public interface to the mailbox +#[repr(C)] +#[repr(align(16))] +pub struct Mbox { + // The address for buffer needs to be 16-byte aligned so that the + // Videcore can handle it properly. + pub buffer: [u32; 36], +} + +/// Deref to RegisterBlock +/// +/// Allows writing +/// ``` +/// self.STATUS.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*Mbox::ptr()).STATUS.read() } +/// ``` +impl ops::Deref for Mbox { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*Self::ptr() } + } +} + +impl Mbox { + pub fn new() -> Mbox { + Mbox { buffer: [0; 36] } + } + + /// Returns a pointer to the register block + fn ptr() -> *const RegisterBlock { + VIDEOCORE_MBOX 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(MboxError::ResponseError), + _ => Err(MboxError::UnknownError), + }; + } + } + } +} diff --git a/0E_exceptions_groundwork/src/mmu.rs b/0E_exceptions_groundwork/src/mmu.rs new file mode 100644 index 00000000..5cfb24cf --- /dev/null +++ b/0E_exceptions_groundwork/src/mmu.rs @@ -0,0 +1,226 @@ +/* + * 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. + */ + +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; + +static mut LVL2_TABLE: [u64; NUM_ENTRIES_4KIB] = [0; NUM_ENTRIES_4KIB]; +static mut SINGLE_LVL3_TABLE: [u64; NUM_ENTRIES_4KIB] = [0; NUM_ENTRIES_4KIB]; + +/// Set up identity mapped page tables for the first 1 gigabyte 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. + mod mair { + pub const DEVICE: u64 = 0; + pub const NORMAL: u64 = 1; + pub const NORMAL_NON_CACHEABLE: u64 = 2; + } + + // Set up the first LVL2 entry, pointing to a 4KiB table base address. + let lvl3_base: u64 = SINGLE_LVL3_TABLE.base_addr() >> 12; + LVL2_TABLE[0] = (STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Table + + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(lvl3_base)) + .value; + + // The second 2 MiB block. + LVL2_TABLE[1] = (STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Block + + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) + + STAGE1_DESCRIPTOR::AP::RW_EL1 + + STAGE1_DESCRIPTOR::SH::OuterShareable + // Set the Access Flag to false. This will cause a synchronous exception + // when code tries to read from this virtual address. + + STAGE1_DESCRIPTOR::AF::False + + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(1) + + STAGE1_DESCRIPTOR::XN::True) + .value; + + // Fill the rest of the LVL2 (2MiB) entries as block + // descriptors. Differentiate between normal and device mem. + let mmio_base: u64 = (super::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(2). Start at the third 2 MiB DRAM block, which will point + // virtual 0x400000 to physical 0x400000, configured as cacheable memory. + for (i, entry) in LVL2_TABLE.iter_mut().enumerate().skip(2) { + let j: u64 = i as u64; + + let mem_attr = if j >= mmio_base { + 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. + // + // 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 mut __ro_start: u64; + + // The non-inclusive end of the read-only area, aka the address of the + // first byte _after_ the RO area. + static mut __ro_end: u64; + } + + const PAGESIZE: u64 = 4096; + let ro_first_page_index: u64 = &__ro_start as *const _ as u64 / PAGESIZE; + + // Notice the subtraction to calculate the last page index of the RO area + // and not the first page index after the RO area. + let ro_last_page_index: u64 = (&__ro_end as *const _ as u64 / PAGESIZE) - 1; + + 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.iter_mut().enumerate() { + let j: u64 = i as u64; + + let mem_attr = if j < ro_first_page_index || j > ro_last_page_index { + STAGE1_DESCRIPTOR::AP::RW_EL1 + STAGE1_DESCRIPTOR::XN::True + } else { + STAGE1_DESCRIPTOR::AP::RO_EL1 + STAGE1_DESCRIPTOR::XN::False + }; + + *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.base_addr()); + + // Configure various settings of stage 1 of the EL1 translation regime. + let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + TCR_EL1.write( + TCR_EL1::TBI0::Ignored + + TCR_EL1::IPS.val(ips) + + TCR_EL1::TG0::KiB_4 // 4 KiB granule + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::T0SZ.val(34), // Start walks at level 2 + ); + + // Switch the MMU on. + // + // First, force all previous changes to be seen before the MMU is enabled. + barrier::isb(barrier::SY); + + // Enable the MMU and turn on data and instruction caching. + SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); + + // Force MMU init to complete before next instruction + barrier::isb(barrier::SY); +} diff --git a/0E_exceptions_groundwork/src/uart.rs b/0E_exceptions_groundwork/src/uart.rs new file mode 100644 index 00000000..d48e7ba8 --- /dev/null +++ b/0E_exceptions_groundwork/src/uart.rs @@ -0,0 +1,284 @@ +/* + * 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. + */ + +use super::MMIO_BASE; +use crate::delays; +use crate::gpio; +use crate::mbox; +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) [] + ] +} + +pub const UART_PHYS_BASE: u32 = MMIO_BASE + 0x20_1000; + +#[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 UartError { + MailboxError, +} +pub type Result = ::core::result::Result; + +pub struct Uart { + uart_base: u32, +} + +impl ops::Deref for Uart { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl Uart { + pub const fn new(uart_base: u32) -> Uart { + Uart { uart_base } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const RegisterBlock { + self.uart_base as *const _ + } + + ///Set baud rate and characteristics (115200 8N1) and map to GPIO + pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> { + // turn off UART0 + self.CR.set(0); + + // set up clock for consistent divisor values + mbox.buffer[0] = 9 * 4; + mbox.buffer[1] = mbox::REQUEST; + mbox.buffer[2] = mbox::tag::SETCLKRATE; + mbox.buffer[3] = 12; + mbox.buffer[4] = 8; + mbox.buffer[5] = mbox::clock::UART; // UART clock + mbox.buffer[6] = 4_000_000; // 4Mhz + mbox.buffer[7] = 0; // skip turbo setting + mbox.buffer[8] = 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 mbox.call(mbox::channel::PROP).is_err() { + return Err(UartError::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(()) + } + + /// Send a character + pub fn send(&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); + } + + /// Receive a character + pub 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 + } + + /// Display a string + pub fn puts(&self, string: &str) { + for c in string.chars() { + // convert newline to carrige return + newline + if c == '\n' { + self.send('\r') + } + + self.send(c); + } + } + + /// Display a binary value in hexadecimal + pub fn hex(&self, d: u64) { + let mut n; + + for i in 0..16 { + // get highest tetrad + n = d.wrapping_shr(60 - i * 4) & 0xF; + + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + // Add proper offset for ASCII table + if n > 9 { + n += 0x37; + } else { + n += 0x30; + } + + self.send(n as u8 as char); + } + } +} diff --git a/0E_exceptions_groundwork/src/vectors.S b/0E_exceptions_groundwork/src/vectors.S new file mode 100644 index 00000000..58c832b3 --- /dev/null +++ b/0E_exceptions_groundwork/src/vectors.S @@ -0,0 +1,109 @@ +// +// 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. +// + +.macro SAVE_CONTEXT_AND_CALL_HANDLER 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 + b \handler +.endm + +.macro 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 +.endm + +.macro FIQ_DUMMY +.balign 0x80 +1: wfe + b 1b +.endm + +.section .vectors, "ax" +.global __exception_vectors_start +__exception_vectors_start: + SAVE_CONTEXT_AND_CALL_HANDLER current_el0_synchronous // 0x000 + SAVE_CONTEXT_AND_CALL_HANDLER current_el0_irq // 0x080 + FIQ_DUMMY // 0x100 + SAVE_CONTEXT_AND_CALL_HANDLER current_el0_serror // 0x180 + + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_synchronous // 0x200 + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_irq // 0x280 + FIQ_DUMMY // 0x300 + SAVE_CONTEXT_AND_CALL_HANDLER current_elx_serror // 0x380 + + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_synchronous // 0x400 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_irq // 0x480 + FIQ_DUMMY // 0x500 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch64_serror // 0x580 + + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch32_synchronous // 0x600 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch32_irq // 0x680 + FIQ_DUMMY // 0x700 + SAVE_CONTEXT_AND_CALL_HANDLER lower_aarch32_serror // 0x780