commit
641d58614a
@ -1,2 +1,3 @@
|
||||
xbuild_sysroot
|
||||
**/target/*
|
||||
**/.gdb_history
|
||||
|
@ -0,0 +1,19 @@
|
||||
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
||||
# configuration file. It makes it possible to enable/disable
|
||||
# certain cops (checks) and to alter their behavior if they accept
|
||||
# any parameters. The file can be placed either in your home
|
||||
# directory or in some project directory.
|
||||
#
|
||||
# RuboCop will start looking for the configuration file in the directory
|
||||
# where the inspected file is and continue its way up to the root directory.
|
||||
#
|
||||
# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md
|
||||
|
||||
Layout/IndentationWidth:
|
||||
Width: 4
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 100
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 20
|
@ -0,0 +1,9 @@
|
||||
newline_style = "Unix"
|
||||
edition = "2018"
|
||||
merge_imports = true
|
||||
format_code_in_doc_comments = true
|
||||
normalize_comments = true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
report_fixme = "Always"
|
||||
report_todo = "Always"
|
@ -1,36 +0,0 @@
|
||||
language: rust
|
||||
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- andre.o.richter@gmail.com
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- ruby
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- os: osx
|
||||
|
||||
cache: cargo
|
||||
|
||||
before_script:
|
||||
- rustup component add rust-src llvm-tools-preview
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-xbuild || cargo install cargo-xbuild)
|
||||
- (test -x $HOME/.cargo/bin/cargo-objcopy || cargo install cargo-binutils)
|
||||
- cargo install-update -a
|
||||
|
||||
script:
|
||||
- ruby utils/make_all.rb
|
@ -1,5 +0,0 @@
|
||||
[target.aarch64-unknown-none-softfloat]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.ld",
|
||||
"-C", "target-cpu=cortex-a53",
|
||||
]
|
@ -1,16 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "kernel8"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-abort"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f"
|
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "kernel8"
|
||||
version = "0.1.0"
|
||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
panic-abort = "0.3.1"
|
||||
|
||||
[package.metadata.cargo-xbuild]
|
||||
sysroot_path = "../xbuild_sysroot"
|
@ -1,68 +0,0 @@
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
TARGET = aarch64-unknown-none-softfloat
|
||||
|
||||
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
|
||||
|
||||
|
||||
XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release
|
||||
CARGO_OUTPUT = target/$(TARGET)/release/kernel8
|
||||
|
||||
OBJCOPY = cargo objcopy --
|
||||
OBJCOPY_PARAMS = --strip-all -O binary
|
||||
|
||||
CONTAINER_UTILS = andrerichter/raspi3-utils
|
||||
|
||||
DOCKER_CMD = docker run -it --rm
|
||||
DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work
|
||||
|
||||
DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
|
||||
|
||||
.PHONY: all qemu clippy clean objdump nm
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
$(CARGO_OUTPUT): $(SOURCES)
|
||||
$(XRUSTC_CMD)
|
||||
|
||||
kernel8.img: $(CARGO_OUTPUT)
|
||||
cp $< .
|
||||
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
|
||||
|
||||
qemu: all
|
||||
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \
|
||||
$(DOCKER_EXEC_QEMU) -d in_asm
|
||||
|
||||
clippy:
|
||||
cargo xclippy --target=$(TARGET)
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
objdump:
|
||||
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel8
|
||||
|
||||
nm:
|
||||
cargo nm --target $(TARGET) -- kernel8 | sort
|
@ -1,117 +0,0 @@
|
||||
# Tutorial 01 - Bare Minimum
|
||||
|
||||
Okay, we're not going to do much here, just test our toolchain. The resulting
|
||||
kernel8.img should boot on the Raspberry Pi 3, and stop all CPU cores in an
|
||||
infinite waiting loop. You can check that by running
|
||||
|
||||
```console
|
||||
ferris@box:~$ make qemu
|
||||
... some output removed for clearity: ...
|
||||
----------------
|
||||
IN:
|
||||
0x00080000: d503205f wfe
|
||||
0x00080004: 17ffffff b #0x80000
|
||||
```
|
||||
|
||||
## Crate setup
|
||||
|
||||
In this tutorial, we are compiling a kernel that is in the end only executing a
|
||||
single assembly instruction which we program with an assembly file.
|
||||
|
||||
However, since we want to use the toolchain that is delivered with `rustup` as
|
||||
much as possible, we are already setting up a Rust crate. This allows us to use
|
||||
`rustc` and LLVM's `lld.ld` linker to process our assembly file.
|
||||
|
||||
## Target
|
||||
|
||||
The Raspberry Pi 3 features a processor that uses ARM's `AArch64` architecture.
|
||||
Conveniently, Rust already provides a generic target for bare-metal aarch64 code
|
||||
that we can leverage. It is called [aarch64-unknown-none-softfloat].
|
||||
|
||||
[aarch64-unknown-none-softfloat]: https://github.com/rust-lang/rust/blob/master/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs
|
||||
|
||||
In the `Makefile`, we select this target in various places by passing it to
|
||||
cargo using the `--target` cmdline argument.
|
||||
|
||||
Additionally, we provide a config file in `.cargo/config` were we make further
|
||||
specializations:
|
||||
|
||||
```toml
|
||||
[target.aarch64-unknown-none-softfloat]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.ld",
|
||||
"-C", "target-cpu=cortex-a53",
|
||||
]
|
||||
```
|
||||
|
||||
The first line tells rustc to use our custom `link.ld` linker script. The
|
||||
second argument specifies the exact CPU type that is used on Raspberry Pi 3, so
|
||||
that the compiler can optimize for it.
|
||||
|
||||
The target itself tells the compiler already to not use floating point
|
||||
operations, which is hinted by the `-softfloat` appendix. This is a common
|
||||
choice when writing an operating system kernel. If floating point is not
|
||||
explicitly disabled, it is possible that the compiler uses auto-vectorization to
|
||||
optimize, for example, operations on array data structures. This would
|
||||
implicitly result in use of floating point registers and operations. However,
|
||||
since it is very costly to save and restore floating point registers during
|
||||
context-switches, use of fp is usually disabled from the get go to save the
|
||||
cycles and optimize the kernel for fast context switching.
|
||||
|
||||
Since the `aarch64-unknown-none-softfloat` target is not shipped with an associated
|
||||
precompiled standard library, and since we anyways modify the target via the
|
||||
`.cargo/config` file, we are using [cargo-xbuild][xbuild] to compile our own
|
||||
standard library. This way, we can ensure that our bare-metal code is optimized
|
||||
throughout.
|
||||
|
||||
[xbuild]: https://github.com/rust-osdev/cargo-xbuild
|
||||
|
||||
## Linker script `link.ld`
|
||||
|
||||
We just set the base address where our kernel8.img will be loaded, and we put
|
||||
the only section we have there, which is `.text.boot`. Important note, for
|
||||
AArch64 the load address is **0x80_000**, and not **0x80_00** as with AArch32.
|
||||
|
||||
## Makefile
|
||||
|
||||
Our Makefile has a few useful targets:
|
||||
- `kernel8` compiles the crate either in release or debug mode. For the latter,
|
||||
add `DEBUG=1` before invoking make, e.g. `DEBUG=1 make`
|
||||
- `kernel8.img` uses `cargo objcopy` to generate our kernel binary. Citing the [binutils documentation][butils]:
|
||||
- "_When objcopy generates a raw binary file, it will essentially produce a
|
||||
memory dump of the contents of the input object file. All symbols and
|
||||
relocation information will be discarded. The memory dump will start at
|
||||
the load address of the lowest section copied into the output file._"
|
||||
- `qemu` loads our kernel into an emulated RPi3, and shows as output the
|
||||
assembler blocks that are executed. This happens in a docker container.
|
||||
|
||||
[butils]: https://sourceware.org/binutils/docs/binutils/objcopy.html
|
||||
|
||||
## main.rs
|
||||
|
||||
We define the crate to not use the standard library (`#![no_std]`), indicate
|
||||
that it does not have a main function via `#![no_main]`, and also define a stub
|
||||
for the `panic_fmt()` handler, which is a requirement for `no_std` crates. We do
|
||||
this by pulling in the [panic-abort][pa] crate.
|
||||
|
||||
[pa]: https://crates.io/crates/panic-abort
|
||||
|
||||
In summary, we (mis)use `main.rs` as a wrapper to process our assembly file via
|
||||
`rustc`. The assembly file iself is included with the [global_asm!()][gasm]
|
||||
macro.
|
||||
|
||||
[gasm]: https://doc.rust-lang.org/unstable-book/language-features/global-asm.html
|
||||
|
||||
## boot_cores.S
|
||||
|
||||
When the control is passed to kernel8.img, the environment is not ready yet for
|
||||
Rust. Therefore we must implement a small preamble in assembly, no Rust for now.
|
||||
|
||||
All we do is executing [wfe][wfe], an instruction that puts the CPU cores to
|
||||
sleep until an asynchronous event occurs. If that happens, we jump right back to
|
||||
`wfe` again.
|
||||
|
||||
[wfe]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDBGCFH.html
|
||||
|
||||
Note that the CPU has 4 cores. All of them will execute the same infinite loop
|
||||
for now.
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
ENTRY(_boot_cores);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x80000;
|
||||
|
||||
.text :
|
||||
{
|
||||
KEEP(*(.text.boot)) *(.text .text.*)
|
||||
}
|
||||
|
||||
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 bzt (bztsrc@github)
|
||||
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
.section ".text.boot"
|
||||
|
||||
.global _boot_cores
|
||||
|
||||
_boot_cores:
|
||||
1: wfe
|
||||
b 1b
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(global_asm)]
|
||||
|
||||
extern crate panic_abort;
|
||||
|
||||
global_asm!(include_str!("boot_cores.S"));
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"rust.features": [
|
||||
"bsp_rpi3"
|
||||
],
|
||||
"rust.all_targets": false,
|
||||
"editor.rulers": [
|
||||
100
|
||||
],
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
|
@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "kernel8"
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
raspi3_boot = { path = "raspi3_boot" }
|
||||
|
||||
[package.metadata.cargo-xbuild]
|
||||
sysroot_path = "../xbuild_sysroot"
|
||||
|
||||
# The features section is used to select the target board.
|
||||
[features]
|
||||
default = []
|
||||
bsp_rpi3 = []
|
||||
|
||||
[dependencies]
|
||||
|
@ -0,0 +1,76 @@
|
||||
## SPDX-License-Identifier: MIT
|
||||
##
|
||||
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
# Default to the RPi3
|
||||
ifndef BSP
|
||||
BSP = rpi3
|
||||
endif
|
||||
|
||||
# BSP-specific arguments
|
||||
ifeq ($(BSP),rpi3)
|
||||
TARGET = aarch64-unknown-none-softfloat
|
||||
OUTPUT = kernel8.img
|
||||
QEMU_BINARY = qemu-system-aarch64
|
||||
QEMU_MACHINE_TYPE = raspi3
|
||||
QEMU_MISC_ARGS = -d in_asm
|
||||
LINKER_FILE = src/bsp/rpi/link.ld
|
||||
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
|
||||
endif
|
||||
|
||||
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld)
|
||||
|
||||
XRUSTC_CMD = cargo xrustc \
|
||||
--target=$(TARGET) \
|
||||
--features bsp_$(BSP) \
|
||||
--release \
|
||||
-- \
|
||||
-C link-arg=-T$(LINKER_FILE) \
|
||||
$(RUSTC_MISC_ARGS)
|
||||
|
||||
CARGO_OUTPUT = target/$(TARGET)/release/kernel
|
||||
|
||||
OBJCOPY_CMD = cargo objcopy \
|
||||
-- \
|
||||
--strip-all \
|
||||
-O binary
|
||||
|
||||
CONTAINER_UTILS = rustembedded/osdev-utils
|
||||
|
||||
DOCKER_CMD = docker run -it --rm
|
||||
DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work
|
||||
DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT)
|
||||
|
||||
.PHONY: all doc qemu clippy clean readelf objdump nm
|
||||
|
||||
all: clean $(OUTPUT)
|
||||
|
||||
$(CARGO_OUTPUT): $(SOURCES)
|
||||
RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD)
|
||||
|
||||
$(OUTPUT): $(CARGO_OUTPUT)
|
||||
cp $< .
|
||||
$(OBJCOPY_CMD) $< $(OUTPUT)
|
||||
|
||||
doc:
|
||||
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
|
||||
xdg-open target/$(TARGET)/doc/kernel/index.html
|
||||
|
||||
qemu: all
|
||||
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \
|
||||
$(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS)
|
||||
|
||||
clippy:
|
||||
cargo xclippy --target=$(TARGET) --features bsp_$(BSP)
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
readelf:
|
||||
readelf -a kernel
|
||||
|
||||
objdump:
|
||||
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel
|
||||
|
||||
nm:
|
||||
cargo nm --target $(TARGET) -- kernel | sort
|
@ -0,0 +1,43 @@
|
||||
# Tutorial 01 - Wait Forever
|
||||
|
||||
## tl;dr
|
||||
|
||||
Project skeleton is set up; Code just halts all CPU cores executing the kernel code.
|
||||
|
||||
- Toolchain: `cargo xbuild` tools (`xrustc`, `xclippy`) and the
|
||||
`aarch64-unknown-none-softfloat` target are used for building `AArch64`
|
||||
bare-metal code.
|
||||
- `Makefile` targets:
|
||||
- `doc`: Generate documentation.
|
||||
- `qemu`: Run the `kernel` in QEMU
|
||||
- `clippy`
|
||||
- `clean`
|
||||
- `readelf`: Inspect the `ELF` output.
|
||||
- `objdump`: Inspect the assembly.
|
||||
- `nm`: Inspect the symbols.
|
||||
- Code is organized into `kernel`, `arch` and `BSP` (Board Support Package)
|
||||
parts.
|
||||
- Conditional compilation includes respective `arch` and `BSP` according to
|
||||
user-supplied arguments.
|
||||
- Custom `link.ld` linker script.
|
||||
- Load address at `0x80_000`
|
||||
- Only `.text` section.
|
||||
- `main.rs`: Important [inner attributes]:
|
||||
- `#![no_std]`, `#![no_main]`
|
||||
- Assembly `_start()` function that executes `wfe` (Wait For Event), halting all
|
||||
cores that are executing `_start()`.
|
||||
- We (have to) define a `#[panic_handler]` function.
|
||||
- Just waits infinitely for a cpu event.
|
||||
|
||||
[inner attributes]: https://doc.rust-lang.org/reference/attributes.html
|
||||
|
||||
### Give it a try
|
||||
|
||||
In the project folder, invoke QEMU and observe the CPU core spinning on `wfe`:
|
||||
```console
|
||||
make qemu
|
||||
[...]
|
||||
IN:
|
||||
0x00080000: d503205f wfe
|
||||
0x00080004: 17ffffff b #0x80000
|
||||
```
|
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.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "kernel8"
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"raspi3_boot 0.1.0",
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-abort"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "r0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "raspi3_boot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f"
|
||||
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "kernel8"
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
raspi3_boot = { path = "raspi3_boot" }
|
||||
register = "0.3.3"
|
||||
|
||||
[package.metadata.cargo-xbuild]
|
||||
sysroot_path = "../xbuild_sysroot"
|
||||
|
||||
# The features section is used to select the target board.
|
||||
[features]
|
||||
default = []
|
||||
bsp_rpi3 = []
|
||||
|
||||
[dependencies]
|
||||
r0 = "0.2.*"
|
@ -0,0 +1,76 @@
|
||||
## SPDX-License-Identifier: MIT
|
||||
##
|
||||
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
# Default to the RPi3
|
||||
ifndef BSP
|
||||
BSP = rpi3
|
||||
endif
|
||||
|
||||
# BSP-specific arguments
|
||||
ifeq ($(BSP),rpi3)
|
||||
TARGET = aarch64-unknown-none-softfloat
|
||||
OUTPUT = kernel8.img
|
||||
QEMU_BINARY = qemu-system-aarch64
|
||||
QEMU_MACHINE_TYPE = raspi3
|
||||
QEMU_MISC_ARGS = -d in_asm
|
||||
LINKER_FILE = src/bsp/rpi/link.ld
|
||||
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
|
||||
endif
|
||||
|
||||
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld)
|
||||
|
||||
XRUSTC_CMD = cargo xrustc \
|
||||
--target=$(TARGET) \
|
||||
--features bsp_$(BSP) \
|
||||
--release \
|
||||
-- \
|
||||
-C link-arg=-T$(LINKER_FILE) \
|
||||
$(RUSTC_MISC_ARGS)
|
||||
|
||||
CARGO_OUTPUT = target/$(TARGET)/release/kernel
|
||||
|
||||
OBJCOPY_CMD = cargo objcopy \
|
||||
-- \
|
||||
--strip-all \
|
||||
-O binary
|
||||
|
||||
CONTAINER_UTILS = rustembedded/osdev-utils
|
||||
|
||||
DOCKER_CMD = docker run -it --rm
|
||||
DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work
|
||||
DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT)
|
||||
|
||||
.PHONY: all doc qemu clippy clean readelf objdump nm
|
||||
|
||||
all: clean $(OUTPUT)
|
||||
|
||||
$(CARGO_OUTPUT): $(SOURCES)
|
||||
RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD)
|
||||
|
||||
$(OUTPUT): $(CARGO_OUTPUT)
|
||||
cp $< .
|
||||
$(OBJCOPY_CMD) $< $(OUTPUT)
|
||||
|
||||
doc:
|
||||
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
|
||||
xdg-open target/$(TARGET)/doc/kernel/index.html
|
||||
|
||||
qemu: all
|
||||
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \
|
||||
$(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS)
|
||||
|
||||
clippy:
|
||||
cargo xclippy --target=$(TARGET) --features bsp_$(BSP)
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
readelf:
|
||||
readelf -a kernel
|
||||
|
||||
objdump:
|
||||
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel
|
||||
|
||||
nm:
|
||||
cargo nm --target $(TARGET) -- kernel | sort
|
@ -0,0 +1,137 @@
|
||||
# Tutorial 02 - Runtime Init
|
||||
|
||||
## tl;dr
|
||||
|
||||
We are calling into Rust code for the first time and zero the [bss](https://en.wikipedia.org/wiki/.bss) section.
|
||||
Check out `make qemu` again to see the additional code run.
|
||||
|
||||
- More sections in linker script:
|
||||
- `.rodata`, `.data`
|
||||
- `.bss`
|
||||
- `_start()`:
|
||||
- Halt core if core != `core0`.
|
||||
- `core0` jumps to `init()` Rust function.
|
||||
- `init()` in `runtime.rs`
|
||||
- Zeros the `.bss` section.
|
||||
- Calls `kernel_init()`, which calls `panic!()`, which eventually halts
|
||||
`core0` as well.
|
||||
|
||||
## Diff to previous
|
||||
```diff
|
||||
|
||||
diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml
|
||||
--- 01_wait_forever/Cargo.toml
|
||||
+++ 02_runtime_init/Cargo.toml
|
||||
@@ -13,4 +13,4 @@
|
||||
bsp_rpi3 = []
|
||||
|
||||
[dependencies]
|
||||
-
|
||||
+r0 = "0.2.*"
|
||||
|
||||
diff -uNr 01_wait_forever/src/arch/aarch64/start.S 02_runtime_init/src/arch/aarch64/start.S
|
||||
--- 01_wait_forever/src/arch/aarch64/start.S
|
||||
+++ 02_runtime_init/src/arch/aarch64/start.S
|
||||
@@ -7,5 +7,15 @@
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
-1: wfe // Wait for event
|
||||
- b 1b // In case an event happend, jump back to 1
|
||||
+ mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
|
||||
+ and x1, x1, #3 // Clear all bits except [1:0], which hold core id
|
||||
+ cbz x1, 2f // Jump to label 2 if we are core 0
|
||||
+1: wfe // Wait for event
|
||||
+ b 1b // In case an event happend, jump back to 1
|
||||
+2: // If we are here, we are core0
|
||||
+ ldr x1, =_start // Load address of function "_start()"
|
||||
+ mov sp, x1 // Set start of stack to before our code, aka first
|
||||
+ // address before "_start()"
|
||||
+ bl init // Jump to the "init()" kernel function
|
||||
+ b 1b // We should never reach here. But just in case,
|
||||
+ // park this core aswell
|
||||
|
||||
diff -uNr 01_wait_forever/src/bsp/rpi/link.ld 02_runtime_init/src/bsp/rpi/link.ld
|
||||
--- 01_wait_forever/src/bsp/rpi/link.ld
|
||||
+++ 02_runtime_init/src/bsp/rpi/link.ld
|
||||
@@ -13,5 +13,23 @@
|
||||
*(.text._start) *(.text*)
|
||||
}
|
||||
|
||||
+ .rodata :
|
||||
+ {
|
||||
+ *(.rodata*)
|
||||
+ }
|
||||
+
|
||||
+ .data :
|
||||
+ {
|
||||
+ *(.data*)
|
||||
+ }
|
||||
+
|
||||
+ /* Align to 8 byte boundary */
|
||||
+ .bss ALIGN(8):
|
||||
+ {
|
||||
+ __bss_start = .;
|
||||
+ *(.bss*);
|
||||
+ __bss_end = .;
|
||||
+ }
|
||||
+
|
||||
/DISCARD/ : { *(.comment*) }
|
||||
}
|
||||
|
||||
diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs
|
||||
--- 01_wait_forever/src/main.rs
|
||||
+++ 02_runtime_init/src/main.rs
|
||||
@@ -16,9 +16,19 @@
|
||||
// the first function to run.
|
||||
mod arch;
|
||||
|
||||
+// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`.
|
||||
+mod runtime_init;
|
||||
+
|
||||
// Conditionally includes the selected `BSP` code.
|
||||
mod bsp;
|
||||
|
||||
mod panic_wait;
|
||||
|
||||
-// Kernel code coming next tutorial.
|
||||
+/// Early init code.
|
||||
+///
|
||||
+/// # Safety
|
||||
+///
|
||||
+/// - Only a single core must be active and running this function.
|
||||
+unsafe fn kernel_init() -> ! {
|
||||
+ panic!()
|
||||
+}
|
||||
|
||||
diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.rs
|
||||
--- 01_wait_forever/src/runtime_init.rs
|
||||
+++ 02_runtime_init/src/runtime_init.rs
|
||||
@@ -0,0 +1,25 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
+
|
||||
+//! Rust runtime initialization code.
|
||||
+
|
||||
+/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
|
||||
+/// init code.
|
||||
+///
|
||||
+/// # Safety
|
||||
+///
|
||||
+/// - Only a single core must be active and running this function.
|
||||
+#[no_mangle]
|
||||
+pub unsafe extern "C" fn init() -> ! {
|
||||
+ extern "C" {
|
||||
+ // Boundaries of the .bss section, provided by the linker script.
|
||||
+ static mut __bss_start: u64;
|
||||
+ static mut __bss_end: u64;
|
||||
+ }
|
||||
+
|
||||
+ // Zero out the .bss section.
|
||||
+ r0::zero_bss(&mut __bss_start, &mut __bss_end);
|
||||
+
|
||||
+ crate::kernel_init()
|
||||
+}
|
||||
|
||||
```
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Conditional exporting of processor architecture code.
|
||||
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
mod aarch64;
|
||||
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
pub use aarch64::*;
|
@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! AArch64.
|
||||
|
||||
global_asm!(include_str!("aarch64/start.S"));
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Implementation of the kernel's architecture abstraction code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Pause execution on the calling CPU core.
|
||||
#[inline(always)]
|
||||
pub fn wait_forever() -> ! {
|
||||
unsafe {
|
||||
loop {
|
||||
asm!("wfe" :::: "volatile")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
.section ".text._start"
|
||||
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
|
||||
and x1, x1, #3 // Clear all bits except [1:0], which hold core id
|
||||
cbz x1, 2f // Jump to label 2 if we are core 0
|
||||
1: wfe // Wait for event
|
||||
b 1b // In case an event happend, jump back to 1
|
||||
2: // If we are here, we are core0
|
||||
ldr x1, =_start // Load address of function "_start()"
|
||||
mov sp, x1 // Set start of stack to before our code, aka first
|
||||
// address before "_start()"
|
||||
bl init // Jump to the "init()" kernel function
|
||||
b 1b // We should never reach here. But just in case,
|
||||
// park this core aswell
|
@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Conditional exporting of Board Support Packages.
|
||||
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
mod rpi;
|
||||
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
pub use rpi::*;
|
@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Board Support Package for the Raspberry Pi.
|
||||
|
||||
// Coming soon.
|
@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Set current address to the value from which the RPi starts execution */
|
||||
. = 0x80000;
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text._start) *(.text*)
|
||||
}
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
.data :
|
||||
{
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
/* Align to 8 byte boundary */
|
||||
.bss ALIGN(8):
|
||||
{
|
||||
__bss_start = .;
|
||||
*(.bss*);
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
/DISCARD/ : { *(.comment*) }
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
// Rust embedded logo for `make doc`.
|
||||
#![doc(html_logo_url = "https://git.io/JeGIp")]
|
||||
|
||||
//! The `kernel`
|
||||
|
||||
#![feature(asm)]
|
||||
#![feature(global_asm)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
|
||||
// the first function to run.
|
||||
mod arch;
|
||||
|
||||
// `_start()` then calls `runtime_init::init()`, which on completion, jumps to `kernel_init()`.
|
||||
mod runtime_init;
|
||||
|
||||
// Conditionally includes the selected `BSP` code.
|
||||
mod bsp;
|
||||
|
||||
mod panic_wait;
|
||||
|
||||
/// Early init code.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Only a single core must be active and running this function.
|
||||
unsafe fn kernel_init() -> ! {
|
||||
panic!()
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! A panic handler that infinitely waits.
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
crate::arch::wait_forever()
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Rust runtime initialization code.
|
||||
|
||||
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
|
||||
/// init code.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Only a single core must be active and running this function.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn init() -> ! {
|
||||
extern "C" {
|
||||
// Boundaries of the .bss section, provided by the linker script.
|
||||
static mut __bss_start: u64;
|
||||
static mut __bss_end: u64;
|
||||
}
|
||||
|
||||
// Zero out the .bss section.
|
||||
r0::zero_bss(&mut __bss_start, &mut __bss_end);
|
||||
|
||||
crate::kernel_init()
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"rust.features": [
|
||||
"bsp_rpi3"
|
||||
],
|
||||
"rust.all_targets": false,
|
||||
"editor.rulers": [
|
||||
100
|
||||
],
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "kernel8"
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
raspi3_boot = { path = "raspi3_boot" }
|
||||
register = "0.3.3"
|
||||
|
||||
[package.metadata.cargo-xbuild]
|
||||
sysroot_path = "../xbuild_sysroot"
|
||||
|
||||
# The features section is used to select the target board.
|
||||
[features]
|
||||
default = []
|
||||
bsp_rpi3 = []
|
||||
|
||||
[dependencies]
|
||||
r0 = "0.2.*"
|
@ -0,0 +1,76 @@
|
||||
## SPDX-License-Identifier: MIT
|
||||
##
|
||||
## Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
# Default to the RPi3
|
||||
ifndef BSP
|
||||
BSP = rpi3
|
||||
endif
|
||||
|
||||
# BSP-specific arguments
|
||||
ifeq ($(BSP),rpi3)
|
||||
TARGET = aarch64-unknown-none-softfloat
|
||||
OUTPUT = kernel8.img
|
||||
QEMU_BINARY = qemu-system-aarch64
|
||||
QEMU_MACHINE_TYPE = raspi3
|
||||
QEMU_MISC_ARGS = -serial stdio
|
||||
LINKER_FILE = src/bsp/rpi/link.ld
|
||||
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
|
||||
endif
|
||||
|
||||
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld)
|
||||
|
||||
XRUSTC_CMD = cargo xrustc \
|
||||
--target=$(TARGET) \
|
||||
--features bsp_$(BSP) \
|
||||
--release \
|
||||
-- \
|
||||
-C link-arg=-T$(LINKER_FILE) \
|
||||
$(RUSTC_MISC_ARGS)
|
||||
|
||||
CARGO_OUTPUT = target/$(TARGET)/release/kernel
|
||||
|
||||
OBJCOPY_CMD = cargo objcopy \
|
||||
-- \
|
||||
--strip-all \
|
||||
-O binary
|
||||
|
||||
CONTAINER_UTILS = rustembedded/osdev-utils
|
||||
|
||||
DOCKER_CMD = docker run -it --rm
|
||||
DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work
|
||||
DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT)
|
||||
|
||||
.PHONY: all doc qemu clippy clean readelf objdump nm
|
||||
|
||||
all: clean $(OUTPUT)
|
||||
|
||||
$(CARGO_OUTPUT): $(SOURCES)
|
||||
RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD)
|
||||
|
||||
$(OUTPUT): $(CARGO_OUTPUT)
|
||||
cp $< .
|
||||
$(OBJCOPY_CMD) $< $(OUTPUT)
|
||||
|
||||
doc:
|
||||
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
|
||||
xdg-open target/$(TARGET)/doc/kernel/index.html
|
||||
|
||||
qemu: all
|
||||
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \
|
||||
$(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS)
|
||||
|
||||
clippy:
|
||||
cargo xclippy --target=$(TARGET) --features bsp_$(BSP)
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
readelf:
|
||||
readelf -a kernel
|
||||
|
||||
objdump:
|
||||
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel
|
||||
|
||||
nm:
|
||||
cargo nm --target $(TARGET) -- kernel | sort
|
@ -0,0 +1,232 @@
|
||||
# Tutorial 03 - Hacky Hello World
|
||||
|
||||
## tl;dr
|
||||
|
||||
Introducing global `print!()` macros to enable "printf debugging" at the
|
||||
earliest; To keep tutorial length reasonable, printing functions for now "abuse" a
|
||||
QEMU property that lets us use the RPi's `UART` without setting it up properly;
|
||||
Using the real hardware `UART` is enabled step-by-step in following tutorials.
|
||||
|
||||
- `interface.rs` is introduced:
|
||||
- Provides `Traits` for abstracting `kernel` from `BSP` and `arch` code.
|
||||
- Panic handler `print!()`s supplied error messages.
|
||||
- This is showcased in `main()`.
|
||||
|
||||
### Give it a try
|
||||
|
||||
QEMU is no longer run in assembly mode. It will from now on show the output of `UART0`.
|
||||
|
||||
```console
|
||||
make qemu
|
||||
[...]
|
||||
Hello from Rust!
|
||||
Kernel panic: Stopping here.
|
||||
```
|
||||
|
||||
## Diff to previous
|
||||
```diff
|
||||
|
||||
diff -uNr 02_runtime_init/Makefile 03_hacky_hello_world/Makefile
|
||||
--- 02_runtime_init/Makefile
|
||||
+++ 03_hacky_hello_world/Makefile
|
||||
@@ -13,7 +13,7 @@
|
||||
OUTPUT = kernel8.img
|
||||
QEMU_BINARY = qemu-system-aarch64
|
||||
QEMU_MACHINE_TYPE = raspi3
|
||||
- QEMU_MISC_ARGS = -d in_asm
|
||||
+ QEMU_MISC_ARGS = -serial stdio
|
||||
LINKER_FILE = src/bsp/rpi/link.ld
|
||||
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
|
||||
endif
|
||||
|
||||
diff -uNr 02_runtime_init/src/bsp/rpi.rs 03_hacky_hello_world/src/bsp/rpi.rs
|
||||
--- 02_runtime_init/src/bsp/rpi.rs
|
||||
+++ 03_hacky_hello_world/src/bsp/rpi.rs
|
||||
@@ -4,4 +4,35 @@
|
||||
|
||||
//! Board Support Package for the Raspberry Pi.
|
||||
|
||||
-// Coming soon.
|
||||
+use crate::interface;
|
||||
+use core::fmt;
|
||||
+
|
||||
+/// A mystical, magical device for generating QEMU output out of the void.
|
||||
+struct QEMUOutput;
|
||||
+
|
||||
+/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used
|
||||
+/// to implement the `kernel`'s `print!` and `println!` macros.
|
||||
+///
|
||||
+/// See [`src/print.rs`].
|
||||
+///
|
||||
+/// [`src/print.rs`]: ../../print/index.html
|
||||
+impl interface::console::Write for QEMUOutput {
|
||||
+ fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
+ for c in s.chars() {
|
||||
+ unsafe {
|
||||
+ core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+//--------------------------------------------------------------------------------------------------
|
||||
+// Implementation of the kernel's BSP calls
|
||||
+//--------------------------------------------------------------------------------------------------
|
||||
+
|
||||
+/// Returns a ready-to-use `console::Write` implementation.
|
||||
+pub fn console() -> impl interface::console::Write {
|
||||
+ QEMUOutput {}
|
||||
+}
|
||||
|
||||
diff -uNr 02_runtime_init/src/interface.rs 03_hacky_hello_world/src/interface.rs
|
||||
--- 02_runtime_init/src/interface.rs
|
||||
+++ 03_hacky_hello_world/src/interface.rs
|
||||
@@ -0,0 +1,36 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
+
|
||||
+//! Trait definitions for coupling `kernel` and `BSP` code.
|
||||
+//!
|
||||
+//! ```
|
||||
+//! +-------------------+
|
||||
+//! | Interface (Trait) |
|
||||
+//! | |
|
||||
+//! +--+-------------+--+
|
||||
+//! ^ ^
|
||||
+//! | |
|
||||
+//! | |
|
||||
+//! +----------+--+ +--+----------+
|
||||
+//! | Kernel code | | BSP Code |
|
||||
+//! | | | |
|
||||
+//! +-------------+ +-------------+
|
||||
+//! ```
|
||||
+
|
||||
+/// System console operations.
|
||||
+pub mod console {
|
||||
+ /// Console write functions.
|
||||
+ ///
|
||||
+ /// `core::fmt::Write` is exactly what we need for now. Re-export it here because
|
||||
+ /// implementing `console::Write` gives a better hint to the reader about the
|
||||
+ /// intention.
|
||||
+ pub use core::fmt::Write;
|
||||
+
|
||||
+ /// Console read functions.
|
||||
+ pub trait Read {
|
||||
+ fn read_char(&mut self) -> char {
|
||||
+ ' '
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
|
||||
--- 02_runtime_init/src/main.rs
|
||||
+++ 03_hacky_hello_world/src/main.rs
|
||||
@@ -6,9 +6,23 @@
|
||||
#![doc(html_logo_url = "https://git.io/JeGIp")]
|
||||
|
||||
//! The `kernel`
|
||||
+//!
|
||||
+//! The `kernel` is composed by glueing together code from
|
||||
+//!
|
||||
+//! - [Hardware-specific Board Support Packages] (`BSPs`).
|
||||
+//! - [Architecture-specific code].
|
||||
+//! - HW- and architecture-agnostic `kernel` code.
|
||||
+//!
|
||||
+//! using the [`kernel::interface`] traits.
|
||||
+//!
|
||||
+//! [Hardware-specific Board Support Packages]: bsp/index.html
|
||||
+//! [Architecture-specific code]: arch/index.html
|
||||
+//! [`kernel::interface`]: interface/index.html
|
||||
|
||||
#![feature(asm)]
|
||||
+#![feature(format_args_nl)]
|
||||
#![feature(global_asm)]
|
||||
+#![feature(panic_info_message)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
@@ -22,7 +36,9 @@
|
||||
// Conditionally includes the selected `BSP` code.
|
||||
mod bsp;
|
||||
|
||||
+mod interface;
|
||||
mod panic_wait;
|
||||
+mod print;
|
||||
|
||||
/// Early init code.
|
||||
///
|
||||
@@ -30,5 +46,7 @@
|
||||
///
|
||||
/// - Only a single core must be active and running this function.
|
||||
unsafe fn kernel_init() -> ! {
|
||||
- panic!()
|
||||
+ println!("Hello from Rust!");
|
||||
+
|
||||
+ panic!("Stopping here.")
|
||||
}
|
||||
|
||||
diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.rs
|
||||
--- 02_runtime_init/src/panic_wait.rs
|
||||
+++ 03_hacky_hello_world/src/panic_wait.rs
|
||||
@@ -4,9 +4,16 @@
|
||||
|
||||
//! A panic handler that infinitely waits.
|
||||
|
||||
+use crate::{arch, println};
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[panic_handler]
|
||||
-fn panic(_info: &PanicInfo) -> ! {
|
||||
- crate::arch::wait_forever()
|
||||
+fn panic(info: &PanicInfo) -> ! {
|
||||
+ if let Some(args) = info.message() {
|
||||
+ println!("Kernel panic: {}", args);
|
||||
+ } else {
|
||||
+ println!("Kernel panic!");
|
||||
+ }
|
||||
+
|
||||
+ arch::wait_forever()
|
||||
}
|
||||
|
||||
diff -uNr 02_runtime_init/src/print.rs 03_hacky_hello_world/src/print.rs
|
||||
--- 02_runtime_init/src/print.rs
|
||||
+++ 03_hacky_hello_world/src/print.rs
|
||||
@@ -0,0 +1,33 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
+
|
||||
+//! Printing facilities.
|
||||
+
|
||||
+use crate::{bsp, interface};
|
||||
+use core::fmt;
|
||||
+
|
||||
+/// Prints without a newline.
|
||||
+///
|
||||
+/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html
|
||||
+#[macro_export]
|
||||
+macro_rules! print {
|
||||
+ ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*)));
|
||||
+}
|
||||
+
|
||||
+/// Prints with a newline.
|
||||
+///
|
||||
+/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html
|
||||
+#[macro_export]
|
||||
+macro_rules! println {
|
||||
+ () => ($crate::print!("\n"));
|
||||
+ ($($arg:tt)*) => ({
|
||||
+ $crate::print::_print(format_args_nl!($($arg)*));
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+pub fn _print(args: fmt::Arguments) {
|
||||
+ use interface::console::Write;
|
||||
+
|
||||
+ bsp::console().write_fmt(args).unwrap();
|
||||
+}
|
||||
|
||||
```
|
Binary file not shown.
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