commit
641d58614a
@ -1,2 +1,3 @@
|
|||||||
xbuild_sysroot
|
xbuild_sysroot
|
||||||
**/target/*
|
**/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]
|
[package]
|
||||||
name = "kernel8"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
raspi3_boot = { path = "raspi3_boot" }
|
|
||||||
|
|
||||||
[package.metadata.cargo-xbuild]
|
[package.metadata.cargo-xbuild]
|
||||||
sysroot_path = "../xbuild_sysroot"
|
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
|
||||||
|
```
|
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,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.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel8"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
name = "r0"
|
name = "r0"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]
|
[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"
|
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
@ -1,12 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kernel8"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
raspi3_boot = { path = "raspi3_boot" }
|
|
||||||
register = "0.3.3"
|
|
||||||
|
|
||||||
[package.metadata.cargo-xbuild]
|
[package.metadata.cargo-xbuild]
|
||||||
sysroot_path = "../xbuild_sysroot"
|
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.
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]
|
[package]
|
||||||
name = "kernel8"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
raspi3_boot = { path = "raspi3_boot" }
|
|
||||||
register = "0.3.3"
|
|
||||||
|
|
||||||
[package.metadata.cargo-xbuild]
|
[package.metadata.cargo-xbuild]
|
||||||
sysroot_path = "../xbuild_sysroot"
|
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.
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
|
||||||
|
],
|
||||||
|
}
|
@ -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…
Reference in New Issue