diff --git a/.githooks/pre-commit b/.githooks/pre-commit index e7f98f06..1aa4e42e 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,6 +1,10 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require_relative '../utils/copyrighted' +require_relative '../utils/helpers/copyrighted' + +puts "Warning, git pre-commit hooks temporarily disabled!" +exit source_files_exts = ['.S', '.rs', '.rb'] @@ -9,7 +13,6 @@ staged_files = `git --no-pager diff --name-only --cached`.split(/\n/) need_inspection = [] staged_files.each do |f| puts f - exit need_inspection << f if f.include?('Makefile') || source_files_exts.include?(File.extname(f)) end diff --git a/.gitignore b/.gitignore index 94499626..7618b873 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ xbuild_sysroot **/target/* +**/.gdb_history diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..f154b675 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,19 @@ +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md + +Layout/IndentationWidth: + Width: 4 + +Metrics/LineLength: + Max: 100 + +Metrics/MethodLength: + Max: 20 diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..25a74537 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,9 @@ +newline_style = "Unix" +edition = "2018" +merge_imports = true +format_code_in_doc_comments = true +normalize_comments = true +wrap_comments = true +comment_width = 100 +report_fixme = "Always" +report_todo = "Always" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1434c494..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: rust - -notifications: - email: - recipients: - - andre.o.richter@gmail.com - on_success: never - on_failure: change - -addons: - apt: - packages: - - ruby - -rust: - - nightly - -os: - - linux - - osx - -matrix: - allow_failures: - - os: osx - -cache: cargo - -before_script: - - rustup component add rust-src llvm-tools-preview - - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) - - (test -x $HOME/.cargo/bin/cargo-xbuild || cargo install cargo-xbuild) - - (test -x $HOME/.cargo/bin/cargo-objcopy || cargo install cargo-binutils) - - cargo install-update -a - -script: - - ruby utils/make_all.rb diff --git a/01_bareminimum/.cargo/config b/01_bareminimum/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/01_bareminimum/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/01_bareminimum/Cargo.lock b/01_bareminimum/Cargo.lock deleted file mode 100644 index b06d8dbc..00000000 --- a/01_bareminimum/Cargo.lock +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "panic-abort 0.3.1 (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" - -[metadata] -"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" diff --git a/01_bareminimum/Cargo.toml b/01_bareminimum/Cargo.toml deleted file mode 100644 index 26996a7f..00000000 --- a/01_bareminimum/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/01_bareminimum/Makefile b/01_bareminimum/Makefile deleted file mode 100644 index e98a5947..00000000 --- a/01_bareminimum/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -d in_asm - -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/01_bareminimum/README.md b/01_bareminimum/README.md deleted file mode 100644 index cc5237ad..00000000 --- a/01_bareminimum/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# Tutorial 01 - Bare Minimum - -Okay, we're not going to do much here, just test our toolchain. The resulting -kernel8.img should boot on the Raspberry Pi 3, and stop all CPU cores in an -infinite waiting loop. You can check that by running - -```console -ferris@box:~$ make qemu -... some output removed for clearity: ... ----------------- -IN: -0x00080000: d503205f wfe -0x00080004: 17ffffff b #0x80000 -``` - -## Crate setup - -In this tutorial, we are compiling a kernel that is in the end only executing a -single assembly instruction which we program with an assembly file. - -However, since we want to use the toolchain that is delivered with `rustup` as -much as possible, we are already setting up a Rust crate. This allows us to use -`rustc` and LLVM's `lld.ld` linker to process our assembly file. - -## Target - -The Raspberry Pi 3 features a processor that uses ARM's `AArch64` architecture. -Conveniently, Rust already provides a generic target for bare-metal aarch64 code -that we can leverage. It is called [aarch64-unknown-none-softfloat]. - -[aarch64-unknown-none-softfloat]: https://github.com/rust-lang/rust/blob/master/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs - -In the `Makefile`, we select this target in various places by passing it to -cargo using the `--target` cmdline argument. - -Additionally, we provide a config file in `.cargo/config` were we make further -specializations: - -```toml -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] -``` - -The first line tells rustc to use our custom `link.ld` linker script. The -second argument specifies the exact CPU type that is used on Raspberry Pi 3, so -that the compiler can optimize for it. - -The target itself tells the compiler already to not use floating point -operations, which is hinted by the `-softfloat` appendix. This is a common -choice when writing an operating system kernel. If floating point is not -explicitly disabled, it is possible that the compiler uses auto-vectorization to -optimize, for example, operations on array data structures. This would -implicitly result in use of floating point registers and operations. However, -since it is very costly to save and restore floating point registers during -context-switches, use of fp is usually disabled from the get go to save the -cycles and optimize the kernel for fast context switching. - -Since the `aarch64-unknown-none-softfloat` target is not shipped with an associated -precompiled standard library, and since we anyways modify the target via the -`.cargo/config` file, we are using [cargo-xbuild][xbuild] to compile our own -standard library. This way, we can ensure that our bare-metal code is optimized -throughout. - -[xbuild]: https://github.com/rust-osdev/cargo-xbuild - -## Linker script `link.ld` - -We just set the base address where our kernel8.img will be loaded, and we put -the only section we have there, which is `.text.boot`. Important note, for -AArch64 the load address is **0x80_000**, and not **0x80_00** as with AArch32. - -## Makefile - -Our Makefile has a few useful targets: -- `kernel8` compiles the crate either in release or debug mode. For the latter, - add `DEBUG=1` before invoking make, e.g. `DEBUG=1 make` -- `kernel8.img` uses `cargo objcopy` to generate our kernel binary. Citing the [binutils documentation][butils]: - - "_When objcopy generates a raw binary file, it will essentially produce a - memory dump of the contents of the input object file. All symbols and - relocation information will be discarded. The memory dump will start at - the load address of the lowest section copied into the output file._" -- `qemu` loads our kernel into an emulated RPi3, and shows as output the - assembler blocks that are executed. This happens in a docker container. - -[butils]: https://sourceware.org/binutils/docs/binutils/objcopy.html - -## main.rs - -We define the crate to not use the standard library (`#![no_std]`), indicate -that it does not have a main function via `#![no_main]`, and also define a stub -for the `panic_fmt()` handler, which is a requirement for `no_std` crates. We do -this by pulling in the [panic-abort][pa] crate. - -[pa]: https://crates.io/crates/panic-abort - -In summary, we (mis)use `main.rs` as a wrapper to process our assembly file via -`rustc`. The assembly file iself is included with the [global_asm!()][gasm] -macro. - -[gasm]: https://doc.rust-lang.org/unstable-book/language-features/global-asm.html - -## boot_cores.S - -When the control is passed to kernel8.img, the environment is not ready yet for -Rust. Therefore we must implement a small preamble in assembly, no Rust for now. - -All we do is executing [wfe][wfe], an instruction that puts the CPU cores to -sleep until an asynchronous event occurs. If that happens, we jump right back to -`wfe` again. - -[wfe]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDBGCFH.html - -Note that the CPU has 4 cores. All of them will execute the same infinite loop -for now. diff --git a/01_bareminimum/link.ld b/01_bareminimum/link.ld deleted file mode 100644 index 13de9dc4..00000000 --- a/01_bareminimum/link.ld +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/01_bareminimum/src/boot_cores.S b/01_bareminimum/src/boot_cores.S deleted file mode 100644 index 15a1431d..00000000 --- a/01_bareminimum/src/boot_cores.S +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: -1: wfe - b 1b diff --git a/01_bareminimum/src/main.rs b/01_bareminimum/src/main.rs deleted file mode 100644 index 6a5e45dc..00000000 --- a/01_bareminimum/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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(global_asm)] - -extern crate panic_abort; - -global_asm!(include_str!("boot_cores.S")); diff --git a/01_wait_forever/.vscode/settings.json b/01_wait_forever/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/01_wait_forever/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/01_wait_forever/Cargo.lock b/01_wait_forever/Cargo.lock new file mode 100644 index 00000000..3e9c3111 --- /dev/null +++ b/01_wait_forever/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "kernel" +version = "0.1.0" + diff --git a/02_multicore_rust/Cargo.toml b/01_wait_forever/Cargo.toml similarity index 61% rename from 02_multicore_rust/Cargo.toml rename to 01_wait_forever/Cargo.toml index dd59a6da..c0949451 100644 --- a/02_multicore_rust/Cargo.toml +++ b/01_wait_forever/Cargo.toml @@ -1,11 +1,16 @@ [package] -name = "kernel8" +name = "kernel" version = "0.1.0" authors = ["Andre Richter "] edition = "2018" -[dependencies] -raspi3_boot = { path = "raspi3_boot" } - [package.metadata.cargo-xbuild] sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = [] + +[dependencies] + diff --git a/01_wait_forever/Makefile b/01_wait_forever/Makefile new file mode 100644 index 00000000..ec59366e --- /dev/null +++ b/01_wait_forever/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -d in_asm + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/01_wait_forever/README.md b/01_wait_forever/README.md new file mode 100644 index 00000000..5abac1b2 --- /dev/null +++ b/01_wait_forever/README.md @@ -0,0 +1,43 @@ +# Tutorial 01 - Wait Forever + +## tl;dr + +Project skeleton is set up; Code just halts all CPU cores executing the kernel code. + +- Toolchain: `cargo xbuild` tools (`xrustc`, `xclippy`) and the + `aarch64-unknown-none-softfloat` target are used for building `AArch64` + bare-metal code. +- `Makefile` targets: + - `doc`: Generate documentation. + - `qemu`: Run the `kernel` in QEMU + - `clippy` + - `clean` + - `readelf`: Inspect the `ELF` output. + - `objdump`: Inspect the assembly. + - `nm`: Inspect the symbols. +- Code is organized into `kernel`, `arch` and `BSP` (Board Support Package) + parts. + - Conditional compilation includes respective `arch` and `BSP` according to + user-supplied arguments. +- Custom `link.ld` linker script. + - Load address at `0x80_000` + - Only `.text` section. +- `main.rs`: Important [inner attributes]: + - `#![no_std]`, `#![no_main]` +- Assembly `_start()` function that executes `wfe` (Wait For Event), halting all + cores that are executing `_start()`. +- We (have to) define a `#[panic_handler]` function. + - Just waits infinitely for a cpu event. + +[inner attributes]: https://doc.rust-lang.org/reference/attributes.html + +### Give it a try + +In the project folder, invoke QEMU and observe the CPU core spinning on `wfe`: +```console +make qemu +[...] +IN: +0x00080000: d503205f wfe +0x00080004: 17ffffff b #0x80000 +``` diff --git a/01_bareminimum/kernel8 b/01_wait_forever/kernel similarity index 99% rename from 01_bareminimum/kernel8 rename to 01_wait_forever/kernel index 6464a210..34cf93dc 100755 Binary files a/01_bareminimum/kernel8 and b/01_wait_forever/kernel differ diff --git a/01_bareminimum/kernel8.img b/01_wait_forever/kernel8.img similarity index 100% rename from 01_bareminimum/kernel8.img rename to 01_wait_forever/kernel8.img diff --git a/01_wait_forever/src/arch.rs b/01_wait_forever/src/arch.rs new file mode 100644 index 00000000..421e377f --- /dev/null +++ b/01_wait_forever/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(feature = "bsp_rpi3")] +mod aarch64; + +#[cfg(feature = "bsp_rpi3")] +pub use aarch64::*; diff --git a/01_wait_forever/src/arch/aarch64.rs b/01_wait_forever/src/arch/aarch64.rs new file mode 100644 index 00000000..91fc1708 --- /dev/null +++ b/01_wait_forever/src/arch/aarch64.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +global_asm!(include_str!("aarch64/start.S")); + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + unsafe { + loop { + asm!("wfe" :::: "volatile") + } + } +} diff --git a/01_wait_forever/src/arch/aarch64/start.S b/01_wait_forever/src/arch/aarch64/start.S new file mode 100644 index 00000000..23fa4a54 --- /dev/null +++ b/01_wait_forever/src/arch/aarch64/start.S @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +.section ".text._start" + +.global _start + +_start: +1: wfe // Wait for event + b 1b // In case an event happend, jump back to 1 diff --git a/01_wait_forever/src/bsp.rs b/01_wait_forever/src/bsp.rs new file mode 100644 index 00000000..127f99db --- /dev/null +++ b/01_wait_forever/src/bsp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +mod rpi; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi::*; diff --git a/01_wait_forever/src/bsp/rpi.rs b/01_wait_forever/src/bsp/rpi.rs new file mode 100644 index 00000000..bd7cc7b8 --- /dev/null +++ b/01_wait_forever/src/bsp/rpi.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +// Coming soon. diff --git a/01_wait_forever/src/bsp/rpi/link.ld b/01_wait_forever/src/bsp/rpi/link.ld new file mode 100644 index 00000000..837f26d0 --- /dev/null +++ b/01_wait_forever/src/bsp/rpi/link.ld @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/01_wait_forever/src/main.rs b/01_wait_forever/src/main.rs new file mode 100644 index 00000000..81f6c115 --- /dev/null +++ b/01_wait_forever/src/main.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` + +#![feature(asm)] +#![feature(global_asm)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod panic_wait; + +// Kernel code coming next tutorial. diff --git a/01_wait_forever/src/panic_wait.rs b/01_wait_forever/src/panic_wait.rs new file mode 100644 index 00000000..3d8ad6c3 --- /dev/null +++ b/01_wait_forever/src/panic_wait.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + crate::arch::wait_forever() +} diff --git a/02_multicore_rust/.cargo/config b/02_multicore_rust/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/02_multicore_rust/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/02_multicore_rust/Makefile b/02_multicore_rust/Makefile deleted file mode 100644 index e98a5947..00000000 --- a/02_multicore_rust/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -d in_asm - -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/02_multicore_rust/README.md b/02_multicore_rust/README.md deleted file mode 100644 index 798a004c..00000000 --- a/02_multicore_rust/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Tutorial 02 - Multicore Rust - -Now let's try something more complex, shall we? By complex I mean stopping the -CPU cores just like in the first tutorial, but this time stop one of them from -**Rust**! - -## Boot code - -In order to conveniently incorporate Rust code, we are restructuring our crate a -bit. - -We reuse a lot of steps that are explained in great detail in [The -Embedonomicon][nom], so please take your time and read up on it. Afterwards, you -can compare to the files in this crate and see what we actually kept to get our -Raspberry Pi 3 tutorial going. Here's a short summary of the new structure of -the crate: - - - `raspi3_boot/`: The extern crate containing boot code as presented in the - Embedonomicon. - - In a small deviation to the Embedonomicon, `lib.rs` also includes - `boot_cores.S` from the previous tutorial, still with the - [global_asm!][gasm] macro. - - Therefore, `boot_cores.S` has been moved into `raspi3_boot/src/`. - - `src`: Source code of our actual Rust code, currently only containing - `main.rs` executing an endless loop. - -[nom]: https://rust-embedded.github.io/embedonomicon/ -[gasm]: https://doc.rust-lang.org/unstable-book/language-features/global-asm.html - -### Changes to `boot_cores.S` - -In contrast to the previous tutorial, we are now [distinguishing the -cores][dist]. To do so, we read the [mpidr_el1][mpdir] system register. If it is -not zero, we enter the former infinite waiting loop, aka stopping the respective -CPU core. - -If the result of the read from `mpidr_el1` is zero, which means we are executing -on core0, we set up the stack for that core, and afterwards call the Rust -`reset()` function of the boot code in `raspi3_boot/src/lib.rs`. In case the -Rust code returns (which it never should), we also jump to the same infinite -loop the other CPU cores are running. - -The Rust `reset()`, in turn, will then zero-out the `bss section` (the next -section explains what that is) and finally call our `main()` function from -`main.rs`. - -[dist]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/CFHCIDCH.html -[mpdir]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.html - -## Changes to `link.ld` - -Since we are using a high-level language now in the form of Rust, we also take -precautions to have eventual space reserved in memory for the [bss -segment][bss], which is needed in case zero-initialized static variables are -allocated in the Rust code. - -[bss]: https://en.wikipedia.org/wiki/.bss - -Therefore, we added the `bss` segment to the linker script and export its -properties via `__bss_start` and `__bss_size`, which will be picked up and -zeroed out by the boot code in `raspi3_boot/src/lib.rs`. - -Additionally, there is a [data segment][data] now. - -[data]: https://en.wikipedia.org/wiki/Data_segment - -Finally, we need to take care that we still start the text segment with the -assembly code and not the newly added Rust code. This is taken care of by -placing the `.text.boot` section before all other new text sections -`KEEP(*(.text.boot)) *(.text .text.* ...`. - -This way, the assembly stays at the `0x80_000` address, which is the entry point -of the RPi3 CPU. - -## Changes to `Makefile` - -We've added one more target: -- [clippy] is Rust's linter, and can give you useful advise to improve your - code. Invoke with `make clippy`. - -[clippy]: https://github.com/rust-lang-nursery/rust-clippy - -From now on, we can use the same Makefile for every tutorial, regardless of the -number of Rust sources, and we won't discuss it any further. - -## main.rs - -Finally, our first Rust code. Just an empty loop, but still! :-) diff --git a/02_multicore_rust/kernel8.img b/02_multicore_rust/kernel8.img deleted file mode 100755 index 1bbbbc18..00000000 Binary files a/02_multicore_rust/kernel8.img and /dev/null differ diff --git a/02_multicore_rust/link.ld b/02_multicore_rust/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/02_multicore_rust/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/02_multicore_rust/raspi3_boot/Cargo.toml b/02_multicore_rust/raspi3_boot/Cargo.toml deleted file mode 100644 index 1c3c1c69..00000000 --- a/02_multicore_rust/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/02_multicore_rust/raspi3_boot/src/boot_cores.S b/02_multicore_rust/raspi3_boot/src/boot_cores.S deleted file mode 100644 index f6f28af6..00000000 --- a/02_multicore_rust/raspi3_boot/src/boot_cores.S +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: - // read cpu id, stop slave cores - mrs x1, mpidr_el1 - and x1, x1, #3 - cbz x1, 2f - // cpu id > 0, stop -1: wfe - b 1b -2: // cpu id == 0 - - // set stack before our code - ldr x1, =_boot_cores - mov sp, x1 - - // jump to Rust code, should not return - bl reset - // for failsafe, halt this core too - b 1b diff --git a/02_multicore_rust/raspi3_boot/src/lib.rs b/02_multicore_rust/raspi3_boot/src/lib.rs deleted file mode 100644 index a185d0a9..00000000 --- a/02_multicore_rust/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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] -#![feature(global_asm)] - -//! 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -#[no_mangle] -pub unsafe extern "C" 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(); -} - -// Disable all cores except core 0, and then jump to reset() -global_asm!(include_str!("boot_cores.S")); diff --git a/02_multicore_rust/src/main.rs b/02_multicore_rust/src/main.rs deleted file mode 100644 index 08a6b7e4..00000000 --- a/02_multicore_rust/src/main.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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] - -fn kernel_entry() -> ! { - loop {} -} - -raspi3_boot::entry!(kernel_entry); diff --git a/02_runtime_init/.vscode/settings.json b/02_runtime_init/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/02_runtime_init/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/02_multicore_rust/Cargo.lock b/02_runtime_init/Cargo.lock similarity index 50% rename from 02_multicore_rust/Cargo.lock rename to 02_runtime_init/Cargo.lock index 69f37748..551acef0 100644 --- a/02_multicore_rust/Cargo.lock +++ b/02_runtime_init/Cargo.lock @@ -1,30 +1,16 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "kernel8" +name = "kernel" version = "0.1.0" dependencies = [ - "raspi3_boot 0.1.0", + "r0 0.2.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 = [ - "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)", -] - [metadata] -"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" diff --git a/04_mailboxes/Cargo.toml b/02_runtime_init/Cargo.toml similarity index 59% rename from 04_mailboxes/Cargo.toml rename to 02_runtime_init/Cargo.toml index a14dbb2d..da487f1a 100644 --- a/04_mailboxes/Cargo.toml +++ b/02_runtime_init/Cargo.toml @@ -1,12 +1,16 @@ [package] -name = "kernel8" +name = "kernel" version = "0.1.0" authors = ["Andre Richter "] edition = "2018" -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -register = "0.3.3" - [package.metadata.cargo-xbuild] sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = [] + +[dependencies] +r0 = "0.2.*" diff --git a/02_runtime_init/Makefile b/02_runtime_init/Makefile new file mode 100644 index 00000000..ec59366e --- /dev/null +++ b/02_runtime_init/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -d in_asm + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/02_runtime_init/README.md b/02_runtime_init/README.md new file mode 100644 index 00000000..3907ef38 --- /dev/null +++ b/02_runtime_init/README.md @@ -0,0 +1,137 @@ +# Tutorial 02 - Runtime Init + +## tl;dr + +We are calling into Rust code for the first time and zero the [bss](https://en.wikipedia.org/wiki/.bss) section. +Check out `make qemu` again to see the additional code run. + +- More sections in linker script: + - `.rodata`, `.data` + - `.bss` +- `_start()`: + - Halt core if core != `core0`. + - `core0` jumps to `init()` Rust function. +- `init()` in `runtime.rs` + - Zeros the `.bss` section. + - Calls `kernel_init()`, which calls `panic!()`, which eventually halts + `core0` as well. + +## Diff to previous +```diff + +diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml +--- 01_wait_forever/Cargo.toml ++++ 02_runtime_init/Cargo.toml +@@ -13,4 +13,4 @@ + bsp_rpi3 = [] + + [dependencies] +- ++r0 = "0.2.*" + +diff -uNr 01_wait_forever/src/arch/aarch64/start.S 02_runtime_init/src/arch/aarch64/start.S +--- 01_wait_forever/src/arch/aarch64/start.S ++++ 02_runtime_init/src/arch/aarch64/start.S +@@ -7,5 +7,15 @@ + .global _start + + _start: +-1: wfe // Wait for event +- b 1b // In case an event happend, jump back to 1 ++ mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register ++ and x1, x1, #3 // Clear all bits except [1:0], which hold core id ++ cbz x1, 2f // Jump to label 2 if we are core 0 ++1: wfe // Wait for event ++ b 1b // In case an event happend, jump back to 1 ++2: // If we are here, we are core0 ++ ldr x1, =_start // Load address of function "_start()" ++ mov sp, x1 // Set start of stack to before our code, aka first ++ // address before "_start()" ++ bl init // Jump to the "init()" kernel function ++ b 1b // We should never reach here. But just in case, ++ // park this core aswell + +diff -uNr 01_wait_forever/src/bsp/rpi/link.ld 02_runtime_init/src/bsp/rpi/link.ld +--- 01_wait_forever/src/bsp/rpi/link.ld ++++ 02_runtime_init/src/bsp/rpi/link.ld +@@ -13,5 +13,23 @@ + *(.text._start) *(.text*) + } + ++ .rodata : ++ { ++ *(.rodata*) ++ } ++ ++ .data : ++ { ++ *(.data*) ++ } ++ ++ /* Align to 8 byte boundary */ ++ .bss ALIGN(8): ++ { ++ __bss_start = .; ++ *(.bss*); ++ __bss_end = .; ++ } ++ + /DISCARD/ : { *(.comment*) } + } + +diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs +--- 01_wait_forever/src/main.rs ++++ 02_runtime_init/src/main.rs +@@ -16,9 +16,19 @@ + // the first function to run. + mod arch; + ++// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. ++mod runtime_init; ++ + // Conditionally includes the selected `BSP` code. + mod bsp; + + mod panic_wait; + +-// Kernel code coming next tutorial. ++/// Early init code. ++/// ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++unsafe fn kernel_init() -> ! { ++ panic!() ++} + +diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.rs +--- 01_wait_forever/src/runtime_init.rs ++++ 02_runtime_init/src/runtime_init.rs +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Rust runtime initialization code. ++ ++/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel ++/// init code. ++/// ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++#[no_mangle] ++pub unsafe extern "C" fn init() -> ! { ++ extern "C" { ++ // Boundaries of the .bss section, provided by the linker script. ++ static mut __bss_start: u64; ++ static mut __bss_end: u64; ++ } ++ ++ // Zero out the .bss section. ++ r0::zero_bss(&mut __bss_start, &mut __bss_end); ++ ++ crate::kernel_init() ++} + +``` diff --git a/02_multicore_rust/kernel8 b/02_runtime_init/kernel old mode 100755 new mode 100644 similarity index 95% rename from 02_multicore_rust/kernel8 rename to 02_runtime_init/kernel index e9d90d01..0e9baf28 Binary files a/02_multicore_rust/kernel8 and b/02_runtime_init/kernel differ diff --git a/02_runtime_init/kernel8.img b/02_runtime_init/kernel8.img new file mode 100755 index 00000000..633cf1dc Binary files /dev/null and b/02_runtime_init/kernel8.img differ diff --git a/02_runtime_init/src/arch.rs b/02_runtime_init/src/arch.rs new file mode 100644 index 00000000..421e377f --- /dev/null +++ b/02_runtime_init/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(feature = "bsp_rpi3")] +mod aarch64; + +#[cfg(feature = "bsp_rpi3")] +pub use aarch64::*; diff --git a/02_runtime_init/src/arch/aarch64.rs b/02_runtime_init/src/arch/aarch64.rs new file mode 100644 index 00000000..91fc1708 --- /dev/null +++ b/02_runtime_init/src/arch/aarch64.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +global_asm!(include_str!("aarch64/start.S")); + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + unsafe { + loop { + asm!("wfe" :::: "volatile") + } + } +} diff --git a/02_runtime_init/src/arch/aarch64/start.S b/02_runtime_init/src/arch/aarch64/start.S new file mode 100644 index 00000000..41395e3b --- /dev/null +++ b/02_runtime_init/src/arch/aarch64/start.S @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +.section ".text._start" + +.global _start + +_start: + mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register + and x1, x1, #3 // Clear all bits except [1:0], which hold core id + cbz x1, 2f // Jump to label 2 if we are core 0 +1: wfe // Wait for event + b 1b // In case an event happend, jump back to 1 +2: // If we are here, we are core0 + ldr x1, =_start // Load address of function "_start()" + mov sp, x1 // Set start of stack to before our code, aka first + // address before "_start()" + bl init // Jump to the "init()" kernel function + b 1b // We should never reach here. But just in case, + // park this core aswell diff --git a/02_runtime_init/src/bsp.rs b/02_runtime_init/src/bsp.rs new file mode 100644 index 00000000..127f99db --- /dev/null +++ b/02_runtime_init/src/bsp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +mod rpi; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi::*; diff --git a/02_runtime_init/src/bsp/rpi.rs b/02_runtime_init/src/bsp/rpi.rs new file mode 100644 index 00000000..bd7cc7b8 --- /dev/null +++ b/02_runtime_init/src/bsp/rpi.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +// Coming soon. diff --git a/02_runtime_init/src/bsp/rpi/link.ld b/02_runtime_init/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/02_runtime_init/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/02_runtime_init/src/main.rs b/02_runtime_init/src/main.rs new file mode 100644 index 00000000..b7141947 --- /dev/null +++ b/02_runtime_init/src/main.rs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` + +#![feature(asm)] +#![feature(global_asm)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod panic_wait; + +/// Early init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +unsafe fn kernel_init() -> ! { + panic!() +} diff --git a/02_runtime_init/src/panic_wait.rs b/02_runtime_init/src/panic_wait.rs new file mode 100644 index 00000000..3d8ad6c3 --- /dev/null +++ b/02_runtime_init/src/panic_wait.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + crate::arch::wait_forever() +} diff --git a/02_runtime_init/src/runtime_init.rs b/02_runtime_init/src/runtime_init.rs new file mode 100644 index 00000000..009dac0d --- /dev/null +++ b/02_runtime_init/src/runtime_init.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +#[no_mangle] +pub unsafe extern "C" fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/03_hacky_hello_world/.vscode/settings.json b/03_hacky_hello_world/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/03_hacky_hello_world/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/03_hacky_hello_world/Cargo.lock b/03_hacky_hello_world/Cargo.lock new file mode 100644 index 00000000..551acef0 --- /dev/null +++ b/03_hacky_hello_world/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "r0 0.2.2 (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" + +[metadata] +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" diff --git a/03_uart1/Cargo.toml b/03_hacky_hello_world/Cargo.toml similarity index 59% rename from 03_uart1/Cargo.toml rename to 03_hacky_hello_world/Cargo.toml index a14dbb2d..da487f1a 100644 --- a/03_uart1/Cargo.toml +++ b/03_hacky_hello_world/Cargo.toml @@ -1,12 +1,16 @@ [package] -name = "kernel8" +name = "kernel" version = "0.1.0" authors = ["Andre Richter "] edition = "2018" -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -register = "0.3.3" - [package.metadata.cargo-xbuild] sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = [] + +[dependencies] +r0 = "0.2.*" diff --git a/03_hacky_hello_world/Makefile b/03_hacky_hello_world/Makefile new file mode 100644 index 00000000..e80eb9d3 --- /dev/null +++ b/03_hacky_hello_world/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/03_hacky_hello_world/README.md b/03_hacky_hello_world/README.md new file mode 100644 index 00000000..c6b173b0 --- /dev/null +++ b/03_hacky_hello_world/README.md @@ -0,0 +1,232 @@ +# Tutorial 03 - Hacky Hello World + +## tl;dr + +Introducing global `print!()` macros to enable "printf debugging" at the +earliest; To keep tutorial length reasonable, printing functions for now "abuse" a +QEMU property that lets us use the RPi's `UART` without setting it up properly; +Using the real hardware `UART` is enabled step-by-step in following tutorials. + +- `interface.rs` is introduced: + - Provides `Traits` for abstracting `kernel` from `BSP` and `arch` code. +- Panic handler `print!()`s supplied error messages. + - This is showcased in `main()`. + +### Give it a try + +QEMU is no longer run in assembly mode. It will from now on show the output of `UART0`. + +```console +make qemu +[...] +Hello from Rust! +Kernel panic: Stopping here. +``` + +## Diff to previous +```diff + +diff -uNr 02_runtime_init/Makefile 03_hacky_hello_world/Makefile +--- 02_runtime_init/Makefile ++++ 03_hacky_hello_world/Makefile +@@ -13,7 +13,7 @@ + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 +- QEMU_MISC_ARGS = -d in_asm ++ QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + endif + +diff -uNr 02_runtime_init/src/bsp/rpi.rs 03_hacky_hello_world/src/bsp/rpi.rs +--- 02_runtime_init/src/bsp/rpi.rs ++++ 03_hacky_hello_world/src/bsp/rpi.rs +@@ -4,4 +4,35 @@ + + //! Board Support Package for the Raspberry Pi. + +-// Coming soon. ++use crate::interface; ++use core::fmt; ++ ++/// A mystical, magical device for generating QEMU output out of the void. ++struct QEMUOutput; ++ ++/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used ++/// to implement the `kernel`'s `print!` and `println!` macros. ++/// ++/// See [`src/print.rs`]. ++/// ++/// [`src/print.rs`]: ../../print/index.html ++impl interface::console::Write for QEMUOutput { ++ fn write_str(&mut self, s: &str) -> fmt::Result { ++ for c in s.chars() { ++ unsafe { ++ core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); ++ } ++ } ++ ++ Ok(()) ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// Implementation of the kernel's BSP calls ++//-------------------------------------------------------------------------------------------------- ++ ++/// Returns a ready-to-use `console::Write` implementation. ++pub fn console() -> impl interface::console::Write { ++ QEMUOutput {} ++} + +diff -uNr 02_runtime_init/src/interface.rs 03_hacky_hello_world/src/interface.rs +--- 02_runtime_init/src/interface.rs ++++ 03_hacky_hello_world/src/interface.rs +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Trait definitions for coupling `kernel` and `BSP` code. ++//! ++//! ``` ++//! +-------------------+ ++//! | Interface (Trait) | ++//! | | ++//! +--+-------------+--+ ++//! ^ ^ ++//! | | ++//! | | ++//! +----------+--+ +--+----------+ ++//! | Kernel code | | BSP Code | ++//! | | | | ++//! +-------------+ +-------------+ ++//! ``` ++ ++/// System console operations. ++pub mod console { ++ /// Console write functions. ++ /// ++ /// `core::fmt::Write` is exactly what we need for now. Re-export it here because ++ /// implementing `console::Write` gives a better hint to the reader about the ++ /// intention. ++ pub use core::fmt::Write; ++ ++ /// Console read functions. ++ pub trait Read { ++ fn read_char(&mut self) -> char { ++ ' ' ++ } ++ } ++} + +diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs +--- 02_runtime_init/src/main.rs ++++ 03_hacky_hello_world/src/main.rs +@@ -6,9 +6,23 @@ + #![doc(html_logo_url = "https://git.io/JeGIp")] + + //! The `kernel` ++//! ++//! The `kernel` is composed by glueing together code from ++//! ++//! - [Hardware-specific Board Support Packages] (`BSPs`). ++//! - [Architecture-specific code]. ++//! - HW- and architecture-agnostic `kernel` code. ++//! ++//! using the [`kernel::interface`] traits. ++//! ++//! [Hardware-specific Board Support Packages]: bsp/index.html ++//! [Architecture-specific code]: arch/index.html ++//! [`kernel::interface`]: interface/index.html + + #![feature(asm)] ++#![feature(format_args_nl)] + #![feature(global_asm)] ++#![feature(panic_info_message)] + #![no_main] + #![no_std] + +@@ -22,7 +36,9 @@ + // Conditionally includes the selected `BSP` code. + mod bsp; + ++mod interface; + mod panic_wait; ++mod print; + + /// Early init code. + /// +@@ -30,5 +46,7 @@ + /// + /// - Only a single core must be active and running this function. + unsafe fn kernel_init() -> ! { +- panic!() ++ println!("Hello from Rust!"); ++ ++ panic!("Stopping here.") + } + +diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.rs +--- 02_runtime_init/src/panic_wait.rs ++++ 03_hacky_hello_world/src/panic_wait.rs +@@ -4,9 +4,16 @@ + + //! A panic handler that infinitely waits. + ++use crate::{arch, println}; + use core::panic::PanicInfo; + + #[panic_handler] +-fn panic(_info: &PanicInfo) -> ! { +- crate::arch::wait_forever() ++fn panic(info: &PanicInfo) -> ! { ++ if let Some(args) = info.message() { ++ println!("Kernel panic: {}", args); ++ } else { ++ println!("Kernel panic!"); ++ } ++ ++ arch::wait_forever() + } + +diff -uNr 02_runtime_init/src/print.rs 03_hacky_hello_world/src/print.rs +--- 02_runtime_init/src/print.rs ++++ 03_hacky_hello_world/src/print.rs +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Printing facilities. ++ ++use crate::{bsp, interface}; ++use core::fmt; ++ ++/// Prints without a newline. ++/// ++/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html ++#[macro_export] ++macro_rules! print { ++ ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); ++} ++ ++/// Prints with a newline. ++/// ++/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html ++#[macro_export] ++macro_rules! println { ++ () => ($crate::print!("\n")); ++ ($($arg:tt)*) => ({ ++ $crate::print::_print(format_args_nl!($($arg)*)); ++ }) ++} ++ ++pub fn _print(args: fmt::Arguments) { ++ use interface::console::Write; ++ ++ bsp::console().write_fmt(args).unwrap(); ++} + +``` diff --git a/07_abstraction/kernel8 b/03_hacky_hello_world/kernel similarity index 82% rename from 07_abstraction/kernel8 rename to 03_hacky_hello_world/kernel index ea343760..80375823 100755 Binary files a/07_abstraction/kernel8 and b/03_hacky_hello_world/kernel differ diff --git a/03_hacky_hello_world/kernel8.img b/03_hacky_hello_world/kernel8.img new file mode 100755 index 00000000..5e10e7c5 Binary files /dev/null and b/03_hacky_hello_world/kernel8.img differ diff --git a/03_hacky_hello_world/src/arch.rs b/03_hacky_hello_world/src/arch.rs new file mode 100644 index 00000000..421e377f --- /dev/null +++ b/03_hacky_hello_world/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(feature = "bsp_rpi3")] +mod aarch64; + +#[cfg(feature = "bsp_rpi3")] +pub use aarch64::*; diff --git a/03_hacky_hello_world/src/arch/aarch64.rs b/03_hacky_hello_world/src/arch/aarch64.rs new file mode 100644 index 00000000..91fc1708 --- /dev/null +++ b/03_hacky_hello_world/src/arch/aarch64.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +global_asm!(include_str!("aarch64/start.S")); + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + unsafe { + loop { + asm!("wfe" :::: "volatile") + } + } +} diff --git a/03_hacky_hello_world/src/arch/aarch64/start.S b/03_hacky_hello_world/src/arch/aarch64/start.S new file mode 100644 index 00000000..41395e3b --- /dev/null +++ b/03_hacky_hello_world/src/arch/aarch64/start.S @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +.section ".text._start" + +.global _start + +_start: + mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register + and x1, x1, #3 // Clear all bits except [1:0], which hold core id + cbz x1, 2f // Jump to label 2 if we are core 0 +1: wfe // Wait for event + b 1b // In case an event happend, jump back to 1 +2: // If we are here, we are core0 + ldr x1, =_start // Load address of function "_start()" + mov sp, x1 // Set start of stack to before our code, aka first + // address before "_start()" + bl init // Jump to the "init()" kernel function + b 1b // We should never reach here. But just in case, + // park this core aswell diff --git a/03_hacky_hello_world/src/bsp.rs b/03_hacky_hello_world/src/bsp.rs new file mode 100644 index 00000000..127f99db --- /dev/null +++ b/03_hacky_hello_world/src/bsp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +mod rpi; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi::*; diff --git a/03_hacky_hello_world/src/bsp/rpi.rs b/03_hacky_hello_world/src/bsp/rpi.rs new file mode 100644 index 00000000..504c5990 --- /dev/null +++ b/03_hacky_hello_world/src/bsp/rpi.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +use crate::interface; +use core::fmt; + +/// A mystical, magical device for generating QEMU output out of the void. +struct QEMUOutput; + +/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used +/// to implement the `kernel`'s `print!` and `println!` macros. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl interface::console::Write for QEMUOutput { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + unsafe { + core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); + } + } + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Returns a ready-to-use `console::Write` implementation. +pub fn console() -> impl interface::console::Write { + QEMUOutput {} +} diff --git a/03_hacky_hello_world/src/bsp/rpi/link.ld b/03_hacky_hello_world/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/03_hacky_hello_world/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/03_hacky_hello_world/src/interface.rs b/03_hacky_hello_world/src/interface.rs new file mode 100644 index 00000000..79d74a5f --- /dev/null +++ b/03_hacky_hello_world/src/interface.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + /// Console write functions. + /// + /// `core::fmt::Write` is exactly what we need for now. Re-export it here because + /// implementing `console::Write` gives a better hint to the reader about the + /// intention. + pub use core::fmt::Write; + + /// Console read functions. + pub trait Read { + fn read_char(&mut self) -> char { + ' ' + } + } +} diff --git a/03_hacky_hello_world/src/main.rs b/03_hacky_hello_world/src/main.rs new file mode 100644 index 00000000..0416783c --- /dev/null +++ b/03_hacky_hello_world/src/main.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(asm)] +#![feature(format_args_nl)] +#![feature(global_asm)] +#![feature(panic_info_message)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +unsafe fn kernel_init() -> ! { + println!("Hello from Rust!"); + + panic!("Stopping here.") +} diff --git a/03_hacky_hello_world/src/panic_wait.rs b/03_hacky_hello_world/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/03_hacky_hello_world/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/03_hacky_hello_world/src/print.rs b/03_hacky_hello_world/src/print.rs new file mode 100644 index 00000000..ce6587b5 --- /dev/null +++ b/03_hacky_hello_world/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/03_hacky_hello_world/src/runtime_init.rs b/03_hacky_hello_world/src/runtime_init.rs new file mode 100644 index 00000000..009dac0d --- /dev/null +++ b/03_hacky_hello_world/src/runtime_init.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +#[no_mangle] +pub unsafe extern "C" fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/03_uart1/.cargo/config b/03_uart1/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/03_uart1/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/03_uart1/Makefile b/03_uart1/Makefile deleted file mode 100644 index 56649ff8..00000000 --- a/03_uart1/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial null -serial stdio - -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/03_uart1/README.md b/03_uart1/README.md deleted file mode 100644 index 979892cc..00000000 --- a/03_uart1/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Tutorial 03 - UART1, Auxilary mini UART - -It is time for the famous Hello World example. We're going to write on the UART1 -first, as it is easier to program, since it has a fixed clocked frequency. - -## gpio.rs - -We have a new file that defines the GPIO controller addresses. It is going to be -very popular, as many device will need it in the future. - -We are using the [register][register] crate to modify MMIO addresses, because it -allows easy wrapping of addresses to volatile types. It will also be used for -UART registers. - -[register]: https://crates.io/crates/register - -## uart.rs - -A very minimal implementation. - -`MiniUart::init(&self)` initializes the device and maps it to the GPIO ports. - -`MiniUart::send(&self, c: char)` sends a character over the serial line. - -`MiniUart::getc(&self)` receives a character. The carrige return character (13) -will be converted into a newline character (10). - -`MiniUart::puts(&self, string: &str)` prints out a string. On newline, a carrige -return character will also be sent (13 + 10). - -## main.rs - -First, we have to call the uart initialization code. Then we wait for the first -keypress from the user before we say "Hello Rustacean!". If you've purchased an -USB serial cable, you should see it on `screen`'s screen. After that, every -character typed in `screen` will be echoed back. If you haven't turned off local -echo, that means you'll see every pressed key twice. - -## Simulation - -We can also use `QEMU` to simulate the UART output of our bare-metal binary on -the host PC. - -```console -ferris@box:~$ make qemu -[0] UART is live! -[1] Press a key to continue booting... Greetings fellow Rustacean! -``` - -However, let it be said that it is more thrilling to see your first output from -the real hardware target, so don't be shy and grab a USB-serial. diff --git a/03_uart1/kernel8 b/03_uart1/kernel8 deleted file mode 100755 index dd158f90..00000000 Binary files a/03_uart1/kernel8 and /dev/null differ diff --git a/03_uart1/kernel8.img b/03_uart1/kernel8.img deleted file mode 100755 index e1b6aa21..00000000 Binary files a/03_uart1/kernel8.img and /dev/null differ diff --git a/03_uart1/link.ld b/03_uart1/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/03_uart1/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/03_uart1/raspi3_boot/Cargo.toml b/03_uart1/raspi3_boot/Cargo.toml deleted file mode 100644 index 1c3c1c69..00000000 --- a/03_uart1/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/03_uart1/raspi3_boot/src/boot_cores.S b/03_uart1/raspi3_boot/src/boot_cores.S deleted file mode 100644 index f6f28af6..00000000 --- a/03_uart1/raspi3_boot/src/boot_cores.S +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: - // read cpu id, stop slave cores - mrs x1, mpidr_el1 - and x1, x1, #3 - cbz x1, 2f - // cpu id > 0, stop -1: wfe - b 1b -2: // cpu id == 0 - - // set stack before our code - ldr x1, =_boot_cores - mov sp, x1 - - // jump to Rust code, should not return - bl reset - // for failsafe, halt this core too - b 1b diff --git a/03_uart1/raspi3_boot/src/lib.rs b/03_uart1/raspi3_boot/src/lib.rs deleted file mode 100644 index a185d0a9..00000000 --- a/03_uart1/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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] -#![feature(global_asm)] - -//! 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -#[no_mangle] -pub unsafe extern "C" 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(); -} - -// Disable all cores except core 0, and then jump to reset() -global_asm!(include_str!("boot_cores.S")); diff --git a/03_uart1/src/gpio.rs b/03_uart1/src/gpio.rs deleted file mode 100644 index 408af5ab..00000000 --- a/03_uart1/src/gpio.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 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, - RXD1 = 0b010 // Mini UART - Alternate function 5 - - ], - - /// Pin 14 - FSEL14 OFFSET(12) NUMBITS(3) [ - Input = 0b000, - Output = 0b001, - 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/03_uart1/src/main.rs b/03_uart1/src/main.rs deleted file mode 100644 index 6feec605..00000000 --- a/03_uart1/src/main.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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)] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod uart; - -fn kernel_entry() -> ! { - let uart = uart::MiniUart::new(); - - // set up serial console - uart.init(); - uart.puts("\n[0] UART is live!\n"); - - uart.puts("[1] Press a key to continue booting... "); - uart.getc(); - uart.puts("Greetings fellow Rustacean!\n"); - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/03_uart1/src/uart.rs b/03_uart1/src/uart.rs deleted file mode 100644 index bca57093..00000000 --- a/03_uart1/src/uart.rs +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 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::MMIO_BASE; -use crate::gpio; -use core::ops; -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 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) [] - ] -} - -const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000; - -#[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; - -/// 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() -> MiniUart { - MiniUart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - MINI_UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self) { - // 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 - unsafe { - (*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" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).write( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).set(0); - } - - self.AUX_MU_CNTL - .write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled); - } - - /// Send a character - pub fn send(&self, c: char) { - // wait until we can send - loop { - if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) { - break; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // write the character to the buffer - self.AUX_MU_IO.set(c as u32); - } - - /// Receive a character - pub 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // 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 - } - - /// 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); - } - } -} diff --git a/04_mailboxes/.cargo/config b/04_mailboxes/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/04_mailboxes/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/04_mailboxes/Makefile b/04_mailboxes/Makefile deleted file mode 100644 index 56649ff8..00000000 --- a/04_mailboxes/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial null -serial stdio - -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/04_mailboxes/README.md b/04_mailboxes/README.md deleted file mode 100644 index 4be7859a..00000000 --- a/04_mailboxes/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Tutorial 04 - Mailboxes - -The Raspberry Pi 3 also has a more powerful UART, `UART0`, that among other -features, supports programmable clock rates. Before we can go on with setting -up `UART0`, we need mailboxes. Mailboxes are an interface between the Pi's ARM -CPU cores and the GPU. They will be used as a means to request work from the -GPU, for example, requesting to program a certain clock for `UART0`. - -In this tutorial, we'll start slowly and use it to query the Raspberry's serial -number and print that out on the already functional `UART1`. - -## uart.rs - -`MiniUart::hex(&self, d: u32)` prints out a binary value in hexadecimal format. - -## mbox.rs - -The mailbox interface. First we fill up the message in the `mbox.buffer` array, -then we call `Mbox::call(&mut self, channel: u32)` to pass it to the GPU, -specifying the mailbox channel. In this example we have used the [property -channel], which requires the message to be formatted as: - -[property channel]: (https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface) - -``` - 0. size of the message in bytes, (x+1)*4 - 1. mbox::REQUEST magic value, indicates request message - 2-x. tags - x+1. mbox::tag::LAST magic value, indicates no more tags -``` - -Where each tag looks like: - -``` - n+0. tag identifier - n+1. value buffer size in bytes - n+2. must be zero - n+3. optional value buffer -``` - -### Synchronization - -When signaling the GPU about a new mailbox message, we need to take care that -mailbox buffer setup has really finished. Both setting up mailbox contents and -signaling the GPU is done with store operations to independent memory locations -(RAM and MMIO). Since compilers are free to reorder instructions without -control-flow or data-dependencies for optimization purposes, we need to take -care that signaling the GPU really takes place _after_ all of the contents have -been written to the mailbox buffer. - -One way to do this would be to define the whole mailbox buffer as `volatile`, as -well as the location that we write to to signal the GPU. The compiler is not -allowed to reorder memory operations tagged with the `volatile` keyword with -each other. But this is not needed here. We don't care if the compiler optimizes -the buffer setup code as long as signaling the GPU takes place afterwards. - -Therefore, we prevent premature signaling by inserting an explicit [compiler -fence] after the buffer preparation code. Since we signal the CPU by calling -another function, the fence would only be effective if that function was a) -inlined and b) the inlined instructions then reordered with buffer setup -code. Otherwise the compiler has to assume that the called function has -dependencies on previous memory operations and not reorder here. Although there -is little chance that the reordering scenario happens, I'll leave the fence -there nonetheless for academic purposes :-) - -Please note that such reordering might also be done by CPUs that feature -[out-of-order execution]. Lucky us, although the Rasperry Pi 3 features -`ARMv8.0-A` CPU cores, the `Cortex-A53` variant is used, [which does not support -this feature]. Otherwise, a [fence] that additionally [emits corresponding CPU -instructions] to prevent this behavior would be needed. - -[compiler fence]: https://doc.rust-lang.org/beta/core/sync/atomic/fn.compiler_fence.html -[out-of-order execution]: https://en.wikipedia.org/wiki/Out-of-order_execution -[which does not support this feature]: https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores -[fence]: https://doc.rust-lang.org/std/sync/atomic/fn.fence.html -[emits corresponding CPU instructions]: https://developer.arm.com/products/architecture/a-profile/docs/100941/latest/barriers - -## main.rs - -We query the board's serial number and then we display it on the UART. diff --git a/04_mailboxes/kernel8 b/04_mailboxes/kernel8 deleted file mode 100755 index 043ce120..00000000 Binary files a/04_mailboxes/kernel8 and /dev/null differ diff --git a/04_mailboxes/kernel8.img b/04_mailboxes/kernel8.img deleted file mode 100755 index cf8c62b3..00000000 Binary files a/04_mailboxes/kernel8.img and /dev/null differ diff --git a/04_mailboxes/link.ld b/04_mailboxes/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/04_mailboxes/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/04_mailboxes/raspi3_boot/Cargo.toml b/04_mailboxes/raspi3_boot/Cargo.toml deleted file mode 100644 index 1c3c1c69..00000000 --- a/04_mailboxes/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/04_mailboxes/raspi3_boot/src/boot_cores.S b/04_mailboxes/raspi3_boot/src/boot_cores.S deleted file mode 100644 index f6f28af6..00000000 --- a/04_mailboxes/raspi3_boot/src/boot_cores.S +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: - // read cpu id, stop slave cores - mrs x1, mpidr_el1 - and x1, x1, #3 - cbz x1, 2f - // cpu id > 0, stop -1: wfe - b 1b -2: // cpu id == 0 - - // set stack before our code - ldr x1, =_boot_cores - mov sp, x1 - - // jump to Rust code, should not return - bl reset - // for failsafe, halt this core too - b 1b diff --git a/04_mailboxes/raspi3_boot/src/lib.rs b/04_mailboxes/raspi3_boot/src/lib.rs deleted file mode 100644 index a185d0a9..00000000 --- a/04_mailboxes/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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] -#![feature(global_asm)] - -//! 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -#[no_mangle] -pub unsafe extern "C" 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(); -} - -// Disable all cores except core 0, and then jump to reset() -global_asm!(include_str!("boot_cores.S")); diff --git a/04_mailboxes/src/gpio.rs b/04_mailboxes/src/gpio.rs deleted file mode 100644 index 408af5ab..00000000 --- a/04_mailboxes/src/gpio.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 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, - RXD1 = 0b010 // Mini UART - Alternate function 5 - - ], - - /// Pin 14 - FSEL14 OFFSET(12) NUMBITS(3) [ - Input = 0b000, - Output = 0b001, - 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/04_mailboxes/src/main.rs b/04_mailboxes/src/main.rs deleted file mode 100644 index 0c44920b..00000000 --- a/04_mailboxes/src/main.rs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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(asm)] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod mbox; -mod uart; - -use core::sync::atomic::{compiler_fence, Ordering}; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::MiniUart::new(); - - // set up serial console - uart.init(); - uart.puts("\n[0] UART is live!\n"); - - uart.puts("[1] Press a key to continue booting... "); - uart.getc(); - uart.puts("Greetings fellow Rustacean!\n"); - - // get the board's unique serial number with a mailbox call - mbox.buffer[0] = 8 * 4; // length of the message - mbox.buffer[1] = mbox::REQUEST; // this is a request message - mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command - mbox.buffer[3] = 8; // buffer size - mbox.buffer[4] = 8; - mbox.buffer[5] = 0; // clear output buffer - mbox.buffer[6] = 0; - mbox.buffer[7] = 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); - - // send the message to the GPU and receive answer - match mbox.call(mbox::channel::PROP) { - Err(_) => uart.puts("[i] Unable to query serial!\n"), - Ok(()) => { - uart.puts("[i] My serial number is: 0x"); - uart.hex(mbox.buffer[6]); - uart.hex(mbox.buffer[5]); - uart.puts("\n"); - } - }; - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/04_mailboxes/src/mbox.rs b/04_mailboxes/src/mbox.rs deleted file mode 100644 index 88f8fadd..00000000 --- a/04_mailboxes/src/mbox.rs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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::{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 GETSERIAL: u32 = 0x10004; - pub const LAST: u32 = 0; -} - -// 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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/04_mailboxes/src/uart.rs b/04_mailboxes/src/uart.rs deleted file mode 100644 index 42ff4e43..00000000 --- a/04_mailboxes/src/uart.rs +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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::MMIO_BASE; -use crate::gpio; -use core::ops; -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 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) [] - ] -} - -const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000; - -#[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; - -/// 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() -> MiniUart { - MiniUart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - MINI_UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self) { - // 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 - unsafe { - (*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" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).write( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).set(0); - } - - self.AUX_MU_CNTL - .write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled); - } - - /// Send a character - pub fn send(&self, c: char) { - // wait until we can send - loop { - if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) { - break; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // write the character to the buffer - self.AUX_MU_IO.set(c as u32); - } - - /// Receive a character - pub 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // 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 - } - - /// 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: u32) { - let mut n; - - for i in 0..8 { - // get highest tetrad - n = d.wrapping_shr(28 - 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/04_zero_overhead_abstraction/.vscode/settings.json b/04_zero_overhead_abstraction/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/04_zero_overhead_abstraction/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/03_uart1/Cargo.lock b/04_zero_overhead_abstraction/Cargo.lock similarity index 78% rename from 03_uart1/Cargo.lock rename to 04_zero_overhead_abstraction/Cargo.lock index 82ecd2c6..d9d11c3f 100644 --- a/03_uart1/Cargo.lock +++ b/04_zero_overhead_abstraction/Cargo.lock @@ -1,31 +1,26 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "kernel8" -version = "0.1.0" +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "raspi3_boot 0.1.0", "register 0.3.3 (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" +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[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 = [ - "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.3" @@ -40,7 +35,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" "checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/04_zero_overhead_abstraction/Cargo.toml b/04_zero_overhead_abstraction/Cargo.toml new file mode 100644 index 00000000..a0734bd3 --- /dev/null +++ b/04_zero_overhead_abstraction/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } diff --git a/04_zero_overhead_abstraction/Makefile b/04_zero_overhead_abstraction/Makefile new file mode 100644 index 00000000..e80eb9d3 --- /dev/null +++ b/04_zero_overhead_abstraction/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/04_zero_overhead_abstraction/README.md b/04_zero_overhead_abstraction/README.md new file mode 100644 index 00000000..371650b3 --- /dev/null +++ b/04_zero_overhead_abstraction/README.md @@ -0,0 +1,154 @@ +# Tutorial 04 - Zero Overhead Abstraction + +## tl;dr + +All hand-written assembly is replaced by Rust code from the [cortex-a] crate, +which provides zero-overhead abstractions and wraps the `unsafe` parts. + +[cortex-a]: https://github.com/rust-embedded/cortex-a + +## Diff to previous +```diff + +diff -uNr 03_hacky_hello_world/Cargo.toml 04_zero_overhead_abstraction/Cargo.toml +--- 03_hacky_hello_world/Cargo.toml ++++ 04_zero_overhead_abstraction/Cargo.toml +@@ -10,7 +10,10 @@ + # The features section is used to select the target board. + [features] + default = [] +-bsp_rpi3 = [] ++bsp_rpi3 = ["cortex-a"] + + [dependencies] + r0 = "0.2.*" ++ ++# Optional dependencies ++cortex-a = { version = "2.*", optional = true } + +diff -uNr 03_hacky_hello_world/src/arch/aarch64/start.S 04_zero_overhead_abstraction/src/arch/aarch64/start.S +--- 03_hacky_hello_world/src/arch/aarch64/start.S ++++ 04_zero_overhead_abstraction/src/arch/aarch64/start.S +@@ -1,21 +0,0 @@ +-// SPDX-License-Identifier: MIT +-// +-// Copyright (c) 2018-2019 Andre Richter +- +-.section ".text._start" +- +-.global _start +- +-_start: +- mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register +- and x1, x1, #3 // Clear all bits except [1:0], which hold core id +- cbz x1, 2f // Jump to label 2 if we are core 0 +-1: wfe // Wait for event +- b 1b // In case an event happend, jump back to 1 +-2: // If we are here, we are core0 +- ldr x1, =_start // Load address of function "_start()" +- mov sp, x1 // Set start of stack to before our code, aka first +- // address before "_start()" +- bl init // Jump to the "init()" kernel function +- b 1b // We should never reach here. But just in case, +- // park this core aswell + +diff -uNr 03_hacky_hello_world/src/arch/aarch64.rs 04_zero_overhead_abstraction/src/arch/aarch64.rs +--- 03_hacky_hello_world/src/arch/aarch64.rs ++++ 04_zero_overhead_abstraction/src/arch/aarch64.rs +@@ -4,7 +4,28 @@ + + //! AArch64. + +-global_asm!(include_str!("aarch64/start.S")); ++use crate::bsp; ++use cortex_a::{asm, regs::*}; ++ ++/// The entry of the `kernel` binary. ++/// ++/// The function must be named `_start`, because the linker is looking for this exact name. ++/// ++/// # Safety ++/// ++/// - Linker script must ensure to place this function at `0x80_000`. ++#[no_mangle] ++pub unsafe extern "C" fn _start() -> ! { ++ const CORE_MASK: u64 = 0x3; ++ ++ if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { ++ SP.set(bsp::BOOT_CORE_STACK_START); ++ crate::runtime_init::init() ++ } else { ++ // if not core0, infinitely wait for events ++ wait_forever() ++ } ++} + + //-------------------------------------------------------------------------------------------------- + // Implementation of the kernel's architecture abstraction code +@@ -13,9 +34,7 @@ + /// Pause execution on the calling CPU core. + #[inline(always)] + pub fn wait_forever() -> ! { +- unsafe { +- loop { +- asm!("wfe" :::: "volatile") +- } ++ loop { ++ asm::wfe() + } + } + +diff -uNr 03_hacky_hello_world/src/bsp/rpi.rs 04_zero_overhead_abstraction/src/bsp/rpi.rs +--- 03_hacky_hello_world/src/bsp/rpi.rs ++++ 04_zero_overhead_abstraction/src/bsp/rpi.rs +@@ -7,6 +7,9 @@ + use crate::interface; + use core::fmt; + ++pub const BOOT_CORE_ID: u64 = 0; ++pub const BOOT_CORE_STACK_START: u64 = 0x80_000; ++ + /// A mystical, magical device for generating QEMU output out of the void. + struct QEMUOutput; + + +diff -uNr 03_hacky_hello_world/src/main.rs 04_zero_overhead_abstraction/src/main.rs +--- 03_hacky_hello_world/src/main.rs ++++ 04_zero_overhead_abstraction/src/main.rs +@@ -19,9 +19,7 @@ + //! [Architecture-specific code]: arch/index.html + //! [`kernel::interface`]: interface/index.html + +-#![feature(asm)] + #![feature(format_args_nl)] +-#![feature(global_asm)] + #![feature(panic_info_message)] + #![no_main] + #![no_std] +@@ -46,7 +44,8 @@ + /// + /// - Only a single core must be active and running this function. + unsafe fn kernel_init() -> ! { +- println!("Hello from Rust!"); ++ println!("[0] Hello from pure Rust!"); + +- panic!("Stopping here.") ++ println!("[1] Stopping here."); ++ arch::wait_forever() + } + +diff -uNr 03_hacky_hello_world/src/runtime_init.rs 04_zero_overhead_abstraction/src/runtime_init.rs +--- 03_hacky_hello_world/src/runtime_init.rs ++++ 04_zero_overhead_abstraction/src/runtime_init.rs +@@ -10,8 +10,7 @@ + /// # Safety + /// + /// - Only a single core must be active and running this function. +-#[no_mangle] +-pub unsafe extern "C" fn init() -> ! { ++pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + +``` diff --git a/05_uart0/kernel8 b/04_zero_overhead_abstraction/kernel similarity index 82% rename from 05_uart0/kernel8 rename to 04_zero_overhead_abstraction/kernel index c801eef1..f06bc3af 100755 Binary files a/05_uart0/kernel8 and b/04_zero_overhead_abstraction/kernel differ diff --git a/04_zero_overhead_abstraction/kernel8.img b/04_zero_overhead_abstraction/kernel8.img new file mode 100755 index 00000000..ee2552d9 Binary files /dev/null and b/04_zero_overhead_abstraction/kernel8.img differ diff --git a/04_zero_overhead_abstraction/src/arch.rs b/04_zero_overhead_abstraction/src/arch.rs new file mode 100644 index 00000000..421e377f --- /dev/null +++ b/04_zero_overhead_abstraction/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(feature = "bsp_rpi3")] +mod aarch64; + +#[cfg(feature = "bsp_rpi3")] +pub use aarch64::*; diff --git a/04_zero_overhead_abstraction/src/arch/aarch64.rs b/04_zero_overhead_abstraction/src/arch/aarch64.rs new file mode 100644 index 00000000..3dc16a08 --- /dev/null +++ b/04_zero_overhead_abstraction/src/arch/aarch64.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +use crate::bsp; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/04_zero_overhead_abstraction/src/bsp.rs b/04_zero_overhead_abstraction/src/bsp.rs new file mode 100644 index 00000000..127f99db --- /dev/null +++ b/04_zero_overhead_abstraction/src/bsp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +mod rpi; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi::*; diff --git a/04_zero_overhead_abstraction/src/bsp/rpi.rs b/04_zero_overhead_abstraction/src/bsp/rpi.rs new file mode 100644 index 00000000..59e06d37 --- /dev/null +++ b/04_zero_overhead_abstraction/src/bsp/rpi.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +use crate::interface; +use core::fmt; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +/// A mystical, magical device for generating QEMU output out of the void. +struct QEMUOutput; + +/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used +/// to implement the `kernel`'s `print!` and `println!` macros. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl interface::console::Write for QEMUOutput { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + unsafe { + core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); + } + } + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Returns a ready-to-use `console::Write` implementation. +pub fn console() -> impl interface::console::Write { + QEMUOutput {} +} diff --git a/04_zero_overhead_abstraction/src/bsp/rpi/link.ld b/04_zero_overhead_abstraction/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/04_zero_overhead_abstraction/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/04_zero_overhead_abstraction/src/interface.rs b/04_zero_overhead_abstraction/src/interface.rs new file mode 100644 index 00000000..79d74a5f --- /dev/null +++ b/04_zero_overhead_abstraction/src/interface.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + /// Console write functions. + /// + /// `core::fmt::Write` is exactly what we need for now. Re-export it here because + /// implementing `console::Write` gives a better hint to the reader about the + /// intention. + pub use core::fmt::Write; + + /// Console read functions. + pub trait Read { + fn read_char(&mut self) -> char { + ' ' + } + } +} diff --git a/04_zero_overhead_abstraction/src/main.rs b/04_zero_overhead_abstraction/src/main.rs new file mode 100644 index 00000000..fecad796 --- /dev/null +++ b/04_zero_overhead_abstraction/src/main.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +unsafe fn kernel_init() -> ! { + println!("[0] Hello from pure Rust!"); + + println!("[1] Stopping here."); + arch::wait_forever() +} diff --git a/04_zero_overhead_abstraction/src/panic_wait.rs b/04_zero_overhead_abstraction/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/04_zero_overhead_abstraction/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/04_zero_overhead_abstraction/src/print.rs b/04_zero_overhead_abstraction/src/print.rs new file mode 100644 index 00000000..ce6587b5 --- /dev/null +++ b/04_zero_overhead_abstraction/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/04_zero_overhead_abstraction/src/runtime_init.rs b/04_zero_overhead_abstraction/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/04_zero_overhead_abstraction/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/05_safe_globals/.vscode/settings.json b/05_safe_globals/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/05_safe_globals/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/04_mailboxes/Cargo.lock b/05_safe_globals/Cargo.lock similarity index 78% rename from 04_mailboxes/Cargo.lock rename to 05_safe_globals/Cargo.lock index 82ecd2c6..d9d11c3f 100644 --- a/04_mailboxes/Cargo.lock +++ b/05_safe_globals/Cargo.lock @@ -1,31 +1,26 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "kernel8" -version = "0.1.0" +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "raspi3_boot 0.1.0", "register 0.3.3 (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" +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[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 = [ - "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.3" @@ -40,7 +35,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" "checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/05_safe_globals/Cargo.toml b/05_safe_globals/Cargo.toml new file mode 100644 index 00000000..a0734bd3 --- /dev/null +++ b/05_safe_globals/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } diff --git a/05_safe_globals/Makefile b/05_safe_globals/Makefile new file mode 100644 index 00000000..e80eb9d3 --- /dev/null +++ b/05_safe_globals/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/05_safe_globals/README.md b/05_safe_globals/README.md new file mode 100644 index 00000000..51fe8053 --- /dev/null +++ b/05_safe_globals/README.md @@ -0,0 +1,349 @@ +# Tutorial 05 - Safe Globals + +## A slightly longer tl;dr + +When we introduced the globally usable `print!` macros in [tutorial 03], we +cheated a bit. Calling `core::fmt`'s `write_fmt()` function, which takes an +`&mut self`, was only working because on each call, a new instance of +`QEMUOutput` was created. + +If we would want to preserve some state, e.g. statistics about the number of +characters written, we need to make a single global instance of `QEMUOutput` (in +Rust, using the `static` keyword). + +A `static QEMU_OUTPUT`, however, would not allow to call functions taking `&mut +self`. For that, we would need a `static mut`, but calling functions that mutate +state on `static mut`s is unsafe. The Rust compiler's reasoning for this is that +it can then not prevent anymore that multiple cores/threads are mutating the +data concurrently (it is a global, so everyone can reference it from anywhere. +The borrow checker can't help here). + +The solution to this problem is to wrap the global into a synchronization +primitive. In our case, a variant of a *MUTual EXclusion* primivite. `Mutex` is +introduced as a trait in `interfaces.rs`, and implemented by the name of +`NullLock` in `sync.rs` in the `arch` folder. For teaching purposes, to make the +code lean, it leaves out the actual architecture-specific logic for protection +against concurrent access, since we don't need it as long as the kernel only +executes on a single core with interrupts disabled. + +Instead, it focuses on showcasing the Rust core concept of [interior mutability]. +Make sure to read up on it. I also recommend to read this article about an +[accurate mental model for Rust's reference types]. + +If you want to compare the `NullLock` to some real-world mutex implementations, +you can check out implemntations in the [spin crate] or the [parking lot crate]. + +[tutorial 03]: ../03_hacky_hello_world +[interior mutability]: https://doc.rust-lang.org/std/cell/index.html +[accurate mental model for Rust's reference types]: https://docs.rs/dtolnay/0.0.6/dtolnay/macro._02__reference_types.html +[spin crate]: https://github.com/mvdnes/spin-rs +[parking lot crate]: https://github.com/Amanieu/parking_lot + +### Give it a try + +```console +make qemu +[...] +[0] Hello from pure Rust! +[1] Chars written: 26 +[2] Stopping here. +``` + +## Diff to previous +```diff + +diff -uNr 04_zero_overhead_abstraction/src/arch/aarch64/sync.rs 05_safe_globals/src/arch/aarch64/sync.rs +--- 04_zero_overhead_abstraction/src/arch/aarch64/sync.rs ++++ 05_safe_globals/src/arch/aarch64/sync.rs +@@ -0,0 +1,44 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Synchronization primitives. ++ ++use crate::interface; ++use core::cell::UnsafeCell; ++ ++/// A pseudo-lock for teaching purposes. ++/// ++/// Used to introduce [interior mutability]. ++/// ++/// In contrast to a real Mutex implementation, does not protect against concurrent access to the ++/// contained data. This part is preserved for later lessons. ++/// ++/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is ++/// executing single-threaded, aka only running on a single core with interrupts disabled. ++/// ++/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html ++pub struct NullLock { ++ data: UnsafeCell, ++} ++ ++unsafe impl Send for NullLock {} ++unsafe impl Sync for NullLock {} ++ ++impl NullLock { ++ pub const fn new(data: T) -> NullLock { ++ NullLock { ++ data: UnsafeCell::new(data), ++ } ++ } ++} ++ ++impl interface::sync::Mutex for &NullLock { ++ type Data = T; ++ ++ fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { ++ // In a real lock, there would be code encapsulating this line that ensures that this ++ // mutable reference will ever only be given out once at a time. ++ f(unsafe { &mut *self.data.get() }) ++ } ++} + +diff -uNr 04_zero_overhead_abstraction/src/arch/aarch64.rs 05_safe_globals/src/arch/aarch64.rs +--- 04_zero_overhead_abstraction/src/arch/aarch64.rs ++++ 05_safe_globals/src/arch/aarch64.rs +@@ -4,6 +4,8 @@ + + //! AArch64. + ++pub mod sync; ++ + use crate::bsp; + use cortex_a::{asm, regs::*}; + + +diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rpi.rs +--- 04_zero_overhead_abstraction/src/bsp/rpi.rs ++++ 05_safe_globals/src/bsp/rpi.rs +@@ -4,38 +4,114 @@ + + //! Board Support Package for the Raspberry Pi. + +-use crate::interface; ++use crate::{arch::sync::NullLock, interface}; + use core::fmt; + + pub const BOOT_CORE_ID: u64 = 0; + pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + + /// A mystical, magical device for generating QEMU output out of the void. +-struct QEMUOutput; ++/// ++/// The mutex protected part. ++struct QEMUOutputInner { ++ chars_written: usize, ++} ++ ++impl QEMUOutputInner { ++ const fn new() -> QEMUOutputInner { ++ QEMUOutputInner { chars_written: 0 } ++ } ++ ++ /// Send a character. ++ fn write_char(&mut self, c: char) { ++ unsafe { ++ core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); ++ } ++ } ++} + +-/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used +-/// to implement the `kernel`'s `print!` and `println!` macros. ++/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are ++/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, ++/// we get `write_fmt()` automatically. ++/// ++/// The function takes an `&mut self`, so it must be implemented for the inner struct. + /// + /// See [`src/print.rs`]. + /// + /// [`src/print.rs`]: ../../print/index.html +-impl interface::console::Write for QEMUOutput { ++impl fmt::Write for QEMUOutputInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { +- unsafe { +- core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); ++ // Convert newline to carrige return + newline. ++ if c == '\n' { ++ self.write_char('\r') + } ++ ++ self.write_char(c); + } + ++ self.chars_written += s.len(); ++ + Ok(()) + } + } + + //-------------------------------------------------------------------------------------------------- ++// BSP-public ++//-------------------------------------------------------------------------------------------------- ++ ++/// The main struct. ++pub struct QEMUOutput { ++ inner: NullLock, ++} ++ ++impl QEMUOutput { ++ pub const fn new() -> QEMUOutput { ++ QEMUOutput { ++ inner: NullLock::new(QEMUOutputInner::new()), ++ } ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// OS interface implementations ++//-------------------------------------------------------------------------------------------------- ++ ++/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to ++/// serialize access. ++impl interface::console::Write for QEMUOutput { ++ fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { ++ use interface::sync::Mutex; ++ ++ // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase ++ // readability. ++ let mut r = &self.inner; ++ r.lock(|inner| fmt::Write::write_fmt(inner, args)) ++ } ++} ++ ++impl interface::console::Read for QEMUOutput {} ++ ++impl interface::console::Statistics for QEMUOutput { ++ fn chars_written(&self) -> usize { ++ use interface::sync::Mutex; ++ ++ let mut r = &self.inner; ++ r.lock(|inner| inner.chars_written) ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// Global instances ++//-------------------------------------------------------------------------------------------------- ++ ++static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new(); ++ ++//-------------------------------------------------------------------------------------------------- + // Implementation of the kernel's BSP calls + //-------------------------------------------------------------------------------------------------- + +-/// Returns a ready-to-use `console::Write` implementation. +-pub fn console() -> impl interface::console::Write { +- QEMUOutput {} ++/// Return a reference to a `console::All` implementation. ++pub fn console() -> &'static impl interface::console::All { ++ &QEMU_OUTPUT + } + +diff -uNr 04_zero_overhead_abstraction/src/interface.rs 05_safe_globals/src/interface.rs +--- 04_zero_overhead_abstraction/src/interface.rs ++++ 05_safe_globals/src/interface.rs +@@ -20,17 +20,66 @@ + + /// System console operations. + pub mod console { ++ use core::fmt; ++ + /// Console write functions. +- /// +- /// `core::fmt::Write` is exactly what we need for now. Re-export it here because +- /// implementing `console::Write` gives a better hint to the reader about the +- /// intention. +- pub use core::fmt::Write; ++ pub trait Write { ++ fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; ++ } + + /// Console read functions. + pub trait Read { +- fn read_char(&mut self) -> char { ++ fn read_char(&self) -> char { + ' ' + } + } ++ ++ /// Console statistics. ++ pub trait Statistics { ++ /// Return the number of characters written. ++ fn chars_written(&self) -> usize { ++ 0 ++ } ++ ++ /// Return the number of characters read. ++ fn chars_read(&self) -> usize { ++ 0 ++ } ++ } ++ ++ /// Trait alias for a full-fledged console. ++ pub trait All = Write + Read + Statistics; ++} ++ ++/// Synchronization primitives. ++pub mod sync { ++ /// Any object implementing this trait guarantees exclusive access to the data contained within ++ /// the mutex for the duration of the lock. ++ /// ++ /// The trait follows the [Rust embedded WG's ++ /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore ++ /// provides some goodness such as [deadlock ++ /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). ++ /// ++ /// # Example ++ /// ++ /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is ++ /// best implemented **for a reference to a container struct**, and has a usage pattern that ++ /// might feel strange at first: ++ /// ++ /// ``` ++ /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); ++ /// ++ /// fn foo() { ++ /// let mut r = &MUT; // Note that r is mutable ++ /// r.lock(|data| *data += 1); ++ /// } ++ /// ``` ++ pub trait Mutex { ++ /// Type of data encapsulated by the mutex. ++ type Data; ++ ++ /// Creates a critical section and grants temporary mutable access to the encapsulated data. ++ fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; ++ } + } + +diff -uNr 04_zero_overhead_abstraction/src/main.rs 05_safe_globals/src/main.rs +--- 04_zero_overhead_abstraction/src/main.rs ++++ 05_safe_globals/src/main.rs +@@ -21,6 +21,7 @@ + + #![feature(format_args_nl)] + #![feature(panic_info_message)] ++#![feature(trait_alias)] + #![no_main] + #![no_std] + +@@ -44,8 +45,12 @@ + /// + /// - Only a single core must be active and running this function. + unsafe fn kernel_init() -> ! { ++ use interface::console::Statistics; ++ + println!("[0] Hello from pure Rust!"); + +- println!("[1] Stopping here."); ++ println!("[1] Chars written: {}", bsp::console().chars_written()); ++ ++ println!("[2] Stopping here."); + arch::wait_forever() + } + +``` diff --git a/09_delays/kernel8 b/05_safe_globals/kernel similarity index 82% rename from 09_delays/kernel8 rename to 05_safe_globals/kernel index 5b299d27..64d29db5 100755 Binary files a/09_delays/kernel8 and b/05_safe_globals/kernel differ diff --git a/05_safe_globals/kernel8.img b/05_safe_globals/kernel8.img new file mode 100755 index 00000000..fe4e6117 Binary files /dev/null and b/05_safe_globals/kernel8.img differ diff --git a/05_safe_globals/src/arch.rs b/05_safe_globals/src/arch.rs new file mode 100644 index 00000000..421e377f --- /dev/null +++ b/05_safe_globals/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(feature = "bsp_rpi3")] +mod aarch64; + +#[cfg(feature = "bsp_rpi3")] +pub use aarch64::*; diff --git a/05_safe_globals/src/arch/aarch64.rs b/05_safe_globals/src/arch/aarch64.rs new file mode 100644 index 00000000..a89bded3 --- /dev/null +++ b/05_safe_globals/src/arch/aarch64.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; + +use crate::bsp; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/05_safe_globals/src/arch/aarch64/sync.rs b/05_safe_globals/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/05_safe_globals/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/05_safe_globals/src/bsp.rs b/05_safe_globals/src/bsp.rs new file mode 100644 index 00000000..127f99db --- /dev/null +++ b/05_safe_globals/src/bsp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +mod rpi; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi::*; diff --git a/05_safe_globals/src/bsp/rpi.rs b/05_safe_globals/src/bsp/rpi.rs new file mode 100644 index 00000000..281af612 --- /dev/null +++ b/05_safe_globals/src/bsp/rpi.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +use crate::{arch::sync::NullLock, interface}; +use core::fmt; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +/// A mystical, magical device for generating QEMU output out of the void. +/// +/// The mutex protected part. +struct QEMUOutputInner { + chars_written: usize, +} + +impl QEMUOutputInner { + const fn new() -> QEMUOutputInner { + QEMUOutputInner { chars_written: 0 } + } + + /// Send a character. + fn write_char(&mut self, c: char) { + unsafe { + core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); + } + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for QEMUOutputInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The main struct. +pub struct QEMUOutput { + inner: NullLock, +} + +impl QEMUOutput { + pub const fn new() -> QEMUOutput { + QEMUOutput { + inner: NullLock::new(QEMUOutputInner::new()), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to +/// serialize access. +impl interface::console::Write for QEMUOutput { + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + use interface::sync::Mutex; + + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } +} + +impl interface::console::Read for QEMUOutput {} + +impl interface::console::Statistics for QEMUOutput { + fn chars_written(&self) -> usize { + use interface::sync::Mutex; + + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new(); + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &QEMU_OUTPUT +} diff --git a/05_safe_globals/src/bsp/rpi/link.ld b/05_safe_globals/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/05_safe_globals/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/05_safe_globals/src/interface.rs b/05_safe_globals/src/interface.rs new file mode 100644 index 00000000..005e625d --- /dev/null +++ b/05_safe_globals/src/interface.rs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} diff --git a/05_safe_globals/src/main.rs b/05_safe_globals/src/main.rs new file mode 100644 index 00000000..e565b41d --- /dev/null +++ b/05_safe_globals/src/main.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +unsafe fn kernel_init() -> ! { + use interface::console::Statistics; + + println!("[0] Hello from pure Rust!"); + + println!("[1] Chars written: {}", bsp::console().chars_written()); + + println!("[2] Stopping here."); + arch::wait_forever() +} diff --git a/05_safe_globals/src/panic_wait.rs b/05_safe_globals/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/05_safe_globals/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/05_safe_globals/src/print.rs b/05_safe_globals/src/print.rs new file mode 100644 index 00000000..ce6587b5 --- /dev/null +++ b/05_safe_globals/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/05_safe_globals/src/runtime_init.rs b/05_safe_globals/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/05_safe_globals/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/05_uart0/.cargo/config b/05_uart0/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/05_uart0/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/05_uart0/Cargo.toml b/05_uart0/Cargo.toml deleted file mode 100644 index a14dbb2d..00000000 --- a/05_uart0/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/05_uart0/Makefile b/05_uart0/Makefile deleted file mode 100644 index d1723093..00000000 --- a/05_uart0/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -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/05_uart0/README.md b/05_uart0/README.md deleted file mode 100644 index 07062ea7..00000000 --- a/05_uart0/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Tutorial 05 - UART0, PL011 - -Finally, we can set up `UART0` thanks to the mailbox interface. This tutorial -produces the same output as tutorial 04, but it prints the serial number on -`UART0`. - -## uart.rs - -In the init function, we use the mailbox to set a base clock for the UART: - -```rust -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 -}; - -``` - -Afterwards, we can program the rate divisors: - -```rust -self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud -self.FBRD.write(FBRD::FBRD.val(0xB)); -``` - -Baud rate calculation won't be covered in detail here. Please see [this -reference from ARM](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0183g/I49493.html) -for details. - -The API for using the UART is identical to the `UART1` API. - -## main.rs - -We query the board's serial number and display it on the serial console. diff --git a/05_uart0/kernel8.img b/05_uart0/kernel8.img deleted file mode 100755 index 38baf14b..00000000 Binary files a/05_uart0/kernel8.img and /dev/null differ diff --git a/05_uart0/link.ld b/05_uart0/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/05_uart0/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/05_uart0/raspi3_boot/Cargo.toml b/05_uart0/raspi3_boot/Cargo.toml deleted file mode 100644 index 1c3c1c69..00000000 --- a/05_uart0/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/05_uart0/raspi3_boot/src/boot_cores.S b/05_uart0/raspi3_boot/src/boot_cores.S deleted file mode 100644 index f6f28af6..00000000 --- a/05_uart0/raspi3_boot/src/boot_cores.S +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: - // read cpu id, stop slave cores - mrs x1, mpidr_el1 - and x1, x1, #3 - cbz x1, 2f - // cpu id > 0, stop -1: wfe - b 1b -2: // cpu id == 0 - - // set stack before our code - ldr x1, =_boot_cores - mov sp, x1 - - // jump to Rust code, should not return - bl reset - // for failsafe, halt this core too - b 1b diff --git a/05_uart0/raspi3_boot/src/lib.rs b/05_uart0/raspi3_boot/src/lib.rs deleted file mode 100644 index a185d0a9..00000000 --- a/05_uart0/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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] -#![feature(global_asm)] - -//! 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -#[no_mangle] -pub unsafe extern "C" 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(); -} - -// Disable all cores except core 0, and then jump to reset() -global_asm!(include_str!("boot_cores.S")); diff --git a/05_uart0/src/gpio.rs b/05_uart0/src/gpio.rs deleted file mode 100644 index da6a5be4..00000000 --- a/05_uart0/src/gpio.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/05_uart0/src/main.rs b/05_uart0/src/main.rs deleted file mode 100644 index 7ccca746..00000000 --- a/05_uart0/src/main.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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)] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod mbox; -mod uart; - -use core::sync::atomic::{compiler_fence, Ordering}; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - - // set up serial console - match uart.init(&mut mbox) { - Ok(_) => uart.puts("\n[0] UART is live!\n"), - Err(_) => loop { - unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early - }, - } - - uart.puts("[1] Press a key to continue booting... "); - uart.getc(); - uart.puts("Greetings fellow Rustacean!\n"); - - // get the board's unique serial number with a mailbox call - mbox.buffer[0] = 8 * 4; // length of the message - mbox.buffer[1] = mbox::REQUEST; // this is a request message - mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command - mbox.buffer[3] = 8; // buffer size - mbox.buffer[4] = 8; - mbox.buffer[5] = 0; // clear output buffer - mbox.buffer[6] = 0; - mbox.buffer[7] = 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); - - // send the message to the GPU and receive answer - let serial_avail = match mbox.call(mbox::channel::PROP) { - Err(_) => false, - Ok(()) => true, - }; - - if serial_avail { - uart.puts("[i] My serial number is: 0x"); - uart.hex(mbox.buffer[6]); - uart.hex(mbox.buffer[5]); - uart.puts("\n"); - } else { - uart.puts("[i] Unable to query serial!\n"); - } - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/05_uart0/src/mbox.rs b/05_uart0/src/mbox.rs deleted file mode 100644 index daff3771..00000000 --- a/05_uart0/src/mbox.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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::{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 GETSERIAL: u32 = 0x10004; - 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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/05_uart0/src/uart.rs b/05_uart0/src/uart.rs deleted file mode 100644 index 722cb034..00000000 --- a/05_uart0/src/uart.rs +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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::gpio; -use crate::mbox; -use core::{ - ops, - sync::atomic::{compiler_fence, Ordering}, -}; -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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self, mbox: &mut mbox::Mbox) -> 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 - unsafe { - (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); - - (*gpio::GPPUD).set(0); // enable pins 14 and 15 - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).write( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // 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: u32) { - let mut n; - - for i in 0..8 { - // get highest tetrad - n = d.wrapping_shr(28 - 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/06_drivers_gpio_uart/.vscode/settings.json b/06_drivers_gpio_uart/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/06_drivers_gpio_uart/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/06_raspbootin64/Cargo.lock b/06_drivers_gpio_uart/Cargo.lock similarity index 78% rename from 06_raspbootin64/Cargo.lock rename to 06_drivers_gpio_uart/Cargo.lock index 82ecd2c6..9a17339b 100644 --- a/06_raspbootin64/Cargo.lock +++ b/06_drivers_gpio_uart/Cargo.lock @@ -1,31 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "kernel8" -version = "0.1.0" +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "raspi3_boot 0.1.0", "register 0.3.3 (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" +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "r0" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "raspi3_boot" -version = "0.1.0" -dependencies = [ - "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.3" @@ -40,7 +36,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" "checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/06_drivers_gpio_uart/Cargo.toml b/06_drivers_gpio_uart/Cargo.toml new file mode 100644 index 00000000..cf0f0636 --- /dev/null +++ b/06_drivers_gpio_uart/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/06_drivers_gpio_uart/Makefile b/06_drivers_gpio_uart/Makefile new file mode 100644 index 00000000..6ba564ab --- /dev/null +++ b/06_drivers_gpio_uart/Makefile @@ -0,0 +1,89 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all doc qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +endif + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/06_drivers_gpio_uart/README.md b/06_drivers_gpio_uart/README.md new file mode 100644 index 00000000..fd256520 --- /dev/null +++ b/06_drivers_gpio_uart/README.md @@ -0,0 +1,959 @@ +# Tutorial 06 - Drivers: GPIO and UART + +## tl;dr + +Now that we enabled safe globals in the previous tutorial, the infrastructure is +laid for adding the first real device drivers. We throw out the magic QEMU +console and use a real UART now. Like serious embedded hackers do! + +- For the first time, we will be able to run the code on the real hardware. + - Therefore, building is now differentiated between the **RPi 3** and the **RPi4**. + - By default, all `Makefile` targets will build for the **RPi 3**. + - In order to build for the the **RPi4**, prepend `BSP=rpi4` to each target. For example: + - `BSP=rpi4 make` + - `BSP=rpi4 make doc` + - Unfortunately, QEMU does not yet support the **RPi4**, so `BSP=rpi4 make qemu` won't work. +- A `DeviceDriver` trait is added for abstracting `BSP` driver implementations + from kernel code. +- Drivers are stored in `bsp/driver`, and can be reused between `BSP`s. + - Introducing the `GPIO` driver, which pinmuxes the RPi's PL011 UART. + - Most importantly, the `PL011Uart` driver: It implements the `Console` + traits and is from now on used as the system console output. +- `BSP`s now contain a`memory_map.rs`. In the specific case, they contain the + RPi's MMIO addresses which are used to instantiate compatible device drivers + from `bsp/driver`. + +## Boot it from SD card + +Some steps for preparing the SD card differ between RPi3 and RPi4, so be careful. + +### Common for both + +1. Make a single `FAT32` partition named `boot`. +2. On the card, generate a file named `config.txt` with the following contents: + +```txt +init_uart_clock=48000000 +``` +### Pi 3 + +3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card: + - [bootcode.bin](https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin) + - [fixup.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat) + - [start.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start.elf) +4. Run `make` and copy the [kernel8.img](kernel8.img) onto the SD card. + +### Pi 4 + +3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card: + - [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat) + - [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start.elf) + - [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb) +4. Run `BSP=rpi4 make` and copy the [kernel8.img](kernel8.img) onto the SD card. + +### Common again + +5. Insert the SD card into the RPi and connect the USB serial to your host PC. + - Wiring diagram at [top-level README](../README.md#usb-serial). +6. Run `screen` (you might need to install it first): + +```console +sudo screen /dev/ttyUSB0 115200 +``` + +7. Hit Enter to kick off the kernel boot process. Observe the output: + +```console +[0] Booting on: Raspberry Pi 3 +[1] Drivers loaded: + 1. GPIO + 2. PL011Uart +[2] Chars written: 84 +[3] Echoing input now +``` + +8. Exit screen by pressing ctrl-a ctrl-d or disconnecting the USB serial. + +## Diff to previous +```diff + +diff -uNr 05_safe_globals/Cargo.toml 06_drivers_gpio_uart/Cargo.toml +--- 05_safe_globals/Cargo.toml ++++ 06_drivers_gpio_uart/Cargo.toml +@@ -10,10 +10,12 @@ + # The features section is used to select the target board. + [features] + default = [] +-bsp_rpi3 = ["cortex-a"] ++bsp_rpi3 = ["cortex-a", "register"] ++bsp_rpi4 = ["cortex-a", "register"] + + [dependencies] + r0 = "0.2.*" + + # Optional dependencies + cortex-a = { version = "2.*", optional = true } ++register = { version = "0.3.*", optional = true } + +diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile +--- 05_safe_globals/Makefile ++++ 06_drivers_gpio_uart/Makefile +@@ -16,6 +16,14 @@ + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 ++else ifeq ($(BSP),rpi4) ++ TARGET = aarch64-unknown-none-softfloat ++ OUTPUT = kernel8.img ++# QEMU_BINARY = qemu-system-aarch64 ++# QEMU_MACHINE_TYPE = ++# QEMU_MISC_ARGS = -serial stdio ++ LINKER_FILE = src/bsp/rpi/link.ld ++ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 + endif + + SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) +@@ -56,9 +64,14 @@ + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + ++ifeq ($(QEMU_MACHINE_TYPE),) ++qemu: ++ @echo "This board is not yet supported for QEMU." ++else + qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) ++endif + + clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +diff -uNr 05_safe_globals/src/arch/aarch64.rs 06_drivers_gpio_uart/src/arch/aarch64.rs +--- 05_safe_globals/src/arch/aarch64.rs ++++ 06_drivers_gpio_uart/src/arch/aarch64.rs +@@ -33,6 +33,15 @@ + // Implementation of the kernel's architecture abstraction code + //-------------------------------------------------------------------------------------------------- + ++pub use asm::nop; ++ ++/// Spin for `n` cycles. ++pub fn spin_for_cycles(n: usize) { ++ for _ in 0..n { ++ asm::nop(); ++ } ++} ++ + /// Pause execution on the calling CPU core. + #[inline(always)] + pub fn wait_forever() -> ! { + +diff -uNr 05_safe_globals/src/arch.rs 06_drivers_gpio_uart/src/arch.rs +--- 05_safe_globals/src/arch.rs ++++ 06_drivers_gpio_uart/src/arch.rs +@@ -4,8 +4,8 @@ + + //! Conditional exporting of processor architecture code. + +-#[cfg(feature = "bsp_rpi3")] ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] + mod aarch64; + +-#[cfg(feature = "bsp_rpi3")] ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] + pub use aarch64::*; + +diff -uNr 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_gpio.rs 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs +--- 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_gpio.rs ++++ 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs +@@ -0,0 +1,157 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! GPIO driver. ++ ++use crate::{arch, arch::sync::NullLock, interface}; ++use core::ops; ++use register::{mmio::ReadWrite, register_bitfields}; ++ ++// GPIO registers. ++// ++// Descriptions taken from ++// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf ++register_bitfields! { ++ u32, ++ ++ /// GPIO Function Select 1 ++ GPFSEL1 [ ++ /// Pin 15 ++ FSEL15 OFFSET(15) NUMBITS(3) [ ++ Input = 0b000, ++ Output = 0b001, ++ AltFunc0 = 0b100 // PL011 UART RX ++ ++ ], ++ ++ /// Pin 14 ++ FSEL14 OFFSET(12) NUMBITS(3) [ ++ Input = 0b000, ++ Output = 0b001, ++ AltFunc0 = 0b100 // PL011 UART TX ++ ] ++ ], ++ ++ /// GPIO Pull-up/down Clock Register 0 ++ GPPUDCLK0 [ ++ /// Pin 15 ++ PUDCLK15 OFFSET(15) NUMBITS(1) [ ++ NoEffect = 0, ++ AssertClock = 1 ++ ], ++ ++ /// Pin 14 ++ PUDCLK14 OFFSET(14) NUMBITS(1) [ ++ NoEffect = 0, ++ AssertClock = 1 ++ ] ++ ] ++} ++ ++#[allow(non_snake_case)] ++#[repr(C)] ++pub struct RegisterBlock { ++ pub GPFSEL0: ReadWrite, // 0x00 ++ pub GPFSEL1: ReadWrite, // 0x04 ++ pub GPFSEL2: ReadWrite, // 0x08 ++ pub GPFSEL3: ReadWrite, // 0x0C ++ pub GPFSEL4: ReadWrite, // 0x10 ++ pub GPFSEL5: ReadWrite, // 0x14 ++ __reserved_0: u32, // 0x18 ++ GPSET0: ReadWrite, // 0x1C ++ GPSET1: ReadWrite, // 0x20 ++ __reserved_1: u32, // ++ GPCLR0: ReadWrite, // 0x28 ++ __reserved_2: [u32; 2], // ++ GPLEV0: ReadWrite, // 0x34 ++ GPLEV1: ReadWrite, // 0x38 ++ __reserved_3: u32, // ++ GPEDS0: ReadWrite, // 0x40 ++ GPEDS1: ReadWrite, // 0x44 ++ __reserved_4: [u32; 7], // ++ GPHEN0: ReadWrite, // 0x64 ++ GPHEN1: ReadWrite, // 0x68 ++ __reserved_5: [u32; 10], // ++ pub GPPUD: ReadWrite, // 0x94 ++ pub GPPUDCLK0: ReadWrite, // 0x98 ++ pub GPPUDCLK1: ReadWrite, // 0x9C ++} ++ ++/// The driver's private data. ++struct GPIOInner { ++ base_addr: usize, ++} ++ ++/// Deref to RegisterBlock. ++impl ops::Deref for GPIOInner { ++ type Target = RegisterBlock; ++ ++ fn deref(&self) -> &Self::Target { ++ unsafe { &*self.ptr() } ++ } ++} ++ ++impl GPIOInner { ++ const fn new(base_addr: usize) -> GPIOInner { ++ GPIOInner { base_addr } ++ } ++ ++ /// Return a pointer to the register block. ++ fn ptr(&self) -> *const RegisterBlock { ++ self.base_addr as *const _ ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// BSP-public ++//-------------------------------------------------------------------------------------------------- ++use interface::sync::Mutex; ++ ++/// The driver's main struct. ++pub struct GPIO { ++ inner: NullLock, ++} ++ ++impl GPIO { ++ pub const unsafe fn new(base_addr: usize) -> GPIO { ++ GPIO { ++ inner: NullLock::new(GPIOInner::new(base_addr)), ++ } ++ } ++ ++ /// Map PL011 UART as standard output. ++ /// ++ /// TX to pin 14 ++ /// RX to pin 15 ++ pub fn map_pl011_uart(&self) { ++ let mut r = &self.inner; ++ r.lock(|inner| { ++ // Map to pins. ++ inner ++ .GPFSEL1 ++ .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); ++ ++ // Enable pins 14 and 15. ++ inner.GPPUD.set(0); ++ arch::spin_for_cycles(150); ++ ++ inner ++ .GPPUDCLK0 ++ .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); ++ arch::spin_for_cycles(150); ++ ++ inner.GPPUDCLK0.set(0); ++ }) ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// OS interface implementations ++//-------------------------------------------------------------------------------------------------- ++ ++impl interface::driver::DeviceDriver for GPIO { ++ fn compatible(&self) -> &str { ++ "GPIO" ++ } ++} + +diff -uNr 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +--- 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs ++++ 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! PL011 UART driver. ++ ++use crate::{arch, arch::sync::NullLock, interface}; ++use core::{fmt, ops}; ++use register::{mmio::*, register_bitfields}; ++ ++// PL011 UART registers. ++// ++// Descriptions taken from ++// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf ++register_bitfields! { ++ u32, ++ ++ /// Flag Register ++ FR [ ++ /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the ++ /// Line Control Register, UARTLCR_ LCRH. ++ /// ++ /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If ++ /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does ++ /// not indicate if there is data in the transmit shift register. ++ TXFE OFFSET(7) NUMBITS(1) [], ++ ++ /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the ++ /// UARTLCR_ LCRH Register. ++ /// ++ /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If ++ /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. ++ TXFF OFFSET(5) NUMBITS(1) [], ++ ++ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the ++ /// UARTLCR_H Register. ++ /// ++ /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If ++ /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. ++ RXFE OFFSET(4) NUMBITS(1) [] ++ ], ++ ++ /// Integer Baud rate divisor ++ IBRD [ ++ /// Integer Baud rate divisor ++ IBRD OFFSET(0) NUMBITS(16) [] ++ ], ++ ++ /// Fractional Baud rate divisor ++ FBRD [ ++ /// Fractional Baud rate divisor ++ FBRD OFFSET(0) NUMBITS(6) [] ++ ], ++ ++ /// Line Control register ++ LCRH [ ++ /// Word length. These bits indicate the number of data bits transmitted or received in a ++ /// frame. ++ WLEN OFFSET(5) NUMBITS(2) [ ++ FiveBit = 0b00, ++ SixBit = 0b01, ++ SevenBit = 0b10, ++ EightBit = 0b11 ++ ], ++ ++ /// Enable FIFOs: ++ /// ++ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding ++ /// registers ++ /// ++ /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). ++ FEN OFFSET(4) NUMBITS(1) [ ++ FifosDisabled = 0, ++ FifosEnabled = 1 ++ ] ++ ], ++ ++ /// Control Register ++ CR [ ++ /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. ++ /// Data reception occurs for UART signals. When the UART is disabled in the middle of ++ /// reception, it completes the current character before stopping. ++ RXE OFFSET(9) NUMBITS(1) [ ++ Disabled = 0, ++ Enabled = 1 ++ ], ++ ++ /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. ++ /// Data transmission occurs for UART signals. When the UART is disabled in the middle of ++ /// transmission, it completes the current character before stopping. ++ TXE OFFSET(8) NUMBITS(1) [ ++ Disabled = 0, ++ Enabled = 1 ++ ], ++ ++ /// UART enable ++ UARTEN OFFSET(0) NUMBITS(1) [ ++ /// If the UART is disabled in the middle of transmission or reception, it completes the ++ /// current character before stopping. ++ Disabled = 0, ++ Enabled = 1 ++ ] ++ ], ++ ++ /// 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 ++} ++ ++/// The driver's mutex protected part. ++struct PL011UartInner { ++ base_addr: usize, ++ chars_written: usize, ++} ++ ++/// Deref to RegisterBlock. ++/// ++/// Allows writing ++/// ``` ++/// self.DR.read() ++/// ``` ++/// instead of something along the lines of ++/// ``` ++/// unsafe { (*PL011UartInner::ptr()).DR.read() } ++/// ``` ++impl ops::Deref for PL011UartInner { ++ type Target = RegisterBlock; ++ ++ fn deref(&self) -> &Self::Target { ++ unsafe { &*self.ptr() } ++ } ++} ++ ++impl PL011UartInner { ++ const fn new(base_addr: usize) -> PL011UartInner { ++ PL011UartInner { ++ base_addr, ++ chars_written: 0, ++ } ++ } ++ ++ /// Return a pointer to the register block. ++ fn ptr(&self) -> *const RegisterBlock { ++ self.base_addr as *const _ ++ } ++ ++ /// Send a character. ++ fn write_char(&mut self, c: char) { ++ // Wait until we can send. ++ loop { ++ if !self.FR.is_set(FR::TXFF) { ++ break; ++ } ++ ++ arch::nop(); ++ } ++ ++ // Write the character to the buffer. ++ self.DR.set(c as u32); ++ } ++} ++ ++/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are ++/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, ++/// we get `write_fmt()` automatically. ++/// ++/// The function takes an `&mut self`, so it must be implemented for the inner struct. ++/// ++/// See [`src/print.rs`]. ++/// ++/// [`src/print.rs`]: ../../print/index.html ++impl fmt::Write for PL011UartInner { ++ fn write_str(&mut self, s: &str) -> fmt::Result { ++ for c in s.chars() { ++ // Convert newline to carrige return + newline. ++ if c == '\n' { ++ self.write_char('\r') ++ } ++ ++ self.write_char(c); ++ } ++ ++ self.chars_written += s.len(); ++ ++ Ok(()) ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// BSP-public ++//-------------------------------------------------------------------------------------------------- ++ ++/// The driver's main struct. ++pub struct PL011Uart { ++ inner: NullLock, ++} ++ ++impl PL011Uart { ++ /// # Safety ++ /// ++ /// The user must ensure to provide the correct `base_addr`. ++ pub const unsafe fn new(base_addr: usize) -> PL011Uart { ++ PL011Uart { ++ inner: NullLock::new(PL011UartInner::new(base_addr)), ++ } ++ } ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// OS interface implementations ++//-------------------------------------------------------------------------------------------------- ++use interface::sync::Mutex; ++ ++impl interface::driver::DeviceDriver for PL011Uart { ++ fn compatible(&self) -> &str { ++ "PL011Uart" ++ } ++ ++ /// Set up baud rate and characteristics ++ /// ++ /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the ++ /// firmware). ++ fn init(&self) -> interface::driver::Result { ++ let mut r = &self.inner; ++ r.lock(|inner| { ++ // Turn it off temporarily. ++ inner.CR.set(0); ++ ++ inner.ICR.write(ICR::ALL::CLEAR); ++ inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. ++ inner.FBRD.write(FBRD::FBRD.val(3)); ++ inner ++ .LCRH ++ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on ++ inner ++ .CR ++ .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); ++ }); ++ ++ Ok(()) ++ } ++} ++ ++impl interface::console::Write for PL011Uart { ++ /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to ++ /// serialize access. ++ fn write_char(&self, c: char) { ++ let mut r = &self.inner; ++ r.lock(|inner| inner.write_char(c)); ++ } ++ ++ fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { ++ // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase ++ // readability. ++ let mut r = &self.inner; ++ r.lock(|inner| fmt::Write::write_fmt(inner, args)) ++ } ++} ++ ++impl interface::console::Read for PL011Uart { ++ fn read_char(&self) -> char { ++ let mut r = &self.inner; ++ r.lock(|inner| { ++ // Wait until buffer is filled. ++ loop { ++ if !inner.FR.is_set(FR::RXFE) { ++ break; ++ } ++ ++ arch::nop(); ++ } ++ ++ // Read one character. ++ let mut ret = inner.DR.get() as u8 as char; ++ ++ // Convert carrige return to newline. ++ if ret == '\r' { ++ ret = '\n' ++ } ++ ++ ret ++ }) ++ } ++} ++ ++impl interface::console::Statistics for PL011Uart { ++ fn chars_written(&self) -> usize { ++ let mut r = &self.inner; ++ r.lock(|inner| inner.chars_written) ++ } ++} + +diff -uNr 05_safe_globals/src/bsp/driver/bcm.rs 06_drivers_gpio_uart/src/bsp/driver/bcm.rs +--- 05_safe_globals/src/bsp/driver/bcm.rs ++++ 06_drivers_gpio_uart/src/bsp/driver/bcm.rs +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! BCM driver top level. ++ ++mod bcm2xxx_gpio; ++mod bcm2xxx_pl011_uart; ++ ++pub use bcm2xxx_gpio::GPIO; ++pub use bcm2xxx_pl011_uart::PL011Uart; + +diff -uNr 05_safe_globals/src/bsp/driver.rs 06_drivers_gpio_uart/src/bsp/driver.rs +--- 05_safe_globals/src/bsp/driver.rs ++++ 06_drivers_gpio_uart/src/bsp/driver.rs +@@ -0,0 +1,11 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Drivers. ++ ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] ++mod bcm; ++ ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] ++pub use bcm::*; + +diff -uNr 05_safe_globals/src/bsp/rpi/memory_map.rs 06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs +--- 05_safe_globals/src/bsp/rpi/memory_map.rs ++++ 06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs +@@ -0,0 +1,18 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! The board's memory map. ++ ++/// Physical devices. ++#[rustfmt::skip] ++pub mod mmio { ++ #[cfg(feature = "bsp_rpi3")] ++ pub const BASE: usize = 0x3F00_0000; ++ ++ #[cfg(feature = "bsp_rpi4")] ++ pub const BASE: usize = 0xFE00_0000; ++ ++ pub const GPIO_BASE: usize = BASE + 0x0020_0000; ++ pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; ++} + +diff -uNr 05_safe_globals/src/bsp/rpi.rs 06_drivers_gpio_uart/src/bsp/rpi.rs +--- 05_safe_globals/src/bsp/rpi.rs ++++ 06_drivers_gpio_uart/src/bsp/rpi.rs +@@ -4,114 +4,55 @@ + + //! Board Support Package for the Raspberry Pi. + +-use crate::{arch::sync::NullLock, interface}; +-use core::fmt; ++mod memory_map; ++ ++use super::driver; ++use crate::interface; + + pub const BOOT_CORE_ID: u64 = 0; + pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +-/// A mystical, magical device for generating QEMU output out of the void. +-/// +-/// The mutex protected part. +-struct QEMUOutputInner { +- chars_written: usize, +-} +- +-impl QEMUOutputInner { +- const fn new() -> QEMUOutputInner { +- QEMUOutputInner { chars_written: 0 } +- } +- +- /// Send a character. +- fn write_char(&mut self, c: char) { +- unsafe { +- core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8); +- } +- } +-} +- +-/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +-/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +-/// we get `write_fmt()` automatically. +-/// +-/// The function takes an `&mut self`, so it must be implemented for the inner struct. +-/// +-/// See [`src/print.rs`]. +-/// +-/// [`src/print.rs`]: ../../print/index.html +-impl fmt::Write for QEMUOutputInner { +- fn write_str(&mut self, s: &str) -> fmt::Result { +- for c in s.chars() { +- // Convert newline to carrige return + newline. +- if c == '\n' { +- self.write_char('\r') +- } +- +- self.write_char(c); +- } +- +- self.chars_written += s.len(); +- +- Ok(()) +- } +-} +- + //-------------------------------------------------------------------------------------------------- +-// BSP-public ++// Global BSP driver instances + //-------------------------------------------------------------------------------------------------- + +-/// The main struct. +-pub struct QEMUOutput { +- inner: NullLock, +-} +- +-impl QEMUOutput { +- pub const fn new() -> QEMUOutput { +- QEMUOutput { +- inner: NullLock::new(QEMUOutputInner::new()), +- } +- } +-} ++static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; ++static PL011_UART: driver::PL011Uart = ++ unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + + //-------------------------------------------------------------------------------------------------- +-// OS interface implementations ++// Implementation of the kernel's BSP calls + //-------------------------------------------------------------------------------------------------- + +-/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to +-/// serialize access. +-impl interface::console::Write for QEMUOutput { +- fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { +- use interface::sync::Mutex; +- +- // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase +- // readability. +- let mut r = &self.inner; +- r.lock(|inner| fmt::Write::write_fmt(inner, args)) ++/// Board identification. ++pub fn board_name() -> &'static str { ++ #[cfg(feature = "bsp_rpi3")] ++ { ++ "Raspberry Pi 3" + } +-} +- +-impl interface::console::Read for QEMUOutput {} + +-impl interface::console::Statistics for QEMUOutput { +- fn chars_written(&self) -> usize { +- use interface::sync::Mutex; +- +- let mut r = &self.inner; +- r.lock(|inner| inner.chars_written) ++ #[cfg(feature = "bsp_rpi4")] ++ { ++ "Raspberry Pi 4" + } + } + +-//-------------------------------------------------------------------------------------------------- +-// Global instances +-//-------------------------------------------------------------------------------------------------- +- +-static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new(); +- +-//-------------------------------------------------------------------------------------------------- +-// Implementation of the kernel's BSP calls +-//-------------------------------------------------------------------------------------------------- +- + /// Return a reference to a `console::All` implementation. + pub fn console() -> &'static impl interface::console::All { +- &QEMU_OUTPUT ++ &PL011_UART ++} ++ ++/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. ++/// ++/// # Safety ++/// ++/// The order of devices is the order in which `DeviceDriver::init()` is called. ++pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { ++ [&GPIO, &PL011_UART] ++} ++ ++/// BSP initialization code that runs after driver init. ++pub fn post_driver_init() { ++ // Configure PL011Uart's output pins. ++ GPIO.map_pl011_uart(); + } + +diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs +--- 05_safe_globals/src/bsp.rs ++++ 06_drivers_gpio_uart/src/bsp.rs +@@ -4,8 +4,10 @@ + + //! Conditional exporting of Board Support Packages. + +-#[cfg(feature = "bsp_rpi3")] ++mod driver; ++ ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] + mod rpi; + +-#[cfg(feature = "bsp_rpi3")] ++#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] + pub use rpi::*; + +diff -uNr 05_safe_globals/src/interface.rs 06_drivers_gpio_uart/src/interface.rs +--- 05_safe_globals/src/interface.rs ++++ 06_drivers_gpio_uart/src/interface.rs +@@ -24,6 +24,7 @@ + + /// Console write functions. + pub trait Write { ++ fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + } + +@@ -83,3 +84,20 @@ + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } + } ++ ++/// Driver interfaces. ++pub mod driver { ++ /// Driver result type, e.g. for indicating successful driver init. ++ pub type Result = core::result::Result<(), ()>; ++ ++ /// Device Driver functions. ++ pub trait DeviceDriver { ++ /// Return a compatibility string for identifying the driver. ++ fn compatible(&self) -> &str; ++ ++ /// Called by the kernel to bring up the device. ++ fn init(&self) -> Result { ++ Ok(()) ++ } ++ } ++} + +diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs +--- 05_safe_globals/src/main.rs ++++ 06_drivers_gpio_uart/src/main.rs +@@ -41,16 +41,50 @@ + + /// Early init code. + /// ++/// Concerned with with initializing `BSP` and `arch` parts. ++/// + /// # Safety + /// + /// - Only a single core must be active and running this function. ++/// - The init calls in this function must appear in the correct order. + unsafe fn kernel_init() -> ! { +- use interface::console::Statistics; ++ for i in bsp::device_drivers().iter() { ++ if let Err(()) = i.init() { ++ // This message will only be readable if, at the time of failure, the return value of ++ // `bsp::console()` is already in functioning state. ++ panic!("Error loading driver: {}", i.compatible()) ++ } ++ } ++ ++ bsp::post_driver_init(); ++ ++ // Transition from unsafe to safe. ++ kernel_main() ++} ++ ++/// The main function running after the early init. ++fn kernel_main() -> ! { ++ use interface::console::All; ++ ++ // UART should be functional now. Wait for user to hit Enter. ++ loop { ++ if bsp::console().read_char() == '\n' { ++ break; ++ } ++ } ++ ++ println!("[0] Booting on: {}", bsp::board_name()); + +- println!("[0] Hello from pure Rust!"); ++ println!("[1] Drivers loaded:"); ++ for (i, driver) in bsp::device_drivers().iter().enumerate() { ++ println!(" {}. {}", i + 1, driver.compatible()); ++ } + +- println!("[1] Chars written: {}", bsp::console().chars_written()); ++ println!("[2] Chars written: {}", bsp::console().chars_written()); ++ println!("[3] Echoing input now"); + +- println!("[2] Stopping here."); +- arch::wait_forever() ++ loop { ++ let c = bsp::console().read_char(); ++ bsp::console().write_char(c); ++ } + } + +``` diff --git a/0A_power/kernel8 b/06_drivers_gpio_uart/kernel similarity index 78% rename from 0A_power/kernel8 rename to 06_drivers_gpio_uart/kernel index 3e949a47..7fdc4e81 100755 Binary files a/0A_power/kernel8 and b/06_drivers_gpio_uart/kernel differ diff --git a/06_drivers_gpio_uart/kernel8.img b/06_drivers_gpio_uart/kernel8.img new file mode 100755 index 00000000..dd0d3ae5 Binary files /dev/null and b/06_drivers_gpio_uart/kernel8.img differ diff --git a/06_drivers_gpio_uart/src/arch.rs b/06_drivers_gpio_uart/src/arch.rs new file mode 100644 index 00000000..b1f035c5 --- /dev/null +++ b/06_drivers_gpio_uart/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/06_drivers_gpio_uart/src/arch/aarch64.rs b/06_drivers_gpio_uart/src/arch/aarch64.rs new file mode 100644 index 00000000..f2b79084 --- /dev/null +++ b/06_drivers_gpio_uart/src/arch/aarch64.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; + +use crate::bsp; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/06_drivers_gpio_uart/src/arch/aarch64/sync.rs b/06_drivers_gpio_uart/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/06_drivers_gpio_uart/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/06_drivers_gpio_uart/src/bsp.rs b/06_drivers_gpio_uart/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/06_drivers_gpio_uart/src/bsp/driver.rs b/06_drivers_gpio_uart/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/06_drivers_gpio_uart/src/bsp/driver/bcm.rs b/06_drivers_gpio_uart/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/0E_cache_performance/src/gpio.rs b/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs similarity index 57% rename from 0E_cache_performance/src/gpio.rs rename to 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs index 7affea08..a9ceda61 100644 --- a/0E_cache_performance/src/gpio.rs +++ b/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -1,30 +1,15 @@ -/* - * 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. - */ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; use core::ops; use register::{mmio::ReadWrite, register_bitfields}; +// GPIO registers. +// // Descriptions taken from // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf register_bitfields! { @@ -36,8 +21,7 @@ register_bitfields! { FSEL15 OFFSET(15) NUMBITS(3) [ Input = 0b000, Output = 0b001, - RXD0 = 0b100, // UART0 - Alternate function 0 - RXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART RX ], @@ -45,8 +29,7 @@ register_bitfields! { FSEL14 OFFSET(12) NUMBITS(3) [ Input = 0b000, Output = 0b001, - TXD0 = 0b100, // UART0 - Alternate function 0 - TXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART TX ] ], @@ -95,12 +78,13 @@ pub struct RegisterBlock { pub GPPUDCLK1: ReadWrite, // 0x9C } -/// Public interface to the GPIO MMIO area -pub struct GPIO { +/// The driver's private data. +struct GPIOInner { base_addr: usize, } -impl ops::Deref for GPIO { +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { type Target = RegisterBlock; fn deref(&self) -> &Self::Target { @@ -108,13 +92,66 @@ impl ops::Deref for GPIO { } } -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } } - /// Returns a pointer to the register block + /// Return a pointer to the register block. fn ptr(&self) -> *const RegisterBlock { self.base_addr as *const _ } } + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..05172871 --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// 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 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + ret + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/06_drivers_gpio_uart/src/bsp/rpi.rs b/06_drivers_gpio_uart/src/bsp/rpi.rs new file mode 100644 index 00000000..c22c47bb --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/rpi.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} diff --git a/06_drivers_gpio_uart/src/bsp/rpi/link.ld b/06_drivers_gpio_uart/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs b/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/06_drivers_gpio_uart/src/interface.rs b/06_drivers_gpio_uart/src/interface.rs new file mode 100644 index 00000000..683faa84 --- /dev/null +++ b/06_drivers_gpio_uart/src/interface.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} diff --git a/06_drivers_gpio_uart/src/main.rs b/06_drivers_gpio_uart/src/main.rs new file mode 100644 index 00000000..894e0d52 --- /dev/null +++ b/06_drivers_gpio_uart/src/main.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } + + bsp::post_driver_init(); + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use interface::console::All; + + // UART should be functional now. Wait for user to hit Enter. + loop { + if bsp::console().read_char() == '\n' { + break; + } + } + + println!("[0] Booting on: {}", bsp::board_name()); + + println!("[1] Drivers loaded:"); + for (i, driver) in bsp::device_drivers().iter().enumerate() { + println!(" {}. {}", i + 1, driver.compatible()); + } + + println!("[2] Chars written: {}", bsp::console().chars_written()); + println!("[3] Echoing input now"); + + loop { + let c = bsp::console().read_char(); + bsp::console().write_char(c); + } +} diff --git a/06_drivers_gpio_uart/src/panic_wait.rs b/06_drivers_gpio_uart/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/06_drivers_gpio_uart/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/06_drivers_gpio_uart/src/print.rs b/06_drivers_gpio_uart/src/print.rs new file mode 100644 index 00000000..ce6587b5 --- /dev/null +++ b/06_drivers_gpio_uart/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/06_drivers_gpio_uart/src/runtime_init.rs b/06_drivers_gpio_uart/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/06_drivers_gpio_uart/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/06_raspbootin64/.cargo/config b/06_raspbootin64/.cargo/config deleted file mode 100644 index c8c5ae10..00000000 --- a/06_raspbootin64/.cargo/config +++ /dev/null @@ -1,6 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", - "-C", "relocation-model=pic", -] diff --git a/06_raspbootin64/Cargo.toml b/06_raspbootin64/Cargo.toml deleted file mode 100644 index 38cac1c9..00000000 --- a/06_raspbootin64/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot/pic" diff --git a/06_raspbootin64/Makefile b/06_raspbootin64/Makefile deleted file mode 100644 index d1723093..00000000 --- a/06_raspbootin64/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img - -.PHONY: all qemu clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -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/06_raspbootin64/README.md b/06_raspbootin64/README.md deleted file mode 100644 index f7fd6034..00000000 --- a/06_raspbootin64/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# Tutorial 06 - Raspbootin64 - -We are now at a point where we have a running serial connection, but for each -new feature we want to try, we still have to write and exchange the SD card -every time. - -As this tends to get very annoying and also to avoid potential SD card damage, -we create a kernel8.img that will load the real kernel8.img over serial. - -This tutorial is a rewrite of the well known serial boot loader -[raspbootin][bootin] in 64-bit. We only provide one part of the loader, the -kernel receiver, which runs on the RPi. For the other part, the sender, which -runs on your PC, we will rely on the original [raspbootcom][bootcom] utility. - -[bootin]:(https://github.com/mrvn/raspbootin) -[bootcom]:(https://github.com/mrvn/raspbootin/blob/master/raspbootcom/raspbootcom.cc) - -For convenience, it is already packaged in our `raspi3-utils` docker -container. So if you are running a Linux host, it will be as easy as calling -another Makefile target. It will be included starting with the next tutorial, -`07_abstraction`. You can invoke it with: - -```sh -make raspboot -``` - -If you want to use it with earlier versions of this tutorial, here is a bash -command to invoke it: - -```sh -docker run -it --rm \ - --privileged -v /dev/:/dev/ \ - -v $PWD:/work -w /work \ - raspi3-utils \ - raspbootcom /dev/ttyUSB0 kernel8.img -``` - -In any case, if your USB device is enumerated differently, adapt accordingly. - -If you want to send kernels from a Windows machine, I suggest to take a look at -John Cronin's rewrite, [raspbootin-server][w32] which can be compiled for the -Win32 API. Even more, [@milanvidakovic](https://github.com/milanvidakovic) was -so kind to share a [Java version][java] of the kernel sender with you. - -[w32]:(https://github.com/jncronin/rpi-boot/blob/master/raspbootin-server.c) -[java]:(https://github.com/milanvidakovic/Raspbootin64Client) - -## Chain Loading - -In order to load the new kernel to the same address, we have to move ourself out -of the way. It's called `chain loading`: One code loads the next code to the -same position in memory, therefore the latter thinks it was loaded by the -firmware. To implement that, we use a different linking address this time (we -subtract `2048` from the original address). You can check that with: - -```console -ferris@box:~$ cargo nm -- kernel8 | grep _boot_cores -000000000007f800 T _boot_cores -``` - -However, since the GPU loads us to `0x80_000` regardless, as a first action in -our binary, we have to copy our code to that link address. This is added to -`boot_cores.S`: - -```asm - // relocate our code from load address to link address - ldr x1, =0x80000 - ldr x2, =_boot_cores //<- actual link addr (0x80000 - 2048) from link.ld - ldr w3, =__loader_size -3: ldr x4, [x1], #8 - str x4, [x2], #8 - sub w3, w3, #1 - cbnz w3, 3b -``` - -When we're done, the memory at `0x80_000` is free to use. - -We also should minimize the size of the loader, since it will be overwritten by -the newly loaded code anyway. By removing `Uart::puts()` and other functions, -we've managed to shrink the loader's size by some bytes. - -## Position Independent Code (PIC) - -For reasons stated above, our code will initially execute from address -`0x80_000` despite the binary being actually linked to `0x7f_800`. In order to -ensure that our binary will not reference hardcoded addresses that actually -contain no or wrong data, we need to make this binary `position -independent`. This means that all addresses will always be runtime-computable as -an offset to the current `Program Counter`, and not hardcoded. - -To enable PIC for our loader, we add the following line to the compiler flags in -the`.cargo/config`: - -```toml -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", - "-C", "relocation-model=pic", # <-- New -] -``` - -## boot_cores.S - -In addition to the relocation copying, we also need to adjust the branch -instruction that jumps to the reset handler, because we want to jump to _the -relocated reset handler_, not the original one. - -Since rustc now generates jumps relative to the current instruction due to the -`position independence`, we can leverage this feature and add the same offset -to the reset address that we implicitly used for the relocation copying (`2048`). -This ensures that we jump to the reset handler _in the relocated loader code_. - -## Linker and Boot Code - -We use a different linking address this time. We calculate our code's size to -know how many bytes we have to copy. - -Additionally, we can remove the `bss section` entirely, since our loader does -not use any static variables. - -## main.rs - -We print 'RBIN64', receive the new kernel over serial and save it at the memory -address where the start.elf would have been loaded it. When finished, we restore -the arguments and jump to the new kernel using an absolute address. diff --git a/06_raspbootin64/kernel8 b/06_raspbootin64/kernel8 deleted file mode 100755 index 23dda14c..00000000 Binary files a/06_raspbootin64/kernel8 and /dev/null differ diff --git a/06_raspbootin64/kernel8.img b/06_raspbootin64/kernel8.img deleted file mode 100755 index 0c4e6b52..00000000 Binary files a/06_raspbootin64/kernel8.img and /dev/null differ diff --git a/06_raspbootin64/link.ld b/06_raspbootin64/link.ld deleted file mode 100644 index 90ad85e4..00000000 --- a/06_raspbootin64/link.ld +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 - 2048; - - _code = .; - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - _end = .; - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} - -__loader_size = (_end - _code) >> 3; diff --git a/06_raspbootin64/raspi3_boot/Cargo.toml b/06_raspbootin64/raspi3_boot/Cargo.toml deleted file mode 100644 index 1c3c1c69..00000000 --- a/06_raspbootin64/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/06_raspbootin64/raspi3_boot/src/boot_cores.S b/06_raspbootin64/raspi3_boot/src/boot_cores.S deleted file mode 100644 index e1a7fa55..00000000 --- a/06_raspbootin64/raspi3_boot/src/boot_cores.S +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * 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. - * - */ - -.section ".text.boot" - -.global _boot_cores - -_boot_cores: - // read cpu id, stop slave cores - mrs x1, mpidr_el1 - and x1, x1, #3 - cbz x1, 2f - // cpu id > 0, stop -1: wfe - b 1b -2: // cpu id == 0 - - // relocate our code from load address to link address - ldr x1, =0x80000 - ldr x2, =_boot_cores - ldr w3, =__loader_size -3: ldr x4, [x1], #8 - str x4, [x2], #8 - sub w3, w3, #1 - cbnz w3, 3b - - // set stack before our code - ldr x1, =_boot_cores - mov sp, x1 - - // jump to relocated Rust code, should not return - bl reset-2048 - // for failsafe, halt this core too - b 1b diff --git a/06_raspbootin64/raspi3_boot/src/lib.rs b/06_raspbootin64/raspi3_boot/src/lib.rs deleted file mode 100644 index 2e29dceb..00000000 --- a/06_raspbootin64/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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] -#![feature(global_asm)] - -//! 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[export_name = "main"] - pub unsafe fn __main() -> ! { - // type check the given path - let f: fn() -> ! = $path; - - f() - } - }; -} - -/// Reset function. -/// -/// Trampolines into the user's `main()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -#[no_mangle] -pub unsafe extern "C" fn reset() -> ! { - extern "Rust" { - fn main() -> !; - } - - main(); -} - -// Disable all cores except core 0, and then jump to reset() -global_asm!(include_str!("boot_cores.S")); diff --git a/06_raspbootin64/src/gpio.rs b/06_raspbootin64/src/gpio.rs deleted file mode 100644 index da6a5be4..00000000 --- a/06_raspbootin64/src/gpio.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/06_raspbootin64/src/main.rs b/06_raspbootin64/src/main.rs deleted file mode 100644 index ff0bb94e..00000000 --- a/06_raspbootin64/src/main.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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)] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod mbox; -mod uart; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - - // set up serial console - if uart.init(&mut mbox).is_err() { - unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early - } - - // Say hello - for c in "RBIN64\r\n".chars() { - uart.send(c); - } - - // Notify raspbootcom to send the kernel - uart.send(3 as char); - uart.send(3 as char); - uart.send(3 as char); - - // Read the kernel's size - let mut size: u32 = u32::from(uart.getc()); - size |= u32::from(uart.getc()) << 8; - size |= u32::from(uart.getc()) << 16; - size |= u32::from(uart.getc()) << 24; - - // For now, blindly trust it's not too big - uart.send('O'); - uart.send('K'); - - let kernel_addr: *mut u8 = 0x80_000 as *mut u8; - unsafe { - // Read the kernel byte by byte - for i in 0..size { - *kernel_addr.offset(i as isize) = uart.getc(); - } - } - - // Use black magic to get a function pointer to 0x80_000 - let kernel: extern "C" fn() -> ! = unsafe { core::mem::transmute(kernel_addr as *const ()) }; - - // Jump to loaded kernel and never return! - kernel() -} - -raspi3_boot::entry!(kernel_entry); diff --git a/06_raspbootin64/src/mbox.rs b/06_raspbootin64/src/mbox.rs deleted file mode 100644 index d6343bee..00000000 --- a/06_raspbootin64/src/mbox.rs +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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::{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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - 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/06_raspbootin64/src/uart.rs b/06_raspbootin64/src/uart.rs deleted file mode 100644 index 26b7a0b7..00000000 --- a/06_raspbootin64/src/uart.rs +++ /dev/null @@ -1,246 +0,0 @@ -/* - * 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::gpio; -use crate::mbox; -use core::{ - ops, - sync::atomic::{compiler_fence, Ordering}, -}; -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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self, mbox: &mut mbox::Mbox) -> 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 - unsafe { - (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); - - (*gpio::GPPUD).set(0); // enable pins 14 and 15 - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*gpio::GPPUDCLK0).modify( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm!("nop" :::: "volatile"); - } - - (*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; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // write the character to the buffer - self.DR.set(c as u32); - } - - /// Receive a character - pub fn getc(&self) -> u8 { - // wait until something is in the buffer - loop { - if !self.FR.is_set(FR::RXFE) { - break; - } - - unsafe { asm!("nop" :::: "volatile") }; - } - - // read it and return - self.DR.get() as u8 - } -} diff --git a/07_abstraction/.cargo/config b/07_abstraction/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/07_abstraction/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/07_abstraction/Cargo.lock b/07_abstraction/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/07_abstraction/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/07_abstraction/Cargo.toml b/07_abstraction/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/07_abstraction/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/07_abstraction/Makefile b/07_abstraction/Makefile deleted file mode 100644 index 411b9bc3..00000000 --- a/07_abstraction/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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/07_abstraction/README.md b/07_abstraction/README.md deleted file mode 100644 index 1b0517b4..00000000 --- a/07_abstraction/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# Tutorial 07 - Abstraction - -This is a short one regarding code changes, but has lots of text because two -important Rust principles are introduced: Abstraction and modularity. - -From a functional perspective, this tutorial is the same as `05_uart0`, but with -the key difference that we threw out all manually crafted assembler. Both the -main and the boot crate do not use `#![feature(global_asm)]` or -`#![feature(asm)]` anymore. Instead, we pulled in the [cortex-a][crate] crate, -which now provides `cortex-a` specific features like register access or safe -wrappers around assembly instructions. - -[crate]: https://github.com/andre-richter/cortex-a - -For single assembler instructions, we now have the `cortex-a::asm` namespace, -e.g. providing `asm::nop()`. - -For registers, there is `cortex-a::regs`. The interface is the same as we have -it for MMIO accesses, aka provided by [register-rs][register-rs] and therefore -based on [tock-regs][tock-regs]. For registers like the stack pointer, which are -generally read and written as a whole, there's the common [get()][get] and -[set()][set] functions which take and return primitive integer types. - -[register-rs]: https://github.com/rust-osdev/register-rs -[tock-regs]: https://github.com/tock/tock/tree/master/libraries/tock-register-interface -[get]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/sp/trait.RegisterReadWrite.html#tymethod.get -[set]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/sp/trait.RegisterReadWrite.html#tymethod.set - -Registers that are divided into multiple fields, like `CNTP_CTL_EL0`, on the -other hand, are backed by a respective [type][cntp_type] definition that allow -for fine-grained reading and modifications. - -[cntp_type]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/cntp_ctl_el0/CNTP_CTL_EL0/index.html - -The register API is based on the [tock project's][tock] register -interface. Please see [their homepage][tock_registers] for all the details. - -[tock]: https://github.com/tock/tock -[tock_registers]: https://github.com/tock/tock/tree/master/libraries/tock-register-interface - -To some extent, this namespacing also makes our code more portable. For example, -if we want to reuse parts of it on another processor architecture, we could pull -in the respective crate and change our use-clause from `use cortex-a::asm` to -`use new_architecture::asm`. Of course this also demands that both crates adhere -to a common set of wrappers that provide the same functionality. Assembler and -register instructions like we use them here are actually a weak example. Where -this modular approach can really pay off is for common peripherals like timers -or memory management units, where implementations differ between processors, but -usage is often the same (e.g. setting a timer for x amount of microseconds). - -In Rust, we have the [Embedded Devices Working -Group](https://github.com/rust-lang-nursery/embedded-wg), which among other -goals, tries to establish a common set of wrapper- and interface-crates that -introduce abstraction on different levels of the system. Check out the [Awesome -Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust) list for -an overview. - -## Boot Code - -Like mentioned above, we threw out the `boot_cores.S` assembler file and -replaced it with a Rust function. Why? Because we can, for the fun of it. - -```rust -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} -``` - -Since this is the first code that the RPi3 will execute, the stack has not been -set up yet. Actually it is this function that will do it for the first -time. Therefore, it is important to check that code generated from this function -does not call any subroutines that need a working stack themselves. - -The `get()` and `asm` wrappers that we use from the `cortex-a` crate are all -inlined, so we fulfill this requirement. The compilation result of this function -should yield something like the following, where you can see that the stack -pointer is not used apart from ourselves setting it. - -```console -ferris@box:~$ cargo objdump --target aarch64-unknown-none-softfloat -- -disassemble -print-imm-hex kernel8 - -[...] (Some output omitted) - -_boot_cores: - 80000: a8 00 38 d5 mrs x8, MPIDR_EL1 - 80004: 1f 05 40 f2 tst x8, #0x3 - 80008: 60 00 00 54 b.eq #0xc <_boot_cores+0x14> - 8000c: 5f 20 03 d5 wfe - 80010: ff ff ff 17 b #-0x4 <_boot_cores+0xc> - 80014: e8 03 0d 32 orr w8, wzr, #0x80000 - 80018: 1f 01 00 91 mov sp, x8 - 8001c: e0 01 00 94 bl #0x780 -``` - -It is important to always manually check this, and not blindly rely on the -compiler. - -## Test it - -Since this is the first tutorial after we've written our own bootloader over -serial, you can now for the first time test this convenient interface: - -```sh -make raspboot -``` diff --git a/07_abstraction/kernel8.img b/07_abstraction/kernel8.img deleted file mode 100755 index c76164f1..00000000 Binary files a/07_abstraction/kernel8.img and /dev/null differ diff --git a/07_abstraction/link.ld b/07_abstraction/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/07_abstraction/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/07_abstraction/raspi3_boot/Cargo.toml b/07_abstraction/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/07_abstraction/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/07_abstraction/raspi3_boot/src/lib.rs b/07_abstraction/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/07_abstraction/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/07_abstraction/src/gpio.rs b/07_abstraction/src/gpio.rs deleted file mode 100644 index da6a5be4..00000000 --- a/07_abstraction/src/gpio.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/07_abstraction/src/main.rs b/07_abstraction/src/main.rs deleted file mode 100644 index 937b662d..00000000 --- a/07_abstraction/src/main.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod mbox; -mod uart; - -use core::sync::atomic::{compiler_fence, Ordering}; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - - // set up serial console - match uart.init(&mut mbox) { - 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"); - - // get the board's unique serial number with a mailbox call - mbox.buffer[0] = 8 * 4; // length of the message - mbox.buffer[1] = mbox::REQUEST; // this is a request message - mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command - mbox.buffer[3] = 8; // buffer size - mbox.buffer[4] = 8; - mbox.buffer[5] = 0; // clear output buffer - mbox.buffer[6] = 0; - mbox.buffer[7] = 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); - - // send the message to the GPU and receive answer - let serial_avail = match mbox.call(mbox::channel::PROP) { - Err(_) => false, - Ok(()) => true, - }; - - if serial_avail { - uart.puts("[i] My serial number is: 0x"); - uart.hex(mbox.buffer[6]); - uart.hex(mbox.buffer[5]); - uart.puts("\n"); - } else { - uart.puts("[i] Unable to query serial!\n"); - } - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/07_abstraction/src/mbox.rs b/07_abstraction/src/mbox.rs deleted file mode 100644 index 5b7e3589..00000000 --- a/07_abstraction/src/mbox.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 GETSERIAL: u32 = 0x10004; - 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/07_abstraction/src/uart.rs b/07_abstraction/src/uart.rs deleted file mode 100644 index db4c639a..00000000 --- a/07_abstraction/src/uart.rs +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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::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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self, mbox: &mut mbox::Mbox) -> 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 - unsafe { - (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); - - (*gpio::GPPUD).set(0); // enable pins 14 and 15 - for _ in 0..150 { - asm::nop(); - } - - (*gpio::GPPUDCLK0).modify( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm::nop(); - } - - (*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: u32) { - let mut n; - - for i in 0..8 { - // get highest tetrad - n = d.wrapping_shr(28 - 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/07_uart_chainloader/.vscode/settings.json b/07_uart_chainloader/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/07_uart_chainloader/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/05_uart0/Cargo.lock b/07_uart_chainloader/Cargo.lock similarity index 78% rename from 05_uart0/Cargo.lock rename to 07_uart_chainloader/Cargo.lock index 82ecd2c6..9a17339b 100644 --- a/05_uart0/Cargo.lock +++ b/07_uart_chainloader/Cargo.lock @@ -1,31 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "kernel8" -version = "0.1.0" +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "raspi3_boot 0.1.0", "register 0.3.3 (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" +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "r0" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "raspi3_boot" -version = "0.1.0" -dependencies = [ - "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.3" @@ -40,7 +36,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" "checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/07_uart_chainloader/Cargo.toml b/07_uart_chainloader/Cargo.toml new file mode 100644 index 00000000..cf0f0636 --- /dev/null +++ b/07_uart_chainloader/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/07_uart_chainloader/Makefile b/07_uart_chainloader/Makefile new file mode 100644 index 00000000..f411e281 --- /dev/null +++ b/07_uart_chainloader/Makefile @@ -0,0 +1,108 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic + CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic + CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu qemuasm chainboot clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." + +qemuasm: + @echo "This board is not yet supported for QEMU." +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +qemuasm: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) -d in_asm +endif + +chainboot: + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(CHAINBOOT_DEMO_PAYLOAD) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/07_uart_chainloader/README.md b/07_uart_chainloader/README.md new file mode 100644 index 00000000..bc4ab3bd --- /dev/null +++ b/07_uart_chainloader/README.md @@ -0,0 +1,472 @@ +# Tutorial 07 - UART Chainloader + +## tl;dr + +Running from an SD card was a nice experience, but it would be extremely tedious +to do it for every new binary. Let's write a [chainloader] using [position +independent code]. This will be the last binary you need to put on the SD card +for quite some time. Each following tutorial will provide a `chainboot` target in +the `Makefile` that lets you conveniently load the kernel over `UART`. + +Our chainloader is called `MiniLoad` and is inspired by [raspbootin]. + +[chainloader]: https://en.wikipedia.org/wiki/Chain_loading +[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code +[raspbootin]: https://github.com/mrvn/raspbootin + +You can try it with this tutorial already: +1. Depending on your target hardware:`make` or `BSP=rpi4 make`. +2. Copy `kernel8.img` to the SD card. +3. Execute `make chainboot` or `BSP=rpi4 make chainboot`. +4. Now plug in the USB Serial. +5. Observe the loader fetching a kernel over `UART`: + +```console +make chainboot +[...] +### Listening on /dev/ttyUSB0 + __ __ _ _ _ _ +| \/ (_)_ _ (_) | ___ __ _ __| | +| |\/| | | ' \| | |__/ _ \/ _` / _` | +|_| |_|_|_||_|_|____\___/\__,_\__,_| + + Raspberry Pi 3 + +[ML] Requestibinary +### sending kernel demo_payload_rpi3.img [7840 byte] +### finished sending +[ML] Loaded! Executing the payload now + +[0] Booting on: Raspberry Pi 3 +[1] Drivers loaded: + 1. GPIO + 2. PL011Uart +[2] Chars written: 84 +[3] Echoing input now +``` + +In this tutorial, a version of the kernel from the previous tutorial is loaded +for demo purposes. In subsequent tuts, it will be the working directory's +kernel. + +### Observing the jump + +The `Makefile` in this tutorial has an additional target, `qemuasm`, that lets +you nicely observe the jump from the loaded address (`0x80_XXX`) to the +relocated code at (`0x3EFF_0XXX`): + +```console +make qemuasm +[...] +IN: +0x000809fc: d0000008 adrp x8, #0x82000 +0x00080a00: 52800020 movz w0, #0x1 +0x00080a04: f9408908 ldr x8, [x8, #0x110] +0x00080a08: d63f0100 blr x8 + +---------------- +IN: +0x3eff0528: d0000008 adrp x8, #0x3eff2000 +0x3eff052c: d0000009 adrp x9, #0x3eff2000 +0x3eff0530: f9411508 ldr x8, [x8, #0x228] +0x3eff0534: f9411929 ldr x9, [x9, #0x230] +0x3eff0538: eb08013f cmp x9, x8 +0x3eff053c: 540000c2 b.hs #0x3eff0554 +[...] +``` + +## Diff to previous +```diff +Binary files 06_drivers_gpio_uart/demo_payload_rpi3.img and 07_uart_chainloader/demo_payload_rpi3.img differ +Binary files 06_drivers_gpio_uart/demo_payload_rpi4.img and 07_uart_chainloader/demo_payload_rpi4.img differ + +diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile +--- 06_drivers_gpio_uart/Makefile ++++ 07_uart_chainloader/Makefile +@@ -15,7 +15,8 @@ + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld +- RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 ++ RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic ++ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img + else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +@@ -23,7 +24,8 @@ + # QEMU_MACHINE_TYPE = + # QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld +- RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 ++ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic ++ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img + endif + + SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) +@@ -47,9 +49,14 @@ + + DOCKER_CMD = docker run -it --rm + DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +-DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) ++DOCKER_ARG_TTY = --privileged -v /dev:/dev + +-.PHONY: all doc qemu clippy clean readelf objdump nm ++DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) ++DOCKER_EXEC_RASPBOOT = raspbootcom ++DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 ++# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 ++ ++.PHONY: all doc qemu qemuasm chainboot clippy clean readelf objdump nm + + all: clean $(OUTPUT) + +@@ -67,12 +74,24 @@ + ifeq ($(QEMU_MACHINE_TYPE),) + qemu: + @echo "This board is not yet supported for QEMU." ++ ++qemuasm: ++ @echo "This board is not yet supported for QEMU." + else + qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) ++ ++qemuasm: all ++ $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ ++ $(DOCKER_EXEC_QEMU) -d in_asm + endif + ++chainboot: ++ $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ ++ $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ ++ $(CHAINBOOT_DEMO_PAYLOAD) ++ + clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + + +diff -uNr 06_drivers_gpio_uart/src/arch/aarch64.rs 07_uart_chainloader/src/arch/aarch64.rs +--- 06_drivers_gpio_uart/src/arch/aarch64.rs ++++ 07_uart_chainloader/src/arch/aarch64.rs +@@ -22,7 +22,7 @@ + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); +- crate::runtime_init::init() ++ crate::relocate::relocate_self::() + } else { + // if not core0, infinitely wait for events + wait_forever() + +diff -uNr 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs 07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +--- 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs ++++ 07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +@@ -272,6 +272,18 @@ + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } ++ ++ fn flush(&self) { ++ let mut r = &self.inner; ++ // Spin until the TX FIFO empty flag is set. ++ r.lock(|inner| loop { ++ if inner.FR.is_set(FR::TXFE) { ++ break; ++ } ++ ++ arch::nop(); ++ }); ++ } + } + + impl interface::console::Read for PL011Uart { +@@ -288,14 +300,19 @@ + } + + // Read one character. +- let mut ret = inner.DR.get() as u8 as char; ++ inner.DR.get() as u8 as char ++ }) ++ } + +- // Convert carrige return to newline. +- if ret == '\r' { +- ret = '\n' ++ fn clear(&self) { ++ let mut r = &self.inner; ++ r.lock(|inner| loop { ++ // Read from the RX FIFO until the empty bit is '1'. ++ if !inner.FR.is_set(FR::RXFE) { ++ inner.DR.get(); ++ } else { ++ break; + } +- +- ret + }) + } + } + +diff -uNr 06_drivers_gpio_uart/src/bsp/rpi/link.ld 07_uart_chainloader/src/bsp/rpi/link.ld +--- 06_drivers_gpio_uart/src/bsp/rpi/link.ld ++++ 07_uart_chainloader/src/bsp/rpi/link.ld +@@ -5,9 +5,10 @@ + + SECTIONS + { +- /* Set current address to the value from which the RPi starts execution */ +- . = 0x80000; ++ /* Set the link address to the top-most 40 KiB of DRAM (assuming 1GiB) */ ++ . = 0x3F000000 - 0x10000; + ++ __binary_start = .; + .text : + { + *(.text._start) *(.text*) +@@ -31,5 +32,14 @@ + __bss_end = .; + } + ++ .got : ++ { ++ *(.got*) ++ } ++ ++ /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */ ++ . = ALIGN(8); ++ __binary_end = .; ++ + /DISCARD/ : { *(.comment*) } + } + +diff -uNr 06_drivers_gpio_uart/src/bsp/rpi.rs 07_uart_chainloader/src/bsp/rpi.rs +--- 06_drivers_gpio_uart/src/bsp/rpi.rs ++++ 07_uart_chainloader/src/bsp/rpi.rs +@@ -12,6 +12,9 @@ + pub const BOOT_CORE_ID: u64 = 0; + pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + ++/// The address on which the RPi3 firmware loads every binary by default. ++pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x80_000; ++ + //-------------------------------------------------------------------------------------------------- + // Global BSP driver instances + //-------------------------------------------------------------------------------------------------- + +diff -uNr 06_drivers_gpio_uart/src/interface.rs 07_uart_chainloader/src/interface.rs +--- 06_drivers_gpio_uart/src/interface.rs ++++ 07_uart_chainloader/src/interface.rs +@@ -26,6 +26,10 @@ + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; ++ ++ /// Block execution until the last character has been physically put on the TX wire ++ /// (draining TX buffers/FIFOs, if any). ++ fn flush(&self); + } + + /// Console read functions. +@@ -33,6 +37,9 @@ + fn read_char(&self) -> char { + ' ' + } ++ ++ /// Clear RX buffers, if any. ++ fn clear(&self); + } + + /// Console statistics. + +diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs +--- 06_drivers_gpio_uart/src/main.rs ++++ 07_uart_chainloader/src/main.rs +@@ -29,7 +29,11 @@ + // the first function to run. + mod arch; + +-// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. ++// `_start()` then calls `relocate::relocate_self()`. ++mod relocate; ++ ++// `relocate::relocate_self()` calls `runtime_init::init()`, which on completion, jumps to ++// `kernel_init()`. + mod runtime_init; + + // Conditionally includes the selected `BSP` code. +@@ -66,25 +70,48 @@ + fn kernel_main() -> ! { + use interface::console::All; + +- // UART should be functional now. Wait for user to hit Enter. +- loop { +- if bsp::console().read_char() == '\n' { +- break; +- } ++ println!(" __ __ _ _ _ _ "); ++ println!("| \\/ (_)_ _ (_) | ___ __ _ __| |"); ++ println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |"); ++ println!("|_| |_|_|_||_|_|____\\___/\\__,_\\__,_|"); ++ println!(); ++ println!("{:^37}", bsp::board_name()); ++ println!(); ++ println!("[ML] Requesting binary"); ++ bsp::console().flush(); ++ ++ // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader ++ // protocol. ++ bsp::console().clear(); ++ ++ // Notify raspbootcom to send the binary. ++ for _ in 0..3 { ++ bsp::console().write_char(3 as char); + } + +- println!("[0] Booting on: {}", bsp::board_name()); +- +- println!("[1] Drivers loaded:"); +- for (i, driver) in bsp::device_drivers().iter().enumerate() { +- println!(" {}. {}", i + 1, driver.compatible()); ++ // Read the binary's size. ++ let mut size: u32 = u32::from(bsp::console().read_char() as u8); ++ size |= u32::from(bsp::console().read_char() as u8) << 8; ++ size |= u32::from(bsp::console().read_char() as u8) << 16; ++ size |= u32::from(bsp::console().read_char() as u8) << 24; ++ ++ // Trust it's not too big. ++ print!("OK"); ++ ++ let kernel_addr: *mut u8 = bsp::BOARD_DEFAULT_LOAD_ADDRESS as *mut u8; ++ unsafe { ++ // Read the kernel byte by byte. ++ for i in 0..size { ++ *kernel_addr.offset(i as isize) = bsp::console().read_char() as u8; ++ } + } + +- println!("[2] Chars written: {}", bsp::console().chars_written()); +- println!("[3] Echoing input now"); ++ println!("[ML] Loaded! Executing the payload now\n"); ++ bsp::console().flush(); + +- loop { +- let c = bsp::console().read_char(); +- bsp::console().write_char(c); +- } ++ // Use black magic to get a function pointer. ++ let kernel: extern "C" fn() -> ! = unsafe { core::mem::transmute(kernel_addr as *const ()) }; ++ ++ // Jump to loaded kernel! ++ kernel() + } + +diff -uNr 06_drivers_gpio_uart/src/relocate.rs 07_uart_chainloader/src/relocate.rs +--- 06_drivers_gpio_uart/src/relocate.rs ++++ 07_uart_chainloader/src/relocate.rs +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Relocation code. ++ ++/// Relocates the own binary from `bsp::BOARD_DEFAULT_LOAD_ADDRESS` to the `__binary_start` address ++/// from the linker script. ++/// ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++/// - Function must not use the `bss` section. ++pub unsafe fn relocate_self() -> ! { ++ extern "C" { ++ static __binary_start: usize; ++ static __binary_end: usize; ++ } ++ ++ let binary_start_addr: usize = &__binary_start as *const _ as _; ++ let binary_end_addr: usize = &__binary_end as *const _ as _; ++ let binary_size_in_byte: usize = binary_end_addr - binary_start_addr; ++ ++ // Get the relocation destination address from the linker symbol. ++ let mut reloc_dst_addr: *mut T = binary_start_addr as *mut T; ++ ++ // The address of where the previous firmware loaded us. ++ let mut src_addr: *const T = crate::bsp::BOARD_DEFAULT_LOAD_ADDRESS as *const _; ++ ++ // Copy the whole binary. ++ // ++ // This is essentially a `memcpy()` optimized for throughput by transferring in chunks of T. ++ let n = binary_size_in_byte / core::mem::size_of::(); ++ for _ in 0..n { ++ use core::ptr; ++ ++ ptr::write_volatile::(reloc_dst_addr, ptr::read_volatile::(src_addr)); ++ reloc_dst_addr = reloc_dst_addr.offset(1); ++ src_addr = src_addr.offset(1); ++ } ++ ++ // Call `init()` through a trait object, causing the jump to use an absolute address to reach ++ // the relocated binary. An elaborate explanation can be found in the runtime_init.rs source ++ // comments. ++ crate::runtime_init::get().init() ++} + +diff -uNr 06_drivers_gpio_uart/src/runtime_init.rs 07_uart_chainloader/src/runtime_init.rs +--- 06_drivers_gpio_uart/src/runtime_init.rs ++++ 07_uart_chainloader/src/runtime_init.rs +@@ -4,21 +4,39 @@ + + //! Rust runtime initialization code. + +-/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +-/// init code. ++/// We are outsmarting the compiler here by using a trait as a layer of indirection. Because we are ++/// generating PIC code, a static dispatch to `init()` would generate a relative jump from the ++/// callee to `init()`. However, when calling `init()`, code just finished copying the binary to the ++/// actual link-time address, and hence is still running at whatever location the previous loader ++/// has put it. So we do not want a relative jump, because it would not jump to the relocated code. + /// +-/// # Safety +-/// +-/// - Only a single core must be active and running this function. +-pub unsafe fn init() -> ! { +- extern "C" { +- // Boundaries of the .bss section, provided by the linker script. +- static mut __bss_start: u64; +- static mut __bss_end: u64; ++/// By indirecting through a trait object, we can make use of the property that vtables store ++/// absolute addresses. So calling `init()` this way will kick execution to the relocated binary. ++pub trait RunTimeInit { ++ /// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to ++ /// kernel init code. ++ /// ++ /// # Safety ++ /// ++ /// - Only a single core must be active and running this function. ++ unsafe fn init(&self) -> ! { ++ extern "C" { ++ // Boundaries of the .bss section, provided by the linker script. ++ static mut __bss_start: u64; ++ static mut __bss_end: u64; ++ } ++ ++ // Zero out the .bss section. ++ r0::zero_bss(&mut __bss_start, &mut __bss_end); ++ ++ crate::kernel_init() + } ++} + +- // Zero out the .bss section. +- r0::zero_bss(&mut __bss_start, &mut __bss_end); ++struct Traitor; ++impl RunTimeInit for Traitor {} + +- crate::kernel_init() ++/// Give the callee a `RunTimeInit` trait object. ++pub fn get() -> &'static dyn RunTimeInit { ++ &Traitor {} + } + +``` diff --git a/07_uart_chainloader/demo_payload_rpi3.img b/07_uart_chainloader/demo_payload_rpi3.img new file mode 100755 index 00000000..49823838 Binary files /dev/null and b/07_uart_chainloader/demo_payload_rpi3.img differ diff --git a/07_uart_chainloader/demo_payload_rpi4.img b/07_uart_chainloader/demo_payload_rpi4.img new file mode 100755 index 00000000..68a5b9ee Binary files /dev/null and b/07_uart_chainloader/demo_payload_rpi4.img differ diff --git a/08_random/kernel8 b/07_uart_chainloader/kernel similarity index 76% rename from 08_random/kernel8 rename to 07_uart_chainloader/kernel index 9097108f..0297e9fb 100755 Binary files a/08_random/kernel8 and b/07_uart_chainloader/kernel differ diff --git a/07_uart_chainloader/kernel8.img b/07_uart_chainloader/kernel8.img new file mode 100755 index 00000000..bfe91892 Binary files /dev/null and b/07_uart_chainloader/kernel8.img differ diff --git a/07_uart_chainloader/src/arch.rs b/07_uart_chainloader/src/arch.rs new file mode 100644 index 00000000..b1f035c5 --- /dev/null +++ b/07_uart_chainloader/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/07_uart_chainloader/src/arch/aarch64.rs b/07_uart_chainloader/src/arch/aarch64.rs new file mode 100644 index 00000000..09a908cd --- /dev/null +++ b/07_uart_chainloader/src/arch/aarch64.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; + +use crate::bsp; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::relocate::relocate_self::() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/07_uart_chainloader/src/arch/aarch64/sync.rs b/07_uart_chainloader/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/07_uart_chainloader/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/07_uart_chainloader/src/bsp.rs b/07_uart_chainloader/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/07_uart_chainloader/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/07_uart_chainloader/src/bsp/driver.rs b/07_uart_chainloader/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/07_uart_chainloader/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/07_uart_chainloader/src/bsp/driver/bcm.rs b/07_uart_chainloader/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/07_uart_chainloader/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/0F_globals_synchronization_println/src/devices/hw/gpio.rs b/07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_gpio.rs similarity index 57% rename from 0F_globals_synchronization_println/src/devices/hw/gpio.rs rename to 07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_gpio.rs index 7affea08..a9ceda61 100644 --- a/0F_globals_synchronization_println/src/devices/hw/gpio.rs +++ b/07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -1,30 +1,15 @@ -/* - * 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. - */ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; use core::ops; use register::{mmio::ReadWrite, register_bitfields}; +// GPIO registers. +// // Descriptions taken from // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf register_bitfields! { @@ -36,8 +21,7 @@ register_bitfields! { FSEL15 OFFSET(15) NUMBITS(3) [ Input = 0b000, Output = 0b001, - RXD0 = 0b100, // UART0 - Alternate function 0 - RXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART RX ], @@ -45,8 +29,7 @@ register_bitfields! { FSEL14 OFFSET(12) NUMBITS(3) [ Input = 0b000, Output = 0b001, - TXD0 = 0b100, // UART0 - Alternate function 0 - TXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART TX ] ], @@ -95,12 +78,13 @@ pub struct RegisterBlock { pub GPPUDCLK1: ReadWrite, // 0x9C } -/// Public interface to the GPIO MMIO area -pub struct GPIO { +/// The driver's private data. +struct GPIOInner { base_addr: usize, } -impl ops::Deref for GPIO { +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { type Target = RegisterBlock; fn deref(&self) -> &Self::Target { @@ -108,13 +92,66 @@ impl ops::Deref for GPIO { } } -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } } - /// Returns a pointer to the register block + /// Return a pointer to the register block. fn ptr(&self) -> *const RegisterBlock { self.base_addr as *const _ } } + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..e05a230d --- /dev/null +++ b/07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// 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 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until the TX FIFO empty flag is set. + r.lock(|inner| loop { + if inner.FR.is_set(FR::TXFE) { + break; + } + + arch::nop(); + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + inner.DR.get() as u8 as char + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| loop { + // Read from the RX FIFO until the empty bit is '1'. + if !inner.FR.is_set(FR::RXFE) { + inner.DR.get(); + } else { + break; + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/07_uart_chainloader/src/bsp/rpi.rs b/07_uart_chainloader/src/bsp/rpi.rs new file mode 100644 index 00000000..bfcf041e --- /dev/null +++ b/07_uart_chainloader/src/bsp/rpi.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +/// The address on which the RPi3 firmware loads every binary by default. +pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} diff --git a/07_uart_chainloader/src/bsp/rpi/link.ld b/07_uart_chainloader/src/bsp/rpi/link.ld new file mode 100644 index 00000000..d512c82c --- /dev/null +++ b/07_uart_chainloader/src/bsp/rpi/link.ld @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set the link address to the top-most 40 KiB of DRAM (assuming 1GiB) */ + . = 0x3F000000 - 0x10000; + + __binary_start = .; + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + .got : + { + *(.got*) + } + + /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */ + . = ALIGN(8); + __binary_end = .; + + /DISCARD/ : { *(.comment*) } +} diff --git a/07_uart_chainloader/src/bsp/rpi/memory_map.rs b/07_uart_chainloader/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/07_uart_chainloader/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/07_uart_chainloader/src/interface.rs b/07_uart_chainloader/src/interface.rs new file mode 100644 index 00000000..0f832222 --- /dev/null +++ b/07_uart_chainloader/src/interface.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} diff --git a/07_uart_chainloader/src/main.rs b/07_uart_chainloader/src/main.rs new file mode 100644 index 00000000..89e7ade2 --- /dev/null +++ b/07_uart_chainloader/src/main.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `relocate::relocate_self()`. +mod relocate; + +// `relocate::relocate_self()` calls `runtime_init::init()`, which on completion, jumps to +// `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } + + bsp::post_driver_init(); + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use interface::console::All; + + println!(" __ __ _ _ _ _ "); + println!("| \\/ (_)_ _ (_) | ___ __ _ __| |"); + println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |"); + println!("|_| |_|_|_||_|_|____\\___/\\__,_\\__,_|"); + println!(); + println!("{:^37}", bsp::board_name()); + println!(); + println!("[ML] Requesting binary"); + bsp::console().flush(); + + // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader + // protocol. + bsp::console().clear(); + + // Notify raspbootcom to send the binary. + for _ in 0..3 { + bsp::console().write_char(3 as char); + } + + // Read the binary's size. + let mut size: u32 = u32::from(bsp::console().read_char() as u8); + size |= u32::from(bsp::console().read_char() as u8) << 8; + size |= u32::from(bsp::console().read_char() as u8) << 16; + size |= u32::from(bsp::console().read_char() as u8) << 24; + + // Trust it's not too big. + print!("OK"); + + let kernel_addr: *mut u8 = bsp::BOARD_DEFAULT_LOAD_ADDRESS as *mut u8; + unsafe { + // Read the kernel byte by byte. + for i in 0..size { + *kernel_addr.offset(i as isize) = bsp::console().read_char() as u8; + } + } + + println!("[ML] Loaded! Executing the payload now\n"); + bsp::console().flush(); + + // Use black magic to get a function pointer. + let kernel: extern "C" fn() -> ! = unsafe { core::mem::transmute(kernel_addr as *const ()) }; + + // Jump to loaded kernel! + kernel() +} diff --git a/07_uart_chainloader/src/panic_wait.rs b/07_uart_chainloader/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/07_uart_chainloader/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/07_uart_chainloader/src/print.rs b/07_uart_chainloader/src/print.rs new file mode 100644 index 00000000..ce6587b5 --- /dev/null +++ b/07_uart_chainloader/src/print.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/07_uart_chainloader/src/relocate.rs b/07_uart_chainloader/src/relocate.rs new file mode 100644 index 00000000..30cd2257 --- /dev/null +++ b/07_uart_chainloader/src/relocate.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Relocation code. + +/// Relocates the own binary from `bsp::BOARD_DEFAULT_LOAD_ADDRESS` to the `__binary_start` address +/// from the linker script. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - Function must not use the `bss` section. +pub unsafe fn relocate_self() -> ! { + extern "C" { + static __binary_start: usize; + static __binary_end: usize; + } + + let binary_start_addr: usize = &__binary_start as *const _ as _; + let binary_end_addr: usize = &__binary_end as *const _ as _; + let binary_size_in_byte: usize = binary_end_addr - binary_start_addr; + + // Get the relocation destination address from the linker symbol. + let mut reloc_dst_addr: *mut T = binary_start_addr as *mut T; + + // The address of where the previous firmware loaded us. + let mut src_addr: *const T = crate::bsp::BOARD_DEFAULT_LOAD_ADDRESS as *const _; + + // Copy the whole binary. + // + // This is essentially a `memcpy()` optimized for throughput by transferring in chunks of T. + let n = binary_size_in_byte / core::mem::size_of::(); + for _ in 0..n { + use core::ptr; + + ptr::write_volatile::(reloc_dst_addr, ptr::read_volatile::(src_addr)); + reloc_dst_addr = reloc_dst_addr.offset(1); + src_addr = src_addr.offset(1); + } + + // Call `init()` through a trait object, causing the jump to use an absolute address to reach + // the relocated binary. An elaborate explanation can be found in the runtime_init.rs source + // comments. + crate::runtime_init::get().init() +} diff --git a/07_uart_chainloader/src/runtime_init.rs b/07_uart_chainloader/src/runtime_init.rs new file mode 100644 index 00000000..cde735f5 --- /dev/null +++ b/07_uart_chainloader/src/runtime_init.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// We are outsmarting the compiler here by using a trait as a layer of indirection. Because we are +/// generating PIC code, a static dispatch to `init()` would generate a relative jump from the +/// callee to `init()`. However, when calling `init()`, code just finished copying the binary to the +/// actual link-time address, and hence is still running at whatever location the previous loader +/// has put it. So we do not want a relative jump, because it would not jump to the relocated code. +/// +/// By indirecting through a trait object, we can make use of the property that vtables store +/// absolute addresses. So calling `init()` this way will kick execution to the relocated binary. +pub trait RunTimeInit { + /// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to + /// kernel init code. + /// + /// # Safety + /// + /// - Only a single core must be active and running this function. + unsafe fn init(&self) -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() + } +} + +struct Traitor; +impl RunTimeInit for Traitor {} + +/// Give the callee a `RunTimeInit` trait object. +pub fn get() -> &'static dyn RunTimeInit { + &Traitor {} +} diff --git a/08_random/.cargo/config b/08_random/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/08_random/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/08_random/Cargo.lock b/08_random/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/08_random/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/08_random/Cargo.toml b/08_random/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/08_random/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/08_random/Makefile b/08_random/Makefile deleted file mode 100644 index 12815149..00000000 --- a/08_random/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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/08_random/README.md b/08_random/README.md deleted file mode 100644 index 5e7c98ba..00000000 --- a/08_random/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Tutorial 08 - Hardware Random Number Generator - -This going to be an easy tutorial. We query a number from the (undocumented) -hardware random number generator. You can use this to implement a simple, but -accurate dice throw in any game. It is important as without hardware support you -can only generate pseudo-random numbers. - -## rand.s - -Due to lack of documentation, we [mimic the respective Linux driver](https://github.com/torvalds/linux/blob/master/drivers/char/hw_random/bcm2835-rng.c). - -`Rng::init(&self)` initializes the hardware. - -`Rng::rand(&self, min: u32, max: u32)` returns a random number between min and -max. - -## main.rs - -Press a key to query a random value and then display it on the serial console. diff --git a/08_random/kernel8.img b/08_random/kernel8.img deleted file mode 100755 index f991fad0..00000000 Binary files a/08_random/kernel8.img and /dev/null differ diff --git a/08_random/link.ld b/08_random/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/08_random/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/08_random/raspi3_boot/Cargo.toml b/08_random/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/08_random/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/08_random/raspi3_boot/src/lib.rs b/08_random/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/08_random/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/08_random/src/gpio.rs b/08_random/src/gpio.rs deleted file mode 100644 index da6a5be4..00000000 --- a/08_random/src/gpio.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/08_random/src/main.rs b/08_random/src/main.rs deleted file mode 100644 index be364ab9..00000000 --- a/08_random/src/main.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod gpio; -mod mbox; -mod rand; -mod uart; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - - // set up serial console - match uart.init(&mut mbox) { - 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"); - - // set up random number generator - let rng = rand::Rng::new(); - rng.init(); - - uart.puts("[2] RNG ready.\n\n"); - - uart.puts("Press any key to generate random numbers.\n"); - - // echo everything back - loop { - uart.getc(); - - uart.puts("0x"); - uart.hex(rng.rand(0, 4_294_967_295)); - uart.puts("\n"); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/08_random/src/mbox.rs b/08_random/src/mbox.rs deleted file mode 100644 index aeae88bb..00000000 --- a/08_random/src/mbox.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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/08_random/src/rand.rs b/08_random/src/rand.rs deleted file mode 100644 index 140e03d1..00000000 --- a/08_random/src/rand.rs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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::*, register_bitfields}; - -register_bitfields! { - u32, - - CTRL [ - ENABLE OFFSET(0) NUMBITS(1) [ - True = 1, - False = 0 - ] - ], - - INT_MASK [ - INT_OFF OFFSET(0) NUMBITS(1) [ - True = 1, - False = 0 - ] - ] -} - -const RNG_BASE: u32 = MMIO_BASE + 0x104_000; -const RNG_WARMUP_COUNT: u32 = 0x40_000; - -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - CTRL: ReadWrite, // 0x00 - STATUS: ReadWrite, // 0x04 - DATA: ReadOnly, // 0x08 - __reserved_0: u32, // 0x0c - INT_MASK: ReadWrite, // 0x10 -} - -/// Public interface to the RNG -pub struct Rng; - -impl ops::Deref for Rng { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Rng { - pub fn new() -> Rng { - Rng - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - RNG_BASE as *const _ - } - - /// Initialize the RNG - pub fn init(&self) { - // Disable interrupts - self.INT_MASK.modify(INT_MASK::INT_OFF::True); - - // Set warm-up count and enable - self.STATUS.set(RNG_WARMUP_COUNT); - self.CTRL.modify(CTRL::ENABLE::True); - } - - /// Return a random number between [min..max] - pub fn rand(&self, min: u32, max: u32) -> u32 { - // wait for gaining some entropy - loop { - if (self.STATUS.get() >> 24) != 0 { - break; - } - - asm::nop(); - } - - let r = self.DATA.get(); - - r % (max - min) + min - } -} diff --git a/08_random/src/uart.rs b/08_random/src/uart.rs deleted file mode 100644 index db4c639a..00000000 --- a/08_random/src/uart.rs +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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::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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self, mbox: &mut mbox::Mbox) -> 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 - unsafe { - (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); - - (*gpio::GPPUD).set(0); // enable pins 14 and 15 - for _ in 0..150 { - asm::nop(); - } - - (*gpio::GPPUDCLK0).modify( - gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, - ); - for _ in 0..150 { - asm::nop(); - } - - (*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: u32) { - let mut n; - - for i in 0..8 { - // get highest tetrad - n = d.wrapping_shr(28 - 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/08_timestamps/.vscode/settings.json b/08_timestamps/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/08_timestamps/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/08_timestamps/Cargo.lock b/08_timestamps/Cargo.lock new file mode 100644 index 00000000..9a17339b --- /dev/null +++ b/08_timestamps/Cargo.lock @@ -0,0 +1,42 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tock-registers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" +"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/08_timestamps/Cargo.toml b/08_timestamps/Cargo.toml new file mode 100644 index 00000000..cf0f0636 --- /dev/null +++ b/08_timestamps/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/08_timestamps/Makefile b/08_timestamps/Makefile new file mode 100644 index 00000000..fe1b643c --- /dev/null +++ b/08_timestamps/Makefile @@ -0,0 +1,99 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu chainboot clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +endif + +chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/08_timestamps/README.md b/08_timestamps/README.md new file mode 100644 index 00000000..7940c540 --- /dev/null +++ b/08_timestamps/README.md @@ -0,0 +1,597 @@ +# Tutorial 08 - Timestamps + +## tl;dr + +We add abstractions for the architectural timer, implement it for `aarch64` and +use it to annotate prints with timestamps; A `warn!()` macro is added. + +Check it out via chainboot (added in previous tutorial): +```console +make chainboot +[...] +### Listening on /dev/ttyUSB0 + __ __ _ _ _ _ +| \/ (_)_ _ (_) | ___ __ _ __| | +| |\/| | | ' \| | |__/ _ \/ _` / _` | +|_| |_|_|_||_|_|____\___/\__,_\__,_| + + Raspberry Pi 3 + +[ML] Requesting binary +### sending kernel kernel8.img [13872 byte] +### finished sending +[ML] Loaded! Executing the payload now + +[ 1.233286] Booting on: Raspberry Pi 3 +[ 1.236282] Architectural timer resolution: 52 ns +[ 1.241023] Drivers loaded: +[ 1.243833] 1. GPIO +[ 1.246467] 2. PL011Uart +[W 1.249453] Spin duration smaller than architecturally supported, skipping +[ 1.256390] Spinning for 1 second +[ 2.259728] Spinning for 1 second +[ 3.262286] Spinning for 1 second +``` + +## Diff to previous +```diff +Binary files 07_uart_chainloader/demo_payload_rpi3.img and 08_timestamps/demo_payload_rpi3.img differ +Binary files 07_uart_chainloader/demo_payload_rpi4.img and 08_timestamps/demo_payload_rpi4.img differ + +diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile +--- 07_uart_chainloader/Makefile ++++ 08_timestamps/Makefile +@@ -15,8 +15,7 @@ + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld +- RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic +- CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img ++ RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +@@ -24,8 +23,7 @@ + # QEMU_MACHINE_TYPE = + # QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld +- RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic +- CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img ++ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 + endif + + SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) +@@ -56,7 +54,7 @@ + DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 + # DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +-.PHONY: all doc qemu qemuasm chainboot clippy clean readelf objdump nm ++.PHONY: all doc qemu chainboot clippy clean readelf objdump nm + + all: clean $(OUTPUT) + +@@ -74,23 +72,16 @@ + ifeq ($(QEMU_MACHINE_TYPE),) + qemu: + @echo "This board is not yet supported for QEMU." +- +-qemuasm: +- @echo "This board is not yet supported for QEMU." + else + qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +- +-qemuasm: all +- $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ +- $(DOCKER_EXEC_QEMU) -d in_asm + endif + +-chainboot: ++chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ +- $(CHAINBOOT_DEMO_PAYLOAD) ++ $(OUTPUT) + + clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +diff -uNr 07_uart_chainloader/src/arch/aarch64/time.rs 08_timestamps/src/arch/aarch64/time.rs +--- 07_uart_chainloader/src/arch/aarch64/time.rs ++++ 08_timestamps/src/arch/aarch64/time.rs +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Timer primitives. ++ ++use crate::{interface, warn}; ++use core::time::Duration; ++use cortex_a::regs::*; ++ ++const NS_PER_S: u64 = 1_000_000_000; ++ ++pub struct Timer; ++ ++impl interface::time::Timer for Timer { ++ fn resoultion(&self) -> Duration { ++ Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) ++ } ++ ++ fn uptime(&self) -> Duration { ++ let frq: u64 = CNTFRQ_EL0.get() as u64; ++ let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; ++ ++ Duration::from_nanos(current_count / frq) ++ } ++ ++ fn spin_for(&self, duration: Duration) { ++ // Instantly return on zero. ++ if duration.as_nanos() == 0 { ++ return; ++ } ++ ++ // Calculate the register compare value. ++ let frq = CNTFRQ_EL0.get() as u64; ++ let x = match frq.checked_mul(duration.as_nanos() as u64) { ++ None => { ++ warn!("Spin duration too long, skipping"); ++ return; ++ } ++ Some(val) => val, ++ }; ++ let tval = x / NS_PER_S; ++ ++ // Check if it is within supported bounds. ++ let warn: Option<&str> = if tval == 0 { ++ Some("smaller") ++ } else if tval > u32::max_value().into() { ++ Some("bigger") ++ } else { ++ None ++ }; ++ ++ if let Some(w) = warn { ++ warn!( ++ "Spin duration {} than architecturally supported, skipping", ++ w ++ ); ++ return; ++ } ++ ++ // Set the compare value register. ++ CNTP_TVAL_EL0.set(tval as u32); ++ ++ // Kick off the counting. // Disable timer interrupt. ++ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); ++ ++ loop { ++ // ISTATUS will be '1' when cval ticks have passed. Busy-check it. ++ if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { ++ break; ++ } ++ } ++ ++ // Disable counting again. ++ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); ++ } ++} + +diff -uNr 07_uart_chainloader/src/arch/aarch64.rs 08_timestamps/src/arch/aarch64.rs +--- 07_uart_chainloader/src/arch/aarch64.rs ++++ 08_timestamps/src/arch/aarch64.rs +@@ -5,8 +5,9 @@ + //! AArch64. + + pub mod sync; ++mod time; + +-use crate::bsp; ++use crate::{bsp, interface}; + use cortex_a::{asm, regs::*}; + + /// The entry of the `kernel` binary. +@@ -22,7 +23,7 @@ + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); +- crate::relocate::relocate_self::() ++ crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() +@@ -30,6 +31,12 @@ + } + + //-------------------------------------------------------------------------------------------------- ++// Global instances ++//-------------------------------------------------------------------------------------------------- ++ ++static TIMER: time::Timer = time::Timer; ++ ++//-------------------------------------------------------------------------------------------------- + // Implementation of the kernel's architecture abstraction code + //-------------------------------------------------------------------------------------------------- + +@@ -42,6 +49,11 @@ + } + } + ++/// Return a reference to a `interface::time::TimeKeeper` implementation. ++pub fn timer() -> &'static impl interface::time::Timer { ++ &TIMER ++} ++ + /// Pause execution on the calling CPU core. + #[inline(always)] + pub fn wait_forever() -> ! { + +diff -uNr 07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs 08_timestamps/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +--- 07_uart_chainloader/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs ++++ 08_timestamps/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +@@ -300,7 +300,14 @@ + } + + // Read one character. +- inner.DR.get() as u8 as char ++ let mut ret = inner.DR.get() as u8 as char; ++ ++ // Convert carrige return to newline. ++ if ret == '\r' { ++ ret = '\n' ++ } ++ ++ ret + }) + } + + +diff -uNr 07_uart_chainloader/src/bsp/rpi/link.ld 08_timestamps/src/bsp/rpi/link.ld +--- 07_uart_chainloader/src/bsp/rpi/link.ld ++++ 08_timestamps/src/bsp/rpi/link.ld +@@ -5,10 +5,9 @@ + + SECTIONS + { +- /* Set the link address to the top-most 40 KiB of DRAM (assuming 1GiB) */ +- . = 0x3F000000 - 0x10000; ++ /* Set current address to the value from which the RPi starts execution */ ++ . = 0x80000; + +- __binary_start = .; + .text : + { + *(.text._start) *(.text*) +@@ -32,14 +31,5 @@ + __bss_end = .; + } + +- .got : +- { +- *(.got*) +- } +- +- /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */ +- . = ALIGN(8); +- __binary_end = .; +- + /DISCARD/ : { *(.comment*) } + } + +diff -uNr 07_uart_chainloader/src/bsp/rpi.rs 08_timestamps/src/bsp/rpi.rs +--- 07_uart_chainloader/src/bsp/rpi.rs ++++ 08_timestamps/src/bsp/rpi.rs +@@ -12,9 +12,6 @@ + pub const BOOT_CORE_ID: u64 = 0; + pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +-/// The address on which the RPi3 firmware loads every binary by default. +-pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x80_000; +- + //-------------------------------------------------------------------------------------------------- + // Global BSP driver instances + //-------------------------------------------------------------------------------------------------- + +diff -uNr 07_uart_chainloader/src/interface.rs 08_timestamps/src/interface.rs +--- 07_uart_chainloader/src/interface.rs ++++ 08_timestamps/src/interface.rs +@@ -108,3 +108,22 @@ + } + } + } ++ ++/// Timekeeping interfaces. ++pub mod time { ++ use core::time::Duration; ++ ++ /// Timer functions. ++ pub trait Timer { ++ /// The timer's resolution. ++ fn resoultion(&self) -> Duration; ++ ++ /// The uptime since power-on of the device. ++ /// ++ /// This includes time consumed by firmware and bootloaders. ++ fn uptime(&self) -> Duration; ++ ++ /// Spin for a given duration. ++ fn spin_for(&self, duration: Duration); ++ } ++} + +diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs +--- 07_uart_chainloader/src/main.rs ++++ 08_timestamps/src/main.rs +@@ -29,11 +29,7 @@ + // the first function to run. + mod arch; + +-// `_start()` then calls `relocate::relocate_self()`. +-mod relocate; +- +-// `relocate::relocate_self()` calls `runtime_init::init()`, which on completion, jumps to +-// `kernel_init()`. ++// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. + mod runtime_init; + + // Conditionally includes the selected `BSP` code. +@@ -68,50 +64,25 @@ + + /// The main function running after the early init. + fn kernel_main() -> ! { +- use interface::console::All; +- +- println!(" __ __ _ _ _ _ "); +- println!("| \\/ (_)_ _ (_) | ___ __ _ __| |"); +- println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |"); +- println!("|_| |_|_|_||_|_|____\\___/\\__,_\\__,_|"); +- println!(); +- println!("{:^37}", bsp::board_name()); +- println!(); +- println!("[ML] Requesting binary"); +- bsp::console().flush(); +- +- // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader +- // protocol. +- bsp::console().clear(); +- +- // Notify raspbootcom to send the binary. +- for _ in 0..3 { +- bsp::console().write_char(3 as char); +- } ++ use core::time::Duration; ++ use interface::time::Timer; + +- // Read the binary's size. +- let mut size: u32 = u32::from(bsp::console().read_char() as u8); +- size |= u32::from(bsp::console().read_char() as u8) << 8; +- size |= u32::from(bsp::console().read_char() as u8) << 16; +- size |= u32::from(bsp::console().read_char() as u8) << 24; +- +- // Trust it's not too big. +- print!("OK"); +- +- let kernel_addr: *mut u8 = bsp::BOARD_DEFAULT_LOAD_ADDRESS as *mut u8; +- unsafe { +- // Read the kernel byte by byte. +- for i in 0..size { +- *kernel_addr.offset(i as isize) = bsp::console().read_char() as u8; +- } ++ println!("Booting on: {}", bsp::board_name()); ++ println!( ++ "Architectural timer resolution: {} ns", ++ arch::timer().resoultion().as_nanos() ++ ); ++ ++ println!("Drivers loaded:"); ++ for (i, driver) in bsp::device_drivers().iter().enumerate() { ++ println!(" {}. {}", i + 1, driver.compatible()); + } + +- println!("[ML] Loaded! Executing the payload now\n"); +- bsp::console().flush(); +- +- // Use black magic to get a function pointer. +- let kernel: extern "C" fn() -> ! = unsafe { core::mem::transmute(kernel_addr as *const ()) }; ++ // Test a failing timer case. ++ arch::timer().spin_for(Duration::from_nanos(1)); + +- // Jump to loaded kernel! +- kernel() ++ loop { ++ println!("Spinning for 1 second"); ++ arch::timer().spin_for(Duration::from_secs(1)); ++ } + } + +diff -uNr 07_uart_chainloader/src/print.rs 08_timestamps/src/print.rs +--- 07_uart_chainloader/src/print.rs ++++ 08_timestamps/src/print.rs +@@ -16,13 +16,71 @@ + } + + /// Prints with a newline. +-/// +-/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html + #[macro_export] + macro_rules! println { + () => ($crate::print!("\n")); +- ($($arg:tt)*) => ({ +- $crate::print::_print(format_args_nl!($($arg)*)); ++ ($string:expr) => ({ ++ #[allow(unused_imports)] ++ use crate::interface::time::Timer; ++ ++ let timestamp = $crate::arch::timer().uptime(); ++ let timestamp_subsec_us = timestamp.subsec_micros(); ++ ++ $crate::print::_print(format_args_nl!( ++ concat!("[ {:>3}.{:03}{:03}] ", $string), ++ timestamp.as_secs(), ++ timestamp_subsec_us / 1_000, ++ timestamp_subsec_us modulo 1_000 ++ )); ++ }); ++ ($format_string:expr, $($arg:tt)*) => ({ ++ #[allow(unused_imports)] ++ use crate::interface::time::Timer; ++ ++ let timestamp = $crate::arch::timer().uptime(); ++ let timestamp_subsec_us = timestamp.subsec_micros(); ++ ++ $crate::print::_print(format_args_nl!( ++ concat!("[ {:>3}.{:03}{:03}] ", $format_string), ++ timestamp.as_secs(), ++ timestamp_subsec_us / 1_000, ++ timestamp_subsec_us modulo 1_000, ++ $($arg)* ++ )); ++ }) ++} ++ ++/// Prints a warning, with newline. ++#[macro_export] ++macro_rules! warn { ++ ($string:expr) => ({ ++ #[allow(unused_imports)] ++ use crate::interface::time::Timer; ++ ++ let timestamp = $crate::arch::timer().uptime(); ++ let timestamp_subsec_us = timestamp.subsec_micros(); ++ ++ $crate::print::_print(format_args_nl!( ++ concat!("[W {:>3}.{:03}{:03}] ", $string), ++ timestamp.as_secs(), ++ timestamp_subsec_us / 1_000, ++ timestamp_subsec_us modulo 1_000 ++ )); ++ }); ++ ($format_string:expr, $($arg:tt)*) => ({ ++ #[allow(unused_imports)] ++ use crate::interface::time::Timer; ++ ++ let timestamp = $crate::arch::timer().uptime(); ++ let timestamp_subsec_us = timestamp.subsec_micros(); ++ ++ $crate::print::_print(format_args_nl!( ++ concat!("[W {:>3}.{:03}{:03}] ", $format_string), ++ timestamp.as_secs(), ++ timestamp_subsec_us / 1_000, ++ timestamp_subsec_us modulo 1_000, ++ $($arg)* ++ )); + }) + } + + +diff -uNr 07_uart_chainloader/src/relocate.rs 08_timestamps/src/relocate.rs +--- 07_uart_chainloader/src/relocate.rs ++++ 08_timestamps/src/relocate.rs +@@ -1,46 +0,0 @@ +-// SPDX-License-Identifier: MIT +-// +-// Copyright (c) 2018-2019 Andre Richter +- +-//! Relocation code. +- +-/// Relocates the own binary from `bsp::BOARD_DEFAULT_LOAD_ADDRESS` to the `__binary_start` address +-/// from the linker script. +-/// +-/// # Safety +-/// +-/// - Only a single core must be active and running this function. +-/// - Function must not use the `bss` section. +-pub unsafe fn relocate_self() -> ! { +- extern "C" { +- static __binary_start: usize; +- static __binary_end: usize; +- } +- +- let binary_start_addr: usize = &__binary_start as *const _ as _; +- let binary_end_addr: usize = &__binary_end as *const _ as _; +- let binary_size_in_byte: usize = binary_end_addr - binary_start_addr; +- +- // Get the relocation destination address from the linker symbol. +- let mut reloc_dst_addr: *mut T = binary_start_addr as *mut T; +- +- // The address of where the previous firmware loaded us. +- let mut src_addr: *const T = crate::bsp::BOARD_DEFAULT_LOAD_ADDRESS as *const _; +- +- // Copy the whole binary. +- // +- // This is essentially a `memcpy()` optimized for throughput by transferring in chunks of T. +- let n = binary_size_in_byte / core::mem::size_of::(); +- for _ in 0..n { +- use core::ptr; +- +- ptr::write_volatile::(reloc_dst_addr, ptr::read_volatile::(src_addr)); +- reloc_dst_addr = reloc_dst_addr.offset(1); +- src_addr = src_addr.offset(1); +- } +- +- // Call `init()` through a trait object, causing the jump to use an absolute address to reach +- // the relocated binary. An elaborate explanation can be found in the runtime_init.rs source +- // comments. +- crate::runtime_init::get().init() +-} + +diff -uNr 07_uart_chainloader/src/runtime_init.rs 08_timestamps/src/runtime_init.rs +--- 07_uart_chainloader/src/runtime_init.rs ++++ 08_timestamps/src/runtime_init.rs +@@ -4,39 +4,21 @@ + + //! Rust runtime initialization code. + +-/// We are outsmarting the compiler here by using a trait as a layer of indirection. Because we are +-/// generating PIC code, a static dispatch to `init()` would generate a relative jump from the +-/// callee to `init()`. However, when calling `init()`, code just finished copying the binary to the +-/// actual link-time address, and hence is still running at whatever location the previous loader +-/// has put it. So we do not want a relative jump, because it would not jump to the relocated code. ++/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel ++/// init code. + /// +-/// By indirecting through a trait object, we can make use of the property that vtables store +-/// absolute addresses. So calling `init()` this way will kick execution to the relocated binary. +-pub trait RunTimeInit { +- /// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to +- /// kernel init code. +- /// +- /// # Safety +- /// +- /// - Only a single core must be active and running this function. +- unsafe fn init(&self) -> ! { +- extern "C" { +- // Boundaries of the .bss section, provided by the linker script. +- static mut __bss_start: u64; +- static mut __bss_end: u64; +- } +- +- // Zero out the .bss section. +- r0::zero_bss(&mut __bss_start, &mut __bss_end); +- +- crate::kernel_init() ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++pub unsafe fn init() -> ! { ++ extern "C" { ++ // Boundaries of the .bss section, provided by the linker script. ++ static mut __bss_start: u64; ++ static mut __bss_end: u64; + } +-} + +-struct Traitor; +-impl RunTimeInit for Traitor {} ++ // Zero out the .bss section. ++ r0::zero_bss(&mut __bss_start, &mut __bss_end); + +-/// Give the callee a `RunTimeInit` trait object. +-pub fn get() -> &'static dyn RunTimeInit { +- &Traitor {} ++ crate::kernel_init() + } + +``` diff --git a/0F_globals_synchronization_println/kernel8 b/08_timestamps/kernel similarity index 71% rename from 0F_globals_synchronization_println/kernel8 rename to 08_timestamps/kernel index cc1c2b2c..3bfb485a 100755 Binary files a/0F_globals_synchronization_println/kernel8 and b/08_timestamps/kernel differ diff --git a/08_timestamps/kernel8.img b/08_timestamps/kernel8.img new file mode 100755 index 00000000..e6f58b22 Binary files /dev/null and b/08_timestamps/kernel8.img differ diff --git a/08_timestamps/src/arch.rs b/08_timestamps/src/arch.rs new file mode 100644 index 00000000..b1f035c5 --- /dev/null +++ b/08_timestamps/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/08_timestamps/src/arch/aarch64.rs b/08_timestamps/src/arch/aarch64.rs new file mode 100644 index 00000000..a44ece9b --- /dev/null +++ b/08_timestamps/src/arch/aarch64.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; +mod time; + +use crate::{bsp, interface}; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static TIMER: time::Timer = time::Timer; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Return a reference to a `interface::time::TimeKeeper` implementation. +pub fn timer() -> &'static impl interface::time::Timer { + &TIMER +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/08_timestamps/src/arch/aarch64/sync.rs b/08_timestamps/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/08_timestamps/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/08_timestamps/src/arch/aarch64/time.rs b/08_timestamps/src/arch/aarch64/time.rs new file mode 100644 index 00000000..ba43474f --- /dev/null +++ b/08_timestamps/src/arch/aarch64/time.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Timer primitives. + +use crate::{interface, warn}; +use core::time::Duration; +use cortex_a::regs::*; + +const NS_PER_S: u64 = 1_000_000_000; + +pub struct Timer; + +impl interface::time::Timer for Timer { + fn resoultion(&self) -> Duration { + Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + } + + fn uptime(&self) -> Duration { + let frq: u64 = CNTFRQ_EL0.get() as u64; + let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; + + Duration::from_nanos(current_count / frq) + } + + fn spin_for(&self, duration: Duration) { + // Instantly return on zero. + if duration.as_nanos() == 0 { + return; + } + + // Calculate the register compare value. + let frq = CNTFRQ_EL0.get() as u64; + let x = match frq.checked_mul(duration.as_nanos() as u64) { + None => { + warn!("Spin duration too long, skipping"); + return; + } + Some(val) => val, + }; + let tval = x / NS_PER_S; + + // Check if it is within supported bounds. + let warn: Option<&str> = if tval == 0 { + Some("smaller") + } else if tval > u32::max_value().into() { + Some("bigger") + } else { + None + }; + + if let Some(w) = warn { + warn!( + "Spin duration {} than architecturally supported, skipping", + w + ); + return; + } + + // Set the compare value register. + CNTP_TVAL_EL0.set(tval as u32); + + // Kick off the counting. // Disable timer interrupt. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + + loop { + // ISTATUS will be '1' when cval ticks have passed. Busy-check it. + if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { + break; + } + } + + // Disable counting again. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); + } +} diff --git a/08_timestamps/src/bsp.rs b/08_timestamps/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/08_timestamps/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/08_timestamps/src/bsp/driver.rs b/08_timestamps/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/08_timestamps/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/08_timestamps/src/bsp/driver/bcm.rs b/08_timestamps/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/08_timestamps/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/10_DMA_memory/src/devices/hw/gpio.rs b/08_timestamps/src/bsp/driver/bcm/bcm2xxx_gpio.rs similarity index 57% rename from 10_DMA_memory/src/devices/hw/gpio.rs rename to 08_timestamps/src/bsp/driver/bcm/bcm2xxx_gpio.rs index 7affea08..a9ceda61 100644 --- a/10_DMA_memory/src/devices/hw/gpio.rs +++ b/08_timestamps/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -1,30 +1,15 @@ -/* - * 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. - */ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; use core::ops; use register::{mmio::ReadWrite, register_bitfields}; +// GPIO registers. +// // Descriptions taken from // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf register_bitfields! { @@ -36,8 +21,7 @@ register_bitfields! { FSEL15 OFFSET(15) NUMBITS(3) [ Input = 0b000, Output = 0b001, - RXD0 = 0b100, // UART0 - Alternate function 0 - RXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART RX ], @@ -45,8 +29,7 @@ register_bitfields! { FSEL14 OFFSET(12) NUMBITS(3) [ Input = 0b000, Output = 0b001, - TXD0 = 0b100, // UART0 - Alternate function 0 - TXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART TX ] ], @@ -95,12 +78,13 @@ pub struct RegisterBlock { pub GPPUDCLK1: ReadWrite, // 0x9C } -/// Public interface to the GPIO MMIO area -pub struct GPIO { +/// The driver's private data. +struct GPIOInner { base_addr: usize, } -impl ops::Deref for GPIO { +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { type Target = RegisterBlock; fn deref(&self) -> &Self::Target { @@ -108,13 +92,66 @@ impl ops::Deref for GPIO { } } -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } } - /// Returns a pointer to the register block + /// Return a pointer to the register block. fn ptr(&self) -> *const RegisterBlock { self.base_addr as *const _ } } + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/08_timestamps/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/08_timestamps/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..c75312c9 --- /dev/null +++ b/08_timestamps/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// 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 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until the TX FIFO empty flag is set. + r.lock(|inner| loop { + if inner.FR.is_set(FR::TXFE) { + break; + } + + arch::nop(); + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + ret + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| loop { + // Read from the RX FIFO until the empty bit is '1'. + if !inner.FR.is_set(FR::RXFE) { + inner.DR.get(); + } else { + break; + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/08_timestamps/src/bsp/rpi.rs b/08_timestamps/src/bsp/rpi.rs new file mode 100644 index 00000000..c22c47bb --- /dev/null +++ b/08_timestamps/src/bsp/rpi.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} diff --git a/08_timestamps/src/bsp/rpi/link.ld b/08_timestamps/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/08_timestamps/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/08_timestamps/src/bsp/rpi/memory_map.rs b/08_timestamps/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/08_timestamps/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/08_timestamps/src/interface.rs b/08_timestamps/src/interface.rs new file mode 100644 index 00000000..15fa3c15 --- /dev/null +++ b/08_timestamps/src/interface.rs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} + +/// Timekeeping interfaces. +pub mod time { + use core::time::Duration; + + /// Timer functions. + pub trait Timer { + /// The timer's resolution. + fn resoultion(&self) -> Duration; + + /// The uptime since power-on of the device. + /// + /// This includes time consumed by firmware and bootloaders. + fn uptime(&self) -> Duration; + + /// Spin for a given duration. + fn spin_for(&self, duration: Duration); + } +} diff --git a/08_timestamps/src/main.rs b/08_timestamps/src/main.rs new file mode 100644 index 00000000..6730048a --- /dev/null +++ b/08_timestamps/src/main.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } + + bsp::post_driver_init(); + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use core::time::Duration; + use interface::time::Timer; + + println!("Booting on: {}", bsp::board_name()); + println!( + "Architectural timer resolution: {} ns", + arch::timer().resoultion().as_nanos() + ); + + println!("Drivers loaded:"); + for (i, driver) in bsp::device_drivers().iter().enumerate() { + println!(" {}. {}", i + 1, driver.compatible()); + } + + // Test a failing timer case. + arch::timer().spin_for(Duration::from_nanos(1)); + + loop { + println!("Spinning for 1 second"); + arch::timer().spin_for(Duration::from_secs(1)); + } +} diff --git a/08_timestamps/src/panic_wait.rs b/08_timestamps/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/08_timestamps/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/08_timestamps/src/print.rs b/08_timestamps/src/print.rs new file mode 100644 index 00000000..86ab59b3 --- /dev/null +++ b/08_timestamps/src/print.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +/// Prints a warning, with newline. +#[macro_export] +macro_rules! warn { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/08_timestamps/src/runtime_init.rs b/08_timestamps/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/08_timestamps/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/09_delays/.cargo/config b/09_delays/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/09_delays/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/09_delays/Cargo.lock b/09_delays/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/09_delays/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/09_delays/Cargo.toml b/09_delays/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/09_delays/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/09_delays/Makefile b/09_delays/Makefile deleted file mode 100644 index 411b9bc3..00000000 --- a/09_delays/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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/09_delays/README.md b/09_delays/README.md deleted file mode 100644 index 185bd36b..00000000 --- a/09_delays/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Tutorial 09 - Delays - -It is very important to wait precise amounts of time while you are interfacing -with low level hardware. In this tutorial, we'll cover thee ways. One is CPU -frequency dependent (and useful if wait time is given in CPU clock cycles), the -other two are µs based. - -## delays.rs - -`delays::wait_cycles(cyc: u32)` this is very straightforward, we execute the -`nop` instruction n times. - -`delays::wait_usec(n: u32)` this implementation uses ARM system registers -(available on all AArch64 CPUs). - -`delays::SysTmr::wait_usec_st(&self, n: u64)` is a BCM specific implementation, -which uses the System Timer peripheral (not available on qemu). - -## uart.rs - -We can now conveniently use `delays::wait_cycles()` in `Uart::init()`. - -## main.rs - -We test our different wait implementations. diff --git a/09_delays/kernel8.img b/09_delays/kernel8.img deleted file mode 100755 index 03ad0193..00000000 Binary files a/09_delays/kernel8.img and /dev/null differ diff --git a/09_delays/link.ld b/09_delays/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/09_delays/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/09_delays/raspi3_boot/Cargo.toml b/09_delays/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/09_delays/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/09_delays/raspi3_boot/src/lib.rs b/09_delays/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/09_delays/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/09_delays/src/delays.rs b/09_delays/src/delays.rs deleted file mode 100644 index 53af476c..00000000 --- a/09_delays/src/delays.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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::MMIO_BASE; -use core::ops; -use cortex_a::{asm, regs::*}; -use register::mmio::*; - -const DELAY_BASE: u32 = MMIO_BASE + 0x3004; - -/* - * - * Using the RPi3 SoC's system timer peripheral - * - */ -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - SYSTMR_LO: ReadOnly, // 0x00 - SYSTMR_HI: ReadOnly, // 0x04 -} - -/// Public interface to the BCM System Timer -pub struct SysTmr; - -impl ops::Deref for SysTmr { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl SysTmr { - pub fn new() -> SysTmr { - SysTmr - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - DELAY_BASE as *const _ - } - - /// Get System Timer's counter - pub fn get_system_timer(&self) -> u64 { - // Since it is MMIO, we must emit two separate 32 bit reads - let mut hi = self.SYSTMR_HI.get(); - let mut lo = self.SYSTMR_LO.get(); - - // We have to repeat if high word changed during read. This - // will emit a clippy warning that needs be ignored, or you - // lose an MMIO read. - if hi != self.SYSTMR_HI.get() { - hi = self.SYSTMR_HI.get(); - lo = self.SYSTMR_LO.get(); - } - - // Compose long int value - (u64::from(hi) << 32) | u64::from(lo) - } - - /// Wait N microsec (with BCM System Timer) - pub fn wait_usec_st(&self, n: u64) { - let t = self.get_system_timer(); - - // We must check if it's non-zero, because qemu does not - // emulate system timer, and returning constant zero would - // mean infinite loop - if t > 0 { - loop { - if self.get_system_timer() > (t + n) { - break; - } - } - } - } -} - -/* - * - * Using the CPU's counter registers - * - */ -/// Wait N microsec (ARM CPU only) -pub fn wait_usec(n: u32) { - // Get the counter frequency - let frq = CNTFRQ_EL0.get(); - - // Calculate number of ticks - let tval = (u64::from(frq) * u64::from(n) / 1_000_000) as u32; - - // Set the compare value register - CNTP_TVAL_EL0.set(tval); - - // Kick off the counting // Disable timer interrupt - CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); - - loop { - // ISTATUS will be one when cval ticks have passed. Continuously check it. - if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { - break; - } - } - - // Disable counting again - CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); -} - -/* - * - * 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/09_delays/src/gpio.rs b/09_delays/src/gpio.rs deleted file mode 100644 index da6a5be4..00000000 --- a/09_delays/src/gpio.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 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 - ] - ] -} - -pub const GPFSEL1: *const ReadWrite = - (MMIO_BASE + 0x0020_0004) as *const ReadWrite; - -pub const GPPUD: *const ReadWrite = (MMIO_BASE + 0x0020_0094) as *const ReadWrite; - -pub const GPPUDCLK0: *const ReadWrite = - (MMIO_BASE + 0x0020_0098) as *const ReadWrite; diff --git a/09_delays/src/main.rs b/09_delays/src/main.rs deleted file mode 100644 index 8c92ba69..00000000 --- a/09_delays/src/main.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod delays; -mod gpio; -mod mbox; -mod uart; - -fn kernel_entry() -> ! { - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - - // set up serial console - match uart.init(&mut mbox) { - 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("[i] Waiting 1_000_000 CPU cycles (ARM CPU): "); - delays::wait_cycles(1_000_000); - uart.puts("OK\n"); - - uart.puts("[i] Waiting 1 second (ARM CPU): "); - delays::wait_usec(1_000_000); - uart.puts("OK\n"); - - let t = delays::SysTmr::new(); - if t.get_system_timer() != 0 { - uart.puts("[i] Waiting 1 second (BCM System Timer): "); - t.wait_usec_st(1_000_000); - uart.puts("OK\n"); - } - - uart.puts("[i] Looping forever now!\n"); - loop { - delays::wait_usec(1_000_000); - uart.puts("Tick: 1s\n"); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/09_delays/src/mbox.rs b/09_delays/src/mbox.rs deleted file mode 100644 index aeae88bb..00000000 --- a/09_delays/src/mbox.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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/09_delays/src/uart.rs b/09_delays/src/uart.rs deleted file mode 100644 index 75310d17..00000000 --- a/09_delays/src/uart.rs +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - UART_BASE as *const _ - } - - ///Set baud rate and characteristics (115200 8N1) and map to GPIO - pub fn init(&self, mbox: &mut mbox::Mbox) -> 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 - unsafe { - (*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); - } - } -} diff --git a/09_hw_debug_JTAG/.vscode/settings.json b/09_hw_debug_JTAG/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/09_hw_debug_JTAG/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/09_hw_debug_JTAG/Cargo.lock b/09_hw_debug_JTAG/Cargo.lock new file mode 100644 index 00000000..9a17339b --- /dev/null +++ b/09_hw_debug_JTAG/Cargo.lock @@ -0,0 +1,42 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tock-registers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" +"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/09_hw_debug_JTAG/Cargo.toml b/09_hw_debug_JTAG/Cargo.toml new file mode 100644 index 00000000..cf0f0636 --- /dev/null +++ b/09_hw_debug_JTAG/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/09_hw_debug_JTAG/Makefile b/09_hw_debug_JTAG/Makefile new file mode 100644 index 00000000..4cf5e508 --- /dev/null +++ b/09_hw_debug_JTAG/Makefile @@ -0,0 +1,127 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi3.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi3.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi4.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev +DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag +DOCKER_ARG_NET = --network host + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu chainboot clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +endif + +chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) + +jtagboot: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + /jtag/$(JTAG_BOOT_IMAGE) + +openocd: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + openocd $(OPENOCD_ARG) + +define gen_gdb + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) $1 + cp $(CARGO_OUTPUT) kernel_for_jtag + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + gdb-multiarch -q kernel_for_jtag +endef + +gdb: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2) + +gdb-opt0: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2 -C opt-level=0) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/09_hw_debug_JTAG/README.md b/09_hw_debug_JTAG/README.md new file mode 100644 index 00000000..f78dbac0 --- /dev/null +++ b/09_hw_debug_JTAG/README.md @@ -0,0 +1,340 @@ +# Tutorial 09 - Hardware Debugging using JTAG + +In the upcoming tutorials, we are going to touch sensitive areas of the RPi's SoC that can make our +debugging life very hard. For example, changing the processor's `Privilege Level` or introducing +`Virtual Memory`. + +A hardware based debugger can sometimes be the last resort when searching for a tricky bug. +Especially for debugging intricate, architecture-specific HW issues, it will be handy, because in +this area `QEMU` sometimes can not help, since it abstracts certain features of the HW and doesn't +simulate down to the very last bit. + +So lets introduce `JTAG` debugging. Once set up, it will allow us to single-step through our kernel +on the real HW. How cool is that?! + +![JTAG live demo](../doc/jtag_demo.gif) + +## Outline + +From kernel perspective, this tutorial is the same as the previous one. We are just wrapping +infrastructure for JTAG debugging around it. + +## Software Setup + +We need to add another line to the `config.txt` file from the SD Card: + +```toml +init_uart_clock=48000000 +enable_jtag_gpio=1 +``` + +## Hardware Setup + +Unlike microcontroller boards like the `STM32F3DISCOVERY`, which is used in our WG's [Embedded Rust +Book](https://rust-embedded.github.io/book/start/hardware.html), the Raspberry Pi does not have an +embedded debugger on its board. Hence, you need to buy one. + +For this tutorial, we will use the [ARM-USB-TINY-H] from OLIMEX. It has a standard [ARM JTAG 20 +connector]. Unfortunately, the RPi does not, so we have to connect it via jumper wires. + +[ARM-USB-TINY-H]: https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H +[ARM JTAG 20 connector]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0499dj/BEHEIHCE.html + +### Wiring + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GPIO #NameJTAG #NoteDiagram
VTREF1to 3.3V
GND4to GND
22TRST3
26TDI5
27TMS7
25TCK9
23RTCK11
24TDO13
+ +

+ +## Getting ready to connect + +Upon booting, thanks to the changes we made to `config.txt`, the RPi's firmware will configure the +respective GPIO pins for `JTAG` functionality. + +What is left to do now is to pause the execution of the RPi and then connect +over `JTAG`. Therefore, we add a new `Makefile` target, `make jtagboot`, which +uses the `chainboot` approach to load a tiny helper binary onto the RPi that +just parks the executing core into a waiting state. + +The helper binary is maintained separately in this repository's [X1_JTAG_boot](../X1_JTAG_boot) +folder, and is a modified version of the kernel we used in our tutorials so far. + +```console +make jtagboot +[...] +Raspbootcom V1.0 +### Listening on /dev/ttyUSB0 + __ __ _ _ _ _ +| \/ (_)_ _ (_) | ___ __ _ __| | +| |\/| | | ' \| | |__/ _ \/ _` / _` | +|_| |_|_|_||_|_|____\___/\__,_\__,_| + + Raspberry Pi 3 + +[ML] Reqbinary +### sending kernel /jtag/jtag_boot_rpi3.img [8960 byte] +### finished sending +[ML] Loaded! Executing the payload now + +[ 0.805909] Parking CPU core. Please connect over JTAG now. +``` + +It is important to keep the USB serial connected and the terminal with the `jtagboot` open and +running. When we load the actual kernel later, `UART` output will appear here. + +## OpenOCD + +Next, we need to launch the [Open On-Chip Debugger](http://openocd.org/), aka `OpenOCD` to actually +connect the `JTAG`. + +As always, our tutorials try to be as painless as possible regarding dev-tools, which is why we have +packaged everything into the [dedicated Docker container](../docker/rustembedded-osdev-utils) that +is already used for chainbooting and `QEMU`. + +Connect the Olimex USB JTAG debugger, open a new terminal and in the same folder, type `make +openocd` (in that order!). You will see some initial output: + +```console +make openocd +[...] +Open On-Chip Debugger 0.10.0 +[...] +Info : Listening on port 6666 for tcl connections +Info : Listening on port 4444 for telnet connections +Info : clock speed 1000 kHz +Info : JTAG tap: rpi3.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x4) +Info : rpi3.core0: hardware has 6 breakpoints, 4 watchpoints +Info : rpi3.core1: hardware has 6 breakpoints, 4 watchpoints +Info : rpi3.core2: hardware has 6 breakpoints, 4 watchpoints +Info : rpi3.core3: hardware has 6 breakpoints, 4 watchpoints +Info : Listening on port 3333 for gdb connections +Info : Listening on port 3334 for gdb connections +Info : Listening on port 3335 for gdb connections +Info : Listening on port 3336 for gdb connections +``` + +`OpenOCD` has detected the four cores of the RPi, and opened four network ports to which `gdb` can +now connect to debug the respective core. + +## GDB + +Finally, we need an `AArch64`-capable version of `gdb`. You guessed right, it's already packaged in +the osdev container. It can be launched via `make gdb`. + +This Makefile target actually does a little more. It builds a special version of our kernel with +debug information included. This enables `gdb` to show the `Rust` source code line we are currently +debugging. It also launches `gdb` such that it already loads this debug build (`kernel_for_jtag`). + +We can now use the `gdb` commandline to + 1. Set breakpoints in our kernel + 2. Load the kernel via JTAG into memory (remember that currently, the RPi is still executing the + minimal JTAG boot binary). + 3. Manipulate the program counter of the RPi to start execution at our kernel's entry point. + 4. Single-step through its execution. + +```shell +make gdb +[...] +>>> target remote :3333 # Connect to OpenOCD, core0 +>>> load # Load the kernel into the RPi's DRAM over JTAG. +Loading section .text, size 0x2660 lma 0x80000 +Loading section .rodata, size 0xfa5 lma 0x82660 +Loading section .data, size 0x18 lma 0x83608 +Start address 0x80000, load size 13853 +Transfer rate: 65 KB/sec, 4617 bytes/write. +>>> set $pc = 0x80000 # Set RPI's program counter to the start of the + # kernel binary. +>>> break main.rs:70 +Breakpoint 1 at 0x80124: file src/main.rs, line 70. +>>> cont +Breakpoint 1, kernel::kernel_main () at src/main.rs:70 +70 println!("Booting on: {}", bsp::board_name()); +>>> step # Single-step through the kernel +>>> step +>>> ... +``` + +### Remarks + +#### Optimization + +When debugging an OS binary, you have to make a trade-off between the granularity at which you can +step through your Rust source-code and the optimization level of the generated binary. The `make` +and `make gdb` targets produce a `--release` binary, which includes an optimization level of three +(`-opt-level=3`). However, in this case, the compiler will inline very aggressively and pack +together reads and writes where possible. As a result, it will not always be possible to hit +breakpoints exactly where you want to regarding the line of source code file. + +For this reason, the Makefile also provides the `make gdb-opt0` target, which uses `-opt-level=0`. +Hence, it will allow you to have finer debugging granularity. However, please keep in mind that when +debugging code that closely deals with HW, a compiler optimization that squashes reads or writes to +volatile registers can make all the difference in execution. FYI, the demo gif above has been +recorded with `gdb-opt0`. + +#### GDB control + +At some point, you may reach delay loops or code that waits on user input from the serial. Here, +single stepping might not be feasible or work anymore. You can jump over these roadblocks by setting +other breakpoints beyond these areas, and reach them using the `cont` command. + +Pressing `ctrl+c` in `gdb` will stop execution of the RPi again in case you continued it without +further breakpoints. + +## Notes on USB connection constraints + +If you followed the tutorial from top to bottom, everything should be fine regarding USB +connections. + +Still, please note that in its current form, our `Makefile` makes implicit assumptions about the +naming of the connected USB devices. It expects `/dev/ttyUSB0` to be the `UART` device. + +Hence, please ensure the following order of connecting the devices to your box: + 1. Connect the USB serial. + 2. Afterwards, the Olimex debugger. + +This way, Linux enumerates the devices accordingly. This has to be done only once. It is fine to +disconnect and connect the serial multiple times, e.g. for kicking off different `make jtagboot` +runs, while keeping the debugger connected. + +## In summary + +1. `make jtagboot` and keep terminal open. +2. Connect USB serial device. +3. Connect `JTAG` debugger USB device. +4. In new terminal, `make openocd`. +5. In new terminal, `make gdb` or make `make gdb-opt0`. + +## Additional resources + +- https://metebalci.com/blog/bare-metal-raspberry-pi-3b-jtag +- https://www.suse.com/c/debugging-raspberry-pi-3-with-jtag + +## Acknowledgments + +Thanks to [@naotaco](https://github.com/naotaco) for laying the groundwork for this tutorial. + +## Diff to previous +```diff +Binary files 08_timestamps/kernel_for_jtag and 09_hw_debug_JTAG/kernel_for_jtag differ + +diff -uNr 08_timestamps/Makefile 09_hw_debug_JTAG/Makefile +--- 08_timestamps/Makefile ++++ 09_hw_debug_JTAG/Makefile +@@ -14,6 +14,8 @@ + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio ++ OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi3.cfg ++ JTAG_BOOT_IMAGE = jtag_boot_rpi3.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + else ifeq ($(BSP),rpi4) +@@ -22,6 +24,8 @@ + # QEMU_BINARY = qemu-system-aarch64 + # QEMU_MACHINE_TYPE = + # QEMU_MISC_ARGS = -serial stdio ++ OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg ++ JTAG_BOOT_IMAGE = jtag_boot_rpi4.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 + endif +@@ -48,6 +52,8 @@ + DOCKER_CMD = docker run -it --rm + DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work + DOCKER_ARG_TTY = --privileged -v /dev:/dev ++DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag ++DOCKER_ARG_NET = --network host + + DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + DOCKER_EXEC_RASPBOOT = raspbootcom +@@ -83,6 +89,28 @@ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) + ++jtagboot: ++ $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ ++ $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ ++ /jtag/$(JTAG_BOOT_IMAGE) ++ ++openocd: ++ $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ ++ openocd $(OPENOCD_ARG) ++ ++define gen_gdb ++ RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) $1 ++ cp $(CARGO_OUTPUT) kernel_for_jtag ++ $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ ++ gdb-multiarch -q kernel_for_jtag ++endef ++ ++gdb: clean $(SOURCES) ++ $(call gen_gdb,-C debuginfo=2) ++ ++gdb-opt0: clean $(SOURCES) ++ $(call gen_gdb,-C debuginfo=2 -C opt-level=0) ++ + clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +``` diff --git a/09_hw_debug_JTAG/kernel b/09_hw_debug_JTAG/kernel new file mode 100755 index 00000000..3bfb485a Binary files /dev/null and b/09_hw_debug_JTAG/kernel differ diff --git a/09_hw_debug_JTAG/kernel8.img b/09_hw_debug_JTAG/kernel8.img new file mode 100755 index 00000000..e6f58b22 Binary files /dev/null and b/09_hw_debug_JTAG/kernel8.img differ diff --git a/09_hw_debug_JTAG/src/arch.rs b/09_hw_debug_JTAG/src/arch.rs new file mode 100644 index 00000000..b1f035c5 --- /dev/null +++ b/09_hw_debug_JTAG/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/09_hw_debug_JTAG/src/arch/aarch64.rs b/09_hw_debug_JTAG/src/arch/aarch64.rs new file mode 100644 index 00000000..a44ece9b --- /dev/null +++ b/09_hw_debug_JTAG/src/arch/aarch64.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; +mod time; + +use crate::{bsp, interface}; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static TIMER: time::Timer = time::Timer; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Return a reference to a `interface::time::TimeKeeper` implementation. +pub fn timer() -> &'static impl interface::time::Timer { + &TIMER +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/09_hw_debug_JTAG/src/arch/aarch64/sync.rs b/09_hw_debug_JTAG/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/09_hw_debug_JTAG/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/09_hw_debug_JTAG/src/arch/aarch64/time.rs b/09_hw_debug_JTAG/src/arch/aarch64/time.rs new file mode 100644 index 00000000..ba43474f --- /dev/null +++ b/09_hw_debug_JTAG/src/arch/aarch64/time.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Timer primitives. + +use crate::{interface, warn}; +use core::time::Duration; +use cortex_a::regs::*; + +const NS_PER_S: u64 = 1_000_000_000; + +pub struct Timer; + +impl interface::time::Timer for Timer { + fn resoultion(&self) -> Duration { + Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + } + + fn uptime(&self) -> Duration { + let frq: u64 = CNTFRQ_EL0.get() as u64; + let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; + + Duration::from_nanos(current_count / frq) + } + + fn spin_for(&self, duration: Duration) { + // Instantly return on zero. + if duration.as_nanos() == 0 { + return; + } + + // Calculate the register compare value. + let frq = CNTFRQ_EL0.get() as u64; + let x = match frq.checked_mul(duration.as_nanos() as u64) { + None => { + warn!("Spin duration too long, skipping"); + return; + } + Some(val) => val, + }; + let tval = x / NS_PER_S; + + // Check if it is within supported bounds. + let warn: Option<&str> = if tval == 0 { + Some("smaller") + } else if tval > u32::max_value().into() { + Some("bigger") + } else { + None + }; + + if let Some(w) = warn { + warn!( + "Spin duration {} than architecturally supported, skipping", + w + ); + return; + } + + // Set the compare value register. + CNTP_TVAL_EL0.set(tval as u32); + + // Kick off the counting. // Disable timer interrupt. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + + loop { + // ISTATUS will be '1' when cval ticks have passed. Busy-check it. + if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { + break; + } + } + + // Disable counting again. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); + } +} diff --git a/09_hw_debug_JTAG/src/bsp.rs b/09_hw_debug_JTAG/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/09_hw_debug_JTAG/src/bsp/driver.rs b/09_hw_debug_JTAG/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/09_hw_debug_JTAG/src/bsp/driver/bcm.rs b/09_hw_debug_JTAG/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/0D_virtual_memory/src/gpio.rs b/09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_gpio.rs similarity index 57% rename from 0D_virtual_memory/src/gpio.rs rename to 09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_gpio.rs index 7affea08..a9ceda61 100644 --- a/0D_virtual_memory/src/gpio.rs +++ b/09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -1,30 +1,15 @@ -/* - * 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. - */ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; use core::ops; use register::{mmio::ReadWrite, register_bitfields}; +// GPIO registers. +// // Descriptions taken from // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf register_bitfields! { @@ -36,8 +21,7 @@ register_bitfields! { FSEL15 OFFSET(15) NUMBITS(3) [ Input = 0b000, Output = 0b001, - RXD0 = 0b100, // UART0 - Alternate function 0 - RXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART RX ], @@ -45,8 +29,7 @@ register_bitfields! { FSEL14 OFFSET(12) NUMBITS(3) [ Input = 0b000, Output = 0b001, - TXD0 = 0b100, // UART0 - Alternate function 0 - TXD1 = 0b010 // Mini UART - Alternate function 5 + AltFunc0 = 0b100 // PL011 UART TX ] ], @@ -95,12 +78,13 @@ pub struct RegisterBlock { pub GPPUDCLK1: ReadWrite, // 0x9C } -/// Public interface to the GPIO MMIO area -pub struct GPIO { +/// The driver's private data. +struct GPIOInner { base_addr: usize, } -impl ops::Deref for GPIO { +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { type Target = RegisterBlock; fn deref(&self) -> &Self::Target { @@ -108,13 +92,66 @@ impl ops::Deref for GPIO { } } -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } } - /// Returns a pointer to the register block + /// Return a pointer to the register block. fn ptr(&self) -> *const RegisterBlock { self.base_addr as *const _ } } + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..c75312c9 --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// 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 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until the TX FIFO empty flag is set. + r.lock(|inner| loop { + if inner.FR.is_set(FR::TXFE) { + break; + } + + arch::nop(); + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + ret + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| loop { + // Read from the RX FIFO until the empty bit is '1'. + if !inner.FR.is_set(FR::RXFE) { + inner.DR.get(); + } else { + break; + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/09_hw_debug_JTAG/src/bsp/rpi.rs b/09_hw_debug_JTAG/src/bsp/rpi.rs new file mode 100644 index 00000000..c22c47bb --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/rpi.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} diff --git a/09_hw_debug_JTAG/src/bsp/rpi/link.ld b/09_hw_debug_JTAG/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/09_hw_debug_JTAG/src/bsp/rpi/memory_map.rs b/09_hw_debug_JTAG/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/09_hw_debug_JTAG/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/09_hw_debug_JTAG/src/interface.rs b/09_hw_debug_JTAG/src/interface.rs new file mode 100644 index 00000000..15fa3c15 --- /dev/null +++ b/09_hw_debug_JTAG/src/interface.rs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} + +/// Timekeeping interfaces. +pub mod time { + use core::time::Duration; + + /// Timer functions. + pub trait Timer { + /// The timer's resolution. + fn resoultion(&self) -> Duration; + + /// The uptime since power-on of the device. + /// + /// This includes time consumed by firmware and bootloaders. + fn uptime(&self) -> Duration; + + /// Spin for a given duration. + fn spin_for(&self, duration: Duration); + } +} diff --git a/09_hw_debug_JTAG/src/main.rs b/09_hw_debug_JTAG/src/main.rs new file mode 100644 index 00000000..6730048a --- /dev/null +++ b/09_hw_debug_JTAG/src/main.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; + +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +mod bsp; + +mod interface; +mod panic_wait; +mod print; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } + + bsp::post_driver_init(); + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use core::time::Duration; + use interface::time::Timer; + + println!("Booting on: {}", bsp::board_name()); + println!( + "Architectural timer resolution: {} ns", + arch::timer().resoultion().as_nanos() + ); + + println!("Drivers loaded:"); + for (i, driver) in bsp::device_drivers().iter().enumerate() { + println!(" {}. {}", i + 1, driver.compatible()); + } + + // Test a failing timer case. + arch::timer().spin_for(Duration::from_nanos(1)); + + loop { + println!("Spinning for 1 second"); + arch::timer().spin_for(Duration::from_secs(1)); + } +} diff --git a/09_hw_debug_JTAG/src/panic_wait.rs b/09_hw_debug_JTAG/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/09_hw_debug_JTAG/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/09_hw_debug_JTAG/src/print.rs b/09_hw_debug_JTAG/src/print.rs new file mode 100644 index 00000000..86ab59b3 --- /dev/null +++ b/09_hw_debug_JTAG/src/print.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +/// Prints a warning, with newline. +#[macro_export] +macro_rules! warn { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/09_hw_debug_JTAG/src/runtime_init.rs b/09_hw_debug_JTAG/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/09_hw_debug_JTAG/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/0A_power/.cargo/config b/0A_power/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0A_power/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0A_power/Cargo.lock b/0A_power/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0A_power/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0A_power/Cargo.toml b/0A_power/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0A_power/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0A_power/Makefile b/0A_power/Makefile deleted file mode 100644 index 16e51f6c..00000000 --- a/0A_power/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) kernel8.img - -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/0A_power/README.md b/0A_power/README.md deleted file mode 100644 index fcf4f2f9..00000000 --- a/0A_power/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Tutorial 0A - Power management - -For embedded systems, power consumption is critical. The Raspberry Pi 3 has a -very sophisticated PM interface. You can turn each device on and off -independently. There's a catch though. The GPIO VCC pins are hardwired, there's -no way to turn them off programmatically. This means if you connect some devices -to them, you'll have to implement a way to turn those devices off (with a -transistor connected to a data GPIO pin for example). - -## power.rs - -Unfortunately, the documentation about the PM interface is very very rare. We -will therefore more or less implement a carbon copy of respective functions of -Linux' -[bcm2835_wdt.c](https://github.com/torvalds/linux/blob/master/drivers/watchdog/bcm2835_wdt.c) -driver. - -The power management controller is one of the peripherals that are not emulated -properly by QEMU. Our implementation therefore works on real hardware only. - -`Power::off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO)` shuts down the -board to an almost zero power consumption state. - -`Power::reset(&self)` reboots the machine. Also handled by the PMC, and since -the Raspberry Pi does not have a hardware reset button, it's very useful. - -When using `make raspboot` and choosing `reset()`, you can see your code in -action nicely as you generate a boot-loop. - - -## gpio.rs - -We introduce a lot of new GPIO pins. It's a good time to refactor the GPIO MMIO -interface into its own type with the common `RegisterBlock` implementation that -you already know from the other components. - -## main.rs - -We display a simple menu, and wait for user input. Depending on the input, we -reboot the system or power it off. diff --git a/0A_power/kernel8.img b/0A_power/kernel8.img deleted file mode 100755 index b6ae6d0e..00000000 Binary files a/0A_power/kernel8.img and /dev/null differ diff --git a/0A_power/link.ld b/0A_power/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/0A_power/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0A_power/raspi3_boot/Cargo.toml b/0A_power/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0A_power/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0A_power/raspi3_boot/src/lib.rs b/0A_power/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/0A_power/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/0A_power/src/delays.rs b/0A_power/src/delays.rs deleted file mode 100644 index 63414e4f..00000000 --- a/0A_power/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/0A_power/src/gpio.rs b/0A_power/src/gpio.rs deleted file mode 100644 index 608ba532..00000000 --- a/0A_power/src/gpio.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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/0A_power/src/main.rs b/0A_power/src/main.rs deleted file mode 100644 index b928ee51..00000000 --- a/0A_power/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod delays; -mod gpio; -mod mbox; -mod power; -mod uart; - -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(); - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - let power = power::Power::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"); - - loop { - uart.puts("\n 1 - power off\n 2 - reset\nChoose one: "); - let c = uart.getc(); - uart.send(c); - - match c { - '1' => { - if power.off(&mut mbox, &gpio).is_err() { - uart.puts("Mailbox error in Power::off()"); - } - } - '2' => power.reset(), - _ => {} - } - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0A_power/src/mbox.rs b/0A_power/src/mbox.rs deleted file mode 100644 index f5a420a5..00000000 --- a/0A_power/src/mbox.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 SETPOWER: u32 = 0x28001; - 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/0A_power/src/power.rs b/0A_power/src/power.rs deleted file mode 100644 index 4b555a05..00000000 --- a/0A_power/src/power.rs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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::MMIO_BASE; -use crate::delays; -use crate::gpio; -use crate::mbox; -use core::ops; -use core::sync::atomic::{compiler_fence, Ordering}; -use register::mmio::*; - -const POWER_BASE: u32 = MMIO_BASE + 0x100_01C; - -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - PM_RSTC: ReadWrite, // 0x1C - PM_RSTS: ReadWrite, // 0x20 - PM_WDOG: ReadWrite, // 0x24 -} - -const PM_PASSWORD: u32 = 0x5a_000_000; -const PM_RSTC_WRCFG_CLR: u32 = 0xffff_ffcf; -const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020; - -// The Raspberry Pi firmware uses the RSTS register to know which -// partition to boot from. The partition value is spread into bits 0, 2, -// 4, 6, 8, 10. Partition 63 is a special partition used by the -// firmware to indicate halt. -const PM_RSTS_RASPBERRYPI_HALT: u32 = 0x555; - -pub enum PowerError { - MailboxError, -} -pub type Result = ::core::result::Result; - -/// Public interface to the Power subsystem -pub struct Power; - -impl ops::Deref for Power { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Power { - pub fn new() -> Power { - Power - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - POWER_BASE as *const _ - } - - /// Shutdown the board - pub fn off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> { - // power off devices one by one - for dev_id in 0..16 { - mbox.buffer[0] = 8 * 4; - mbox.buffer[1] = mbox::REQUEST; - mbox.buffer[2] = mbox::tag::SETPOWER; - mbox.buffer[3] = 8; - mbox.buffer[4] = 8; - mbox.buffer[5] = dev_id; // device id - mbox.buffer[6] = 0; // bit 0: off, bit 1: no wait - mbox.buffer[7] = 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(PowerError::MailboxError); - }; - } - - // power off gpio pins (but not VCC pins) - gpio.GPFSEL0.set(0); - gpio.GPFSEL1.set(0); - gpio.GPFSEL2.set(0); - gpio.GPFSEL3.set(0); - gpio.GPFSEL4.set(0); - gpio.GPFSEL5.set(0); - - gpio.GPPUD.set(0); - delays::wait_cycles(150); - - gpio.GPPUDCLK0.set(0xffff_ffff); - gpio.GPPUDCLK1.set(0xffff_ffff); - delays::wait_cycles(150); - - // flush GPIO setup - gpio.GPPUDCLK0.set(0); - gpio.GPPUDCLK1.set(0); - - // We set the watchdog hard reset bit here to distinguish this - // reset from the normal (full) reset. bootcode.bin will not - // reboot after a hard reset. - let mut val = self.PM_RSTS.get(); - val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT; - self.PM_RSTS.set(val); - - // Continue with normal reset mechanism - self.reset(); - } - - /// Reboot - pub fn reset(&self) -> ! { - // use a timeout of 10 ticks (~150us) - self.PM_WDOG.set(PM_PASSWORD | 10); - let mut val = self.PM_RSTC.get(); - val &= PM_RSTC_WRCFG_CLR; - val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; - self.PM_RSTC.set(val); - - loop {} - } -} diff --git a/0A_power/src/uart.rs b/0A_power/src/uart.rs deleted file mode 100644 index 8950fea6..00000000 --- a/0A_power/src/uart.rs +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - 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); - } - } -} diff --git a/0B_hw_debug_JTAG/.cargo/config b/0B_hw_debug_JTAG/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0B_hw_debug_JTAG/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0B_hw_debug_JTAG/Cargo.lock b/0B_hw_debug_JTAG/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0B_hw_debug_JTAG/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0B_hw_debug_JTAG/Cargo.toml b/0B_hw_debug_JTAG/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0B_hw_debug_JTAG/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0B_hw_debug_JTAG/Makefile b/0B_hw_debug_JTAG/Makefile deleted file mode 100644 index 913fc341..00000000 --- a/0B_hw_debug_JTAG/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/0B_hw_debug_JTAG/README.md b/0B_hw_debug_JTAG/README.md deleted file mode 100644 index 5812d673..00000000 --- a/0B_hw_debug_JTAG/README.md +++ /dev/null @@ -1,265 +0,0 @@ -# Tutorial 0B - Hardware Debugging using JTAG - -In the upcoming tutorials, we are going to touch sensitive areas of the RPi's -SoC that can make our debugging life very hard. For example, changing the -processor's `Exception Level` or introducing `virtual memory`. - -A hardware based debugger can sometimes be the last resort when searching for a -tricky bug. Especially for debugging intricate, architecture-specific HW issues, -it will be handy, because in this area `QEMU` sometimes can not help, since it -abstracts certain features of our RPi's HW and doesn't simulate down to the very -last bit. - -So lets introduce `JTAG` debugging. Once set up, it will allow us to single-step -through our kernel on the real HW. How cool is that?! - -![JTAG live demo](../doc/jtag_demo.gif) - -## Outline - -Functionally, this tutorial is the same as the previous one, where we reset or -power down the RPi. Around that, we add infrastructure for JTAG debugging. - -## Hardware - -Unlike microcontroller boards like the `STM32F3DISCOVERY`, which is used in our -WG's [Embedded Rust Book](https://rust-embedded.github.io/book/start/hardware.html), -the RPi does not have an embedded debugger on it's board. Hence, you need to buy one. - -For this tutorial, we will use the -[ARM-USB-TINY-H](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/) by -OLIMEX. It has a standard -[ARM JTAG 20 connector](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0499dj/BEHEIHCE.html). -Unfortunately, the RPi does not, so we have to connect it via jumper wires. - -### Wiring - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GPIO #NameJTAG #NoteDiagram
VTREF1to 3.3V
GND4to GND
22TRST3
26TDI5
27TMS7
25TCK9
23RTCK11
24TDO13
- -

- -## Configuring GPIO for JTAG - -Before it is possible to connect, we additionally have to configure the -respective GPIO pins for `JTAG` functionality from software. Our approach is as -allows: - -Via `raspboot`, we load a tiny helper binary onto the RPi which configures the -pins respectively and then parks the executing core in an endless loop, waiting -for the `JTAG` debugger to connect. The helper binary is maintained separately -in this repository's [X1_JTAG_boot](../X1_JTAG_boot) folder, and is a -stripped-down version of the code we use in our tutorials. - -This functionality is provided by the new `Makefile` target `make jtagboot`. - -```console -ferris@box:~$ make jtagboot -Raspbootcom V1.0 -### Listening on /dev/ttyUSB0 -RBIN64 -### sending kernel /jtag/jtag_boot.img [759 byte] -### finished sending - - -[i] JTAG is live. Please connect. -``` - -It is important to keep the USB serial connected and the terminal open with -`raspboot` running. When we load the actual kernel later, `UART` output will -appear here. - -## OpenOCD - -Next, we need to launch the [Open On-Chip Debugger](http://openocd.org/), aka -`OpenOCD` to actually connect the `JTAG`. - -As always, our tutorials try to be as painless as possible regarding dev-tools, -which is why we have packaged everything into a [dedicated Docker container](../docker/raspi3-openocd) that will be provisioned automagically on the first run. - - - -Now connect the Olimex USB JTAG debugger, open a new terminal and in the same -folder, type `make openocd` (in that order!). You will see some initial output: - -```console -ferris@box:~$ make openocd -Open On-Chip Debugger 0.10.0+dev-ge243075 (2019-03-07-19:07) -Licensed under GNU GPL v2 -For bug reports, read - http://openocd.org/doc/doxygen/bugs.html -trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst -adapter speed: 1000 kHz -jtag_ntrst_delay: 500 -Info : Listening on port 6666 for tcl connections -Info : Listening on port 4444 for telnet connections -Info : clock speed 1000 kHz -Info : JTAG tap: rpi3.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x4) -Info : rpi3.core0: hardware has 6 breakpoints, 4 watchpoints -Info : rpi3.core1: hardware has 6 breakpoints, 4 watchpoints -Info : rpi3.core2: hardware has 6 breakpoints, 4 watchpoints -Info : rpi3.core3: hardware has 6 breakpoints, 4 watchpoints -Info : Listening on port 3333 for gdb connections -Info : Listening on port 3334 for gdb connections -Info : Listening on port 3335 for gdb connections -Info : Listening on port 3336 for gdb connections -``` - -`OpenOCD` has detected the four cores of the RPi, and opened four network ports -to which `gdb` can now connect to debug the respective core. - -## GDB - -Finally, we need an `AArch64`-capable version of `gdb`. You guessed right, we -packaged [another container for you](../docker/raspi3-gdb). It can be launched -via `make gdb`. - -This Makefile target actually does a little more. It builds a special version of -our kernel with debug information included. This enables `gdb` to show the `Rust` -source code line we are currently debugging. It also launches `gdb` such -that it already loads this debug build (`kernel8_for_jtag`). - -We can now use the `gdb` commandline to - 1. Set breakpoints in our kernel - 2. Load the kernel via JTAG in memory (remember that currently, the RPi is still executing -the minimal JTAG pin enablement binary). - 3. Manipulate the program counter of the RPi to start execution at our kernel's entry point. - 4. Single-step through its execution. - -```shell ->>> break _boot_cores -Breakpoint 1 at 0x80000 ->>> target remote :3333 # Connect to OpenOCD, raspi3.core0 ->>> load # Load the kernel into the Raspi's DRAM over JTAG. -Loading section .text, size 0x6cc lma 0x80000 -Loading section .rodata, size 0x9a lma 0x806cc -Start address 0x80000, load size 1894 -Transfer rate: 66 KB/sec, 947 bytes/write. ->>> set $pc = 0x80000 # Set RPI's program counter to the start of the kernel binary. ->>> cont -Breakpoint 1, 0x0000000000080000 in _boot_cores () ->>> step ->>> step # Single-step through the kernel ->>> ... -``` - -### Remarks - -#### Optimization - -When debugging an OS binary, you have to make a trade-off between the -granularity at which you can step through your Rust source-code and the -optimization level of the generated binary. The `make` and `make gdb` targets -produce a `--release` binary, which includes an optimization level of three -(`-opt-level=3`). However, in this case, the compiler will inline very -aggressively and pack together reads and writes where possible. As a result, it -will not always be possible to hit breakpoints exactly where you want to -regarding the line of source code file. - -For this reason, the Makefile also provides the `make gdb-opt0` target, which -uses `-opt-level=0`. Hence, it will allow you to have finer debugging -granularity. However, please keep in mind that when debugging code that closely -deals with HW, a compiler optimization that squashes reads or writes to volatile -registers can make all the difference in execution. FYI, the demo gif above has -been recorded with `gdb-opt0`. - -#### GDB control - -At some point, you may reach delay loops or code that waits on user input from -the serial. Here, single stepping might not be feasible or work anymore. You can -jump over these roadblocks by setting other breakpoints beyond these areas, and -reach them using the `cont` command. - -Pressing `ctrl+c` in `gdb` will stop execution of the RPi again in case you -continued it without further breakpoints. - -## Notes on USB connection constraints - -If you followed the tutorial from top to bottom, everything should be fine -regarding USB connections. - -Still, please note that in its current form, our `Makefile` makes implicit -assumptions about the naming of the connected USB devices. It expects -`/dev/ttyUSB0` to be the `UART` device. - -Hence, please ensure the following order of connecting the devices to your box: - 1. Connect the USB serial. - 2. Afterwards, the Olimex debugger. - -This way, Linux enumerates the devices accordingly. This has to be done only -once. It is fine to disconnect and connect the serial multiple times, e.g. for -kicking off different `make jtagboot` runs, while keeping the debugger -connected. - -## In summary - -1. `make jtagboot` and keep terminal open. -2. Connect USB serial device. -3. Connect `JTAG` debugger USB device. -4. In new terminal, `make openocd`. -5. In new terminal, `make gdb` or make `make gdb-opt0`. - -## Acknowledgments - -Thanks to [@naotaco](https://github.com/naotaco) for laying the groundwork for -this tutorial. diff --git a/0B_hw_debug_JTAG/kernel8 b/0B_hw_debug_JTAG/kernel8 deleted file mode 100755 index 3e949a47..00000000 Binary files a/0B_hw_debug_JTAG/kernel8 and /dev/null differ diff --git a/0B_hw_debug_JTAG/kernel8.img b/0B_hw_debug_JTAG/kernel8.img deleted file mode 100755 index b6ae6d0e..00000000 Binary files a/0B_hw_debug_JTAG/kernel8.img and /dev/null differ diff --git a/0B_hw_debug_JTAG/link.ld b/0B_hw_debug_JTAG/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/0B_hw_debug_JTAG/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0B_hw_debug_JTAG/raspi3_boot/Cargo.toml b/0B_hw_debug_JTAG/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0B_hw_debug_JTAG/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0B_hw_debug_JTAG/raspi3_boot/src/lib.rs b/0B_hw_debug_JTAG/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/0B_hw_debug_JTAG/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/0B_hw_debug_JTAG/src/delays.rs b/0B_hw_debug_JTAG/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/0B_hw_debug_JTAG/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/0B_hw_debug_JTAG/src/gpio.rs b/0B_hw_debug_JTAG/src/gpio.rs deleted file mode 100644 index ad104078..00000000 --- a/0B_hw_debug_JTAG/src/gpio.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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::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/0B_hw_debug_JTAG/src/main.rs b/0B_hw_debug_JTAG/src/main.rs deleted file mode 100644 index 281cc66f..00000000 --- a/0B_hw_debug_JTAG/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod delays; -mod gpio; -mod mbox; -mod power; -mod uart; - -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(); - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::new(); - let power = power::Power::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"); - - loop { - uart.puts("\n 1 - power off\n 2 - reset\nChoose one: "); - let c = uart.getc(); - uart.send(c); - - match c { - '1' => { - if power.off(&mut mbox, &gpio).is_err() { - uart.puts("Mailbox error in Power::off()"); - } - } - '2' => power.reset(), - _ => {} - } - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0B_hw_debug_JTAG/src/mbox.rs b/0B_hw_debug_JTAG/src/mbox.rs deleted file mode 100644 index 484c494e..00000000 --- a/0B_hw_debug_JTAG/src/mbox.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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::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 SETPOWER: u32 = 0x28001; - 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/0B_hw_debug_JTAG/src/power.rs b/0B_hw_debug_JTAG/src/power.rs deleted file mode 100644 index 4b555a05..00000000 --- a/0B_hw_debug_JTAG/src/power.rs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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::MMIO_BASE; -use crate::delays; -use crate::gpio; -use crate::mbox; -use core::ops; -use core::sync::atomic::{compiler_fence, Ordering}; -use register::mmio::*; - -const POWER_BASE: u32 = MMIO_BASE + 0x100_01C; - -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - PM_RSTC: ReadWrite, // 0x1C - PM_RSTS: ReadWrite, // 0x20 - PM_WDOG: ReadWrite, // 0x24 -} - -const PM_PASSWORD: u32 = 0x5a_000_000; -const PM_RSTC_WRCFG_CLR: u32 = 0xffff_ffcf; -const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020; - -// The Raspberry Pi firmware uses the RSTS register to know which -// partition to boot from. The partition value is spread into bits 0, 2, -// 4, 6, 8, 10. Partition 63 is a special partition used by the -// firmware to indicate halt. -const PM_RSTS_RASPBERRYPI_HALT: u32 = 0x555; - -pub enum PowerError { - MailboxError, -} -pub type Result = ::core::result::Result; - -/// Public interface to the Power subsystem -pub struct Power; - -impl ops::Deref for Power { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Power { - pub fn new() -> Power { - Power - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - POWER_BASE as *const _ - } - - /// Shutdown the board - pub fn off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> { - // power off devices one by one - for dev_id in 0..16 { - mbox.buffer[0] = 8 * 4; - mbox.buffer[1] = mbox::REQUEST; - mbox.buffer[2] = mbox::tag::SETPOWER; - mbox.buffer[3] = 8; - mbox.buffer[4] = 8; - mbox.buffer[5] = dev_id; // device id - mbox.buffer[6] = 0; // bit 0: off, bit 1: no wait - mbox.buffer[7] = 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(PowerError::MailboxError); - }; - } - - // power off gpio pins (but not VCC pins) - gpio.GPFSEL0.set(0); - gpio.GPFSEL1.set(0); - gpio.GPFSEL2.set(0); - gpio.GPFSEL3.set(0); - gpio.GPFSEL4.set(0); - gpio.GPFSEL5.set(0); - - gpio.GPPUD.set(0); - delays::wait_cycles(150); - - gpio.GPPUDCLK0.set(0xffff_ffff); - gpio.GPPUDCLK1.set(0xffff_ffff); - delays::wait_cycles(150); - - // flush GPIO setup - gpio.GPPUDCLK0.set(0); - gpio.GPPUDCLK1.set(0); - - // We set the watchdog hard reset bit here to distinguish this - // reset from the normal (full) reset. bootcode.bin will not - // reboot after a hard reset. - let mut val = self.PM_RSTS.get(); - val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT; - self.PM_RSTS.set(val); - - // Continue with normal reset mechanism - self.reset(); - } - - /// Reboot - pub fn reset(&self) -> ! { - // use a timeout of 10 ticks (~150us) - self.PM_WDOG.set(PM_PASSWORD | 10); - let mut val = self.PM_RSTC.get(); - val &= PM_RSTC_WRCFG_CLR; - val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; - self.PM_RSTC.set(val); - - loop {} - } -} diff --git a/0B_hw_debug_JTAG/src/uart.rs b/0B_hw_debug_JTAG/src/uart.rs deleted file mode 100644 index 28fdb206..00000000 --- a/0B_hw_debug_JTAG/src/uart.rs +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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::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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - 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); - } - } -} diff --git a/0C_exception_levels/.cargo/config b/0C_exception_levels/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0C_exception_levels/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0C_exception_levels/Cargo.lock b/0C_exception_levels/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0C_exception_levels/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0C_exception_levels/Cargo.toml b/0C_exception_levels/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0C_exception_levels/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0C_exception_levels/Makefile b/0C_exception_levels/Makefile deleted file mode 100644 index e5d41e64..00000000 --- a/0C_exception_levels/Makefile +++ /dev/null @@ -1,105 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg - -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/0C_exception_levels/README.md b/0C_exception_levels/README.md deleted file mode 100644 index 738e8665..00000000 --- a/0C_exception_levels/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Tutorial 0C - Exception Levels - -In `AArch64`, there are four so-called exception levels: - -| Exception Level | Typically used for | -| ------------- | ------------- | -| EL0 | Userspace applications | -| EL1 | OS Kernel | -| EL2 | Hypervisor | -| EL3 | Low-Level Firmware | - -If you are familiar with the `x86` architecture, `ELs` are the counterpart to [privilege rings](https://en.wikipedia.org/wiki/Protection_ring). - -At this point, I strongly recommend that you glimpse over `Chapter 3` of the [Programmer’s Guide for ARMv8-A](http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf) before you continue. -It gives a concise overview about the topic. - -## Scope of this tutorial - -If you set up your SD Card exactly like mentioned in the repository's [top-level README](../README.md#prerequisites), -our binary will start executing in `EL2`. Since we have an OS-focus, we will now write code that will cause a transition -into the more appropriate `EL1`. - -## Checking for EL2 in the entrypoint - -First of all, we need to ensure that we actually run in `EL2` before we can call respective code to transition to EL1: - -```rust -/// 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(); - } -} -``` - -If this is the case, we continue with preparing the `EL2` -> `EL1` transition in `setup_and_enter_el1_from_el2()`. - -## Transition preparation - -Since `EL2` is more privileged than `EL1`, it has control over various processor features and can allow or disallow -`EL1` code to use them. One such example is access to timer and counter registers. We are already using them since -[tutorial 09_delays](../09_delays/), so we want to keep them. Therefore we set the respective flags in the -[Counter-timer Hypervisor Control register](https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/cnthctl_el2.rs.html) -and additionally set the virtual offset to zero so that we get the real physical value everytime: - -```rust -// Enable timer counter registers for EL1 -CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); - -// No offset for reading the counters -CNTVOFF_EL2.set(0); -``` - -Next, we configure the [Hypervisor Configuration Register](https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/hcr_el2.rs.html) such that `EL1` should actually run in `AArch64` mode, and not in `AArch32`, which would also be possible. - -```rust -// Set EL1 execution state to AArch64 -HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64; -``` - -## Returning from an exception that never happened - -There is actually only one way to transition from a higher EL to a lower EL, which is by way of executing -the [ERET](https://docs.rs/cortex-a/2.4.0/src/cortex_a/asm.rs.html#49-62) instruction. - -This instruction will copy the contents of the [Saved Program Status Register - EL2](https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/spsr_el2.rs.html) -to `Current Program Status Register - EL1` and jump to the instruction address that is stored in the [Exception Link Register - EL2](https://docs.rs/cortex-a/2.4.0/src/cortex_a/regs/elr_el2.rs.html). - -This is basically the reverse of what is happening when an exception is taken. You'll learn about it in tutorial [10_exception_groundwork](../10_exceptions_groundwork). - -```rust -// Set up a simulated exception return. -// -// First, fake a saved program status, where all interrupts were -// masked and SP_EL1 was used as a stack pointer. -SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, -); - -// Second, let the link register point to reset(). -ELR_EL2.set(reset as *const () as u64); -``` - -As you can see, we are populating `ELR_EL2` with the address of the [reset()](raspi3_boot/src/lib.rs#L51) function that we earlier used to call directly from the entrypoint. - -Finally, we set the stack pointer for `SP_EL1` and call `ERET`: - -```rust -// 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() -``` - -## Are we stackless? - -We just wrote a big rust function, `setup_and_enter_el1_from_el2()`, that is executed in a context where we -do not have a stack yet. We should double-check the generated machine code: - -```console -ferris@box:~$ make objdump -cargo objdump --target aarch64-unknown-none-softfloat -- -disassemble -print-imm-hex kernel8 - -kernel8: file format ELF64-aarch64-little - -Disassembly of section .text: -raspi3_boot::setup_and_enter_el1_from_el2::hf5d23e5bead7ee4e: - 808bc: e8 03 1f aa mov x8, xzr - 808c0: e9 07 00 32 orr w9, wzr, #0x3 - 808c4: 09 e1 1c d5 msr CNTHCTL_EL2, x9 - 808c8: 68 e0 1c d5 msr CNTVOFF_EL2, x8 - 808cc: 08 00 00 90 adrp x8, #0x0 - 808d0: ea 03 01 32 orr w10, wzr, #0x80000000 - 808d4: 0a 11 1c d5 msr HCR_EL2, x10 - 808d8: ab 78 80 52 mov w11, #0x3c5 - 808dc: 0b 40 1c d5 msr SPSR_EL2, x11 - 808e0: ec 03 0d 32 orr w12, wzr, #0x80000 - 808e4: 08 21 22 91 add x8, x8, #0x888 - 808e8: 28 40 1c d5 msr ELR_EL2, x8 - 808ec: 0c 41 1c d5 msr SP_EL1, x12 - 808f0: e0 03 9f d6 eret -``` - -Looks good! Thanks zero-overhead abstractions in the [cortex-a](https://github.com/rust-embedded/cortex-a) crate! :heart_eyes: - -## Testing - -In `main.rs`, we added some tests to see if access to the counter timer registers is actually working, and if the mask bits in `SPSR_EL2` made it to `EL1` as well: - -```console -ferris@box:~$ make raspboot - -[0] UART is live! -[1] Press a key to continue booting... Greetings fellow Rustacean! -[i] Executing in EL: 1 - -Testing EL1 access to timer registers: - Delaying for 3 seconds now. - 1..2..3 - Works! - -Checking interrupt mask bits: - D: Masked. - A: Masked. - I: Masked. - F: Masked. -``` diff --git a/0C_exception_levels/kernel8 b/0C_exception_levels/kernel8 deleted file mode 100755 index ffe4d6b3..00000000 Binary files a/0C_exception_levels/kernel8 and /dev/null differ diff --git a/0C_exception_levels/kernel8.img b/0C_exception_levels/kernel8.img deleted file mode 100755 index c37dc420..00000000 Binary files a/0C_exception_levels/kernel8.img and /dev/null differ diff --git a/0C_exception_levels/link.ld b/0C_exception_levels/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/0C_exception_levels/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0C_exception_levels/raspi3_boot/Cargo.toml b/0C_exception_levels/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0C_exception_levels/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0C_exception_levels/raspi3_boot/src/lib.rs b/0C_exception_levels/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/0C_exception_levels/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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/0C_exception_levels/src/delays.rs b/0C_exception_levels/src/delays.rs deleted file mode 100644 index 585584f2..00000000 --- a/0C_exception_levels/src/delays.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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; -use cortex_a::regs::*; - -/* - * - * Using the CPU's counter registers - * - */ -/// Wait N microsec (ARM CPU only) -pub fn wait_usec(n: u32) { - // Get the counter frequency - let frq = CNTFRQ_EL0.get(); - - // Calculate number of ticks - let tval = (u64::from(frq) * u64::from(n) / 1_000_000) as u32; - - // Set the compare value register - CNTP_TVAL_EL0.set(tval); - - // Kick off the counting // Disable timer interrupt - CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); - - loop { - // ISTATUS will be one when cval ticks have passed. Continuously check it. - if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { - break; - } - } - - // Disable counting again - CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); -} - -/* - * - * 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/0C_exception_levels/src/gpio.rs b/0C_exception_levels/src/gpio.rs deleted file mode 100644 index ad104078..00000000 --- a/0C_exception_levels/src/gpio.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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::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/0C_exception_levels/src/main.rs b/0C_exception_levels/src/main.rs deleted file mode 100644 index 1dc8a905..00000000 --- a/0C_exception_levels/src/main.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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] - -const MMIO_BASE: u32 = 0x3F00_0000; - -mod delays; -mod gpio; -mod mbox; -mod uart; - -use cortex_a::regs::*; - -fn check_timer(uart: &uart::Uart) { - uart.puts( - "Testing EL1 access to timer registers:\ - \n Delaying for 3 seconds now.\n", - ); - delays::wait_usec(1_000_000); - uart.puts(" 1.."); - delays::wait_usec(1_000_000); - uart.puts("2.."); - delays::wait_usec(1_000_000); - uart.puts( - "3\ - \n Works!\n\n", - ); -} - -fn check_daif(uart: &uart::Uart) { - uart.puts("Checking interrupt mask bits:\n"); - - let daif = DAIF.extract(); - for x in &[ - (" D: ", DAIF::D), - (" A: ", DAIF::A), - (" I: ", DAIF::I), - (" F: ", DAIF::F), - ] { - uart.puts(x.0); - if daif.is_set(x.1) { - uart.puts("Masked.\n"); - } else { - uart.puts("Unmasked.\n"); - } - } -} - -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(); - let mut mbox = mbox::Mbox::new(); - let uart = uart::Uart::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("[i] Executing in EL: "); - uart.dec(CurrentEL.read(CurrentEL::EL)); - uart.puts("\n\n"); - - check_timer(&uart); - check_daif(&uart); - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0C_exception_levels/src/mbox.rs b/0C_exception_levels/src/mbox.rs deleted file mode 100644 index a9791c80..00000000 --- a/0C_exception_levels/src/mbox.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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::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/0C_exception_levels/src/uart.rs b/0C_exception_levels/src/uart.rs deleted file mode 100644 index d3f5796c..00000000 --- a/0C_exception_levels/src/uart.rs +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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::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) [] - ] -} - -const UART_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; - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*Self::ptr() } - } -} - -impl Uart { - pub fn new() -> Uart { - Uart - } - - /// Returns a pointer to the register block - fn ptr() -> *const RegisterBlock { - 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 decimal - pub fn dec(&self, d: u32) { - let mut digits: [char; 10] = ['\0'; 10]; - let mut d = d; - - for i in digits.iter_mut() { - *i = ((d % 10) + 0x30) as u8 as char; - - d /= 10; - - if d == 0 { - break; - } - } - - for c in digits.iter().rev() { - self.send(*c); - } - } -} diff --git a/0D_virtual_memory/.cargo/config b/0D_virtual_memory/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0D_virtual_memory/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0D_virtual_memory/Cargo.lock b/0D_virtual_memory/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0D_virtual_memory/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0D_virtual_memory/Cargo.toml b/0D_virtual_memory/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0D_virtual_memory/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0D_virtual_memory/Makefile b/0D_virtual_memory/Makefile deleted file mode 100644 index 78b808e6..00000000 --- a/0D_virtual_memory/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) \ - $(DOCKER_EXEC_RASPBOOT_DEV) kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/0D_virtual_memory/README.md b/0D_virtual_memory/README.md deleted file mode 100644 index b8fbbde2..00000000 --- a/0D_virtual_memory/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# Tutorial 0D - Virtual Memory - -Virtual memory is an immensely complex, but exciting topic. In this first -lesson, we start slow and switch on the MMU and use static page tables. We will -only be concerned about the first `1 GiB` of address space. That is the amount -of `DRAM` the usual Raspberry Pi 3 has. As we already know, the upper `16 MiB` -of this gigabyte-window are occupied by the Raspberry's peripherals such as the -UART. - -## MMU and paging theory - -At this point, we will not reinvent the wheel again and go into detailed -descriptions of how paging in modern application processors works. The internet -is full of great resources regarding this topic, and we encourage you to read -some of it to get a high-level understanding of the topic. - -To follow the rest of this `AArch64` specific tutorial, we strongly recommend -that you stop right here and first read `Chapter 12` of the [ARM Cortex-A Series -Programmer's Guide for -ARMv8-A](http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf) -before you continue. This will set you up with all the `AArch64`-specific -knowledge needed to follow along. - -Back from reading `Chapter 12` already? Good job :+1:! - -## Approach - -The following partitioning will be used for the static page tables: -- The first `2 MiB` will be mapped using a Level 3 table with `4 KiB` granule. - - This aperture includes, among others, the kernel's code, read-only data, and - mutable data. All of which will be `identity mapped` to make our life easy - for now. - - In the past, we already made sure that the linker script aligns the - respective regions to `4 KiB` boundaries. - - This way, we can conveniently flag corresponding regions in distinct page - table entries. E.g. marking the code regions executable, while the mutable - data regions are not. -- All the rest will be mapped using `2 MiB` granule. - -The actual code is divided into two files: `memory.rs` and `memory/mmu.rs`. - -### memory.rs - -This file is used to describe our kernel's memory layout in a high-level -abstraction using our own descriptor format. We can define ranges of arbitrary -length and set respective attributes, for example if the bits and bytes in this -range should be executable or not. - -The descriptors we use here are agnostic of the hardware `MMU`'s actual -descriptors, and we are also agnostic of the paging granule the `MMU` will use. -Having this distinction is less of a technical need and more a convenience -feature for us in order to easily describe the kernels memory layout, and -hopefully it makes the whole concept a bit more graspable for the reader. - -The file contains a global `static KERNEL_VIRTUAL_LAYOUT` array which -stores these descriptors. The policy is to only store regions that are **not** -ordinary, normal chacheable DRAM. However, nothing prevents you from defining -those too if you wish to. Here is an example for the device MMIO region: - -```rust -// Device MMIO -Descriptor { - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, -}, -``` - -Finally, the file contains the following function: - -```rust -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> -``` - -It will be used by code in `mmu.rs` to request attributes for a virtual address -and the translation of the address. The function scans `KERNEL_VIRTUAL_LAYOUT` -for a descriptor that contains the queried address, and returns the respective -findings for the first entry that is a hit. If no entry is found, it returns -default attributes for normal chacheable DRAM and the input address, hence -telling the `MMU` code that the requested address should be `identity mapped`. - -Due to this default return, it is not needed to define normal cacheable DRAM -regions in `KERNEL_VIRTUAL_LAYOUT`. - -### mmu.rs - -This file contains the `AArch64` specific code. It is a driver, if you like, and -the split in paging granule mentioned before is hardcoded here (`4 KiB` page -descriptors for the first `2 MiB` and `2 MiB` block descriptors for everything -else). - -Two static page table arrays are instantiated, `LVL2_TABLE` and `LVL3_TABLE`, -and they are populated using `get_virt_addr_properties()` and a bunch of utility -functions that convert our own descriptors to the actual `64 bit` descriptor -entries needed by the MMU hardware for the page table arrays. - -Each page table has an entry (`AttrIndex`) that indexes into the -[MAIR_EL1](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500d/CIHDHJBB.html) -register, which holds information about the cacheability of the respective -page. We currently define normal cacheable memory and device memory (which is -not cached). - -```rust -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // Define the memory types that we will map. Cacheable normal DRAM and - // device. - MAIR_EL1.write( - // 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, - ); -} -``` - -Afterwards, the [Translation Table Base Register 0 - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/ttbr0_el1.rs) is set up with the base address of the `LVL3_TABLE` and -the [Translation Control Register - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/tcr_el1.rs) is -configured. - -Finally, the MMU is turned on through the [System Control Register - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/sctlr_el1.rs). The last step also enables caching for data and instructions. - -## Address translation examples - -For educational purposes, in `memory.rs`, a layout is defined which allows to -access the `UART` via two different virtual addresses: -- Since we identity map the whole `Device MMIO` region, it is accessible by -asserting its physical base address (`0x3F20_1000`) after the `MMU` is turned -on. -- Additionally, it is also mapped into the last `4 KiB` entry of the `LVL3` -table, making it accessible through base address `0x001F_F000`. - -The following two block diagrams visualize the underlying translations for the -two mappings, accessing the UART's Control Register (`CR`, offset `0x30`). - -### Adress translation using a 2 MiB block descriptor - -![2 MiB translation block diagram](../doc/page_tables_2MiB.png) - -### Adress translation using a 4 KiB page descriptor - -![4 KiB translation block diagram](../doc/page_tables_4KiB.png) - - -## Zero-cost abstraction - -The MMU init code is also a good example to see the great potential of Rust's -zero-cost abstractions[[1]](https://blog.rust-lang.org/2015/05/11/traits.html)[[2]](https://ruudvanasseldonk.com/2016/11/30/zero-cost-abstractions) for embedded programming. - -Take this piece of code for setting up the `MAIR_EL1` register using the -[cortex-a](https://crates.io/crates/cortex-a) crate: - - - -```rust -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // Define the memory types that we will map. Cacheable normal DRAM and - // device. - MAIR_EL1.write( - // 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, - ); -} -``` - -This piece of code is super expressive, and it makes use of `traits`, different -`types` and `constants` to provide type-safe register manipulation. - -In the end, this code sets the first four bytes of the register to certain -values according to the data sheet. Looking at the generated code, we can see -that despite all the type-safety and abstractions, we get super lean code: - -```text -00000000000803ac kernel8::memory::mmu::init::h7ef502c5548c1a62: - ... - 803cc: 88 e0 9f 52 mov w8, #0xff04 - ... - 803d8: 08 a2 18 d5 msr MAIR_EL1, x8 -``` - -## Output - -```console -ferris@box:~$ make raspboot - -[0] UART is live! -[1] Press a key to continue booting... Greetings fellow Rustacean! -[i] MMU: 4 KiB granule supported! -[i] MMU: Up to 40 Bit physical address range supported! -[2] MMU online. - -Writing through the virtual mapping at base address 0x00000000001FF000. -``` diff --git a/0D_virtual_memory/kernel8 b/0D_virtual_memory/kernel8 deleted file mode 100755 index e37ffda1..00000000 Binary files a/0D_virtual_memory/kernel8 and /dev/null differ diff --git a/0D_virtual_memory/kernel8.img b/0D_virtual_memory/kernel8.img deleted file mode 100755 index 0497dbef..00000000 Binary files a/0D_virtual_memory/kernel8.img and /dev/null differ diff --git a/0D_virtual_memory/link.ld b/0D_virtual_memory/link.ld deleted file mode 100644 index cb6f21a6..00000000 --- a/0D_virtual_memory/link.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -ENTRY(_boot_cores); - -SECTIONS -{ - . = 0x80000; /* This is already 4KiB aligned */ - __ro_start = .; - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - . = ALIGN(4096); /* Fill up to 4KiB */ - __ro_end = .; - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0D_virtual_memory/raspi3_boot/Cargo.toml b/0D_virtual_memory/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0D_virtual_memory/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0D_virtual_memory/raspi3_boot/src/lib.rs b/0D_virtual_memory/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/0D_virtual_memory/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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/0D_virtual_memory/src/delays.rs b/0D_virtual_memory/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/0D_virtual_memory/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/0D_virtual_memory/src/main.rs b/0D_virtual_memory/src/main.rs deleted file mode 100644 index 266eba10..00000000 --- a/0D_virtual_memory/src/main.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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] - -mod delays; -mod gpio; -mod mbox; -mod memory; -mod uart; - -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(memory::map::physical::GPIO_BASE); - let mut mbox = mbox::Mbox::new(memory::map::physical::VIDEOCORE_MBOX_BASE); - - { - // Before the MMU is live, instantiate a UART driver with the physical address - let uart = uart::Uart::new(memory::map::physical::UART_BASE); - - // 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"); - - memory::mmu::print_features(&uart); - - match unsafe { memory::mmu::init() } { - Err(s) => { - uart.puts("[2][Error] MMU: "); - uart.puts(s); - uart.puts("\n"); - } - // The following write is already using the identity mapped - // translation in the LVL2 table. - Ok(()) => uart.puts("[2] MMU online.\n"), - } - } // After this closure, the UART instance is not valid anymore. - - // Instantiate a new UART using the remapped address. No need to init() - // again, though. - let uart = uart::Uart::new(memory::map::virt::REMAPPED_UART_BASE); - - uart.puts("\nWriting through the virtual mapping at base address 0x"); - uart.hex(memory::map::virt::REMAPPED_UART_BASE as u64); - uart.puts(".\n"); - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0D_virtual_memory/src/mbox.rs b/0D_virtual_memory/src/mbox.rs deleted file mode 100644 index 2e4bf0ad..00000000 --- a/0D_virtual_memory/src/mbox.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 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], - base_addr: usize, -} - -/// 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(base_addr: usize) -> Mbox { - Mbox { - buffer: [0; 36], - base_addr, - } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } - - /// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success - pub fn call(&self, channel: u32) -> Result<()> { - // wait until we can write to the mailbox - loop { - if !self.STATUS.is_set(STATUS::FULL) { - break; - } - - asm::nop(); - } - - let buf_ptr = self.buffer.as_ptr() as u32; - - // write the address of our message to the mailbox with channel identifier - self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF)); - - // now wait for the response - loop { - // is there a response? - loop { - if !self.STATUS.is_set(STATUS::EMPTY) { - break; - } - - asm::nop(); - } - - let resp: u32 = self.READ.get(); - - // is it a response to our message? - if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) { - // is it a valid successful response? - return match self.buffer[1] { - response::SUCCESS => Ok(()), - response::ERROR => Err(MboxError::ResponseError), - _ => Err(MboxError::UnknownError), - }; - } - } - } -} diff --git a/0D_virtual_memory/src/memory.rs b/0D_virtual_memory/src/memory.rs deleted file mode 100644 index c2135539..00000000 --- a/0D_virtual_memory/src/memory.rs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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::ops::RangeInclusive; - -pub mod mmu; - -/// System memory map. -#[rustfmt::skip] -pub mod map { - pub const START: usize = 0x0000_0000; - pub const END: usize = 0x3FFF_FFFF; - - pub mod physical { - pub const MMIO_BASE: usize = 0x3F00_0000; - pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880; - pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; - pub const UART_BASE: usize = MMIO_BASE + 0x0020_1000; - pub const MMIO_END: usize = super::END; - } - - pub mod virt { - pub const KERN_STACK_START: usize = super::START; - pub const KERN_STACK_END: usize = 0x0007_FFFF; - - // The last 4 KiB slot in the first 2 MiB - pub const REMAPPED_UART_BASE: usize = 0x001F_F000; - pub const REMAPPED_UART_END: usize = 0x001F_FFFF; - } -} - -/// Types used for compiling the virtual memory layout of the kernel using -/// address ranges. -pub mod kernel_mem_range { - use core::ops::RangeInclusive; - - #[derive(Copy, Clone)] - pub enum MemAttributes { - CacheableDRAM, - Device, - } - - #[derive(Copy, Clone)] - pub enum AccessPermissions { - ReadOnly, - ReadWrite, - } - - #[derive(Copy, Clone)] - pub enum Translation { - Identity, - Offset(usize), - } - - #[derive(Copy, Clone)] - pub struct AttributeFields { - pub mem_attributes: MemAttributes, - pub acc_perms: AccessPermissions, - pub execute_never: bool, - } - - impl Default for AttributeFields { - fn default() -> AttributeFields { - AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - } - } - } - - pub struct Descriptor { - pub virtual_range: fn() -> RangeInclusive, - pub translation: Translation, - pub attribute_fields: AttributeFields, - } -} - -use kernel_mem_range::*; - -/// A virtual memory layout that is agnostic of the paging granularity that the -/// hardware MMU will use. -/// -/// Contains only special ranges, aka anything that is _not_ normal cacheable -/// DRAM. -static KERNEL_VIRTUAL_LAYOUT: [Descriptor; 5] = [ - // Kernel stack - Descriptor { - virtual_range: || { - RangeInclusive::new(map::virt::KERN_STACK_START, map::virt::KERN_STACK_END) - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Kernel code and RO data - Descriptor { - virtual_range: || { - // Using the linker script, we ensure that the RO area is consecutive and 4 - // KiB aligned, and we export the boundaries via symbols: - // - // [__ro_start, __ro_end) - 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 exclusive 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 turn the exclusive end into an - // inclusive end - RangeInclusive::new( - &__ro_start as *const _ as usize, - &__ro_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadOnly, - execute_never: false, - }, - }, - // Kernel data and BSS - Descriptor { - virtual_range: || { - extern "C" { - static __ro_end: u64; - static __bss_end: u64; - } - - unsafe { - RangeInclusive::new( - &__ro_end as *const _ as usize, - &__bss_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Remapped UART - Descriptor { - virtual_range: || { - RangeInclusive::new(map::virt::REMAPPED_UART_BASE, map::virt::REMAPPED_UART_END) - }, - translation: Translation::Offset(map::physical::UART_BASE), - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Device MMIO - Descriptor { - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, -]; - -/// For a given virtual address, find and return the output address and -/// according attributes. -/// -/// If the address is not covered in VIRTUAL_LAYOUT, return a default for normal -/// cacheable DRAM. -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> { - if virt_addr > map::END { - return Err("Address out of range."); - } - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - if (i.virtual_range)().contains(&virt_addr) { - let output_addr = match i.translation { - Translation::Identity => virt_addr, - Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), - }; - - return Ok((output_addr, i.attribute_fields)); - } - } - - Ok((virt_addr, AttributeFields::default())) -} diff --git a/0D_virtual_memory/src/memory/mmu.rs b/0D_virtual_memory/src/memory/mmu.rs deleted file mode 100644 index 9dae90f3..00000000 --- a/0D_virtual_memory/src/memory/mmu.rs +++ /dev/null @@ -1,359 +0,0 @@ -/* - * 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::memory::{get_virt_addr_properties, AttributeFields}; -use crate::uart; -use cortex_a::{barrier, regs::*}; -use register::register_bitfields; - -/// Parse the ID_AA64MMFR0_EL1 register for runtime information about supported -/// MMU features. -pub fn print_features(uart: &uart::Uart) { - let mmfr = ID_AA64MMFR0_EL1.extract(); - - if let Some(ID_AA64MMFR0_EL1::TGran4::Value::Supported) = - mmfr.read_as_enum(ID_AA64MMFR0_EL1::TGran4) - { - uart.puts("[i] MMU: 4 KiB granule supported!\n"); - } - - if let Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) = - mmfr.read_as_enum(ID_AA64MMFR0_EL1::PARange) - { - uart.puts("[i] MMU: Up to 40 Bit physical address range supported!\n"); - } -} - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Privileged execute-never - PXN OFFSET(53) 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 - ] - ] -} - -const FOUR_KIB: usize = 4 * 1024; -const FOUR_KIB_SHIFT: usize = 12; // log2(4 * 1024) - -const TWO_MIB: usize = 2 * 1024 * 1024; -const TWO_MIB_SHIFT: usize = 21; // log2(2 * 1024 * 1024) - -/// A descriptor pointing to the next page table. -struct TableDescriptor(register::FieldValue); - -impl TableDescriptor { - fn new(next_lvl_table_addr: usize) -> Result { - if next_lvl_table_addr % FOUR_KIB != 0 { - return Err("TableDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = next_lvl_table_addr >> FOUR_KIB_SHIFT; - - Ok(TableDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A function that maps the generic memory range attributes to HW-specific -/// attributes of the MMU. -fn into_mmu_attributes( - attribute_fields: AttributeFields, -) -> register::FieldValue { - use crate::memory::{AccessPermissions, MemAttributes}; - - // Memory attributes - let mut desc = match attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - } - MemAttributes::Device => { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } - }; - - // Access Permissions - desc += match attribute_fields.acc_perms { - AccessPermissions::ReadOnly => STAGE1_DESCRIPTOR::AP::RO_EL1, - AccessPermissions::ReadWrite => STAGE1_DESCRIPTOR::AP::RW_EL1, - }; - - // Execute Never - desc += if attribute_fields.execute_never { - STAGE1_DESCRIPTOR::PXN::True - } else { - STAGE1_DESCRIPTOR::PXN::False - }; - - desc -} - -/// A Level2 block descriptor with 2 MiB aperture. -/// -/// The output points to physical memory. -struct Lvl2BlockDescriptor(register::FieldValue); - -impl Lvl2BlockDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % TWO_MIB != 0 { - return Err("BlockDescriptor: Address is not 2 MiB aligned."); - } - - let shifted = output_addr >> TWO_MIB_SHIFT; - - Ok(Lvl2BlockDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A page descriptor with 4 KiB aperture. -/// -/// The output points to physical memory. -struct PageDescriptor(register::FieldValue); - -impl PageDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % FOUR_KIB != 0 { - return Err("PageDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = output_addr >> FOUR_KIB_SHIFT; - - Ok(PageDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// Constants for indexing the MAIR_EL1. -#[allow(dead_code)] -mod mair { - pub const DEVICE: u64 = 0; - pub const NORMAL: u64 = 1; -} - -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // Define the memory types that we will map. Cacheable normal DRAM and - // device. - MAIR_EL1.write( - // 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, - ); -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for [u64; 512] { - fn base_addr_u64(&self) -> u64 { - self as *const u64 as u64 - } - - fn base_addr_usize(&self) -> usize { - self as *const u64 as usize - } -} - -const NUM_ENTRIES_4KIB: usize = 512; - -// A wrapper struct is needed here so that the align attribute can be used. -#[repr(C)] -#[repr(align(4096))] -struct PageTable { - entries: [u64; NUM_ENTRIES_4KIB], -} - -/// The LVL2 page table containng the 2 MiB entries. -static mut LVL2_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// The LVL3 page table containing the 4 KiB entries. -/// -/// The first entry of the LVL2_TABLE will forward to this table. -static mut LVL3_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// Set up identity mapped page tables for the first 1 GiB of address space. -/// -/// The first 2 MiB are 4 KiB granule, the rest 2 MiB. -/// -/// # Safety -/// -/// - User must ensure that the hardware supports the paremeters being set here. -pub unsafe fn init() -> Result<(), &'static str> { - // Prepare the memory attribute indirection register. - set_up_mair(); - - // Point the first 2 MiB of virtual addresses to the follow-up LVL3 - // page-table. - LVL2_TABLE.entries[0] = match TableDescriptor::new(LVL3_TABLE.entries.base_addr_usize()) { - Err(s) => return Err(s), - Ok(d) => d.value(), - }; - - // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. - // - // Notice the skip(1) which makes the iteration start at the second 2 MiB - // block (0x20_0000). - for (block_descriptor_nr, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { - let virt_addr = block_descriptor_nr << TWO_MIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let block_desc = match Lvl2BlockDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = block_desc.value(); - } - - // Finally, fill the single LVL3 table (4 KiB granule). - for (page_descriptor_nr, entry) in LVL3_TABLE.entries.iter_mut().enumerate() { - let virt_addr = page_descriptor_nr << FOUR_KIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let page_desc = match PageDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = page_desc.value(); - } - - // Point to the LVL2 table base address in TTBR0. - TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr_u64()); - - // 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); - - Ok(()) -} diff --git a/0D_virtual_memory/src/uart.rs b/0D_virtual_memory/src/uart.rs deleted file mode 100644 index 9505be3a..00000000 --- a/0D_virtual_memory/src/uart.rs +++ /dev/null @@ -1,281 +0,0 @@ -/* - * 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::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) [] - ] -} - -#[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 { - base_addr: usize, -} - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl Uart { - pub fn new(base_addr: usize) -> Uart { - Uart { 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, 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_cache_performance/.cargo/config b/0E_cache_performance/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0E_cache_performance/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0E_cache_performance/Cargo.lock b/0E_cache_performance/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0E_cache_performance/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0E_cache_performance/Cargo.toml b/0E_cache_performance/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0E_cache_performance/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0E_cache_performance/Makefile b/0E_cache_performance/Makefile deleted file mode 100644 index f5c8310d..00000000 --- a/0E_cache_performance/Makefile +++ /dev/null @@ -1,103 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) \ - $(DOCKER_EXEC_RASPBOOT_DEV) kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/0E_cache_performance/README.md b/0E_cache_performance/README.md deleted file mode 100644 index 6db274a6..00000000 --- a/0E_cache_performance/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Tutorial 0E - Cache Performance - -Now that we finally have virtual memory capabilities available, we also have -fine grained control over `cacheability`. You've caught a glimpse already in the -last tutorial, where we used page table entries to reference the `MAIR_EL1` -register to indicate the cacheability of a page or block. - -Unfortunately, for the user it is often hard to grasp the advantage of caching -in early stages of OS or bare-metal software development. This tutorial is a -short interlude that tries to give you a feeling of what caching can do for -performance. - -## Benchmark - -Let's write a tiny, arbitrary micro-benchmark to showcase the performance of -operating with data on the same DRAM with caching enabled and disabled. - -### mmu.rs - -Therefore, we will map the same physical memory via two different virtual -addresses. We set up our pagetables such that the virtual address `0x400000` -points to the physical DRAM at `0x200000`, and we configure it as -`non-cacheable` in the page tables. - -There is also an identity mapped block, which starts at virtual `0x200000` and -points at physical `0x200000`. This time, the block is configured as cacheable. - -### benchmark.rs - -We write a little function that iteratively reads memory of five times the size -of a `cacheline`, in steps of 8 bytes, aka one processor register at a time. We -read the value, add 1, and write it back. This whole process is repeated -`20_000` times. - -The benchmark function is called twice. Once for the cacheable and once for the -non-cacheable virtual addresses. Remember that both virtual addresses point to -the _same_ physical DRAM, so the difference in time that we will see will -showcase how much faster it is to operate on DRAM with caching enabled. - -## Output - -On my Raspberry, I get the following results: - -```console -ferris@box:~$ make raspboot - -[0] UART is live! -[1] Press a key to continue booting... Greetings fellow Rustacean! -[2] MMU online. -Benchmarking non-cacheable DRAM modifications at virtual 0x0000000000400000, physical 0x0000000000200000: -1040 miliseconds. - -Benchmarking cacheable DRAM modifications at virtual 0x0000000000200000, physical 0x0000000000200000: -53 miliseconds. - -With caching, the function is 1800% faster! -``` - -Impressive, isn't it? diff --git a/0E_cache_performance/kernel8 b/0E_cache_performance/kernel8 deleted file mode 100755 index 8044ad1e..00000000 Binary files a/0E_cache_performance/kernel8 and /dev/null differ diff --git a/0E_cache_performance/kernel8.img b/0E_cache_performance/kernel8.img deleted file mode 100755 index 9724227e..00000000 Binary files a/0E_cache_performance/kernel8.img and /dev/null differ diff --git a/0E_cache_performance/link.ld b/0E_cache_performance/link.ld deleted file mode 100644 index cb6f21a6..00000000 --- a/0E_cache_performance/link.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -ENTRY(_boot_cores); - -SECTIONS -{ - . = 0x80000; /* This is already 4KiB aligned */ - __ro_start = .; - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - . = ALIGN(4096); /* Fill up to 4KiB */ - __ro_end = .; - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0E_cache_performance/raspi3_boot/Cargo.toml b/0E_cache_performance/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0E_cache_performance/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0E_cache_performance/raspi3_boot/src/lib.rs b/0E_cache_performance/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/0E_cache_performance/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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_cache_performance/src/benchmark.rs b/0E_cache_performance/src/benchmark.rs deleted file mode 100644 index 346399f5..00000000 --- a/0E_cache_performance/src/benchmark.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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::uart; -use core::sync::atomic::{compiler_fence, Ordering}; -use cortex_a::{barrier, regs::*}; - -/// We assume that addr is cacheline aligned -fn batch_modify_time(addr: usize) -> Option { - const CACHELINE_SIZE_BYTES: usize = 64; // TODO: retrieve this from a system register - const NUM_CACHELINES_TOUCHED: usize = 5; - const NUM_BENCH_ITERATIONS: usize = 20_000; - - const NUM_BYTES_TOUCHED: usize = CACHELINE_SIZE_BYTES * NUM_CACHELINES_TOUCHED; - - let mem = unsafe { core::slice::from_raw_parts_mut(addr as *mut usize, NUM_BYTES_TOUCHED) }; - - // Benchmark starts here - let t1 = CNTPCT_EL0.get(); - - compiler_fence(Ordering::SeqCst); - - let mut temp: usize; - for _ in 0..NUM_BENCH_ITERATIONS { - for qword in mem.iter_mut() { - unsafe { - temp = core::ptr::read_volatile(qword); - core::ptr::write_volatile(qword, temp + 1); - } - } - } - - // Insert a barrier to ensure that the last memory operation has finished - // before we retrieve the elapsed time with the subsequent counter read. Not - // needed at all given the sample size, but let's be a bit pedantic here for - // education purposes. For measuring single-instructions, this would be - // needed. - unsafe { barrier::dsb(barrier::SY) }; - - let t2 = CNTPCT_EL0.get(); - let frq = u64::from(CNTFRQ_EL0.get()); - - ((t2 - t1) * 1000).checked_div(frq) -} - -pub fn run(uart: &uart::Uart) { - use crate::memory::map; - - const ERROR_STRING: &str = "Something went wrong!"; - - uart.puts("Benchmarking non-cacheable DRAM modifications at virtual 0x"); - uart.hex(map::virt::NON_CACHEABLE_START as u64); - uart.puts(", physical 0x"); - uart.hex(map::virt::CACHEABLE_START as u64); - uart.puts(":\n"); - - let result_nc = match batch_modify_time(map::virt::NON_CACHEABLE_START) { - Some(t) => { - uart.dec(t as u32); - uart.puts(" miliseconds.\n\n"); - t - } - None => { - uart.puts(ERROR_STRING); - return; - } - }; - - uart.puts("Benchmarking cacheable DRAM modifications at virtual 0x"); - uart.hex(map::virt::CACHEABLE_START as u64); - uart.puts(", physical 0x"); - uart.hex(map::virt::CACHEABLE_START as u64); - uart.puts(":\n"); - - let result_c = match batch_modify_time(map::virt::CACHEABLE_START) { - Some(t) => { - uart.dec(t as u32); - uart.puts(" miliseconds.\n\n"); - t - } - None => { - uart.puts(ERROR_STRING); - return; - } - }; - - if let Some(t) = (result_nc - result_c).checked_div(result_c) { - uart.puts("With caching, the function is "); - uart.dec((t * 100) as u32); - uart.puts("% faster!\n"); - } -} diff --git a/0E_cache_performance/src/delays.rs b/0E_cache_performance/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/0E_cache_performance/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2019 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -use cortex_a::asm; - -/* - * - * Using the CPU's cycles - * - */ -/// Wait N CPU cycles (ARM CPU only) -pub fn wait_cycles(cyc: u32) { - for _ in 0..cyc { - asm::nop(); - } -} diff --git a/0E_cache_performance/src/main.rs b/0E_cache_performance/src/main.rs deleted file mode 100644 index 9d2e8c64..00000000 --- a/0E_cache_performance/src/main.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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] - -mod benchmark; -mod delays; -mod gpio; -mod mbox; -mod memory; -mod uart; - -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(memory::map::physical::GPIO_BASE); - let mut mbox = mbox::Mbox::new(memory::map::physical::VIDEOCORE_MBOX_BASE); - let uart = uart::Uart::new(memory::map::physical::UART_BASE); - - // 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"); - - match unsafe { memory::mmu::init() } { - Err(s) => { - uart.puts("[2][Error] MMU: "); - uart.puts(s); - uart.puts("\n"); - } - Ok(()) => uart.puts("[2] MMU online.\n"), - } - - benchmark::run(&uart); - - // echo everything back - loop { - uart.send(uart.getc()); - } -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0E_cache_performance/src/mbox.rs b/0E_cache_performance/src/mbox.rs deleted file mode 100644 index 2e4bf0ad..00000000 --- a/0E_cache_performance/src/mbox.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 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], - base_addr: usize, -} - -/// 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(base_addr: usize) -> Mbox { - Mbox { - buffer: [0; 36], - base_addr, - } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } - - /// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success - pub fn call(&self, channel: u32) -> Result<()> { - // wait until we can write to the mailbox - loop { - if !self.STATUS.is_set(STATUS::FULL) { - break; - } - - asm::nop(); - } - - let buf_ptr = self.buffer.as_ptr() as u32; - - // write the address of our message to the mailbox with channel identifier - self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF)); - - // now wait for the response - loop { - // is there a response? - loop { - if !self.STATUS.is_set(STATUS::EMPTY) { - break; - } - - asm::nop(); - } - - let resp: u32 = self.READ.get(); - - // is it a response to our message? - if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) { - // is it a valid successful response? - return match self.buffer[1] { - response::SUCCESS => Ok(()), - response::ERROR => Err(MboxError::ResponseError), - _ => Err(MboxError::UnknownError), - }; - } - } - } -} diff --git a/0E_cache_performance/src/memory.rs b/0E_cache_performance/src/memory.rs deleted file mode 100644 index 72db92dd..00000000 --- a/0E_cache_performance/src/memory.rs +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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::ops::RangeInclusive; - -pub mod mmu; - -/// System memory map. -#[rustfmt::skip] -pub mod map { - pub const START: usize = 0x0000_0000; - pub const END: usize = 0x3FFF_FFFF; - - pub mod physical { - pub const MMIO_BASE: usize = 0x3F00_0000; - pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880; - pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; - pub const UART_BASE: usize = MMIO_BASE + 0x0020_1000; - pub const MMIO_END: usize = super::END; - } - - pub mod virt { - pub const KERN_STACK_START: usize = super::START; - pub const KERN_STACK_END: usize = 0x0007_FFFF; - - // The second 2 MiB block - pub const CACHEABLE_START: usize = 0x0020_0000; - - // The third 2 MiB block - pub const NON_CACHEABLE_START: usize = 0x0040_0000; - pub const NON_CACHEABLE_END: usize = 0x005F_FFFF; - } -} - -/// Types used for compiling the virtual memory layout of the kernel using -/// address ranges. -pub mod kernel_mem_range { - use core::ops::RangeInclusive; - - #[derive(Copy, Clone)] - pub enum MemAttributes { - CacheableDRAM, - NonCacheableDRAM, - Device, - } - - #[derive(Copy, Clone)] - pub enum AccessPermissions { - ReadOnly, - ReadWrite, - } - - #[derive(Copy, Clone)] - pub enum Translation { - Identity, - Offset(usize), - } - - #[derive(Copy, Clone)] - pub struct AttributeFields { - pub mem_attributes: MemAttributes, - pub acc_perms: AccessPermissions, - pub execute_never: bool, - } - - impl Default for AttributeFields { - fn default() -> AttributeFields { - AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - } - } - } - - pub struct Descriptor { - pub virtual_range: fn() -> RangeInclusive, - pub translation: Translation, - pub attribute_fields: AttributeFields, - } -} - -use kernel_mem_range::*; - -/// A virtual memory layout that is agnostic of the paging granularity that the -/// hardware MMU will use. -/// -/// Contains only special ranges, aka anything that is _not_ normal cacheable -/// DRAM. -static KERNEL_VIRTUAL_LAYOUT: [Descriptor; 5] = [ - // Kernel stack - Descriptor { - virtual_range: || { - RangeInclusive::new(map::virt::KERN_STACK_START, map::virt::KERN_STACK_END) - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Kernel code and RO data - Descriptor { - virtual_range: || { - // Using the linker script, we ensure that the RO area is consecutive and 4 - // KiB aligned, and we export the boundaries via symbols: - // - // [__ro_start, __ro_end) - 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 exclusive 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 turn the exclusive end into an - // inclusive end - RangeInclusive::new( - &__ro_start as *const _ as usize, - &__ro_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadOnly, - execute_never: false, - }, - }, - // Kernel data and BSS - Descriptor { - virtual_range: || { - extern "C" { - static __ro_end: u64; - static __bss_end: u64; - } - - unsafe { - RangeInclusive::new( - &__ro_end as *const _ as usize, - &__bss_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Non-cacheable DRAM - Descriptor { - virtual_range: || { - RangeInclusive::new(map::virt::NON_CACHEABLE_START, map::virt::NON_CACHEABLE_END) - }, - translation: Translation::Offset(map::virt::CACHEABLE_START), - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::NonCacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - // Device MMIO - Descriptor { - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, -]; - -/// For a given virtual address, find and return the output address and -/// according attributes. -/// -/// If the address is not covered in VIRTUAL_LAYOUT, return a default for normal -/// cacheable DRAM. -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> { - if virt_addr > map::END { - return Err("Address out of range."); - } - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - if (i.virtual_range)().contains(&virt_addr) { - let output_addr = match i.translation { - Translation::Identity => virt_addr, - Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), - }; - - return Ok((output_addr, i.attribute_fields)); - } - } - - Ok((virt_addr, AttributeFields::default())) -} diff --git a/0E_cache_performance/src/memory/mmu.rs b/0E_cache_performance/src/memory/mmu.rs deleted file mode 100644 index aa4e43c6..00000000 --- a/0E_cache_performance/src/memory/mmu.rs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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::memory::{get_virt_addr_properties, AttributeFields}; -use cortex_a::{barrier, regs::*}; -use register::register_bitfields; - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Privileged execute-never - PXN OFFSET(53) 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 - ] - ] -} - -const FOUR_KIB: usize = 4 * 1024; -const FOUR_KIB_SHIFT: usize = 12; // log2(4 * 1024) - -const TWO_MIB: usize = 2 * 1024 * 1024; -const TWO_MIB_SHIFT: usize = 21; // log2(2 * 1024 * 1024) - -/// A descriptor pointing to the next page table. -struct TableDescriptor(register::FieldValue); - -impl TableDescriptor { - fn new(next_lvl_table_addr: usize) -> Result { - if next_lvl_table_addr % FOUR_KIB != 0 { - return Err("TableDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = next_lvl_table_addr >> FOUR_KIB_SHIFT; - - Ok(TableDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A function that maps the generic memory range attributes to HW-specific -/// attributes of the MMU. -fn into_mmu_attributes( - attribute_fields: AttributeFields, -) -> register::FieldValue { - use crate::memory::{AccessPermissions, MemAttributes}; - - // Memory attributes - let mut desc = match attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - } - MemAttributes::NonCacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable - + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) - } - MemAttributes::Device => { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } - }; - - // Access Permissions - desc += match attribute_fields.acc_perms { - AccessPermissions::ReadOnly => STAGE1_DESCRIPTOR::AP::RO_EL1, - AccessPermissions::ReadWrite => STAGE1_DESCRIPTOR::AP::RW_EL1, - }; - - // Execute Never - desc += if attribute_fields.execute_never { - STAGE1_DESCRIPTOR::PXN::True - } else { - STAGE1_DESCRIPTOR::PXN::False - }; - - desc -} - -/// A Level2 block descriptor with 2 MiB aperture. -/// -/// The output points to physical memory. -struct Lvl2BlockDescriptor(register::FieldValue); - -impl Lvl2BlockDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % TWO_MIB != 0 { - return Err("BlockDescriptor: Address is not 2 MiB aligned."); - } - - let shifted = output_addr >> TWO_MIB_SHIFT; - - Ok(Lvl2BlockDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A page descriptor with 4 KiB aperture. -/// -/// The output points to physical memory. -struct PageDescriptor(register::FieldValue); - -impl PageDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % FOUR_KIB != 0 { - return Err("PageDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = output_addr >> FOUR_KIB_SHIFT; - - Ok(PageDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// Constants for indexing the MAIR_EL1. -#[allow(dead_code)] -mod mair { - pub const DEVICE: u64 = 0; - pub const NORMAL: u64 = 1; - pub const NORMAL_NON_CACHEABLE: u64 = 2; -} - -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // 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, - ); -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for [u64; 512] { - fn base_addr_u64(&self) -> u64 { - self as *const u64 as u64 - } - - fn base_addr_usize(&self) -> usize { - self as *const u64 as usize - } -} - -const NUM_ENTRIES_4KIB: usize = 512; - -// A wrapper struct is needed here so that the align attribute can be used. -#[repr(C)] -#[repr(align(4096))] -struct PageTable { - entries: [u64; NUM_ENTRIES_4KIB], -} - -/// The LVL2 page table containng the 2 MiB entries. -static mut LVL2_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// The LVL3 page table containing the 4 KiB entries. -/// -/// The first entry of the LVL2_TABLE will forward to this table. -static mut LVL3_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// Set up identity mapped page tables for the first 1 GiB of address space. -/// -/// The first 2 MiB are 4 KiB granule, the rest 2 MiB. -/// -/// # Safety -/// -/// - User must ensure that the hardware supports the paremeters being set here. -pub unsafe fn init() -> Result<(), &'static str> { - // Prepare the memory attribute indirection register. - set_up_mair(); - - // Point the first 2 MiB of virtual addresses to the follow-up LVL3 - // page-table. - LVL2_TABLE.entries[0] = match TableDescriptor::new(LVL3_TABLE.entries.base_addr_usize()) { - Err(s) => return Err(s), - Ok(d) => d.value(), - }; - - // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. - // - // Notice the skip(1) which makes the iteration start at the second 2 MiB - // block (0x20_0000). - for (block_descriptor_nr, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { - let virt_addr = block_descriptor_nr << TWO_MIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let block_desc = match Lvl2BlockDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = block_desc.value(); - } - - // Finally, fill the single LVL3 table (4 KiB granule). - for (page_descriptor_nr, entry) in LVL3_TABLE.entries.iter_mut().enumerate() { - let virt_addr = page_descriptor_nr << FOUR_KIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let page_desc = match PageDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = page_desc.value(); - } - - // Point to the LVL2 table base address in TTBR0. - TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr_u64()); - - // 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); - - Ok(()) -} diff --git a/0E_cache_performance/src/uart.rs b/0E_cache_performance/src/uart.rs deleted file mode 100644 index 093ef6a7..00000000 --- a/0E_cache_performance/src/uart.rs +++ /dev/null @@ -1,301 +0,0 @@ -/* - * 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::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) [] - ] -} - -#[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 { - base_addr: usize, -} - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl Uart { - pub fn new(base_addr: usize) -> Uart { - Uart { 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, 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); - } - } - - /// Display a binary value in decimal - pub fn dec(&self, d: u32) { - let mut digits: [char; 10] = ['\0'; 10]; - let mut d = d; - - for i in digits.iter_mut() { - *i = ((d % 10) + 0x30) as u8 as char; - - d /= 10; - - if d == 0 { - break; - } - } - - for c in digits.iter().rev() { - self.send(*c); - } - } -} diff --git a/0F_globals_synchronization_println/.cargo/config b/0F_globals_synchronization_println/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/0F_globals_synchronization_println/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/0F_globals_synchronization_println/Cargo.lock b/0F_globals_synchronization_println/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/0F_globals_synchronization_println/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/0F_globals_synchronization_println/Cargo.toml b/0F_globals_synchronization_println/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/0F_globals_synchronization_println/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/0F_globals_synchronization_println/Makefile b/0F_globals_synchronization_println/Makefile deleted file mode 100644 index 4e02fa9d..00000000 --- a/0F_globals_synchronization_println/Makefile +++ /dev/null @@ -1,103 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host - -DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_QEMU) -serial stdio - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/0F_globals_synchronization_println/README.md b/0F_globals_synchronization_println/README.md deleted file mode 100644 index bd0f34c8..00000000 --- a/0F_globals_synchronization_println/README.md +++ /dev/null @@ -1,455 +0,0 @@ -# Tutorial 0F - Globals, Synchronization and `println!` - -Until now, we use a rather inelegant way of printing messages: We are directly -calling the `UART` device driver's functions for putting and receiving -characters on the serial line, e.g. `uart.puts()`. Also, we have only very -bare-bones implementations for printing hex or decimal integers. This both looks -ugly in the code, and is not very flexible. For example, if at some point we -decide to replace the `UART` as the output device, we have to manually find and -replace all the respective calls, and need to take care that we do not use the -device before it was probed or after it was shut down. - -Hence, it is time to get some elegant format-string-based printing going, like -we know it from other languages, e.g. `C`'s `printf()`, and introduce an -abstraction layer that allows us to decouple printing functions from the actual -output device. - -On this occasion, we will also learn important lessons about about **mutable -global variables**, which are called **static variables** in Rust, get to know -**trait objects** and hear about Rust's concept of **interior mutability**. - -## The Virtual Console - -First, we introduce a `Console` type in `src/devics/virt/console.rs`: - -```rust -pub struct Console { - output: Output, -} -``` - -When everything is finished, this type will be used as a `virtual device` that -forwards calls to printing functions to the currently active output device. - -### Code Restructuring - -In case you wonder about the path: The introduction of the first `virtual -device` in our code was a good opportunity to introduce a better structure for -our modules. Basically, we differentiate between real (HW) and virtual devices -now: - -```console -src -├── devices -│   ├── hw -│   │   ├── gpio.rs -│   │   ├── uart.rs -│   │   └── videocore_mbox.rs -│   ├── hw.rs -│   ├── virt -│   │   └── console.rs -│   └── virt.rs -├── devices.rs -``` - -### Console Implementation - -The `Console` type has a single field of type `Output`: - -```rust -/// Possible outputs which the console can store. -pub enum Output { - None(NullConsole), - Uart(hw::Uart), -} -``` - -How will it be used? Let us have a look: - -```rust -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::Uart(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; - } -``` - -Basically two things can be done. - -1. `output` can be replaced during runtime. -2. Using `current_ptr()`, a reference to the current `output` is returned as a - [trait object](https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/dyn-trait-for-trait-objects.html) - that implements the `ConsoleOps` trait. Hence, for the first time in the - tutorials, Rust's [dynamic dispatch](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch) - is used. - -So what does the `ConsoleOps` trait define? - -```rust -pub trait ConsoleOps: Drop { - fn putc(&self, c: char) {} - fn puts(&self, string: &str) {} - fn getc(&self) -> char { - ' ' - } - fn flush(&self) {} -} -``` - -All in all, it is basically the same that is already present in the `UART` -driver: Reading and writing a single character, and writing a whole string. What -is new is the `flush` function, which is meant for devices that implement output -FIFOs. - -So any device that can be stored into `output` must implement this trait, -otherwise a compile-time error would occur. - -### Dispatching to the Current Output - -In order to use the `Console` as a HW-agnostic device for printing, some -dispatching code is needed. Therefore, it implements the `ConsoleOps` trait -itself, and forwards the trait calls during run-time to whatever is stored in -`output`. - -```rust -/// 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() - } -} -``` - -Congratulations :tada:. - -This is not much code, but enough so that you've implemented your first, very -basic kind of [Hardware Abstraction Layer (HAL)](https://en.wikipedia.org/wiki/Hardware_abstraction). - -## Making it Static (and Mutable) - -Now we need an instance of the virtual console in form of a _static variable_ -(remember, this is Rust speak for global) to make our life easier and our code -less bloated. Doing so enables calls to printing functions from every place in -the code, without dragging along references to the console everywhere. - -At times, we also want to replace the `output` field of our console variable, so we -need a `mutable` static. - -In system programming languages like `C` or `C++`, this would be quite easy. For -example, the declaration below is enough to allow mutation of `console`, since -the language does not have a built-in concept of mutable and immutable types: - -```C++ -Console console = Console::Console(); - -int kernel_entry() { - console.replace_with(...) -} -``` - -However, in Rust, if you do - -```rust -static mut CONSOLE: devices::virt::Console = - devices::virt::Console::new(); - -fn kernel_entry() -> ! { - CONSOLE.replace_with(...) // <-- Compiler: "Where's my unsafe{}?!!" -} -``` - -the compiler will shout angrily at you whenever you try to use `CONSOLE` that -this is unsafe code, and frankly, that is a good thing. - -In contrast to the C-family of languages, Rust is from the ground up designed -with multi-core and multi-threading in mind. Thanks to the **borrow-checker**, -Rust ensures that in safe code, there can ever only exist a single mutable -reference to a variable. - -This way, it is ensured at compile time that no situations are created where -code that might execute concurrently (that is, for example, code running at the -same time on different physical processor cores) fiddles with the same data -or resources in an unsychronized way. - -By instantiating a **mutable** static variable, we allow all code from every -source-code file to easily operate on this mutable reference. Since the variable -is not instantiated at runtime and explicitly passed on in function calls, it is -not possible for the borrow-checker to draw any conclusions about the number of -mutable references in use. As a result, access to mutable statics needs to be -marked with `unsafe{}` in any case in Rust. - -So how can we make this safe again? What we need in this case is a -**synchronization primitive**. You've probably heard of them -before. **Spinlocks** and **mutexes** are two examples. What they do is to -ensure _at runtime_ that there is no concurrent access to the data they protect. - -### How to Build a Synchronization Primitive in Rust - -In contrast to mutable statics, **immutable statics** are considered safe by -Rust as long as they are marked -[Sync](https://doc.rust-lang.org/std/marker/trait.Sync.html). It is perfectly -fine to share an infinite number of references to them. So here is the strategy: - -1. Build a wrapper type that can be instantiated as an **immutable static** and - that encapsulates the actual mutable data. -2. Provide a function that returns a mutable reference to the wrapped type. -3. This function will need to be marked `unsafe`. In order to consider it safe - nonetheless, it must feature code that ensures at runtime that only a - single reference is given out at times. - -This is the basic concept of all synchronization primitives in Rust. For -educational purposes, in the tutorials, we will roll our own, and not reuse -stuff from the core library or popular crates like [spin](https://crates.io/crates/spin). - -### The `NullLock` - -The first implementation will actually be very easy. We do not yet have to worry -that a situation arises where (i) code tries to take the lock while it is -already locked or (ii) where there is contention for the lock. This is because -the kernel is still in a state where everything is executed linearly from start -to finish: - -1. Asynchronous exceptions like Interrupts are not enabled yet, so there never is - any interruption in the program flow. -2. We know that we currently do not have any code yet that raises synchronous exceptions. -2. Only a single core is active, all others are parked. Therefore, no concurrent - execution of code is happening. - -> Hint: You will learn about asynchronous and synchronous exceptions in the -> tutorial after the next. - -So all that needs be done is wrapping the data and giving back the mutable -reference. Introducing the `NullLock` in `sync.rs`: - -```rust -use core::cell::UnsafeCell; - -pub struct NullLock { - data: UnsafeCell, -} - -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 one at a - // time. - f(unsafe { &mut *self.data.get() }) - } -} -``` - -First, the lock type is marked with the `Sync` [marker trait](https://doc.rust-lang.org/std/marker/trait.Sync.html) to tell the -compiler that it is safe to share references to it between threads. More -literature on this topic in [[1]](https://doc.rust-lang.org/beta/nomicon/send-and-sync.html)[[2]](https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html). - -Second, a `lock()` function is provided which returns mutable references to the -wrapped data in the -[UnsafeCell](https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html). Quoting -from the UnsafeCell documentation: - - -> The core primitive for interior mutability in Rust. -> -> UnsafeCell is a type that wraps some T and indicates unsafe interior operations on the wrapped type. Types with an UnsafeCell field are considered to have an 'unsafe interior'. The UnsafeCell type is the only legal way to obtain aliasable data that is considered mutable. In general, transmuting an &T type into an &mut T is considered undefined behavior. -> -> [...] -> -> The UnsafeCell API itself is technically very simple: it gives you a raw pointer *mut T to its contents. It is up to you as the abstraction designer to use that raw pointer correctly. - -In upcoming tutorials, when the need arises, the `NullLock` will be gradually -extended to provide proper locking using architectural features the RPi3 -provides for this case. - -### Closures - -The Rust standard library and some popular crates for synchronization primitives -use the concept of returning -[RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) -type [guards](https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.lock) -that allow usage of the locked data until the guard goes out of scope. - -In the author's opinion, RAII guards have the disadvantage that the user must -explicitly scope their lifetime with braces `{}`, which is prone to being -forgotten. This in turn would lead to the lock being held much longer than -needed. For educational purposes, the `lock()` functions in the tutorials will -therefore take [closures](https://doc.rust-lang.org/book/ch13-01-closures.html) -as arguments. They give better visual cues about the parts of the code during -which the lock is held. - -Example: - -```rust -static CONSOLE: sync::NullLock = - sync::NullLock::new(devices::virt::Console::new()); - -fn kernel_entry() -> ! { - - ... - - CONSOLE.lock(|c| { // - c.getc(); // Unlocked only inside here - }); // - - ... -} -``` - -> Disclaimer: No investigations have been made if using closures results in -> poorer performance. If so, the hit is taken willingly for said educational -> purposes. - -## `print!` and `println!` - -In `macros.rs`, printing macros from the Rust core library are reused to empower -the kernel with [all the format-string beauty Rust provides](https://doc.rust-lang.org/std/fmt/). The macros eventually call the -function `_print()`, which redirects to the global `CONSOLE` of the kernel (will -be introduced in a minute): - -```rust -pub fn _print(args: fmt::Arguments) { - use core::fmt::Write; - - crate::CONSOLE.lock(|c| { - c.write_fmt(args).unwrap(); - }) -} -``` - -To make this work, the virtual console needs to provide an implementation of -`core::fmt::Write`. In this case, it is as easy as forwarding the -macro-formatted string via `self.current_ptr().puts(s)`. - -## Stitching it All Together - -In `main.rs`, a static `CONSOLE` is defined: - -```rust -/// The global console. Output of the print! and println! macros. -static CONSOLE: sync::NullLock = - sync::NullLock::new(devices::virt::Console::new()); -``` - -By default, it encapsulates a `NullConsole` output, which, well, does -nothing. This is just a safety measure to ensure that the print macros can be -called any time, even before a real physical output is available. In `main.rs`, -a respective call is made that will never appear as an output anywhere: - -```rust -// This will be invisible, because CONSOLE is dispatching to the NullConsole -// at this point in time. -println!("Is there anybody out there?"); -``` - -After initializing the `GPIO` and `VidecoreMbox` drivers, the `UART` is -initialized and replaces the `NullConsole` as the static output: - -```rust -match uart.init(&mut v_mbox, &gpio) { - Ok(_) => { - CONSOLE.lock(|c| { - // Moves uart into the global CONSOLE. It is not accessible - // anymore for the remaining parts of kernel_entry(). - c.replace_with(uart.into()); - }); - println!("\n[0] UART is live!"); - } -``` - -Here it becomes clear why the virtual console is designed such that it stores an -output _by value_. It is not possible to safely store a reference to something -that is generated at runtime in a static data structure. This is because the -static has `static` lifetime, aka lives forever. Whereas a reference to -something generated during runtime might become invalid at some point in the -future. - -Hence, `move semantics` are used to achieve our goal. Once `uart` has moved into -`CONSOLE`, it will live there until it is replaces again. That is also why the -`ConsoleOps` trait demands that its implementors also implement the `Drop` -trait. When calling `CONSOLE.replace()`, the old output will go out of scope, -and hence its drop function will be called. The drop function can then take care -of gracefully shutting down or disabling the device it belongs to. - -While the print macros implicitly call the lock function, there are some places -where it is done explicitly. For example, when querying a keystroke from the -user: - -```rust - print!("[1] Press a key to continue booting... "); - CONSOLE.lock(|c| { - c.getc(); - }); - println!("Greetings fellow Rustacean!"); - -``` - -## Summary - -Lots of things happened in this tutorial: -1. The kernel's code was restructured. -2. The virtual console was introduced as a **Hardware Abstraction Layer**. - 1. **Trait objects** and **dynamic dispatch** were used for the first time. -3. The peculiarities of **mutable static variables** were discussed and what role the **Sync marker trait** plays for them. -4. **Synchronization primitives** were introduced and (a special) one was built. - 1. You learned about **UnsafeCell** and its role in providing **interior mutability**. - 2. You read about **Closures** vs. **RAII guards**. -5. And finally, the `print!` and `println!` macros from the core library are now - usable in the kernel! - -## Output - -```console -ferris@box:~$ make raspboot - -[0] UART is live! -[1] Press a key to continue booting... Greetings fellow Rustacean! -[2] MMU online. -[i] Kernel memory layout: - 0x00000000 - 0x0007FFFF | 512 KiB | C RW PXN | Kernel stack - 0x00080000 - 0x00082FFF | 12 KiB | C RO PX | Kernel code and RO data - 0x00083000 - 0x0008500F | 8 KiB | C RW PXN | Kernel data and BSS - 0x3F000000 - 0x3FFFFFFF | 16 MiB | Dev RW PXN | Device MMIO - -$> -``` diff --git a/0F_globals_synchronization_println/kernel8.img b/0F_globals_synchronization_println/kernel8.img deleted file mode 100755 index 853c32ea..00000000 Binary files a/0F_globals_synchronization_println/kernel8.img and /dev/null differ diff --git a/0F_globals_synchronization_println/link.ld b/0F_globals_synchronization_println/link.ld deleted file mode 100644 index cb6f21a6..00000000 --- a/0F_globals_synchronization_println/link.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -ENTRY(_boot_cores); - -SECTIONS -{ - . = 0x80000; /* This is already 4KiB aligned */ - __ro_start = .; - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - . = ALIGN(4096); /* Fill up to 4KiB */ - __ro_end = .; - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/0F_globals_synchronization_println/raspi3_boot/Cargo.toml b/0F_globals_synchronization_println/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/0F_globals_synchronization_println/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/0F_globals_synchronization_println/raspi3_boot/src/lib.rs b/0F_globals_synchronization_println/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/0F_globals_synchronization_println/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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/0F_globals_synchronization_println/src/delays.rs b/0F_globals_synchronization_println/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/0F_globals_synchronization_println/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/0F_globals_synchronization_println/src/devices.rs b/0F_globals_synchronization_println/src/devices.rs deleted file mode 100644 index 227b92c2..00000000 --- a/0F_globals_synchronization_println/src/devices.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/0F_globals_synchronization_println/src/devices/hw.rs b/0F_globals_synchronization_println/src/devices/hw.rs deleted file mode 100644 index d9684419..00000000 --- a/0F_globals_synchronization_println/src/devices/hw.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 uart; -mod videocore_mbox; - -pub use gpio::GPIO; -pub use uart::Uart; -pub use videocore_mbox::VideocoreMbox; diff --git a/0F_globals_synchronization_println/src/devices/hw/uart.rs b/0F_globals_synchronization_println/src/devices/hw/uart.rs deleted file mode 100644 index c27b62d6..00000000 --- a/0F_globals_synchronization_println/src/devices/hw/uart.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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 UartError { - MailboxError, -} -pub type Result = ::core::result::Result; - -pub struct Uart { - base_addr: usize, -} - -impl ops::Deref for Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl Uart { - pub fn new(base_addr: usize) -> Uart { - Uart { 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(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(()) - } -} - -impl Drop for Uart { - fn drop(&mut self) { - self.CR - .write(CR::UARTEN::Disabled + CR::TXE::Disabled + CR::RXE::Disabled); - } -} - -impl ConsoleOps for Uart { - /// 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/0F_globals_synchronization_println/src/devices/hw/videocore_mbox.rs b/0F_globals_synchronization_println/src/devices/hw/videocore_mbox.rs deleted file mode 100644 index 0f6e294b..00000000 --- a/0F_globals_synchronization_println/src/devices/hw/videocore_mbox.rs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2019 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -use core::ops; -use cortex_a::asm; -use register::{ - mmio::{ReadOnly, WriteOnly}, - register_bitfields, -}; - -register_bitfields! { - u32, - - STATUS [ - FULL OFFSET(31) NUMBITS(1) [], - EMPTY OFFSET(30) NUMBITS(1) [] - ] -} - -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - READ: ReadOnly, // 0x00 - __reserved_0: [u32; 5], // 0x04 - STATUS: ReadOnly, // 0x18 - __reserved_1: u32, // 0x1C - WRITE: WriteOnly, // 0x20 -} - -// Custom errors -pub enum VideocoreMboxError { - ResponseError, - UnknownError, -} -pub type Result = ::core::result::Result; - -// Channels -pub mod channel { - pub const PROP: u32 = 8; -} - -// Tags -pub mod tag { - pub const SETCLKRATE: u32 = 0x38002; - pub const LAST: u32 = 0; -} - -// Clocks -pub mod clock { - pub const UART: u32 = 0x0_0000_0002; -} - -// Responses -mod response { - pub const SUCCESS: u32 = 0x8000_0000; - pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response) -} - -pub const REQUEST: u32 = 0; - -// Public interface to the mailbox -#[repr(C)] -#[repr(align(16))] -pub struct VideocoreMbox { - // The address for buffer needs to be 16-byte aligned so that the Videcore - // can handle it properly. Hence, put it at the start of the struct so that - // the align attribute is effective. - pub buffer: [u32; 36], - base_addr: usize, -} - -/// Deref to RegisterBlock -/// -/// Allows writing -/// ``` -/// self.STATUS.read() -/// ``` -/// instead of something along the lines of -/// ``` -/// unsafe { (*Mbox::ptr()).STATUS.read() } -/// ``` -impl ops::Deref for VideocoreMbox { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl VideocoreMbox { - pub fn new(base_addr: usize) -> VideocoreMbox { - VideocoreMbox { - buffer: [0; 36], - base_addr, - } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } - - /// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success - pub fn call(&self, channel: u32) -> Result<()> { - // wait until we can write to the mailbox - loop { - if !self.STATUS.is_set(STATUS::FULL) { - break; - } - - asm::nop(); - } - - let buf_ptr = self.buffer.as_ptr() as u32; - - // write the address of our message to the mailbox with channel identifier - self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF)); - - // now wait for the response - loop { - // is there a response? - loop { - if !self.STATUS.is_set(STATUS::EMPTY) { - break; - } - - asm::nop(); - } - - let resp: u32 = self.READ.get(); - - // is it a response to our message? - if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) { - // is it a valid successful response? - return match self.buffer[1] { - response::SUCCESS => Ok(()), - response::ERROR => Err(VideocoreMboxError::ResponseError), - _ => Err(VideocoreMboxError::UnknownError), - }; - } - } - } -} diff --git a/0F_globals_synchronization_println/src/devices/virt.rs b/0F_globals_synchronization_println/src/devices/virt.rs deleted file mode 100644 index 30ed5469..00000000 --- a/0F_globals_synchronization_println/src/devices/virt.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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/0F_globals_synchronization_println/src/devices/virt/console.rs b/0F_globals_synchronization_println/src/devices/virt/console.rs deleted file mode 100644 index 84244da8..00000000 --- a/0F_globals_synchronization_println/src/devices/virt/console.rs +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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), - Uart(hw::Uart), -} - -impl From for Output { - fn from(instance: hw::Uart) -> Self { - Output::Uart(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::Uart(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/0F_globals_synchronization_println/src/macros.rs b/0F_globals_synchronization_println/src/macros.rs deleted file mode 100644 index 28280be9..00000000 --- a/0F_globals_synchronization_println/src/macros.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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/0F_globals_synchronization_println/src/main.rs b/0F_globals_synchronization_println/src/main.rs deleted file mode 100644 index 7e4b15b2..00000000 --- a/0F_globals_synchronization_println/src/main.rs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2019 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#![no_std] -#![no_main] -#![feature(const_fn)] -#![feature(custom_attribute)] -#![feature(format_args_nl)] - -mod delays; -mod devices; -mod macros; -mod memory; -mod sync; - -/// The global console. Output of the print! and println! macros. -static CONSOLE: sync::NullLock = - sync::NullLock::new(devices::virt::Console::new()); - -fn kernel_entry() -> ! { - use devices::hw; - use devices::virt::ConsoleOps; - - // This will be invisible, because CONSOLE is dispatching to the NullConsole - // at this point in time. - println!("Is there anybody out there?"); - - //------------------------------------------------------------ - // Instantiate GPIO device - //------------------------------------------------------------ - let gpio = hw::GPIO::new(memory::map::physical::GPIO_BASE); - - //------------------------------------------------------------ - // Instantiate Videocore Mailbox - //------------------------------------------------------------ - let mut v_mbox = hw::VideocoreMbox::new(memory::map::physical::VIDEOCORE_MBOX_BASE); - - //------------------------------------------------------------ - // Instantiate PL011 UART and put it in CONSOLE - //------------------------------------------------------------ - let uart = hw::Uart::new(memory::map::physical::UART_BASE); - - match uart.init(&mut v_mbox, &gpio) { - Ok(_) => { - CONSOLE.lock(|c| { - // Moves uart into the global CONSOLE. It is not accessible - // anymore for the remaining parts of kernel_entry(). - c.replace_with(uart.into()); - }); - - println!("\n[0] UART is live!"); - } - Err(_) => loop { - cortex_a::asm::wfe() // If UART fails, abort early - }, - } - - //------------------------------------------------------------ - // Greet the user - //------------------------------------------------------------ - print!("[1] Press a key to continue booting... "); - CONSOLE.lock(|c| { - c.getc(); - }); - println!("Greetings fellow Rustacean!"); - - //------------------------------------------------------------ - // Bring up memory subsystem - //------------------------------------------------------------ - if unsafe { memory::mmu::init() }.is_err() { - println!("[2][Error] Could not set up MMU. Aborting."); - } else { - println!("[2] MMU online."); - } - - memory::print_layout(); - - //------------------------------------------------------------ - // Start a command prompt - //------------------------------------------------------------ - CONSOLE.lock(|c| { - c.command_prompt(); - }) -} - -raspi3_boot::entry!(kernel_entry); diff --git a/0F_globals_synchronization_println/src/memory.rs b/0F_globals_synchronization_println/src/memory.rs deleted file mode 100644 index dd00a8f4..00000000 --- a/0F_globals_synchronization_println/src/memory.rs +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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::fmt; -use core::ops::RangeInclusive; - -pub mod mmu; - -/// System memory map. -#[rustfmt::skip] -pub mod map { - pub const START: usize = 0x0000_0000; - pub const END: usize = 0x3FFF_FFFF; - - pub mod physical { - pub const MMIO_BASE: usize = 0x3F00_0000; - pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880; - pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; - pub const UART_BASE: usize = MMIO_BASE + 0x0020_1000; - pub const MMIO_END: usize = super::END; - } - - pub mod virt { - pub const KERN_STACK_START: usize = super::START; - pub const KERN_STACK_END: usize = 0x0007_FFFF; - } -} - -/// Types used for compiling the virtual memory layout of the kernel using -/// address ranges. -pub mod kernel_mem_range { - use core::ops::RangeInclusive; - - #[allow(dead_code)] - #[derive(Copy, Clone)] - pub enum MemAttributes { - CacheableDRAM, - NonCacheableDRAM, - Device, - } - - #[derive(Copy, Clone)] - pub enum AccessPermissions { - ReadOnly, - ReadWrite, - } - - #[allow(dead_code)] - #[derive(Copy, Clone)] - pub enum Translation { - Identity, - Offset(usize), - } - - #[derive(Copy, Clone)] - pub struct AttributeFields { - pub mem_attributes: MemAttributes, - pub acc_perms: AccessPermissions, - pub execute_never: bool, - } - - impl Default for AttributeFields { - fn default() -> AttributeFields { - AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - } - } - } - - pub struct Descriptor { - pub name: &'static str, - pub virtual_range: fn() -> RangeInclusive, - pub translation: Translation, - pub attribute_fields: AttributeFields, - } -} - -use kernel_mem_range::*; - -/// A virtual memory layout that is agnostic of the paging granularity that the -/// hardware MMU will use. -/// -/// Contains only special ranges, aka anything that is _not_ normal cacheable -/// DRAM. -static KERNEL_VIRTUAL_LAYOUT: [Descriptor; 4] = [ - Descriptor { - name: "Kernel stack", - virtual_range: || { - RangeInclusive::new(map::virt::KERN_STACK_START, map::virt::KERN_STACK_END) - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Kernel code and RO data", - virtual_range: || { - // Using the linker script, we ensure that the RO area is consecutive and 4 - // KiB aligned, and we export the boundaries via symbols: - // - // [__ro_start, __ro_end) - 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 exclusive 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 turn the exclusive end into an - // inclusive end - RangeInclusive::new( - &__ro_start as *const _ as usize, - &__ro_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadOnly, - execute_never: false, - }, - }, - Descriptor { - name: "Kernel data and BSS", - virtual_range: || { - extern "C" { - static __ro_end: u64; - static __bss_end: u64; - } - - unsafe { - RangeInclusive::new( - &__ro_end as *const _ as usize, - &__bss_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Device MMIO", - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, -]; - -/// For a given virtual address, find and return the output address and -/// according attributes. -/// -/// If the address is not covered in VIRTUAL_LAYOUT, return a default for normal -/// cacheable DRAM. -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> { - if virt_addr > map::END { - return Err("Address out of range."); - } - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - if (i.virtual_range)().contains(&virt_addr) { - let output_addr = match i.translation { - Translation::Identity => virt_addr, - Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), - }; - - return Ok((output_addr, i.attribute_fields)); - } - } - - Ok((virt_addr, AttributeFields::default())) -} - -/// Human-readable output of a Descriptor. -impl fmt::Display for Descriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Call the function to which self.range points, and dereference the - // result, which causes Rust to copy the value. - let start = *(self.virtual_range)().start(); - let end = *(self.virtual_range)().end(); - let size = end - start + 1; - - // log2(1024) - const KIB_RSHIFT: u32 = 10; - - // log2(1024 * 1024) - const MIB_RSHIFT: u32 = 20; - - let (size, unit) = if (size >> MIB_RSHIFT) > 0 { - (size >> MIB_RSHIFT, "MiB") - } else if (size >> KIB_RSHIFT) > 0 { - (size >> KIB_RSHIFT, "KiB") - } else { - (size, "Byte") - }; - - let attr = match self.attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => "C", - MemAttributes::NonCacheableDRAM => "NC", - MemAttributes::Device => "Dev", - }; - - let acc_p = match self.attribute_fields.acc_perms { - AccessPermissions::ReadOnly => "RO", - AccessPermissions::ReadWrite => "RW", - }; - - let xn = if self.attribute_fields.execute_never { - "PXN" - } else { - "PX" - }; - - write!( - f, - " {:#010X} - {:#010X} | {: >3} {} | {: <3} {} {: <3} | {}", - start, end, size, unit, attr, acc_p, xn, self.name - ) - } -} - -/// Print the kernel memory layout. -pub fn print_layout() { - println!("[i] Kernel memory layout:"); - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - println!("{}", i); - } -} diff --git a/0F_globals_synchronization_println/src/memory/mmu.rs b/0F_globals_synchronization_println/src/memory/mmu.rs deleted file mode 100644 index aa4e43c6..00000000 --- a/0F_globals_synchronization_println/src/memory/mmu.rs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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::memory::{get_virt_addr_properties, AttributeFields}; -use cortex_a::{barrier, regs::*}; -use register::register_bitfields; - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Privileged execute-never - PXN OFFSET(53) 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 - ] - ] -} - -const FOUR_KIB: usize = 4 * 1024; -const FOUR_KIB_SHIFT: usize = 12; // log2(4 * 1024) - -const TWO_MIB: usize = 2 * 1024 * 1024; -const TWO_MIB_SHIFT: usize = 21; // log2(2 * 1024 * 1024) - -/// A descriptor pointing to the next page table. -struct TableDescriptor(register::FieldValue); - -impl TableDescriptor { - fn new(next_lvl_table_addr: usize) -> Result { - if next_lvl_table_addr % FOUR_KIB != 0 { - return Err("TableDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = next_lvl_table_addr >> FOUR_KIB_SHIFT; - - Ok(TableDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A function that maps the generic memory range attributes to HW-specific -/// attributes of the MMU. -fn into_mmu_attributes( - attribute_fields: AttributeFields, -) -> register::FieldValue { - use crate::memory::{AccessPermissions, MemAttributes}; - - // Memory attributes - let mut desc = match attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - } - MemAttributes::NonCacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable - + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) - } - MemAttributes::Device => { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } - }; - - // Access Permissions - desc += match attribute_fields.acc_perms { - AccessPermissions::ReadOnly => STAGE1_DESCRIPTOR::AP::RO_EL1, - AccessPermissions::ReadWrite => STAGE1_DESCRIPTOR::AP::RW_EL1, - }; - - // Execute Never - desc += if attribute_fields.execute_never { - STAGE1_DESCRIPTOR::PXN::True - } else { - STAGE1_DESCRIPTOR::PXN::False - }; - - desc -} - -/// A Level2 block descriptor with 2 MiB aperture. -/// -/// The output points to physical memory. -struct Lvl2BlockDescriptor(register::FieldValue); - -impl Lvl2BlockDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % TWO_MIB != 0 { - return Err("BlockDescriptor: Address is not 2 MiB aligned."); - } - - let shifted = output_addr >> TWO_MIB_SHIFT; - - Ok(Lvl2BlockDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A page descriptor with 4 KiB aperture. -/// -/// The output points to physical memory. -struct PageDescriptor(register::FieldValue); - -impl PageDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % FOUR_KIB != 0 { - return Err("PageDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = output_addr >> FOUR_KIB_SHIFT; - - Ok(PageDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// Constants for indexing the MAIR_EL1. -#[allow(dead_code)] -mod mair { - pub const DEVICE: u64 = 0; - pub const NORMAL: u64 = 1; - pub const NORMAL_NON_CACHEABLE: u64 = 2; -} - -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // 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, - ); -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for [u64; 512] { - fn base_addr_u64(&self) -> u64 { - self as *const u64 as u64 - } - - fn base_addr_usize(&self) -> usize { - self as *const u64 as usize - } -} - -const NUM_ENTRIES_4KIB: usize = 512; - -// A wrapper struct is needed here so that the align attribute can be used. -#[repr(C)] -#[repr(align(4096))] -struct PageTable { - entries: [u64; NUM_ENTRIES_4KIB], -} - -/// The LVL2 page table containng the 2 MiB entries. -static mut LVL2_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// The LVL3 page table containing the 4 KiB entries. -/// -/// The first entry of the LVL2_TABLE will forward to this table. -static mut LVL3_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// Set up identity mapped page tables for the first 1 GiB of address space. -/// -/// The first 2 MiB are 4 KiB granule, the rest 2 MiB. -/// -/// # Safety -/// -/// - User must ensure that the hardware supports the paremeters being set here. -pub unsafe fn init() -> Result<(), &'static str> { - // Prepare the memory attribute indirection register. - set_up_mair(); - - // Point the first 2 MiB of virtual addresses to the follow-up LVL3 - // page-table. - LVL2_TABLE.entries[0] = match TableDescriptor::new(LVL3_TABLE.entries.base_addr_usize()) { - Err(s) => return Err(s), - Ok(d) => d.value(), - }; - - // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. - // - // Notice the skip(1) which makes the iteration start at the second 2 MiB - // block (0x20_0000). - for (block_descriptor_nr, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { - let virt_addr = block_descriptor_nr << TWO_MIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let block_desc = match Lvl2BlockDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = block_desc.value(); - } - - // Finally, fill the single LVL3 table (4 KiB granule). - for (page_descriptor_nr, entry) in LVL3_TABLE.entries.iter_mut().enumerate() { - let virt_addr = page_descriptor_nr << FOUR_KIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let page_desc = match PageDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = page_desc.value(); - } - - // Point to the LVL2 table base address in TTBR0. - TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr_u64()); - - // 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); - - Ok(()) -} diff --git a/0F_globals_synchronization_println/src/sync.rs b/0F_globals_synchronization_println/src/sync.rs deleted file mode 100644 index 3cbc1714..00000000 --- a/0F_globals_synchronization_println/src/sync.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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, -} - -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 one at a - // time. - f(unsafe { &mut *self.data.get() }) - } -} diff --git a/10_DMA_memory/.cargo/config b/10_DMA_memory/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/10_DMA_memory/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/10_DMA_memory/Cargo.lock b/10_DMA_memory/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/10_DMA_memory/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/10_DMA_memory/Cargo.toml b/10_DMA_memory/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/10_DMA_memory/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/10_DMA_memory/Makefile b/10_DMA_memory/Makefile deleted file mode 100644 index 7a8d3e52..00000000 --- a/10_DMA_memory/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host -DOCKER_ARG_EMU = -v $(shell pwd)/../emulation:/emulation - -DOCKER_EXEC_QEMU = bash /emulation/qemu_multi_uart.sh -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_EMU) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_QEMU) - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ - kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/10_DMA_memory/README.md b/10_DMA_memory/README.md deleted file mode 100644 index 57a4481b..00000000 --- a/10_DMA_memory/README.md +++ /dev/null @@ -1,264 +0,0 @@ -# Tutorial 10 - DMA Memory - -There's a secret I haven't told you! A certain part of our code doesn't work -anymore since the [virtual memory](../0D_virtual_memory) tutorial. There is a -regression that manifests in the `Videocore Mailbox` driver. It will only work -until **paging and caching** is switched on. Afterwards, the `call()` method -will fail. Why is that? - -The reason is that in our code, the RPi's processor is sharing a `DRAM buffer` -with the `Videocore` device. In other words, the concept of **shared memory** is -used. Let's recall a simplified version of the protocol: - -1. RPi `CPU` checks the `STATUS` MMIO register of the `Videcore` if a message can - be written. -2. If so, `CPU` writes the address of the `DRAM buffer` in which the actual - message is stored into the `Videocore`'s `WRITE` MMIO register. -3. `CPU` checks the `STATUS` and `READ` MMIO registers if the Videocore has - answered. -4. If so, `CPU` checks the first `u32` word of the earlier provided `DRAM buffer` - if the response is valid (the `Videocore` puts its answer into the same buffer - in which the original request was stored. This is what is commonly called - a `DMA` transaction). - -At step **4**, things break. The reason is that code and **page tables** were -set up in a way that the `DRAM buffer` used for message exchange between `CPU` -and Videcore is attributed as _cacheable_. - -So when the `CPU` is writing to the buffer, the contents might not get written -back to `DRAM` in time before the notification of a new message is signaled to -the Videocore via the `WRITE` MMIO register (which is correctly attributed as -device memory in the page tables and hence not cached). - -Even if the contents would land in `DRAM` in time, the `Videocore`'s answer -which overwrites the same buffer would not be reflected in the `CPU`'s cache, -since there is no coherency mechanism in place between the two. The RPi `CPU` -would read back the same values it put into the buffer itself when setting up -the message, and not the `DRAM` content that contains the answer. - -![DMA block diagram](../doc/dma_0.png) - -The regression did not manifest yet because the Mailbox is only used before -paging and caching is switched on, and never afterwards. However, now is a good time -to fix this. - -## An Allocator for DMA Memory - -The first step is to introduce a region of _non-cacheable DRAM_ in the -`KERNEL_VIRTUAL_LAYOUT` in `memory.rs`: - -```rust -Descriptor { - name: "DMA heap pool", - virtual_range: || RangeInclusive::new(map::virt::DMA_HEAP_START, map::virt::DMA_HEAP_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::NonCacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, -}, -``` - -When you saw the inferior performance of non-cacheable mapped DRAM compared to -cacheable DRAM in the [cache performance tutorial](../0E_cache_performance) -earlier and asked yourself why anybody would ever want this: Exactly for the -use-case at hand! - -Theoretically, some linker hacks could be used to ensure that the `Videcore` is -using a buffer that is statically linked to the DMA heap pool once paging and -caching is turned on. However, in real-world kernels, it is common to frequently -map/allocate and unmap/free chunks of `DMA` memory at runtime, for example in -device drivers for DMA-capable devices. - -Hence, let's introduce an `allocator`. - -### Bump Allocation - -As always in the tutorials, a simple implementation is used for getting started -with basic concepts of a topic, and upgrades are introduced when they are -needed. - -In a `bump allocator`, when a requests comes in, it always returns the next -possible aligned region of its heap until it runs out of memory. What makes it -really simple is that it doesn't provide means for freeing memory again. When no -more memory is left, game is over. - -Conveniently enough, [Rust already provides memory allocation APIs](https://doc.rust-lang.org/alloc/alloc/index.html). There is an -[Alloc](https://doc.rust-lang.org/alloc/alloc/trait.Alloc.html) and a -[GlobalAlloc](https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html) -trait. The latter is intended for realizing a _default allocator_, meaning it -would be the allocator used for any standard language construtcs that -automatically allocate something on the heap, for example a -[Box](https://doc.rust-lang.org/alloc/boxed/index.html). There can only be one -global allocator, so the tutorials will make use of it for cacheable DRAM later. - -Hence, for the DMA bump allocator, -[Alloc](https://doc.rust-lang.org/alloc/alloc/trait.Alloc.html) will be -used. What is also really nice is that for both traits, only the `alloc()` -method needs to be implemented. If this is done, you automatically get a bunch -of additional default methods for free, e.g. `alloc_zeroed()`. - -Here is the implementation in `memory/bump_allocator.rs`: - -```rust -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 = crate::memory::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) {} -} -``` - -The `alloc()` method returns a pointer to memory. However, it is safer to -operate with [slices](https://doc.rust-lang.org/alloc/slice/index.html), since -they are intrinsically bounds-checked. Therefore, the `BumpAllocator` gets an -additional method called `alloc_slice_zeroed()`, which wraps around -`alloc_zeroed()` provided by the `Alloc` trait and on success returns a `&'a mut -[T]`. - -### Global Instance - -A global instance of the allocator is needed, and since its methods demand -_mutable references_ to `self`, it is wrapped into a `NullLock`, which was -introduced in the [last tutorial](../0F_globals_synchronization_println): - -```rust -/// 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::virt::DMA_HEAP_START as usize, - memory::map::virt::DMA_HEAP_END as usize, - "Global DMA Allocator", - )); - -``` - -## Videocore Driver - -The `Videocore` driver has to be changed to use the allocator during -instantiation, and in contrast to earlier, this could fail now: - -```rust -let ret = crate::DMA_ALLOCATOR.lock(|d| d.alloc_slice_zeroed(MBOX_SIZE, MBOX_ALIGNMENT)); - -if ret.is_err() { - return Err(()); -} -``` - -## Reorg of the Kernel Init - -Since the `Videcore` now depends on the `DMA Allocator`, its initialization must -now happen _after_ the `MMU init`, which turns on **paging and caching**. This, -in turn, means that the `PL011 UART`, which is used for printing and needs the -`Videcore` for its setup, has to shift its init as well. So there is a lot of -shuffling happening. - -In summary, the new init procedure would be: - -1. GPIO -2. MMU -3. Videcore -4. PL011 UART - -That is a bit unfortunate, because if anything goes wrong at `MMU` or -`Videocore` init, we can not print any fault info on the console. For this -reason, the `MiniUart` from the earlier tutorials is revived, because it only -needs the `GPIO` driver to set itself up. So here is the revamped init: - -1. GPIO -2. MiniUart -3. MMU -4. Videcore -5. PL011 UART - -Using this procedure, the `MiniUart` can report faults for any of the subsequent -stages like`MMU` or `Videocore` init. If all is successful and the more capable -`PL011 UART` comes online, we can let it conveniently replace the `MiniUart` -through the `CONSOLE.replace_with()` scheme introduced in the [last tutorial](../0F_globals_synchronization_println). - -### Make it Fault - -If you feel curious and want to put all the theory to action, take a look at the -code in `main.rs` for the DMA allocator instantiation and try the changes in the -comments: - -```rust -/// 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::virt::DMA_HEAP_START as usize, - memory::map::virt::DMA_HEAP_END as usize, - "Global DMA Allocator", - // Try the following arguments instead to see the PL011 UART init - // fail. It will cause the allocator to use memory that is marked - // cacheable and therefore not DMA-safe. The communication with the - // Videocore will therefore fail. - - // 0x00600000 as usize, - // 0x007FFFFF as usize, - // "Global Non-DMA Allocator", - )); -``` - -This might only work on the real HW and not in QEMU. - -## QEMU - -On the actual HW it is possible to reprogram the same `GPIO` pins at runtime to -either use the `MiniUart` or the `PL011`, and as a result the console output of -both is sent through the same USB-serial dongle. This is transparent to the -user. - -On QEMU, unfortunately, two different virtual terminals must be used and this -multiplexing is not possible. As a result, you'll see that the QEMU output has -changed in optics a bit and now provides separate views for the two `UARTs`. - -## Output - -```console -ferris@box:~$ make raspboot - -[0] MiniUart online. -[1] Press a key to continue booting... Greetings fellow Rustacean! -[2] MMU online. -[i] Kernel memory layout: - 0x00000000 - 0x0007FFFF | 512 KiB | C RW PXN | Kernel stack - 0x00080000 - 0x00083FFF | 16 KiB | C RO PX | Kernel code and RO data - 0x00084000 - 0x0008700F | 12 KiB | C RW PXN | Kernel data and BSS - 0x00200000 - 0x005FFFFF | 4 MiB | NC RW PXN | DMA heap pool - 0x3F000000 - 0x3FFFFFFF | 16 MiB | Dev RW PXN | 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. - -$> -``` diff --git a/10_DMA_memory/kernel8 b/10_DMA_memory/kernel8 deleted file mode 100755 index d1061bd0..00000000 Binary files a/10_DMA_memory/kernel8 and /dev/null differ diff --git a/10_DMA_memory/kernel8.img b/10_DMA_memory/kernel8.img deleted file mode 100755 index a38b0f62..00000000 Binary files a/10_DMA_memory/kernel8.img and /dev/null differ diff --git a/10_DMA_memory/link.ld b/10_DMA_memory/link.ld deleted file mode 100644 index cb6f21a6..00000000 --- a/10_DMA_memory/link.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 Andre Richter - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -ENTRY(_boot_cores); - -SECTIONS -{ - . = 0x80000; /* This is already 4KiB aligned */ - __ro_start = .; - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - . = ALIGN(4096); /* Fill up to 4KiB */ - __ro_end = .; - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/10_DMA_memory/raspi3_boot/Cargo.lock b/10_DMA_memory/raspi3_boot/Cargo.lock deleted file mode 100644 index 7428c6de..00000000 --- a/10_DMA_memory/raspi3_boot/Cargo.lock +++ /dev/null @@ -1,46 +0,0 @@ -[[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_DMA_memory/raspi3_boot/Cargo.toml b/10_DMA_memory/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/10_DMA_memory/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/10_DMA_memory/raspi3_boot/src/lib.rs b/10_DMA_memory/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/10_DMA_memory/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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_DMA_memory/src/delays.rs b/10_DMA_memory/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/10_DMA_memory/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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_DMA_memory/src/devices.rs b/10_DMA_memory/src/devices.rs deleted file mode 100644 index 227b92c2..00000000 --- a/10_DMA_memory/src/devices.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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_DMA_memory/src/devices/hw.rs b/10_DMA_memory/src/devices/hw.rs deleted file mode 100644 index 08a6c06c..00000000 --- a/10_DMA_memory/src/devices/hw.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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_DMA_memory/src/devices/hw/mini_uart.rs b/10_DMA_memory/src/devices/hw/mini_uart.rs deleted file mode 100644 index f98b06bf..00000000 --- a/10_DMA_memory/src/devices/hw/mini_uart.rs +++ /dev/null @@ -1,266 +0,0 @@ -/* - * 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: usize, -} - -/// 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: usize) -> 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_DMA_memory/src/devices/hw/pl011_uart.rs b/10_DMA_memory/src/devices/hw/pl011_uart.rs deleted file mode 100644 index 44580d35..00000000 --- a/10_DMA_memory/src/devices/hw/pl011_uart.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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: usize, -} - -impl ops::Deref for PL011Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl PL011Uart { - pub fn new(base_addr: usize) -> 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_DMA_memory/src/devices/hw/videocore_mbox.rs b/10_DMA_memory/src/devices/hw/videocore_mbox.rs deleted file mode 100644 index 729777aa..00000000 --- a/10_DMA_memory/src/devices/hw/videocore_mbox.rs +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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: usize, -} - -/// 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: usize) -> ::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_DMA_memory/src/devices/virt.rs b/10_DMA_memory/src/devices/virt.rs deleted file mode 100644 index 30ed5469..00000000 --- a/10_DMA_memory/src/devices/virt.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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_DMA_memory/src/devices/virt/console.rs b/10_DMA_memory/src/devices/virt/console.rs deleted file mode 100644 index 6972325a..00000000 --- a/10_DMA_memory/src/devices/virt/console.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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_DMA_memory/src/macros.rs b/10_DMA_memory/src/macros.rs deleted file mode 100644 index 28280be9..00000000 --- a/10_DMA_memory/src/macros.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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_DMA_memory/src/main.rs b/10_DMA_memory/src/main.rs deleted file mode 100644 index 06fdd57a..00000000 --- a/10_DMA_memory/src/main.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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(label_break_value)] - -mod delays; -mod devices; -mod macros; -mod memory; -mod sync; - -/// The global console. Output of the print! and println! macros. -static CONSOLE: sync::NullLock = - sync::NullLock::new(devices::virt::Console::new()); - -/// 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::virt::DMA_HEAP_START as usize, - memory::map::virt::DMA_HEAP_END as usize, - "Global DMA Allocator", - // Try the following arguments instead to see the PL011 UART init - // fail. It will cause the allocator to use memory that is marked - // cacheable and therefore not DMA-safe. The communication with the - // Videocore will therefore fail. - - // 0x00600000 as usize, - // 0x007FFFFF as usize, - // "Global Non-DMA Allocator", - )); - -fn kernel_entry() -> ! { - use devices::hw; - use devices::virt::ConsoleOps; - - //------------------------------------------------------------ - // Instantiate GPIO device - //------------------------------------------------------------ - let gpio = hw::GPIO::new(memory::map::physical::GPIO_BASE); - - //------------------------------------------------------------ - // Instantiate MiniUart - //------------------------------------------------------------ - let mini_uart = hw::MiniUart::new(memory::map::physical::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!"); - - // 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: { - //------------------------------------------------------------ - // Bring up memory subsystem - //------------------------------------------------------------ - if unsafe { memory::mmu::init() }.is_err() { - println!("[2][Error] Could not set up MMU. Aborting."); - break 'init; - }; - println!("[2] MMU online."); - - memory::print_layout(); - - //------------------------------------------------------------ - // Instantiate Videocore Mailbox - //------------------------------------------------------------ - let mut v_mbox; - match hw::VideocoreMbox::new(memory::map::physical::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::physical::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." - ), - } - } - - //------------------------------------------------------------ - // Start a command prompt - //------------------------------------------------------------ - CONSOLE.lock(|c| { - c.command_prompt(); - }) -} - -raspi3_boot::entry!(kernel_entry); diff --git a/10_DMA_memory/src/memory.rs b/10_DMA_memory/src/memory.rs deleted file mode 100644 index f1da0c87..00000000 --- a/10_DMA_memory/src/memory.rs +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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::fmt; -use core::ops::RangeInclusive; - -mod bump_allocator; -pub use bump_allocator::BumpAllocator; - -pub mod mmu; - -/// System memory map. -#[rustfmt::skip] -pub mod map { - pub const START: usize = 0x0000_0000; - pub const END: usize = 0x3FFF_FFFF; - - pub mod physical { - pub const MMIO_BASE: usize = 0x3F00_0000; - pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880; - pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; - pub const PL011_UART_BASE: usize = MMIO_BASE + 0x0020_1000; - pub const MINI_UART_BASE: usize = MMIO_BASE + 0x0021_5000; - pub const MMIO_END: usize = super::END; - } - - pub mod virt { - pub const KERN_STACK_START: usize = super::START; - pub const KERN_STACK_END: usize = 0x0007_FFFF; - - // The second 2 MiB block. - pub const DMA_HEAP_START: usize = 0x0020_0000; - pub const DMA_HEAP_END: usize = 0x005F_FFFF; - } -} - -/// Types used for compiling the virtual memory layout of the kernel using -/// address ranges. -pub mod kernel_mem_range { - use core::ops::RangeInclusive; - - #[derive(Copy, Clone)] - pub enum MemAttributes { - CacheableDRAM, - NonCacheableDRAM, - Device, - } - - #[derive(Copy, Clone)] - pub enum AccessPermissions { - ReadOnly, - ReadWrite, - } - - #[allow(dead_code)] - #[derive(Copy, Clone)] - pub enum Translation { - Identity, - Offset(usize), - } - - #[derive(Copy, Clone)] - pub struct AttributeFields { - pub mem_attributes: MemAttributes, - pub acc_perms: AccessPermissions, - pub execute_never: bool, - } - - impl Default for AttributeFields { - fn default() -> AttributeFields { - AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - } - } - } - - pub struct Descriptor { - pub name: &'static str, - pub virtual_range: fn() -> RangeInclusive, - pub translation: Translation, - pub attribute_fields: AttributeFields, - } -} - -use kernel_mem_range::*; - -/// A virtual memory layout that is agnostic of the paging granularity that the -/// hardware MMU will use. -/// -/// Contains only special ranges, aka anything that is _not_ normal cacheable -/// DRAM. -static KERNEL_VIRTUAL_LAYOUT: [Descriptor; 5] = [ - Descriptor { - name: "Kernel stack", - virtual_range: || { - RangeInclusive::new(map::virt::KERN_STACK_START, map::virt::KERN_STACK_END) - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Kernel code and RO data", - virtual_range: || { - // Using the linker script, we ensure that the RO area is consecutive and 4 - // KiB aligned, and we export the boundaries via symbols: - // - // [__ro_start, __ro_end) - 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 exclusive 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 turn the exclusive end into an - // inclusive end - RangeInclusive::new( - &__ro_start as *const _ as usize, - &__ro_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadOnly, - execute_never: false, - }, - }, - Descriptor { - name: "Kernel data and BSS", - virtual_range: || { - extern "C" { - static __ro_end: u64; - static __bss_end: u64; - } - - unsafe { - RangeInclusive::new( - &__ro_end as *const _ as usize, - &__bss_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "DMA heap pool", - virtual_range: || RangeInclusive::new(map::virt::DMA_HEAP_START, map::virt::DMA_HEAP_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::NonCacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Device MMIO", - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, -]; - -/// For a given virtual address, find and return the output address and -/// according attributes. -/// -/// If the address is not covered in VIRTUAL_LAYOUT, return a default for normal -/// cacheable DRAM. -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> { - if virt_addr > map::END { - return Err("Address out of range."); - } - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - if (i.virtual_range)().contains(&virt_addr) { - let output_addr = match i.translation { - Translation::Identity => virt_addr, - Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), - }; - - return Ok((output_addr, i.attribute_fields)); - } - } - - Ok((virt_addr, AttributeFields::default())) -} - -/// Human-readable output of a Descriptor. -impl fmt::Display for Descriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Call the function to which self.range points, and dereference the - // result, which causes Rust to copy the value. - let start = *(self.virtual_range)().start(); - let end = *(self.virtual_range)().end(); - let size = end - start + 1; - - // log2(1024) - const KIB_RSHIFT: u32 = 10; - - // log2(1024 * 1024) - const MIB_RSHIFT: u32 = 20; - - let (size, unit) = if (size >> MIB_RSHIFT) > 0 { - (size >> MIB_RSHIFT, "MiB") - } else if (size >> KIB_RSHIFT) > 0 { - (size >> KIB_RSHIFT, "KiB") - } else { - (size, "Byte") - }; - - let attr = match self.attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => "C", - MemAttributes::NonCacheableDRAM => "NC", - MemAttributes::Device => "Dev", - }; - - let acc_p = match self.attribute_fields.acc_perms { - AccessPermissions::ReadOnly => "RO", - AccessPermissions::ReadWrite => "RW", - }; - - let xn = if self.attribute_fields.execute_never { - "PXN" - } else { - "PX" - }; - - write!( - f, - " {:#010X} - {:#010X} | {: >3} {} | {: <3} {} {: <3} | {}", - start, end, size, unit, attr, acc_p, xn, self.name - ) - } -} - -/// Print the kernel memory layout. -pub fn print_layout() { - println!("[i] Kernel memory layout:"); - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - println!("{}", i); - } -} - -/// Calculate the next possible aligned address without sanity checking the -/// input parameters. -#[inline] -fn aligned_addr_unchecked(addr: usize, alignment: usize) -> usize { - (addr + (alignment - 1)) & !(alignment - 1) -} diff --git a/10_DMA_memory/src/memory/bump_allocator.rs b/10_DMA_memory/src/memory/bump_allocator.rs deleted file mode 100644 index 0bab4129..00000000 --- a/10_DMA_memory/src/memory/bump_allocator.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 = crate::memory::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_DMA_memory/src/memory/mmu.rs b/10_DMA_memory/src/memory/mmu.rs deleted file mode 100644 index aa4e43c6..00000000 --- a/10_DMA_memory/src/memory/mmu.rs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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::memory::{get_virt_addr_properties, AttributeFields}; -use cortex_a::{barrier, regs::*}; -use register::register_bitfields; - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Privileged execute-never - PXN OFFSET(53) 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 - ] - ] -} - -const FOUR_KIB: usize = 4 * 1024; -const FOUR_KIB_SHIFT: usize = 12; // log2(4 * 1024) - -const TWO_MIB: usize = 2 * 1024 * 1024; -const TWO_MIB_SHIFT: usize = 21; // log2(2 * 1024 * 1024) - -/// A descriptor pointing to the next page table. -struct TableDescriptor(register::FieldValue); - -impl TableDescriptor { - fn new(next_lvl_table_addr: usize) -> Result { - if next_lvl_table_addr % FOUR_KIB != 0 { - return Err("TableDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = next_lvl_table_addr >> FOUR_KIB_SHIFT; - - Ok(TableDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A function that maps the generic memory range attributes to HW-specific -/// attributes of the MMU. -fn into_mmu_attributes( - attribute_fields: AttributeFields, -) -> register::FieldValue { - use crate::memory::{AccessPermissions, MemAttributes}; - - // Memory attributes - let mut desc = match attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - } - MemAttributes::NonCacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable - + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) - } - MemAttributes::Device => { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } - }; - - // Access Permissions - desc += match attribute_fields.acc_perms { - AccessPermissions::ReadOnly => STAGE1_DESCRIPTOR::AP::RO_EL1, - AccessPermissions::ReadWrite => STAGE1_DESCRIPTOR::AP::RW_EL1, - }; - - // Execute Never - desc += if attribute_fields.execute_never { - STAGE1_DESCRIPTOR::PXN::True - } else { - STAGE1_DESCRIPTOR::PXN::False - }; - - desc -} - -/// A Level2 block descriptor with 2 MiB aperture. -/// -/// The output points to physical memory. -struct Lvl2BlockDescriptor(register::FieldValue); - -impl Lvl2BlockDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % TWO_MIB != 0 { - return Err("BlockDescriptor: Address is not 2 MiB aligned."); - } - - let shifted = output_addr >> TWO_MIB_SHIFT; - - Ok(Lvl2BlockDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A page descriptor with 4 KiB aperture. -/// -/// The output points to physical memory. -struct PageDescriptor(register::FieldValue); - -impl PageDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % FOUR_KIB != 0 { - return Err("PageDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = output_addr >> FOUR_KIB_SHIFT; - - Ok(PageDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// Constants for indexing the MAIR_EL1. -#[allow(dead_code)] -mod mair { - pub const DEVICE: u64 = 0; - pub const NORMAL: u64 = 1; - pub const NORMAL_NON_CACHEABLE: u64 = 2; -} - -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // 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, - ); -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for [u64; 512] { - fn base_addr_u64(&self) -> u64 { - self as *const u64 as u64 - } - - fn base_addr_usize(&self) -> usize { - self as *const u64 as usize - } -} - -const NUM_ENTRIES_4KIB: usize = 512; - -// A wrapper struct is needed here so that the align attribute can be used. -#[repr(C)] -#[repr(align(4096))] -struct PageTable { - entries: [u64; NUM_ENTRIES_4KIB], -} - -/// The LVL2 page table containng the 2 MiB entries. -static mut LVL2_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// The LVL3 page table containing the 4 KiB entries. -/// -/// The first entry of the LVL2_TABLE will forward to this table. -static mut LVL3_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// Set up identity mapped page tables for the first 1 GiB of address space. -/// -/// The first 2 MiB are 4 KiB granule, the rest 2 MiB. -/// -/// # Safety -/// -/// - User must ensure that the hardware supports the paremeters being set here. -pub unsafe fn init() -> Result<(), &'static str> { - // Prepare the memory attribute indirection register. - set_up_mair(); - - // Point the first 2 MiB of virtual addresses to the follow-up LVL3 - // page-table. - LVL2_TABLE.entries[0] = match TableDescriptor::new(LVL3_TABLE.entries.base_addr_usize()) { - Err(s) => return Err(s), - Ok(d) => d.value(), - }; - - // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. - // - // Notice the skip(1) which makes the iteration start at the second 2 MiB - // block (0x20_0000). - for (block_descriptor_nr, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { - let virt_addr = block_descriptor_nr << TWO_MIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let block_desc = match Lvl2BlockDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = block_desc.value(); - } - - // Finally, fill the single LVL3 table (4 KiB granule). - for (page_descriptor_nr, entry) in LVL3_TABLE.entries.iter_mut().enumerate() { - let virt_addr = page_descriptor_nr << FOUR_KIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let page_desc = match PageDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = page_desc.value(); - } - - // Point to the LVL2 table base address in TTBR0. - TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr_u64()); - - // 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); - - Ok(()) -} diff --git a/10_DMA_memory/src/sync.rs b/10_DMA_memory/src/sync.rs deleted file mode 100644 index 3cbc1714..00000000 --- a/10_DMA_memory/src/sync.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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, -} - -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 one at a - // time. - f(unsafe { &mut *self.data.get() }) - } -} diff --git a/11_exceptions_groundwork/.cargo/config b/11_exceptions_groundwork/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/11_exceptions_groundwork/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/11_exceptions_groundwork/Cargo.lock b/11_exceptions_groundwork/Cargo.lock deleted file mode 100644 index d218ad56..00000000 --- a/11_exceptions_groundwork/Cargo.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "cortex-a" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel8" -version = "0.1.0" -dependencies = [ - "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", - "register 0.3.3 (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.7.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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" -"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/11_exceptions_groundwork/Cargo.toml b/11_exceptions_groundwork/Cargo.toml deleted file mode 100644 index 43d1e22e..00000000 --- a/11_exceptions_groundwork/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "kernel8" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.7.0" -register = "0.3.3" - -[package.metadata.cargo-xbuild] -sysroot_path = "../xbuild_sysroot" diff --git a/11_exceptions_groundwork/Makefile b/11_exceptions_groundwork/Makefile deleted file mode 100644 index 72d854de..00000000 --- a/11_exceptions_groundwork/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -# -# 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-softfloat - -SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld - - -XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release -CARGO_OUTPUT = target/$(TARGET)/release/kernel8 - -OBJCOPY = cargo objcopy -- -OBJCOPY_PARAMS = --strip-all -O binary - -CONTAINER_UTILS = andrerichter/raspi3-utils -CONTAINER_OPENOCD = andrerichter/raspi3-openocd -# CONTAINER_OPENOCD_ARG = -f openocd/tcl/interface/ftdi/olimex-jtag-tiny.cfg -f /openocd/rpi3.cfg -CONTAINER_GDB = andrerichter/raspi3-gdb - -DOCKER_CMD = docker run -it --rm -DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work -DOCKER_ARG_TTY = --privileged -v /dev:/dev -DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag -DOCKER_ARG_NET = --network host -DOCKER_ARG_EMU = -v $(shell pwd)/../emulation:/emulation - -DOCKER_EXEC_QEMU = bash /emulation/qemu_multi_uart.sh -DOCKER_EXEC_RASPBOOT = raspbootcom -DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 -# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 - -.PHONY: all qemu raspboot clippy clean objdump nm jtagboot openocd gdb gdb-opt0 - -all: clean kernel8.img - -$(CARGO_OUTPUT): $(SOURCES) - $(XRUSTC_CMD) - -kernel8.img: $(CARGO_OUTPUT) - cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img - -qemu: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_EMU) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_QEMU) - -raspboot: all - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ - $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) \ - $(DOCKER_EXEC_RASPBOOT_DEV) kernel8.img - -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 - -jtagboot: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ - $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) /jtag/jtag_boot.img - -openocd: - $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD) \ - $(CONTAINER_OPENOCD_ARG) - -define gen_gdb - $(XRUSTC_CMD) -- $1 - cp $(CARGO_OUTPUT) kernel8_for_jtag - $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \ - gdb-multiarch -q kernel8_for_jtag -endef - -gdb: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2) - -gdb-opt0: clean $(SOURCES) - $(call gen_gdb,-C debuginfo=2 -C opt-level=0) diff --git a/11_exceptions_groundwork/README.md b/11_exceptions_groundwork/README.md deleted file mode 100644 index 1f25325d..00000000 --- a/11_exceptions_groundwork/README.md +++ /dev/null @@ -1,292 +0,0 @@ -# Tutorial 11 - 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 `0D`. -After the exception handler is finished, it returns to the first instruction -after the memory read that caused the exception. - -## Output - -```console -ferris@box:~$ make raspboot - -[0] MiniUart online. -[1] Press a key to continue booting... Greetings fellow Rustacean! -[2] MMU online. -[i] Kernel memory layout: - 0x00000000 - 0x0007FFFF | 512 KiB | C RW PXN | Kernel stack - 0x00080000 - 0x00084FFF | 20 KiB | C RO PX | Kernel code and RO data - 0x00085000 - 0x0008800F | 12 KiB | C RW PXN | Kernel data and BSS - 0x00200000 - 0x005FFFFF | 4 MiB | NC RW PXN | DMA heap pool - 0x3F000000 - 0x3FFFFFFF | 16 MiB | Dev RW PXN | 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: 0x00080C20 - Incrementing ELR_EL1 by 4 now to continue with the first instruction after the exception! - ELR_EL1 modified: 0x00080C24 - Returning from exception... - -[i] Whoa! We recovered from an exception. - -$> -``` diff --git a/11_exceptions_groundwork/kernel8 b/11_exceptions_groundwork/kernel8 deleted file mode 100755 index ef99bf7d..00000000 Binary files a/11_exceptions_groundwork/kernel8 and /dev/null differ diff --git a/11_exceptions_groundwork/kernel8.img b/11_exceptions_groundwork/kernel8.img deleted file mode 100755 index 2ce0e2a5..00000000 Binary files a/11_exceptions_groundwork/kernel8.img and /dev/null differ diff --git a/11_exceptions_groundwork/link.ld b/11_exceptions_groundwork/link.ld deleted file mode 100644 index 00bb9b46..00000000 --- a/11_exceptions_groundwork/link.ld +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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/11_exceptions_groundwork/raspi3_boot/Cargo.lock b/11_exceptions_groundwork/raspi3_boot/Cargo.lock deleted file mode 100644 index 7428c6de..00000000 --- a/11_exceptions_groundwork/raspi3_boot/Cargo.lock +++ /dev/null @@ -1,46 +0,0 @@ -[[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/11_exceptions_groundwork/raspi3_boot/Cargo.toml b/11_exceptions_groundwork/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/11_exceptions_groundwork/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/11_exceptions_groundwork/raspi3_boot/src/lib.rs b/11_exceptions_groundwork/raspi3_boot/src/lib.rs deleted file mode 100644 index 01ccd956..00000000 --- a/11_exceptions_groundwork/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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 - HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); - - // Set up a simulated exception return. - // - // First, fake a saved program status, where all interrupts were - // masked and SP_EL1 was used as a stack pointer. - SPSR_EL2.write( - SPSR_EL2::D::Masked - + SPSR_EL2::A::Masked - + SPSR_EL2::I::Masked - + SPSR_EL2::F::Masked - + SPSR_EL2::M::EL1h, - ); - - // Second, let the link register point to 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. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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/11_exceptions_groundwork/src/delays.rs b/11_exceptions_groundwork/src/delays.rs deleted file mode 100644 index b1c1fa0f..00000000 --- a/11_exceptions_groundwork/src/delays.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/devices.rs b/11_exceptions_groundwork/src/devices.rs deleted file mode 100644 index 227b92c2..00000000 --- a/11_exceptions_groundwork/src/devices.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/devices/hw.rs b/11_exceptions_groundwork/src/devices/hw.rs deleted file mode 100644 index 08a6c06c..00000000 --- a/11_exceptions_groundwork/src/devices/hw.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/devices/hw/gpio.rs b/11_exceptions_groundwork/src/devices/hw/gpio.rs deleted file mode 100644 index 7affea08..00000000 --- a/11_exceptions_groundwork/src/devices/hw/gpio.rs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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: usize, -} - -impl ops::Deref for GPIO { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } -} diff --git a/11_exceptions_groundwork/src/devices/hw/mini_uart.rs b/11_exceptions_groundwork/src/devices/hw/mini_uart.rs deleted file mode 100644 index f98b06bf..00000000 --- a/11_exceptions_groundwork/src/devices/hw/mini_uart.rs +++ /dev/null @@ -1,266 +0,0 @@ -/* - * 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: usize, -} - -/// 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: usize) -> 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/11_exceptions_groundwork/src/devices/hw/pl011_uart.rs b/11_exceptions_groundwork/src/devices/hw/pl011_uart.rs deleted file mode 100644 index 44580d35..00000000 --- a/11_exceptions_groundwork/src/devices/hw/pl011_uart.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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: usize, -} - -impl ops::Deref for PL011Uart { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl PL011Uart { - pub fn new(base_addr: usize) -> 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/11_exceptions_groundwork/src/devices/hw/videocore_mbox.rs b/11_exceptions_groundwork/src/devices/hw/videocore_mbox.rs deleted file mode 100644 index 729777aa..00000000 --- a/11_exceptions_groundwork/src/devices/hw/videocore_mbox.rs +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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: usize, -} - -/// 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: usize) -> ::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/11_exceptions_groundwork/src/devices/virt.rs b/11_exceptions_groundwork/src/devices/virt.rs deleted file mode 100644 index 30ed5469..00000000 --- a/11_exceptions_groundwork/src/devices/virt.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/devices/virt/console.rs b/11_exceptions_groundwork/src/devices/virt/console.rs deleted file mode 100644 index 6972325a..00000000 --- a/11_exceptions_groundwork/src/devices/virt/console.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/exception.rs b/11_exceptions_groundwork/src/exception.rs deleted file mode 100644 index 189d4c36..00000000 --- a/11_exceptions_groundwork/src/exception.rs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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")); - -/// # Safety -/// -/// - User must ensure to call this function at a suitable place, since it -/// changes the state of the hardware. -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/11_exceptions_groundwork/src/macros.rs b/11_exceptions_groundwork/src/macros.rs deleted file mode 100644 index 28280be9..00000000 --- a/11_exceptions_groundwork/src/macros.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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/11_exceptions_groundwork/src/main.rs b/11_exceptions_groundwork/src/main.rs deleted file mode 100644 index 2e8ae768..00000000 --- a/11_exceptions_groundwork/src/main.rs +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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)] - -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::virt::DMA_HEAP_START as usize, - memory::map::virt::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::physical::GPIO_BASE); - - //------------------------------------------------------------ - // Instantiate MiniUart - //------------------------------------------------------------ - let mini_uart = hw::MiniUart::new(memory::map::physical::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!"); - - // 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: { - //------------------------------------------------------------ - // Bring up memory subsystem - //------------------------------------------------------------ - if unsafe { memory::mmu::init() }.is_err() { - println!("[2][Error] Could not set up MMU. Aborting."); - break 'init; - }; - println!("[2] MMU online."); - - memory::print_layout(); - - //------------------------------------------------------------ - // Instantiate Videocore Mailbox - //------------------------------------------------------------ - let mut v_mbox; - match hw::VideocoreMbox::new(memory::map::physical::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::physical::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/11_exceptions_groundwork/src/memory.rs b/11_exceptions_groundwork/src/memory.rs deleted file mode 100644 index f1da0c87..00000000 --- a/11_exceptions_groundwork/src/memory.rs +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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::fmt; -use core::ops::RangeInclusive; - -mod bump_allocator; -pub use bump_allocator::BumpAllocator; - -pub mod mmu; - -/// System memory map. -#[rustfmt::skip] -pub mod map { - pub const START: usize = 0x0000_0000; - pub const END: usize = 0x3FFF_FFFF; - - pub mod physical { - pub const MMIO_BASE: usize = 0x3F00_0000; - pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880; - pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; - pub const PL011_UART_BASE: usize = MMIO_BASE + 0x0020_1000; - pub const MINI_UART_BASE: usize = MMIO_BASE + 0x0021_5000; - pub const MMIO_END: usize = super::END; - } - - pub mod virt { - pub const KERN_STACK_START: usize = super::START; - pub const KERN_STACK_END: usize = 0x0007_FFFF; - - // The second 2 MiB block. - pub const DMA_HEAP_START: usize = 0x0020_0000; - pub const DMA_HEAP_END: usize = 0x005F_FFFF; - } -} - -/// Types used for compiling the virtual memory layout of the kernel using -/// address ranges. -pub mod kernel_mem_range { - use core::ops::RangeInclusive; - - #[derive(Copy, Clone)] - pub enum MemAttributes { - CacheableDRAM, - NonCacheableDRAM, - Device, - } - - #[derive(Copy, Clone)] - pub enum AccessPermissions { - ReadOnly, - ReadWrite, - } - - #[allow(dead_code)] - #[derive(Copy, Clone)] - pub enum Translation { - Identity, - Offset(usize), - } - - #[derive(Copy, Clone)] - pub struct AttributeFields { - pub mem_attributes: MemAttributes, - pub acc_perms: AccessPermissions, - pub execute_never: bool, - } - - impl Default for AttributeFields { - fn default() -> AttributeFields { - AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - } - } - } - - pub struct Descriptor { - pub name: &'static str, - pub virtual_range: fn() -> RangeInclusive, - pub translation: Translation, - pub attribute_fields: AttributeFields, - } -} - -use kernel_mem_range::*; - -/// A virtual memory layout that is agnostic of the paging granularity that the -/// hardware MMU will use. -/// -/// Contains only special ranges, aka anything that is _not_ normal cacheable -/// DRAM. -static KERNEL_VIRTUAL_LAYOUT: [Descriptor; 5] = [ - Descriptor { - name: "Kernel stack", - virtual_range: || { - RangeInclusive::new(map::virt::KERN_STACK_START, map::virt::KERN_STACK_END) - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Kernel code and RO data", - virtual_range: || { - // Using the linker script, we ensure that the RO area is consecutive and 4 - // KiB aligned, and we export the boundaries via symbols: - // - // [__ro_start, __ro_end) - 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 exclusive 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 turn the exclusive end into an - // inclusive end - RangeInclusive::new( - &__ro_start as *const _ as usize, - &__ro_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadOnly, - execute_never: false, - }, - }, - Descriptor { - name: "Kernel data and BSS", - virtual_range: || { - extern "C" { - static __ro_end: u64; - static __bss_end: u64; - } - - unsafe { - RangeInclusive::new( - &__ro_end as *const _ as usize, - &__bss_end as *const _ as usize - 1, - ) - } - }, - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::CacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "DMA heap pool", - virtual_range: || RangeInclusive::new(map::virt::DMA_HEAP_START, map::virt::DMA_HEAP_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::NonCacheableDRAM, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, - Descriptor { - name: "Device MMIO", - virtual_range: || RangeInclusive::new(map::physical::MMIO_BASE, map::physical::MMIO_END), - translation: Translation::Identity, - attribute_fields: AttributeFields { - mem_attributes: MemAttributes::Device, - acc_perms: AccessPermissions::ReadWrite, - execute_never: true, - }, - }, -]; - -/// For a given virtual address, find and return the output address and -/// according attributes. -/// -/// If the address is not covered in VIRTUAL_LAYOUT, return a default for normal -/// cacheable DRAM. -fn get_virt_addr_properties(virt_addr: usize) -> Result<(usize, AttributeFields), &'static str> { - if virt_addr > map::END { - return Err("Address out of range."); - } - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - if (i.virtual_range)().contains(&virt_addr) { - let output_addr = match i.translation { - Translation::Identity => virt_addr, - Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), - }; - - return Ok((output_addr, i.attribute_fields)); - } - } - - Ok((virt_addr, AttributeFields::default())) -} - -/// Human-readable output of a Descriptor. -impl fmt::Display for Descriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Call the function to which self.range points, and dereference the - // result, which causes Rust to copy the value. - let start = *(self.virtual_range)().start(); - let end = *(self.virtual_range)().end(); - let size = end - start + 1; - - // log2(1024) - const KIB_RSHIFT: u32 = 10; - - // log2(1024 * 1024) - const MIB_RSHIFT: u32 = 20; - - let (size, unit) = if (size >> MIB_RSHIFT) > 0 { - (size >> MIB_RSHIFT, "MiB") - } else if (size >> KIB_RSHIFT) > 0 { - (size >> KIB_RSHIFT, "KiB") - } else { - (size, "Byte") - }; - - let attr = match self.attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => "C", - MemAttributes::NonCacheableDRAM => "NC", - MemAttributes::Device => "Dev", - }; - - let acc_p = match self.attribute_fields.acc_perms { - AccessPermissions::ReadOnly => "RO", - AccessPermissions::ReadWrite => "RW", - }; - - let xn = if self.attribute_fields.execute_never { - "PXN" - } else { - "PX" - }; - - write!( - f, - " {:#010X} - {:#010X} | {: >3} {} | {: <3} {} {: <3} | {}", - start, end, size, unit, attr, acc_p, xn, self.name - ) - } -} - -/// Print the kernel memory layout. -pub fn print_layout() { - println!("[i] Kernel memory layout:"); - - for i in KERNEL_VIRTUAL_LAYOUT.iter() { - println!("{}", i); - } -} - -/// Calculate the next possible aligned address without sanity checking the -/// input parameters. -#[inline] -fn aligned_addr_unchecked(addr: usize, alignment: usize) -> usize { - (addr + (alignment - 1)) & !(alignment - 1) -} diff --git a/11_exceptions_groundwork/src/memory/bump_allocator.rs b/11_exceptions_groundwork/src/memory/bump_allocator.rs deleted file mode 100644 index 0bab4129..00000000 --- a/11_exceptions_groundwork/src/memory/bump_allocator.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 = crate::memory::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/11_exceptions_groundwork/src/memory/mmu.rs b/11_exceptions_groundwork/src/memory/mmu.rs deleted file mode 100644 index aa4e43c6..00000000 --- a/11_exceptions_groundwork/src/memory/mmu.rs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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::memory::{get_virt_addr_properties, AttributeFields}; -use cortex_a::{barrier, regs::*}; -use register::register_bitfields; - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Privileged execute-never - PXN OFFSET(53) 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 - ] - ] -} - -const FOUR_KIB: usize = 4 * 1024; -const FOUR_KIB_SHIFT: usize = 12; // log2(4 * 1024) - -const TWO_MIB: usize = 2 * 1024 * 1024; -const TWO_MIB_SHIFT: usize = 21; // log2(2 * 1024 * 1024) - -/// A descriptor pointing to the next page table. -struct TableDescriptor(register::FieldValue); - -impl TableDescriptor { - fn new(next_lvl_table_addr: usize) -> Result { - if next_lvl_table_addr % FOUR_KIB != 0 { - return Err("TableDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = next_lvl_table_addr >> FOUR_KIB_SHIFT; - - Ok(TableDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A function that maps the generic memory range attributes to HW-specific -/// attributes of the MMU. -fn into_mmu_attributes( - attribute_fields: AttributeFields, -) -> register::FieldValue { - use crate::memory::{AccessPermissions, MemAttributes}; - - // Memory attributes - let mut desc = match attribute_fields.mem_attributes { - MemAttributes::CacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - } - MemAttributes::NonCacheableDRAM => { - STAGE1_DESCRIPTOR::SH::InnerShareable - + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE) - } - MemAttributes::Device => { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } - }; - - // Access Permissions - desc += match attribute_fields.acc_perms { - AccessPermissions::ReadOnly => STAGE1_DESCRIPTOR::AP::RO_EL1, - AccessPermissions::ReadWrite => STAGE1_DESCRIPTOR::AP::RW_EL1, - }; - - // Execute Never - desc += if attribute_fields.execute_never { - STAGE1_DESCRIPTOR::PXN::True - } else { - STAGE1_DESCRIPTOR::PXN::False - }; - - desc -} - -/// A Level2 block descriptor with 2 MiB aperture. -/// -/// The output points to physical memory. -struct Lvl2BlockDescriptor(register::FieldValue); - -impl Lvl2BlockDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % TWO_MIB != 0 { - return Err("BlockDescriptor: Address is not 2 MiB aligned."); - } - - let shifted = output_addr >> TWO_MIB_SHIFT; - - Ok(Lvl2BlockDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// A page descriptor with 4 KiB aperture. -/// -/// The output points to physical memory. -struct PageDescriptor(register::FieldValue); - -impl PageDescriptor { - fn new( - output_addr: usize, - attribute_fields: AttributeFields, - ) -> Result { - if output_addr % FOUR_KIB != 0 { - return Err("PageDescriptor: Address is not 4 KiB aligned."); - } - - let shifted = output_addr >> FOUR_KIB_SHIFT; - - Ok(PageDescriptor( - STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::AF::True - + into_mmu_attributes(attribute_fields) - + STAGE1_DESCRIPTOR::TYPE::Table - + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(shifted as u64), - )) - } - - fn value(&self) -> u64 { - self.0.value - } -} - -/// Constants for indexing the MAIR_EL1. -#[allow(dead_code)] -mod mair { - pub const DEVICE: u64 = 0; - pub const NORMAL: u64 = 1; - pub const NORMAL_NON_CACHEABLE: u64 = 2; -} - -/// Setup function for the MAIR_EL1 register. -fn set_up_mair() { - // 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, - ); -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for [u64; 512] { - fn base_addr_u64(&self) -> u64 { - self as *const u64 as u64 - } - - fn base_addr_usize(&self) -> usize { - self as *const u64 as usize - } -} - -const NUM_ENTRIES_4KIB: usize = 512; - -// A wrapper struct is needed here so that the align attribute can be used. -#[repr(C)] -#[repr(align(4096))] -struct PageTable { - entries: [u64; NUM_ENTRIES_4KIB], -} - -/// The LVL2 page table containng the 2 MiB entries. -static mut LVL2_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// The LVL3 page table containing the 4 KiB entries. -/// -/// The first entry of the LVL2_TABLE will forward to this table. -static mut LVL3_TABLE: PageTable = PageTable { - entries: [0; NUM_ENTRIES_4KIB], -}; - -/// Set up identity mapped page tables for the first 1 GiB of address space. -/// -/// The first 2 MiB are 4 KiB granule, the rest 2 MiB. -/// -/// # Safety -/// -/// - User must ensure that the hardware supports the paremeters being set here. -pub unsafe fn init() -> Result<(), &'static str> { - // Prepare the memory attribute indirection register. - set_up_mair(); - - // Point the first 2 MiB of virtual addresses to the follow-up LVL3 - // page-table. - LVL2_TABLE.entries[0] = match TableDescriptor::new(LVL3_TABLE.entries.base_addr_usize()) { - Err(s) => return Err(s), - Ok(d) => d.value(), - }; - - // Fill the rest of the LVL2 (2 MiB) entries as block descriptors. - // - // Notice the skip(1) which makes the iteration start at the second 2 MiB - // block (0x20_0000). - for (block_descriptor_nr, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) { - let virt_addr = block_descriptor_nr << TWO_MIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let block_desc = match Lvl2BlockDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = block_desc.value(); - } - - // Finally, fill the single LVL3 table (4 KiB granule). - for (page_descriptor_nr, entry) in LVL3_TABLE.entries.iter_mut().enumerate() { - let virt_addr = page_descriptor_nr << FOUR_KIB_SHIFT; - - let (output_addr, attribute_fields) = match get_virt_addr_properties(virt_addr) { - Err(s) => return Err(s), - Ok((a, b)) => (a, b), - }; - - let page_desc = match PageDescriptor::new(output_addr, attribute_fields) { - Err(s) => return Err(s), - Ok(desc) => desc, - }; - - *entry = page_desc.value(); - } - - // Point to the LVL2 table base address in TTBR0. - TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr_u64()); - - // 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); - - Ok(()) -} diff --git a/11_exceptions_groundwork/src/sync.rs b/11_exceptions_groundwork/src/sync.rs deleted file mode 100644 index 3cbc1714..00000000 --- a/11_exceptions_groundwork/src/sync.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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, -} - -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 one at a - // time. - f(unsafe { &mut *self.data.get() }) - } -} diff --git a/11_exceptions_groundwork/src/vectors.S b/11_exceptions_groundwork/src/vectors.S deleted file mode 100644 index b2f294c8..00000000 --- a/11_exceptions_groundwork/src/vectors.S +++ /dev/null @@ -1,113 +0,0 @@ -// -// 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 diff --git a/12_Spinlocks/README.md b/12_Spinlocks/README.md deleted file mode 100644 index 04dffb18..00000000 --- a/12_Spinlocks/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Tutorial 12 - Spinlocks - -Coming soon! - -Will replace the `NullLocks` with real Spinlocks! diff --git a/13_Interrupts/README.md b/13_Interrupts/README.md deleted file mode 100644 index 6cf668f8..00000000 --- a/13_Interrupts/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Tutorial 13 - Interrupts - -Coming soon! diff --git a/LICENSE b/LICENSE index 43728826..38d6ab9b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright (C) 2017 bzt (bztsrc@github) -Copyright (c) 2018 Andre Richter +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 diff --git a/README.md b/README.md index 9e389161..688f6eb6 100644 --- a/README.md +++ b/README.md @@ -1,186 +1,54 @@ -# Bare-metal and Operating System development tutorials in Rust on the Raspberry Pi 3 - -[![Build Status](https://travis-ci.org/rust-embedded/rust-raspi3-OS-tutorials.svg?branch=master)](https://travis-ci.org/rust-embedded/rust-raspi3-OS-tutorials) +# Operating System development tutorials in Rust on the Raspberry Pi ## Notice -**The tutorials are being rewritten [on the v2 branch](https://github.com/rust-embedded/rust-raspi3-OS-tutorials/tree/rewrite_for_v2), so please have a look from time to time. The master branch will only receive updates if something breaks.** +**This is a work-in-progress rewrite, started on September 2019. I will first add +code and minimal READMEs, and later write accompanying full-fledged tutorial +text.** -The rewrite also supports both, the Raspberry Pi 3 and the **Raspberry Pi 4**! +- The code written in these tutorials supports and runs on the **Raspberry Pi 3** and the **Raspberry Pi 4**. + - Tutorials 1 till 5 are groundwork code which only makes sense to run on QEMU. + - Starting with [tutorial 6](06_drivers_gpio_uart), you can load and run the kernel on Raspberrys and observe output over UART. +- For practical purposes, the kernel will be a classic [monolith]. +- For editing, I recommend [Visual Studio Code] with the [Rust Language Server] extension. +- Check out the `make doc` command to browse the code with HTML goodness. +- Note that the branch is subject to frequent force pushing. If updates happened + since you last visited, make sure to clone a clean copy to be safe. _Cheers, -Andre_ - -## Introduction - -This repository aims to provide easy reference code for programming bare metal -on the Raspberry Pi 3 in the [Rust systems programming language]. Emphasis is on -leveraging Rust's zero-overhead abstractions to compile lean code that is -readable, concise and safe (at least as safe as it gets on bare-metal hardware). - -[Rust systems programming language]: https://www.rust-lang.org - -The target audience is **hobby OS developers** who are new to [ARM's 64 bit ARMv8-A -architecture](https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs). -It will give you examples on how to do common Operating Systems tasks, like writing -to the serial console, setting up virtual memory and exception handling and using -various peripherals like a hardware-backed random number generator. - -However, it is *not* a tutorial on how to write a _complete_ OS from start to finish. -Rather, it comprises a set of micro-tutorials that introduce different topics one -after the other. Maybe in the distant future, a meta tutorial that -combines all the resources to a full-fleged kernel that can multi-task some simple -userspace processes is possible, but don't take my word for it. - -## Environment - -This repo tries to put a focus on user friendliness. Therefore, I made some -efforts to eliminate the biggest painpoint in embedded development: _Toolchain -hassles_. - -Users eager to try the code should not be bothered with complicated toolchain -installation/compilation steps. This is achieved by trying to use the standard -Rust toolchain as much as possible, and bridge existing gaps with Docker -containers. Please [install Docker for your distro]. - -The setup consists of the following components: -1. Compiler, linker and binutils are used from Rust nightly. -2. QEMU will be used for emulation, but RPi3 support in QEMU is very fresh and has not landed in most of the pre-packaged versions of popular distributions. [This] container will provide it ready to go. - -Please notice that you won't need to download or prepare the containers -upfront. As long as you have docker installed, they will be pulled automatically -the first time the Makefile needs them. +[Andre](https://github.com/andre-richter)_ -If you want to know more about docker and peek at the the containers used in these -tutorials, please refer [to the repository's docker folder](docker). - -[install Docker for your distro]: https://www.docker.com/community-edition#/download -[This]: https://github.com/andre-richter/docker-raspi3-qemu + [monolith]: https://en.wikipedia.org/wiki/Monolithic_kernel + [Visual Studio Code]: https://code.visualstudio.com + [Rust Language Server]: https://github.com/rust-lang/rls ## Prerequisites Before you can start, you'll need a suitable Rust toolchain. ```bash -curl https://sh.rustup.rs -sSf | sh -s -- \ - --default-toolchain nightly \ - --component rust-src llvm-tools-preview clippy rustfmt +curl https://sh.rustup.rs -sSf \ + | \ + sh -s -- \ + --default-toolchain nightly \ + --component rust-src llvm-tools-preview clippy rustfmt rls rust-analysis cargo install cargo-xbuild cargo-binutils ``` -Additionally, a Micro SD card with [firmware -files](https://github.com/raspberrypi/firmware/tree/master/boot) on a FAT -filesystem is needed. +## USB Serial -I recommend to get a [Micro SD card USB -adapter](http://media.kingston.com/images/products/prodReader-FCR-MRG2-img.jpg) -(many manufacturers ship SD cards with such an adapter), so that you can connect -the card to any desktop computer just like an USB stick, no special card reader -interface required (although many laptops have those these days). +It is highly recommended to get a USB serial debug cable. It also powers the Raspberry once you +connect it, so you don't need extra power over the dedicated power-USB. I use a bunch of +[these](https://www.amazon.de/dp/B0757FQ5CX/ref=cm_sw_r_tw_dp_U_x_ozGRDbVTJAG4Q). -You can create an MBR partitioning scheme on the SD card with an LBA FAT32 (type -0x0C) partition, format it and copy `bootcode.bin`, `start.elf` and `fixup.dat` -onto it. **Delete all other files or booting might not work**. Alternatively, -you can download a raspbian image, `dd` it to the SD card, mount it and delete -the unnecessary .img files. Whichever you prefer. What's important, you'll -create `kernel8.img` with these tutorials which must be copied to the root -directory on the SD card, and no other `.img` files should exists there. +You connect it to the GPIO pins 14/15 as shown beyond. -I'd also recommend to get an [USB serial debug -cable](https://www.adafruit.com/product/954). You connect it to the GPIO pins -14/15. +[Tutorial 6](06_drivers_gpio_uart) is the first where you can use it. Go to the README there +for instructions on how to prepare the SD card to run your self-made kernels from it. ![UART wiring diagram](doc/wiring.png) -Then, run `screen` on your desktop computer like - -```bash -sudo screen /dev/ttyUSB0 115200 -``` - -Exit screen again by pressing ctrl-a ctrl-d - -## About this repository - -The tutorial is basically a combination of two awesome resources. - 1. First of all, it is a fork of [Zoltan Baldaszti]'s awesome [tutorial] on bare metal programming on RPi3 in `C`. - 1. Rust code will be based on his files, READMEs will be adapted, and I might change things here and there if I think it is beneficial. However, credits to this guy plz! - 2. The second props go to [Jorge Aparicio] for ["The Embedonomicon"], from which the boot code is taken. - -[Zoltan Baldaszti]: https://github.com/bztsrc -[tutorial]: https://github.com/bztsrc/raspi3-tutorial -[Jorge Aparicio]: https://github.com/japaric -["The Embedonomicon"]: https://rust-embedded.github.io/embedonomicon/ - -## About the hardware - -There are lots of pages on the internet describing the Raspberry Pi 3 hardware -in detail, so I'll be brief and cover only the basics. - -The board is shipped with a [BCM2837 -SoC](https://github.com/raspberrypi/documentation/tree/master/hardware/raspberrypi/bcm2837) -chip. That includes a - - - VideoCore GPU - - ARM-Cortex-A53 CPU (ARMv8) - - Some MMIO mapped pheripherals. - -Interestingly the CPU is not the main processor on the board. When it's powered -up, first GPU runs. When it's finished with the initialization by executing the -code in bootcode.bin, it will load and execute the start.elf executable. That's -not an ARM executable, but compiled for the GPU. What interests us is that -start.elf looks for different ARM executables, all starting with `kernel` and -ending in `.img`. As we're going to program the CPU in AArch64 mode, we'll need -`kernel8.img` only, which is the last to look for. Once it's loaded, the GPU -triggers the reset line on the ARM processor, which starts executing code at -address 0x80000 (or more precisely at 0, but the GPU puts an ARM jump code there -first). - -The RAM (1G for the Raspberry Pi 3) is shared among the CPU and the GPU, meaning -one can read what the other has written into memory. To avoid confusion, a well -defined, so called [mailbox -interface](https://github.com/raspberrypi/firmware/wiki/Mailboxes) is -established. The CPU writes a message into the mailbox, and tells the GPU to -read it. The GPU (knowing that the message is entirely in memory) interprets it, -and places a response message at the same address. The CPU has to poll the -memory to know when the GPU is finished, and then it can read the response. - -Similarily, all peripherals communicates in memory with the CPU. Each has it's -dedicated memory address starting from 0x3F000000, but it's not in real RAM -(called Memory Mapped IO). Now there's no mailbox for peripherals, instead each -device has it's own protocol. What's common for these devices that their memory -must be read and written in 32 bit units at 4 bytes aligned addresses (so called -words), and each has control/status and data words. Unfortunately Broadcom (the -manufacturer of the SoC chip) is legendary bad at documenting their -products. The best we've got is the BCM2835 documentation, which is close -enough. - -There's also a Memory Management Unit in the CPU which allows creating virtual -address spaces. This can be programmed by specific CPU registers, and care must -be taken when you map these MMIO addresses into a virtual address space. - -Some of the more interesting MMIO addresses are: -``` -0x3F003000 - System Timer -0x3F00B000 - Interrupt controller -0x3F00B880 - VideoCore mailbox -0x3F100000 - Power management -0x3F104000 - Random Number Generator -0x3F200000 - General Purpose IO controller -0x3F201000 - UART0 (serial port, PL011) -0x3F215000 - UART1 (serial port, AUX mini UART) -0x3F300000 - External Mass Media Controller (SD card reader) -0x3F980000 - Universal Serial Bus controller -``` -For more information, see Raspberry Pi firmware wiki and documentation on github. - -https://github.com/raspberrypi - -Good luck and enjoy hacking with your Raspberry! :-) - -Andre - ## License Licensed under the MIT license ([LICENSE-MIT](LICENSE) or http://opensource.org/licenses/MIT). diff --git a/TODO.md b/TODO.md deleted file mode 100644 index ac1ee4d5..00000000 --- a/TODO.md +++ /dev/null @@ -1,22 +0,0 @@ -# TODO - -## Add debugging targets to Makefile - -sudo apt installl gdb-multiarch - -qemu-gdb: - $(DOCKER_CMD) -p 1234:1234 $(UTILS_CONTAINER) $(QEMU_CMD) -s -S - -gdb-multiarch kernel8.img -ex "target remote :1234" -set architecture aarch64 -symbols-file ?? - -combine this with rust-gdb? - -## Find a way to easily switch betwenn release build and local development - -```toml -[patch.crates-io] -cortex-a = { path = "../../cortex-a" } -register = { path = "../../register-rs" } -``` diff --git a/X1_JTAG_boot/.cargo/config b/X1_JTAG_boot/.cargo/config deleted file mode 100644 index de3f84c9..00000000 --- a/X1_JTAG_boot/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-C", "link-arg=-Tlink.ld", - "-C", "target-cpu=cortex-a53", -] diff --git a/X1_JTAG_boot/.vscode/settings.json b/X1_JTAG_boot/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/X1_JTAG_boot/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/X1_JTAG_boot/Cargo.lock b/X1_JTAG_boot/Cargo.lock index b1188c19..9a17339b 100644 --- a/X1_JTAG_boot/Cargo.lock +++ b/X1_JTAG_boot/Cargo.lock @@ -9,33 +9,19 @@ dependencies = [ ] [[package]] -name = "jtag_boot" +name = "kernel" version = "0.1.0" dependencies = [ "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raspi3_boot 0.1.0", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "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.7.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.3" @@ -51,7 +37,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" -"checksum 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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" "checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/X1_JTAG_boot/Cargo.toml b/X1_JTAG_boot/Cargo.toml index 3db9c194..cf0f0636 100644 --- a/X1_JTAG_boot/Cargo.toml +++ b/X1_JTAG_boot/Cargo.toml @@ -1,13 +1,21 @@ [package] -name = "jtag_boot" +name = "kernel" version = "0.1.0" authors = ["Andre Richter "] edition = "2018" -[dependencies] -raspi3_boot = { path = "raspi3_boot" } -cortex-a = "2.3.1" -register = "0.3.3" - [package.metadata.cargo-xbuild] sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } +register = { version = "0.3.*", optional = true } diff --git a/X1_JTAG_boot/Makefile b/X1_JTAG_boot/Makefile index 6e5b8124..849fac9f 100644 --- a/X1_JTAG_boot/Makefile +++ b/X1_JTAG_boot/Makefile @@ -1,69 +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. -# - -TARGET = aarch64-unknown-none-softfloat - -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 jtag_boot.img -RASPBOOT_CMD = raspbootcom -RASPBOOT_DEV = /dev/ttyUSB0 -# RASPBOOT_DEV = /dev/ttyACM0 - - -.PHONY: all qemu raspboot clippy clean objdump nm - -all: clean jtag_boot.img - -target/$(TARGET)/release/jtag_boot: $(SOURCES) - cargo xbuild --target=$(TARGET) --release - -jtag_boot.img: target/$(TARGET)/release/jtag_boot +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_MISC_ARGS = -serial stdio + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu chainboot clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + # RUSTFLAGS="-D warnings -D missing_docs" + $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) cp $< . - $(OBJCOPY) $(OBJCOPY_PARAMS) $< jtag_boot.img + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo "This board is not yet supported for QEMU." +else qemu: all - $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) +endif -raspboot: all - $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) \ - $(RASPBOOT_DEV) jtag_boot.img +chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) clippy: - cargo xclippy --target=$(TARGET) + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) clean: cargo clean +readelf: + readelf -a kernel + objdump: - cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex jtag_boot + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel nm: - cargo nm --target $(TARGET) -- jtag_boot | sort + cargo nm --target $(TARGET) -- kernel | sort diff --git a/X1_JTAG_boot/README.md b/X1_JTAG_boot/README.md index d10287c4..98efb8f4 100644 --- a/X1_JTAG_boot/README.md +++ b/X1_JTAG_boot/README.md @@ -1,4 +1,3 @@ # Xtra 1 - JTAG boot -Not much is happening here. The binary configures the RPi's GPIO pins for `JTAG` -mode and waits for a debugger to connect. +Not much is happening here. The binary just patiently waits for a `JTAG` debugger to connect. diff --git a/X1_JTAG_boot/jtag_boot b/X1_JTAG_boot/jtag_boot deleted file mode 100755 index 9bbaa70c..00000000 Binary files a/X1_JTAG_boot/jtag_boot and /dev/null differ diff --git a/X1_JTAG_boot/jtag_boot.img b/X1_JTAG_boot/jtag_boot.img deleted file mode 100755 index 85fa1834..00000000 Binary files a/X1_JTAG_boot/jtag_boot.img and /dev/null differ diff --git a/X1_JTAG_boot/jtag_boot_rpi3.img b/X1_JTAG_boot/jtag_boot_rpi3.img new file mode 100755 index 00000000..ec8198e9 Binary files /dev/null and b/X1_JTAG_boot/jtag_boot_rpi3.img differ diff --git a/X1_JTAG_boot/jtag_boot_rpi4.img b/X1_JTAG_boot/jtag_boot_rpi4.img new file mode 100755 index 00000000..468ed917 Binary files /dev/null and b/X1_JTAG_boot/jtag_boot_rpi4.img differ diff --git a/X1_JTAG_boot/link.ld b/X1_JTAG_boot/link.ld deleted file mode 100644 index 5c499f89..00000000 --- a/X1_JTAG_boot/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - - .text : - { - KEEP(*(.text.boot)) *(.text .text.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .data : - { - *(.data .data.*) - } - - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} diff --git a/X1_JTAG_boot/raspi3_boot/Cargo.toml b/X1_JTAG_boot/raspi3_boot/Cargo.toml deleted file mode 100644 index 024fd184..00000000 --- a/X1_JTAG_boot/raspi3_boot/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "raspi3_boot" -version = "0.1.0" -authors = ["Andre Richter "] -edition = "2018" - -[dependencies] -cortex-a = "2.7.0" -panic-abort = "0.3.1" -r0 = "0.2.2" diff --git a/X1_JTAG_boot/raspi3_boot/src/lib.rs b/X1_JTAG_boot/raspi3_boot/src/lib.rs deleted file mode 100644 index a6b59ef1..00000000 --- a/X1_JTAG_boot/raspi3_boot/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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) => { - /// # Safety - /// - /// - User must ensure to provide a suitable main function for the - /// platform. - #[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()`. -/// -/// # Safety -/// -/// - Only a single core must be active and running this function. -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(); -} - -/// Entrypoint of the processor. -/// -/// Parks all cores except core0, and then jumps to the internal -/// `reset()` function. -/// -/// # Safety -/// -/// - Linker script must ensure to place this function at `0x80_000`. -#[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 STACK_START: u64 = 0x80_000; - - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { - SP.set(STACK_START); - reset() - } else { - // if not core0, infinitely wait for events - loop { - asm::wfe(); - } - } -} diff --git a/X1_JTAG_boot/src/arch.rs b/X1_JTAG_boot/src/arch.rs new file mode 100644 index 00000000..b1f035c5 --- /dev/null +++ b/X1_JTAG_boot/src/arch.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; diff --git a/X1_JTAG_boot/src/arch/aarch64.rs b/X1_JTAG_boot/src/arch/aarch64.rs new file mode 100644 index 00000000..a44ece9b --- /dev/null +++ b/X1_JTAG_boot/src/arch/aarch64.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +pub mod sync; +mod time; + +use crate::{bsp, interface}; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK { + SP.set(bsp::BOOT_CORE_STACK_START); + crate::runtime_init::init() + } else { + // if not core0, infinitely wait for events + wait_forever() + } +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static TIMER: time::Timer = time::Timer; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Return a reference to a `interface::time::TimeKeeper` implementation. +pub fn timer() -> &'static impl interface::time::Timer { + &TIMER +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} diff --git a/X1_JTAG_boot/src/arch/aarch64/sync.rs b/X1_JTAG_boot/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..dfebc0e1 --- /dev/null +++ b/X1_JTAG_boot/src/arch/aarch64/sync.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/X1_JTAG_boot/src/arch/aarch64/time.rs b/X1_JTAG_boot/src/arch/aarch64/time.rs new file mode 100644 index 00000000..ba43474f --- /dev/null +++ b/X1_JTAG_boot/src/arch/aarch64/time.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Timer primitives. + +use crate::{interface, warn}; +use core::time::Duration; +use cortex_a::regs::*; + +const NS_PER_S: u64 = 1_000_000_000; + +pub struct Timer; + +impl interface::time::Timer for Timer { + fn resoultion(&self) -> Duration { + Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + } + + fn uptime(&self) -> Duration { + let frq: u64 = CNTFRQ_EL0.get() as u64; + let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; + + Duration::from_nanos(current_count / frq) + } + + fn spin_for(&self, duration: Duration) { + // Instantly return on zero. + if duration.as_nanos() == 0 { + return; + } + + // Calculate the register compare value. + let frq = CNTFRQ_EL0.get() as u64; + let x = match frq.checked_mul(duration.as_nanos() as u64) { + None => { + warn!("Spin duration too long, skipping"); + return; + } + Some(val) => val, + }; + let tval = x / NS_PER_S; + + // Check if it is within supported bounds. + let warn: Option<&str> = if tval == 0 { + Some("smaller") + } else if tval > u32::max_value().into() { + Some("bigger") + } else { + None + }; + + if let Some(w) = warn { + warn!( + "Spin duration {} than architecturally supported, skipping", + w + ); + return; + } + + // Set the compare value register. + CNTP_TVAL_EL0.set(tval as u32); + + // Kick off the counting. // Disable timer interrupt. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + + loop { + // ISTATUS will be '1' when cval ticks have passed. Busy-check it. + if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) { + break; + } + } + + // Disable counting again. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); + } +} diff --git a/X1_JTAG_boot/src/bsp.rs b/X1_JTAG_boot/src/bsp.rs new file mode 100644 index 00000000..3db8e14a --- /dev/null +++ b/X1_JTAG_boot/src/bsp.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; diff --git a/X1_JTAG_boot/src/bsp/driver.rs b/X1_JTAG_boot/src/bsp/driver.rs new file mode 100644 index 00000000..c910274e --- /dev/null +++ b/X1_JTAG_boot/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/X1_JTAG_boot/src/bsp/driver/bcm.rs b/X1_JTAG_boot/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..15283aea --- /dev/null +++ b/X1_JTAG_boot/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::PL011Uart; diff --git a/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_gpio.rs b/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_gpio.rs new file mode 100644 index 00000000..f6ef27b8 --- /dev/null +++ b/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::ops; +use register::{mmio::ReadWrite, register_bitfields}; + +// GPIO registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// GPIO Function Select 1 + GPFSEL1 [ + /// Pin 15 + FSEL15 OFFSET(15) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART RX + + ], + + /// Pin 14 + FSEL14 OFFSET(12) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART TX + ] + ], + + /// GPIO Function Select 2 + GPFSEL2 [ + /// Pin 27 + FSEL27 OFFSET(21) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG TMS + ], + + /// Pin 26 + FSEL26 OFFSET(18) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG TDI + ], + + /// Pin 25 + FSEL25 OFFSET(15) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG TCK + ], + + /// Pin 24 + FSEL24 OFFSET(12) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG TDO + ], + + /// Pin 23 + FSEL23 OFFSET(9) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG RTCK + ], + + /// Pin 22 + FSEL22 OFFSET(6) NUMBITS(3)[ + Input = 0b000, + Output = 0b001, + AltFunc4 = 0b011 // JTAG TRST + ] + ], + + /// GPIO Pull-up/down Clock Register 0 + GPPUDCLK0 [ + /// Pin 27 + PUDCLK27 OFFSET(27) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + /// Pin 26 + PUDCLK26 OFFSET(26) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 25 + PUDCLK25 OFFSET(25) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 24 + PUDCLK24 OFFSET(24) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 23 + PUDCLK23 OFFSET(23) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 22 + PUDCLK22 OFFSET(22) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 15 + PUDCLK15 OFFSET(15) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 14 + PUDCLK14 OFFSET(14) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct RegisterBlock { + pub GPFSEL0: ReadWrite, // 0x00 + pub GPFSEL1: ReadWrite, // 0x04 + pub GPFSEL2: ReadWrite, // 0x08 + pub GPFSEL3: ReadWrite, // 0x0C + pub GPFSEL4: ReadWrite, // 0x10 + pub GPFSEL5: ReadWrite, // 0x14 + __reserved_0: u32, // 0x18 + GPSET0: ReadWrite, // 0x1C + GPSET1: ReadWrite, // 0x20 + __reserved_1: u32, // + GPCLR0: ReadWrite, // 0x28 + __reserved_2: [u32; 2], // + GPLEV0: ReadWrite, // 0x34 + GPLEV1: ReadWrite, // 0x38 + __reserved_3: u32, // + GPEDS0: ReadWrite, // 0x40 + GPEDS1: ReadWrite, // 0x44 + __reserved_4: [u32; 7], // + GPHEN0: ReadWrite, // 0x64 + GPHEN1: ReadWrite, // 0x68 + __reserved_5: [u32; 10], // + pub GPPUD: ReadWrite, // 0x94 + pub GPPUDCLK0: ReadWrite, // 0x98 + pub GPPUDCLK1: ReadWrite, // 0x9C +} + +/// The driver's private data. +struct GPIOInner { + base_addr: usize, +} + +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} + +impl interface::driver::JTAGOps for GPIO { + fn enable(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + inner.GPFSEL2.modify( + GPFSEL2::FSEL22::AltFunc4 + + GPFSEL2::FSEL23::AltFunc4 + + GPFSEL2::FSEL24::AltFunc4 + + GPFSEL2::FSEL25::AltFunc4 + + GPFSEL2::FSEL26::AltFunc4 + + GPFSEL2::FSEL27::AltFunc4, + ); + + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.write( + GPPUDCLK0::PUDCLK22::AssertClock + + GPPUDCLK0::PUDCLK23::AssertClock + + GPPUDCLK0::PUDCLK24::AssertClock + + GPPUDCLK0::PUDCLK25::AssertClock + + GPPUDCLK0::PUDCLK26::AssertClock + + GPPUDCLK0::PUDCLK27::AssertClock, + ); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + + Ok(()) + }) + } +} diff --git a/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..c75312c9 --- /dev/null +++ b/X1_JTAG_boot/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields}; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// 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 +} + +/// The driver's mutex protected part. +struct PL011UartInner { + base_addr: usize, + chars_written: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + const fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Wait until we can send. + loop { + if !self.FR.is_set(FR::TXFF) { + break; + } + + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + /// Set up baud rate and characteristics + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| { + // Turn it off temporarily. + inner.CR.set(0); + + inner.ICR.write(ICR::ALL::CLEAR); + inner.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + inner.FBRD.write(FBRD::FBRD.val(3)); + inner + .LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + inner + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + }); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until the TX FIFO empty flag is set. + r.lock(|inner| loop { + if inner.FR.is_set(FR::TXFE) { + break; + } + + arch::nop(); + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Wait until buffer is filled. + loop { + if !inner.FR.is_set(FR::RXFE) { + break; + } + + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + ret + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| loop { + // Read from the RX FIFO until the empty bit is '1'. + if !inner.FR.is_set(FR::RXFE) { + inner.DR.get(); + } else { + break; + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } +} diff --git a/X1_JTAG_boot/src/bsp/rpi.rs b/X1_JTAG_boot/src/bsp/rpi.rs new file mode 100644 index 00000000..d96cf21a --- /dev/null +++ b/X1_JTAG_boot/src/bsp/rpi.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; + +use super::driver; +use crate::interface; + +pub const BOOT_CORE_ID: u64 = 0; +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} + +pub fn jtag() -> &'static impl interface::driver::JTAGOps { + &GPIO +} diff --git a/X1_JTAG_boot/src/bsp/rpi/link.ld b/X1_JTAG_boot/src/bsp/rpi/link.ld new file mode 100644 index 00000000..53b65640 --- /dev/null +++ b/X1_JTAG_boot/src/bsp/rpi/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/X1_JTAG_boot/src/bsp/rpi/memory_map.rs b/X1_JTAG_boot/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..ed617f5e --- /dev/null +++ b/X1_JTAG_boot/src/bsp/rpi/memory_map.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; +} diff --git a/X1_JTAG_boot/src/gpio.rs b/X1_JTAG_boot/src/gpio.rs deleted file mode 100644 index ac23f945..00000000 --- a/X1_JTAG_boot/src/gpio.rs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2019 Andre Richter - * Copyright (c) 2019 Nao Taco - * - * 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 Function Select 2 - GPFSEL2 [ - /// Pin 27 - FSEL27 OFFSET(21) NUMBITS(3)[ - Input = 0b000, - Output = 0b001, - ARM_TMS = 0b011 // JTAG TMS - Alternate function 4 - ], - - /// Pin 26 - FSEL26 OFFSET(18) NUMBITS(3)[ - Input = 0b000, - Output = 0b001, - ARM_TDI = 0b011 // JTAG TDI - Alternate function 4 - ], - - /// Pin 25 - FSEL25 OFFSET(15) NUMBITS(3)[ - Input = 0b000, - Output = 0b001, - ARM_TCK = 0b011 // JTAG TCK - Alternate function 4 - ], - - /// Pin 24 - FSEL24 OFFSET(12) NUMBITS(3)[ // GPIO24 - Input = 0b000, - Output = 0b001, - ARM_TDO = 0b011 // JTAG TDO - Alternate function 4 - ], - - /// Pin 23 - FSEL23 OFFSET(9) NUMBITS(3)[ - Input = 0b000, - Output = 0b001, - ARM_RTCK = 0b011 // JTAG RTCK - Alternate function 4 - ], - - /// Pin 22 - FSEL22 OFFSET(6) NUMBITS(3)[ - Input = 0b000, - Output = 0b001, - ARM_TRST = 0b011 // JTAG TRST - Alternate function 4 - ] - ], - - /// 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: usize, -} - -impl ops::Deref for GPIO { - type Target = RegisterBlock; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl GPIO { - pub fn new(base_addr: usize) -> GPIO { - GPIO { base_addr } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } -} diff --git a/X1_JTAG_boot/src/interface.rs b/X1_JTAG_boot/src/interface.rs new file mode 100644 index 00000000..4a660c76 --- /dev/null +++ b/X1_JTAG_boot/src/interface.rs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + fn write_char(&self, c: char); + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } + + /// JTAG driver operations + pub trait JTAGOps { + /// Enable JTAG. + fn enable(&self) -> Result { + Err(()) + } + } +} + +/// Timekeeping interfaces. +pub mod time { + use core::time::Duration; + + /// Timer functions. + pub trait Timer { + /// The timer's resolution. + fn resoultion(&self) -> Duration; + + /// The uptime since power-on of the device. + /// + /// This includes time consumed by firmware and bootloaders. + fn uptime(&self) -> Duration; + + /// Spin for a given duration. + fn spin_for(&self, duration: Duration); + } +} diff --git a/X1_JTAG_boot/src/main.rs b/X1_JTAG_boot/src/main.rs index 23cbd132..b26ef330 100644 --- a/X1_JTAG_boot/src/main.rs +++ b/X1_JTAG_boot/src/main.rs @@ -1,65 +1,70 @@ -/* - * 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. - */ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter -#![no_std] -#![no_main] +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] -const MMIO_BASE: usize = 0x3F00_0000; -const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000; -const MINI_UART_BASE: usize = MMIO_BASE + 0x0021_5000; +//! The `kernel` +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] -mod gpio; -mod mini_uart; +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +mod arch; -pub fn setup_jtag(gpio: &gpio::GPIO) { - gpio.GPFSEL2.modify( - gpio::GPFSEL2::FSEL27::ARM_TMS - + gpio::GPFSEL2::FSEL26::ARM_TDI - + gpio::GPFSEL2::FSEL25::ARM_TCK - + gpio::GPFSEL2::FSEL24::ARM_TDO - + gpio::GPFSEL2::FSEL23::ARM_RTCK - + gpio::GPFSEL2::FSEL22::ARM_TRST, - ); -} +// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; -fn kernel_entry() -> ! { - let gpio = gpio::GPIO::new(GPIO_BASE); +// Conditionally includes the selected `BSP` code. +mod bsp; - //------------------------------------------------------------ - // Instantiate MiniUart - //------------------------------------------------------------ - let mini_uart = mini_uart::MiniUart::new(MINI_UART_BASE); - mini_uart.init(&gpio); +mod interface; +mod panic_wait; +mod print; - //------------------------------------------------------------ - // Configure JTAG pins - //------------------------------------------------------------ - setup_jtag(&gpio); +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + // This message will only be readable if, at the time of failure, the return value of + // `bsp::console()` is already in functioning state. + panic!("Error loading driver: {}", i.compatible()) + } + } - mini_uart.puts("\n[i] JTAG is live. Please connect.\n"); + bsp::post_driver_init(); - loop {} + // Transition from unsafe to safe. + kernel_main() } -raspi3_boot::entry!(kernel_entry); +/// The main function running after the early init. +fn kernel_main() -> ! { + println!("Parking CPU core. Please connect over JTAG now."); + + arch::wait_forever() +} diff --git a/X1_JTAG_boot/src/mini_uart.rs b/X1_JTAG_boot/src/mini_uart.rs deleted file mode 100644 index db21db49..00000000 --- a/X1_JTAG_boot/src/mini_uart.rs +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 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: usize, -} - -/// 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: usize) -> 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); - } - - /// 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 - pub 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); - } - } -} diff --git a/X1_JTAG_boot/src/panic_wait.rs b/X1_JTAG_boot/src/panic_wait.rs new file mode 100644 index 00000000..5e6d3fa5 --- /dev/null +++ b/X1_JTAG_boot/src/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, println}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + arch::wait_forever() +} diff --git a/X1_JTAG_boot/src/print.rs b/X1_JTAG_boot/src/print.rs new file mode 100644 index 00000000..86ab59b3 --- /dev/null +++ b/X1_JTAG_boot/src/print.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +/// Prints a warning, with newline. +#[macro_export] +macro_rules! warn { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/X1_JTAG_boot/src/runtime_init.rs b/X1_JTAG_boot/src/runtime_init.rs new file mode 100644 index 00000000..4cd0415a --- /dev/null +++ b/X1_JTAG_boot/src/runtime_init.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_init() +} diff --git a/doc/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf b/doc/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf deleted file mode 100644 index 5e8d4d72..00000000 Binary files a/doc/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf and /dev/null differ diff --git a/doc/bcm2386_SoC_datasheet_QA7_rev3.4.pdf b/doc/bcm2386_SoC_datasheet_QA7_rev3.4.pdf deleted file mode 100644 index 598393d3..00000000 Binary files a/doc/bcm2386_SoC_datasheet_QA7_rev3.4.pdf and /dev/null differ diff --git a/doc/dma_0.png b/doc/dma_0.png deleted file mode 100644 index 65c8c619..00000000 Binary files a/doc/dma_0.png and /dev/null differ diff --git a/doc/dma_0.svg b/doc/dma_0.svg deleted file mode 100644 index 8ea0f46a..00000000 --- a/doc/dma_0.svg +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - core0 - - core1 - - core2 - - core3 - - - Cache - - - CPU - - DRAM - - Videocore - - - - diff --git a/doc/jtag_demo.gif b/doc/jtag_demo.gif index faf6c5cc..689620ae 100644 Binary files a/doc/jtag_demo.gif and b/doc/jtag_demo.gif differ diff --git a/doc/page_tables_2MiB.png b/doc/page_tables_2MiB.png deleted file mode 100644 index 930d3b8e..00000000 Binary files a/doc/page_tables_2MiB.png and /dev/null differ diff --git a/doc/page_tables_2MiB.svg b/doc/page_tables_2MiB.svg deleted file mode 100644 index c98d9b4b..00000000 --- a/doc/page_tables_2MiB.svg +++ /dev/null @@ -1,1303 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - 0 - - ... - Device MMIO - - - - ... - 511 - 504 - Device MMIO - - UART_BASE + CR_Offset =0x3F20_1000 + 0x30 = 0x3F20_1030 =0b111111001_000000001000000110000 - - TTBR_EL1 - - LVL2 tablebase address - LVL3 Table base addr - static mut LVL2_TABLE - } - - 0b1_1111_1001 = 505Select entrty 505 ofLVL2 table - Virtual Address: - - APAccessPermissions - 53 - 47 - 9 - 10 - 8 - 6 - 7 - 4 - 1 - 2 - 0 - - - MAIR_EL1index - - TYPE - VALID - - - - SHShareability - AFAccessFlag - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PXNPrivilegedexecutenever - 63 - - - - - - Final 48 Bit Physical Address: 0x3F201030 - 0b1_1111_1001 - - - } - - 0 - 20 - 47 - 21 - The LVL2 Block entry pointsto the start address ofa 2 MiB frame. The last21 Bit of the VA are theindex into the frame. - output address - UART - 505 - - - - - - - - - 21 - - - - 0b1_1111_1001 - 0b1000000110000 - - - - 0 - - - diff --git a/doc/page_tables_4KiB.png b/doc/page_tables_4KiB.png deleted file mode 100644 index 349e9037..00000000 Binary files a/doc/page_tables_4KiB.png and /dev/null differ diff --git a/doc/page_tables_4KiB.svg b/doc/page_tables_4KiB.svg deleted file mode 100644 index 99c1c911..00000000 --- a/doc/page_tables_4KiB.svg +++ /dev/null @@ -1,1566 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - ... - 0 - 127 - Stack - Stack - - - - - ... - Code + RO data - Code + RO data - 128 - - - - - ... - Data and BSS - Data and BSS - - - - ... - Remapped UART - 511 - - - - 0 - - ... - Device MMIO - - - - ... - 511 - 504 - Device MMIO - - REMAPPED_UART_BASE + CR_Offset =0x001F_F000 + 0x30 = 0x001F_F030 =0b000000000_111111111_000000110000 - - TTBR_EL1 - - LVL2 tablebase address - LVL3 Table base addr - static mut LVL2_TABLE - static mut LVL3_TABLE - } - - Select entry 0 ofLVL2 table - - } - Virtual Address: - - 0b1_1111_1111 = 511Select entry 511 ofLVL3 table - - APAccessPermissions - 53 - 12 - 47 - 9 - 10 - 8 - 6 - 7 - 4 - 1 - 2 - 0 - - - MAIR_EL1index - - TYPE - VALID - - - - SHShareability - AFAccessFlag - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PXNPrivilegedexecutenever - 63 - - - - - - Final 48 Bit Physical Address: 0x3F201030 - - - 0x3F201 - - 0x030 - - 0x3F201 - } - - - 0 - 11 - 47 - 12 - Entry contains baseaddress of follow-upLVL3 Table. - The LVL3 PT entry pointsto the start address ofa 4 KiB frame. The last12 Bit of the VA are theindex into the frame. - output address - 1 - - diff --git a/doc/wiring_jtag.fzz b/doc/wiring_jtag.fzz new file mode 100644 index 00000000..d7f89095 Binary files /dev/null and b/doc/wiring_jtag.fzz differ diff --git a/doc/wiring_jtag.png b/doc/wiring_jtag.png index 88f0ba1f..0a3e0db2 100644 Binary files a/doc/wiring_jtag.png and b/doc/wiring_jtag.png differ diff --git a/doc/wiring_jtag.xcf b/doc/wiring_jtag.xcf new file mode 100644 index 00000000..0499589e Binary files /dev/null and b/doc/wiring_jtag.xcf differ diff --git a/docker/raspi3-gdb/Dockerfile b/docker/raspi3-gdb/Dockerfile deleted file mode 100644 index f66681d9..00000000 --- a/docker/raspi3-gdb/Dockerfile +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# ============================================================================= -# -# 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. -# -# ============================================================================= -FROM ubuntu:18.04 - -LABEL maintainer="The Cortex-A team , Andre Richter " - -RUN set -ex; \ - tempPkgs=' \ - ca-certificates \ - wget \ - '; \ - apt-get update; \ - apt-get install -q -y --no-install-recommends \ - $tempPkgs \ - gdb-multiarch \ - ; \ - wget -P ~ git.io/.gdbinit; \ - apt-get purge -y --auto-remove $tempPkgs; \ - apt-get autoremove -q -y; \ - apt-get clean -q -y; \ - rm -rf /var/lib/apt/lists/* - -COPY auto /root/.gdbinit.d/auto diff --git a/docker/raspi3-openocd/Dockerfile b/docker/raspi3-openocd/Dockerfile deleted file mode 100644 index 88ab72d0..00000000 --- a/docker/raspi3-openocd/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# -# ============================================================================= -# -# MIT License -# -# Copyright (c) 2019 Andre Richter -# Copyright (c) 2019 Nao Taco -# -# 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. -# -# ============================================================================= -FROM ubuntu:18.04 - -LABEL maintainer="The Cortex-A team , Andre Richter " - -RUN set -ex; \ - tempPkgs=' \ - automake \ - build-essential \ - ca-certificates \ - git \ - libtool \ - pkg-config \ - '; \ - apt-get update; \ - apt-get install -q -y --no-install-recommends \ - $tempPkgs \ - libusb-1.0.0-dev \ - ; \ - git clone --depth 1 https://git.code.sf.net/p/openocd/code openocd; \ - cd openocd; \ - ./bootstrap; \ - ./configure --enable-ftdi; \ - make; \ - make install; \ - apt-get purge -y --auto-remove $tempPkgs; \ - apt-get autoremove -q -y; \ - apt-get clean -q -y; \ - rm -rf /var/lib/apt/lists/* - -COPY rpi3.cfg /openocd/ - -ENTRYPOINT ["openocd"] -CMD ["-f", "/openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg", "-f", "/openocd/rpi3.cfg"] diff --git a/docker/raspi3-utils/Dockerfile b/docker/raspi3-utils/Dockerfile deleted file mode 100644 index 3abb890e..00000000 --- a/docker/raspi3-utils/Dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# -# ============================================================================= -# -# MIT License -# -# Copyright (c) 2017-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. -# -# ============================================================================= -FROM ubuntu:18.04 - -LABEL maintainer="The Cortex-A team , Andre Richter " - -RUN set -ex; \ - apt-get update; \ - apt-get install -q -y --no-install-recommends \ - build-essential \ - ca-certificates \ - git \ - libglib2.0-dev \ - libpixman-1-dev \ - locales \ - pkg-config \ - python \ - screen \ - tmux \ - ; \ - echo "set -g mouse on" > ~/.tmux.conf; \ - # Cleanup - apt-get autoremove -q -y; \ - apt-get clean -q -y; \ - rm -rf /var/lib/apt/lists/* - -RUN locale-gen en_US.UTF-8 - -ENV LANG=en_US.UTF-8 \ - LANGUAGE=en_US:en \ - LC_ALL=en_US.UTF-8 - -RUN git clone git://git.qemu.org/qemu.git; \ - cd qemu; \ - ./configure --target-list=aarch64-softmmu --enable-modules \ - --enable-tcg-interpreter --enable-debug-tcg \ - --python=/usr/bin/python2.7; \ - make; \ - make install; \ - cd ..; \ - rm -rf qemu - -RUN git clone https://github.com/andre-richter/raspbootin.git; \ - cd raspbootin/raspbootcom; \ - make; \ - cp raspbootcom /usr/bin; \ - cd ../..; \ - rm -rf raspbootin diff --git a/docker/rustembedded-osdev-utils/Dockerfile b/docker/rustembedded-osdev-utils/Dockerfile new file mode 100644 index 00000000..34922eda --- /dev/null +++ b/docker/rustembedded-osdev-utils/Dockerfile @@ -0,0 +1,74 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2017-2019 Andre Richter +## Copyright (c) 2019 Nao Taco +FROM ubuntu:18.04 + +LABEL maintainer="The resources team , Andre Richter " + +RUN set -ex; \ + tempPkgs=' \ + automake \ + build-essential \ + ca-certificates \ + git \ + libglib2.0-dev \ + libpixman-1-dev \ + libtool \ + pkg-config \ + wget \ + '; \ + apt-get update; \ + apt-get install -q -y --no-install-recommends \ + $tempPkgs \ + # persistent packages + gdb-multiarch \ + libusb-1.0.0-dev \ + python \ + locales \ + ; \ + # QEMU + git clone --depth 1 git://git.qemu.org/qemu.git; \ + cd qemu; \ + ./configure --target-list=aarch64-softmmu --enable-modules \ + --enable-tcg-interpreter --enable-debug-tcg \ + --python=/usr/bin/python2.7; \ + make; \ + make install; \ + cd ..; \ + rm -rf qemu; \ + # Raspbootcom + git clone --depth 1 https://github.com/andre-richter/raspbootin.git; \ + cd raspbootin/raspbootcom; \ + make; \ + cp raspbootcom /usr/bin; \ + cd ../..; \ + rm -rf raspbootin; \ + # Openocd + git clone --depth 1 https://git.code.sf.net/p/openocd/code openocd; \ + cd openocd; \ + ./bootstrap; \ + ./configure --enable-ftdi; \ + make; \ + make install; \ + # GDB + wget -P ~ git.io/.gdbinit; \ + # Cleanup + apt-get purge -y --auto-remove $tempPkgs; \ + apt-get autoremove -q -y; \ + apt-get clean -q -y; \ + rm -rf /var/lib/apt/lists/* + +# Locales +RUN locale-gen en_US.UTF-8 + +ENV LANG=en_US.UTF-8 \ + LANGUAGE=en_US:en \ + LC_ALL=en_US.UTF-8 + +# Openocd +COPY rpi3.cfg /openocd/ +COPY rpi4.cfg /openocd/ + +# GDB +COPY auto /root/.gdbinit.d/auto diff --git a/docker/raspi3-gdb/auto b/docker/rustembedded-osdev-utils/auto similarity index 100% rename from docker/raspi3-gdb/auto rename to docker/rustembedded-osdev-utils/auto diff --git a/docker/raspi3-openocd/rpi3.cfg b/docker/rustembedded-osdev-utils/rpi3.cfg similarity index 100% rename from docker/raspi3-openocd/rpi3.cfg rename to docker/rustembedded-osdev-utils/rpi3.cfg diff --git a/docker/rustembedded-osdev-utils/rpi4.cfg b/docker/rustembedded-osdev-utils/rpi4.cfg new file mode 100644 index 00000000..68b24562 --- /dev/null +++ b/docker/rustembedded-osdev-utils/rpi4.cfg @@ -0,0 +1,49 @@ +# Script from +# https://www.suse.com/c/debugging-raspberry-pi-3-with-jtag/ +# with minor adaptions. + +transport select jtag + +# we need to enable srst even though we don't connect it +reset_config trst_and_srst + +adapter_khz 1000 +jtag_ntrst_delay 500 + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME rpi4 +} + +# +# Main DAP +# +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x4ba00477 +} + +jtag newtap $_CHIPNAME tap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -enable +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.tap + +set _TARGETNAME $_CHIPNAME.core +set _CTINAME $_CHIPNAME.cti + +set DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000} +set CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000} +set _cores 4 + +for { set _core 0 } { $_core < $_cores } { incr _core } { + + cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 0 \ + -ctibase [lindex $CTIBASE $_core] + + target create $_TARGETNAME$_core aarch64 \ + -dap $_CHIPNAME.dap -coreid $_core \ + -dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core + + $_TARGETNAME$_core configure -event reset-assert-post "aarch64 dbginit" + $_TARGETNAME$_core configure -event gdb-attach { halt } +} diff --git a/emulation/instructions.txt b/emulation/instructions.txt deleted file mode 100644 index e6b1ed87..00000000 --- a/emulation/instructions.txt +++ /dev/null @@ -1,14 +0,0 @@ -######################################################### -# Welcome to the Raspi3 Emulation with # -# MiniUart and PL011UART # -# # -# # -# 1. Use the mouse to select different panes # -# and interact with the UARTS. # -# # -# 2. To quit the emulation: # -# 2.1 Select the pane in which QEMU runs (this one). # -# 2.2 CTRL-C to close qemu. # -######################################################### - -=== QEMU === diff --git a/emulation/qemu_multi_uart.sh b/emulation/qemu_multi_uart.sh deleted file mode 100644 index 7184161f..00000000 --- a/emulation/qemu_multi_uart.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -# -# 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. -# - -tmux new-session -d -s raspi3 & -sleep 1 -tmux new-window -t raspi3:1 -tmux new-window -t raspi3:2 - -tmux send-keys -t raspi3:0 "clear; echo '=== MiniUart ==='; bash /dev/ptmx" C-m -tmux send-keys -t raspi3:1 "clear; printf '=== PL011 Uart ===\n\n';bash /dev/ptmx" C-m - -FIRST=$(ps aux | grep ptmx | sort | awk '{print $7}' | sed '1q;d') -SECOND=$(ps aux | grep ptmx | sort | awk '{print $7}' | sed '2q;d') - -tmux send-keys -t raspi3:2 "clear; cat /emulation/instructions.txt && qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial /dev/$SECOND -serial /dev/$FIRST && tmux kill-session" C-m - -tmux join-pane -s raspi3:0 -t 2 -tmux join-pane -s raspi3:1 -t 2 - -tmux select-pane -t 1 -tmux attach-session -t raspi3 diff --git a/utils/clean_all.rb b/utils/clean_all.rb index a579114b..d8611d02 100755 --- a/utils/clean_all.rb +++ b/utils/clean_all.rb @@ -1,43 +1,25 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# require 'fileutils' +require_relative 'helpers/tutorial_folders.rb' def clean_all - crates = Dir['**/Cargo.toml'].sort! - - crates.each do |x| - next if x.include?('raspi3_boot') + crates = tutorial_folders - x = File.dirname(x) - Dir.chdir(x) do - system('make clean') || exit(1) + crates.each do |x| + x = File.dirname(x) + Dir.chdir(x) do + puts "Cleaning #{x}" + system('rm -rf target') || exit(1) + end end - end - FileUtils.rm_rf('xbuild_sysroot') + FileUtils.rm_rf('xbuild_sysroot') end clean_all if $PROGRAM_NAME == __FILE__ diff --git a/utils/clippy_all.rb b/utils/clippy_all.rb index 98a123b6..96a7376e 100755 --- a/utils/clippy_all.rb +++ b/utils/clippy_all.rb @@ -1,39 +1,23 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# + +require_relative 'helpers/tutorial_folders.rb' def clippy_all - crates = Dir['*/Cargo.toml'].sort! - crates.delete_if { |x| x.include?('bareminimum') } + crates = tutorial_folders - crates.each do |x| - x = File.dirname(x) + crates.each do |x| + x = File.dirname(x) - Dir.chdir(x) do - system('make clippy') + Dir.chdir(x) do + puts "Clippy: #{x}" + system('make clippy') + end end - end end clippy_all if $PROGRAM_NAME == __FILE__ diff --git a/utils/copyrighted.rb b/utils/copyrighted.rb deleted file mode 100644 index b6924f70..00000000 --- a/utils/copyrighted.rb +++ /dev/null @@ -1,63 +0,0 @@ -# -# 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. -# - -def checkin_years(file) - checkin_years = `git --no-pager log \ - --reverse --pretty=format:'%ad' --date=format:'%Y' #{file}` - - checkin_years.split(/\n/).map!(&:to_i) -end - -def copyrighted?(file, is_being_checked_in) - checkin_years = checkin_years(file) - - checkin_years << Time.now.year if is_being_checked_in - - checkin_min = checkin_years.min - checkin_max = checkin_years.max - - copyright_lines = File.readlines(file).grep(/.*Copyright.*/) - - min_seen = false - max_seen = false - copyright_lines.each do |x| - x.scan(/\d\d\d\d/).each do |y| - y = y.to_i - - min_seen = true if y == checkin_min - max_seen = true if y == checkin_max - end - end - - unless min_seen && max_seen - puts file + ': ' - puts "\tHeader: " + copyright_lines.inspect - puts "\tMin year: " + checkin_min.to_s - puts "\tMax year: " + checkin_max.to_s - - return false - end - - true -end diff --git a/utils/diff_all.rb b/utils/diff_all.rb new file mode 100755 index 00000000..4df965f4 --- /dev/null +++ b/utils/diff_all.rb @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2018-2019 Andre Richter + +require 'fileutils' +require_relative 'helpers/tutorial_folders.rb' + +def diff_all + crates = tutorial_folders + + (0..(crates.length - 2)).each do |i| + old = File.dirname(crates[i]) + new = File.dirname(crates[i + 1]) + puts "Diffing #{old} -> #{new}" + system("bash utils/helpers/diff_tut_folders.bash #{old} #{new}") + end +end + +diff_all if $PROGRAM_NAME == __FILE__ diff --git a/utils/fmt_all.rb b/utils/fmt_all.rb index 8b4e4c82..9f63af4a 100755 --- a/utils/fmt_all.rb +++ b/utils/fmt_all.rb @@ -1,38 +1,23 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# + +require_relative 'helpers/tutorial_folders.rb' def fmt_all - crates = Dir['**/Cargo.toml'].sort! + crates = tutorial_folders - crates.each do |x| - x = File.dirname(x) + crates.each do |x| + x = File.dirname(x) - Dir.chdir(x) do - system('cargo fmt') + Dir.chdir(x) do + puts "Formatting #{x}" + system('cargo fmt') + end end - end end fmt_all if $PROGRAM_NAME == __FILE__ diff --git a/utils/helpers/copyrighted.rb b/utils/helpers/copyrighted.rb new file mode 100644 index 00000000..8c500098 --- /dev/null +++ b/utils/helpers/copyrighted.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2018-2019 Andre Richter + +def checkin_years(file) + checkin_years = `git --no-pager log \ + --reverse --pretty=format:'%ad' --date=format:'%Y' #{file}` + + checkin_years.split(/\n/).map!(&:to_i) +end + +def copyrighted?(file, is_being_checked_in) + checkin_years = checkin_years(file) + + checkin_years << Time.now.year if is_being_checked_in + + checkin_min = checkin_years.min + checkin_max = checkin_years.max + + copyright_lines = File.readlines(file).grep(/.*Copyright.*/) + + min_seen = false + max_seen = false + copyright_lines.each do |x| + x.scan(/\d\d\d\d/).each do |y| + y = y.to_i + + min_seen = true if y == checkin_min + max_seen = true if y == checkin_max + end + end + + unless min_seen && max_seen + puts file + ': ' + puts "\tHeader: " + copyright_lines.inspect + puts "\tMin year: " + checkin_min.to_s + puts "\tMax year: " + checkin_max.to_s + + return false + end + + true +end diff --git a/utils/helpers/diff_tut_folders.bash b/utils/helpers/diff_tut_folders.bash new file mode 100755 index 00000000..de2e1121 --- /dev/null +++ b/utils/helpers/diff_tut_folders.bash @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2018-2019 Andre Richter + +DIFF=$( + diff -uNr \ + -x README.md \ + -x kernel \ + -x kernel8.img \ + -x Cargo.lock \ + -x target \ + $1 $2 \ + | sed -r "s/[12][90][127][90]-.*//g" \ + | sed -r "s/[[:space:]]*$//g" \ + | sed -r "s/%/modulo/g" \ + | sed -r "s/diff -uNr -x README.md -x kernel -x kernel8.img -x Cargo.lock -x target/\ndiff -uNr/g" + ) + +HEADER="## Diff to previous" +ORIGINAL=$( + cat $2/README.md \ + | sed -rn "/$HEADER/q;p" + ) + +echo "$ORIGINAL" > "$2/README.md" +printf "\n$HEADER\n" >> "$2/README.md" +printf "\`\`\`diff\n" >> "$2/README.md" +echo "$DIFF" >> "$2/README.md" +printf "\n\`\`\`\n" >> "$2/README.md" diff --git a/utils/helpers/tutorial_folders.rb b/utils/helpers/tutorial_folders.rb new file mode 100755 index 00000000..d96f5b74 --- /dev/null +++ b/utils/helpers/tutorial_folders.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2018-2019 Andre Richter + +require 'fileutils' + +def tutorial_folders + crates = Dir['**/Cargo.toml'] + + crates.delete_if do |x| + !/[0-9][0-9]/.match?(x[0..1]) + end + + crates.sort! +end + +puts tutorial_folders if $PROGRAM_NAME == __FILE__ diff --git a/utils/make_all.rb b/utils/make_all.rb index 5d53187f..dd5290e4 100755 --- a/utils/make_all.rb +++ b/utils/make_all.rb @@ -1,43 +1,25 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# -def make_all - crates = Dir['**/Cargo.toml'].sort! +require_relative 'helpers/tutorial_folders.rb' - crates.each do |x| - next if x.include?('raspi3_boot') +def make_all + crates = tutorial_folders - x = File.dirname(x) - puts "\n\n" + x.to_s + "\n\n" - Dir.chdir(x) do - unless system('make') - puts "\n\nBuild failed!" - exit(1) # Exit with error code - end + crates.each do |x| + x = File.dirname(x) + puts "\n\n" + x.to_s + "\n\n" + Dir.chdir(x) do + unless system('make') + puts "\n\nBuild failed!" + exit(1) # Exit with error code + end + end end - end end make_all if $PROGRAM_NAME == __FILE__ diff --git a/utils/make_panic_test.rb b/utils/make_panic_test.rb deleted file mode 100755 index 33ee87fa..00000000 --- a/utils/make_panic_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env ruby -# -# 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. -# - -def make_panic_test - crates = Dir['**/Cargo.toml'].sort! - - crates.each do |x| - next if x.include?('raspi3_boot') - - x = File.dirname(x) - puts "\n" + x.to_s + ':' - Dir.chdir(x) do - system('make nm | grep panic_fmt') - end - end -end - -make_panic_test if $PROGRAM_NAME == __FILE__ diff --git a/utils/raspboot.bash b/utils/raspboot.bash new file mode 100755 index 00000000..5b1e7f6f --- /dev/null +++ b/utils/raspboot.bash @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2018-2019 Andre Richter + +CONTAINER_UTILS="rustembedded/osdev-utils" + +DOCKER_CMD="docker run -it --rm" +DOCKER_ARG_CURDIR="-v $(pwd):/work -w /work" +DOCKER_ARG_TTY="--privileged -v /dev:/dev" +DOCKER_EXEC_RASPBOOT="raspbootcom /dev/ttyUSB0" + +$DOCKER_CMD \ + $DOCKER_ARG_CURDIR \ + $DOCKER_ARG_TTY \ + $CONTAINER_UTILS \ + $DOCKER_EXEC_RASPBOOT \ + kernel8.img diff --git a/utils/ready_for_publish.rb b/utils/ready_for_publish.rb index beb6c2a4..0beba7af 100755 --- a/utils/ready_for_publish.rb +++ b/utils/ready_for_publish.rb @@ -1,40 +1,23 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# require_relative 'clean_all' require_relative 'clippy_all' require_relative 'fmt_all' require_relative 'make_all' -require_relative 'make_panic_test' require_relative 'sanity_checks' +require_relative 'diff_all' clean_all +fmt_all sanity_checks clippy_all clean_all -fmt_all make_all -make_panic_test +diff_all +clean_all diff --git a/utils/sanity_checks.rb b/utils/sanity_checks.rb index 51a4772f..a36721f9 100755 --- a/utils/sanity_checks.rb +++ b/utils/sanity_checks.rb @@ -1,60 +1,44 @@ #!/usr/bin/env ruby -# -# MIT License +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT # # 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. -# -require_relative 'copyrighted' +require_relative 'helpers/copyrighted' +require_relative 'helpers/tutorial_folders.rb' def patched? - crates = Dir['**/Cargo.toml'].sort! + crates = tutorial_folders - crates.each do |f| - unless File.readlines(f).grep(/patch.crates-io/).empty? - puts "#{fb} contains patch.crates-io!" - exit(1) + crates.each do |f| + unless File.readlines(f).grep(/patch.crates-io/).empty? + puts "#{fb} contains patch.crates-io!" + exit(1) + end end - end end def check_old_copyrights - error = false + error = false - sources = Dir.glob('**/*.{S,rs,rb}') + Dir.glob('**/Makefile') + sources = Dir.glob('**/*.{S,rs,rb}') + Dir.glob('**/Makefile') - sources.delete_if do |x| - !system("git ls-files --error-unmatch #{x}", %i[out err] => File::NULL) - end + sources.delete_if do |x| + !system("git ls-files --error-unmatch #{x}", %i[out err] => File::NULL) + end - sources.sort.reverse_each do |f| - error = true unless copyrighted?(f, false) - end + sources.sort.reverse_each do |f| + puts "Checking for copyright: #{f}" + error = true unless copyrighted?(f, false) + end - exit(1) if error + exit(1) if error end def sanity_checks - patched? - check_old_copyrights + patched? + check_old_copyrights end sanity_checks if $PROGRAM_NAME == __FILE__