Add code for global println lesson

pull/15/head
Andre Richter 5 years ago
parent 4f4e88cfbe
commit c6e6ccd2a4
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

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

@ -0,0 +1,55 @@
[[package]]
name = "cortex-a"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"cortex-a 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_boot 0.1.0",
"register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "panic-abort"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "r0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "raspi3_boot"
version = "0.1.0"
dependencies = [
"cortex-a 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "register"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tock-registers"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum cortex-a 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97867bac786c0a2912f7df981bdb8b6ea109a2422c22b37faf651d558a054453"
"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0f44a6dc9a98359515541a0c46ef4e3630a30879c1d7a4038f31dd533570bfb"
"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2"

@ -0,0 +1,13 @@
[package]
name = "kernel8"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
raspi3_boot = { path = "raspi3_boot" }
cortex-a = "2.4.0"
register = "0.3.2"
[package.metadata.cargo-xbuild]
sysroot_path = "../xbuild_sysroot"

@ -0,0 +1,65 @@
#
# 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
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
.PHONY: all qemu raspboot clippy clean objdump nm
all: clean kernel8.img
target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release
kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
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

@ -0,0 +1,28 @@
# Tutorial 0E - Global `println!`
Coming soon!
This lesson will teach about:
- Restructuring the current codebase.
- Realizing global println! and print! macros by reusing macros from the Rust
standard library.
- The NullLock, a wrapper that allows using global static variables without
explicit need for `unsafe {}` code. It is a teaching concept that is only
valid in single-threaded IRQ-disabled environments. However, it already lays
the groundwork for the introduction of proper locking mechanisms, e.g. real
Spinlocks.
```console
ferris@box:~$ make raspboot
[0] UART is live!
[1] Press a key to continue booting... Greetings fellow Rustacean!
[2] Switching MMU on now... MMU online.
[i] Memory layout:
0x00000000 - 0x0007FFFF | 512 KiB | Kernel stack
0x00080000 - 0x00082FFF | 12 KiB | Kernel code and RO data
0x00083000 - 0x00085007 | 8 KiB | Kernel data and BSS
0x3F000000 - 0x3FFFFFFF | 16 MiB | Device MMIO
$>
```

Binary file not shown.

Binary file not shown.

@ -0,0 +1,57 @@
/*
* 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; /* This is already 4KiB aligned */
__ro_start = .;
.text :
{
KEEP(*(.text.boot)) *(.text .text.*)
}
.rodata :
{
*(.rodata .rodata.*)
}
. = ALIGN(4096); /* Fill up to 4KiB */
__ro_end = .;
.data :
{
*(.data .data.*)
}
.bss ALIGN(8):
{
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}

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

@ -0,0 +1,130 @@
/*
* 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]
//! 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) => {
#[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()`.
unsafe fn reset() -> ! {
extern "C" {
// Boundaries of the .bss section, provided by the linker script
static mut __bss_start: u64;
static mut __bss_end: u64;
}
// Zeroes the .bss section
r0::zero_bss(&mut __bss_start, &mut __bss_end);
extern "Rust" {
fn main() -> !;
}
main()
}
/// Prepare and execute transition from EL2 to EL1.
#[inline]
fn setup_and_enter_el1_from_el2() -> ! {
use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1
CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
// No offset for reading the counters
CNTVOFF_EL2.set(0);
// Set EL1 execution state to AArch64
// TODO: Explain the SWIO bit
HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::SWIO::SET);
// Set up a simulated exception return.
//
// First, fake a saved program status, where all interrupts were
// masked and SP_EL1 was used as a stack pointer.
SPSR_EL2.write(
SPSR_EL2::D::Masked
+ SPSR_EL2::A::Masked
+ SPSR_EL2::I::Masked
+ SPSR_EL2::F::Masked
+ SPSR_EL2::M::EL1h,
);
// Second, let the link register point to reset().
ELR_EL2.set(reset as *const () as u64);
// Set up SP_EL1 (stack pointer), which will be used by EL1 once
// we "return" to it.
SP_EL1.set(STACK_START);
// Use `eret` to "return" to EL1. This will result in execution of
// `reset()` in EL1.
asm::eret()
}
/// Entrypoint of the processor.
///
/// Parks all cores except core0 and checks if we started in EL2. If
/// so, proceeds with setting up EL1.
#[link_section = ".text.boot"]
#[no_mangle]
pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value;
if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
setup_and_enter_el1_from_el2()
}
// if not core0 or EL != 2, infinitely wait for events
loop {
asm::wfe();
}
}

@ -0,0 +1,37 @@
/*
* 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 cortex_a::asm;
/*
*
* Using the CPU's cycles
*
*/
/// Wait N CPU cycles (ARM CPU only)
pub fn wait_cycles(cyc: u32) {
for _ in 0..cyc {
asm::nop();
}
}

@ -0,0 +1,26 @@
/*
* MIT License
*
* Copyright (c) 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.
*/
pub mod hw;
pub mod virt;

@ -0,0 +1,31 @@
/*
* MIT License
*
* Copyright (c) 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.
*/
mod gpio;
mod pl011_uart;
mod videocore_mbox;
pub use gpio::GPIO;
pub use pl011_uart::PL011Uart;
pub use videocore_mbox::VideocoreMbox;

@ -0,0 +1,120 @@
/*
* 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 core::ops;
use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area
pub struct GPIO {
base_addr: u32,
}
impl ops::Deref for GPIO {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr() }
}
}
impl GPIO {
pub fn new(base_addr: u32) -> GPIO {
GPIO { base_addr }
}
/// Returns a pointer to the register block
fn ptr(&self) -> *const RegisterBlock {
self.base_addr as *const _
}
}

@ -0,0 +1,276 @@
/*
* 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::gpio;
use super::videocore_mbox;
use crate::delays;
use crate::devices::virt::ConsoleOps;
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use register::{mmio::*, register_bitfields};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum PL011UartError {
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, PL011UartError>;
pub struct PL011Uart {
base_addr: u32,
}
impl ops::Deref for PL011Uart {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr() }
}
}
impl PL011Uart {
pub fn new(base_addr: u32) -> PL011Uart {
PL011Uart { base_addr }
}
/// Returns a pointer to the register block
fn ptr(&self) -> *const RegisterBlock {
self.base_addr as *const _
}
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(
&self,
v_mbox: &mut videocore_mbox::VideocoreMbox,
gpio: &gpio::GPIO,
) -> Result<()> {
// turn off UART0
self.CR.set(0);
// set up clock for consistent divisor values
v_mbox.buffer[0] = 9 * 4;
v_mbox.buffer[1] = videocore_mbox::REQUEST;
v_mbox.buffer[2] = videocore_mbox::tag::SETCLKRATE;
v_mbox.buffer[3] = 12;
v_mbox.buffer[4] = 8;
v_mbox.buffer[5] = videocore_mbox::clock::UART; // UART clock
v_mbox.buffer[6] = 4_000_000; // 4Mhz
v_mbox.buffer[7] = 0; // skip turbo setting
v_mbox.buffer[8] = videocore_mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which
// is done by a store operation as well).
compiler_fence(Ordering::Release);
if v_mbox.call(videocore_mbox::channel::PROP).is_err() {
return Err(PL011UartError::MailboxError); // Abort if UART clocks couldn't be set
};
// map UART0 to GPIO pins
gpio.GPFSEL1
.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150);
gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150);
gpio.GPPUDCLK0.set(0);
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(())
}
}
impl Drop for PL011Uart {
fn drop(&mut self) {
self.CR
.write(CR::UARTEN::Disabled + CR::TXE::Disabled + CR::RXE::Disabled);
}
}
impl ConsoleOps for PL011Uart {
/// Send a character
fn putc(&self, c: char) {
// wait until we can send
loop {
if !self.FR.is_set(FR::TXFF) {
break;
}
asm::nop();
}
// write the character to the buffer
self.DR.set(c as u32);
}
/// Display a string
fn puts(&self, string: &str) {
for c in string.chars() {
// convert newline to carrige return + newline
if c == '\n' {
self.putc('\r')
}
self.putc(c);
}
}
/// Receive a character
fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if !self.FR.is_set(FR::RXFE) {
break;
}
asm::nop();
}
// read it and return
let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline
if ret == '\r' {
ret = '\n'
}
ret
}
}

@ -0,0 +1,164 @@
/*
* 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 core::ops;
use cortex_a::asm;
use register::{
mmio::{ReadOnly, WriteOnly},
register_bitfields,
};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
READ: ReadOnly<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 VideocoreMboxError {
ResponseError,
UnknownError,
}
pub type Result<T> = ::core::result::Result<T, VideocoreMboxError>;
// Channels
pub mod channel {
pub const PROP: u32 = 8;
}
// Tags
pub mod tag {
pub const SETCLKRATE: u32 = 0x38002;
pub const LAST: u32 = 0;
}
// Clocks
pub mod clock {
pub const UART: u32 = 0x0_0000_0002;
}
// Responses
mod response {
pub const SUCCESS: u32 = 0x8000_0000;
pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response)
}
pub const REQUEST: u32 = 0;
// Public interface to the mailbox
#[repr(C)]
#[repr(align(16))]
pub struct VideocoreMbox {
// The address for buffer needs to be 16-byte aligned so that the Videcore
// can handle it properly. Hence, put it at the start of the struct so that
// the align attribute is effective.
pub buffer: [u32; 36],
base_addr: u32,
}
/// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.STATUS.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*Mbox::ptr()).STATUS.read() }
/// ```
impl ops::Deref for VideocoreMbox {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr() }
}
}
impl VideocoreMbox {
pub fn new(base_addr: u32) -> VideocoreMbox {
VideocoreMbox {
buffer: [0; 36],
base_addr,
}
}
/// Returns a pointer to the register block
fn ptr(&self) -> *const RegisterBlock {
self.base_addr as *const _
}
/// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success
pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if !self.STATUS.is_set(STATUS::FULL) {
break;
}
asm::nop();
}
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier
self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
// now wait for the response
loop {
// is there a response?
loop {
if !self.STATUS.is_set(STATUS::EMPTY) {
break;
}
asm::nop();
}
let resp: u32 = self.READ.get();
// is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response?
return match self.buffer[1] {
response::SUCCESS => Ok(()),
response::ERROR => Err(VideocoreMboxError::ResponseError),
_ => Err(VideocoreMboxError::UnknownError),
};
}
}
}
}

@ -0,0 +1,27 @@
/*
* MIT License
*
* Copyright (c) 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.
*/
mod console;
pub use console::{Console, ConsoleOps};

@ -0,0 +1,136 @@
/*
* MIT License
*
* Copyright (c) 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 crate::devices::hw;
use core::fmt;
/// A trait that must be implemented by devices that are candidates for the
/// global console.
#[allow(unused_variables)]
pub trait ConsoleOps: Drop {
fn putc(&self, c: char) {}
fn puts(&self, string: &str) {}
fn getc(&self) -> char {
' '
}
fn flush(&self) {}
}
/// A dummy console that just ignores its inputs.
pub struct NullConsole;
impl Drop for NullConsole {
fn drop(&mut self) {}
}
impl ConsoleOps for NullConsole {}
/// Possible outputs which the console can store.
pub enum Output {
None(NullConsole),
PL011Uart(hw::PL011Uart),
}
impl From<hw::PL011Uart> for Output {
fn from(instance: hw::PL011Uart) -> Self {
Output::PL011Uart(instance)
}
}
pub struct Console {
output: Output,
}
impl Console {
pub const fn new() -> Console {
Console {
output: Output::None(NullConsole {}),
}
}
#[inline(always)]
fn current_ptr(&self) -> &dyn ConsoleOps {
match &self.output {
Output::None(i) => i,
Output::PL011Uart(i) => i,
}
}
/// Overwrite the current output. The old output will go out of scope and
/// it's Drop function will be called.
pub fn replace_with(&mut self, x: Output) {
self.current_ptr().flush();
self.output = x;
}
/// A command prompt. Currently does nothing.
pub fn command_prompt(&self) -> ! {
self.puts("\n$> ");
let mut input;
loop {
input = self.getc();
if input == '\n' {
self.puts("\n$> ")
} else {
self.putc(input);
}
}
}
}
impl Drop for Console {
fn drop(&mut self) {}
}
/// Dispatch the respective function to the currently stored output device.
impl ConsoleOps for Console {
fn putc(&self, c: char) {
self.current_ptr().putc(c);
}
fn puts(&self, string: &str) {
self.current_ptr().puts(string);
}
fn getc(&self) -> char {
self.current_ptr().getc()
}
fn flush(&self) {
self.current_ptr().flush()
}
}
/// Implementing this trait enables usage of the format_args! macros, which in
/// turn are used to implement the kernel's print! and println! macros.
///
/// See src/macros.rs.
impl fmt::Write for Console {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.current_ptr().puts(s);
Ok(())
}
}

@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) 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 core::fmt;
// https://doc.rust-lang.org/src/std/macros.rs.html
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::macros::_print(format_args!($($arg)*)));
}
// https://doc.rust-lang.org/src/std/macros.rs.html
#[macro_export]
macro_rules! println {
() => (print!("\n"));
($($arg:tt)*) => ({
$crate::macros::_print(format_args_nl!($($arg)*));
})
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
crate::CONSOLE.lock(|c| {
c.write_fmt(args).unwrap();
})
}

@ -0,0 +1,106 @@
/*
* 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(const_fn)]
#![feature(custom_attribute)]
#![feature(format_args_nl)]
#![feature(range_contains)]
mod delays;
mod devices;
mod macros;
mod memory;
mod sync;
/// The global console. Output of the print! and println! macros.
static CONSOLE: sync::NullLock<devices::virt::Console> =
sync::NullLock::new(devices::virt::Console::new());
fn kernel_entry() -> ! {
use devices::hw;
use devices::virt::ConsoleOps;
// This will be invisible, because CONSOLE is dispatching to the NullConsole
// at this point in time.
println!("Is there anybody out there?");
//------------------------------------------------------------
// Instantiate GPIO device
//------------------------------------------------------------
let gpio = hw::GPIO::new(memory::map::GPIO_BASE);
//------------------------------------------------------------
// Instantiate Videocore Mailbox
//------------------------------------------------------------
let mut v_mbox = hw::VideocoreMbox::new(memory::map::VIDEOCORE_MBOX_BASE);
//------------------------------------------------------------
// Instantiate PL011 UART and put it in CONSOLE
//------------------------------------------------------------
let uart = hw::PL011Uart::new(memory::map::PL011_UART_BASE);
match uart.init(&mut v_mbox, &gpio) {
Ok(_) => {
CONSOLE.lock(|c| {
// Moves uart into the global CONSOLE. It is not accessible
// anymore for the remaining parts of kernel_entry().
c.replace_with(uart.into());
});
println!("\n[0] UART is live!");
}
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
},
}
//------------------------------------------------------------
// Greet the user
//------------------------------------------------------------
print!("[1] Press a key to continue booting... ");
CONSOLE.lock(|c| {
c.getc();
});
println!("Greetings fellow Rustacean!");
//------------------------------------------------------------
// Bring up memory subsystem
//------------------------------------------------------------
print!("[2] Switching MMU on now... ");
unsafe { memory::mmu::init() };
println!("MMU online.");
memory::print_layout();
//------------------------------------------------------------
// Start a command prompt
//------------------------------------------------------------
CONSOLE.lock(|c| {
c.command_prompt();
})
}
raspi3_boot::entry!(kernel_entry);

@ -0,0 +1,113 @@
/*
* MIT License
*
* Copyright (c) 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 crate::println;
pub mod mmu;
/// The system memory map.
#[rustfmt::skip]
pub mod map {
pub const KERN_STACK_BOT: u32 = 0x0000_0000;
pub const KERN_STACK_TOP: u32 = 0x0007_FFFF;
pub const MMIO_BASE: u32 = 0x3F00_0000;
pub const VIDEOCORE_MBOX_BASE: u32 = MMIO_BASE + 0x0000_B880;
pub const GPIO_BASE: u32 = MMIO_BASE + 0x0020_0000;
pub const PL011_UART_BASE: u32 = MMIO_BASE + 0x0020_1000;
pub const PHYS_ADDR_MAX: u32 = 0x3FFF_FFFF;
}
const PAGESIZE: u64 = 4096;
fn get_ro_start_end() -> (u64, u64) {
// Using the linker script, we ensure that the RO area is consecutive and 4
// KiB aligned, and we export the boundaries via symbols.
extern "C" {
// The inclusive start of the read-only area, aka the address of the
// first byte of the area.
static __ro_start: u64;
// The non-inclusive end of the read-only area, aka the address of the
// first byte _after_ the RO area.
static __ro_end: u64;
}
unsafe {
// Notice the subtraction to calculate the last page index of the RO
// area and not the first page index after the RO area.
(
&__ro_start as *const _ as u64,
&__ro_end as *const _ as u64 - 1,
)
}
}
pub fn print_layout() {
use crate::memory::map::*;
// log2(1024)
const KIB_RSHIFT: u32 = 10;
// log2(1024 * 1024)
const MIB_RSHIFT: u32 = 20;
println!("[i] Memory layout:");
println!(
" {:#010X} - {:#010X} | {: >4} KiB | Kernel stack",
KERN_STACK_BOT,
KERN_STACK_TOP,
(KERN_STACK_TOP - KERN_STACK_BOT + 1) >> KIB_RSHIFT
);
let (ro_start, ro_end) = get_ro_start_end();
println!(
" {:#010X} - {:#010X} | {: >4} KiB | Kernel code and RO data",
ro_start,
ro_end,
(ro_end - ro_start + 1) >> KIB_RSHIFT
);
extern "C" {
static __bss_end: u64;
}
let start = ro_end + 1;
let end = unsafe { &__bss_end as *const _ as u64 } - 1;
println!(
" {:#010X} - {:#010X} | {: >4} KiB | Kernel data and BSS",
start,
end,
(end - start + 1) >> KIB_RSHIFT
);
println!(
" {:#010X} - {:#010X} | {: >4} MiB | Device MMIO",
MMIO_BASE,
PHYS_ADDR_MAX,
(PHYS_ADDR_MAX - MMIO_BASE + 1) >> MIB_RSHIFT
);
}

@ -0,0 +1,229 @@
/*
* 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 cortex_a::{barrier, regs::*};
use register::register_bitfields;
register_bitfields! {u64,
// AArch64 Reference Manual page 2150
STAGE1_DESCRIPTOR [
/// Execute-never
XN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Various address fields, depending on use case
LVL2_OUTPUT_ADDR_4KiB OFFSET(21) NUMBITS(27) [], // [47:21]
NEXT_LVL_TABLE_ADDR_4KiB OFFSET(12) NUMBITS(36) [], // [47:12]
/// Access flag
AF OFFSET(10) NUMBITS(1) [
False = 0,
True = 1
],
/// Shareability field
SH OFFSET(8) NUMBITS(2) [
OuterShareable = 0b10,
InnerShareable = 0b11
],
/// Access Permissions
AP OFFSET(6) NUMBITS(2) [
RW_EL1 = 0b00,
RW_EL1_EL0 = 0b01,
RO_EL1 = 0b10,
RO_EL1_EL0 = 0b11
],
/// Memory attributes index into the MAIR_EL1 register
AttrIndx OFFSET(2) NUMBITS(3) [],
TYPE OFFSET(1) NUMBITS(1) [
Block = 0,
Table = 1
],
VALID OFFSET(0) NUMBITS(1) [
False = 0,
True = 1
]
]
}
trait BaseAddr {
fn base_addr(&self) -> u64;
}
impl BaseAddr for [u64; 512] {
fn base_addr(&self) -> u64 {
self as *const u64 as u64
}
}
const NUM_ENTRIES_4KIB: usize = 512;
// We need a wrapper struct here so that we can make use of the align attribute.
#[repr(C)]
#[repr(align(4096))]
struct PageTable {
entries: [u64; NUM_ENTRIES_4KIB],
}
static mut LVL2_TABLE: PageTable = PageTable {
entries: [0; NUM_ENTRIES_4KIB],
};
static mut SINGLE_LVL3_TABLE: PageTable = PageTable {
entries: [0; NUM_ENTRIES_4KIB],
};
/// Set up identity mapped page tables for the first 1 GiB of address space.
pub unsafe fn init() {
// First, define the three memory types that we will map. Cacheable and
// non-cacheable normal DRAM, and device.
MAIR_EL1.write(
// Attribute 2
MAIR_EL1::Attr2_HIGH::Memory_OuterNonCacheable
+ MAIR_EL1::Attr2_LOW_MEMORY::InnerNonCacheable
// Attribute 1
+ MAIR_EL1::Attr1_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc
+ MAIR_EL1::Attr1_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc
// Attribute 0
+ MAIR_EL1::Attr0_HIGH::Device
+ MAIR_EL1::Attr0_LOW_DEVICE::Device_nGnRE,
);
// Descriptive consts for indexing into the correct MAIR_EL1 attributes.
#[allow(dead_code)]
mod mair {
pub const DEVICE: u64 = 0;
pub const NORMAL: u64 = 1;
pub const NORMAL_NON_CACHEABLE: u64 = 2;
}
// The first 2 MiB.
//
// Set up the first LVL2 entry, pointing to the base address of a follow-up
// table containing 4 KiB pages.
//
// 0x0000_0000_0000_0000 |
// |> 2 MiB
// 0x0000_0000_001F_FFFF |
let lvl3_base: u64 = SINGLE_LVL3_TABLE.entries.base_addr() >> 12;
LVL2_TABLE.entries[0] = (STAGE1_DESCRIPTOR::VALID::True
+ STAGE1_DESCRIPTOR::TYPE::Table
+ STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(lvl3_base))
.value;
// Fill the rest of the LVL2 (2 MiB) entries as block descriptors.
//
// Differentiate between
// - cacheable DRAM
// - device memory
//
// Ranges are stored in memory.rs.
//
// 0x0000_0000_0020_0000 |
// |> 1006 MiB cacheable DRAM
// 0x0000_0000_3EFF_FFFF |
// 0x0000_0000_3F00_0000 |
// |> 16 MiB device (MMIO)
// 0x0000_0000_4000_0000 |
let mmio_first_block_index: u64 = (crate::memory::map::MMIO_BASE >> 21).into();
let common = STAGE1_DESCRIPTOR::VALID::True
+ STAGE1_DESCRIPTOR::TYPE::Block
+ STAGE1_DESCRIPTOR::AP::RW_EL1
+ STAGE1_DESCRIPTOR::AF::True
+ STAGE1_DESCRIPTOR::XN::True;
// Notice the skip(1) which makes the iteration start at the second 2 MiB
// block (0x20_0000).
for (i, entry) in LVL2_TABLE.entries.iter_mut().enumerate().skip(1) {
let j: u64 = i as u64;
let mem_attr = if j >= mmio_first_block_index {
STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE)
} else {
STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL)
};
*entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(j)).value;
}
// Finally, fill the single LVL3 table (4 KiB granule). Differentiate
// between code+RO and RW pages.
let (ro_start_addr, ro_end_addr) = super::get_ro_start_end();
let ro_first_page_index = ro_start_addr / super::PAGESIZE;
let ro_last_page_index = ro_end_addr / super::PAGESIZE;
let common = STAGE1_DESCRIPTOR::VALID::True
+ STAGE1_DESCRIPTOR::TYPE::Table
+ STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL)
+ STAGE1_DESCRIPTOR::SH::InnerShareable
+ STAGE1_DESCRIPTOR::AF::True;
for (i, entry) in SINGLE_LVL3_TABLE.entries.iter_mut().enumerate() {
let j: u64 = i as u64;
let mem_attr = if (ro_first_page_index..=ro_last_page_index).contains(&j) {
STAGE1_DESCRIPTOR::AP::RO_EL1 + STAGE1_DESCRIPTOR::XN::False
} else {
STAGE1_DESCRIPTOR::AP::RW_EL1 + STAGE1_DESCRIPTOR::XN::True
};
*entry = (common + mem_attr + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(j)).value;
}
// Point to the LVL2 table base address in TTBR0.
TTBR0_EL1.set_baddr(LVL2_TABLE.entries.base_addr());
// Configure various settings of stage 1 of the EL1 translation regime.
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange);
TCR_EL1.write(
TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips)
+ TCR_EL1::TG0::KiB_4 // 4 KiB granule
+ TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks
+ TCR_EL1::T0SZ.val(34), // Start walks at level 2
);
// Switch the MMU on.
//
// First, force all previous changes to be seen before the MMU is enabled.
barrier::isb(barrier::SY);
// Enable the MMU and turn on data and instruction caching.
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
// Force MMU init to complete before next instruction
barrier::isb(barrier::SY);
}

@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (c) 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 core::cell::UnsafeCell;
pub struct NullLock<T> {
data: UnsafeCell<T>,
}
// Since we are instantiating this struct as a static variable, which could
// potentially be shared between different threads, we need to tell the compiler
// that sharing of this struct is safe by marking it with the Sync trait.
//
// At this point in time, we can do so without worrying, because the kernel
// anyways runs on a single core and interrupts are disabled. In short, multiple
// threads don't exist yet in our code.
//
// Literature:
// https://doc.rust-lang.org/beta/nomicon/send-and-sync.html
// https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html
unsafe impl<T> Sync for NullLock<T> {}
impl<T> NullLock<T> {
pub const fn new(data: T) -> NullLock<T> {
NullLock {
data: UnsafeCell::new(data),
}
}
}
impl<T> NullLock<T> {
pub fn lock<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
// In a real lock, there would be code around this line that ensures
// that this mutable reference will ever only be given out to one thread
// at a time.
f(unsafe { &mut *self.data.get() })
}
}
Loading…
Cancel
Save