Introduce abstraction tut, shuffle tut numbers again

pull/4/head
Andre Richter 6 years ago
parent 09d36953a0
commit 1b046df046

@ -5,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
panic-abort = "0.1.1"
r0 = "0.2.2"
r0 = "0.2.2"

@ -18,7 +18,7 @@ runs on your PC, we will rely on the original [raspbootcom][bootcom] utility.
For convenience, it is already packaged in our `raspi3-utils` docker
container. So if you are running a Linux host, it will be as easy as calling
another Makefile target. It will be included starting with the next tutorial,
`07_random`. You can invoke it with
`07_abstraction`. You can invoke it with
```bash
make raspboot
@ -30,9 +30,9 @@ command to invoke it:
```bash
docker run -it --rm \
--privileged -v /dev/:/dev/ \
-v $PWD:/work -w /work \
raspi3-utils \
raspbootcom /dev/ttyUSB0 kernel8.img
-v $PWD:/work -w /work \
raspi3-utils \
raspbootcom /dev/ttyUSB0 kernel8.img
```
In any case, if your USB device is enumerated differently, adapt accordingly.

@ -1,7 +1,13 @@
[[package]]
name = "cortex-a"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -20,6 +26,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue"
version = "0.1.0"
dependencies = [
"cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -38,6 +45,7 @@ dependencies = [
]
[metadata]
"checksum cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a659ffa30a45a5a05970bb3419796563b6517c03bb68950e7ab4c65dad94680"
"checksum panic-abort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75553c30311427a2d9f24a646fc8cedb00e1da1c6bd1608d71d634184c60392e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"

@ -5,4 +5,5 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.2"
volatile-register = "0.2.0"

@ -0,0 +1,109 @@
# Tutorial 07 - Abstraction
This is a short one regarding code changes, but has lots of text because two
important Rust principles are introduced: Abstraction and modularity.
From a functional perspective, this tutorial is the same as `05_uart0`, but with
the key difference that we threw out all manually crafted assembler. Both the
main and the glue crate do not use `#![feature(global_asm)]` or
`#![feature(asm)]` anymore. Instead, we pulled in the [cortex-a][crate] crate,
which now provides `cortex-a` specific features like register access or safe
wrappers around assembly instructions.
[crate]: https://github.com/andre-richter/cortex-a
For single assembler instructions, we now have the `cortex-a::asm` namespace,
e.g. providing `asm::nop()`.
For registers, there is `cortex-a::register`. For registers like the stack
pointer, which are generally read and written as a whole, there's simple
[read()][sp_read] and [write()][sp_write] functions which take and return
primitive integer types.
[sp_read]: https://docs.rs/cortex-a/0.1.2/cortex_a/register/sp/fn.read.html
[sp_write]: https://docs.rs/cortex-a/0.1.2/cortex_a/register/sp/fn.write.html
Registers that are divided into multiple fields, e.g. `MPIDR_EL1` ([see the ARM
Reference Manual][el1]), on the other hand, are abstracted into their [own
types][mpidr_type] and offer getter and/or setter methods, respectively.
[el1]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.html
[mpidr_type]:https://docs.rs/cortex-a/0.1.2/cortex_a/register/mpidr_el1/struct.MPIDR_EL1.html
To some extent, this namespacing also makes our code more portable. For example,
if we want to reuse parts of it on another processor architecture, we could pull
in the respective crate and change our use-clause from `use cortex-a::asm` to
`use new_architecture::asm`. Of course this also demands that both crates adhere
to a common set of wrappers that provide the same functionality. Assembler and
register instructions like we use them here are actually a weak example. Where
this modular approach can really pay off is for common peripherals like timers
or memory management units, where implementations differ between processors, but
usage is often the same (e.g. setting a timer for x amount of microseconds).
In Rust, we have the [Embedded Devices Working
Group](https://github.com/rust-lang-nursery/embedded-wg), which among other
goals, tries to establish a common set of wrapper- and interface-crates that
introduce abstraction on different levels of the system. Check out the [Awesome
Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust) list for
an overview.
## Glue Code
Like mentioned above, we threw out the `boot_cores.S` assembler file and
replaced it with a Rust function. Why? Because we can, for the fun of it.
```rust
#[link_section = ".text.boot"]
#[no_mangle]
pub extern "C" fn _boot_cores() -> ! {
match register::mpidr_el1::read().core_id() {
0 => unsafe {
register::sp::write(0x80_000);
reset()
},
_ => loop {
// if not core0, infinitely wait for events
asm::wfe();
},
}
}
```
Since this is the first code that the RPi3 will execute, the stack has not been
set up yet. Actually it is this function that will do it for the first
time. Therefore, it is important to check that code generated from this function
does not call any subroutines that need a working stack themselves.
The `register` and `asm` wrappers that we use from the `cortex-a` crate are all
inlined, so we fulfill this requirement. The compilation result of this function
should yield something like the following, where you can see that the stack
pointer is not used apart from ourselves setting it.
```bash
./dockcross-linux-aarch64 bash
[andre:/work] $ aarch64-linux-gnu-objdump -CD kernel8
[...] (Some output omitted)
0000000000080000 <_boot_cores>:
80000: d53800a8 mrs x8, mpidr_el1
80004: f240051f tst x8, #0x3
80008: 54000060 b.eq 80014 <_boot_cores+0x14>
8000c: d503205f wfe
80010: 17ffffff b 8000c <_boot_cores+0xc>
80014: 320d03e8 orr w8, wzr, #0x80000
80018: 9100011f mov sp, x8
8001c: 9400016b bl 805c8 <raspi3_glue::reset::h2a7ad49cd9d2154d>
```
It is important to always manually check this, and not blindly rely on the
compiler.
## Test it
Since this is the first tutorial after we've written our own bootloader over
serial, you can now for the first time test this convenient interface:
```bash
make raspboot
```

Binary file not shown.

@ -4,5 +4,6 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
cortex-a = "0.1.2"
panic-abort = "0.1.1"
r0 = "0.2.2"
r0 = "0.2.2"

@ -25,12 +25,13 @@
#![feature(lang_items)]
#![no_std]
#![feature(global_asm)]
extern crate cortex_a;
extern crate panic_abort;
extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
@ -51,8 +52,7 @@ impl Termination for () {
}
}
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
unsafe fn reset() -> ! {
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
@ -69,5 +69,22 @@ pub unsafe extern "C" fn reset() -> ! {
loop {}
}
// Disable all cores except core 0, and then jump to reset()
global_asm!(include_str!("boot_cores.S"));
/// Entrypoint of the RPi3.
///
/// Parks all cores except core0, and then jumps to the internal
/// `reset()` function, which will call the user's `main()` after
/// initializing the `bss` section.
#[link_section = ".text.boot"]
#[no_mangle]
pub extern "C" fn _boot_cores() -> ! {
match register::mpidr_el1::read().core_id() {
0 => unsafe {
register::sp::write(0x80_000);
reset()
},
_ => loop {
// if not core0, infinitely wait for events
asm::wfe();
},
}
}

@ -22,8 +22,8 @@
* SOFTWARE.
*/
use volatile_register::RW;
use super::MMIO_BASE;
use volatile_register::RW;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;

@ -0,0 +1,85 @@
/*
* 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]
extern crate cortex_a;
extern crate raspi3_glue;
extern crate volatile_register;
const MMIO_BASE: u32 = 0x3F00_0000;
mod gpio;
mod mbox;
mod uart;
use core::sync::atomic::{compiler_fence, Ordering};
fn main() {
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
// set up serial console
if uart.init(&mut mbox).is_err() {
return; // If UART fails, abort early
}
// get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message
mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command
mbox.buffer[3] = 8; // buffer size
mbox.buffer[4] = 8;
mbox.buffer[5] = 0; // clear output buffer
mbox.buffer[6] = 0;
mbox.buffer[7] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which is
// done by a store operation as well).
compiler_fence(Ordering::Release);
// send the message to the GPU and receive answer
let serial_avail = match mbox.call(mbox::channel::PROP) {
Err(_) => false,
Ok(()) => true,
};
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
if serial_avail {
uart.puts("My serial number is: ");
uart.hex(mbox.buffer[6]);
uart.hex(mbox.buffer[5]);
uart.puts("\n");
} else {
uart.puts("Unable to query serial!\n");
}
// echo everything back
loop {
uart.send(uart.getc());
}
}

@ -23,6 +23,7 @@
*/
use super::MMIO_BASE;
use cortex_a::asm;
use volatile_register::{RO, WO};
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
@ -53,6 +54,7 @@ pub mod channel {
// Tags
pub mod tag {
pub const GETSERIAL: u32 = 0x10004;
pub const SETCLKRATE: u32 = 0x38002;
pub const LAST: u32 = 0;
}
@ -96,12 +98,11 @@ impl Mbox {
pub fn call(&mut self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
unsafe {
if ((*self.registers).STATUS.read() & FULL) != FULL {
break;
}
asm!("nop" :::: "volatile");
if (unsafe { (*self.registers).STATUS.read() } & FULL) != FULL {
break;
}
asm::nop();
}
// write the address of our message to the mailbox with channel identifier
@ -115,12 +116,11 @@ impl Mbox {
loop {
// is there a response?
loop {
unsafe {
if ((*self.registers).STATUS.read() & EMPTY) != EMPTY {
break;
}
asm!("nop" :::: "volatile");
if (unsafe { (*self.registers).STATUS.read() } & EMPTY) != EMPTY {
break;
}
asm::nop();
}
let resp: u32 = unsafe { (*self.registers).READ.read() };

@ -23,10 +23,11 @@
*/
use super::MMIO_BASE;
use volatile_register::*;
use mbox;
use gpio;
use core::sync::atomic::{compiler_fence, Ordering};
use cortex_a::asm;
use gpio;
use mbox;
use volatile_register::*;
const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
@ -100,12 +101,12 @@ impl Uart {
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 {
asm!("nop" :::: "volatile");
asm::nop();
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
for _ in 0..150 {
asm!("nop" :::: "volatile");
asm::nop();
}
(*gpio::GPPUDCLK0).write(0);
@ -121,30 +122,28 @@ impl Uart {
/// Send a character
pub fn send(&self, c: char) {
unsafe {
// wait until we can send
loop {
if ((*self.registers).FR.read() & 0x20) != 0x20 {
break;
}
asm!("nop" :::: "volatile");
// wait until we can send
loop {
if (unsafe { (*self.registers).FR.read() } & 0x20) != 0x20 {
break;
}
// write the character to the buffer
(*self.registers).DR.write(c as u32);
asm::nop();
}
// write the character to the buffer
unsafe { (*self.registers).DR.write(c as u32) };
}
/// Receive a character
pub fn getc(&self) -> char {
unsafe {
// wait until something is in the buffer
loop {
if ((*self.registers).FR.read() & 0x10) != 0x10 {
break;
}
asm!("nop" :::: "volatile");
// wait until something is in the buffer
loop {
if (unsafe { (*self.registers).FR.read() } & 0x10) != 0x10 {
break;
}
asm::nop();
}
// read it and return

52
08_random/Cargo.lock generated

@ -0,0 +1,52 @@
[[package]]
name = "cortex-a"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "panic-abort"
version = "0.1.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_glue"
version = "0.1.0"
dependencies = [
"cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "volatile-register"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum cortex-a 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a659ffa30a45a5a05970bb3419796563b6517c03bb68950e7ab4c65dad94680"
"checksum panic-abort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75553c30311427a2d9f24a646fc8cedb00e1da1c6bd1608d71d634184c60392e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

@ -0,0 +1,9 @@
[package]
name = "kernel8"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.2"
volatile-register = "0.2.0"

@ -0,0 +1,72 @@
#
# 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.
#
TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean cross_cont_download kernel8.img
cross_cont_download:
ifeq (,$(wildcard $(CROSS_CONTAINER)))
docker run --rm dockcross/linux-arm64 > $(CROSS_CONTAINER)
chmod +x $(CROSS_CONTAINER)
endif
target/$(TARGET)/debug/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy
clean:
cargo clean
rm -f kernel8

@ -1,4 +1,4 @@
# Tutorial 07 - Hardware Random Number Generator
# Tutorial 08 - Hardware Random Number Generator
This going to be an easy tutorial. We query a number from the (undocumented)
hardware random number generator. You can use this to implement a simple, but

@ -0,0 +1,32 @@
{
"arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"linker-flavor": "ld.lld",
"linker-is-gnu": true,
"pre-link-args": {
"ld.lld": [
"--script=link.ld"
]
},
"llvm-target": "aarch64-unknown-none",
"no-compiler-rt": true,
"features": "+a53,+strict-align",
"max-atomic-width": 128,
"os": "none",
"panic": "abort",
"panic-strategy": "abort",
"relocation-model": "pic",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64",
"disable-redzone": true,
"abi-blacklist": [
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"win64",
"sysv64"
]
}

@ -1,6 +1,5 @@
/*
* 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
@ -24,25 +23,22 @@
*
*/
.section ".text.boot"
SECTIONS
{
. = 0x80000;
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
PROVIDE(_data = .);
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}
_end = .;
.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
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}
__bss_size = (__bss_end - __bss_start)>>3;

@ -0,0 +1,9 @@
[package]
name = "raspi3_glue"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
cortex-a = "0.1.2"
panic-abort = "0.1.1"
r0 = "0.2.2"

@ -0,0 +1,90 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* 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.
*/
#![feature(lang_items)]
#![no_std]
extern crate cortex_a;
extern crate panic_abort;
extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
T: Termination,
{
user_main().report() as isize
}
#[lang = "termination"]
trait Termination {
fn report(self) -> i32;
}
impl Termination for () {
fn report(self) -> i32 {
0
}
}
unsafe fn reset() -> ! {
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
// Boundaries of the .bss section
static mut __bss_start: u32;
static mut __bss_end: u32;
}
// Zeroes the .bss section
r0::zero_bss(&mut __bss_start, &mut __bss_end);
main(0, ptr::null());
loop {}
}
/// Entrypoint of the RPi3.
///
/// Parks all cores except core0, and then jumps to the internal
/// `reset()` function, which will call the user's `main()` after
/// initializing the `bss` section.
#[link_section = ".text.boot"]
#[no_mangle]
pub extern "C" fn _boot_cores() -> ! {
match register::mpidr_el1::read().core_id() {
0 => unsafe {
register::sp::write(0x80_000);
reset()
},
_ => loop {
// if not core0, infinitely wait for events
asm::wfe();
},
}
}

@ -0,0 +1,30 @@
/*
* 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 volatile_register::RW;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;

@ -23,17 +23,17 @@
*/
#![no_std]
#![feature(asm)]
extern crate cortex_a;
extern crate raspi3_glue;
extern crate volatile_register;
const MMIO_BASE: u32 = 0x3F00_0000;
mod mbox;
mod gpio;
mod uart;
mod mbox;
mod rand;
mod uart;
fn main() {
let mut mbox = mbox::Mbox::new();

@ -0,0 +1,138 @@
/*
* 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 cortex_a::asm;
use volatile_register::{RO, WO};
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)]
#[repr(C)]
struct Registers {
READ: RO<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<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 SETCLKRATE: u32 = 0x38002;
pub const LAST: u32 = 0;
}
// Clocks
pub mod clock {
pub const UART: u32 = 0x0_0000_0002;
}
// Responses
mod response {
pub const SUCCESS: u32 = 0x8000_0000;
pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response)
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox
#[repr(C)]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36],
registers: *const Registers,
}
impl Mbox {
pub fn new() -> Mbox {
Mbox {
buffer: [0; 36],
registers: VIDEOCORE_MBOX as *const Registers,
}
}
/// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success
pub fn call(&mut self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if (unsafe { (*self.registers).STATUS.read() } & FULL) != FULL {
break;
}
asm::nop();
}
// write the address of our message to the mailbox with channel identifier
unsafe {
(*self.registers)
.WRITE
.write(((self.buffer.as_mut_ptr() as u32) & !0xF) | (channel & 0xF));
}
// now wait for the response
loop {
// is there a response?
loop {
if (unsafe { (*self.registers).STATUS.read() } & EMPTY) != EMPTY {
break;
}
asm::nop();
}
let resp: u32 = unsafe { (*self.registers).READ.read() };
// is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_mut_ptr() as u32)) {
// is it a valid successful response?
return match self.buffer[1] {
response::SUCCESS => Ok(()),
response::ERROR => Err(MboxError::ResponseError),
_ => Err(MboxError::UnknownError),
};
}
}
}
}

@ -23,6 +23,7 @@
*/
use super::MMIO_BASE;
use cortex_a::asm;
use volatile_register::*;
const RNG_BASE: u32 = MMIO_BASE + 0x104_000;
@ -66,7 +67,7 @@ impl Rng {
break;
}
asm!("nop" :::: "volatile");
asm::nop();
}
}
}

@ -0,0 +1,191 @@
/*
* 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::sync::atomic::{compiler_fence, Ordering};
use cortex_a::asm;
use gpio;
use mbox;
use volatile_register::*;
const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
struct Registers {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
}
pub enum UartError {
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, UartError>;
pub struct Uart {
registers: *const Registers,
}
impl Uart {
pub fn new() -> Uart {
Uart {
registers: UART_BASE as *const Registers,
}
}
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { (*self.registers).CR.write(0) };
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
mbox.buffer[1] = mbox::REQUEST;
mbox.buffer[2] = mbox::tag::SETCLKRATE;
mbox.buffer[3] = 12;
mbox.buffer[4] = 8;
mbox.buffer[5] = mbox::clock::UART; // UART clock
mbox.buffer[6] = 4_000_000; // 4Mhz
mbox.buffer[7] = 0; // skip turbo setting
mbox.buffer[8] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which
// is done by a store operation as well).
compiler_fence(Ordering::Release);
if mbox.call(mbox::channel::PROP).is_err() {
return Err(UartError::MailboxError); // Abort if UART clocks couldn't be set
};
// map UART0 to GPIO pins
unsafe {
(*gpio::GPFSEL1).modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write(0);
(*self.registers).ICR.write(0x7FF); // clear interrupts
(*self.registers).IBRD.write(2); // 115200 baud
(*self.registers).FBRD.write(0xB);
(*self.registers).LCRH.write(0b11 << 5); // 8n1
(*self.registers).CR.write(0x301); // enable Tx, Rx, FIFO
}
Ok(())
}
/// Send a character
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (unsafe { (*self.registers).FR.read() } & 0x20) != 0x20 {
break;
}
asm::nop();
}
// write the character to the buffer
unsafe { (*self.registers).DR.write(c as u32) };
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (unsafe { (*self.registers).FR.read() } & 0x10) != 0x10 {
break;
}
asm::nop();
}
// read it and return
let mut ret = unsafe { (*self.registers).DR.read() 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);
}
}
}
Loading…
Cancel
Save