Merge pull request #35 from rust-embedded/rewrite_for_v2

Push the rewrite to master
pull/37/head
Andre Richter 5 years ago committed by GitHub
commit 641d58614a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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

1
.gitignore vendored

@ -1,2 +1,3 @@
xbuild_sysroot
**/target/*
**/.gdb_history

@ -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

@ -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"

@ -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

@ -1,5 +0,0 @@
[target.aarch64-unknown-none-softfloat]
rustflags = [
"-C", "link-arg=-Tlink.ld",
"-C", "target-cpu=cortex-a53",
]

@ -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"

@ -1,11 +0,0 @@
[package]
name = "kernel8"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
panic-abort = "0.3.1"
[package.metadata.cargo-xbuild]
sysroot_path = "../xbuild_sysroot"

@ -1,68 +0,0 @@
#
# MIT License
#
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
#
# 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

@ -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.

@ -1,37 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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*) }
}

@ -1,33 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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

@ -1,31 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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"));

@ -0,0 +1,10 @@
{
"editor.formatOnSave": true,
"rust.features": [
"bsp_rpi3"
],
"rust.all_targets": false,
"editor.rulers": [
100
],
}

@ -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"

@ -1,11 +1,16 @@
[package]
name = "kernel8"
name = "kernel"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
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]

@ -0,0 +1,76 @@
## SPDX-License-Identifier: MIT
##
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
# 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

@ -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
```

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of processor architecture code.
#[cfg(feature = "bsp_rpi3")]
mod aarch64;
#[cfg(feature = "bsp_rpi3")]
pub use aarch64::*;

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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")
}
}
}

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
.section ".text._start"
.global _start
_start:
1: wfe // Wait for event
b 1b // In case an event happend, jump back to 1

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of Board Support Packages.
#[cfg(feature = "bsp_rpi3")]
mod rpi;
#[cfg(feature = "bsp_rpi3")]
pub use rpi::*;

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
// Coming soon.

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*/
SECTIONS
{
/* Set current address to the value from which the RPi starts execution */
. = 0x80000;
.text :
{
*(.text._start) *(.text*)
}
/DISCARD/ : { *(.comment*) }
}

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
// 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.

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! A panic handler that infinitely waits.
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
crate::arch::wait_forever()
}

@ -1,5 +0,0 @@
[target.aarch64-unknown-none-softfloat]
rustflags = [
"-C", "link-arg=-Tlink.ld",
"-C", "target-cpu=cortex-a53",
]

@ -1,68 +0,0 @@
#
# MIT License
#
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
#
# 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

@ -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! :-)

Binary file not shown.

@ -1,55 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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*) }
}

@ -1,9 +0,0 @@
[package]
name = "raspi3_boot"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
panic-abort = "0.3.1"
r0 = "0.2.2"

@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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

@ -1,79 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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"));

@ -1,32 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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);

@ -0,0 +1,10 @@
{
"editor.formatOnSave": true,
"rust.features": [
"bsp_rpi3"
],
"rust.all_targets": false,
"editor.rulers": [
100
],
}

@ -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"

@ -1,12 +1,16 @@
[package]
name = "kernel8"
name = "kernel"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
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.*"

@ -0,0 +1,76 @@
## SPDX-License-Identifier: MIT
##
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
# 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

@ -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 <andre.o.richter@gmail.com>
+
+//! 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()
+}
```

Binary file not shown.

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of processor architecture code.
#[cfg(feature = "bsp_rpi3")]
mod aarch64;
#[cfg(feature = "bsp_rpi3")]
pub use aarch64::*;

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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")
}
}
}

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
.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

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of Board Support Packages.
#[cfg(feature = "bsp_rpi3")]
mod rpi;
#[cfg(feature = "bsp_rpi3")]
pub use rpi::*;

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
// Coming soon.

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*/
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*) }
}

@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
// 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!()
}

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! A panic handler that infinitely waits.
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
crate::arch::wait_forever()
}

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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()
}

@ -0,0 +1,10 @@
{
"editor.formatOnSave": true,
"rust.features": [
"bsp_rpi3"
],
"rust.all_targets": false,
"editor.rulers": [
100
],
}

@ -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"

@ -1,12 +1,16 @@
[package]
name = "kernel8"
name = "kernel"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
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.*"

@ -0,0 +1,76 @@
## SPDX-License-Identifier: MIT
##
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
# 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

@ -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 <andre.o.richter@gmail.com>
+
+//! 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 <andre.o.richter@gmail.com>
+
+//! 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();
+}
```

Binary file not shown.

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of processor architecture code.
#[cfg(feature = "bsp_rpi3")]
mod aarch64;
#[cfg(feature = "bsp_rpi3")]
pub use aarch64::*;

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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")
}
}
}

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
.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

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! Conditional exporting of Board Support Packages.
#[cfg(feature = "bsp_rpi3")]
mod rpi;
#[cfg(feature = "bsp_rpi3")]
pub use rpi::*;

@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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 {}
}

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*/
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*) }
}

@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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 {
' '
}
}
}

@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
// 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.")
}

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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()
}

@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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();
}

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
//! 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()
}

@ -1,5 +0,0 @@
[target.aarch64-unknown-none-softfloat]
rustflags = [
"-C", "link-arg=-Tlink.ld",
"-C", "target-cpu=cortex-a53",
]

@ -1,68 +0,0 @@
#
# MIT License
#
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
#
# 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

@ -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.

Binary file not shown.

Binary file not shown.

@ -1,55 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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*) }
}

@ -1,9 +0,0 @@
[package]
name = "raspi3_boot"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
panic-abort = "0.3.1"
r0 = "0.2.2"

@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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

@ -1,79 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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"));

@ -1,73 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -1,51 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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);

@ -1,235 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08
AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
AUX_MU_MCR: WriteOnly<u32>, // 0x50
AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
__reserved_2: [u32; 2], // 0x58
AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
__reserved_3: u32, // 0x64
AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 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);
}
}
}

@ -1,5 +0,0 @@
[target.aarch64-unknown-none-softfloat]
rustflags = [
"-C", "link-arg=-Tlink.ld",
"-C", "target-cpu=cortex-a53",
]

@ -1,68 +0,0 @@
#
# MIT License
#
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
#
# 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

@ -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.

Binary file not shown.

Binary file not shown.

@ -1,55 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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*) }
}

@ -1,9 +0,0 @@
[package]
name = "raspi3_boot"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
panic-abort = "0.3.1"
r0 = "0.2.2"

@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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

@ -1,79 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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"));

@ -1,73 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -1,81 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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);

@ -1,156 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
__reserved_1: u32, // 0x1C
WRITE: WriteOnly<u32>, // 0x20
}
// Custom errors
pub enum MboxError {
ResponseError,
UnknownError,
}
pub type Result<T> = ::core::result::Result<T, MboxError>;
// 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),
};
}
}
}
}

@ -1,255 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* 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<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08
AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
AUX_MU_MCR: WriteOnly<u32>, // 0x50
AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
__reserved_2: [u32; 2], // 0x58
AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
__reserved_3: u32, // 0x64
AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 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);
}
}
}

@ -0,0 +1,10 @@
{
"editor.formatOnSave": true,
"rust.features": [
"bsp_rpi3"
],
"rust.all_targets": false,
"editor.rulers": [
100
],
}

@ -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"

@ -0,0 +1,19 @@
[package]
name = "kernel"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
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 }

@ -0,0 +1,76 @@
## SPDX-License-Identifier: MIT
##
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
# 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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save