Add code for 0F_DMA_memory
parent
c6e6ccd2a4
commit
48cf71b6d4
@ -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,66 @@
|
||||
#
|
||||
# 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_MISC = -v $(shell pwd)/../emulation:/emulation
|
||||
DOCKER_TTY = --privileged -v /dev:/dev
|
||||
QEMU_CMD = bash /emulation/qemu_multi_uart.sh
|
||||
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) $(DOCKER_MISC) $(UTILS_CONTAINER) $(QEMU_CMD)
|
||||
|
||||
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,27 @@
|
||||
# Tutorial 0F - DMA Memory
|
||||
|
||||
Coming soon!
|
||||
|
||||
This lesson will teach about:
|
||||
- Simple bump memory allocators and non-cacheable memory.
|
||||
- Using MiniUart for early boot messages and dynamically switching to the PL011
|
||||
Uart later (which now needs the memory allocator that theoretically could fail
|
||||
- which the MiniUart could then print).
|
||||
|
||||
```console
|
||||
[0] MiniUart online.
|
||||
[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 - 0x00083FFF | 16 KiB | Kernel code and RO data
|
||||
0x00084000 - 0x00087007 | 12 KiB | Kernel data and BSS
|
||||
0x00200000 - 0x005FFFFF | 4 MiB | DMA heap pool
|
||||
0x3F000000 - 0x3FFFFFFF | 16 MiB | Device MMIO
|
||||
[i] Global DMA Allocator:
|
||||
Allocated Addr 0x00200000 Size 0x90
|
||||
[3] Videocore Mailbox set up (DMA mem heap allocation successful).
|
||||
[4] PL011 UART online. Output switched to it.
|
||||
|
||||
$>
|
||||
```
|
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,46 @@
|
||||
[[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 = "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,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,33 @@
|
||||
/*
|
||||
* 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 mini_uart;
|
||||
mod pl011_uart;
|
||||
mod videocore_mbox;
|
||||
|
||||
pub use gpio::GPIO;
|
||||
pub use mini_uart::MiniUart;
|
||||
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,266 @@
|
||||
/*
|
||||
* 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 crate::devices::virt::ConsoleOps;
|
||||
use core::ops;
|
||||
use cortex_a::asm;
|
||||
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 is empty and the transmitter is
|
||||
/// idle. (Finished shifting out the last bit).
|
||||
TX_IDLE OFFSET(6) NUMBITS(1) [],
|
||||
|
||||
/// 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) []
|
||||
]
|
||||
}
|
||||
|
||||
#[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 {
|
||||
base_addr: u32,
|
||||
}
|
||||
|
||||
/// 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(base_addr: u32) -> MiniUart {
|
||||
MiniUart { 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, gpio: &gpio::GPIO) {
|
||||
// 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
|
||||
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();
|
||||
}
|
||||
|
||||
gpio.GPPUDCLK0
|
||||
.write(gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock);
|
||||
for _ in 0..150 {
|
||||
asm::nop();
|
||||
}
|
||||
|
||||
gpio.GPPUDCLK0.set(0);
|
||||
|
||||
self.AUX_MU_CNTL
|
||||
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
|
||||
|
||||
// Clear FIFOs before using the device
|
||||
self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
|
||||
}
|
||||
|
||||
pub fn wait_tx_fifo_empty(&self) {
|
||||
loop {
|
||||
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE) {
|
||||
break;
|
||||
}
|
||||
|
||||
asm::nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MiniUart {
|
||||
fn drop(&mut self) {
|
||||
self.AUX_ENABLES
|
||||
.modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for MiniUart {
|
||||
/// Send a character
|
||||
fn putc(&self, c: char) {
|
||||
// wait until we can send
|
||||
loop {
|
||||
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) {
|
||||
break;
|
||||
}
|
||||
|
||||
asm::nop();
|
||||
}
|
||||
|
||||
// write the character to the buffer
|
||||
self.AUX_MU_IO.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.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) {
|
||||
break;
|
||||
}
|
||||
|
||||
asm::nop();
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
/// line.
|
||||
fn flush(&self) {
|
||||
self.wait_tx_fifo_empty();
|
||||
}
|
||||
}
|
@ -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,170 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// The address for buffer needs to be 16-byte aligned so that the Videcore can
|
||||
// handle it properly.
|
||||
const MBOX_ALIGNMENT: usize = 16;
|
||||
const MBOX_SIZE: usize = 36;
|
||||
|
||||
// Public interface to the mailbox
|
||||
pub struct VideocoreMbox<'a> {
|
||||
pub buffer: &'a mut [u32],
|
||||
base_addr: u32,
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.STATUS.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*Mbox::ptr()).STATUS.read() }
|
||||
/// ```
|
||||
impl<'a> ops::Deref for VideocoreMbox<'a> {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VideocoreMbox<'a> {
|
||||
pub fn new(base_addr: u32) -> ::core::result::Result<VideocoreMbox<'a>, ()> {
|
||||
let ret = crate::DMA_ALLOCATOR.lock(|d| d.alloc_slice_zeroed(MBOX_SIZE, MBOX_ALIGNMENT));
|
||||
|
||||
if ret.is_err() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(VideocoreMbox {
|
||||
base_addr,
|
||||
buffer: ret.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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,144 @@
|
||||
/*
|
||||
* 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),
|
||||
MiniUart(hw::MiniUart),
|
||||
PL011Uart(hw::PL011Uart),
|
||||
}
|
||||
|
||||
impl From<hw::MiniUart> for Output {
|
||||
fn from(instance: hw::MiniUart) -> Self {
|
||||
Output::MiniUart(instance)
|
||||
}
|
||||
}
|
||||
|
||||
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::MiniUart(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,163 @@
|
||||
/*
|
||||
* 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(allocator_api)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(label_break_value)]
|
||||
#![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());
|
||||
|
||||
/// The global allocator for DMA-able memory. That is, memory which is tagged
|
||||
/// non-cacheable in the page tables.
|
||||
static DMA_ALLOCATOR: sync::NullLock<memory::BumpAllocator> =
|
||||
sync::NullLock::new(memory::BumpAllocator::new(
|
||||
memory::map::DMA_HEAP_START as usize,
|
||||
memory::map::DMA_HEAP_END as usize,
|
||||
"Global DMA Allocator",
|
||||
// Try the following arguments instead to see the PL011 UART init
|
||||
// fail. It will cause the allocator to use memory that are marked
|
||||
// cacheable and therefore not DMA-safe. The answer from the Videocore
|
||||
// won't be received by the CPU because it reads an old cached value
|
||||
// that resembles an error case instead.
|
||||
|
||||
// 0x00600000 as usize,
|
||||
// 0x007FFFFF as usize,
|
||||
// "Global Non-DMA Allocator",
|
||||
));
|
||||
|
||||
fn kernel_entry() -> ! {
|
||||
use devices::hw;
|
||||
use devices::virt::ConsoleOps;
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Instantiate GPIO device
|
||||
//------------------------------------------------------------
|
||||
let gpio = hw::GPIO::new(memory::map::GPIO_BASE);
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Instantiate MiniUart
|
||||
//------------------------------------------------------------
|
||||
let mini_uart = hw::MiniUart::new(memory::map::MINI_UART_BASE);
|
||||
mini_uart.init(&gpio);
|
||||
|
||||
CONSOLE.lock(|c| {
|
||||
// Moves mini_uart into the global CONSOLE. It is not accessible anymore
|
||||
// for the remaining parts of kernel_entry().
|
||||
c.replace_with(mini_uart.into());
|
||||
});
|
||||
println!("\n[0] MiniUart online.");
|
||||
|
||||
//------------------------------------------------------------
|
||||
// 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();
|
||||
|
||||
// We are now in a state where every next step can fail, but we can handle
|
||||
// the error with feedback for the user and fall through to our UART
|
||||
// loopback.
|
||||
'init: {
|
||||
//------------------------------------------------------------
|
||||
// Instantiate Videocore Mailbox
|
||||
//------------------------------------------------------------
|
||||
let mut v_mbox;
|
||||
match hw::VideocoreMbox::new(memory::map::VIDEOCORE_MBOX_BASE) {
|
||||
Ok(i) => {
|
||||
println!("[3] Videocore Mailbox set up (DMA mem heap allocation successful).");
|
||||
v_mbox = i;
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
println!("[3][Error] Could not set up Videocore Mailbox. Aborting.");
|
||||
break 'init;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Instantiate PL011 UART and replace MiniUart with it in CONSOLE
|
||||
//------------------------------------------------------------
|
||||
let pl011_uart = hw::PL011Uart::new(memory::map::PL011_UART_BASE);
|
||||
|
||||
// uart.init() will reconfigure the GPIO, which causes a race against
|
||||
// the MiniUart that is still putting out characters on the physical
|
||||
// line that are already buffered in its TX FIFO.
|
||||
//
|
||||
// To ensure the CPU doesn't rewire the GPIO before the MiniUart has put
|
||||
// its last character, explicitly flush it before rewiring.
|
||||
//
|
||||
// If you switch to an output that happens to not use the same pair of
|
||||
// physical wires (e.g. the Framebuffer), you don't need to do this,
|
||||
// because flush() is anyways called implicitly by replace_with(). This
|
||||
// is just a special case.
|
||||
CONSOLE.lock(|c| c.flush());
|
||||
match pl011_uart.init(&mut v_mbox, &gpio) {
|
||||
Ok(_) => {
|
||||
CONSOLE.lock(|c| {
|
||||
c.replace_with(pl011_uart.into());
|
||||
});
|
||||
|
||||
println!("[4] PL011 UART online. Output switched to it.");
|
||||
}
|
||||
|
||||
Err(_) => println!(
|
||||
"[4][Error] PL011 UART init failed. \
|
||||
Trying to continue with MiniUart."
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Start a command prompt
|
||||
//------------------------------------------------------------
|
||||
CONSOLE.lock(|c| {
|
||||
c.command_prompt();
|
||||
})
|
||||
}
|
||||
|
||||
raspi3_boot::entry!(kernel_entry);
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
mod bump_allocator;
|
||||
pub use bump_allocator::BumpAllocator;
|
||||
|
||||
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;
|
||||
|
||||
/// The second 2 MiB block.
|
||||
pub const DMA_HEAP_START: u32 = 0x0020_0000;
|
||||
pub const DMA_HEAP_END: u32 = 0x005F_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 MINI_UART_BASE: u32 = MMIO_BASE + 0x0021_5000;
|
||||
|
||||
pub const PHYS_ADDR_MAX: u32 = 0x3FFF_FFFF;
|
||||
}
|
||||
|
||||
const PAGESIZE: u64 = 4096;
|
||||
|
||||
#[inline]
|
||||
fn aligned_addr_unchecked(addr: usize, alignment: usize) -> usize {
|
||||
(addr + (alignment - 1)) & !(alignment - 1)
|
||||
}
|
||||
|
||||
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 | DMA heap pool",
|
||||
DMA_HEAP_START,
|
||||
DMA_HEAP_END,
|
||||
(DMA_HEAP_END - DMA_HEAP_START + 1) >> MIB_RSHIFT
|
||||
);
|
||||
|
||||
println!(
|
||||
" {:#010X} - {:#010X} | {: >4} MiB | Device MMIO",
|
||||
MMIO_BASE,
|
||||
PHYS_ADDR_MAX,
|
||||
(PHYS_ADDR_MAX - MMIO_BASE + 1) >> MIB_RSHIFT
|
||||
);
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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;
|
||||
use core::alloc::{Alloc, AllocErr, Layout};
|
||||
use core::mem;
|
||||
use core::ptr::NonNull;
|
||||
use core::slice;
|
||||
|
||||
pub struct BumpAllocator {
|
||||
next: usize,
|
||||
pool_end: usize,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
unsafe impl Alloc for BumpAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
|
||||
let start = super::aligned_addr_unchecked(self.next, layout.align());
|
||||
let end = start + layout.size();
|
||||
|
||||
if end <= self.pool_end {
|
||||
self.next = end;
|
||||
|
||||
println!(
|
||||
"[i] {}:\n Allocated Addr {:#010X} Size {:#X}",
|
||||
self.name,
|
||||
start,
|
||||
layout.size()
|
||||
);
|
||||
|
||||
Ok(NonNull::new_unchecked(start as *mut u8))
|
||||
} else {
|
||||
Err(AllocErr)
|
||||
}
|
||||
}
|
||||
|
||||
// A bump allocator doesn't care
|
||||
unsafe fn dealloc(&mut self, _ptr: NonNull<u8>, _layout: Layout) {}
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self {
|
||||
Self {
|
||||
next: pool_start,
|
||||
pool_end,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a zeroed slice
|
||||
pub fn alloc_slice_zeroed<'a, T>(
|
||||
&mut self,
|
||||
count_of_items: usize,
|
||||
alignment: usize,
|
||||
) -> Result<&'a mut [T], ()> {
|
||||
let l;
|
||||
let size_in_byte = count_of_items * mem::size_of::<T>();
|
||||
match Layout::from_size_align(size_in_byte, alignment) {
|
||||
Ok(layout) => l = layout,
|
||||
|
||||
Err(_) => {
|
||||
println!("[e] Layout Error!");
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
let ptr;
|
||||
match unsafe { self.alloc_zeroed(l) } {
|
||||
Ok(i) => ptr = i.as_ptr(),
|
||||
|
||||
Err(_) => {
|
||||
println!("[e] Layout Error!");
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unsafe { slice::from_raw_parts_mut(ptr as *mut T, count_of_items) })
|
||||
}
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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
|
||||
// - non-cacheable DRAM
|
||||
// - cacheable DRAM
|
||||
// - device memory
|
||||
//
|
||||
// Ranges are stored in memory.rs.
|
||||
//
|
||||
// 0x0000_0000_0020_0000 |
|
||||
// |> 4 MiB non-cacheable DRAM
|
||||
// 0x0000_0000_005F_FFFF |
|
||||
// 0x0000_0000_0060_0000 |
|
||||
// |> 1002 MiB cacheable DRAM
|
||||
// 0x0000_0000_3EFF_FFFF |
|
||||
// 0x0000_0000_3F00_0000 |
|
||||
// |> 16 MiB device (MMIO)
|
||||
// 0x0000_0000_4000_0000 |
|
||||
let dma_first_block_index: u64 = (crate::memory::map::DMA_HEAP_START >> 21).into();
|
||||
let dma_last_block_index: u64 = (crate::memory::map::DMA_HEAP_END >> 21).into();
|
||||
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 (dma_first_block_index..=dma_last_block_index).contains(&j) {
|
||||
STAGE1_DESCRIPTOR::SH::InnerShareable
|
||||
+ STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL_NON_CACHEABLE)
|
||||
} else 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() })
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
#########################################################
|
||||
# Welcome to the Raspi3 Emulation with #
|
||||
# MiniUart and PL011UART #
|
||||
# #
|
||||
# #
|
||||
# 1. Use the mouse to select different panes #
|
||||
# and interact with the UARTS. #
|
||||
# #
|
||||
# 2. To quit the emulation: #
|
||||
# 2.1 Select the pane in which QEMU runs (this one). #
|
||||
# 2.2 CTRL-C to close qemu. #
|
||||
#########################################################
|
||||
|
||||
=== QEMU ===
|
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
tmux new-session -d -s raspi3 &
|
||||
sleep 1
|
||||
tmux new-window -t raspi3:1
|
||||
tmux new-window -t raspi3:2
|
||||
|
||||
tmux send-keys -t raspi3:0 "clear; echo '=== MiniUart ==='; bash /dev/ptmx" C-m
|
||||
tmux send-keys -t raspi3:1 "clear; printf '=== PL011 Uart ===\n\n';bash /dev/ptmx" C-m
|
||||
|
||||
FIRST=$(ps aux | grep ptmx | sort | awk '{print $7}' | sed '1q;d')
|
||||
SECOND=$(ps aux | grep ptmx | sort | awk '{print $7}' | sed '2q;d')
|
||||
|
||||
tmux send-keys -t raspi3:2 "clear; cat /emulation/instructions.txt && qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial /dev/$SECOND -serial /dev/$FIRST && tmux kill-session" C-m
|
||||
|
||||
tmux join-pane -s raspi3:0 -t 2
|
||||
tmux join-pane -s raspi3:1 -t 2
|
||||
|
||||
tmux select-pane -t 1
|
||||
tmux attach-session -t raspi3
|
Loading…
Reference in New Issue