commit
18626a2a19
@ -0,0 +1,235 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "aarch64"
|
||||||
|
version = "2.2.2"
|
||||||
|
source = "git+https://github.com/equation314/aarch64#14a08f4d285ae0ff515b03bff9f5e66eb68feaed"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bcm2837"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/equation314/bcm2837#446f0ea04deb5216ba5e08f10af36e5c1729e6fd"
|
||||||
|
dependencies = [
|
||||||
|
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedvec"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"skeptic 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rcore-bootloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aarch64 2.2.2 (git+https://github.com/equation314/aarch64)",
|
||||||
|
"bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)",
|
||||||
|
"cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "register"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skeptic"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tock-registers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usize_conversions"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ux"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "volatile"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmas-elf"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zero"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum aarch64 2.2.2 (git+https://github.com/equation314/aarch64)" = "<none>"
|
||||||
|
"checksum bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)" = "<none>"
|
||||||
|
"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56"
|
||||||
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
|
"checksum cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92"
|
||||||
|
"checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0"
|
||||||
|
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
|
||||||
|
"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1"
|
||||||
|
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
||||||
|
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||||
|
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb"
|
||||||
|
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||||
|
"checksum skeptic 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "061203a849117b0f7090baf8157aa91dac30545208fbb85166ac58b4ca33d89c"
|
||||||
|
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||||
|
"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316"
|
||||||
|
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||||
|
"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5"
|
||||||
|
"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
|
||||||
|
"checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29"
|
||||||
|
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||||
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
"checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58"
|
||||||
|
"checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5"
|
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "rcore-bootloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["equation314 <equation618@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
xmas-elf = "0.6.2"
|
||||||
|
fixedvec = "0.2.3"
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||||
|
aarch64 = { git = "https://github.com/equation314/aarch64" }
|
||||||
|
bcm2837 = { git = "https://github.com/equation314/bcm2837" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0"
|
@ -0,0 +1,47 @@
|
|||||||
|
arch ?= aarch64
|
||||||
|
mode ?= debug
|
||||||
|
target := $(arch)
|
||||||
|
payload ?=
|
||||||
|
|
||||||
|
bootloader := target/$(target)/$(mode)/rcore-bootloader
|
||||||
|
|
||||||
|
ifeq ($(arch), x86_64)
|
||||||
|
ifeq ($(uname), Darwin)
|
||||||
|
prefix := x86_64-elf-
|
||||||
|
endif
|
||||||
|
else ifeq ($(arch), riscv32)
|
||||||
|
prefix := riscv64-unknown-elf-
|
||||||
|
else ifeq ($(arch), riscv64)
|
||||||
|
prefix := riscv64-unknown-elf-
|
||||||
|
else ifeq ($(arch), aarch64)
|
||||||
|
prefix ?= aarch64-none-elf-
|
||||||
|
ifeq (,$(shell which $(prefix)ld))
|
||||||
|
prefix := aarch64-elf-
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ld := $(prefix)ld
|
||||||
|
objdump := $(prefix)objdump
|
||||||
|
objcopy := $(prefix)objcopy
|
||||||
|
cc := $(prefix)gcc
|
||||||
|
as := $(prefix)as
|
||||||
|
gdb := $(prefix)gdb
|
||||||
|
strip := $(prefix)strip
|
||||||
|
|
||||||
|
export CC = $(cc)
|
||||||
|
export PAYLOAD = $(payload)
|
||||||
|
|
||||||
|
build_args := --target=targets/$(arch).json
|
||||||
|
ifeq ($(mode), release)
|
||||||
|
build_args += --release
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: bootloader
|
||||||
|
|
||||||
|
bootloader: $(payload)
|
||||||
|
@cargo xbuild $(build_args)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@cargo clean
|
@ -0,0 +1,40 @@
|
|||||||
|
extern crate cc;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Result, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Ok(file_path) = gen_payload_asm() {
|
||||||
|
cc::Build::new().file(&file_path).compile("payload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_payload_asm() -> Result<std::path::PathBuf> {
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
let payload = std::env::var("PAYLOAD").unwrap();
|
||||||
|
|
||||||
|
if !Path::new(&payload).is_file() {
|
||||||
|
panic!("Kernel payload `{}` not found", payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_path = Path::new(&out_dir).join("payload.S");
|
||||||
|
let mut f = File::create(&file_path).unwrap();
|
||||||
|
|
||||||
|
println!("{:x?} {:x?}", payload, file_path);
|
||||||
|
|
||||||
|
write!(f, "# generated by build.rs - do not edit")?;
|
||||||
|
write!(f, r#"
|
||||||
|
.section .payload,"a"
|
||||||
|
.align 12
|
||||||
|
.global _kernel_payload_start, _kernel_payload_end
|
||||||
|
_kernel_payload_start:
|
||||||
|
.incbin "{}"
|
||||||
|
_kernel_payload_end:
|
||||||
|
"#, payload)?;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed={}", payload);
|
||||||
|
println!("cargo:rerun-if-env-changed=PAYLOAD");
|
||||||
|
|
||||||
|
Ok(file_path)
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 0x80000; /* Raspbery Pi 3 AArch64 (kernel8.img) load address */
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
KEEP(*(.text.boot)) /* from boot.S */
|
||||||
|
*(.text .text.* .gnu.linkonce.t*)
|
||||||
|
. = ALIGN(4K);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata .rodata.* .gnu.linkonce.r*)
|
||||||
|
. = ALIGN(4K);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
*(.data .data.* .gnu.linkonce.d*)
|
||||||
|
. = ALIGN(4K);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
_sbss = .;
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4K);
|
||||||
|
_ebss = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payload : {
|
||||||
|
*(.payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
use aarch64::addr::{VirtAddr, PhysAddr};
|
||||||
|
use aarch64::paging::{memory_attribute::*, Page, PageTable, PageTableFlags as EF, PhysFrame};
|
||||||
|
use aarch64::paging::{Size4KiB, Size2MiB, Size1GiB};
|
||||||
|
use aarch64::{asm::*, barrier, regs::*};
|
||||||
|
use bcm2837::consts::RAW_IO_BASE;
|
||||||
|
use core::ptr;
|
||||||
|
use fixedvec::FixedVec;
|
||||||
|
use xmas_elf::program::{ProgramHeader64, Type};
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
const ALIGN_2MB: u64 = 0x200000;
|
||||||
|
|
||||||
|
const RECURSIVE_INDEX: usize = 0o777;
|
||||||
|
const KERNEL_OFFSET: u64 = 0xFFFF_0000_0000_0000;
|
||||||
|
|
||||||
|
global_asm!(include_str!("boot.S"));
|
||||||
|
|
||||||
|
fn setup_temp_page_table(start_vaddr: VirtAddr, end_vaddr: VirtAddr, offset: u64) {
|
||||||
|
#[repr(align(4096))]
|
||||||
|
struct PageData([u8; PAGE_SIZE]);
|
||||||
|
static mut PAGE_TABLE_LVL4: PageData = PageData([0; PAGE_SIZE]);
|
||||||
|
static mut PAGE_TABLE_LVL3: PageData = PageData([0; PAGE_SIZE]);
|
||||||
|
static mut PAGE_TABLE_LVL2: PageData = PageData([0; PAGE_SIZE]);
|
||||||
|
|
||||||
|
let frame_lvl4 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64)) };
|
||||||
|
let frame_lvl3 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64)) };
|
||||||
|
let frame_lvl2 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64)) };
|
||||||
|
let p4 = unsafe { &mut *(frame_lvl4.start_address().as_u64() as *mut PageTable) };
|
||||||
|
let p3 = unsafe { &mut *(frame_lvl3.start_address().as_u64() as *mut PageTable) };
|
||||||
|
let p2 = unsafe { &mut *(frame_lvl2.start_address().as_u64() as *mut PageTable) };
|
||||||
|
p4.zero();
|
||||||
|
p3.zero();
|
||||||
|
p2.zero();
|
||||||
|
|
||||||
|
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::UXN;
|
||||||
|
// normal memory
|
||||||
|
for page in Page::<Size2MiB>::range_of(start_vaddr.as_u64(), end_vaddr.as_u64()) {
|
||||||
|
let paddr = PhysAddr::new(page.start_address().as_u64().wrapping_add(offset));
|
||||||
|
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags, MairNormal::attr_value());
|
||||||
|
}
|
||||||
|
// device memory
|
||||||
|
for page in Page::<Size2MiB>::range_of(RAW_IO_BASE as u64, 0x4000_0000) {
|
||||||
|
let paddr = PhysAddr::new(page.start_address().as_u64());
|
||||||
|
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags | EF::PXN, MairDevice::attr_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
p3[0].set_frame(frame_lvl2, EF::default(), MairNormal::attr_value());
|
||||||
|
p3[1].set_block::<Size1GiB>(PhysAddr::new(0x4000_0000), block_flags | EF::PXN, MairDevice::attr_value());
|
||||||
|
|
||||||
|
p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value());
|
||||||
|
p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value());
|
||||||
|
|
||||||
|
ttbr_el1_write(0, frame_lvl4);
|
||||||
|
ttbr_el1_write(1, frame_lvl4);
|
||||||
|
tlb_invalidate_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_mmu() {
|
||||||
|
MAIR_EL1.write(
|
||||||
|
MAIR_EL1::Attr0.val(MairNormal::config_value()) +
|
||||||
|
MAIR_EL1::Attr1.val(MairDevice::config_value()) +
|
||||||
|
MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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::TBI1::Ignored +
|
||||||
|
TCR_EL1::TBI0::Ignored +
|
||||||
|
TCR_EL1::AS::Bits_16 +
|
||||||
|
TCR_EL1::IPS.val(ips) +
|
||||||
|
|
||||||
|
TCR_EL1::TG1::KiB_4 +
|
||||||
|
TCR_EL1::SH1::Inner +
|
||||||
|
TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||||
|
TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||||
|
TCR_EL1::EPD1::EnableTTBR1Walks +
|
||||||
|
TCR_EL1::A1::UseTTBR1ASID +
|
||||||
|
TCR_EL1::T1SZ.val(16) +
|
||||||
|
|
||||||
|
TCR_EL1::TG0::KiB_4 +
|
||||||
|
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(16),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Switch the MMU on.
|
||||||
|
//
|
||||||
|
// First, force all previous changes to be seen before the MMU is enabled.
|
||||||
|
unsafe { 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
|
||||||
|
unsafe { barrier::isb(barrier::SY) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_kernel(kernel_start: usize, segments: &FixedVec<ProgramHeader64>) {
|
||||||
|
// reverse program headers to avoid overlapping in memory copying
|
||||||
|
let mut space = alloc_stack!([ProgramHeader64; 32]);
|
||||||
|
let mut rev_segments = FixedVec::new(&mut space);
|
||||||
|
for i in (0..segments.len()).rev() {
|
||||||
|
rev_segments.push(segments[i]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut start_vaddr, mut end_vaddr) = (VirtAddr::new(core::u64::MAX), VirtAddr::zero());
|
||||||
|
for segment in &rev_segments {
|
||||||
|
if segment.get_type() != Ok(Type::Load) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let virt_addr = segment.virtual_addr;
|
||||||
|
let offset = segment.offset;
|
||||||
|
let file_size = segment.file_size;
|
||||||
|
let mem_size = segment.mem_size;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let src = (kernel_start as u64 + offset) as *const u8;
|
||||||
|
let dst = virt_addr.wrapping_sub(KERNEL_OFFSET) as *mut u8;
|
||||||
|
ptr::copy(src, dst, file_size as usize);
|
||||||
|
ptr::write_bytes(dst.offset(file_size as isize), 0, (mem_size - file_size) as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if virt_addr < start_vaddr.as_u64() {
|
||||||
|
start_vaddr = VirtAddr::new(virt_addr).align_down(ALIGN_2MB);
|
||||||
|
}
|
||||||
|
if virt_addr + mem_size > end_vaddr.as_u64() {
|
||||||
|
end_vaddr = VirtAddr::new(virt_addr + mem_size).align_up(ALIGN_2MB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_temp_page_table(start_vaddr, end_vaddr, KERNEL_OFFSET.wrapping_neg());
|
||||||
|
enable_mmu();
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(lang_items)]
|
||||||
|
#![feature(global_asm)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate fixedvec;
|
||||||
|
extern crate xmas_elf;
|
||||||
|
|
||||||
|
use core::mem::transmute;
|
||||||
|
use core::slice;
|
||||||
|
use fixedvec::FixedVec;
|
||||||
|
use xmas_elf::{
|
||||||
|
header,
|
||||||
|
program::{ProgramHeader, ProgramHeader64},
|
||||||
|
ElfFile,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
#[path = "arch/aarch64/mod.rs"]
|
||||||
|
pub mod arch;
|
||||||
|
pub mod lang_items;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn _kernel_payload_start();
|
||||||
|
fn _kernel_payload_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The entry point of bootloader
|
||||||
|
#[no_mangle] // don't mangle the name of this function
|
||||||
|
pub extern "C" fn boot_main() -> ! {
|
||||||
|
let kernel_size = _kernel_payload_end as usize - _kernel_payload_start as usize;
|
||||||
|
let kernel = unsafe { slice::from_raw_parts(_kernel_payload_start as *const u8, kernel_size) };
|
||||||
|
let kernel_elf = ElfFile::new(kernel).unwrap();
|
||||||
|
header::sanity_check(&kernel_elf).unwrap();
|
||||||
|
|
||||||
|
let mut preallocated_space = alloc_stack!([ProgramHeader64; 32]);
|
||||||
|
let mut segments = FixedVec::new(&mut preallocated_space);
|
||||||
|
|
||||||
|
for program_header in kernel_elf.program_iter() {
|
||||||
|
match program_header {
|
||||||
|
ProgramHeader::Ph64(header) => segments
|
||||||
|
.push(*header)
|
||||||
|
.expect("does not support more than 32 program segments"),
|
||||||
|
ProgramHeader::Ph32(_) => panic!("does not support 32 bit elf files"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = kernel_elf.header.pt2.entry_point();
|
||||||
|
let kernel_main: extern "C" fn() = unsafe { transmute(entry) };
|
||||||
|
|
||||||
|
arch::map_kernel(_kernel_payload_start as usize, &segments);
|
||||||
|
kernel_main();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"abi-blacklist": [
|
||||||
|
"stdcall",
|
||||||
|
"fastcall",
|
||||||
|
"vectorcall",
|
||||||
|
"thiscall",
|
||||||
|
"win64",
|
||||||
|
"sysv64"
|
||||||
|
],
|
||||||
|
"arch": "aarch64",
|
||||||
|
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||||
|
"executables": true,
|
||||||
|
"linker": "rust-lld",
|
||||||
|
"linker-flavor": "ld.lld",
|
||||||
|
"linker-is-gnu": true,
|
||||||
|
"pre-link-args": {
|
||||||
|
"ld.lld": [
|
||||||
|
"-Tsrc/arch/aarch64/boot.ld"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"llvm-target": "aarch64-unknown-none",
|
||||||
|
"no-compiler-rt": true,
|
||||||
|
"features": "+a53,+strict-align,-neon",
|
||||||
|
"max-atomic-width": 128,
|
||||||
|
"os": "none",
|
||||||
|
"panic": "abort",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"relocation-model": "static",
|
||||||
|
"position-independent-executables": true,
|
||||||
|
"target-c-int-width": "32",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": "64",
|
||||||
|
"target-family": "unix",
|
||||||
|
"disable-redzone": true,
|
||||||
|
"eliminate-frame-pointer": false
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "atags"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["koumingyang <1761674434@qq.com>"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
@ -1,67 +0,0 @@
|
|||||||
use raw;
|
|
||||||
use core::slice;
|
|
||||||
use core::str;
|
|
||||||
|
|
||||||
pub use raw::{Core, Mem};
|
|
||||||
|
|
||||||
/// An ATAG.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum Atag {
|
|
||||||
Core(raw::Core),
|
|
||||||
Mem(raw::Mem),
|
|
||||||
Cmd(&'static str),
|
|
||||||
Unknown(u32),
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atag {
|
|
||||||
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
|
|
||||||
pub fn core(self) -> Option<Core> {
|
|
||||||
match self {
|
|
||||||
Atag::Core(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Some` if this is a `Mem` ATAG. Otherwise returns `None`.
|
|
||||||
pub fn mem(self) -> Option<Mem> {
|
|
||||||
match self {
|
|
||||||
Atag::Mem(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Some` with the command line string if this is a `Cmd` ATAG.
|
|
||||||
/// Otherwise returns `None`.
|
|
||||||
pub fn cmd(self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
Atag::Cmd(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between raw::* types and Atag wrapper.
|
|
||||||
impl<'a> From<&'a raw::Atag> for Atag {
|
|
||||||
fn from(atag: &raw::Atag) -> Atag {
|
|
||||||
unsafe {
|
|
||||||
match (atag.tag, &atag.kind) {
|
|
||||||
(raw::Atag::CORE, &raw::Kind { core }) => Atag::Core(core),
|
|
||||||
(raw::Atag::MEM, &raw::Kind { mem }) => Atag::Mem(mem),
|
|
||||||
(raw::Atag::CMDLINE, &raw::Kind { ref cmd }) => {
|
|
||||||
let mut cmd_ptr: *const u8 = &cmd.cmd as *const u8;
|
|
||||||
let mut len: usize = 0;
|
|
||||||
|
|
||||||
while *cmd_ptr.add(len) != 0 {
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmd_slice = slice::from_raw_parts(cmd_ptr, len);
|
|
||||||
Atag::Cmd(str::from_utf8_unchecked(cmd_slice))
|
|
||||||
},
|
|
||||||
(raw::Atag::NONE, _) => Atag::None,
|
|
||||||
(id, _) => Atag::Unknown(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
pub use atag::*;
|
|
||||||
use raw;
|
|
||||||
|
|
||||||
/// The address at which the firmware loads the ATAGS.
|
|
||||||
const ATAG_BASE: usize = 0x100;
|
|
||||||
|
|
||||||
/// An iterator over the ATAGS on this system.
|
|
||||||
pub struct Atags {
|
|
||||||
ptr: &'static raw::Atag,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atags {
|
|
||||||
/// Returns an instance of `Atags`, an iterator over ATAGS on this system.
|
|
||||||
pub fn get() -> Atags {
|
|
||||||
Atags {
|
|
||||||
ptr: unsafe { &*(ATAG_BASE as *const raw::Atag) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Atags {
|
|
||||||
type Item = Atag;
|
|
||||||
|
|
||||||
/// Iterate over Atags. Returns a valid Atag until the iterator hits the
|
|
||||||
/// Atag::None.
|
|
||||||
fn next(&mut self) -> Option<Atag> {
|
|
||||||
let cur = self.ptr;
|
|
||||||
match cur.next() {
|
|
||||||
Some(next) => {
|
|
||||||
let result = Some(Atag::from(cur));
|
|
||||||
self.ptr = next;
|
|
||||||
result
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
mod raw;
|
|
||||||
mod atag;
|
|
||||||
|
|
||||||
pub mod atags;
|
|
@ -1,67 +0,0 @@
|
|||||||
/// A raw `ATAG` as laid out in memory.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Atag {
|
|
||||||
pub dwords: u32,
|
|
||||||
pub tag: u32,
|
|
||||||
pub kind: Kind
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atag {
|
|
||||||
pub const NONE: u32 = 0x00000000;
|
|
||||||
pub const CORE: u32 = 0x54410001;
|
|
||||||
pub const MEM: u32 = 0x54410002;
|
|
||||||
pub const VIDEOTEXT: u32 = 0x54410003;
|
|
||||||
pub const RAMDISK: u32 = 0x54410004;
|
|
||||||
pub const INITRD2: u32 = 0x54420005;
|
|
||||||
pub const SERIAL: u32 = 0x54410006;
|
|
||||||
pub const REVISION: u32 = 0x54410007;
|
|
||||||
pub const VIDEOLFB: u32 = 0x54410008;
|
|
||||||
pub const CMDLINE: u32 = 0x54410009;
|
|
||||||
|
|
||||||
/// Returns the ATAG following `self`, if there is one.
|
|
||||||
pub fn next(&self) -> Option<&Atag> {
|
|
||||||
if self.tag == Atag::NONE {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let current = self as *const Atag as *const u32;
|
|
||||||
let next: &Atag = unsafe {
|
|
||||||
&*(current.add(self.dwords as usize) as *const Atag)
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The possible variant of an ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
pub union Kind {
|
|
||||||
pub core: Core,
|
|
||||||
pub mem: Mem,
|
|
||||||
pub cmd: Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `CORE` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Core {
|
|
||||||
pub flags: u32,
|
|
||||||
pub page_size: u32,
|
|
||||||
pub root_dev: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `MEM` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Mem {
|
|
||||||
pub size: u32,
|
|
||||||
pub start: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `CMDLINE` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Cmd {
|
|
||||||
/// The first byte of the command line string.
|
|
||||||
pub cmd: u8
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bcm2837"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["equation314 <equation618@gmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
use_generic_timer = ["aarch64"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
volatile = "0.2.4"
|
|
||||||
aarch64= { git = "https://github.com/equation314/aarch64", optional = true }
|
|
@ -1,163 +0,0 @@
|
|||||||
use crate::IO_BASE;
|
|
||||||
use crate::timer::delay;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
|
||||||
|
|
||||||
/// The base address of the `GPIO` registers.
|
|
||||||
const GPIO_BASE: usize = IO_BASE + 0x200000;
|
|
||||||
|
|
||||||
/// An alternative GPIO function. (ref: peripherals 6.1, page 92)
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum Function {
|
|
||||||
Input = 0b000,
|
|
||||||
Output = 0b001,
|
|
||||||
Alt0 = 0b100,
|
|
||||||
Alt1 = 0b101,
|
|
||||||
Alt2 = 0b110,
|
|
||||||
Alt3 = 0b111,
|
|
||||||
Alt4 = 0b011,
|
|
||||||
Alt5 = 0b010,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GPIO registers starting from `GPIO_BASE` (ref: peripherals 6.1, page 90)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
FSEL: [Volatile<u32>; 6],
|
|
||||||
__reserved0: u32,
|
|
||||||
SET: [WriteOnly<u32>; 2],
|
|
||||||
__reserved1: u32,
|
|
||||||
CLR: [WriteOnly<u32>; 2],
|
|
||||||
__reserved2: u32,
|
|
||||||
LEV: [ReadOnly<u32>; 2],
|
|
||||||
__reserved3: u32,
|
|
||||||
EDS: [Volatile<u32>; 2],
|
|
||||||
__reserved4: u32,
|
|
||||||
REN: [Volatile<u32>; 2],
|
|
||||||
__reserved5: u32,
|
|
||||||
FEN: [Volatile<u32>; 2],
|
|
||||||
__reserved6: u32,
|
|
||||||
HEN: [Volatile<u32>; 2],
|
|
||||||
__reserved7: u32,
|
|
||||||
LEN: [Volatile<u32>; 2],
|
|
||||||
__reserved8: u32,
|
|
||||||
AREN: [Volatile<u32>; 2],
|
|
||||||
__reserved9: u32,
|
|
||||||
AFEN: [Volatile<u32>; 2],
|
|
||||||
__reserved10: u32,
|
|
||||||
PUD: Volatile<u32>,
|
|
||||||
PUDCLK: [Volatile<u32>; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Possible states for a GPIO pin.
|
|
||||||
pub enum Uninitialized {}
|
|
||||||
pub enum Input {}
|
|
||||||
pub enum Output {}
|
|
||||||
pub enum Alt {}
|
|
||||||
|
|
||||||
/// A GPIO pin in state `State`.
|
|
||||||
///
|
|
||||||
/// The `State` generic always corresponds to an uninstantiatable type that is
|
|
||||||
/// use solely to mark and track the state of a given GPIO pin. A `Gpio`
|
|
||||||
/// structure starts in the `Uninitialized` state and must be transitions into
|
|
||||||
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
|
|
||||||
/// `into_alt` methods before it can be used.
|
|
||||||
pub struct Gpio<State> {
|
|
||||||
pin: u8,
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
_state: PhantomData<State>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Gpio<T> {
|
|
||||||
/// Transitions `self` to state `S`, consuming `self` and returning a new
|
|
||||||
/// `Gpio` instance in state `S`. This method should _never_ be exposed to
|
|
||||||
/// the public!
|
|
||||||
#[inline(always)]
|
|
||||||
fn transition<S>(self) -> Gpio<S> {
|
|
||||||
Gpio {
|
|
||||||
pin: self.pin,
|
|
||||||
registers: self.registers,
|
|
||||||
_state: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the Gpio pull-up/pull-down state for values in `pin_value`
|
|
||||||
/// (ref: peripherals 6.1, page 101)
|
|
||||||
pub fn set_gpio_pd(&mut self, pud_value: u8) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
|
|
||||||
self.registers.PUD.write(pud_value as u32);
|
|
||||||
delay(150);
|
|
||||||
self.registers.PUDCLK[index as usize].write((1 << self.pin) as u32);
|
|
||||||
delay(150);
|
|
||||||
self.registers.PUD.write(0);
|
|
||||||
self.registers.PUDCLK[index as usize].write(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Uninitialized> {
|
|
||||||
/// Returns a new `GPIO` structure for pin number `pin`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `pin` > `53`.
|
|
||||||
pub fn new(pin: u8) -> Gpio<Uninitialized> {
|
|
||||||
if pin > 53 {
|
|
||||||
panic!("Gpio::new(): pin {} exceeds maximum of 53", pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gpio {
|
|
||||||
registers: unsafe { &mut *(GPIO_BASE as *mut Registers) },
|
|
||||||
pin: pin,
|
|
||||||
_state: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables the alternative function `function` for `self`. Consumes self
|
|
||||||
/// and returns a `Gpio` structure in the `Alt` state.
|
|
||||||
pub fn into_alt(self, function: Function) -> Gpio<Alt> {
|
|
||||||
let select = (self.pin / 10) as usize;
|
|
||||||
let offset = 3 * (self.pin % 10) as usize;
|
|
||||||
self.registers.FSEL[select].update(|value| {
|
|
||||||
*value &= !(0b111 << offset);
|
|
||||||
*value |= (function as u32) << offset;
|
|
||||||
});
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Gpio`
|
|
||||||
/// structure in the `Output` state.
|
|
||||||
pub fn into_output(self) -> Gpio<Output> {
|
|
||||||
self.into_alt(Function::Output).transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Gpio`
|
|
||||||
/// structure in the `Input` state.
|
|
||||||
pub fn into_input(self) -> Gpio<Input> {
|
|
||||||
self.into_alt(Function::Input).transition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Output> {
|
|
||||||
/// Sets (turns on) the pin.
|
|
||||||
pub fn set(&mut self) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
self.registers.SET[index as usize].write(1 << (self.pin - index * 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears (turns off) the pin.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
self.registers.CLR[index as usize].write(1 << (self.pin - index * 32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Input> {
|
|
||||||
/// Reads the pin's value. Returns `true` if the level is high and `false`
|
|
||||||
/// if the level is low.
|
|
||||||
pub fn level(&mut self) -> bool {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
let high = 1 << (self.pin - index * 32);
|
|
||||||
(self.registers.LEV[index as usize].read() & high) == high
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
use crate::IO_BASE;
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
const INT_BASE: usize = IO_BASE + 0xB000 + 0x200;
|
|
||||||
|
|
||||||
/// Allowed interrupts (ref: peripherals 7.5, page 113)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
pub enum Interrupt {
|
|
||||||
Timer1 = 1,
|
|
||||||
Timer3 = 3,
|
|
||||||
Usb = 9,
|
|
||||||
Aux = 29,
|
|
||||||
Gpio0 = 49,
|
|
||||||
Gpio1 = 50,
|
|
||||||
Gpio2 = 51,
|
|
||||||
Gpio3 = 52,
|
|
||||||
Uart = 57,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupts registers starting from `INT_BASE` (ref: peripherals 7.5, page 112)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
IRQBasicPending: ReadOnly<u32>,
|
|
||||||
IRQPending: [ReadOnly<u32>; 2],
|
|
||||||
FIQControl: Volatile<u32>,
|
|
||||||
EnableIRQ: [Volatile<u32>; 2],
|
|
||||||
EnableBasicIRQ: Volatile<u32>,
|
|
||||||
DisableIRQ: [Volatile<u32>; 2],
|
|
||||||
DisableBasicIRQ: Volatile<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pending interrupts
|
|
||||||
pub struct PendingInterrupts(u64);
|
|
||||||
|
|
||||||
impl Iterator for PendingInterrupts {
|
|
||||||
type Item = usize;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let int = self.0.trailing_zeros();
|
|
||||||
if int < 64 {
|
|
||||||
self.0 &= !(1 << int);
|
|
||||||
Some(int as usize)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An interrupt controller. Used to enable and disable interrupts as well as to
|
|
||||||
/// check if an interrupt is pending.
|
|
||||||
pub struct Controller {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Controller {
|
|
||||||
/// Returns a new handle to the interrupt controller.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Controller {
|
|
||||||
Controller {
|
|
||||||
registers: unsafe { &mut *(INT_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables the interrupt `int`.
|
|
||||||
pub fn enable(&mut self, int: Interrupt) {
|
|
||||||
self.registers.EnableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables the interrupt `int`.
|
|
||||||
pub fn disable(&mut self, int: Interrupt) {
|
|
||||||
self.registers.DisableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if `int` is pending. Otherwise, returns `false`.
|
|
||||||
pub fn is_pending(&self, int: Interrupt) -> bool {
|
|
||||||
self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return all pending interrupts.
|
|
||||||
pub fn pending_interrupts(&self) -> PendingInterrupts {
|
|
||||||
let irq1 = self.registers.IRQPending[0].read() as u64;
|
|
||||||
let irq2 = self.registers.IRQPending[1].read() as u64;
|
|
||||||
PendingInterrupts((irq2 << 32) | irq1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(asm)]
|
|
||||||
|
|
||||||
extern crate volatile;
|
|
||||||
|
|
||||||
pub mod gpio;
|
|
||||||
pub mod timer;
|
|
||||||
pub mod mailbox;
|
|
||||||
pub mod mini_uart;
|
|
||||||
pub mod interrupt;
|
|
||||||
|
|
||||||
pub const IO_BASE: usize = 0x3F000000;
|
|
@ -1,80 +0,0 @@
|
|||||||
use crate::IO_BASE;
|
|
||||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
|
||||||
|
|
||||||
/// The base address for the `MU` registers.
|
|
||||||
const MAILBOX_BASE: usize = IO_BASE + 0xB000 + 0x880;
|
|
||||||
|
|
||||||
/// Available mailbox channels
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum MailboxChannel {
|
|
||||||
Framebuffer = 1,
|
|
||||||
Property = 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read from mailbox status register (MAILx_STA).
|
|
||||||
#[repr(u32)]
|
|
||||||
enum MailboxStatus {
|
|
||||||
MailboxEmpty = 1 << 30,
|
|
||||||
MailboxFull = 1 << 31,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mailbox registers. We basically only support mailbox 0 & 1. We
|
|
||||||
/// deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
|
|
||||||
/// BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
|
|
||||||
/// the placement of memory barriers.
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
MAIL0_RD: ReadOnly<u32>, // 0x00
|
|
||||||
__reserved0: [u32; 3],
|
|
||||||
MAIL0_POL: ReadOnly<u32>, // 0x10
|
|
||||||
MAIL0_SND: ReadOnly<u32>, // 0x14
|
|
||||||
MAIL0_STA: ReadOnly<u32>, // 0x18
|
|
||||||
MAIL0_CNF: Volatile<u32>, // 0x1c
|
|
||||||
|
|
||||||
MAIL1_WRT: WriteOnly<u32>, // 0x20
|
|
||||||
__reserved1: [u32; 3],
|
|
||||||
_MAIL1_POL: ReadOnly<u32>, // 0x30
|
|
||||||
_MAIL1_SND: ReadOnly<u32>, // 0x34
|
|
||||||
MAIL1_STA: ReadOnly<u32>, // 0x38
|
|
||||||
_MAIL1_CNF: Volatile<u32>, // 0x3c
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi's mailbox.
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes)
|
|
||||||
pub struct Mailbox {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mailbox {
|
|
||||||
/// Returns a new instance of `Mailbox`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Mailbox {
|
|
||||||
Mailbox {
|
|
||||||
registers: unsafe { &mut *(MAILBOX_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read from the requested channel of mailbox 0.
|
|
||||||
pub fn read(&self, channel: MailboxChannel) -> u32 {
|
|
||||||
loop {
|
|
||||||
while self.registers.MAIL0_STA.read() & (MailboxStatus::MailboxEmpty as u32) != 0 {}
|
|
||||||
let data = self.registers.MAIL0_RD.read();
|
|
||||||
if data & 0xF == channel as u32 {
|
|
||||||
return data & !0xF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write to the requested channel of mailbox 1.
|
|
||||||
pub fn write(&mut self, channel: MailboxChannel, data: u32) {
|
|
||||||
while self.registers.MAIL1_STA.read() & (MailboxStatus::MailboxFull as u32) != 0 {}
|
|
||||||
self.registers.MAIL1_WRT.write((data & !0xF) | (channel as u32));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
use crate::IO_BASE;
|
|
||||||
use crate::gpio::{Function, Gpio};
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
/// The `AUXENB` register from page 9 of the BCM2837 documentation.
|
|
||||||
const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>;
|
|
||||||
|
|
||||||
/// The base address for the `MU` registers.
|
|
||||||
const MU_REG_BASE: usize = IO_BASE + 0x215040;
|
|
||||||
|
|
||||||
/// Enum representing bit fields of the `AUX_MU_IIR_REG` register.
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum MiniUartInterruptId {
|
|
||||||
Transmit = 0b010,
|
|
||||||
Recive = 0b100,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enum representing bit fields of the `AUX_MU_LSR_REG` register.
|
|
||||||
#[repr(u8)]
|
|
||||||
enum LsrStatus {
|
|
||||||
DataReady = 1,
|
|
||||||
TxAvailable = 1 << 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
AUX_MU_IO_REG: Volatile<u8>,
|
|
||||||
__r0: [u8; 3],
|
|
||||||
AUX_MU_IER_REG: Volatile<u8>,
|
|
||||||
__r1: [u8; 3],
|
|
||||||
AUX_MU_IIR_REG: Volatile<u8>,
|
|
||||||
__r2: [u8; 3],
|
|
||||||
AUX_MU_LCR_REG: Volatile<u8>,
|
|
||||||
__r3: [u8; 3],
|
|
||||||
AUX_MU_MCR_REG: Volatile<u8>,
|
|
||||||
__r4: [u8; 3],
|
|
||||||
AUX_MU_LSR_REG: ReadOnly<u8>,
|
|
||||||
__r5: [u8; 3],
|
|
||||||
AUX_MU_MSR_REG: ReadOnly<u8>,
|
|
||||||
__r6: [u8; 3],
|
|
||||||
AUX_MU_SCRATCH: Volatile<u8>,
|
|
||||||
__r7: [u8; 3],
|
|
||||||
AUX_MU_CNTL_REG: Volatile<u8>,
|
|
||||||
__r8: [u8; 3],
|
|
||||||
AUX_MU_STAT_REG: ReadOnly<u32>,
|
|
||||||
AUX_MU_BAUD_REG: Volatile<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi's "mini UART".
|
|
||||||
pub struct MiniUart {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
timeout: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MiniUart {
|
|
||||||
/// Returns a new instance of `MiniUart`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> MiniUart {
|
|
||||||
let registers = unsafe { &mut *(MU_REG_BASE as *mut Registers) };
|
|
||||||
|
|
||||||
MiniUart {
|
|
||||||
registers: registers,
|
|
||||||
timeout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initializes the mini UART by enabling it as an auxiliary peripheral,
|
|
||||||
/// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud
|
|
||||||
/// divider of 270), setting GPIO pins 14 and 15 to alternative function 5
|
|
||||||
/// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver.
|
|
||||||
///
|
|
||||||
/// By default, reads will never time out. To set a read timeout, use
|
|
||||||
/// `set_read_timeout()`.
|
|
||||||
pub fn init(&mut self) {
|
|
||||||
// Enable the mini UART as an auxiliary device.
|
|
||||||
unsafe { (*AUX_ENABLES).write(1) }
|
|
||||||
|
|
||||||
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
|
|
||||||
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
|
|
||||||
|
|
||||||
self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
|
|
||||||
self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts
|
|
||||||
self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
|
|
||||||
self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
|
|
||||||
self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
|
|
||||||
|
|
||||||
self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the read timeout to `milliseconds` milliseconds.
|
|
||||||
pub fn set_read_timeout(&mut self, milliseconds: u32) {
|
|
||||||
self.timeout = Some(milliseconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the byte `byte`. This method blocks until there is space available
|
|
||||||
/// in the output FIFO.
|
|
||||||
pub fn write_byte(&mut self, byte: u8) {
|
|
||||||
while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {}
|
|
||||||
self.registers.AUX_MU_IO_REG.write(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if there is at least one byte ready to be read. If this
|
|
||||||
/// method returns `true`, a subsequent call to `read_byte` is guaranteed to
|
|
||||||
/// return immediately. This method does not block.
|
|
||||||
pub fn has_byte(&self) -> bool {
|
|
||||||
self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blocks until there is a byte ready to read. If a read timeout is set,
|
|
||||||
/// this method blocks for at most that amount of time. Otherwise, this
|
|
||||||
/// method blocks indefinitely until there is a byte to read.
|
|
||||||
///
|
|
||||||
/// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the
|
|
||||||
/// timeout expired while waiting for a byte to be ready. If this method
|
|
||||||
/// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to
|
|
||||||
/// return immediately.
|
|
||||||
pub fn wait_for_byte(&self) -> Result<(), ()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a byte. Blocks indefinitely until a byte is ready to be read.
|
|
||||||
pub fn read_byte(&self) -> u8 {
|
|
||||||
while !self.has_byte() {}
|
|
||||||
self.registers.AUX_MU_IO_REG.read()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending.
|
|
||||||
pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool {
|
|
||||||
self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
extern crate aarch64;
|
|
||||||
|
|
||||||
use super::BasicTimer;
|
|
||||||
use aarch64::regs::*;
|
|
||||||
use volatile::*;
|
|
||||||
|
|
||||||
/// The base address for the ARM generic timer, IRQs, mailboxes
|
|
||||||
const GEN_TIMER_REG_BASE: usize = 0x40000000;
|
|
||||||
|
|
||||||
/// Core interrupt sources (ref: QA7 4.10, page 16)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
enum CoreInterrupt {
|
|
||||||
CNTPSIRQ = 0,
|
|
||||||
CNTPNSIRQ = 1,
|
|
||||||
CNTHPIRQ = 2,
|
|
||||||
CNTVIRQ = 3,
|
|
||||||
Mailbox0 = 4,
|
|
||||||
Mailbox1 = 5,
|
|
||||||
Mailbox2 = 6,
|
|
||||||
Mailbox3 = 7,
|
|
||||||
Gpu = 8,
|
|
||||||
Pmu = 9,
|
|
||||||
AxiOutstanding = 10,
|
|
||||||
LocalTimer = 11,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timer, IRQs, mailboxes registers (ref: QA7 chapter 4, page 7)
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Registers {
|
|
||||||
CONTROL: Volatile<u32>,
|
|
||||||
_unused1: [Volatile<u32>; 8],
|
|
||||||
LOCAL_IRQ: Volatile<u32>,
|
|
||||||
_unused2: [Volatile<u32>; 3],
|
|
||||||
LOCAL_TIMER_CTL: Volatile<u32>,
|
|
||||||
LOCAL_TIMER_FLAGS: Volatile<u32>,
|
|
||||||
_unused3: Volatile<u32>,
|
|
||||||
CORE_TIMER_IRQCNTL: [Volatile<u32>; 4],
|
|
||||||
CORE_MAILBOX_IRQCNTL: [Volatile<u32>; 4],
|
|
||||||
CORE_IRQ_SRC: [Volatile<u32>; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The ARM generic timer.
|
|
||||||
pub struct GenericTimer {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicTimer for GenericTimer {
|
|
||||||
fn new() -> Self {
|
|
||||||
GenericTimer {
|
|
||||||
registers: unsafe { &mut *(GEN_TIMER_REG_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self) {
|
|
||||||
self.registers.CORE_TIMER_IRQCNTL[0].write(1 << (CoreInterrupt::CNTPNSIRQ as u8));
|
|
||||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&self) -> u64 {
|
|
||||||
let cntfrq = CNTFRQ_EL0.get(); // 62500000
|
|
||||||
(CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_in(&mut self, us: u32) {
|
|
||||||
let cntfrq = CNTFRQ_EL0.get(); // 62500000
|
|
||||||
CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pending(&self) -> bool {
|
|
||||||
self.registers.CORE_IRQ_SRC[0].read() & (1 << (CoreInterrupt::CNTPNSIRQ as u8)) != 0
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
#[cfg(feature = "use_generic_timer")]
|
|
||||||
mod generic_timer;
|
|
||||||
#[cfg(feature = "use_generic_timer")]
|
|
||||||
pub use self::generic_timer::GenericTimer as Timer;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "use_generic_timer"))]
|
|
||||||
mod system_timer;
|
|
||||||
#[cfg(not(feature = "use_generic_timer"))]
|
|
||||||
pub use self::system_timer::SystemTimer as Timer;
|
|
||||||
|
|
||||||
/// The Raspberry Pi timer.
|
|
||||||
pub trait BasicTimer {
|
|
||||||
/// Returns a new instance.
|
|
||||||
fn new() -> Self;
|
|
||||||
|
|
||||||
/// Initialization timer.
|
|
||||||
fn init(&mut self);
|
|
||||||
|
|
||||||
/// Reads the timer's counter and returns the 64-bit counter value.
|
|
||||||
/// The returned value is the number of elapsed microseconds.
|
|
||||||
fn read(&self) -> u64;
|
|
||||||
|
|
||||||
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
|
|
||||||
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
|
|
||||||
/// interrupt will be issued in `us` microseconds.
|
|
||||||
fn tick_in(&mut self, us: u32);
|
|
||||||
|
|
||||||
/// Returns `true` if timer interruption is pending. Otherwise, returns `false`.
|
|
||||||
fn is_pending(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// wait for `cycle` CPU cycles
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn delay(cycle: u32) {
|
|
||||||
for _ in 0..cycle {
|
|
||||||
unsafe { asm!("nop") }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use super::BasicTimer;
|
|
||||||
use crate::interrupt::{Controller, Interrupt};
|
|
||||||
use crate::IO_BASE;
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
/// The base address for the ARM system timer registers.
|
|
||||||
const TIMER_REG_BASE: usize = IO_BASE + 0x3000;
|
|
||||||
|
|
||||||
/// System timer registers (ref: peripherals 12.1, page 172)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
CS: Volatile<u32>,
|
|
||||||
CLO: ReadOnly<u32>,
|
|
||||||
CHI: ReadOnly<u32>,
|
|
||||||
COMPARE: [Volatile<u32>; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
enum SystemTimerId {
|
|
||||||
Timer0 = 0,
|
|
||||||
Timer1 = 1,
|
|
||||||
Timer2 = 2,
|
|
||||||
Timer3 = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi ARM system timer.
|
|
||||||
pub struct SystemTimer {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicTimer for SystemTimer {
|
|
||||||
fn new() -> Self {
|
|
||||||
SystemTimer {
|
|
||||||
registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self) {
|
|
||||||
Controller::new().enable(Interrupt::Timer1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&self) -> u64 {
|
|
||||||
let low = self.registers.CLO.read();
|
|
||||||
let high = self.registers.CHI.read();
|
|
||||||
((high as u64) << 32) | (low as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_in(&mut self, us: u32) {
|
|
||||||
let current_low = self.registers.CLO.read();
|
|
||||||
let compare = current_low.wrapping_add(us);
|
|
||||||
self.registers.COMPARE[SystemTimerId::Timer1 as usize].write(compare);
|
|
||||||
self.registers.CS.write(1 << (SystemTimerId::Timer1 as usize)); // unmask
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pending(&self) -> bool {
|
|
||||||
let controller = Controller::new();
|
|
||||||
controller.is_pending(Interrupt::Timer1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,8 @@
|
|||||||
|
.section .text.entry
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
ldr x0, =bootstacktop
|
||||||
|
mov sp, x0
|
||||||
|
bl rust_main
|
||||||
|
1: b 1b
|
@ -1,8 +1,8 @@
|
|||||||
pub const RECURSIVE_INDEX: usize = 0o777;
|
pub const RECURSIVE_INDEX: usize = 0o777;
|
||||||
pub const KERNEL_OFFSET: usize = 0;
|
pub const KERNEL_OFFSET: usize = 0xFFFF_0000_0000_0000;
|
||||||
pub const KERNEL_PML4: usize = 0;
|
pub const KERNEL_PML4: usize = 0;
|
||||||
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024;
|
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024;
|
||||||
pub const MEMORY_OFFSET: usize = 0;
|
pub const MEMORY_OFFSET: usize = 0;
|
||||||
pub const USER_STACK_OFFSET: usize = 0xffff_8000_0000_0000;
|
pub const USER_STACK_OFFSET: usize = 0x0000_8000_0000_0000 - USER_STACK_SIZE;
|
||||||
pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024;
|
pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024;
|
||||||
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
|
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
|
Loading…
Reference in new issue