Compare commits
57 Commits
explore/ca
...
develop
Author | SHA1 | Date |
---|---|---|
|
da9f2940ba | |
|
30db2405ef | |
|
a9a97d132d | |
|
f4418c3164 | |
|
637304bdb3 | |
|
a95d4e3fb0 | |
|
162592beaa | |
|
cfe4a230de | |
|
3c57c6e2df | |
|
2d9da8c5db | |
|
0234f2136e | |
|
e5a3ea6998 | |
|
7f4e9de6d5 | |
|
8f26c6fa44 | |
|
f76fab3fff | |
|
9cbc6ce80f | |
|
8b6a585250 | |
|
ea97d29c3c | |
|
61eb2f9538 | |
|
16ec45b97c | |
|
33dbf79041 | |
|
1e17e03a8d | |
|
721af870bf | |
|
40782ea7cc | |
|
4cdeeb8556 | |
|
b26d61cb67 | |
|
cc4170200f | |
|
e3f199f89f | |
|
af3dc82c76 | |
|
7d03ea85a2 | |
|
ca263b33a1 | |
|
90389705a7 | |
|
5e1bbf9758 | |
|
503f43d983 | |
|
5a304557a7 | |
|
f6da27062f | |
|
e6ddbb76e7 | |
|
d706b2edac | |
|
115c93e3f8 | |
|
062591fb48 | |
|
36b2d92515 | |
|
7af16897ec | |
|
a9b9aac50d | |
|
155ae413b4 | |
|
e4b12e2c45 | |
|
79f7ebcaab | |
|
77e3423273 | |
|
0f4824afd3 | |
|
1644f299fe | |
|
82d44a9c62 | |
|
856c4a9e68 | |
|
6281204062 | |
|
d3f561d214 | |
|
01906a02bc | |
|
50e955c6a7 | |
|
601cf7a784 | |
|
147a88acb4 |
|
@ -1,15 +1,12 @@
|
|||
[unstable]
|
||||
build-std = ["core", "compiler_builtins", "alloc"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
[build]
|
||||
# https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199/12
|
||||
pipelining = true
|
||||
|
||||
[target.aarch64-vesper-metta]
|
||||
rustflags = [
|
||||
"-C", "target-feature=-fp-armv8",
|
||||
"-C", "target-cpu=cortex-a53",
|
||||
"-C", "embed-bitcode=yes",
|
||||
"-C", "target-feature=-fp-armv8",
|
||||
"-C", "target-cpu=cortex-a53",
|
||||
"-C", "embed-bitcode=yes",
|
||||
"-Z", "macro-backtrace",
|
||||
]
|
||||
runner = "cargo make test-runner"
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: cortex-a
|
||||
versions:
|
||||
- 5.1.2
|
||||
- 5.1.3
|
||||
- 5.1.4
|
||||
- 5.1.5
|
||||
- 5.1.6
|
||||
- dependency-name: qemu-exit
|
||||
versions:
|
||||
- 1.0.2
|
||||
- dependency-name: register
|
||||
versions:
|
||||
- 1.0.2
|
|
@ -42,12 +42,20 @@ jobs:
|
|||
rustc -Vv
|
||||
cargo -Vv
|
||||
|
||||
- name: "Install Rustup Components"
|
||||
run: rustup component add rust-src llvm-tools-preview
|
||||
|
||||
- name: "Install build tools"
|
||||
run: cargo install cargo-make cargo-binutils
|
||||
|
||||
- name: "Prepare packages (Linux)"
|
||||
run: |
|
||||
sudo apt install software-properties-common
|
||||
sudo add-apt-repository ppa:jacob/virtualisation
|
||||
sudo apt update
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: "Install dev libraries (Linux)"
|
||||
run: sudo apt install libudev-dev
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: "Validate rust-lld"
|
||||
run: |
|
||||
which rust-lld || echo "Not found"
|
||||
|
@ -66,9 +74,6 @@ jobs:
|
|||
|
||||
- name: Install QEMU (Linux)
|
||||
run: |
|
||||
sudo apt install software-properties-common
|
||||
sudo add-apt-repository ppa:jacob/virtualisation
|
||||
sudo apt update
|
||||
sudo apt install qemu-system-aarch64
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
|
@ -104,8 +109,13 @@ jobs:
|
|||
- name: 'Build kernel'
|
||||
run: cargo make build
|
||||
|
||||
- name: 'Run tests'
|
||||
- name: 'Run tests (macOS)'
|
||||
run: cargo make test
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: 'Run tests (other OSes)'
|
||||
run: env QEMU_MACHINE=raspi3 cargo make test
|
||||
if: runner.os != 'macOS'
|
||||
|
||||
check_formatting:
|
||||
name: "Check Formatting"
|
||||
|
@ -133,7 +143,8 @@ jobs:
|
|||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: rustup toolchain install nightly --profile minimal --component clippy
|
||||
- run: rustup component add rust-src llvm-tools-preview
|
||||
- run: sudo apt update
|
||||
- run: sudo apt install libudev-dev
|
||||
- run: rustup toolchain install nightly
|
||||
- run: cargo install cargo-make
|
||||
- run: env CLIPPY_FEATURES=${{ matrix.features }} cargo make clippy
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
.idea/
|
||||
*.iml
|
||||
.nova/
|
||||
.vscode/
|
||||
target/
|
||||
kernel8*
|
||||
.gdb_history
|
||||
|
|
|
@ -1,5 +1,60 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "CoreFoundation-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mach 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "IOKit-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
|
||||
dependencies = [
|
||||
"CoreFoundation-sys",
|
||||
"libc",
|
||||
"mach 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.1"
|
||||
|
@ -8,9 +63,21 @@ checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -19,14 +86,95 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-a"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc37a4862a4f5b40df96f5e0f3bd4ce6a1bd8bb59a965cd476844673faf83896"
|
||||
name = "chainboot"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"register",
|
||||
"bit_field",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"machine",
|
||||
"r0",
|
||||
"seahash",
|
||||
"snafu",
|
||||
"tock-registers",
|
||||
"usize_conversions",
|
||||
"ux",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chainofcommand"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"defer",
|
||||
"fehler",
|
||||
"futures",
|
||||
"seahash",
|
||||
"tokio",
|
||||
"tokio-serial",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-a"
|
||||
version = "7.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd95fd055d118f77d4e4d527201b6ceccd13586b19b4dac1270f7081fef0f98"
|
||||
dependencies = [
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77b75a27dc8d220f1f8521ea69cd55a34d720a200ebb3a624d9aa19193d3b432"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.12.0",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defer"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "647605a6345d5e89c3950a36a638c56478af9b414c55c6f2477c73b115f9acde"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
|
@ -34,25 +182,421 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
name = "fehler"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
checksum = "d5729fe49ba028cd550747b6e62cd3d841beccab5390aa398538c31a2d983635"
|
||||
dependencies = [
|
||||
"fehler-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fehler-macros"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccb5acb1045ebbfa222e2c50679e392a71dd77030b78fb0189f2d9c5974400f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "machine"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"qemu-exit",
|
||||
"r0",
|
||||
"snafu",
|
||||
"tock-registers",
|
||||
"usize_conversions",
|
||||
"ux",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-serial"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f47dcdec193b77e2166faa0e3be4ee04f324ce6780c54efe7af6c3ee7360e4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mio",
|
||||
"nix 0.22.3",
|
||||
"serialport",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.22.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nucleus"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"machine",
|
||||
"r0",
|
||||
"snafu",
|
||||
"tock-registers",
|
||||
"usize_conversions",
|
||||
"ux",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qemu-exit"
|
||||
version = "1.0.1"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e64f0ef443037525a562c4fa6ad3460a351ec10c7ebb0b6c49a87a576d0d1b5"
|
||||
checksum = "9ff023245bfcc73fb890e1f8d5383825b3131cc920020a5c487d6f113dfc428a"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -64,19 +608,105 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
|
||||
|
||||
[[package]]
|
||||
name = "register"
|
||||
version = "1.0.1"
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf386c0c48fea132b7fbbf71c32d1b1d62e7c59043bbabdd15c9ed9f254517e"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"tock-registers",
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.6.10"
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.0.1"
|
||||
source = "git+https://github.com/metta-systems/serialport-rs?branch=macos-ENOTTY-fix#91ee1f740ee7d7c9506df8af082860a478656e18"
|
||||
dependencies = [
|
||||
"CoreFoundation-sys",
|
||||
"IOKit-sys",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"mach 0.2.3",
|
||||
"nix 0.23.1",
|
||||
"regex",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eba135d2c579aa65364522eb78590cdf703176ef71ad4c32b00f58f7afb2df5"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"snafu-derive",
|
||||
|
@ -84,20 +714,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.6.10"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
|
||||
checksum = "7a7fe9b0669ef117c5cabc5549638528f36771f058ff977d7689deb517833a75"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.48"
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -105,16 +742,94 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.6.0"
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot 0.11.2",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-serial"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75b04fbb029a3f77272b3356453c97c5dbdebcf28aa248a34fd80a442f2dda1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures",
|
||||
"log",
|
||||
"mio-serial",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "usize_conversions"
|
||||
|
@ -129,17 +844,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
|
||||
|
||||
[[package]]
|
||||
name = "vesper"
|
||||
version = "0.0.1"
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"qemu-exit",
|
||||
"r0",
|
||||
"register",
|
||||
"snafu",
|
||||
"usize_conversions",
|
||||
"ux",
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"nucleus"
|
||||
"nucleus",
|
||||
"bin/chainboot",
|
||||
"bin/chainofcommand"
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
serialport = { git = "https://github.com/metta-systems/serialport-rs", branch = "macos-ENOTTY-fix" }
|
||||
|
||||
[profile.dev]
|
||||
# See https://github.com/rust-lang/cargo/issues/7359 about why panic=abort is not working here.
|
||||
# It is still defined in the target JSON so not stricly necessary to specify it here anyway.
|
||||
|
|
99
Justfile
99
Justfile
|
@ -1,52 +1,111 @@
|
|||
qemu:
|
||||
# Build and run kernel in QEMU
|
||||
cargo make qemu
|
||||
_default:
|
||||
@just --list
|
||||
|
||||
qemu-gdb:
|
||||
# Build and run kernel in QEMU with GDB port enabled
|
||||
cargo make qemu-gdb
|
||||
# Build default hw kernel and run chainofcommand to boot this kernel onto the board
|
||||
boot: chainofcommand
|
||||
cargo make chainboot
|
||||
|
||||
device:
|
||||
# Build and write kernel to an SD Card
|
||||
cargo make sdcard
|
||||
# Build and run kernel in QEMU with serial port emulation
|
||||
zellij:
|
||||
cargo make zellij-nucleus
|
||||
zellij --layout-path emulation/layout.zellij
|
||||
|
||||
build:
|
||||
# Build default hw kernel
|
||||
# Build and run chainboot in QEMU with serial port emulation
|
||||
zellij-cb:
|
||||
# Connect to it via chainofcommand to load an actual kernel
|
||||
# TODO: actually run chainofcommand in a zellij session too
|
||||
cargo make zellij-cb
|
||||
zellij --layout-path emulation/layout.zellij
|
||||
|
||||
# Build chainofcommand serial loader
|
||||
chainofcommand:
|
||||
cd bin/chainofcommand
|
||||
cargo make build
|
||||
|
||||
# Build and run kernel in QEMU
|
||||
qemu:
|
||||
cargo make qemu
|
||||
|
||||
# Build and run kernel in QEMU with GDB port enabled
|
||||
qemu-gdb:
|
||||
cargo make qemu-gdb
|
||||
|
||||
# Build and run chainboot in QEMU
|
||||
qemu-cb:
|
||||
# Connect to it via chainofcommand to load an actual kernel
|
||||
cargo make qemu-cb
|
||||
|
||||
# Build and write kernel to an SD Card
|
||||
device:
|
||||
cargo make sdcard
|
||||
|
||||
# Build and write kernel to an SD Card, then eject the SD Card volume
|
||||
device-eject:
|
||||
cargo make sdeject
|
||||
|
||||
# Build and write chainboot to an SD Card, then eject the SD Card volume
|
||||
cb-eject:
|
||||
cd bin/chainboot
|
||||
cargo make cb-eject
|
||||
|
||||
# Build default hw kernel
|
||||
build:
|
||||
cargo make build
|
||||
cargo make kernel-binary
|
||||
|
||||
# Clean project
|
||||
clean:
|
||||
# Clean project
|
||||
cargo make clean
|
||||
rm -f kernel8 kernel8.img
|
||||
|
||||
# Run clippy checks
|
||||
clippy:
|
||||
# Run clippy checks
|
||||
# TODO: use cargo-hack
|
||||
cargo make clippy
|
||||
env CLIPPY_FEATURES=noserial cargo make clippy
|
||||
env CLIPPY_FEATURES=qemu cargo make clippy
|
||||
env CLIPPY_FEATURES=noserial,qemu cargo make clippy
|
||||
env CLIPPY_FEATURES=jtag cargo make clippy
|
||||
env CLIPPY_FEATURES=noserial,jtag cargo make clippy
|
||||
|
||||
# Run tests in QEMU
|
||||
test:
|
||||
# Run tests in QEMU
|
||||
cargo make test
|
||||
|
||||
alias disasm := hopper
|
||||
|
||||
# Build and disassemble kernel
|
||||
hopper:
|
||||
# Build and disassemble kernel
|
||||
cargo make hopper
|
||||
|
||||
alias ocd := openocd
|
||||
|
||||
# Start openocd (by default connected via JTAG to a target device)
|
||||
openocd:
|
||||
# Start openocd (by default connected via JTAG to a target device)
|
||||
cargo make openocd
|
||||
|
||||
# Build and run kernel in GDB using openocd or QEMU as target (gdb port 5555)
|
||||
gdb:
|
||||
# Build and run kernel in GDB using openocd or QEMU as target (gdb port 5555)
|
||||
cargo make gdb
|
||||
|
||||
# Build and run chainboot in GDB using openocd or QEMU as target (gdb port 5555)
|
||||
gdb-cb:
|
||||
cargo make gdb-cb
|
||||
|
||||
# Build and print all symbols in the kernel
|
||||
nm:
|
||||
# Build and print all symbols in the kernel
|
||||
cargo make nm
|
||||
|
||||
# Check formatting
|
||||
fmt-check:
|
||||
cargo fmt -- --check
|
||||
|
||||
# Run `cargo expand` on nucleus
|
||||
expand:
|
||||
# Run `cargo expand` on modules
|
||||
cargo make expand -- nucleus
|
||||
|
||||
# Generate and open documentation
|
||||
doc:
|
||||
cargo make docs-flow
|
||||
|
||||
# Run CI tasks
|
||||
ci: clean build test clippy fmt-check
|
||||
|
|
142
Makefile.toml
142
Makefile.toml
|
@ -17,14 +17,22 @@ DEFAULT_TARGET = "aarch64-vesper-metta"
|
|||
# Pass TARGET env var if it does not match the default target above.
|
||||
TARGET = { value = "${DEFAULT_TARGET}", condition = { env_not_set = ["TARGET"] } }
|
||||
|
||||
# Name of the target board "rpi3" or "rpi4"
|
||||
TARGET_BOARD = { value = "rpi4", condition = { env_not_set = ["TARGET_BOARD"] } }
|
||||
# Name of the DTB file for target board configuration, use bcm2710-rpi-3-b-plus.dtb for RasPi3B+
|
||||
TARGET_DTB = { value = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2711-rpi-4-b.dtb", condition = { env_not_set = ["TARGET_DTB"] } }
|
||||
|
||||
# AArch64 QEMU binary
|
||||
QEMU = { value = "qemu-system-aarch64", condition = { env_not_set = ["QEMU"] } }
|
||||
# QEMU machine type, defaults to raspi3b but CI runners override it due to ancient QEMU versions they use.
|
||||
QEMU_MACHINE = { value = "raspi3b", condition = { env_not_set = ["QEMU_MACHINE"] } }
|
||||
|
||||
# An aarch64-enabled GDB
|
||||
GDB = { value = "/usr/local/opt/gdb-8.2.1-aarhc64/bin/aarch64-linux-elf-gdb", condition = { env_not_set = ["GDB"] } }
|
||||
GDB = { value = "/usr/local/opt/gdb/HEAD-a2c58332-aarch64/bin/aarch64-unknown-elf-gdb", condition = { env_not_set = ["GDB"] } }
|
||||
|
||||
# OpenOCD with JLink support and RTT patch from http://openocd.zylin.com/#/c/4055/11
|
||||
OPENOCD = { value = "/usr/local/openocd-aeb7b327-rtt/bin/openocd", condition = { env_not_set = ["OPENOCD"] } }
|
||||
# OpenOCD with JLink support
|
||||
# (RTT patch from http://openocd.zylin.com/#/c/4055/11 has already been merged into main line)
|
||||
OPENOCD = { value = "/usr/local/opt/openocd/4d6519593-rtt/bin/openocd", condition = { env_not_set = ["OPENOCD"] } }
|
||||
|
||||
# Mounted sdcard partition path
|
||||
VOLUME = { value = "/Volumes/BOOT", condition = { env_not_set = ["VOLUME"] } }
|
||||
|
@ -34,10 +42,12 @@ VOLUME = { value = "/Volumes/BOOT", condition = { env_not_set = ["VOLUME"] } }
|
|||
#
|
||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
|
||||
RUST_LIBS = "-Z build-std=compiler_builtins,core,alloc -Z build-std-features=compiler-builtins-mem"
|
||||
TARGET_JSON = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/${TARGET}.json"
|
||||
PLATFORM_TARGET="--target=${TARGET_JSON} --features=${TARGET_FEATURES} ${RUST_LIBS}"
|
||||
|
||||
DEVICE_FEATURES = "noserial"
|
||||
QEMU_FEATURES = "qemu"
|
||||
QEMU_FEATURES = "qemu,rpi3"
|
||||
|
||||
OBJCOPY = "rust-objcopy" # Part of `cargo objcopy` in cargo-binutils
|
||||
OBJCOPY_PARAMS = "--strip-all -O binary"
|
||||
|
@ -50,9 +60,10 @@ QEMU_CONTAINER_CMD = "qemu-system-aarch64"
|
|||
#
|
||||
# Could additionally use -nographic to disable GUI -- this shall be useful for automated tests.
|
||||
#
|
||||
# -d in_asm,unimp,int
|
||||
QEMU_OPTS = "-M raspi3 -d int -semihosting"
|
||||
QEMU_SERIAL_OPTS = "-serial null -serial stdio"
|
||||
# QEMU has renamed the RasPi machines since version 6.2.0, use just `raspi3` for previous versions.
|
||||
QEMU_OPTS = "-M ${QEMU_MACHINE} -d int -semihosting"
|
||||
QEMU_DISASM_OPTS = "-d in_asm,unimp,int"
|
||||
QEMU_SERIAL_OPTS = "-serial pty -serial stdio"
|
||||
QEMU_TESTS_OPTS = "-nographic"
|
||||
# For gdb connection:
|
||||
# - if this is set, MUST have gdb attached for SYS_WRITE0 to work, otherwise QEMU will crash.
|
||||
|
@ -61,8 +72,11 @@ QEMU_GDB_OPTS = "-gdb tcp::5555 -S"
|
|||
|
||||
GDB_CONNECT_FILE = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/gdb-connect"
|
||||
|
||||
KERNEL_ELF = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/kernel8"
|
||||
KERNEL_BIN = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/kernel8.img"
|
||||
KERNEL_ELF = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/release/nucleus"
|
||||
KERNEL_BIN = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/nucleus.bin"
|
||||
|
||||
CHAINBOOT_SERIAL = "/dev/tty.SLAB_USBtoUART"
|
||||
CHAINBOOT_BAUD = 115200
|
||||
|
||||
[tasks.default]
|
||||
alias = "all"
|
||||
|
@ -73,3 +87,113 @@ dependencies = ["kernel-binary"]
|
|||
[tasks.modules]
|
||||
command = "cargo"
|
||||
args = ["modules", "tree"]
|
||||
|
||||
[tasks.build]
|
||||
env = { "TARGET_FEATURES" = "${TARGET_BOARD}" }
|
||||
command = "cargo"
|
||||
args = ["build", "@@split(PLATFORM_TARGET, )", "--release"]
|
||||
|
||||
[tasks.build-qemu]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
command = "cargo"
|
||||
args = ["build", "@@split(PLATFORM_TARGET, )", "--release"]
|
||||
|
||||
[tasks.qemu-runner]
|
||||
dependencies = ["build-qemu", "kernel-binary"]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
script = [
|
||||
"echo Run QEMU ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} with ${KERNEL_BIN}",
|
||||
"${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${TARGET_DTB} -kernel ${KERNEL_BIN}"
|
||||
]
|
||||
|
||||
[tasks.expand]
|
||||
env = { "TARGET_FEATURES" = "" }
|
||||
command = "cargo"
|
||||
args = ["expand", "@@split(PLATFORM_TARGET, )", "--release"]
|
||||
|
||||
[tasks.test]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
command = "cargo"
|
||||
args = ["test", "@@split(PLATFORM_TARGET, )"]
|
||||
|
||||
[tasks.docs]
|
||||
env = { "TARGET_FEATURES" = "" }
|
||||
command = "cargo"
|
||||
args = ["doc", "--open", "--no-deps", "@@split(PLATFORM_TARGET, )"]
|
||||
|
||||
[tasks.clippy]
|
||||
env = { "TARGET_FEATURES" = "rpi3", "CLIPPY_FEATURES" = { value = "--features=${CLIPPY_FEATURES}", condition = { env_set = ["CLIPPY_FEATURES"] } } }
|
||||
command = "cargo"
|
||||
args = ["clippy", "@@split(PLATFORM_TARGET, )", "@@remove-empty(CLIPPY_FEATURES)", "--", "-D", "warnings"]
|
||||
|
||||
# These tasks are written in cargo-make's own script to make it portable across platforms (no `basename` on Windows)
|
||||
[tasks.custom-binary]
|
||||
env = { "BINARY_FILE" = "${BINARY_FILE}" }
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
binaryFile = basename ${BINARY_FILE}
|
||||
outElf = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.elf
|
||||
outBin = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.bin
|
||||
cp ${BINARY_FILE} ${outElf}
|
||||
exec --fail-on-error ${OBJCOPY} %{OBJCOPY_PARAMS} ${BINARY_FILE} ${outBin}
|
||||
echo Copied ${binaryFile} to ${outElf}
|
||||
echo Converted ${binaryFile} to ${outBin}
|
||||
'''
|
||||
]
|
||||
install_crate = { crate_name = "cargo-binutils", binary = "rust-objcopy", test_arg = ["--help"] }
|
||||
|
||||
[tasks.test-binary]
|
||||
env = { "BINARY_FILE" = "${CARGO_MAKE_TASK_ARGS}" }
|
||||
run_task = "custom-binary"
|
||||
|
||||
[tasks.test-runner]
|
||||
dependencies = ["test-binary"]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
binaryFile = basename ${CARGO_MAKE_TASK_ARGS}
|
||||
exec --fail-on-error ${QEMU} %{QEMU_OPTS} %{QEMU_TESTS_OPTS} -dtb ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb -kernel ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.bin
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.gdb-config]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
writefile ${GDB_CONNECT_FILE} "target extended-remote :5555\n"
|
||||
appendfile ${GDB_CONNECT_FILE} "break 0x80000\n"
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.zellij-config]
|
||||
dependencies = ["build-qemu", "kernel-binary"]
|
||||
script_runner = "@duckscript"
|
||||
env = { "ZELLIJ_CONFIG_FILE" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/emulation/zellij-config.sh" }
|
||||
script = [
|
||||
'''
|
||||
writefile ${ZELLIJ_CONFIG_FILE} "QEMU=${QEMU}\n"
|
||||
appendfile ${ZELLIJ_CONFIG_FILE} "QEMU_OPTS=\"${QEMU_OPTS}\"\n"
|
||||
appendfile ${ZELLIJ_CONFIG_FILE} "QEMU_RUNNER_OPTS=${QEMU_RUNNER_OPTS}\n"
|
||||
appendfile ${ZELLIJ_CONFIG_FILE} "CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY=${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}\n"
|
||||
appendfile ${ZELLIJ_CONFIG_FILE} "TARGET_DTB=${TARGET_DTB}\n"
|
||||
appendfile ${ZELLIJ_CONFIG_FILE} "KERNEL_BIN=${KERNEL_BIN}\n"
|
||||
'''
|
||||
]
|
||||
install_crate = { crate_name = "zellij", binary = "zellij", test_arg = ["--help"] }
|
||||
|
||||
[tasks.openocd]
|
||||
script = [
|
||||
"${OPENOCD} -f interface/jlink.cfg -f ../ocd/${TARGET_BOARD}_target.cfg"
|
||||
]
|
||||
|
||||
[tasks.sdeject]
|
||||
dependencies = ["sdcard"]
|
||||
script = [
|
||||
"diskutil unmount ${VOLUME}"
|
||||
]
|
||||
|
||||
[tasks.chainboot]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
command = "echo"
|
||||
args = ["\n***===***\n", "Run the following command in your terminal:\n", " ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/debug/chainofcommand ${CHAINBOOT_SERIAL} ${CHAINBOOT_BAUD} --kernel ${KERNEL_BIN}\n", "***===***\n\n"]
|
||||
|
|
10
README.md
10
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
## About kernel
|
||||
|
||||
Vesper is a capability-based single-address-space exokernel, it tries to remain small and secure. To achieve this, kernel functionality is extremely limited - it provides only address space isolation and IPC, after bootup kernel does not allocate any memory itself.
|
||||
Vesper is a capability-based single-address-space nanokernel, it tries to remain small and secure. To achieve this, kernel functionality is extremely limited - it provides only address space isolation and IPC, after bootup kernel does not allocate any memory itself.
|
||||
|
||||
Exokernel's distinctive trait is that it provides mechanisms but not policies. Vesper tries to move as many policy decisions as possible to the library OS.
|
||||
|
||||
|
@ -94,6 +94,14 @@ You need to have [Hopper](https://hopperapp.com) and hopperv4 cli helper install
|
|||
just disasm
|
||||
```
|
||||
|
||||
### To see other available commands
|
||||
|
||||
```
|
||||
just
|
||||
```
|
||||
|
||||
It will list all just commands with their short descriptions.
|
||||
|
||||
## Development flow
|
||||
|
||||
`mainline`, `develop` and `released` branches:
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
[package]
|
||||
name = "chainboot"
|
||||
version = "0.0.1"
|
||||
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||
description = "Chain boot loader"
|
||||
license = "BlueOak-1.0.0"
|
||||
categories = ["no-std", "embedded", "os"]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[features]
|
||||
default = ["asm"]
|
||||
# Build for running under QEMU with semihosting, so various halt/reboot options would for example quit QEMU instead.
|
||||
qemu = ["machine/qemu"]
|
||||
# Build for debugging it over JTAG/SWD connection - halts on first non-startup function start.
|
||||
jtag = ["machine/jtag"]
|
||||
# Dummy feature, ignored in this crate.
|
||||
noserial = []
|
||||
# Startup relocation code is implemented in assembly
|
||||
asm = []
|
||||
# Mutually exclusive features to choose a target board
|
||||
rpi3 = ["machine/rpi3"]
|
||||
rpi4 = ["machine/rpi4"]
|
||||
|
||||
[dependencies]
|
||||
machine = { path = "../../machine" }
|
||||
r0 = "1.0"
|
||||
cortex-a = "7.0"
|
||||
tock-registers = "0.7"
|
||||
ux = { version = "0.1", default-features = false }
|
||||
usize_conversions = "0.2"
|
||||
bit_field = "0.10"
|
||||
bitflags = "1.3"
|
||||
cfg-if = "1.0"
|
||||
snafu = { version = "0.7", default-features = false }
|
||||
seahash = "4.1"
|
|
@ -0,0 +1,52 @@
|
|||
[env]
|
||||
CHAINBOOT_ELF = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/release/chainboot"
|
||||
CHAINBOOT_BIN = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/chainboot.bin"
|
||||
|
||||
[tasks.kernel-binary]
|
||||
env = { "BINARY_FILE" = "${CHAINBOOT_ELF}" }
|
||||
run_task = "custom-binary"
|
||||
|
||||
[tasks.hopper]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-nucleus]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-cb]
|
||||
env = { "KERNEL_BIN" = "${CHAINBOOT_BIN}", "QEMU_OPTS" = "${QEMU_OPTS} ${QEMU_DISASM_OPTS}" }
|
||||
run_task = "zellij-config"
|
||||
|
||||
[tasks.zellij-cb-gdb]
|
||||
env = { "KERNEL_BIN" = "${CHAINBOOT_BIN}", "QEMU_OPTS" = "${QEMU_OPTS} ${QEMU_DISASM_OPTS} ${QEMU_GDB_OPTS}", "TARGET_BOARD" = "rpi3", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||
run_task = "zellij-config"
|
||||
|
||||
[tasks.qemu]
|
||||
disabled = true
|
||||
|
||||
[tasks.qemu-cb]
|
||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_DISASM_OPTS} -serial pty", "KERNEL_BIN" = "${CHAINBOOT_BIN}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||
extend = "qemu-runner"
|
||||
|
||||
[tasks.gdb]
|
||||
disabled = true
|
||||
|
||||
[tasks.gdb-cb]
|
||||
dependencies = ["build", "kernel-binary", "gdb-config"]
|
||||
env = { "RUST_GDB" = "${GDB}" }
|
||||
script = [
|
||||
"rust-gdb -x ${GDB_CONNECT_FILE} ${CHAINBOOT_ELF}"
|
||||
]
|
||||
|
||||
[tasks.sdcard]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
kernelImage = set "chain_boot_rpi4.img"
|
||||
cp ${CHAINBOOT_BIN} ${VOLUME}/${kernelImage}
|
||||
echo "Copied chainboot to ${VOLUME}/${kernelImage}"
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.cb-eject]
|
||||
dependencies = ["sdeject"]
|
|
@ -0,0 +1,6 @@
|
|||
const LINKER_SCRIPT: &str = "bin/chainboot/src/link.ld";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed={}", LINKER_SCRIPT);
|
||||
println!("cargo:rustc-link-arg=--script={}", LINKER_SCRIPT);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Assembly counterpart to this file.
|
||||
#[cfg(feature = "asm")]
|
||||
core::arch::global_asm!(include_str!("boot.s"));
|
||||
|
||||
// This is quite impossible - the linker constants are resolved to fully constant offsets in asm
|
||||
// version, but are image-relative symbols in rust, and I see no way to force it otherwise.
|
||||
#[no_mangle]
|
||||
#[link_section = ".text._start"]
|
||||
#[cfg(not(feature = "asm"))]
|
||||
pub unsafe extern "C" fn _start() -> ! {
|
||||
use {
|
||||
cortex_a::registers::{MPIDR_EL1, SP},
|
||||
machine::endless_sleep,
|
||||
tock_registers::interfaces::{Readable, Writeable},
|
||||
};
|
||||
|
||||
const CORE_0: u64 = 0;
|
||||
const CORE_MASK: u64 = 0x3;
|
||||
|
||||
if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
|
||||
// if not core0, infinitely wait for events
|
||||
endless_sleep()
|
||||
}
|
||||
|
||||
// These are a problem, because they are not interpreted as constants here.
|
||||
// Subsequently, this code tries to read values from not-yet-existing data locations.
|
||||
extern "C" {
|
||||
// Boundaries of the .bss section, provided by the linker script
|
||||
static mut __bss_start: u64;
|
||||
static mut __bss_end_exclusive: u64;
|
||||
// Load address of the kernel binary
|
||||
static mut __binary_nonzero_lma: u64;
|
||||
// Address to relocate to and image size
|
||||
static mut __binary_nonzero_vma: u64;
|
||||
static mut __binary_nonzero_vma_end_exclusive: u64;
|
||||
// Stack top
|
||||
static mut __boot_core_stack_end_exclusive: u64;
|
||||
}
|
||||
|
||||
// Set stack pointer.
|
||||
SP.set(&mut __boot_core_stack_end_exclusive as *mut u64 as u64);
|
||||
|
||||
// Zeroes the .bss section
|
||||
r0::zero_bss(&mut __bss_start, &mut __bss_end_exclusive);
|
||||
|
||||
// Relocate the code
|
||||
core::ptr::copy_nonoverlapping(
|
||||
&mut __binary_nonzero_lma as *const u64,
|
||||
&mut __binary_nonzero_vma as *mut u64,
|
||||
(&mut __binary_nonzero_vma_end_exclusive as *mut u64 as u64
|
||||
- &mut __binary_nonzero_vma as *mut u64 as u64) as usize,
|
||||
);
|
||||
|
||||
_start_rust();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// The Rust entry of the `kernel` binary.
|
||||
///
|
||||
/// The function is called from the assembly `_start` function, keep it to support "asm" feature.
|
||||
#[no_mangle]
|
||||
#[inline(always)]
|
||||
pub unsafe fn _start_rust(max_kernel_size: u64) -> ! {
|
||||
crate::kernel_init(max_kernel_size)
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
|
||||
// Modifications
|
||||
// Copyright (c) 2021- Berkus <berkus+github@metta.systems>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
// Load the address of a symbol into a register, PC-relative.
|
||||
//
|
||||
// The symbol must lie within +/- 4 GiB of the Program Counter.
|
||||
//
|
||||
// # Resources
|
||||
//
|
||||
// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html
|
||||
.macro ADR_REL register, symbol
|
||||
adrp \register, \symbol
|
||||
add \register, \register, #:lo12:\symbol
|
||||
.endm
|
||||
|
||||
// Load the address of a symbol into a register, absolute.
|
||||
//
|
||||
// # Resources
|
||||
//
|
||||
// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html
|
||||
.macro ADR_ABS register, symbol
|
||||
movz \register, #:abs_g2:\symbol
|
||||
movk \register, #:abs_g1_nc:\symbol
|
||||
movk \register, #:abs_g0_nc:\symbol
|
||||
.endm
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
.section .text._start
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// fn _start()
|
||||
//------------------------------------------------------------------------------
|
||||
_start:
|
||||
// Only proceed on the boot core. Park it otherwise.
|
||||
mrs x1, MPIDR_EL1
|
||||
and x1, x1, 0b11 // core id mask
|
||||
cmp x1, 0 // boot core id
|
||||
b.ne .L_parking_loop
|
||||
|
||||
// If execution reaches here, it is the boot core.
|
||||
|
||||
// Initialize bss.
|
||||
ADR_ABS x0, __bss_start
|
||||
ADR_ABS x1, __bss_end_exclusive
|
||||
|
||||
.L_bss_init_loop:
|
||||
cmp x0, x1
|
||||
b.eq .L_relocate_binary
|
||||
stp xzr, xzr, [x0], #16
|
||||
b .L_bss_init_loop
|
||||
|
||||
// Next, relocate the binary.
|
||||
.L_relocate_binary:
|
||||
ADR_REL x0, __binary_nonzero_lma // The address the binary got loaded to.
|
||||
ADR_ABS x1, __binary_nonzero_vma // The address the binary was linked to.
|
||||
ADR_ABS x2, __binary_nonzero_vma_end_exclusive
|
||||
sub x4, x1, x0 // Get difference between vma and lma as max size
|
||||
|
||||
.L_copy_loop:
|
||||
ldr x3, [x0], #8
|
||||
str x3, [x1], #8
|
||||
cmp x1, x2
|
||||
b.lo .L_copy_loop
|
||||
|
||||
// Prepare the jump to Rust code.
|
||||
// Set the stack pointer.
|
||||
ADR_ABS x0, __rpi_phys_binary_load_addr
|
||||
mov sp, x0
|
||||
|
||||
// Pass maximum kernel size as an argument to Rust init function.
|
||||
mov x0, x4
|
||||
|
||||
// Jump to the relocated Rust code.
|
||||
ADR_ABS x1, _start_rust
|
||||
br x1
|
||||
|
||||
// Infinitely wait for events (aka "park the core").
|
||||
.L_parking_loop:
|
||||
wfe
|
||||
b .L_parking_loop
|
||||
|
||||
.size _start, . - _start
|
||||
.type _start, function
|
||||
.global _start
|
|
@ -0,0 +1,98 @@
|
|||
/* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
|
||||
* Copyright (c) 2021- Berkus <berkus+github@metta.systems>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Information from:
|
||||
* [Output Section Address](https://sourceware.org/binutils/docs/ld/Output-Section-Address.html)
|
||||
* [Output Section LMA](https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html)
|
||||
* [Output Section Attributes](https://sourceware.org/binutils/docs/ld/Output-Section-Attributes.html#Output-Section-Attributes)
|
||||
*/
|
||||
|
||||
/* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */
|
||||
__rpi_phys_binary_load_addr = 0x80000;
|
||||
|
||||
|
||||
ENTRY(__rpi_phys_binary_load_addr)
|
||||
|
||||
/* Flags:
|
||||
* 4 == R
|
||||
* 5 == RX
|
||||
* 6 == RW
|
||||
*
|
||||
* Segments are marked PT_LOAD below so that the ELF file provides virtual and physical addresses.
|
||||
* It doesn't mean all of them need actually be loaded.
|
||||
*/
|
||||
PHDRS
|
||||
{
|
||||
segment_boot_core_stack PT_LOAD FLAGS(6);
|
||||
segment_start_code PT_LOAD FLAGS(5);
|
||||
segment_code PT_LOAD FLAGS(5);
|
||||
segment_data PT_LOAD FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/***********************************************************************************************
|
||||
* Boot Core Stack
|
||||
***********************************************************************************************/
|
||||
.boot_core_stack (NOLOAD) :
|
||||
{
|
||||
/* ^ */
|
||||
/* | stack */
|
||||
. += __rpi_phys_binary_load_addr; /* | growth */
|
||||
/* | direction */
|
||||
__boot_core_stack_end_exclusive = .; /* | */
|
||||
} :segment_boot_core_stack
|
||||
|
||||
. = __rpi_phys_binary_load_addr;
|
||||
|
||||
.text :
|
||||
{
|
||||
KEEP(*(.text._start))
|
||||
/* *(text.memcpy) -- only relevant for Rust relocator impl which is currently impossible */
|
||||
} :segment_start_code
|
||||
|
||||
/* Align to 8 bytes, b/c relocating the binary is done in u64 chunks */
|
||||
. = ALIGN(8);
|
||||
|
||||
__binary_nonzero_lma = .;
|
||||
|
||||
/* Set the link address to 32 MiB */
|
||||
/* This dictates the max size of the loadable kernel. */
|
||||
. += 0x2000000;
|
||||
|
||||
/***********************************************************************************************
|
||||
* Code + RO Data + Global Offset Table
|
||||
***********************************************************************************************/
|
||||
__binary_nonzero_vma = .;
|
||||
.text : AT (ADDR(.text) + SIZEOF(.text))
|
||||
{
|
||||
*(.text._start_rust) /* The Rust entry point */
|
||||
/* *(text.memcpy) -- only relevant for Rust relocator impl which is currently impossible */
|
||||
*(.text*) /* Everything else */
|
||||
} :segment_code
|
||||
|
||||
.rodata : ALIGN(8) { *(.rodata*) } :segment_code
|
||||
.got : ALIGN(8) { *(.got) } :segment_code
|
||||
|
||||
/***********************************************************************************************
|
||||
* Data + BSS
|
||||
***********************************************************************************************/
|
||||
.data : { *(.data*) } :segment_data
|
||||
|
||||
/* Fill up to 8 bytes, b/c relocating the binary is done in u64 chunks */
|
||||
. = ALIGN(8);
|
||||
__binary_nonzero_vma_end_exclusive = .;
|
||||
|
||||
/* Section is zeroed in pairs of u64. Align start and end to 16 bytes */
|
||||
.bss (NOLOAD) : ALIGN(16)
|
||||
{
|
||||
__bss_start = .;
|
||||
*(.bss*);
|
||||
. = ALIGN(16);
|
||||
__bss_end_exclusive = .;
|
||||
} :segment_data
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
// Based on miniload by @andre-richter
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(machine::tests::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use {
|
||||
core::{hash::Hasher, panic::PanicInfo},
|
||||
cortex_a::asm::barrier,
|
||||
machine::{
|
||||
devices::SerialOps,
|
||||
platform::rpi3::{gpio::GPIO, pl011_uart::PL011Uart, BcmHost},
|
||||
print, println, CONSOLE,
|
||||
},
|
||||
seahash::SeaHasher,
|
||||
};
|
||||
|
||||
mod boot;
|
||||
|
||||
/// Early init code.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Only a single core must be active and running this function.
|
||||
/// - The init calls in this function must appear in the correct order.
|
||||
#[inline(always)]
|
||||
unsafe fn kernel_init(max_kernel_size: u64) -> ! {
|
||||
#[cfg(feature = "jtag")]
|
||||
machine::arch::jtag::wait_debugger();
|
||||
|
||||
let gpio = GPIO::default();
|
||||
let uart = PL011Uart::default();
|
||||
let uart = uart.prepare(&gpio).expect("What could go wrong?");
|
||||
CONSOLE.lock(|c| {
|
||||
// Move uart into the global CONSOLE.
|
||||
c.replace_with(uart.into());
|
||||
});
|
||||
|
||||
// println! is usable from here on.
|
||||
|
||||
// Transition from unsafe to safe.
|
||||
kernel_main(max_kernel_size)
|
||||
}
|
||||
|
||||
// https://onlineasciitools.com/convert-text-to-ascii-art (FIGlet) with `cricket` font
|
||||
const LOGO: &str = r#"
|
||||
__ __ __ __
|
||||
.----| |--.---.-|__.-----| |--.-----.-----| |_
|
||||
| __| | _ | | | _ | _ | _ | _|
|
||||
|____|__|__|___._|__|__|__|_____|_____|_____|____|
|
||||
"#;
|
||||
|
||||
fn read_u64() -> u64 {
|
||||
CONSOLE.lock(|c| {
|
||||
let mut val: u64 = u64::from(c.read_byte());
|
||||
val |= u64::from(c.read_byte()) << 8;
|
||||
val |= u64::from(c.read_byte()) << 16;
|
||||
val |= u64::from(c.read_byte()) << 24;
|
||||
val |= u64::from(c.read_byte()) << 32;
|
||||
val |= u64::from(c.read_byte()) << 40;
|
||||
val |= u64::from(c.read_byte()) << 48;
|
||||
val |= u64::from(c.read_byte()) << 56;
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
/// The main function running after the early init.
|
||||
#[inline(always)]
|
||||
fn kernel_main(max_kernel_size: u64) -> ! {
|
||||
#[cfg(test)]
|
||||
test_main();
|
||||
|
||||
print!("{}", LOGO);
|
||||
println!("{:>51}\n", BcmHost::board_name());
|
||||
println!("[<<] Requesting kernel image...");
|
||||
|
||||
let kernel_addr: *mut u8 = BcmHost::kernel_load_address() as *mut u8;
|
||||
|
||||
loop {
|
||||
CONSOLE.lock(|c| c.flush());
|
||||
|
||||
// Discard any spurious received characters before starting with the loader protocol.
|
||||
CONSOLE.lock(|c| c.clear_rx());
|
||||
|
||||
// Notify `chainofcommand` to send the binary.
|
||||
for _ in 0..3 {
|
||||
CONSOLE.lock(|c| c.write_byte(3u8));
|
||||
}
|
||||
|
||||
// Read the binary's size.
|
||||
let size = read_u64();
|
||||
|
||||
// Check the size to fit RAM
|
||||
if size > max_kernel_size {
|
||||
println!("ERR Kernel image too big (over {} bytes)", max_kernel_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
print!("OK");
|
||||
|
||||
// We use seahash, simple and with no_std implementation.
|
||||
let mut hasher = SeaHasher::new();
|
||||
|
||||
// Read the kernel byte by byte.
|
||||
for i in 0..size {
|
||||
let val = CONSOLE.lock(|c| c.read_byte());
|
||||
unsafe {
|
||||
core::ptr::write_volatile(kernel_addr.offset(i as isize), val);
|
||||
}
|
||||
let written = unsafe { core::ptr::read_volatile(kernel_addr.offset(i as isize)) };
|
||||
hasher.write_u8(written);
|
||||
}
|
||||
|
||||
// Read the binary's checksum.
|
||||
let checksum = read_u64();
|
||||
|
||||
let valid = hasher.finish() == checksum;
|
||||
if !valid {
|
||||
println!("ERR Kernel image checksum mismatch");
|
||||
continue;
|
||||
}
|
||||
|
||||
print!("OK");
|
||||
break;
|
||||
}
|
||||
|
||||
println!(
|
||||
"[<<] Loaded! Executing the payload now from {:p}\n",
|
||||
kernel_addr
|
||||
);
|
||||
CONSOLE.lock(|c| c.flush());
|
||||
|
||||
// Use black magic to create a function pointer.
|
||||
let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
|
||||
|
||||
// Force everything to complete before we jump.
|
||||
unsafe { barrier::isb(barrier::SY) };
|
||||
|
||||
// Jump to loaded kernel!
|
||||
kernel()
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panicked(info: &PanicInfo) -> ! {
|
||||
machine::panic::handler(info)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[panic_handler]
|
||||
fn panicked(info: &PanicInfo) -> ! {
|
||||
machine::panic::handler_for_tests(info)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "chainofcommand"
|
||||
version = "0.0.1"
|
||||
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||
description = "Host server for chainboot"
|
||||
license = "BlueOak-1.0.0"
|
||||
categories = ["no-std", "embedded", "os"]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[dependencies]
|
||||
clap = "3.0"
|
||||
seahash = "4.1"
|
||||
anyhow = "1.0"
|
||||
fehler = "1.0"
|
||||
crossterm = { version = "0.23", features = ["event-stream"] }
|
||||
tokio-serial = "5.4"
|
||||
tokio = { version = "1.16", features = ["full"] }
|
||||
futures = "0.3"
|
||||
defer = "0.1"
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
bytes = "1.1"
|
|
@ -0,0 +1,44 @@
|
|||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build"]
|
||||
|
||||
[tasks.test]
|
||||
command = "cargo"
|
||||
args = ["test"]
|
||||
|
||||
[tasks.clippy]
|
||||
command = "cargo"
|
||||
args = ["clippy", "--", "-D", "warnings"]
|
||||
|
||||
[tasks.hopper]
|
||||
disabled = true
|
||||
|
||||
[tasks.kernel-binary]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-nucleus]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-cb]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-cb-gdb]
|
||||
disabled = true
|
||||
|
||||
[tasks.qemu]
|
||||
disabled = true
|
||||
|
||||
[tasks.qemu-cb]
|
||||
disabled = true
|
||||
|
||||
[tasks.sdcard]
|
||||
disabled = true
|
||||
|
||||
[tasks.cb-eject]
|
||||
disabled = true
|
||||
|
||||
[tasks.gdb]
|
||||
disabled = true
|
||||
|
||||
[tasks.gdb-cb]
|
||||
disabled = true
|
|
@ -0,0 +1,436 @@
|
|||
#![feature(trait_alias)]
|
||||
|
||||
use {
|
||||
anyhow::{anyhow, Result},
|
||||
bytes::Bytes,
|
||||
clap::{App, AppSettings, Arg},
|
||||
crossterm::{
|
||||
cursor,
|
||||
event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers},
|
||||
execute, style, terminal,
|
||||
tty::IsTty,
|
||||
},
|
||||
defer::defer,
|
||||
futures::{future::FutureExt, StreamExt},
|
||||
seahash::SeaHasher,
|
||||
std::{
|
||||
fs::File,
|
||||
hash::Hasher,
|
||||
io::{BufRead, BufReader},
|
||||
path::Path,
|
||||
time::Duration,
|
||||
},
|
||||
tokio::{io::AsyncReadExt, sync::mpsc},
|
||||
tokio_serial::{SerialPortBuilderExt, SerialStream},
|
||||
};
|
||||
|
||||
trait Writable = std::io::Write + Send;
|
||||
trait ThePath = AsRef<Path> + std::fmt::Display + Clone + Sync + Send + 'static;
|
||||
|
||||
async fn expect(
|
||||
to_console2: &mpsc::Sender<Vec<u8>>,
|
||||
from_serial: &mut mpsc::Receiver<Vec<u8>>,
|
||||
m: &str,
|
||||
) -> Result<()> {
|
||||
if let Some(buf) = from_serial.recv().await {
|
||||
if buf.len() == m.len() && String::from_utf8_lossy(buf.as_ref()) == m {
|
||||
return Ok(());
|
||||
}
|
||||
to_console2.send(buf).await?;
|
||||
return Err(anyhow!("Failed to receive expected value"));
|
||||
}
|
||||
Err(anyhow!("Failed to receive expected value"))
|
||||
}
|
||||
|
||||
async fn load_kernel<P>(to_console2: &mpsc::Sender<Vec<u8>>, kernel: P) -> Result<(File, u64)>
|
||||
where
|
||||
P: ThePath,
|
||||
{
|
||||
to_console2
|
||||
.send("[>>] Loading kernel image\n".into())
|
||||
.await?;
|
||||
|
||||
let kernel_file = match std::fs::File::open(kernel.clone()) {
|
||||
Ok(file) => file,
|
||||
Err(_) => return Err(anyhow!("Couldn't open kernel file {}", kernel)),
|
||||
};
|
||||
let kernel_size: u64 = kernel_file.metadata()?.len();
|
||||
|
||||
to_console2
|
||||
.send(format!("[>>] .. {} ({} bytes)\n", kernel, kernel_size).into())
|
||||
.await?;
|
||||
|
||||
Ok((kernel_file, kernel_size))
|
||||
}
|
||||
|
||||
async fn send_kernel<P>(
|
||||
to_console2: &mpsc::Sender<Vec<u8>>,
|
||||
to_serial: &mpsc::Sender<Vec<u8>>,
|
||||
from_serial: &mut mpsc::Receiver<Vec<u8>>,
|
||||
kernel: P,
|
||||
) -> Result<()>
|
||||
where
|
||||
P: ThePath,
|
||||
{
|
||||
let (kernel_file, kernel_size) = load_kernel(to_console2, kernel).await?;
|
||||
|
||||
to_console2.send("[>>] Sending image size\n".into()).await?;
|
||||
|
||||
to_serial.send(kernel_size.to_le_bytes().into()).await?;
|
||||
|
||||
// Wait for OK response
|
||||
expect(to_console2, from_serial, "OK").await?;
|
||||
|
||||
to_console2
|
||||
.send("[>>] Sending kernel image\n".into())
|
||||
.await?;
|
||||
|
||||
let mut hasher = SeaHasher::new();
|
||||
let mut reader = BufReader::with_capacity(1, kernel_file);
|
||||
loop {
|
||||
let length = {
|
||||
let buf = reader.fill_buf()?;
|
||||
to_serial.send(buf.into()).await?;
|
||||
hasher.write(buf);
|
||||
buf.len()
|
||||
};
|
||||
if length == 0 {
|
||||
break;
|
||||
}
|
||||
reader.consume(length);
|
||||
}
|
||||
let hashed_value: u64 = hasher.finish();
|
||||
|
||||
to_console2
|
||||
.send(format!("[>>] Sending image checksum {:x}\n", hashed_value).into())
|
||||
.await?;
|
||||
|
||||
to_serial.send(hashed_value.to_le_bytes().into()).await?;
|
||||
|
||||
expect(to_console2, from_serial, "OK").await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Async reading using Tokio: https://fasterthanli.me/articles/a-terminal-case-of-linux
|
||||
|
||||
async fn serial_loop(
|
||||
mut port: tokio_serial::SerialStream,
|
||||
to_console: mpsc::Sender<Vec<u8>>,
|
||||
mut from_console: mpsc::Receiver<Vec<u8>>,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0; 256];
|
||||
loop {
|
||||
tokio::select! {
|
||||
// _ = poll_send => {},
|
||||
|
||||
Some(msg) = from_console.recv() => {
|
||||
// debug!("serial write {} bytes", msg.len());
|
||||
tokio::io::AsyncWriteExt::write_all(&mut port, msg.as_ref()).await?;
|
||||
}
|
||||
|
||||
res = port.read(&mut buf) => {
|
||||
match res {
|
||||
Ok(0) => {
|
||||
// info!("Serial <EOF>");
|
||||
return Ok(());
|
||||
}
|
||||
Ok(n) => {
|
||||
// debug!("Serial read {n} bytes.");
|
||||
to_console.send(buf[0..n].to_owned()).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
// if e.kind() == ErrorKind::TimedOut {
|
||||
// execute!(w, style::Print("\r\nTimeout: the serial device has been unplugged!"))?;
|
||||
// } else {
|
||||
// execute!(w, style::Print(format!("\r\nSerial Error: {:?}\r", e)))?;
|
||||
// }
|
||||
// break;
|
||||
return Err(anyhow!(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn console_loop<P>(
|
||||
to_console2: mpsc::Sender<Vec<u8>>,
|
||||
mut from_internal: mpsc::Receiver<Vec<u8>>,
|
||||
to_serial: mpsc::Sender<Vec<u8>>,
|
||||
mut from_serial: mpsc::Receiver<Vec<u8>>,
|
||||
kernel: P,
|
||||
) -> Result<()>
|
||||
where
|
||||
P: ThePath,
|
||||
{
|
||||
let mut w = std::io::stdout();
|
||||
|
||||
let mut breaks = 0;
|
||||
|
||||
let mut event_reader = EventStream::new();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
|
||||
Some(received) = from_internal.recv() => {
|
||||
for &x in &received[..] {
|
||||
execute!(w, style::Print(format!("{}", x as char)))?;
|
||||
}
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
Some(received) = from_serial.recv() => {
|
||||
// execute!(w, cursor::MoveToNextLine(1), style::Print(format!("[>>] Received {} bytes from serial", from_serial.len())), cursor::MoveToNextLine(1))?;
|
||||
|
||||
for &x in &received[..] {
|
||||
if x == 0x3 {
|
||||
// execute!(w, cursor::MoveToNextLine(1), style::Print("[>>] Received a BREAK"), cursor::MoveToNextLine(1))?;
|
||||
breaks += 1;
|
||||
// Await for 3 consecutive \3 to start downloading
|
||||
if breaks == 3 {
|
||||
// execute!(w, cursor::MoveToNextLine(1), style::Print("[>>] Received 3 BREAKs"), cursor::MoveToNextLine(1))?;
|
||||
breaks = 0;
|
||||
send_kernel(&to_console2, &to_serial, &mut from_serial, kernel.clone()).await?;
|
||||
to_console2.send("[>>] Send successful, pass-through\n".into()).await?;
|
||||
}
|
||||
} else {
|
||||
while breaks > 0 {
|
||||
execute!(w, style::Print(format!("{}", 3 as char)))?;
|
||||
breaks -= 1;
|
||||
}
|
||||
execute!(w, style::Print(format!("{}", x as char)))?;
|
||||
w.flush()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maybe_event = event_reader.next().fuse() => {
|
||||
match maybe_event {
|
||||
Some(Ok(Event::Key(key_event))) => {
|
||||
if key_event.code == KeyCode::Char('c') && key_event.modifiers == KeyModifiers::CONTROL {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(key) = handle_key_event(key_event) {
|
||||
to_serial.send(key.to_vec()).await?;
|
||||
// Local echo
|
||||
execute!(w, style::Print(format!("{:?}", key)))?;
|
||||
w.flush()?;
|
||||
}
|
||||
}
|
||||
Some(Ok(_)) => {},
|
||||
Some(Err(e)) => {
|
||||
execute!(w, style::Print(format!("Console read error: {:?}\r", e)))?;
|
||||
w.flush()?;
|
||||
},
|
||||
None => return Err(anyhow!("woops")),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn main_loop<P>(port: SerialStream, kernel: P) -> Result<()>
|
||||
where
|
||||
P: ThePath,
|
||||
{
|
||||
// read from serial -> to_console==>from_serial -> output to console
|
||||
let (to_console, from_serial) = mpsc::channel(256);
|
||||
let (to_console2, from_internal) = mpsc::channel(256);
|
||||
|
||||
// read from console -> to_serial==>from_console -> output to serial
|
||||
let (to_serial, from_console) = mpsc::channel(256);
|
||||
|
||||
tokio::spawn(serial_loop(port, to_console.clone(), from_console));
|
||||
console_loop(to_console2, from_internal, to_serial, from_serial, kernel).await
|
||||
|
||||
// TODO: framed
|
||||
|
||||
// rx_device -> serial_reader -> app
|
||||
// app -> serial_writer -> serial_consumer -> (poll_send to drive) -> serial_sink -> tx_device
|
||||
// let (rx_device, tx_device) = split(port);
|
||||
|
||||
// let mut serial_reader = FramedRead::new(rx_device, BytesCodec::new());
|
||||
// let serial_sink = FramedWrite::new(tx_device, BytesCodec::new());
|
||||
//
|
||||
// let (serial_writer, serial_consumer) = mpsc::unbounded::<Bytes>();
|
||||
// let mut poll_send = serial_consumer.map(Ok).forward(serial_sink);
|
||||
}
|
||||
|
||||
// From remote_serial -- https://github.com/zhp-rs/remote_serial/ (Licensed under MIT License)
|
||||
fn handle_key_event(key_event: KeyEvent) -> Option<Bytes> {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
let key_str: Option<&[u8]> = match key_event.code {
|
||||
KeyCode::Backspace => Some(b"\x08"),
|
||||
KeyCode::Enter => Some(b"\x0D"),
|
||||
KeyCode::Left => Some(b"\x1b[D"),
|
||||
KeyCode::Right => Some(b"\x1b[C"),
|
||||
KeyCode::Home => Some(b"\x1b[H"),
|
||||
KeyCode::End => Some(b"\x1b[F"),
|
||||
KeyCode::Up => Some(b"\x1b[A"),
|
||||
KeyCode::Down => Some(b"\x1b[B"),
|
||||
KeyCode::Tab => Some(b"\x09"),
|
||||
KeyCode::Delete => Some(b"\x1b[3~"),
|
||||
KeyCode::Insert => Some(b"\x1b[2~"),
|
||||
KeyCode::Esc => Some(b"\x1b"),
|
||||
KeyCode::Char(ch) => {
|
||||
if key_event.modifiers & KeyModifiers::CONTROL == KeyModifiers::CONTROL {
|
||||
buf[0] = ch as u8;
|
||||
if ('a'..='z').contains(&ch) || (ch == ' ') {
|
||||
buf[0] &= 0x1f;
|
||||
Some(&buf[0..1])
|
||||
} else if ('4'..='7').contains(&ch) {
|
||||
// crossterm returns Control-4 thru 7 for \x1c thru \x1f
|
||||
buf[0] = (buf[0] + 8) & 0x1f;
|
||||
Some(&buf[0..1])
|
||||
} else {
|
||||
Some(ch.encode_utf8(&mut buf).as_bytes())
|
||||
}
|
||||
} else {
|
||||
Some(ch.encode_utf8(&mut buf).as_bytes())
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
key_str.map(Bytes::copy_from_slice)
|
||||
}
|
||||
|
||||
// 1. connect to given serial port, e.g. /dev/ttyUSB23234
|
||||
// 2. Await for \3\3\3 start signal, meanwhile pass-through all traffic to console
|
||||
// 3. send selected kernel binary with checksum to the target
|
||||
// 4. go to 2
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let matches = App::new("ChainOfCommand - command chainboot protocol")
|
||||
.about("Use to send freshly built kernel to chainboot-compatible boot loader")
|
||||
.setting(AppSettings::DisableVersionFlag)
|
||||
.arg(
|
||||
Arg::new("port")
|
||||
.help("The device path to a serial port, e.g. /dev/ttyUSB0")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("baud")
|
||||
.help("The baud rate to connect at")
|
||||
.use_delimiter(false)
|
||||
.required(true), // .validator(valid_baud),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("kernel")
|
||||
.long("kernel")
|
||||
.help("Path of the binary kernel image to send")
|
||||
.takes_value(true)
|
||||
.default_value("kernel8.img"),
|
||||
)
|
||||
.get_matches();
|
||||
let port_name = matches.value_of("port").unwrap();
|
||||
let baud_rate = matches.value_of("baud").unwrap().parse::<u32>().unwrap();
|
||||
let kernel = matches.value_of("kernel").unwrap().to_owned();
|
||||
|
||||
// Check that STDIN is a proper tty
|
||||
if !std::io::stdin().is_tty() {
|
||||
panic!("Must have a TTY for stdin");
|
||||
}
|
||||
|
||||
// Disable line buffering, local echo, etc.
|
||||
terminal::enable_raw_mode()?;
|
||||
defer(|| terminal::disable_raw_mode().unwrap_or(()));
|
||||
|
||||
let mut serial_toggle = false;
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
execute!(stdout, cursor::SavePosition)?;
|
||||
|
||||
loop {
|
||||
execute!(
|
||||
stdout,
|
||||
cursor::RestorePosition,
|
||||
style::Print("[>>] Opening serial port ")
|
||||
)?;
|
||||
|
||||
// tokio_serial::new() creates a builder with 8N1 setup without flow control by default.
|
||||
let port = tokio_serial::new(port_name, baud_rate).open_native_async();
|
||||
if let Err(e) = port {
|
||||
let cont = match e.kind {
|
||||
tokio_serial::ErrorKind::NoDevice => true,
|
||||
tokio_serial::ErrorKind::Io(e)
|
||||
if e == std::io::ErrorKind::NotFound
|
||||
|| e == std::io::ErrorKind::PermissionDenied =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if cont {
|
||||
execute!(
|
||||
stdout,
|
||||
cursor::RestorePosition,
|
||||
style::Print(format!(
|
||||
"[>>] Waiting for serial port {}\r",
|
||||
if serial_toggle { "# " } else { " #" }
|
||||
))
|
||||
)?;
|
||||
stdout.flush()?;
|
||||
serial_toggle = !serial_toggle;
|
||||
|
||||
if crossterm::event::poll(Duration::from_millis(1000))? {
|
||||
if let Event::Key(KeyEvent { code, modifiers }) = crossterm::event::read()? {
|
||||
if code == KeyCode::Char('c') && modifiers == KeyModifiers::CONTROL {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
return Err(e.into());
|
||||
}
|
||||
|
||||
execute!(
|
||||
stdout,
|
||||
style::Print("\n[>>] Waiting for handshake, pass-through"),
|
||||
)?;
|
||||
stdout.flush()?;
|
||||
|
||||
// Run in pass-through mode by default.
|
||||
// Once we receive BREAK (0x3) three times, switch to kernel send mode and upload kernel,
|
||||
// then switch back to pass-through mode.
|
||||
|
||||
// Input from STDIN should pass through to serial
|
||||
// Input from serial should pass through to STDOUT
|
||||
|
||||
let port = port?;
|
||||
|
||||
if let Err(e) = main_loop(port, kernel.clone()).await {
|
||||
execute!(stdout, style::Print(format!("\nError: {:?}\n", e)))?;
|
||||
stdout.flush()?;
|
||||
|
||||
let cont = match e.downcast_ref::<std::io::Error>() {
|
||||
Some(e)
|
||||
if e.kind() == std::io::ErrorKind::NotFound
|
||||
|| e.kind() == std::io::ErrorKind::PermissionDenied =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
} || matches!(e.downcast_ref::<tokio_serial::Error>(), Some(e) if e.kind == tokio_serial::ErrorKind::NoDevice)
|
||||
|| matches!(
|
||||
e.downcast_ref::<tokio::sync::mpsc::error::SendError<Vec<u8>>>(),
|
||||
Some(_)
|
||||
);
|
||||
|
||||
if !cont {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// main_loop() returned Ok() we're good to finish
|
||||
break;
|
||||
}
|
||||
execute!(stdout, cursor::SavePosition)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,65 +1,7 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
This is our Code of Conduct and Ethics (the “Code”)
|
||||
|
||||
## Our Pledge
|
||||
It is based on our values and aspirations (be good, innovate, work together, be part of the solution). It describes what we expect from the members of the community in three main rules:
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality,
|
||||
personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others’ private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Maintainers are responsible for clarifying the standards of acceptable behavior
|
||||
and are expected to take appropriate and fair corrective action in response to
|
||||
any instances of unacceptable behavior.
|
||||
|
||||
Maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, or to ban temporarily or permanently any
|
||||
contributor for other behaviors that they deem inappropriate, threatening,
|
||||
offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the Technical Advisory Board (TAB) at
|
||||
<tab@metta.systems>. All complaints will be reviewed and
|
||||
investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. The TAB is obligated to maintain
|
||||
confidentiality with regard to the reporter of an incident. Further details of
|
||||
specific enforcement policies may be posted separately.
|
||||
|
||||
Maintainers who do not follow or enforce the Code of Conduct in good faith may
|
||||
face temporary or permanent repercussions as determined by other members of the
|
||||
project’s leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
1. Do the right thing. Always act with honesty, integrity, and reliability. Keep moral and ethical standards high.
|
||||
2. Be nice. Treat people with dignity and respect, regardless of who they are and where they came from. Stay decent and courteous in all relationships.
|
||||
3. Play fair. Don’t cheat. Be careful to account for and balance the interests of all groups (developers, artists and designers, end users and the general public).
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
zellij-config.sh
|
|
@ -0,0 +1,12 @@
|
|||
#########################################################
|
||||
# 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. #
|
||||
#########################################################
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
template:
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
borderless: true
|
||||
split_size:
|
||||
Fixed: 1
|
||||
run:
|
||||
plugin:
|
||||
location: "zellij:tab-bar"
|
||||
- direction: Vertical
|
||||
body: true
|
||||
|
||||
tabs:
|
||||
- direction: Vertical
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
borderless: true
|
||||
run:
|
||||
command:
|
||||
cmd: "bash"
|
||||
args: ["-c", "bash emulation/qemu_multi_uart.sh"]
|
||||
- direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 30
|
||||
run:
|
||||
command:
|
||||
cmd: "bash"
|
||||
args: ["-c", "clear; echo -e \"\\033]0;MiniUart\\007\"; bash /dev/ptmx FIRST=1"]
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 70
|
||||
run:
|
||||
command:
|
||||
cmd: "bash"
|
||||
args: ["-c", "clear; echo -e \"\\033]0;PL011 Uart\\007\"; bash /dev/ptmx SECOND=1"]
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2018-2019 Berkus Decker <berkus+vesper@metta.systems>
|
||||
# Based on QEMU runner example by Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
echo -e "\033]0;QEMU\007"
|
||||
|
||||
clear
|
||||
cat emulation/instructions.txt
|
||||
|
||||
# Generated by `cargo make zellij-config`
|
||||
source emulation/zellij-config.sh
|
||||
|
||||
# Let the two other serials boot up
|
||||
sleep 1
|
||||
|
||||
FIRST_TTY=$(ps a | grep -e [F]IRST | awk '{ print $2 }')
|
||||
SECOND_TTY=$(ps a | grep -e [S]ECOND | awk '{ print $2 }')
|
||||
|
||||
FIRST_PID=$(ps a | grep -e [F]IRST | awk '{ print $1 }')
|
||||
SECOND_PID=$(ps a | grep -e [S]ECOND | awk '{ print $1 }')
|
||||
|
||||
function cleanup() {
|
||||
# Drop ptmx shells
|
||||
kill $FIRST_PID
|
||||
kill $SECOND_PID
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "=== Running QEMU with MiniUart ${FIRST_TTY} and PL011 Uart ${SECOND_TTY} ==="
|
||||
|
||||
${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb -kernel ${KERNEL_BIN} -serial /dev/tty${SECOND_TTY} -serial /dev/tty${FIRST_TTY}
|
|
@ -0,0 +1,5 @@
|
|||
QEMU=
|
||||
QEMU_OPTS=
|
||||
QEMU_RUNNER_OPTS=
|
||||
CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY=
|
||||
KERNEL_BIN=
|
|
@ -0,0 +1,40 @@
|
|||
[package]
|
||||
name = "machine"
|
||||
version = "0.0.1"
|
||||
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||
description = "Vesper nanokernel shared code library, useful also for the chainboot loader."
|
||||
documentation = "https://docs.metta.systems/vesper"
|
||||
homepage = "https://github.com/metta-systems/vesper"
|
||||
repository = "https://github.com/metta-systems/vesper"
|
||||
readme = "README.md"
|
||||
license = "BlueOak-1.0.0"
|
||||
categories = ["no-std", "embedded", "os"]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
noserial = []
|
||||
# Enable JTAG debugging of kernel - enable jtag helpers and
|
||||
# block waiting for JTAG probe attach at the start of kernel main.
|
||||
jtag = []
|
||||
# Build for running under QEMU with semihosting, so various halt/reboot options would for example quit QEMU instead.
|
||||
qemu = ["rpi3"]
|
||||
# Mutually exclusive features to choose a target board
|
||||
rpi3 = []
|
||||
rpi4 = []
|
||||
|
||||
[dependencies]
|
||||
r0 = "1.0"
|
||||
qemu-exit = "3.0"
|
||||
cortex-a = "7.0"
|
||||
tock-registers = "0.7"
|
||||
ux = { version = "0.1", default-features = false }
|
||||
usize_conversions = "0.2"
|
||||
bit_field = "0.10"
|
||||
bitflags = "1.3"
|
||||
cfg-if = "1.0"
|
||||
snafu = { version = "0.7", default-features = false }
|
|
@ -6,11 +6,12 @@
|
|||
*/
|
||||
|
||||
//! Low-level boot of the Raspberry's processor
|
||||
//! http://infocenter.arm.com/help/topic/com.arm.doc.dai0527a/DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf
|
||||
//! <http://infocenter.arm.com/help/topic/com.arm.doc.dai0527a/DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf>
|
||||
|
||||
use {
|
||||
crate::endless_sleep,
|
||||
cortex_a::{asm, regs::*},
|
||||
cortex_a::{asm, registers::*},
|
||||
tock_registers::interfaces::{Readable, Writeable},
|
||||
};
|
||||
|
||||
// Stack placed before first executable instruction
|
||||
|
@ -23,6 +24,7 @@ macro_rules! entry {
|
|||
/// # Safety
|
||||
/// Only type-checks!
|
||||
#[export_name = "main"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn __main() -> ! {
|
||||
// type check the given path
|
||||
let f: fn() -> ! = $path;
|
||||
|
@ -43,6 +45,7 @@ macro_rules! entry {
|
|||
unsafe fn reset() -> ! {
|
||||
extern "C" {
|
||||
// Boundaries of the .bss section, provided by the linker script
|
||||
// The type, `u64`, indicates that the memory is 8-byte aligned
|
||||
static mut __BSS_START: u64;
|
||||
static mut __BSS_END: u64;
|
||||
}
|
||||
|
@ -65,7 +68,7 @@ unsafe fn reset() -> ! {
|
|||
// #[inline]
|
||||
// fn enable_armv6_unaligned_access() {
|
||||
// unsafe {
|
||||
// asm!(
|
||||
// core::arch::asm!(
|
||||
// "mrc p15, 0, {u}, c1, c0, 0",
|
||||
// "or {u}, {u}, {CR_U}",
|
||||
// "mcr p15, 0, {u}, c1, c0, 0",
|
|
@ -1,16 +1,17 @@
|
|||
//! JTAG helper functions.
|
||||
|
||||
use cortex_a::asm;
|
||||
use {
|
||||
core::ptr::{read_volatile, write_volatile},
|
||||
cortex_a::asm,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
static mut WAIT_FLAG: bool = true;
|
||||
|
||||
/// Wait for debugger to attach.
|
||||
/// Then in gdb issue `> set var *(&WAIT_FLAG) = 0`
|
||||
/// from inside this function's frame to contiue running.
|
||||
/// from inside this function's frame to continue running.
|
||||
pub fn wait_debugger() {
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
while unsafe { read_volatile(&WAIT_FLAG) } {
|
||||
asm::nop();
|
||||
}
|
|
@ -2,4 +2,6 @@
|
|||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
pub mod rpi3;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub type ASID = u16;
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
mod asid;
|
||||
mod phys_addr;
|
||||
mod virt_addr;
|
||||
|
||||
pub use {asid::*, phys_addr::*, virt_addr::*};
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
use {
|
||||
crate::mm::{align_down, align_up},
|
||||
bit_field::BitField,
|
||||
core::{
|
||||
convert::{From, Into},
|
||||
fmt,
|
||||
ops::{Add, AddAssign, Shl, Shr, Sub, SubAssign},
|
||||
},
|
||||
usize_conversions::FromUsize,
|
||||
};
|
||||
|
||||
/// A 64-bit physical memory address.
|
||||
///
|
||||
/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
|
||||
/// on non 64-bit systems. The `UsizeConversions` trait can be used for performing conversions
|
||||
/// between `u64` and `usize`.
|
||||
///
|
||||
/// On `aarch64`, only the 52 lower bits of a physical address can be used. The top 12 bits need
|
||||
/// to be zero. This type guarantees that it always represents a valid physical address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct PhysAddr(u64);
|
||||
|
||||
/// A passed `u64` was not a valid physical address.
|
||||
///
|
||||
/// This means that bits 52 to 64 were not all null.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct PhysAddrNotValid(u64);
|
||||
|
||||
impl PhysAddr {
|
||||
/// Creates a new physical address.
|
||||
///
|
||||
/// Panics if any bits in the bit position 52 to 64 is set.
|
||||
pub fn new(addr: u64) -> PhysAddr {
|
||||
assert_eq!(
|
||||
addr.get_bits(52..64),
|
||||
0,
|
||||
"physical addresses must not have any set bits in positions 52 to 64"
|
||||
);
|
||||
PhysAddr(addr)
|
||||
}
|
||||
|
||||
/// Tries to create a new physical address.
|
||||
///
|
||||
/// Fails if any bits in the bit positions 52 to 64 are set.
|
||||
pub fn try_new(addr: u64) -> Result<PhysAddr, PhysAddrNotValid> {
|
||||
match addr.get_bits(52..64) {
|
||||
0 => Ok(PhysAddr(addr)), // address is valid
|
||||
_ => Err(PhysAddrNotValid(addr)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a physical address that points to `0`.
|
||||
pub const fn zero() -> PhysAddr {
|
||||
PhysAddr(0)
|
||||
}
|
||||
|
||||
/// Converts the address to an `u64`.
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Convenience method for checking if a physical address is null.
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Aligns the physical address upwards to the given alignment.
|
||||
///
|
||||
/// See the `align_up` function for more information.
|
||||
#[must_use]
|
||||
pub fn aligned_up<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
PhysAddr(align_up(self.0, align.into()))
|
||||
}
|
||||
|
||||
/// Aligns the physical address downwards to the given alignment.
|
||||
///
|
||||
/// See the `align_down` function for more information.
|
||||
#[must_use]
|
||||
pub fn aligned_down<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
PhysAddr(align_down(self.0, align.into()))
|
||||
}
|
||||
|
||||
/// Checks whether the physical address has the demanded alignment.
|
||||
pub fn is_aligned<U>(self, align: U) -> bool
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
self.aligned_down(align) == self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PhysAddr({:#x})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Binary for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Octal for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for PhysAddr {
|
||||
fn from(value: u64) -> Self {
|
||||
PhysAddr::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PhysAddr> for u64 {
|
||||
fn from(value: PhysAddr) -> Self {
|
||||
value.as_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PhysAddr> for u128 {
|
||||
fn from(value: PhysAddr) -> Self {
|
||||
value.as_u64() as u128
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<u64> for PhysAddr {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: u64) -> Self::Output {
|
||||
PhysAddr::new(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<u64> for PhysAddr {
|
||||
fn add_assign(&mut self, rhs: u64) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
self + u64::from_usize(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
self.add_assign(u64::from_usize(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<u64> for PhysAddr {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: u64) -> Self::Output {
|
||||
PhysAddr::new(self.0.checked_sub(rhs).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<u64> for PhysAddr {
|
||||
fn sub_assign(&mut self, rhs: u64) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
self - u64::from_usize(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
self.sub_assign(u64::from_usize(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<PhysAddr> for PhysAddr {
|
||||
type Output = u64;
|
||||
fn sub(self, rhs: PhysAddr) -> Self::Output {
|
||||
self.as_u64().checked_sub(rhs.as_u64()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<usize> for PhysAddr {
|
||||
type Output = PhysAddr;
|
||||
|
||||
fn shr(self, shift: usize) -> Self::Output {
|
||||
PhysAddr::new(self.0 >> shift)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl<usize> for PhysAddr {
|
||||
type Output = PhysAddr;
|
||||
|
||||
fn shl(self, shift: usize) -> Self::Output {
|
||||
PhysAddr::new(self.0 << shift)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test_case]
|
||||
pub fn test_invalid_phys_addr() {
|
||||
let result = PhysAddr::try_new(0xfafa_0123_3210_3210);
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e, PhysAddrNotValid(0xfafa_0123_3210_3210));
|
||||
} else {
|
||||
assert!(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
use {
|
||||
crate::mm::{align_down, align_up},
|
||||
bit_field::BitField,
|
||||
|
@ -107,6 +108,7 @@ impl VirtAddr {
|
|||
/// Aligns the virtual address upwards to the given alignment.
|
||||
///
|
||||
/// See the `align_up` free function for more information.
|
||||
#[must_use]
|
||||
pub fn aligned_up<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
|
@ -117,6 +119,7 @@ impl VirtAddr {
|
|||
/// Aligns the virtual address downwards to the given alignment.
|
||||
///
|
||||
/// See the `align_down` free function for more information.
|
||||
#[must_use]
|
||||
pub fn aligned_down<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
|
@ -290,219 +293,3 @@ where
|
|||
*self = VirtAddr::new(self.0 % u64::from_usize(rhs));
|
||||
}
|
||||
}
|
||||
|
||||
/// A 64-bit physical memory address.
|
||||
///
|
||||
/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
|
||||
/// on non 64-bit systems. The `UsizeConversions` trait can be used for performing conversions
|
||||
/// between `u64` and `usize`.
|
||||
///
|
||||
/// On `aarch64`, only the 52 lower bits of a physical address can be used. The top 12 bits need
|
||||
/// to be zero. This type guarantees that it always represents a valid physical address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct PhysAddr(u64);
|
||||
|
||||
/// A passed `u64` was not a valid physical address.
|
||||
///
|
||||
/// This means that bits 52 to 64 were not all null.
|
||||
#[derive(Debug)]
|
||||
pub struct PhysAddrNotValid(u64);
|
||||
|
||||
impl PhysAddr {
|
||||
/// Creates a new physical address.
|
||||
///
|
||||
/// Panics if any bits in the bit position 52 to 64 is set.
|
||||
pub fn new(addr: u64) -> PhysAddr {
|
||||
assert_eq!(
|
||||
addr.get_bits(52..64),
|
||||
0,
|
||||
"physical addresses must not have any set bits in positions 52 to 64"
|
||||
);
|
||||
PhysAddr(addr)
|
||||
}
|
||||
|
||||
/// Tries to create a new physical address.
|
||||
///
|
||||
/// Fails if any bits in the bit positions 52 to 64 are set.
|
||||
pub fn try_new(addr: u64) -> Result<PhysAddr, PhysAddrNotValid> {
|
||||
match addr.get_bits(52..64) {
|
||||
0 => Ok(PhysAddr(addr)), // address is valid
|
||||
_ => Err(PhysAddrNotValid(addr)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a physical address that points to `0`.
|
||||
pub const fn zero() -> PhysAddr {
|
||||
PhysAddr(0)
|
||||
}
|
||||
|
||||
/// Converts the address to an `u64`.
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Convenience method for checking if a physical address is null.
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Aligns the physical address upwards to the given alignment.
|
||||
///
|
||||
/// See the `align_up` function for more information.
|
||||
pub fn aligned_up<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
PhysAddr(align_up(self.0, align.into()))
|
||||
}
|
||||
|
||||
/// Aligns the physical address downwards to the given alignment.
|
||||
///
|
||||
/// See the `align_down` function for more information.
|
||||
pub fn aligned_down<U>(self, align: U) -> Self
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
PhysAddr(align_down(self.0, align.into()))
|
||||
}
|
||||
|
||||
/// Checks whether the physical address has the demanded alignment.
|
||||
pub fn is_aligned<U>(self, align: U) -> bool
|
||||
where
|
||||
U: Into<u64>,
|
||||
{
|
||||
self.aligned_down(align) == self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PhysAddr({:#x})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Binary for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Octal for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for PhysAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for PhysAddr {
|
||||
fn from(value: u64) -> Self {
|
||||
PhysAddr::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PhysAddr> for u64 {
|
||||
fn from(value: PhysAddr) -> Self {
|
||||
value.as_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<u64> for PhysAddr {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: u64) -> Self::Output {
|
||||
PhysAddr::new(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<u64> for PhysAddr {
|
||||
fn add_assign(&mut self, rhs: u64) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
self + u64::from_usize(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
self.add_assign(u64::from_usize(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<u64> for PhysAddr {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: u64) -> Self::Output {
|
||||
PhysAddr::new(self.0.checked_sub(rhs).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<u64> for PhysAddr {
|
||||
fn sub_assign(&mut self, rhs: u64) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
self - u64::from_usize(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<usize> for PhysAddr
|
||||
where
|
||||
u64: FromUsize,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
self.sub_assign(u64::from_usize(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<PhysAddr> for PhysAddr {
|
||||
type Output = u64;
|
||||
fn sub(self, rhs: PhysAddr) -> Self::Output {
|
||||
self.as_u64().checked_sub(rhs.as_u64()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// use super::*;
|
||||
|
||||
// #[test_case]
|
||||
// pub fn test_invalid_phys_addr() {
|
||||
// let result = PhysAddr::try_new(0xfafa_0123_0123_0123_3210_3210_3210_3210);
|
||||
// if let Err(e) = result {
|
||||
// assert_eq!(
|
||||
// e,
|
||||
// PhysAddrNotValid(0xfafa_0123_0123_0123_3210_3210_3210_3210)
|
||||
// );
|
||||
// } else {
|
||||
// assert!(false)
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -13,37 +13,29 @@
|
|||
|
||||
use {
|
||||
crate::{
|
||||
arch::aarch64::memory::{
|
||||
get_virt_addr_properties, AttributeFields, /*FrameAllocator, PhysAddr, VirtAddr,*/
|
||||
},
|
||||
arch::aarch64::memory::{get_virt_addr_properties, AttributeFields},
|
||||
println,
|
||||
},
|
||||
// bitflags::bitflags,
|
||||
core::{
|
||||
// convert::TryInto,
|
||||
// fmt,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
// ptr::Unique,
|
||||
},
|
||||
cortex_a::{
|
||||
barrier,
|
||||
regs::{ID_AA64MMFR0_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1},
|
||||
asm::barrier,
|
||||
registers::{ID_AA64MMFR0_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1},
|
||||
},
|
||||
register::{
|
||||
cpu::{RegisterReadOnly, RegisterReadWrite},
|
||||
tock_registers::{
|
||||
fields::FieldValue,
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields,
|
||||
},
|
||||
// ux::*,
|
||||
};
|
||||
|
||||
mod mair {
|
||||
use cortex_a::regs::MAIR_EL1;
|
||||
use {cortex_a::registers::MAIR_EL1, tock_registers::interfaces::Writeable};
|
||||
|
||||
/// Setup function for the MAIR_EL1 register.
|
||||
pub fn set_up() {
|
||||
use cortex_a::regs::RegisterReadWrite;
|
||||
|
||||
// Define the three memory types that we will map. Normal DRAM, Uncached and device.
|
||||
MAIR_EL1.write(
|
||||
// Attribute 2 -- Device Memory
|
||||
|
@ -296,7 +288,7 @@ register_bitfields! {
|
|||
/// attributes of the MMU.
|
||||
fn into_mmu_attributes(
|
||||
attribute_fields: AttributeFields,
|
||||
) -> register::FieldValue<u64, STAGE1_DESCRIPTOR::Register> {
|
||||
) -> FieldValue<u64, STAGE1_DESCRIPTOR::Register> {
|
||||
use super::{AccessPermissions, MemAttributes};
|
||||
|
||||
// Memory attributes
|
||||
|
@ -402,7 +394,7 @@ impl PageSize for Size2MiB {
|
|||
|
||||
impl NotGiantPageSize for Size2MiB {}
|
||||
|
||||
type EntryFlags = register::FieldValue<u64, STAGE1_DESCRIPTOR::Register>;
|
||||
type EntryFlags = tock_registers::fields::FieldValue<u64, STAGE1_DESCRIPTOR::Register>;
|
||||
// type EntryRegister = register::LocalRegisterCopy<u64, STAGE1_DESCRIPTOR::Register>;
|
||||
|
||||
/// L0 table -- only pointers to L1 tables
|
|
@ -13,8 +13,7 @@ use {
|
|||
mod addr;
|
||||
pub mod mmu;
|
||||
|
||||
pub use addr::PhysAddr;
|
||||
pub use addr::VirtAddr;
|
||||
pub use addr::{PhysAddr, VirtAddr};
|
||||
|
||||
// aarch64 granules and page sizes howto:
|
||||
// https://stackoverflow.com/questions/34269185/simultaneous-existence-of-different-sized-pages-on-aarch64
|
|
@ -39,3 +39,14 @@ pub fn loop_until<F: Fn() -> bool>(f: F) {
|
|||
asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Loop while a passed function returns `true`.
|
||||
#[inline]
|
||||
pub fn loop_while<F: Fn() -> bool>(f: F) {
|
||||
loop {
|
||||
if !f() {
|
||||
break;
|
||||
}
|
||||
asm::nop();
|
||||
}
|
||||
}
|
|
@ -52,14 +52,17 @@
|
|||
use {
|
||||
crate::{arch::endless_sleep, println},
|
||||
cortex_a::{
|
||||
barrier,
|
||||
regs::{RegisterReadOnly, RegisterReadWrite, ESR_EL1, FAR_EL1, VBAR_EL1},
|
||||
asm::barrier,
|
||||
registers::{ESR_EL1, FAR_EL1, VBAR_EL1},
|
||||
},
|
||||
register::{register_bitfields, LocalRegisterCopy},
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, LocalRegisterCopy,
|
||||
},
|
||||
};
|
||||
|
||||
global_asm!(include_str!("vectors.S"));
|
||||
core::arch::global_asm!(include_str!("vectors.S"));
|
||||
|
||||
/// Errors possibly returned from the traps module.
|
||||
#[derive(Debug, Snafu)]
|
|
@ -4,27 +4,51 @@
|
|||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::platform;
|
||||
use core::fmt;
|
||||
use {
|
||||
crate::{devices::SerialOps, platform},
|
||||
core::fmt,
|
||||
};
|
||||
|
||||
/// A trait that must be implemented by devices that are candidates for the
|
||||
/// global console.
|
||||
#[allow(unused_variables)]
|
||||
pub trait ConsoleOps {
|
||||
fn putc(&self, c: char) {}
|
||||
fn puts(&self, string: &str) {}
|
||||
fn getc(&self) -> char {
|
||||
' '
|
||||
}
|
||||
fn flush(&self) {}
|
||||
pub trait ConsoleOps: SerialOps {
|
||||
/// Send a character
|
||||
fn write_char(&self, c: char);
|
||||
/// Display a string
|
||||
fn write_string(&self, string: &str);
|
||||
/// Receive a character
|
||||
fn read_char(&self) -> char;
|
||||
}
|
||||
|
||||
/// A dummy console that just ignores its inputs.
|
||||
pub struct NullConsole;
|
||||
|
||||
impl Drop for NullConsole {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
impl ConsoleOps for NullConsole {}
|
||||
|
||||
impl ConsoleOps for NullConsole {
|
||||
fn write_char(&self, _c: char) {}
|
||||
|
||||
fn write_string(&self, _string: &str) {}
|
||||
|
||||
fn read_char(&self) -> char {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for NullConsole {
|
||||
fn read_byte(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn write_byte(&self, _byte: u8) {}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
||||
fn clear_rx(&self) {}
|
||||
}
|
||||
|
||||
/// Possible outputs which the console can store.
|
||||
pub enum Output {
|
||||
|
@ -65,7 +89,6 @@ impl Console {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn current_ptr(&self) -> &dyn ConsoleOps {
|
||||
match &self.output {
|
||||
Output::None(i) => i,
|
||||
|
@ -84,15 +107,15 @@ impl Console {
|
|||
|
||||
/// A command prompt.
|
||||
pub fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
|
||||
self.puts("\n$> ");
|
||||
self.write_string("\n$> ");
|
||||
|
||||
let mut i = 0;
|
||||
let mut input;
|
||||
loop {
|
||||
input = self.getc();
|
||||
input = self.read_char();
|
||||
|
||||
if input == '\n' {
|
||||
self.puts("\n"); // do \r\n output
|
||||
self.write_char('\n'); // do \r\n output
|
||||
return &buf[..i];
|
||||
} else {
|
||||
if i < buf.len() {
|
||||
|
@ -102,7 +125,7 @@ impl Console {
|
|||
return &buf[..i];
|
||||
}
|
||||
|
||||
self.putc(input);
|
||||
self.write_char(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,21 +137,32 @@ impl Drop for Console {
|
|||
|
||||
/// 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 write_char(&self, c: char) {
|
||||
self.current_ptr().write_char(c);
|
||||
}
|
||||
|
||||
fn puts(&self, string: &str) {
|
||||
self.current_ptr().puts(string);
|
||||
fn write_string(&self, string: &str) {
|
||||
self.current_ptr().write_string(string);
|
||||
}
|
||||
|
||||
fn getc(&self) -> char {
|
||||
self.current_ptr().getc()
|
||||
fn read_char(&self) -> char {
|
||||
self.current_ptr().read_char()
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for Console {
|
||||
fn read_byte(&self) -> u8 {
|
||||
self.current_ptr().read_byte()
|
||||
}
|
||||
fn write_byte(&self, byte: u8) {
|
||||
self.current_ptr().write_byte(byte)
|
||||
}
|
||||
fn flush(&self) {
|
||||
self.current_ptr().flush()
|
||||
}
|
||||
fn clear_rx(&self) {
|
||||
self.current_ptr().clear_rx()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementing this trait enables usage of the format_args! macros, which in
|
||||
|
@ -137,8 +171,7 @@ impl ConsoleOps for Console {
|
|||
/// See src/macros.rs.
|
||||
impl fmt::Write for Console {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.current_ptr().puts(s);
|
||||
|
||||
self.current_ptr().write_string(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
*/
|
||||
pub mod console;
|
||||
pub mod serial;
|
||||
|
||||
pub use {
|
||||
console::{Console, ConsoleOps},
|
||||
serial::SerialOps,
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
pub trait SerialOps {
|
||||
/// Read one byte from serial without translation.
|
||||
fn read_byte(&self) -> u8;
|
||||
/// Write one byte to serial without translation.
|
||||
fn write_byte(&self, byte: u8);
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
/// line.
|
||||
fn flush(&self);
|
||||
/// Consume input until RX FIFO is empty, aka all pending characters have been
|
||||
/// consumed.
|
||||
fn clear_rx(&self);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(const_fn_fn_ptr_basics)]
|
||||
#![feature(nonnull_slice_from_raw_parts)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::tests::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
#![allow(clippy::nonstandard_macro_braces)] // https://github.com/shepmaster/snafu/issues/296
|
||||
#![allow(missing_docs)] // Temp: switch to deny
|
||||
#![deny(warnings)]
|
||||
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
use architecture_not_supported_sorry;
|
||||
|
||||
/// Architecture-specific code.
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
|
||||
pub use arch::*;
|
||||
|
||||
pub mod devices;
|
||||
pub mod macros;
|
||||
mod mm;
|
||||
pub mod panic;
|
||||
pub mod platform;
|
||||
pub mod qemu;
|
||||
mod sync;
|
||||
pub mod tests;
|
||||
pub mod write_to;
|
||||
|
||||
/// The global console. Output of the kernel print! and println! macros goes here.
|
||||
pub static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
|
||||
|
||||
/// The global allocator for DMA-able memory. That is, memory which is tagged
|
||||
/// non-cacheable in the page tables.
|
||||
#[allow(dead_code)]
|
||||
static DMA_ALLOCATOR: sync::NullLock<mm::BumpAllocator> =
|
||||
sync::NullLock::new(mm::BumpAllocator::new(
|
||||
// @todo Init this after we loaded boot memory map
|
||||
memory::map::virt::DMA_HEAP_START as usize,
|
||||
memory::map::virt::DMA_HEAP_END as usize,
|
||||
"Global DMA Allocator",
|
||||
// Try the following arguments instead to see all mailbox operations
|
||||
// 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",
|
||||
));
|
|
@ -55,6 +55,7 @@ unsafe impl Allocator for BumpAllocator {
|
|||
|
||||
impl BumpAllocator {
|
||||
/// Create a named bump allocator between start and end addresses.
|
||||
#[allow(dead_code)]
|
||||
pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self {
|
||||
Self {
|
||||
next: Cell::new(pool_start),
|
|
@ -45,15 +45,15 @@ mod tests {
|
|||
// align 1
|
||||
assert_eq!(align_up(0, 1), 0);
|
||||
assert_eq!(align_up(1234, 1), 1234);
|
||||
assert_eq!(align_up(0xffffffffffffffff, 1), 0xffffffffffffffff);
|
||||
assert_eq!(align_up(0xffff_ffff_ffff_ffff, 1), 0xffff_ffff_ffff_ffff);
|
||||
// align 2
|
||||
assert_eq!(align_up(0, 2), 0);
|
||||
assert_eq!(align_up(1233, 2), 1234);
|
||||
assert_eq!(align_up(0xfffffffffffffffe, 2), 0xfffffffffffffffe);
|
||||
assert_eq!(align_up(0xffff_ffff_ffff_fffe, 2), 0xffff_ffff_ffff_fffe);
|
||||
// address 0
|
||||
assert_eq!(align_up(0, 128), 0);
|
||||
assert_eq!(align_up(0, 1), 0);
|
||||
assert_eq!(align_up(0, 2), 0);
|
||||
assert_eq!(align_up(0, 0x8000000000000000), 0);
|
||||
assert_eq!(align_up(0, 0x8000_0000_0000_0000), 0);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,10 @@
|
|||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panicked(info: &core::panic::PanicInfo) -> ! {
|
||||
pub fn handler(info: &core::panic::PanicInfo) -> ! {
|
||||
// @todo This may fail to print if the panic message is too long for local print buffer.
|
||||
crate::println!("{}", info);
|
||||
crate::endless_sleep()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[panic_handler]
|
||||
fn panicked(info: &core::panic::PanicInfo) -> ! {
|
||||
pub fn handler_for_tests(info: &core::panic::PanicInfo) -> ! {
|
||||
crate::println!("\n[failed]\n");
|
||||
// @todo This may fail to print if the panic message is too long for local print buffer.
|
||||
crate::println!("\nError: {}\n", info);
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
use core::{marker::PhantomData, ops};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
pub mod rpi3;
|
||||
|
||||
pub struct MMIODerefWrapper<T> {
|
||||
base_addr: usize,
|
||||
phantom: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl<T> MMIODerefWrapper<T> {
|
||||
/// Create an instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, duh!
|
||||
pub const unsafe fn new(start_addr: usize) -> Self {
|
||||
Self {
|
||||
base_addr: start_addr,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.GPPUD.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
|
||||
/// ```
|
||||
impl<T> ops::Deref for MMIODerefWrapper<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self.base_addr as *const _) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
use super::mailbox::{self, LocalMailboxStorage, Mailbox, MailboxError, MailboxOps};
|
||||
|
||||
/// FrameBuffer channel supported structure - use with mailbox::channel::FrameBuffer
|
||||
/// Must have the same alignment as the mailbox buffers.
|
||||
type FrameBufferData = LocalMailboxStorage<10>;
|
||||
|
||||
mod index {
|
||||
pub const WIDTH: usize = 0;
|
||||
pub const HEIGHT: usize = 1;
|
||||
pub const VIRTUAL_WIDTH: usize = 2;
|
||||
pub const VIRTUAL_HEIGHT: usize = 3;
|
||||
pub const PITCH: usize = 4;
|
||||
pub const DEPTH: usize = 5;
|
||||
pub const X_OFFSET: usize = 6;
|
||||
pub const Y_OFFSET: usize = 7;
|
||||
pub const POINTER: usize = 8; // FIXME: could be 4096 for the alignment restriction.
|
||||
pub const SIZE: usize = 9;
|
||||
}
|
||||
|
||||
pub struct FrameBuffer {
|
||||
mailbox: Mailbox<10, FrameBufferData>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for FrameBufferData {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"\n\n\n#### FrameBuffer({}x{}, {}x{}, d{}, --{}--, +{}x{}, {}@{:x})\n\n\n",
|
||||
self.storage[index::WIDTH],
|
||||
self.storage[index::HEIGHT],
|
||||
self.storage[index::VIRTUAL_WIDTH],
|
||||
self.storage[index::VIRTUAL_HEIGHT],
|
||||
self.storage[index::HEIGHT],
|
||||
self.storage[index::PITCH],
|
||||
self.storage[index::X_OFFSET],
|
||||
self.storage[index::Y_OFFSET],
|
||||
self.storage[index::SIZE],
|
||||
self.storage[index::POINTER],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameBuffer {
|
||||
pub fn new(
|
||||
base_addr: usize,
|
||||
width: u32,
|
||||
height: u32,
|
||||
depth: u32,
|
||||
) -> Result<FrameBuffer, MailboxError> {
|
||||
let mut fb = FrameBuffer {
|
||||
mailbox: unsafe { Mailbox::<10, FrameBufferData>::new(base_addr)? },
|
||||
};
|
||||
fb.mailbox.buffer.storage[index::WIDTH] = width;
|
||||
fb.mailbox.buffer.storage[index::VIRTUAL_WIDTH] = width;
|
||||
fb.mailbox.buffer.storage[index::HEIGHT] = height;
|
||||
fb.mailbox.buffer.storage[index::VIRTUAL_HEIGHT] = height;
|
||||
fb.mailbox.buffer.storage[index::DEPTH] = depth;
|
||||
Ok(fb)
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxOps for FrameBuffer {
|
||||
fn write(&self, _channel: u32) -> mailbox::Result<()> {
|
||||
self.mailbox.do_write(mailbox::channel::FrameBuffer)
|
||||
}
|
||||
|
||||
fn read(&self, _channel: u32) -> mailbox::Result<()> {
|
||||
unsafe { self.mailbox.do_read(mailbox::channel::FrameBuffer, 0) }
|
||||
}
|
||||
}
|
|
@ -7,34 +7,18 @@
|
|||
|
||||
use {
|
||||
super::BcmHost,
|
||||
crate::arch::loop_delay,
|
||||
core::{marker::PhantomData, ops},
|
||||
register::{
|
||||
mmio::{ReadOnly, ReadWrite, WriteOnly},
|
||||
register_bitfields, FieldValue,
|
||||
crate::platform::MMIODerefWrapper,
|
||||
core::marker::PhantomData,
|
||||
tock_registers::{
|
||||
fields::FieldValue,
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
},
|
||||
};
|
||||
|
||||
// Descriptions taken from
|
||||
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
||||
/// GPIO Pull-up/down Clock Register 0
|
||||
PUDCLK0 [
|
||||
/// Pin 15
|
||||
PUDCLK15 OFFSET(15) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
PUDCLK14 OFFSET(14) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// Generates `pub enums` with no variants for each `ident` passed in.
|
||||
macro states($($name:ident),*) {
|
||||
|
@ -46,102 +30,105 @@ states! {
|
|||
Uninitialized, Input, Output, Alt
|
||||
}
|
||||
|
||||
/// A wrapper type that prevents reads or writes to its value.
|
||||
///
|
||||
/// This type implements no methods. It is meant to make the inner type
|
||||
/// inaccessible to prevent accidental reads or writes.
|
||||
#[repr(C)]
|
||||
pub struct Reserved<T>(T);
|
||||
|
||||
/// The offsets for reach register.
|
||||
/// From https://wiki.osdev.org/Raspberry_Pi_Bare_Bones and
|
||||
/// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
pub FSEL: [ReadWrite<u32>; 6], // 0x00-0x14 function select
|
||||
__reserved_0: Reserved<u32>, // 0x18
|
||||
pub SET: [WriteOnly<u32>; 2], // 0x1c-0x20 set output pin
|
||||
__reserved_1: Reserved<u32>, // 0x24
|
||||
pub CLR: [WriteOnly<u32>; 2], // 0x28-0x2c clear output pin
|
||||
__reserved_2: Reserved<u32>, // 0x30
|
||||
pub LEV: [ReadOnly<u32>; 2], // 0x34-0x38 get input pin level
|
||||
__reserved_3: Reserved<u32>, // 0x3C
|
||||
pub EDS: [ReadWrite<u32>; 2], // 0x40-0x44
|
||||
__reserved_4: Reserved<u32>, // 0x48
|
||||
pub REN: [ReadWrite<u32>; 2], // 0x4c-0x50
|
||||
__reserved_5: Reserved<u32>, // 0x54
|
||||
pub FEN: [ReadWrite<u32>; 2], // 0x58-0x5c
|
||||
__reserved_6: Reserved<u32>, // 0x60
|
||||
pub HEN: [ReadWrite<u32>; 2], // 0x64-0x68
|
||||
__reserved_7: Reserved<u32>, // 0x6c
|
||||
pub LEN: [ReadWrite<u32>; 2], // 0x70-0x74
|
||||
__reserved_8: Reserved<u32>, // 0x78
|
||||
pub AREN: [ReadWrite<u32>; 2], // 0x7c-0x80
|
||||
__reserved_9: Reserved<u32>, // 0x84
|
||||
pub AFEN: [ReadWrite<u32>; 2], // 0x88-0x8c
|
||||
__reserved_10: Reserved<u32>, // 0x90
|
||||
pub PUD: ReadWrite<u32>, // 0x94 pull up down
|
||||
pub PUDCLK: [ReadWrite<u32, PUDCLK0::Register>; 2], // 0x98-0x9C -- @todo remove this register
|
||||
register_structs! {
|
||||
/// The offsets for each register.
|
||||
/// From <https://wiki.osdev.org/Raspberry_Pi_Bare_Bones> and
|
||||
/// <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
(0x00 => pub FSEL: [ReadWrite<u32>; 6]), // function select
|
||||
(0x18 => __reserved_1),
|
||||
(0x1c => pub SET: [WriteOnly<u32>; 2]), // set output pin
|
||||
(0x24 => __reserved_2),
|
||||
(0x28 => pub CLR: [WriteOnly<u32>; 2]), // clear output pin
|
||||
(0x30 => __reserved_3),
|
||||
(0x34 => pub LEV: [ReadOnly<u32>; 2]), // get input pin level
|
||||
(0x3c => __reserved_4),
|
||||
(0x40 => pub EDS: [ReadWrite<u32>; 2]),
|
||||
(0x48 => __reserved_5),
|
||||
(0x4c => pub REN: [ReadWrite<u32>; 2]),
|
||||
(0x54 => __reserved_6),
|
||||
(0x58 => pub FEN: [ReadWrite<u32>; 2]),
|
||||
(0x60 => __reserved_7),
|
||||
(0x64 => pub HEN: [ReadWrite<u32>; 2]),
|
||||
(0x6c => __reserved_8),
|
||||
(0x70 => pub LEN: [ReadWrite<u32>; 2]),
|
||||
(0x78 => __reserved_9),
|
||||
(0x7c => pub AREN: [ReadWrite<u32>; 2]),
|
||||
(0x84 => __reserved_10),
|
||||
(0x88 => pub AFEN: [ReadWrite<u32>; 2]),
|
||||
(0x90 => __reserved_11),
|
||||
#[cfg(feature = "rpi3")]
|
||||
(0x94 => pub PUD: ReadWrite<u32>), // pull up down
|
||||
#[cfg(feature = "rpi3")]
|
||||
(0x98 => pub PUDCLK: [ReadWrite<u32>; 2]),
|
||||
#[cfg(feature = "rpi3")]
|
||||
(0xa0 => __reserved_12),
|
||||
#[cfg(feature = "rpi4")]
|
||||
(0xe4 => PullUpDownControl: [ReadWrite<u32>; 4]),
|
||||
(0xf4 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
// Hide RegisterBlock from public api.
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
/// Public interface to the GPIO MMIO area
|
||||
pub struct GPIO {
|
||||
base_addr: usize,
|
||||
registers: Registers,
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.GPPUD.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
|
||||
/// ```
|
||||
impl ops::Deref for GPIO {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
pub const GPIO_START: usize = 0x20_0000;
|
||||
|
||||
impl Default for GPIO {
|
||||
fn default() -> GPIO {
|
||||
// Default RPi3 GPIO base address
|
||||
const GPIO_BASE: usize = BcmHost::get_peripheral_address() + 0x20_0000;
|
||||
GPIO::new(GPIO_BASE)
|
||||
const GPIO_BASE: usize = BcmHost::get_peripheral_address() + GPIO_START;
|
||||
unsafe { GPIO::new(GPIO_BASE) }
|
||||
}
|
||||
}
|
||||
|
||||
impl GPIO {
|
||||
pub fn new(base_addr: usize) -> GPIO {
|
||||
GPIO { base_addr }
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, duh!
|
||||
pub const unsafe fn new(base_addr: usize) -> GPIO {
|
||||
GPIO {
|
||||
registers: Registers::new(base_addr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pin(&self, pin: usize) -> Pin<Uninitialized> {
|
||||
Pin::new(pin, self.base_addr)
|
||||
unsafe { Pin::new(pin, self.registers.base_addr) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_uart_pins(gpio: &GPIO) {
|
||||
gpio.PUD.set(0);
|
||||
#[cfg(feature = "rpi3")]
|
||||
pub fn power_off(&self) {
|
||||
use crate::arch::loop_delay;
|
||||
|
||||
loop_delay(150);
|
||||
// power off gpio pins (but not VCC pins)
|
||||
for bank in 0..5 {
|
||||
self.registers.FSEL[bank].set(0);
|
||||
}
|
||||
|
||||
// enable pins 14 and 15
|
||||
gpio.PUDCLK[0].write(PUDCLK0::PUDCLK14::AssertClock + PUDCLK0::PUDCLK15::AssertClock);
|
||||
self.registers.PUD.set(0);
|
||||
|
||||
loop_delay(150);
|
||||
loop_delay(2000);
|
||||
|
||||
gpio.PUDCLK[0].set(0);
|
||||
self.registers.PUDCLK[0].set(0xffff_ffff);
|
||||
self.registers.PUDCLK[1].set(0xffff_ffff);
|
||||
|
||||
loop_delay(2000);
|
||||
|
||||
// flush GPIO setup
|
||||
self.registers.PUDCLK[0].set(0);
|
||||
self.registers.PUDCLK[1].set(0);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rpi4")]
|
||||
pub fn power_off(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// An alternative GPIO function.
|
||||
|
@ -163,6 +150,21 @@ impl ::core::convert::From<Function> for u32 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Pull up/down resistor setup.
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
pub enum PullUpDown {
|
||||
None = 0b00,
|
||||
Up = 0b01,
|
||||
Down = 0b10,
|
||||
}
|
||||
|
||||
impl ::core::convert::From<PullUpDown> for u32 {
|
||||
fn from(p: PullUpDown) -> Self {
|
||||
p as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPIO pin in state `State`.
|
||||
///
|
||||
/// The `State` generic always corresponds to an un-instantiable type that is
|
||||
|
@ -172,7 +174,7 @@ impl ::core::convert::From<Function> for u32 {
|
|||
/// `into_alt` methods before it can be used.
|
||||
pub struct Pin<State> {
|
||||
pin: usize,
|
||||
base_addr: usize,
|
||||
registers: Registers,
|
||||
_state: PhantomData<State>,
|
||||
}
|
||||
|
||||
|
@ -184,33 +186,43 @@ impl<State> Pin<State> {
|
|||
fn transition<NewState>(self) -> Pin<NewState> {
|
||||
Pin {
|
||||
pin: self.pin,
|
||||
base_addr: self.base_addr,
|
||||
registers: self.registers,
|
||||
_state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
#[cfg(feature = "rpi3")]
|
||||
pub fn set_pull_up_down(&self, pull: PullUpDown) {
|
||||
use crate::arch::loop_delay;
|
||||
|
||||
let bank = self.pin / 32;
|
||||
let off = self.pin % 32;
|
||||
|
||||
self.registers.PUD.set(0);
|
||||
|
||||
loop_delay(2000);
|
||||
|
||||
self.registers.PUDCLK[bank].modify(FieldValue::<u32, ()>::new(
|
||||
0b1,
|
||||
off,
|
||||
if pull == PullUpDown::Up { 1 } else { 0 },
|
||||
));
|
||||
|
||||
loop_delay(2000);
|
||||
|
||||
self.registers.PUD.set(0);
|
||||
self.registers.PUDCLK[bank].set(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Deref to Pin's Registers
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.PUD.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*Pin::ptr()).PUD.read() }
|
||||
/// ```
|
||||
impl<State> ops::Deref for Pin<State> {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
#[cfg(feature = "rpi4")]
|
||||
pub fn set_pull_up_down(&self, pull: PullUpDown) {
|
||||
let bank = self.pin / 16;
|
||||
let off = self.pin % 16;
|
||||
self.registers.PullUpDownControl[bank].modify(FieldValue::<u32, ()>::new(
|
||||
0b11,
|
||||
off * 2,
|
||||
pull.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,13 +232,13 @@ impl Pin<Uninitialized> {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if `pin` > `53`.
|
||||
fn new(pin: usize, base_addr: usize) -> Pin<Uninitialized> {
|
||||
unsafe fn new(pin: usize, base_addr: usize) -> Pin<Uninitialized> {
|
||||
if pin > 53 {
|
||||
panic!("gpio::Pin::new(): pin {} exceeds maximum of 53", pin);
|
||||
}
|
||||
|
||||
Pin {
|
||||
base_addr,
|
||||
registers: Registers::new(base_addr),
|
||||
pin,
|
||||
_state: PhantomData,
|
||||
}
|
||||
|
@ -237,7 +249,11 @@ impl Pin<Uninitialized> {
|
|||
pub fn into_alt(self, function: Function) -> Pin<Alt> {
|
||||
let bank = self.pin / 10;
|
||||
let off = self.pin % 10;
|
||||
self.FSEL[bank].modify(FieldValue::<u32, ()>::new(0b111, off * 3, function.into()));
|
||||
self.registers.FSEL[bank].modify(FieldValue::<u32, ()>::new(
|
||||
0b111,
|
||||
off * 3,
|
||||
function.into(),
|
||||
));
|
||||
self.transition()
|
||||
}
|
||||
|
||||
|
@ -260,7 +276,7 @@ impl Pin<Output> {
|
|||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let shift = self.pin % 32;
|
||||
self.SET[bank].set(1 << shift);
|
||||
self.registers.SET[bank].set(1 << shift);
|
||||
}
|
||||
|
||||
/// Clears (turns off) this pin.
|
||||
|
@ -268,7 +284,7 @@ impl Pin<Output> {
|
|||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let shift = self.pin % 32;
|
||||
self.CLR[bank].set(1 << shift);
|
||||
self.registers.CLR[bank].set(1 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +297,7 @@ impl Pin<Input> {
|
|||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let off = self.pin % 32;
|
||||
self.LEV[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
|
||||
self.registers.LEV[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +308,7 @@ mod tests {
|
|||
#[test_case]
|
||||
fn test_pin_transitions() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
|
||||
|
||||
let _out = gpio.get_pin(1).into_output();
|
||||
assert_eq!(reg[0], 0b001_000);
|
||||
|
@ -305,7 +321,7 @@ mod tests {
|
|||
#[test_case]
|
||||
fn test_pin_outputs() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
|
||||
|
||||
let pin = gpio.get_pin(1);
|
||||
let mut out = pin.into_output();
|
||||
|
@ -325,7 +341,7 @@ mod tests {
|
|||
#[test_case]
|
||||
fn test_pin_inputs() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
|
||||
|
||||
let pin = gpio.get_pin(1);
|
||||
let inp = pin.into_input();
|
|
@ -13,29 +13,34 @@
|
|||
|
||||
use {
|
||||
super::BcmHost,
|
||||
crate::println,
|
||||
crate::{platform::MMIODerefWrapper, println},
|
||||
core::{
|
||||
ops::Deref,
|
||||
ptr::NonNull,
|
||||
result::Result as CoreResult,
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
},
|
||||
cortex_a::barrier,
|
||||
register::{mmio::*, register_bitfields},
|
||||
cortex_a::asm::barrier,
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, WriteOnly},
|
||||
},
|
||||
};
|
||||
|
||||
/// Public interface to the mailbox.
|
||||
/// The address for the buffer needs to be 16-byte aligned
|
||||
/// so that the VideoCore can handle it properly.
|
||||
/// The reason is that lowest 4 bits of the address will contain the channel number.
|
||||
pub struct Mailbox {
|
||||
// pub buffer: &'a mut [u32],
|
||||
base_addr: usize,
|
||||
buffer: NonNull<[u32]>,
|
||||
pub struct Mailbox<const N_SLOTS: usize, Storage = LocalMailboxStorage<N_SLOTS>> {
|
||||
registers: Registers,
|
||||
pub buffer: Storage,
|
||||
}
|
||||
|
||||
/// Mailbox that is ready to be called.
|
||||
/// This prevents invalid use of the mailbox until it is fully prepared.
|
||||
pub struct PreparedMailbox(Mailbox);
|
||||
pub struct PreparedMailbox<const N_SLOTS: usize, Storage = LocalMailboxStorage<N_SLOTS>>(
|
||||
Mailbox<N_SLOTS, Storage>,
|
||||
);
|
||||
|
||||
const MAILBOX_ALIGNMENT: usize = 16;
|
||||
const MAILBOX_ITEMS_COUNT: usize = 36;
|
||||
|
@ -53,6 +58,9 @@ const CHANNEL_MASK: u32 = 0xf;
|
|||
// always for communication from VC to ARM and Mailbox 1 is for ARM to VC.
|
||||
//
|
||||
// The ARM should never write Mailbox 0 or read Mailbox 1.
|
||||
//
|
||||
// There are 32 mailboxes on the ARM, which could be used for in-processor or inter-processor comms,
|
||||
// TODO: allow using all of them.
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
@ -65,42 +73,35 @@ register_bitfields! {
|
|||
]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
READ: ReadOnly<u32>, // 0x00 This is Mailbox0 read for ARM, can't write
|
||||
__reserved_0: [u32; 5], // 0x04
|
||||
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
|
||||
__reserved_1: u32, // 0x1C
|
||||
WRITE: WriteOnly<u32>, // 0x20 This is Mailbox1 write for ARM, can't read
|
||||
}
|
||||
|
||||
pub enum MailboxError {
|
||||
Response,
|
||||
Unknown,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for MailboxError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
MailboxError::Response => "ResponseError",
|
||||
MailboxError::Unknown => "UnknownError",
|
||||
MailboxError::Timeout => "Timeout",
|
||||
}
|
||||
)
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub RegisterBlock {
|
||||
(0x00 => READ: ReadOnly<u32>), // This is Mailbox0 read for ARM, can't write
|
||||
(0x04 => __reserved_1),
|
||||
(0x18 => STATUS: ReadOnly<u32, STATUS::Register>),
|
||||
(0x1c => __reserved_2),
|
||||
(0x20 => WRITE: WriteOnly<u32>), // This is Mailbox1 write for ARM, can't read
|
||||
(0x24 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = ::core::result::Result<T, MailboxError>;
|
||||
// Hide RegisterBlock from public api.
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
#[derive(Snafu, Debug)]
|
||||
pub enum MailboxError {
|
||||
#[snafu(display("ResponseError"))]
|
||||
Response,
|
||||
#[snafu(display("UnknownError"))]
|
||||
Unknown,
|
||||
#[snafu(display("Timeout"))]
|
||||
Timeout,
|
||||
}
|
||||
|
||||
pub type Result<T> = CoreResult<T, MailboxError>;
|
||||
|
||||
/// Typical operations with a mailbox.
|
||||
pub trait MailboxOps {
|
||||
/// Deref from self to a mailbox RegisterBlock. Used by Deref implementations.
|
||||
fn ptr(&self) -> *const RegisterBlock;
|
||||
fn write(&self, channel: u32) -> Result<()>;
|
||||
fn read(&self, channel: u32) -> Result<()>;
|
||||
fn call(&self, channel: u32) -> Result<()> {
|
||||
|
@ -109,6 +110,50 @@ pub trait MailboxOps {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait MailboxStorage {
|
||||
fn new() -> Self;
|
||||
}
|
||||
|
||||
pub trait MailboxStorageRef {
|
||||
fn as_ref(&self) -> &[u32];
|
||||
fn as_mut(&mut self) -> &mut [u32];
|
||||
fn as_ptr(&self) -> *const u32;
|
||||
fn value_at(&self, index: usize) -> u32;
|
||||
}
|
||||
|
||||
// TODO: allow from 2 to 36 slots (2 because you need at least an End tag)
|
||||
#[repr(align(16))] // MAILBOX_ALIGNMENT
|
||||
pub struct LocalMailboxStorage<const N_SLOTS: usize> {
|
||||
pub storage: [u32; N_SLOTS],
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize> MailboxStorage for LocalMailboxStorage<N_SLOTS> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
storage: [0u32; N_SLOTS],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize> MailboxStorageRef for LocalMailboxStorage<N_SLOTS> {
|
||||
fn as_ref(&self) -> &[u32] {
|
||||
&self.storage
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> &mut [u32] {
|
||||
&mut self.storage
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const u32 {
|
||||
self.storage.as_ptr()
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
fn value_at(&self, index: usize) -> u32 {
|
||||
self.storage[index]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Source https://elinux.org/RPi_Framebuffer
|
||||
* Source for channels 8 and 9: https://github.com/raspberrypi/firmware/wiki/Mailboxes
|
||||
|
@ -239,143 +284,39 @@ pub mod alpha_mode {
|
|||
pub const IGNORED: u32 = 2;
|
||||
}
|
||||
|
||||
pub fn write(regs: &RegisterBlock, buf: *const u32, channel: u32) -> Result<()> {
|
||||
let mut count: u32 = 0;
|
||||
let buf_ptr: u32 = buf as u32;
|
||||
|
||||
// This address adjustment will be performed from the outside when necessary
|
||||
// (see FrameBuffer for example).
|
||||
// let buf_ptr = BcmHost::phys2bus(buf_ptr); not used for PropertyTags channel
|
||||
|
||||
println!("Mailbox::write {:#08x}/{:#x}", buf_ptr, channel);
|
||||
|
||||
// Insert a compiler fence that ensures that all stores to the
|
||||
// mailbox buffer are finished before the GPU is signaled (which is
|
||||
// done by a store operation as well).
|
||||
compiler_fence(Ordering::Release);
|
||||
|
||||
while regs.STATUS.is_set(STATUS::FULL) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
regs.WRITE
|
||||
.set((buf_ptr & !CHANNEL_MASK) | (channel & CHANNEL_MASK));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(regs: &RegisterBlock, expected: u32, channel: u32) -> Result<()> {
|
||||
loop {
|
||||
let mut count: u32 = 0;
|
||||
while regs.STATUS.is_set(STATUS::EMPTY) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
println!("Timed out waiting for mailbox response");
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data
|
||||
* Data memory barriers as we've switched peripheral
|
||||
*/
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
let data: u32 = regs.READ.get();
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
|
||||
println!(
|
||||
"Received mailbox response {:#08x}, expecting {:#08x}",
|
||||
data, expected
|
||||
);
|
||||
|
||||
// is it a response to our message?
|
||||
if ((data & CHANNEL_MASK) == channel) && ((data & !CHANNEL_MASK) == expected) {
|
||||
// is it a valid successful response?
|
||||
return Ok(());
|
||||
} else {
|
||||
// ignore invalid responses and loop again.
|
||||
// will return Timeout above if no matching response is received.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.STATUS.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*Mailbox::ptr()).STATUS.read() }
|
||||
/// ```
|
||||
impl Deref for PreparedMailbox {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Mailbox {
|
||||
impl<const N_SLOTS: usize> core::fmt::Debug for Mailbox<N_SLOTS> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let count = unsafe { self.buffer.as_ref()[0] } / 4;
|
||||
assert_eq!(unsafe { self.buffer.as_ref()[0] }, count * 4);
|
||||
let count = self.buffer.as_ref()[0] / 4;
|
||||
assert_eq!(self.buffer.as_ref()[0], count * 4);
|
||||
assert!(count <= 36);
|
||||
for i in 0usize..count as usize {
|
||||
writeln!(f, "[{:02}] {:08x}", i, unsafe { self.buffer.as_ref()[i] })?;
|
||||
writeln!(f, "[{:02}] {:08x}", i, self.buffer.value_at(i))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for PreparedMailbox {
|
||||
impl<const N_SLOTS: usize> core::fmt::Debug for PreparedMailbox<N_SLOTS> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mailbox {
|
||||
impl<const N_SLOTS: usize> Default for Mailbox<N_SLOTS> {
|
||||
fn default() -> Self {
|
||||
Self::new(MAILBOX_BASE).expect("Couldn't allocate a default mailbox")
|
||||
unsafe { Self::new(MAILBOX_BASE) }.expect("Couldn't allocate a default mailbox")
|
||||
}
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
impl PreparedMailbox {
|
||||
pub fn value_at(&self, index: usize) -> u32 {
|
||||
unsafe { self.0.buffer.as_ref()[index] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
/// Create a new mailbox in the DMA-able memory area.
|
||||
pub fn new(base_addr: usize) -> ::core::result::Result<Mailbox, ()> {
|
||||
use core::alloc::Allocator;
|
||||
crate::DMA_ALLOCATOR
|
||||
.lock(|dma| {
|
||||
dma.allocate_zeroed(
|
||||
core::alloc::Layout::from_size_align(
|
||||
MAILBOX_ITEMS_COUNT * core::mem::size_of::<u32>(),
|
||||
MAILBOX_ALIGNMENT,
|
||||
)
|
||||
.unwrap(), // .map_err(|_| ())?,
|
||||
)
|
||||
})
|
||||
.map(|ret| {
|
||||
Ok(Mailbox {
|
||||
base_addr,
|
||||
buffer: ret.cast::<[u32; MAILBOX_ITEMS_COUNT]>(),
|
||||
})
|
||||
})
|
||||
.map_err(|_| ())?
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> Mailbox<N_SLOTS, Storage> {
|
||||
/// Create a new mailbox locally in an aligned stack space.
|
||||
/// # Safety
|
||||
/// Caller is responsible for picking the correct MMIO register base address.
|
||||
pub unsafe fn new(base_addr: usize) -> Result<Mailbox<N_SLOTS, Storage>> {
|
||||
Ok(Mailbox {
|
||||
registers: Registers::new(base_addr),
|
||||
buffer: Storage::new(),
|
||||
})
|
||||
}
|
||||
|
||||
// Specific mailbox functions
|
||||
|
@ -385,19 +326,17 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn request(&mut self) -> usize {
|
||||
unsafe { self.buffer.as_mut()[1] = REQUEST };
|
||||
self.buffer.as_mut()[1] = REQUEST;
|
||||
2
|
||||
}
|
||||
|
||||
/// Mark mailbox payload as completed.
|
||||
/// Consumes the Mailbox and returns a Preparedmailbox that can be called.
|
||||
#[inline]
|
||||
pub fn end(mut self, index: usize) -> PreparedMailbox {
|
||||
pub fn end(mut self, index: usize) -> PreparedMailbox<N_SLOTS, Storage> {
|
||||
// @todo return Result
|
||||
unsafe {
|
||||
self.buffer.as_mut()[index] = tag::End;
|
||||
self.buffer.as_mut()[0] = (index as u32 + 1) * 4;
|
||||
}
|
||||
self.buffer.as_mut()[index] = tag::End;
|
||||
self.buffer.as_mut()[0] = (index as u32 + 1) * 4;
|
||||
PreparedMailbox(self)
|
||||
}
|
||||
|
||||
|
@ -405,7 +344,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_physical_wh(&mut self, index: usize, width: u32, height: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPhysicalWH;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Request size // val size
|
||||
|
@ -418,7 +357,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_virtual_wh(&mut self, index: usize, width: u32, height: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetVirtualWH;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Request size // val size
|
||||
|
@ -431,7 +370,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_depth(&mut self, index: usize, depth: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetDepth;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Request size // val size
|
||||
|
@ -443,7 +382,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn allocate_buffer_aligned(&mut self, index: usize, alignment: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::AllocateBuffer;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Request size // val size
|
||||
|
@ -456,7 +395,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_led_on(&mut self, index: usize, enable: bool) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetGpioState;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 0; // Response size // val size
|
||||
|
@ -467,7 +406,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_clock_rate(&mut self, index: usize, channel: u32, rate: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetClockRate;
|
||||
buf[index + 1] = 12; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Response size // val size
|
||||
|
@ -478,12 +417,12 @@ impl Mailbox {
|
|||
}
|
||||
|
||||
/// NB: Do not intermix Get/Set and Test tags in one request!
|
||||
/// See https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
|
||||
/// See <https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface>
|
||||
/// * It is not valid to mix Test tags with Get/Set tags in the same operation
|
||||
/// and no tags will be returned.
|
||||
#[inline]
|
||||
pub fn set_pixel_order(&mut self, index: usize, order: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPixelOrder;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -492,12 +431,12 @@ impl Mailbox {
|
|||
}
|
||||
|
||||
/// NB: Do not intermix Get/Set and Test tags in one request!
|
||||
/// See https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
|
||||
/// See <https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface>
|
||||
/// * It is not valid to mix Test tags with Get/Set tags in the same operation
|
||||
/// and no tags will be returned.
|
||||
#[inline]
|
||||
pub fn test_pixel_order(&mut self, index: usize, order: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::TestPixelOrder;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -507,7 +446,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_alpha_mode(&mut self, index: usize, mode: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetAlphaMode;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -517,7 +456,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn get_pitch(&mut self, index: usize) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::GetPitch;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -527,7 +466,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_device_power(&mut self, index: usize, device_id: u32, power_flags: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPowerState;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Response size // val size
|
||||
|
@ -535,42 +474,130 @@ impl Mailbox {
|
|||
buf[index + 4] = power_flags; // bit 0: off, bit 1: no wait
|
||||
index + 5
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxOps for PreparedMailbox {
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.0.base_addr as *const _
|
||||
}
|
||||
// Actual work functions
|
||||
|
||||
fn write(&self, channel: u32) -> Result<()> {
|
||||
write(self, self.0.buffer.as_ptr() as *const _, channel)
|
||||
}
|
||||
/// <https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes> says:
|
||||
/// **With the exception of the property tags mailbox channel,**
|
||||
/// when passing memory addresses as the data part of a mailbox message,
|
||||
/// the addresses should be **bus addresses as seen from the VC.**
|
||||
pub fn do_write(&self, channel: u32) -> Result<()> {
|
||||
let buf_ptr = self.buffer.as_ptr() as *const u32 as u32;
|
||||
let buf_ptr = if channel != channel::PropertyTagsArmToVc {
|
||||
BcmHost::phys2bus(buf_ptr as usize) as u32
|
||||
} else {
|
||||
buf_ptr
|
||||
};
|
||||
|
||||
// @todo read() should probably consume PreparedMailbox completely ?
|
||||
fn read(&self, channel: u32) -> Result<()> {
|
||||
// SAFETY: buffer is HW-mutable in the read call below!
|
||||
read(
|
||||
self,
|
||||
self.0.buffer.as_ptr() as *const [u32] as *const u32 as u32,
|
||||
channel,
|
||||
)?;
|
||||
let mut count: u32 = 0;
|
||||
|
||||
match unsafe { self.0.buffer.as_ref()[1] } {
|
||||
response::SUCCESS => {
|
||||
println!("\n######\nMailbox::returning SUCCESS");
|
||||
Ok(())
|
||||
}
|
||||
response::ERROR => {
|
||||
println!("\n######\nMailbox::returning ResponseError");
|
||||
Err(MailboxError::Response)
|
||||
}
|
||||
_ => {
|
||||
println!("\n######\nMailbox::returning UnknownError");
|
||||
println!("{:x}\n######", unsafe { self.0.buffer.as_ref()[1] });
|
||||
Err(MailboxError::Unknown)
|
||||
println!("Mailbox::write {:#08x}/{:#x}", buf_ptr, channel);
|
||||
|
||||
// Insert a compiler fence that ensures that all stores to the
|
||||
// mailbox buffer are finished before the GPU is signaled (which is
|
||||
// done by a store operation as well).
|
||||
compiler_fence(Ordering::Release);
|
||||
|
||||
while self.registers.STATUS.is_set(STATUS::FULL) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
self.registers
|
||||
.WRITE
|
||||
.set((buf_ptr & !CHANNEL_MASK) | (channel & CHANNEL_MASK));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the mailbox read.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Buffer will be mutated by the hardware before read operation is completed.
|
||||
pub unsafe fn do_read(&self, channel: u32, expected: u32) -> Result<()> {
|
||||
loop {
|
||||
let mut count: u32 = 0;
|
||||
while self.registers.STATUS.is_set(STATUS::EMPTY) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
println!("Timed out waiting for mailbox response");
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data
|
||||
* Data memory barriers as we've switched peripheral
|
||||
*/
|
||||
barrier::dmb(barrier::SY);
|
||||
let data: u32 = self.registers.READ.get();
|
||||
barrier::dmb(barrier::SY);
|
||||
|
||||
println!(
|
||||
"Received mailbox response {:#08x}, expecting {:#08x}",
|
||||
data, expected
|
||||
);
|
||||
|
||||
// is it a response to our message?
|
||||
if ((data & CHANNEL_MASK) == channel) && ((data & !CHANNEL_MASK) == expected) {
|
||||
// is it a valid successful response?
|
||||
return match self.buffer.value_at(1) {
|
||||
response::SUCCESS => {
|
||||
println!("\n######\nMailbox::returning SUCCESS");
|
||||
Ok(())
|
||||
}
|
||||
response::ERROR => {
|
||||
println!("\n######\nMailbox::returning ResponseError");
|
||||
Err(MailboxError::Response)
|
||||
}
|
||||
_ => {
|
||||
println!("\n######\nMailbox::returning UnknownError");
|
||||
println!("{:x}\n######", self.buffer.value_at(1));
|
||||
Err(MailboxError::Unknown)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// ignore invalid responses and loop again.
|
||||
// will return Timeout above if no matching response is received.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> MailboxOps
|
||||
for PreparedMailbox<N_SLOTS, Storage>
|
||||
{
|
||||
fn write(&self, channel: u32) -> Result<()> {
|
||||
self.0.do_write(channel)
|
||||
}
|
||||
|
||||
// @todo read() should probably consume PreparedMailbox completely - because request is overwritten with response
|
||||
fn read(&self, channel: u32) -> Result<()> {
|
||||
unsafe { self.0.do_read(channel, self.0.buffer.as_ptr() as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> MailboxStorageRef
|
||||
for PreparedMailbox<N_SLOTS, Storage>
|
||||
{
|
||||
fn as_ref(&self) -> &[u32] {
|
||||
self.0.buffer.as_ref()
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> &mut [u32] {
|
||||
self.0.buffer.as_mut()
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const u32 {
|
||||
self.0.buffer.as_ptr()
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
fn value_at(&self, index: usize) -> u32 {
|
||||
self.0.buffer.value_at(index)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,21 @@
|
|||
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
|
||||
*/
|
||||
|
||||
#[cfg(not(feature = "noserial"))]
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use {
|
||||
super::{gpio, BcmHost},
|
||||
crate::devices::ConsoleOps,
|
||||
crate::{
|
||||
devices::{ConsoleOps, SerialOps},
|
||||
platform::MMIODerefWrapper,
|
||||
},
|
||||
cfg_if::cfg_if,
|
||||
core::{convert::From, fmt, ops},
|
||||
register::{mmio::*, register_bitfields},
|
||||
core::{convert::From, fmt},
|
||||
tock_registers::{
|
||||
interfaces::ReadWriteable,
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
},
|
||||
};
|
||||
|
||||
// Auxiliary mini UART registers
|
||||
|
@ -82,33 +91,62 @@ register_bitfields! {
|
|||
]
|
||||
],
|
||||
|
||||
/// Mini Uart Baudrate
|
||||
/// Mini Uart Status
|
||||
AUX_MU_STAT [
|
||||
TX_DONE OFFSET(9) NUMBITS(1) [
|
||||
No = 0,
|
||||
Yes = 1
|
||||
],
|
||||
|
||||
/// This bit is set if the transmit FIFO can accept at least
|
||||
/// one byte.
|
||||
SPACE_AVAILABLE OFFSET(1) NUMBITS(1) [
|
||||
No = 0,
|
||||
Yes = 1
|
||||
],
|
||||
|
||||
/// This bit is set if the receive FIFO holds at least 1
|
||||
/// symbol.
|
||||
SYMBOL_AVAILABLE OFFSET(0) NUMBITS(1) [
|
||||
No = 0,
|
||||
Yes = 1
|
||||
]
|
||||
],
|
||||
|
||||
/// Mini Uart Baud rate
|
||||
AUX_MU_BAUD [
|
||||
/// Mini UART baudrate counter
|
||||
/// Mini UART baud rate counter
|
||||
RATE OFFSET(0) NUMBITS(16) []
|
||||
]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
__reserved_0: u32, // 0x00 - AUX_IRQ?
|
||||
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_MSR, AUX_MU_SCRATCH
|
||||
AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
|
||||
__reserved_3: u32, // 0x64 - AUX_MU_STAT
|
||||
AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
// 0x00 - AUX_IRQ?
|
||||
(0x00 => __reserved_1),
|
||||
(0x04 => AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>),
|
||||
(0x08 => __reserved_2),
|
||||
(0x40 => AUX_MU_IO: ReadWrite<u32>),//Mini Uart I/O Data
|
||||
(0x44 => AUX_MU_IER: WriteOnly<u32>),//Mini Uart Interrupt Enable
|
||||
(0x48 => AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>),
|
||||
(0x4c => AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>),
|
||||
(0x50 => AUX_MU_MCR: WriteOnly<u32>),
|
||||
(0x54 => AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>),
|
||||
// 0x58 - AUX_MU_MSR
|
||||
// 0x5c - AUX_MU_SCRATCH
|
||||
(0x58 => __reserved_3),
|
||||
(0x60 => AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>),
|
||||
(0x64 => AUX_MU_STAT: ReadOnly<u32, AUX_MU_STAT::Register>),
|
||||
(0x68 => AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>),
|
||||
(0x6c => @END),
|
||||
}
|
||||
}
|
||||
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
pub struct MiniUart {
|
||||
base_addr: usize,
|
||||
registers: Registers,
|
||||
}
|
||||
|
||||
pub struct PreparedMiniUart(MiniUart);
|
||||
|
@ -124,41 +162,24 @@ impl From<Rate> for 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() }
|
||||
}
|
||||
}
|
||||
|
||||
// [temporary] Used in mmu.rs to set up local paging
|
||||
pub const UART1_BASE: usize = BcmHost::get_peripheral_address() + 0x21_5000;
|
||||
pub const UART1_START: usize = 0x21_5000;
|
||||
|
||||
impl Default for MiniUart {
|
||||
fn default() -> MiniUart {
|
||||
MiniUart::new(UART1_BASE)
|
||||
fn default() -> Self {
|
||||
const UART1_BASE: usize = BcmHost::get_peripheral_address() + UART1_START;
|
||||
unsafe { MiniUart::new(UART1_BASE) }
|
||||
}
|
||||
}
|
||||
|
||||
impl MiniUart {
|
||||
pub fn new(base_addr: usize) -> MiniUart {
|
||||
MiniUart { base_addr }
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, duh!
|
||||
pub const unsafe fn new(base_addr: usize) -> MiniUart {
|
||||
MiniUart {
|
||||
registers: Registers::new(base_addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,16 +188,7 @@ impl MiniUart {
|
|||
if #[cfg(not(feature = "noserial"))] {
|
||||
/// Set baud rate and characteristics (115200 8N1) and map to GPIO
|
||||
pub fn prepare(self, gpio: &gpio::GPIO) -> PreparedMiniUart {
|
||||
// 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(Rate::Baud115200.into()));
|
||||
// GPIO pins should be set up first before enabling the UART
|
||||
|
||||
// Pin 14
|
||||
const MINI_UART_TXD: gpio::Function = gpio::Function::Alt5;
|
||||
|
@ -184,16 +196,24 @@ impl MiniUart {
|
|||
const MINI_UART_RXD: gpio::Function = gpio::Function::Alt5;
|
||||
|
||||
// map UART1 to GPIO pins
|
||||
gpio.get_pin(14).into_alt(MINI_UART_TXD);
|
||||
gpio.get_pin(15).into_alt(MINI_UART_RXD);
|
||||
gpio.get_pin(14).into_alt(MINI_UART_TXD).set_pull_up_down(gpio::PullUpDown::Up);
|
||||
gpio.get_pin(15).into_alt(MINI_UART_RXD).set_pull_up_down(gpio::PullUpDown::Up);
|
||||
|
||||
gpio::enable_uart_pins(gpio);
|
||||
|
||||
self.AUX_MU_CNTL
|
||||
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
|
||||
// initialize UART
|
||||
self.registers.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
|
||||
self.registers.AUX_MU_IER.set(0);
|
||||
self.registers.AUX_MU_CNTL.set(0);
|
||||
self.registers.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
|
||||
self.registers.AUX_MU_MCR.set(0);
|
||||
self.registers.AUX_MU_IER.set(0);
|
||||
self.registers.AUX_MU_BAUD
|
||||
.write(AUX_MU_BAUD::RATE.val(Rate::Baud115200.into()));
|
||||
|
||||
// Clear FIFOs before using the device
|
||||
self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
|
||||
self.registers.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
|
||||
|
||||
self.registers.AUX_MU_CNTL
|
||||
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
|
||||
|
||||
PreparedMiniUart(self)
|
||||
}
|
||||
|
@ -208,71 +228,101 @@ impl MiniUart {
|
|||
impl Drop for PreparedMiniUart {
|
||||
fn drop(&mut self) {
|
||||
self.0
|
||||
.registers
|
||||
.AUX_ENABLES
|
||||
.modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR);
|
||||
// @todo disable gpio.PUD ?
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for PreparedMiniUart {
|
||||
cfg_if! {
|
||||
if #[cfg(not(feature = "noserial"))] {
|
||||
/// Receive a byte without console translation
|
||||
fn read_byte(&self) -> u8 {
|
||||
// wait until something is in the buffer
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE));
|
||||
|
||||
// read it and return
|
||||
self.0.registers.AUX_MU_IO.get() as u8
|
||||
}
|
||||
|
||||
fn write_byte(&self, b: u8) {
|
||||
// wait until we can send
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SPACE_AVAILABLE));
|
||||
|
||||
// write the character to the buffer
|
||||
self.0.registers.AUX_MU_IO.set(b as u32);
|
||||
}
|
||||
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
/// line.
|
||||
fn flush(&self) {
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::TX_DONE));
|
||||
}
|
||||
|
||||
/// Consume input until RX FIFO is empty, aka all pending characters have been
|
||||
/// consumed.
|
||||
fn clear_rx(&self) {
|
||||
crate::arch::loop_while(|| {
|
||||
let pending = self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE);
|
||||
if pending { self.read_byte(); }
|
||||
pending
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fn read_byte(&self) -> u8 { 0 }
|
||||
fn write_byte(&self, _byte: u8) {}
|
||||
fn flush(&self) {}
|
||||
fn clear_rx(&self) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedMiniUart {
|
||||
cfg_if! {
|
||||
if #[cfg(not(feature = "noserial"))] {
|
||||
/// Send a character
|
||||
fn putc(&self, c: char) {
|
||||
// wait until we can send
|
||||
crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY));
|
||||
|
||||
// write the character to the buffer
|
||||
self.0.AUX_MU_IO.set(c as u32);
|
||||
fn write_char(&self, c: char) {
|
||||
self.write_byte(c as u8);
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn puts(&self, string: &str) {
|
||||
fn write_string(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.putc('\r')
|
||||
self.write_char('\r')
|
||||
}
|
||||
|
||||
self.putc(c);
|
||||
self.write_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn getc(&self) -> char {
|
||||
// wait until something is in the buffer
|
||||
crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
|
||||
fn read_char(&self) -> char {
|
||||
let mut ret = self.read_byte() as char;
|
||||
|
||||
// read it and return
|
||||
let mut ret = self.0.AUX_MU_IO.get() as u8 as char;
|
||||
|
||||
// convert carriage return to newline
|
||||
// convert carriage return to newline -- this doesn't work well for reading binaries...
|
||||
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) {
|
||||
crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE));
|
||||
}
|
||||
} else {
|
||||
fn putc(&self, _c: char) {}
|
||||
fn puts(&self, _string: &str) {}
|
||||
fn getc(&self) -> char {
|
||||
fn write_char(&self, _c: char) {}
|
||||
fn write_string(&self, _string: &str) {}
|
||||
fn read_char(&self) -> char {
|
||||
'\n'
|
||||
}
|
||||
fn flush(&self) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for PreparedMiniUart {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.puts(s);
|
||||
self.write_string(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub mod display;
|
||||
pub mod fb;
|
||||
pub mod gpio;
|
||||
pub mod mailbox;
|
||||
pub mod mini_uart;
|
||||
pub mod pl011_uart;
|
||||
pub mod power;
|
||||
pub mod vc;
|
||||
|
||||
/// See BCM2835-ARM-Peripherals.pdf
|
||||
/// See <https://www.raspberrypi.org/forums/viewtopic.php?t=186090> for more details.
|
||||
|
||||
pub struct BcmHost;
|
||||
|
||||
// Per <https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#peripheral-addresses>:
|
||||
//
|
||||
// SoC Peripheral Address Peripheral Size SDRAM Address Source
|
||||
// BCM2835 0x20000000 0x01000000 0x40000000 <https://github.com/raspberrypi/linux/blob/7f465f823c2ecbade5877b8bbcb2093a8060cb0e/arch/arm/boot/dts/bcm2835.dtsi#L21>
|
||||
// BCM2836 0x3f000000 0x01000000 0xc0000000 <https://github.com/raspberrypi/linux/blob/7f465f823c2ecbade5877b8bbcb2093a8060cb0e/arch/arm/boot/dts/bcm2836.dtsi#L10>
|
||||
// BCM2837 0x3f000000 0x01000000 0xc0000000 <https://github.com/raspberrypi/linux/blob/7f465f823c2ecbade5877b8bbcb2093a8060cb0e/arch/arm/boot/dts/bcm2837.dtsi#L9>
|
||||
// BCM2711 0xfe000000 0x01800000 0xc0000000 <https://github.com/raspberrypi/linux/blob/7f465f823c2ecbade5877b8bbcb2093a8060cb0e/arch/arm/boot/dts/bcm2711.dtsi#L41>
|
||||
|
||||
// <https://www.raspberrypi.com/documentation/computers/processors.html>
|
||||
// The BCM2835 is the Broadcom chip used in the Raspberry Pi Model A, B, B+, the Compute Module, and the Raspberry Pi Zero.
|
||||
// The BCM2836 is used in the Raspberry Pi 2 Model B.
|
||||
// The BCM2837 is used in the Raspberry Pi 3, and in later models of the Raspberry Pi 2.
|
||||
// The BCM2837B0 is used in the Raspberry Pi 3B+ and 3A+.
|
||||
// The BCM2711 is used in the Raspberry Pi 4 Model B.
|
||||
// RP3A0 (BCM2710A1 — which is the die packaged inside the BCM2837 chip - Raspberry Pi 3) used in Raspberry Pi Zero 2 W
|
||||
|
||||
// Machine Board Chip
|
||||
// raspi1 raspi bcm2835
|
||||
// raspi1 raspi bcm2835
|
||||
// raspi3b+ raspi bcm2837
|
||||
// raspi4 raspi bcm2711
|
||||
|
||||
impl BcmHost {
|
||||
/// At which address to load the kernel binary.
|
||||
pub const fn kernel_load_address() -> u64 {
|
||||
0x8_0000
|
||||
}
|
||||
|
||||
/// As per <https://www.raspberrypi.org/forums/viewtopic.php?p=1170522#p1170522>
|
||||
///
|
||||
pub fn bus2phys(bus: usize) -> usize {
|
||||
bus & !0xc000_0000
|
||||
}
|
||||
|
||||
pub fn phys2bus(phys: usize) -> usize {
|
||||
phys | 0xc000_0000
|
||||
}
|
||||
}
|
||||
|
||||
// RasPi3B+
|
||||
#[cfg(feature = "rpi3")]
|
||||
impl BcmHost {
|
||||
/// Name of the hardware device this BcmHost is compiled for.
|
||||
pub const fn board_name() -> &'static str {
|
||||
"Raspberry Pi 3+"
|
||||
}
|
||||
|
||||
/// This returns the ARM-side physical address where peripherals are mapped.
|
||||
///
|
||||
pub const fn get_peripheral_address() -> usize {
|
||||
0x3f00_0000
|
||||
}
|
||||
|
||||
/// This returns the size of the peripherals' space.
|
||||
pub const fn get_peripheral_size() -> usize {
|
||||
0x0100_0000
|
||||
}
|
||||
|
||||
/// This returns the bus address of the SDRAM.
|
||||
pub const fn get_sdram_address() -> usize {
|
||||
0xc000_0000 // uncached
|
||||
}
|
||||
}
|
||||
|
||||
// RasPi4
|
||||
#[cfg(feature = "rpi4")]
|
||||
impl BcmHost {
|
||||
/// Name of the hardware device this BcmHost is compiled for.
|
||||
pub const fn board_name() -> &'static str {
|
||||
"Raspberry Pi 4+"
|
||||
}
|
||||
|
||||
/// This returns the ARM-side physical address where peripherals are mapped.
|
||||
///
|
||||
pub const fn get_peripheral_address() -> usize {
|
||||
0xfe00_0000
|
||||
}
|
||||
|
||||
/// This returns the size of the peripherals' space.
|
||||
pub const fn get_peripheral_size() -> usize {
|
||||
0x0180_0000
|
||||
}
|
||||
|
||||
/// This returns the bus address of the SDRAM.
|
||||
pub const fn get_sdram_address() -> usize {
|
||||
0xc000_0000 // uncached
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
|
||||
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
|
||||
*
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ddi0183g/DDI0183G_uart_pl011_r1p5_trm.pdf
|
||||
* https://docs.rs/embedded-serial/0.5.0/embedded_serial/
|
||||
*/
|
||||
|
||||
use {
|
||||
super::{
|
||||
gpio,
|
||||
mailbox::{self, Mailbox, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::{
|
||||
arch::loop_while,
|
||||
devices::{ConsoleOps, SerialOps},
|
||||
platform::MMIODerefWrapper,
|
||||
},
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
},
|
||||
};
|
||||
|
||||
// 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 empty. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the Line Control Register, If the
|
||||
/// FIFO is disabled, this bit is set when the transmit holding
|
||||
/// register is empty. If the FIFO is enabled, the TXFE bit is
|
||||
/// set when the transmit FIFO is empty. This bit does not indicate
|
||||
/// if there is data in the transmit shift register.
|
||||
TXFE OFFSET(7) NUMBITS(1) [],
|
||||
|
||||
/// Receive FIFO full. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the LCRH Register. If the FIFO is
|
||||
/// disabled, this bit is set when the receive holding register
|
||||
/// is full. If the FIFO is enabled, the RXFF bit is set when
|
||||
/// the receive FIFO is full.
|
||||
RXFF OFFSET(6) NUMBITS(1) [],
|
||||
|
||||
/// Transmit FIFO full. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the 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 LCRH 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) [],
|
||||
|
||||
/// UART busy. If this bit is set to 1, the UART is busy
|
||||
/// transmitting data. This bit remains set until the complete
|
||||
/// byte, including all the stop bits, has been sent from the
|
||||
/// shift register. This bit is set as soon as the transmit FIFO
|
||||
/// becomes non-empty, regardless of whether the UART is enabled or not.
|
||||
BUSY OFFSET(3) 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 [
|
||||
Parity OFFSET(1) NUMBITS(1) [
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
],
|
||||
|
||||
/// Use 2 stop bits
|
||||
Stop2 OFFSET(3) NUMBITS(1) [
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
],
|
||||
|
||||
Fifo OFFSET(4) NUMBITS(1) [
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
],
|
||||
|
||||
/// Word length. These bits indicate the number of data bits
|
||||
/// transmitted or received in a frame.
|
||||
WordLength 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) []
|
||||
],
|
||||
|
||||
/// Interupt Mask Set/Clear Register
|
||||
IMSC [
|
||||
/// Meta field for all interrupts
|
||||
ALL OFFSET(0) NUMBITS(11) []
|
||||
],
|
||||
|
||||
/// DMA Control Register
|
||||
DMACR [
|
||||
// RX DMA enabled
|
||||
RXDMAE OFFSET(0) NUMBITS(1) [
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
],
|
||||
|
||||
// TX DMA enabled
|
||||
TXDMAE OFFSET(0) NUMBITS(1) [
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
// https://developer.arm.com/documentation/ddi0183/g/programmers-model/summary-of-registers?lang=en
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
(0x00 => Data: ReadWrite<u32>), // DR
|
||||
(0x04 => Status: ReadWrite<u32>), // RSR/ECR
|
||||
(0x08 => __reserved_1),
|
||||
(0x18 => Flag: ReadOnly<u32, FR::Register>),
|
||||
(0x1c => __reserved_2),
|
||||
(0x24 => IntegerBaudRate: ReadWrite<u32, IBRD::Register>),
|
||||
(0x28 => FractionalBaudRate: ReadWrite<u32, FBRD::Register>),
|
||||
(0x2c => LineControl: ReadWrite<u32, LCRH::Register>),
|
||||
(0x30 => Control: ReadWrite<u32, CR::Register>),
|
||||
(0x34 => InterruptFifoLevelSelect: ReadWrite<u32>),
|
||||
(0x38 => InterruptMaskSetClear: ReadWrite<u32, IMSC::Register>),
|
||||
(0x3c => RawInterruptStatus: ReadOnly<u32>),
|
||||
(0x40 => MaskedInterruptStatus: ReadOnly<u32>),
|
||||
(0x44 => InterruptClear: WriteOnly<u32, ICR::Register>),
|
||||
(0x48 => DmaControl: ReadWrite<u32, DMACR::Register>),
|
||||
(0x4c => __reserved_3),
|
||||
(0x1000 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum PL011UartError {
|
||||
#[snafu(display("PL011 UART setup failed in mailbox operation"))]
|
||||
MailboxError,
|
||||
#[snafu(display(
|
||||
"PL011 UART setup failed due to integer baud rate divisor out of range ({})",
|
||||
ibrd
|
||||
))]
|
||||
InvalidIntegerDivisor { ibrd: u32 },
|
||||
#[snafu(display(
|
||||
"PL011 UART setup failed due to fractional baud rate divisor out of range ({})",
|
||||
fbrd
|
||||
))]
|
||||
InvalidFractionalDivisor { fbrd: u32 },
|
||||
}
|
||||
|
||||
pub type Result<T> = ::core::result::Result<T, PL011UartError>;
|
||||
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
pub struct PL011Uart {
|
||||
registers: Registers,
|
||||
}
|
||||
|
||||
pub struct PreparedPL011Uart(PL011Uart);
|
||||
|
||||
pub struct RateDivisors {
|
||||
integer_baud_rate_divisor: u32,
|
||||
fractional_baud_rate_divisor: u32,
|
||||
}
|
||||
|
||||
impl RateDivisors {
|
||||
// Set integer & fractional part of baud rate.
|
||||
// Integer = clock/(16 * Baud)
|
||||
// e.g. 3000000 / (16 * 115200) = 1.627 = ~1.
|
||||
// Fraction = (Fractional part * 64) + 0.5
|
||||
// e.g. (.627 * 64) + 0.5 = 40.6 = ~40.
|
||||
//
|
||||
// Use integer-only calculation based on [this page](https://krinkinmu.github.io/2020/11/29/PL011.html)
|
||||
// Calculate 64 * clock / (16 * rate) = 4 * clock / rate, then extract 6 lowest bits for fractional part
|
||||
// and the next 16 bits for integer part.
|
||||
pub fn from_clock_and_rate(clock: u64, baud_rate: u32) -> Result<RateDivisors> {
|
||||
let value = 4 * clock / baud_rate as u64;
|
||||
let i = ((value >> 6) & 0xffff) as u32;
|
||||
let f = (value & 0x3f) as u32;
|
||||
// TODO: check for integer overflow, i.e. any bits set above the 0x3fffff mask.
|
||||
// FIXME: can't happen due to calculation above
|
||||
if i > 65535 {
|
||||
return Err(PL011UartError::InvalidIntegerDivisor { ibrd: i });
|
||||
}
|
||||
// FIXME: can't happen due to calculation above
|
||||
if f > 63 {
|
||||
return Err(PL011UartError::InvalidFractionalDivisor { fbrd: f });
|
||||
}
|
||||
Ok(RateDivisors {
|
||||
integer_baud_rate_divisor: i,
|
||||
fractional_baud_rate_divisor: f,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub const UART0_START: usize = 0x20_1000;
|
||||
|
||||
impl Default for PL011Uart {
|
||||
fn default() -> Self {
|
||||
const UART0_BASE: usize = BcmHost::get_peripheral_address() + UART0_START;
|
||||
unsafe { PL011Uart::new(UART0_BASE) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PL011Uart {
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, duh!
|
||||
pub const unsafe fn new(base_addr: usize) -> PL011Uart {
|
||||
PL011Uart {
|
||||
registers: Registers::new(base_addr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set baud rate and characteristics (115200 8N1) and map to GPIO
|
||||
pub fn prepare(self, gpio: &gpio::GPIO) -> Result<PreparedPL011Uart> {
|
||||
// Turn off UART
|
||||
self.registers.Control.set(0);
|
||||
|
||||
// Wait for any ongoing transmissions to complete
|
||||
self.flush_internal();
|
||||
|
||||
// Flush TX FIFO
|
||||
self.registers.LineControl.modify(LCRH::Fifo::Disabled);
|
||||
|
||||
// set up clock for consistent divisor values
|
||||
const CLOCK: u32 = 4_000_000; // 4Mhz
|
||||
const BAUD_RATE: u32 = 115_200;
|
||||
|
||||
let mut mailbox = Mailbox::<9>::default();
|
||||
let index = mailbox.request();
|
||||
let index = mailbox.set_clock_rate(index, mailbox::clock::UART, CLOCK);
|
||||
let mailbox = mailbox.end(index);
|
||||
|
||||
if mailbox.call(mailbox::channel::PropertyTagsArmToVc).is_err() {
|
||||
return Err(PL011UartError::MailboxError); // Abort if UART clocks couldn't be set
|
||||
};
|
||||
|
||||
// Pin 14
|
||||
const UART_TXD: gpio::Function = gpio::Function::Alt0;
|
||||
// Pin 15
|
||||
const UART_RXD: gpio::Function = gpio::Function::Alt0;
|
||||
|
||||
// Map UART0 to GPIO pins and enable pull-ups
|
||||
gpio.get_pin(14)
|
||||
.into_alt(UART_TXD)
|
||||
.set_pull_up_down(gpio::PullUpDown::Up);
|
||||
gpio.get_pin(15)
|
||||
.into_alt(UART_RXD)
|
||||
.set_pull_up_down(gpio::PullUpDown::Up);
|
||||
|
||||
// Clear pending interrupts
|
||||
self.registers.InterruptClear.write(ICR::ALL::SET);
|
||||
|
||||
// From the PL011 Technical Reference Manual:
|
||||
//
|
||||
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
|
||||
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
|
||||
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
|
||||
//
|
||||
// Set the baud rate divisors, 8N1 and FIFO enabled.
|
||||
let divisors = RateDivisors::from_clock_and_rate(CLOCK.into(), BAUD_RATE)?;
|
||||
self.registers
|
||||
.IntegerBaudRate
|
||||
.write(IBRD::IBRD.val(divisors.integer_baud_rate_divisor & 0xffff));
|
||||
self.registers
|
||||
.FractionalBaudRate
|
||||
.write(FBRD::FBRD.val(divisors.fractional_baud_rate_divisor & 0b11_1111));
|
||||
self.registers.LineControl.write(
|
||||
LCRH::WordLength::EightBit
|
||||
+ LCRH::Fifo::Enabled
|
||||
+ LCRH::Parity::Disabled
|
||||
+ LCRH::Stop2::Disabled,
|
||||
);
|
||||
|
||||
// Mask all interrupts by setting corresponding bits to 1
|
||||
self.registers.InterruptMaskSetClear.write(IMSC::ALL::SET);
|
||||
|
||||
// Disable DMA
|
||||
self.registers
|
||||
.DmaControl
|
||||
.write(DMACR::RXDMAE::Disabled + DMACR::TXDMAE::Disabled);
|
||||
|
||||
// Turn on UART
|
||||
self.registers
|
||||
.Control
|
||||
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
|
||||
|
||||
Ok(PreparedPL011Uart(self))
|
||||
}
|
||||
|
||||
fn flush_internal(&self) {
|
||||
loop_while(|| self.registers.Flag.is_set(FR::BUSY));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PreparedPL011Uart {
|
||||
fn drop(&mut self) {
|
||||
self.0.registers.Control.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for PreparedPL011Uart {
|
||||
fn read_byte(&self) -> u8 {
|
||||
// wait until something is in the buffer
|
||||
loop_while(|| self.0.registers.Flag.is_set(FR::RXFE));
|
||||
|
||||
// read it and return
|
||||
self.0.registers.Data.get() as u8
|
||||
}
|
||||
|
||||
fn write_byte(&self, b: u8) {
|
||||
// wait until we can send
|
||||
loop_while(|| self.0.registers.Flag.is_set(FR::TXFF));
|
||||
|
||||
// write the character to the buffer
|
||||
self.0.registers.Data.set(b as u32);
|
||||
}
|
||||
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
/// line.
|
||||
fn flush(&self) {
|
||||
self.0.flush_internal();
|
||||
}
|
||||
|
||||
/// Consume input until RX FIFO is empty, aka all pending characters have been
|
||||
/// consumed.
|
||||
fn clear_rx(&self) {
|
||||
loop_while(|| {
|
||||
let pending = !self.0.registers.Flag.is_set(FR::RXFE);
|
||||
if pending {
|
||||
self.read_byte();
|
||||
}
|
||||
pending
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedPL011Uart {
|
||||
/// Send a character
|
||||
fn write_char(&self, c: char) {
|
||||
self.write_byte(c as u8)
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn write_string(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.write_char('\r')
|
||||
}
|
||||
|
||||
self.write_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn read_char(&self) -> char {
|
||||
let mut ret = self.read_byte() as char;
|
||||
|
||||
// convert carriage return to newline
|
||||
if ret == '\r' {
|
||||
ret = '\n'
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test_case]
|
||||
fn test_divisors() {
|
||||
const CLOCK: u64 = 3_000_000;
|
||||
const BAUD_RATE: u32 = 115_200;
|
||||
|
||||
let divisors = RateDivisors::from_clock_and_rate(CLOCK, BAUD_RATE);
|
||||
assert_eq!(divisors.integer_baud_rate_divisor, 1);
|
||||
assert_eq!(divisors.fractional_baud_rate_divisor, 40);
|
||||
}
|
||||
}
|
|
@ -11,20 +11,24 @@ use {
|
|||
mailbox::{channel, Mailbox, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::arch::loop_delay,
|
||||
core::ops,
|
||||
register::mmio::*,
|
||||
crate::platform::MMIODerefWrapper,
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
registers::ReadWrite,
|
||||
},
|
||||
};
|
||||
|
||||
const POWER_BASE: usize = BcmHost::get_peripheral_address() + 0x0010_001C;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
PM_RSTC: ReadWrite<u32>, // 0x1C
|
||||
PM_RSTS: ReadWrite<u32>, // 0x20
|
||||
PM_WDOG: ReadWrite<u32>, // 0x24
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
(0x00 => __reserved_1),
|
||||
(0x1c => PM_RSTC: ReadWrite<u32>),
|
||||
(0x20 => PM_RSTS: ReadWrite<u32>),
|
||||
(0x24 => PM_WDOG: ReadWrite<u32>),
|
||||
(0x28 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
const PM_PASSWORD: u32 = 0x5a00_0000;
|
||||
|
@ -47,34 +51,40 @@ pub enum PowerError {
|
|||
#[snafu(display("Power setup failed in mailbox operation"))]
|
||||
MailboxError,
|
||||
}
|
||||
|
||||
pub type Result<T> = ::core::result::Result<T, PowerError>;
|
||||
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
const POWER_START: usize = 0x0010_0000;
|
||||
|
||||
/// Public interface to the Power subsystem
|
||||
pub struct Power;
|
||||
pub struct Power {
|
||||
registers: Registers,
|
||||
}
|
||||
|
||||
impl ops::Deref for Power {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::ptr() }
|
||||
impl Default for Power {
|
||||
fn default() -> Power {
|
||||
const POWER_BASE: usize = BcmHost::get_peripheral_address() + POWER_START;
|
||||
unsafe { Power::new(POWER_BASE) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Power {
|
||||
pub fn new() -> Power {
|
||||
Power
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr() -> *const RegisterBlock {
|
||||
POWER_BASE as *const _
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, duh!
|
||||
pub const unsafe fn new(base_addr: usize) -> Power {
|
||||
Power {
|
||||
registers: Registers::new(base_addr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown the board
|
||||
pub fn off(&self, gpio: &gpio::GPIO) -> Result<()> {
|
||||
// power off devices one by one
|
||||
for dev_id in 0..16 {
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<8>::default();
|
||||
let index = mbox.request();
|
||||
let index =
|
||||
mbox.set_device_power(index, dev_id, POWER_STATE_OFF | POWER_STATE_DO_NOT_WAIT);
|
||||
|
@ -84,30 +94,14 @@ impl Power {
|
|||
.map_err(|_| PowerError::MailboxError)?;
|
||||
}
|
||||
|
||||
// power off gpio pins (but not VCC pins)
|
||||
for bank in 0..5 {
|
||||
gpio.FSEL[bank].set(0);
|
||||
}
|
||||
|
||||
gpio.PUD.set(0);
|
||||
|
||||
loop_delay(150);
|
||||
|
||||
gpio.PUDCLK[0].set(0xffff_ffff);
|
||||
gpio.PUDCLK[1].set(0xffff_ffff);
|
||||
|
||||
loop_delay(150);
|
||||
|
||||
// flush GPIO setup
|
||||
gpio.PUDCLK[0].set(0);
|
||||
gpio.PUDCLK[1].set(0);
|
||||
gpio.power_off();
|
||||
|
||||
// We set the watchdog hard reset bit here to distinguish this
|
||||
// reset from the normal (full) reset. bootcode.bin will not
|
||||
// reboot after a hard reset.
|
||||
let mut val = self.PM_RSTS.get();
|
||||
let mut val = self.registers.PM_RSTS.get();
|
||||
val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
|
||||
self.PM_RSTS.set(val);
|
||||
self.registers.PM_RSTS.set(val);
|
||||
|
||||
// Continue with normal reset mechanism
|
||||
self.reset();
|
||||
|
@ -116,11 +110,11 @@ impl Power {
|
|||
/// Reboot
|
||||
pub fn reset(&self) -> ! {
|
||||
// use a timeout of 10 ticks (~150us)
|
||||
self.PM_WDOG.set(PM_PASSWORD | 10);
|
||||
let mut val = self.PM_RSTC.get();
|
||||
self.registers.PM_WDOG.set(PM_PASSWORD | 10);
|
||||
let mut val = self.registers.PM_RSTC.get();
|
||||
val &= PM_RSTC_WRCFG_CLR;
|
||||
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
|
||||
self.PM_RSTC.set(val);
|
||||
self.registers.PM_RSTC.set(val);
|
||||
|
||||
crate::endless_sleep()
|
||||
}
|
|
@ -8,7 +8,7 @@ use {
|
|||
mailbox::{self, channel, response::VAL_LEN_FLAG, Mailbox, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::println,
|
||||
crate::{platform::rpi3::mailbox::MailboxStorageRef, println},
|
||||
core::convert::TryInto,
|
||||
snafu::Snafu,
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ impl VC {
|
|||
* (if the base or size has changed) is implicitly freed.
|
||||
*/
|
||||
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<36>::default();
|
||||
let index = mbox.request();
|
||||
let index = mbox.set_physical_wh(index, w, h);
|
||||
let index = mbox.set_virtual_wh(index, w, h);
|
||||
|
@ -67,7 +67,7 @@ impl VC {
|
|||
|
||||
// SetPixelOrder doesn't work in QEMU, however TestPixelOrder does.
|
||||
// Apparently, QEMU doesn't care about intermixing Get/Set and Test tags either.
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<36>::default();
|
||||
let index = mbox.request();
|
||||
#[cfg(qemu)]
|
||||
let index = mbox.test_pixel_order(index, 1);
|
|
@ -12,7 +12,6 @@ pub mod semihosting {
|
|||
qemu_exit_handle.exit_success()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn exit_failure() -> ! {
|
||||
use qemu_exit::QEMUExit;
|
||||
|
||||
|
@ -22,12 +21,11 @@ pub mod semihosting {
|
|||
qemu_exit_handle.exit_failure()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn sys_write0_call(text: &str) {
|
||||
// SAFETY: text must be \0-terminated!
|
||||
let cmd = 0x04;
|
||||
unsafe {
|
||||
asm!(
|
||||
core::arch::asm!(
|
||||
"hlt #0xF000"
|
||||
, in("w0") cmd
|
||||
, in("x1") text.as_ptr() as u64
|
|
@ -19,8 +19,8 @@ pub struct NullLock<T> {
|
|||
/// 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
|
||||
/// * <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> {
|
|
@ -5,14 +5,27 @@
|
|||
//============================================================================
|
||||
// Testing environment
|
||||
//============================================================================
|
||||
use crate::{println, qemu};
|
||||
use crate::{print, println, qemu};
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_runner(tests: &[&dyn Fn()]) {
|
||||
pub trait TestFn {
|
||||
fn run(&self);
|
||||
}
|
||||
|
||||
impl<T> TestFn for T
|
||||
where
|
||||
T: Fn(),
|
||||
{
|
||||
fn run(&self) {
|
||||
print!("*TEST* {}...\t", core::any::type_name::<T>());
|
||||
self();
|
||||
println!("[ok]\n");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_runner(tests: &[&dyn TestFn]) {
|
||||
println!("Running {} tests", tests.len());
|
||||
for test in tests {
|
||||
test();
|
||||
println!("\n[ok]\n");
|
||||
test.run();
|
||||
}
|
||||
println!("\n[success]\n");
|
||||
qemu::semihosting::exit_success();
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "vesper"
|
||||
name = "nucleus"
|
||||
version = "0.0.1"
|
||||
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||
description = "Vesper exokernel"
|
||||
description = "Vesper nanokernel binary"
|
||||
documentation = "https://docs.metta.systems/vesper"
|
||||
homepage = "https://github.com/metta-systems/vesper"
|
||||
repository = "https://github.com/metta-systems/vesper"
|
||||
|
@ -10,27 +10,30 @@ readme = "README.md"
|
|||
license = "BlueOak-1.0.0"
|
||||
categories = ["no-std", "embedded", "os"]
|
||||
publish = false
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[features]
|
||||
noserial = []
|
||||
noserial = ["machine/noserial"]
|
||||
# Enable JTAG debugging of kernel - enable jtag helpers and
|
||||
# block waiting for JTAG probe attach at the start of kernel main.
|
||||
jtag = []
|
||||
jtag = ["machine/jtag"]
|
||||
# Build for running under QEMU with semihosting, so various halt/reboot options would for example quit QEMU instead.
|
||||
qemu = ["qemu-exit"]
|
||||
qemu = ["machine/qemu"]
|
||||
# Mutually exclusive features to choose a target board
|
||||
rpi3 = ["machine/rpi3"]
|
||||
rpi4 = ["machine/rpi4"]
|
||||
|
||||
[dependencies]
|
||||
machine = { path = "../machine" }
|
||||
r0 = "1.0"
|
||||
qemu-exit = { version = "1.0", optional = true }
|
||||
cortex-a = "5.1"
|
||||
register = "1.0"
|
||||
ux = { version = "0.1.3", default-features = false }
|
||||
usize_conversions = "0.2.0"
|
||||
bit_field = "0.10.0"
|
||||
bitflags = "1.2.1"
|
||||
cortex-a = "7.0"
|
||||
tock-registers = "0.7"
|
||||
ux = { version = "0.1", default-features = false }
|
||||
usize_conversions = "0.2"
|
||||
bit_field = "0.10"
|
||||
bitflags = "1.3"
|
||||
cfg-if = "1.0"
|
||||
snafu = { version = "0.6", default-features = false }
|
||||
snafu = { version = "0.7", default-features = false }
|
||||
|
|
|
@ -3,82 +3,42 @@
|
|||
#
|
||||
# Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
#
|
||||
[tasks.build]
|
||||
env = { "TARGET_FEATURES" = "" }
|
||||
args = ["build", "--target=${TARGET_JSON}", "--release", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
[tasks.expand]
|
||||
env = { "TARGET_FEATURES" = "" }
|
||||
args = ["expand", "--target=${TARGET_JSON}", "--release", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
[tasks.test]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
args = ["test", "--target=${TARGET_JSON}", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
# These tasks are written in cargo-make's own script to make it portable across platforms (no `basename` on Windows)
|
||||
[tasks.kernel-binary]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/release/vesper ${KERNEL_ELF}
|
||||
exec --fail-on-error ${OBJCOPY} %{OBJCOPY_PARAMS} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/release/vesper ${KERNEL_BIN}
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.custom-binary]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
binaryFile = basename ${CARGO_MAKE_TASK_ARGS}
|
||||
cp ${CARGO_MAKE_TASK_ARGS} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.elf
|
||||
exec --fail-on-error ${OBJCOPY} %{OBJCOPY_PARAMS} ${CARGO_MAKE_TASK_ARGS} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.bin
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.test-runner]
|
||||
dependencies = ["custom-binary"]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
binaryFile = basename ${CARGO_MAKE_TASK_ARGS}
|
||||
exec --fail-on-error ${QEMU} %{QEMU_OPTS} %{QEMU_TESTS_OPTS} -dtb ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb -kernel ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${binaryFile}.bin
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.build-qemu]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
command = "cargo"
|
||||
args = ["build", "--target=${TARGET_JSON}", "--release", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
[tasks.qemu-runner]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
script = [
|
||||
"${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb -kernel ${KERNEL_BIN}"
|
||||
]
|
||||
dependencies = ["build-qemu", "kernel-binary"]
|
||||
env = { "BINARY_FILE" = "${KERNEL_ELF}" }
|
||||
run_task = "custom-binary"
|
||||
|
||||
[tasks.qemu]
|
||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||
extend = "qemu-runner"
|
||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}" }
|
||||
|
||||
[tasks.qemu-cb]
|
||||
disabled = true
|
||||
|
||||
[tasks.qemu-gdb]
|
||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS} ${QEMU_GDB_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||
extend = "qemu-runner"
|
||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS} ${QEMU_GDB_OPTS}" }
|
||||
|
||||
[tasks.openocd]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
script = [
|
||||
"${OPENOCD} -f interface/jlink.cfg -f ../doc/rpi2rpi_jtag/rpi3_target.cfg"
|
||||
]
|
||||
[tasks.zellij-nucleus]
|
||||
env = { "KERNEL_BIN" = "${KERNEL_BIN}" }
|
||||
run_task = "zellij-config"
|
||||
|
||||
[tasks.zellij-cb]
|
||||
disabled = true
|
||||
|
||||
[tasks.zellij-cb-gdb]
|
||||
disabled = true
|
||||
|
||||
[tasks.gdb-config]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
writefile ${GDB_CONNECT_FILE} "target remote :5555\n"
|
||||
writefile ${GDB_CONNECT_FILE} "target extended-remote :5555\n"
|
||||
appendfile ${GDB_CONNECT_FILE} "break 0x80000\n"
|
||||
appendfile ${GDB_CONNECT_FILE} "continue\n"
|
||||
'''
|
||||
]
|
||||
|
||||
# Problem: this eats STDIN! need to run in subshell or sth
|
||||
[tasks.gdb]
|
||||
dependencies = ["build", "kernel-binary", "gdb-config"]
|
||||
env = { "RUST_GDB" = "${GDB}" }
|
||||
|
@ -86,33 +46,34 @@ script = [
|
|||
"rust-gdb -x ${GDB_CONNECT_FILE} ${KERNEL_ELF}"
|
||||
]
|
||||
|
||||
[tasks.gdb-cb]
|
||||
disabled = true
|
||||
|
||||
[tasks.install-nm]
|
||||
install_crate = { crate_name = "cargo-binutils", binary = "rust-nm", test_arg = ["--help"] }
|
||||
|
||||
[tasks.install-rustfilt]
|
||||
install_crate = { crate_name = "rustfilt", binary = "rustfilt", test_arg = ["--help"] }
|
||||
|
||||
[tasks.nm]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
dependencies = ["build", "kernel-binary", "install-nm", "install-rustfilt"]
|
||||
script = [
|
||||
"${NM} -- ${KERNEL_ELF} | sort"
|
||||
"${NM} -- ${KERNEL_ELF} | sort -k 1 | rustfilt"
|
||||
]
|
||||
#install_crate = "cargo-binutils"
|
||||
|
||||
[tasks.sdcard]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
script_runner = "@duckscript"
|
||||
script = [
|
||||
'''
|
||||
kernelImage = basename ${KERNEL_BIN}
|
||||
kernelImage = set "kernel8.img"
|
||||
cp ${KERNEL_BIN} ${VOLUME}/${kernelImage}
|
||||
echo "Copied nucleus to ${VOLUME}/${kernelImage}"
|
||||
'''
|
||||
]
|
||||
|
||||
[tasks.sdeject]
|
||||
dependencies = ["sdcard"]
|
||||
script = [
|
||||
"diskutil unmount ${VOLUME}"
|
||||
]
|
||||
|
||||
[tasks.clippy]
|
||||
env = { "TARGET_FEATURES" = { value = "--features=${CLIPPY_FEATURES}", condition = { env_set = ["CLIPPY_FEATURES"] } } }
|
||||
command = "cargo"
|
||||
args = ["clippy", "--target=${TARGET_JSON}", "@@remove-empty(TARGET_FEATURES)", "--", "-D", "warnings"]
|
||||
[tasks.cb-eject]
|
||||
disabled = true
|
||||
|
||||
[tasks.hopper]
|
||||
dependencies = ["build", "kernel-binary"]
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
const LINKER_SCRIPT: &str = "linker/aarch64.ld";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed={}", LINKER_SCRIPT);
|
||||
println!("cargo:rustc-link-arg=--script={}", LINKER_SCRIPT);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
*/
|
||||
pub mod console;
|
||||
|
||||
pub use console::{Console, ConsoleOps};
|
|
@ -3,76 +3,44 @@
|
|||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
//! Vesper single-address-space exokernel.
|
||||
//! Vesper single-address-space nanokernel.
|
||||
//!
|
||||
//! This crate implements the kernel binary proper.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(global_asm)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(nonnull_slice_from_raw_parts)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::tests::test_runner)]
|
||||
#![test_runner(machine::tests::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
use architecture_not_supported_sorry;
|
||||
|
||||
/// Architecture-specific code.
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
pub use arch::*;
|
||||
mod devices;
|
||||
mod macros;
|
||||
mod mm;
|
||||
mod panic;
|
||||
mod platform;
|
||||
#[cfg(feature = "qemu")]
|
||||
mod qemu;
|
||||
mod sync;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod write_to;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use core::panic::PanicInfo;
|
||||
#[allow(unused_imports)]
|
||||
use machine::devices::SerialOps;
|
||||
use {
|
||||
crate::platform::rpi3::{
|
||||
display::{Color, DrawError},
|
||||
mailbox::{channel, Mailbox, MailboxOps},
|
||||
vc::VC,
|
||||
},
|
||||
cfg_if::cfg_if,
|
||||
machine::{
|
||||
arch, entry, memory,
|
||||
platform::rpi3::{
|
||||
display::{Color, DrawError},
|
||||
mailbox::{channel, Mailbox, MailboxOps},
|
||||
vc::VC,
|
||||
},
|
||||
println, CONSOLE,
|
||||
},
|
||||
};
|
||||
|
||||
entry!(kmain);
|
||||
|
||||
/// The global console. Output of the kernel print! and println! macros goes here.
|
||||
static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::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<mm::BumpAllocator> =
|
||||
sync::NullLock::new(mm::BumpAllocator::new(
|
||||
// @todo Init this after we loaded boot memory map
|
||||
memory::map::virt::DMA_HEAP_START as usize,
|
||||
memory::map::virt::DMA_HEAP_END as usize,
|
||||
"Global DMA Allocator",
|
||||
// Try the following arguments instead to see all mailbox operations
|
||||
// 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",
|
||||
));
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panicked(info: &PanicInfo) -> ! {
|
||||
machine::panic::handler(info)
|
||||
}
|
||||
|
||||
fn print_mmu_state_and_features() {
|
||||
memory::mmu::print_features();
|
||||
|
@ -102,7 +70,7 @@ fn init_exception_traps() {
|
|||
|
||||
#[cfg(not(feature = "noserial"))]
|
||||
fn init_uart_serial() {
|
||||
use crate::platform::rpi3::{gpio::GPIO, mini_uart::MiniUart, pl011_uart::PL011Uart};
|
||||
use machine::platform::rpi3::{gpio::GPIO, mini_uart::MiniUart, pl011_uart::PL011Uart};
|
||||
|
||||
let gpio = GPIO::default();
|
||||
let uart = MiniUart::default();
|
||||
|
@ -117,7 +85,6 @@ fn init_uart_serial() {
|
|||
// Then immediately switch to PL011 (just as an example)
|
||||
|
||||
let uart = PL011Uart::default();
|
||||
let mbox = Mailbox::default();
|
||||
|
||||
// uart.init() will reconfigure the GPIO, which causes a race against
|
||||
// the MiniUart that is still putting out characters on the physical
|
||||
|
@ -130,10 +97,9 @@ fn init_uart_serial() {
|
|||
// 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.
|
||||
use crate::devices::console::ConsoleOps;
|
||||
CONSOLE.lock(|c| c.flush());
|
||||
|
||||
match uart.prepare(mbox, &gpio) {
|
||||
match uart.prepare(&gpio) {
|
||||
Ok(uart) => {
|
||||
CONSOLE.lock(|c| {
|
||||
// Move uart into the global CONSOLE.
|
||||
|
@ -150,7 +116,7 @@ fn init_uart_serial() {
|
|||
#[inline]
|
||||
pub fn kmain() -> ! {
|
||||
#[cfg(feature = "jtag")]
|
||||
jtag::wait_debugger();
|
||||
machine::arch::jtag::wait_debugger();
|
||||
|
||||
init_mmu();
|
||||
init_exception_traps();
|
||||
|
@ -180,7 +146,7 @@ fn command_prompt() {
|
|||
b"uart" => init_uart_serial(),
|
||||
b"disp" => check_display_init(),
|
||||
b"trap" => check_data_abort_trap(),
|
||||
b"map" => arch::memory::print_layout(),
|
||||
b"map" => machine::arch::memory::print_layout(),
|
||||
b"led on" => set_led(true),
|
||||
b"led off" => set_led(false),
|
||||
b"help" => print_help(),
|
||||
|
@ -204,7 +170,7 @@ fn print_help() {
|
|||
}
|
||||
|
||||
fn set_led(enable: bool) {
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<8>::default();
|
||||
let index = mbox.request();
|
||||
let index = mbox.set_led_on(index, enable);
|
||||
let mbox = mbox.end(index);
|
||||
|
@ -221,12 +187,12 @@ fn reboot() -> ! {
|
|||
cfg_if! {
|
||||
if #[cfg(feature = "qemu")] {
|
||||
println!("Bye, shutting down QEMU");
|
||||
qemu::semihosting::exit_success()
|
||||
machine::qemu::semihosting::exit_success()
|
||||
} else {
|
||||
use crate::platform::rpi3::power::Power;
|
||||
use machine::platform::rpi3::power::Power;
|
||||
|
||||
println!("Bye, going to reset now");
|
||||
Power::new().reset()
|
||||
Power::default().reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +216,7 @@ fn display_graphics() -> Result<(), DrawError> {
|
|||
display.draw_text(50, 50, "Hello there!", Color::rgb(128, 192, 255))?;
|
||||
|
||||
let mut buf = [0u8; 64];
|
||||
let s = write_to::show(&mut buf, format_args!("Display width {}", display.width));
|
||||
let s = machine::write_to::show(&mut buf, format_args!("Display width {}", display.width));
|
||||
|
||||
if s.is_err() {
|
||||
display.draw_text(50, 150, "Error displaying", Color::red())?
|
||||
|
@ -279,7 +245,12 @@ fn check_data_abort_trap() {
|
|||
|
||||
#[cfg(test)]
|
||||
mod main_tests {
|
||||
use super::*;
|
||||
use {super::*, core::panic::PanicInfo};
|
||||
|
||||
#[panic_handler]
|
||||
fn panicked(info: &PanicInfo) -> ! {
|
||||
machine::panic::handler_for_tests(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_data_abort_trap() {
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
use {
|
||||
super::{
|
||||
mailbox::{channel, read, write, MailboxOps, RegisterBlock, Result},
|
||||
BcmHost,
|
||||
},
|
||||
core::ops::Deref,
|
||||
};
|
||||
|
||||
/// FrameBuffer channel supported structure - use with mailbox::channel::FrameBuffer
|
||||
/// Must have the same alignment as the mailbox buffers.
|
||||
#[repr(C)]
|
||||
#[repr(align(16))]
|
||||
pub struct FrameBuffer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub vwidth: u32,
|
||||
pub vheight: u32,
|
||||
pub pitch: u32,
|
||||
pub depth: u32,
|
||||
pub x_offset: u32,
|
||||
pub y_offset: u32,
|
||||
pub pointer: u32,
|
||||
pub size: u32,
|
||||
// Must be after HW-dictated fields to not break structure alignment.
|
||||
base_addr: usize,
|
||||
}
|
||||
|
||||
// @todo rewrite in terms of using the Mailbox
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.STATUS.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*FrameBuffer::ptr()).STATUS.read() }
|
||||
/// ```
|
||||
impl Deref for FrameBuffer {
|
||||
type Target = RegisterBlock; // mailbox RegisterBlock reused here
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for FrameBuffer {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"\n\n\n#### FrameBuffer({}x{}, {}x{}, d{}, --{}--, +{}x{}, {}@{:x})\n\n\n",
|
||||
self.width,
|
||||
self.height,
|
||||
self.vwidth,
|
||||
self.vheight,
|
||||
self.depth,
|
||||
self.pitch,
|
||||
self.x_offset,
|
||||
self.y_offset,
|
||||
self.size,
|
||||
self.pointer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameBuffer {
|
||||
pub fn new(reg_base: usize, width: u32, height: u32, depth: u32) -> FrameBuffer {
|
||||
FrameBuffer {
|
||||
width,
|
||||
height,
|
||||
vwidth: width,
|
||||
vheight: height,
|
||||
pitch: 0,
|
||||
depth,
|
||||
x_offset: 0,
|
||||
y_offset: 0,
|
||||
pointer: 0, // could be 4096 for the alignment?
|
||||
size: 0,
|
||||
|
||||
base_addr: reg_base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxOps for FrameBuffer {
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
}
|
||||
|
||||
/// https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes says:
|
||||
/// **With the exception of the property tags mailbox channel,**
|
||||
/// when passing memory addresses as the data part of a mailbox message,
|
||||
/// the addresses should be **bus addresses as seen from the VC.**
|
||||
fn write(&self, _channel: u32) -> Result<()> {
|
||||
write(
|
||||
self,
|
||||
BcmHost::phys2bus(&self as *const _ as usize) as *const _,
|
||||
channel::FrameBuffer,
|
||||
)
|
||||
}
|
||||
|
||||
fn read(&self, _channel: u32) -> Result<()> {
|
||||
read(self, 0, channel::FrameBuffer)
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub mod display;
|
||||
pub mod fb;
|
||||
pub mod gpio;
|
||||
pub mod mailbox;
|
||||
pub mod mini_uart;
|
||||
pub mod pl011_uart;
|
||||
pub mod power;
|
||||
pub mod vc;
|
||||
|
||||
/// See BCM2835-ARM-Peripherals.pdf
|
||||
/// See https://www.raspberrypi.org/forums/viewtopic.php?t=186090 for more details.
|
||||
|
||||
pub struct BcmHost;
|
||||
|
||||
impl BcmHost {
|
||||
/// This returns the ARM-side physical address where peripherals are mapped.
|
||||
///
|
||||
/// As per https://www.raspberrypi.org/documentation/hardware/raspberrypi/peripheral_addresses.md
|
||||
/// BCM SOC could address only 1Gb of memory, so 0x4000_0000 is the high watermark.
|
||||
pub const fn get_peripheral_address() -> usize {
|
||||
0x3f00_0000
|
||||
}
|
||||
|
||||
/// This returns the size of the peripherals' space.
|
||||
pub const fn get_peripheral_size() -> usize {
|
||||
0x0100_0000
|
||||
}
|
||||
|
||||
/// This returns the bus address of the SDRAM.
|
||||
pub const fn get_sdram_address() -> usize {
|
||||
0xc000_0000 // uncached
|
||||
}
|
||||
|
||||
/// As per https://www.raspberrypi.org/forums/viewtopic.php?p=1170522#p1170522
|
||||
///
|
||||
pub fn bus2phys(bus: usize) -> usize {
|
||||
bus & !0xc000_0000
|
||||
}
|
||||
|
||||
pub fn phys2bus(phys: usize) -> usize {
|
||||
phys | 0xc000_0000
|
||||
}
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
|
||||
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
|
||||
*
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ddi0183g/DDI0183G_uart_pl011_r1p5_trm.pdf
|
||||
* https://docs.rs/embedded-serial/0.5.0/embedded_serial/
|
||||
*/
|
||||
|
||||
use {
|
||||
super::{
|
||||
gpio,
|
||||
mailbox::{self, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::{arch::loop_until, devices::ConsoleOps},
|
||||
core::ops,
|
||||
register::{mmio::*, register_bitfields},
|
||||
snafu::Snafu,
|
||||
};
|
||||
|
||||
// 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 (UART0_RSRECR=0x04)
|
||||
FR: ReadOnly<u32, FR::Register>, // 0x18
|
||||
__reserved_1: [u32; 1], // 0x1c
|
||||
ILPR: u32, // 0x20
|
||||
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
|
||||
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
|
||||
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
|
||||
CR: WriteOnly<u32, CR::Register>, // 0x30
|
||||
IFLS: u32, // 0x34
|
||||
IMSC: u32, // 0x38
|
||||
RIS: u32, // 0x3C
|
||||
MIS: u32, // 0x40
|
||||
ICR: WriteOnly<u32, ICR::Register>, // 0x44
|
||||
DMACR: u32, // 0x48
|
||||
__reserved_2: [u32; 14], // 0x4c-0x7c
|
||||
ITCR: u32, // 0x80
|
||||
ITIP: u32, // 0x84
|
||||
ITOP: u32, // 0x88
|
||||
TDR: u32, // 0x8C
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum PL011UartError {
|
||||
#[snafu(display("PL011 UART setup failed in mailbox operation"))]
|
||||
MailboxError,
|
||||
}
|
||||
pub type Result<T> = ::core::result::Result<T, PL011UartError>;
|
||||
|
||||
pub struct PL011Uart {
|
||||
base_addr: usize,
|
||||
}
|
||||
|
||||
pub struct PreparedPL011Uart(PL011Uart);
|
||||
|
||||
/// Divisor values for common baud rates
|
||||
pub enum Rate {
|
||||
Baud115200 = 2,
|
||||
}
|
||||
|
||||
impl From<Rate> for u32 {
|
||||
fn from(r: Rate) -> Self {
|
||||
r as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for PL011Uart {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for PreparedPL011Uart {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PL011Uart {
|
||||
fn default() -> Self {
|
||||
const UART0_BASE: usize = BcmHost::get_peripheral_address() + 0x20_1000;
|
||||
PL011Uart::new(UART0_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl PL011Uart {
|
||||
pub fn new(base_addr: usize) -> 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 prepare(
|
||||
self,
|
||||
mut mbox: mailbox::Mailbox,
|
||||
gpio: &gpio::GPIO,
|
||||
) -> Result<PreparedPL011Uart> {
|
||||
// turn off UART0
|
||||
self.CR.set(0);
|
||||
|
||||
// set up clock for consistent divisor values
|
||||
let index = mbox.request();
|
||||
let index = mbox.set_clock_rate(index, mailbox::clock::UART, 4_000_000 /* 4Mhz */);
|
||||
let mbox = mbox.end(index);
|
||||
|
||||
if mbox.call(mailbox::channel::PropertyTagsArmToVc).is_err() {
|
||||
return Err(PL011UartError::MailboxError); // Abort if UART clocks couldn't be set
|
||||
};
|
||||
|
||||
// Pin 14
|
||||
const UART_TXD: gpio::Function = gpio::Function::Alt0;
|
||||
// Pin 15
|
||||
const UART_RXD: gpio::Function = gpio::Function::Alt0;
|
||||
|
||||
// map UART0 to GPIO pins
|
||||
gpio.get_pin(14).into_alt(UART_TXD);
|
||||
gpio.get_pin(15).into_alt(UART_RXD);
|
||||
|
||||
gpio::enable_uart_pins(gpio);
|
||||
|
||||
self.ICR.write(ICR::ALL::CLEAR);
|
||||
// @todo Configure divisors more sanely
|
||||
self.IBRD.write(IBRD::IBRD.val(Rate::Baud115200.into()));
|
||||
self.FBRD.write(FBRD::FBRD.val(0xB)); // Results in 115200 baud
|
||||
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
|
||||
|
||||
self.CR
|
||||
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
|
||||
|
||||
Ok(PreparedPL011Uart(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PreparedPL011Uart {
|
||||
fn drop(&mut self) {
|
||||
self.CR
|
||||
.write(CR::UARTEN::Disabled + CR::TXE::Disabled + CR::RXE::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedPL011Uart {
|
||||
/// Send a character
|
||||
fn putc(&self, c: char) {
|
||||
// wait until we can send
|
||||
loop_until(|| !self.FR.is_set(FR::TXFF));
|
||||
|
||||
// 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 carriage 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_until(|| !self.FR.is_set(FR::RXFE));
|
||||
|
||||
// read it and return
|
||||
let mut ret = self.DR.get() as u8 as char;
|
||||
|
||||
// convert carriage return to newline
|
||||
if ret == '\r' {
|
||||
ret = '\n'
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
|
@ -1,32 +1,34 @@
|
|||
# Broadcom 2837 on Raspberry Pi 3 as JTAG target
|
||||
# Broadcom bcm2837 on Raspberry Pi 3 as JTAG target
|
||||
# From https://www.suse.com/c/debugging-raspberry-pi-3-with-jtag/
|
||||
|
||||
telnet_port 4444
|
||||
gdb_port 5555
|
||||
|
||||
transport select jtag
|
||||
|
||||
# we need to enable srst even though we don't connect it
|
||||
reset_config trst_and_srst
|
||||
|
||||
adapter_khz 4000
|
||||
jtag_ntrst_delay 500
|
||||
echo "Booting JTAG for Raspberry Pi 3"
|
||||
|
||||
if { [info exists CHIPNAME] } {
|
||||
set _CHIPNAME $CHIPNAME
|
||||
} else {
|
||||
set _CHIPNAME rpi3
|
||||
set _CHIPNAME bcm2837
|
||||
}
|
||||
|
||||
#
|
||||
# Main DAP
|
||||
#
|
||||
if { [info exists DAP_TAPID] } {
|
||||
set _DAP_TAPID $DAP_TAPID
|
||||
} else {
|
||||
set _DAP_TAPID 0x4ba00477
|
||||
}
|
||||
|
||||
adapter speed 4000
|
||||
|
||||
transport select jtag
|
||||
# we need to enable srst even though we don't connect it
|
||||
reset_config trst_and_srst
|
||||
|
||||
jtag_ntrst_delay 500
|
||||
|
||||
telnet_port 4444
|
||||
gdb_port 5555
|
||||
|
||||
#
|
||||
# Main DAP
|
||||
#
|
||||
jtag newtap $_CHIPNAME tap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -enable
|
||||
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.tap
|
||||
|
||||
|
@ -35,17 +37,28 @@ set _CTINAME $_CHIPNAME.cti
|
|||
|
||||
set DBGBASE {0x80010000 0x80012000 0x80014000 0x80016000}
|
||||
set CTIBASE {0x80018000 0x80019000 0x8001a000 0x8001b000}
|
||||
|
||||
set _cores 4
|
||||
set _smp_command ""
|
||||
|
||||
for { set _core 0 } { $_core < $_cores } { incr _core } {
|
||||
|
||||
cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 0 \
|
||||
-ctibase [lindex $CTIBASE $_core]
|
||||
-baseaddr [lindex $CTIBASE $_core]
|
||||
|
||||
target create $_TARGETNAME.$_core aarch64 \
|
||||
-dap $_CHIPNAME.dap -coreid $_core \
|
||||
-dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core
|
||||
|
||||
if {$_core != 0} {
|
||||
set _smp_command "$_smp_command ${_TARGETNAME}.${_core}"
|
||||
} else {
|
||||
set _smp_command "target smp ${_TARGETNAME}.${_core}"
|
||||
}
|
||||
|
||||
$_TARGETNAME.$_core configure -event reset-assert-post "aarch64 dbginit"
|
||||
$_TARGETNAME.$_core configure -event gdb-attach { halt }
|
||||
}
|
||||
|
||||
eval $_smp_command
|
||||
targets $_TARGETNAME.0
|
|
@ -0,0 +1,64 @@
|
|||
# Broadcom bcm2711 on Raspberry Pi 4 as JTAG target
|
||||
# From https://gist.github.com/tnishinaga/46a3380e1f47f5e892bbb74e55b3cf3e
|
||||
# See also https://xihan94.gitbook.io/raspberry-pi/raspberry-pi-4-bringup
|
||||
|
||||
echo "Booting JTAG for Raspberry Pi 4"
|
||||
|
||||
if { [info exists CHIPNAME] } {
|
||||
set _CHIPNAME $CHIPNAME
|
||||
} else {
|
||||
set _CHIPNAME bcm2711
|
||||
}
|
||||
|
||||
if { [info exists DAP_TAPID] } {
|
||||
set _DAP_TAPID $DAP_TAPID
|
||||
} else {
|
||||
set _DAP_TAPID 0x4ba00477
|
||||
}
|
||||
|
||||
adapter speed 4000
|
||||
|
||||
transport select jtag
|
||||
# we need to enable srst even though we don't connect it
|
||||
reset_config trst_and_srst
|
||||
|
||||
jtag_ntrst_delay 500
|
||||
|
||||
telnet_port 4444
|
||||
gdb_port 5555
|
||||
|
||||
#
|
||||
# Main DAP
|
||||
#
|
||||
jtag newtap $_CHIPNAME tap -irlen 4 -expected-id $_DAP_TAPID
|
||||
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.tap
|
||||
|
||||
set _TARGETNAME $_CHIPNAME.a72
|
||||
set _CTINAME $_CHIPNAME.cti
|
||||
|
||||
set DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}
|
||||
set CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}
|
||||
|
||||
set _cores 4
|
||||
set _smp_command ""
|
||||
|
||||
for { set _core 0 } { $_core < $_cores } { incr _core } {
|
||||
cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 0 \
|
||||
-baseaddr [lindex $CTIBASE $_core]
|
||||
|
||||
target create ${_TARGETNAME}.${_core} aarch64 \
|
||||
-dap ${_CHIPNAME}.dap -coreid $_core \
|
||||
-dbgbase [lindex $DBGBASE $_core] -cti ${_CTINAME}.${_core}
|
||||
|
||||
if {$_core != 0} {
|
||||
set _smp_command "$_smp_command ${_TARGETNAME}.${_core}"
|
||||
} else {
|
||||
set _smp_command "target smp ${_TARGETNAME}.${_core}"
|
||||
}
|
||||
|
||||
$_TARGETNAME.$_core configure -event reset-assert-post "aarch64 dbginit"
|
||||
$_TARGETNAME.$_core configure -event gdb-attach { halt }
|
||||
}
|
||||
|
||||
eval $_smp_command
|
||||
targets $_TARGETNAME.0
|
|
@ -1 +1,4 @@
|
|||
nightly
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
components = ["clippy", "llvm-tools-preview", "rust-src", "rustfmt"]
|
||||
targets = ["aarch64-unknown-none-softfloat"]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
imports_granularity = "One"
|
|
@ -11,7 +11,6 @@
|
|||
"linker": "rust-lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"--script=linker/aarch64.ld",
|
||||
"--print-gc-sections"
|
||||
]
|
||||
},
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue