From 2bb13e5098d71a68b0ed0556baa16cbe93c06b2b Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Fri, 16 May 2025 14:34:45 +0100 Subject: [PATCH] Use aarch64-rt in bare metal AP section and exercise (#2751) Also use `global_asm!` rather than `cc::Build` to build assembly files. This avoids a dependency and some build scripts. --- src/SUMMARY.md | 1 + src/bare-metal/aps/aarch64-rt.md | 15 ++ src/bare-metal/aps/entry-point.md | 4 +- src/bare-metal/aps/examples/Cargo.lock | 110 +++++++++-- src/bare-metal/aps/examples/Cargo.toml | 10 +- src/bare-metal/aps/examples/build.rs | 27 --- .../aps/examples/src/asm.rs} | 18 +- src/bare-metal/aps/examples/{ => src}/entry.S | 0 .../aps/examples/{ => src}/exceptions.S | 0 src/bare-metal/aps/examples/{ => src}/idmap.S | 0 .../aps/examples/src/main_minimal.rs | 1 + src/bare-metal/aps/examples/src/main_rt.rs | 77 ++++++++ src/exercises/bare-metal/rtc.md | 45 ----- src/exercises/bare-metal/rtc/Cargo.lock | 85 +++++++-- src/exercises/bare-metal/rtc/Cargo.toml | 8 +- src/exercises/bare-metal/rtc/entry.S | 142 -------------- src/exercises/bare-metal/rtc/exceptions.S | 178 ------------------ src/exercises/bare-metal/rtc/idmap.S | 42 ----- src/exercises/bare-metal/rtc/src/logger.rs | 9 +- src/exercises/bare-metal/rtc/src/main.rs | 41 +++- src/exercises/bare-metal/rtc/src/pl011.rs | 168 ----------------- 21 files changed, 319 insertions(+), 662 deletions(-) create mode 100644 src/bare-metal/aps/aarch64-rt.md delete mode 100644 src/bare-metal/aps/examples/build.rs rename src/{exercises/bare-metal/rtc/build.rs => bare-metal/aps/examples/src/asm.rs} (66%) rename src/bare-metal/aps/examples/{ => src}/entry.S (100%) rename src/bare-metal/aps/examples/{ => src}/exceptions.S (100%) rename src/bare-metal/aps/examples/{ => src}/idmap.S (100%) create mode 100644 src/bare-metal/aps/examples/src/main_rt.rs delete mode 100644 src/exercises/bare-metal/rtc/entry.S delete mode 100644 src/exercises/bare-metal/rtc/exceptions.S delete mode 100644 src/exercises/bare-metal/rtc/idmap.S delete mode 100644 src/exercises/bare-metal/rtc/src/pl011.rs diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ef9f0cc5..3d0aeea4 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -361,6 +361,7 @@ - [Logging](bare-metal/aps/logging.md) - [Using It](bare-metal/aps/logging/using.md) - [Exceptions](bare-metal/aps/exceptions.md) + - [aarch64-rt](bare-metal/aps/aarch64-rt.md) - [Other Projects](bare-metal/aps/other-projects.md) - [Useful Crates](bare-metal/useful-crates.md) - [`zerocopy`](bare-metal/useful-crates/zerocopy.md) diff --git a/src/bare-metal/aps/aarch64-rt.md b/src/bare-metal/aps/aarch64-rt.md new file mode 100644 index 00000000..80963e06 --- /dev/null +++ b/src/bare-metal/aps/aarch64-rt.md @@ -0,0 +1,15 @@ +# aarch64-rt + +The `aarch64-rt` crate provides the assembly entry point and exception vector +that we implemented before. We just need to mark our main function with the +`entry!` macro. + +It also provides the `initial_pagetable!` macro to let us define an initial +static pagetable in Rust, rather than in assembly code like we did before. + +We can also use the UART driver from the `arm-pl011-uart` crate rather than +writing our own. + +```rust,editable,compile_fail +{{#include examples/src/main_rt.rs:main}} +``` diff --git a/src/bare-metal/aps/entry-point.md b/src/bare-metal/aps/entry-point.md index ab5f4b4c..1f90a62c 100644 --- a/src/bare-metal/aps/entry-point.md +++ b/src/bare-metal/aps/entry-point.md @@ -3,12 +3,12 @@ Before we can start running Rust code, we need to do some initialisation. ```armasm -{{#include examples/entry.S:entry}} +{{#include examples/src/entry.S:entry}} ```
-This code is in `src/bare-metal/aps/examples/entry.S`. It's not necessary to +This code is in `src/bare-metal/aps/examples/src/entry.S`. It's not necessary to understand this in detail -- the takeaway is that typically some low-level setup is needed to meet Rust's expectations of the system. diff --git a/src/bare-metal/aps/examples/Cargo.lock b/src/bare-metal/aps/examples/Cargo.lock index ff71fe2f..0ff3435f 100644 --- a/src/bare-metal/aps/examples/Cargo.lock +++ b/src/bare-metal/aps/examples/Cargo.lock @@ -2,17 +2,53 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aarch64-paging" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3b8f725e9256b2fac2d25e013e22a6a391b8c07e23c4c5eac6e037a78a28801" +dependencies = [ + "bitflags", + "thiserror", +] + +[[package]] +name = "aarch64-rt" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be4edaff3b5ead5fb7e9ef0d72fa1e93b4052ec2410e0ebc93f6f360c9599ea" +dependencies = [ + "cfg-if", + "smccc", +] + [[package]] name = "ap-examples" version = "0.1.0" dependencies = [ + "aarch64-paging", + "aarch64-rt", + "arm-pl011-uart", "bitflags", - "cc", "log", "smccc", "spin", ] +[[package]] +name = "arm-pl011-uart" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff5b0f1e39ec186e409c6fd80bbb83aa00622ca71c9c0561b5571df3b5f5391f" +dependencies = [ + "bitflags", + "embedded-hal-nb", + "embedded-io", + "safe-mmio", + "thiserror", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -26,14 +62,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] -name = "cc" -version = "1.2.20" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" dependencies = [ - "shlex", + "embedded-hal", + "nb", ] +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "lock_api" version = "0.4.10" @@ -50,6 +105,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "proc-macro2" version = "1.0.94" @@ -68,23 +129,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "safe-mmio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6" +dependencies = [ + "zerocopy", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "smccc" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d28c99a9913f0686f470a6ff020e160195eb1a86f9c1800aab2b3abba4bc9b3" +checksum = "87ed898c59fbfd740492e435cba30a08b74a68d1f1ebb5f1a4b17f6bc7782be8" dependencies = [ "thiserror", ] @@ -134,3 +198,23 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/bare-metal/aps/examples/Cargo.toml b/src/bare-metal/aps/examples/Cargo.toml index 868e9ec9..d7cd5045 100644 --- a/src/bare-metal/aps/examples/Cargo.toml +++ b/src/bare-metal/aps/examples/Cargo.toml @@ -7,14 +7,14 @@ edition = "2021" publish = false [dependencies] +aarch64-paging = { version = "0.9.1", default-features = false } +aarch64-rt = "0.1.3" +arm-pl011-uart = "0.3.1" bitflags = "2.9.0" log = "0.4.27" smccc = "0.2.0" spin = "0.10.0" -[build-dependencies] -cc = "1.2.20" - [[bin]] name = "improved" path = "src/main_improved.rs" @@ -27,6 +27,10 @@ path = "src/main_logger.rs" name = "minimal" path = "src/main_minimal.rs" +[[bin]] +name = "rt" +path = "src/main_rt.rs" + [[bin]] name = "psci" path = "src/main_psci.rs" diff --git a/src/bare-metal/aps/examples/build.rs b/src/bare-metal/aps/examples/build.rs deleted file mode 100644 index 2a7372b4..00000000 --- a/src/bare-metal/aps/examples/build.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use cc::Build; -use std::env; - -fn main() { - env::set_var("CROSS_COMPILE", "aarch64-none-elf"); - env::set_var("CC", "clang"); - - Build::new() - .file("entry.S") - .file("exceptions.S") - .file("idmap.S") - .compile("empty"); -} diff --git a/src/exercises/bare-metal/rtc/build.rs b/src/bare-metal/aps/examples/src/asm.rs similarity index 66% rename from src/exercises/bare-metal/rtc/build.rs rename to src/bare-metal/aps/examples/src/asm.rs index 5fc5a872..123ca626 100644 --- a/src/exercises/bare-metal/rtc/build.rs +++ b/src/bare-metal/aps/examples/src/asm.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,16 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use cc::Build; -use std::env; +use core::arch::global_asm; -fn main() { - env::set_var("CROSS_COMPILE", "aarch64-none-elf"); - env::set_var("CC", "clang"); - - Build::new() - .file("entry.S") - .file("exceptions.S") - .file("idmap.S") - .compile("empty") -} +global_asm!(include_str!("entry.S")); +global_asm!(include_str!("exceptions.S")); +global_asm!(include_str!("idmap.S")); diff --git a/src/bare-metal/aps/examples/entry.S b/src/bare-metal/aps/examples/src/entry.S similarity index 100% rename from src/bare-metal/aps/examples/entry.S rename to src/bare-metal/aps/examples/src/entry.S diff --git a/src/bare-metal/aps/examples/exceptions.S b/src/bare-metal/aps/examples/src/exceptions.S similarity index 100% rename from src/bare-metal/aps/examples/exceptions.S rename to src/bare-metal/aps/examples/src/exceptions.S diff --git a/src/bare-metal/aps/examples/idmap.S b/src/bare-metal/aps/examples/src/idmap.S similarity index 100% rename from src/bare-metal/aps/examples/idmap.S rename to src/bare-metal/aps/examples/src/idmap.S diff --git a/src/bare-metal/aps/examples/src/main_minimal.rs b/src/bare-metal/aps/examples/src/main_minimal.rs index 24c2d5f3..b6d2ed68 100644 --- a/src/bare-metal/aps/examples/src/main_minimal.rs +++ b/src/bare-metal/aps/examples/src/main_minimal.rs @@ -16,6 +16,7 @@ #![no_main] #![no_std] +mod asm; mod exceptions; mod pl011_minimal; diff --git a/src/bare-metal/aps/examples/src/main_rt.rs b/src/bare-metal/aps/examples/src/main_rt.rs new file mode 100644 index 00000000..792773b6 --- /dev/null +++ b/src/bare-metal/aps/examples/src/main_rt.rs @@ -0,0 +1,77 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ANCHOR: main +#![no_main] +#![no_std] + +mod exceptions; + +use aarch64_paging::paging::Attributes; +use aarch64_rt::{entry, initial_pagetable, InitialPagetable}; +use arm_pl011_uart::{PL011Registers, Uart, UniqueMmioPointer}; +use core::fmt::Write; +use core::panic::PanicInfo; +use core::ptr::NonNull; +use log::error; +use smccc::psci::system_off; +use smccc::Hvc; + +/// Base address of the primary PL011 UART. +const PL011_BASE_ADDRESS: NonNull = + NonNull::new(0x900_0000 as _).unwrap(); + +/// Attributes to use for device memory in the initial identity map. +const DEVICE_ATTRIBUTES: Attributes = Attributes::VALID + .union(Attributes::ATTRIBUTE_INDEX_0) + .union(Attributes::ACCESSED) + .union(Attributes::UXN); + +/// Attributes to use for normal memory in the initial identity map. +const MEMORY_ATTRIBUTES: Attributes = Attributes::VALID + .union(Attributes::ATTRIBUTE_INDEX_1) + .union(Attributes::INNER_SHAREABLE) + .union(Attributes::ACCESSED) + .union(Attributes::NON_GLOBAL); + +initial_pagetable!({ + let mut idmap = [0; 512]; + // 1 GiB of device memory. + idmap[0] = DEVICE_ATTRIBUTES.bits(); + // 1 GiB of normal memory. + idmap[1] = MEMORY_ATTRIBUTES.bits() | 0x40000000; + // Another 1 GiB of device memory starting at 256 GiB. + idmap[256] = DEVICE_ATTRIBUTES.bits() | 0x4000000000; + InitialPagetable(idmap) +}); + +entry!(main); +fn main(x0: u64, x1: u64, x2: u64, x3: u64) -> ! { + // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and + // nothing else accesses that address range. + let mut uart = unsafe { Uart::new(UniqueMmioPointer::new(PL011_BASE_ADDRESS)) }; + + writeln!(uart, "main({x0:#x}, {x1:#x}, {x2:#x}, {x3:#x})").unwrap(); + + system_off::().unwrap(); + panic!("system_off returned"); +} +// ANCHOR_END: main + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + error!("{info}"); + system_off::().unwrap(); + loop {} +} diff --git a/src/exercises/bare-metal/rtc.md b/src/exercises/bare-metal/rtc.md index 9a52bffc..7536f90f 100644 --- a/src/exercises/bare-metal/rtc.md +++ b/src/exercises/bare-metal/rtc.md @@ -57,15 +57,6 @@ _src/logger.rs_ (you shouldn't need to change this): {{#include rtc/src/logger.rs}} ``` -_src/pl011.rs_ (you shouldn't need to change this): - - - - -```rust,compile_fail -{{#include rtc/src/pl011.rs}} -``` - _Cargo.toml_ (you shouldn't need to change this): @@ -75,42 +66,6 @@ _Cargo.toml_ (you shouldn't need to change this): {{#include rtc/Cargo.toml}} ``` -_build.rs_ (you shouldn't need to change this): - - - - -```rust,compile_fail -{{#include rtc/build.rs}} -``` - -_entry.S_ (you shouldn't need to change this): - - - - -```armasm -{{#include rtc/entry.S}} -``` - -_exceptions.S_ (you shouldn't need to change this): - - - - -```armasm -{{#include rtc/exceptions.S}} -``` - -_idmap.S_ (you shouldn't need to change this): - - - - -```armasm -{{#include rtc/idmap.S}} -``` - _image.ld_ (you shouldn't need to change this): diff --git a/src/exercises/bare-metal/rtc/Cargo.lock b/src/exercises/bare-metal/rtc/Cargo.lock index 6e510ff0..409be740 100644 --- a/src/exercises/bare-metal/rtc/Cargo.lock +++ b/src/exercises/bare-metal/rtc/Cargo.lock @@ -2,6 +2,26 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aarch64-paging" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3b8f725e9256b2fac2d25e013e22a6a391b8c07e23c4c5eac6e037a78a28801" +dependencies = [ + "bitflags", + "thiserror", +] + +[[package]] +name = "aarch64-rt" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be4edaff3b5ead5fb7e9ef0d72fa1e93b4052ec2410e0ebc93f6f360c9599ea" +dependencies = [ + "cfg-if", + "smccc", +] + [[package]] name = "arm-gic" version = "0.4.0" @@ -14,6 +34,20 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "arm-pl011-uart" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff5b0f1e39ec186e409c6fd80bbb83aa00622ca71c9c0561b5571df3b5f5391f" +dependencies = [ + "bitflags", + "embedded-hal-nb", + "embedded-io", + "safe-mmio", + "thiserror", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -27,13 +61,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] -name = "cc" -version = "1.2.20" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" -dependencies = [ - "shlex", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" @@ -44,6 +75,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal", + "nb", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "lock_api" version = "0.4.10" @@ -60,6 +113,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "num-traits" version = "0.2.16" @@ -91,9 +150,11 @@ dependencies = [ name = "rtc" version = "0.1.0" dependencies = [ + "aarch64-paging", + "aarch64-rt", "arm-gic", + "arm-pl011-uart", "bitflags", - "cc", "chrono", "log", "smccc", @@ -115,17 +176,11 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "smccc" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d28c99a9913f0686f470a6ff020e160195eb1a86f9c1800aab2b3abba4bc9b3" +checksum = "87ed898c59fbfd740492e435cba30a08b74a68d1f1ebb5f1a4b17f6bc7782be8" dependencies = [ "thiserror", ] diff --git a/src/exercises/bare-metal/rtc/Cargo.toml b/src/exercises/bare-metal/rtc/Cargo.toml index 6bf32d31..aac094a2 100644 --- a/src/exercises/bare-metal/rtc/Cargo.toml +++ b/src/exercises/bare-metal/rtc/Cargo.toml @@ -7,12 +7,12 @@ edition = "2021" publish = false [dependencies] +aarch64-paging = { version = "0.9.1", default-features = false } +aarch64-rt = "0.1.3" arm-gic = "0.4.0" +arm-pl011-uart = "0.3.1" bitflags = "2.9.0" chrono = { version = "0.4.41", default-features = false } log = "0.4.27" -smccc = "0.2.0" +smccc = "0.2.1" spin = "0.10.0" - -[build-dependencies] -cc = "1.2.20" diff --git a/src/exercises/bare-metal/rtc/entry.S b/src/exercises/bare-metal/rtc/entry.S deleted file mode 100644 index cd554f28..00000000 --- a/src/exercises/bare-metal/rtc/entry.S +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.macro adr_l, reg:req, sym:req - adrp \reg, \sym - add \reg, \reg, :lo12:\sym -.endm - -.macro mov_i, reg:req, imm:req - movz \reg, :abs_g3:\imm - movk \reg, :abs_g2_nc:\imm - movk \reg, :abs_g1_nc:\imm - movk \reg, :abs_g0_nc:\imm -.endm - -.set .L_MAIR_DEV_nGnRE, 0x04 -.set .L_MAIR_MEM_WBWA, 0xff -.set .Lmairval, .L_MAIR_DEV_nGnRE | (.L_MAIR_MEM_WBWA << 8) - -/* 4 KiB granule size for TTBR0_EL1. */ -.set .L_TCR_TG0_4KB, 0x0 << 14 -/* 4 KiB granule size for TTBR1_EL1. */ -.set .L_TCR_TG1_4KB, 0x2 << 30 -/* Disable translation table walk for TTBR1_EL1, generating a translation fault instead. */ -.set .L_TCR_EPD1, 0x1 << 23 -/* Translation table walks for TTBR0_EL1 are inner sharable. */ -.set .L_TCR_SH_INNER, 0x3 << 12 -/* - * Translation table walks for TTBR0_EL1 are outer write-back read-allocate write-allocate - * cacheable. - */ -.set .L_TCR_RGN_OWB, 0x1 << 10 -/* - * Translation table walks for TTBR0_EL1 are inner write-back read-allocate write-allocate - * cacheable. - */ -.set .L_TCR_RGN_IWB, 0x1 << 8 -/* Size offset for TTBR0_EL1 is 2**39 bytes (512 GiB). */ -.set .L_TCR_T0SZ_512, 64 - 39 -.set .Ltcrval, .L_TCR_TG0_4KB | .L_TCR_TG1_4KB | .L_TCR_EPD1 | .L_TCR_RGN_OWB -.set .Ltcrval, .Ltcrval | .L_TCR_RGN_IWB | .L_TCR_SH_INNER | .L_TCR_T0SZ_512 - -/* Stage 1 instruction access cacheability is unaffected. */ -.set .L_SCTLR_ELx_I, 0x1 << 12 -/* SP alignment fault if SP is not aligned to a 16 byte boundary. */ -.set .L_SCTLR_ELx_SA, 0x1 << 3 -/* Stage 1 data access cacheability is unaffected. */ -.set .L_SCTLR_ELx_C, 0x1 << 2 -/* EL0 and EL1 stage 1 MMU enabled. */ -.set .L_SCTLR_ELx_M, 0x1 << 0 -/* Privileged Access Never is unchanged on taking an exception to EL1. */ -.set .L_SCTLR_EL1_SPAN, 0x1 << 23 -/* SETEND instruction disabled at EL0 in aarch32 mode. */ -.set .L_SCTLR_EL1_SED, 0x1 << 8 -/* Various IT instructions are disabled at EL0 in aarch32 mode. */ -.set .L_SCTLR_EL1_ITD, 0x1 << 7 -.set .L_SCTLR_EL1_RES1, (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29) -.set .Lsctlrval, .L_SCTLR_ELx_M | .L_SCTLR_ELx_C | .L_SCTLR_ELx_SA | .L_SCTLR_EL1_ITD | .L_SCTLR_EL1_SED -.set .Lsctlrval, .Lsctlrval | .L_SCTLR_ELx_I | .L_SCTLR_EL1_SPAN | .L_SCTLR_EL1_RES1 - -/** - * This is a generic entry point for an image. It carries out the operations required to prepare the - * loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above, - * prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3 - * for the Rust entry point, as these may contain boot parameters. - */ -.section .init.entry, "ax" -.global entry -entry: - /* Load and apply the memory management configuration, ready to enable MMU and caches. */ - adrp x30, idmap - msr ttbr0_el1, x30 - - mov_i x30, .Lmairval - msr mair_el1, x30 - - mov_i x30, .Ltcrval - /* Copy the supported PA range into TCR_EL1.IPS. */ - mrs x29, id_aa64mmfr0_el1 - bfi x30, x29, #32, #4 - - msr tcr_el1, x30 - - mov_i x30, .Lsctlrval - - /* - * Ensure everything before this point has completed, then invalidate any potentially stale - * local TLB entries before they start being used. - */ - isb - tlbi vmalle1 - ic iallu - dsb nsh - isb - - /* - * Configure sctlr_el1 to enable MMU and cache and don't proceed until this has completed. - */ - msr sctlr_el1, x30 - isb - - /* Disable trapping floating point access in EL1. */ - mrs x30, cpacr_el1 - orr x30, x30, #(0x3 << 20) - msr cpacr_el1, x30 - isb - - /* Zero out the bss section. */ - adr_l x29, bss_begin - adr_l x30, bss_end -0: cmp x29, x30 - b.hs 1f - stp xzr, xzr, [x29], #16 - b 0b - -1: /* Prepare the stack. */ - adr_l x30, boot_stack_end - mov sp, x30 - - /* Set up exception vector. */ - adr x30, vector_table_el1 - msr vbar_el1, x30 - - /* Call into Rust code. */ - bl main - - /* Loop forever waiting for interrupts. */ -2: wfi - b 2b diff --git a/src/exercises/bare-metal/rtc/exceptions.S b/src/exercises/bare-metal/rtc/exceptions.S deleted file mode 100644 index 434b0503..00000000 --- a/src/exercises/bare-metal/rtc/exceptions.S +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Saves the volatile registers onto the stack. This currently takes 14 - * instructions, so it can be used in exception handlers with 18 instructions - * left. - * - * On return, x0 and x1 are initialised to elr_el2 and spsr_el2 respectively, - * which can be used as the first and second arguments of a subsequent call. - */ -.macro save_volatile_to_stack - /* Reserve stack space and save registers x0-x18, x29 & x30. */ - stp x0, x1, [sp, #-(8 * 24)]! - stp x2, x3, [sp, #8 * 2] - stp x4, x5, [sp, #8 * 4] - stp x6, x7, [sp, #8 * 6] - stp x8, x9, [sp, #8 * 8] - stp x10, x11, [sp, #8 * 10] - stp x12, x13, [sp, #8 * 12] - stp x14, x15, [sp, #8 * 14] - stp x16, x17, [sp, #8 * 16] - str x18, [sp, #8 * 18] - stp x29, x30, [sp, #8 * 20] - - /* - * Save elr_el1 & spsr_el1. This such that we can take nested exception - * and still be able to unwind. - */ - mrs x0, elr_el1 - mrs x1, spsr_el1 - stp x0, x1, [sp, #8 * 22] -.endm - -/** - * Restores the volatile registers from the stack. This currently takes 14 - * instructions, so it can be used in exception handlers while still leaving 18 - * instructions left; if paired with save_volatile_to_stack, there are 4 - * instructions to spare. - */ -.macro restore_volatile_from_stack - /* Restore registers x2-x18, x29 & x30. */ - ldp x2, x3, [sp, #8 * 2] - ldp x4, x5, [sp, #8 * 4] - ldp x6, x7, [sp, #8 * 6] - ldp x8, x9, [sp, #8 * 8] - ldp x10, x11, [sp, #8 * 10] - ldp x12, x13, [sp, #8 * 12] - ldp x14, x15, [sp, #8 * 14] - ldp x16, x17, [sp, #8 * 16] - ldr x18, [sp, #8 * 18] - ldp x29, x30, [sp, #8 * 20] - - /* Restore registers elr_el1 & spsr_el1, using x0 & x1 as scratch. */ - ldp x0, x1, [sp, #8 * 22] - msr elr_el1, x0 - msr spsr_el1, x1 - - /* Restore x0 & x1, and release stack space. */ - ldp x0, x1, [sp], #8 * 24 -.endm - -/** - * This is a generic handler for exceptions taken at the current EL while using - * SP0. It behaves similarly to the SPx case by first switching to SPx, doing - * the work, then switching back to SP0 before returning. - * - * Switching to SPx and calling the Rust handler takes 16 instructions. To - * restore and return we need an additional 16 instructions, so we can implement - * the whole handler within the allotted 32 instructions. - */ -.macro current_exception_sp0 handler:req - msr spsel, #1 - save_volatile_to_stack - bl \handler - restore_volatile_from_stack - msr spsel, #0 - eret -.endm - -/** - * This is a generic handler for exceptions taken at the current EL while using - * SPx. It saves volatile registers, calls the Rust handler, restores volatile - * registers, then returns. - * - * This also works for exceptions taken from EL0, if we don't care about - * non-volatile registers. - * - * Saving state and jumping to the Rust handler takes 15 instructions, and - * restoring and returning also takes 15 instructions, so we can fit the whole - * handler in 30 instructions, under the limit of 32. - */ -.macro current_exception_spx handler:req - save_volatile_to_stack - bl \handler - restore_volatile_from_stack - eret -.endm - -.section .text.vector_table_el1, "ax" -.global vector_table_el1 -.balign 0x800 -vector_table_el1: -sync_cur_sp0: - current_exception_sp0 sync_exception_current - -.balign 0x80 -irq_cur_sp0: - current_exception_sp0 irq_current - -.balign 0x80 -fiq_cur_sp0: - current_exception_sp0 fiq_current - -.balign 0x80 -serr_cur_sp0: - current_exception_sp0 serr_current - -.balign 0x80 -sync_cur_spx: - current_exception_spx sync_exception_current - -.balign 0x80 -irq_cur_spx: - current_exception_spx irq_current - -.balign 0x80 -fiq_cur_spx: - current_exception_spx fiq_current - -.balign 0x80 -serr_cur_spx: - current_exception_spx serr_current - -.balign 0x80 -sync_lower_64: - current_exception_spx sync_lower - -.balign 0x80 -irq_lower_64: - current_exception_spx irq_lower - -.balign 0x80 -fiq_lower_64: - current_exception_spx fiq_lower - -.balign 0x80 -serr_lower_64: - current_exception_spx serr_lower - -.balign 0x80 -sync_lower_32: - current_exception_spx sync_lower - -.balign 0x80 -irq_lower_32: - current_exception_spx irq_lower - -.balign 0x80 -fiq_lower_32: - current_exception_spx fiq_lower - -.balign 0x80 -serr_lower_32: - current_exception_spx serr_lower diff --git a/src/exercises/bare-metal/rtc/idmap.S b/src/exercises/bare-metal/rtc/idmap.S deleted file mode 100644 index a79f3af2..00000000 --- a/src/exercises/bare-metal/rtc/idmap.S +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.set .L_TT_TYPE_BLOCK, 0x1 -.set .L_TT_TYPE_PAGE, 0x3 -.set .L_TT_TYPE_TABLE, 0x3 - -/* Access flag. */ -.set .L_TT_AF, 0x1 << 10 -/* Not global. */ -.set .L_TT_NG, 0x1 << 11 -.set .L_TT_XN, 0x3 << 53 - -.set .L_TT_MT_DEV, 0x0 << 2 // MAIR #0 (DEV_nGnRE) -.set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8) // MAIR #1 (MEM_WBWA), inner shareable - -.set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN -.set .L_BLOCK_MEM, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG - -.section ".rodata.idmap", "a", %progbits -.global idmap -.align 12 -idmap: - /* level 1 */ - .quad .L_BLOCK_DEV | 0x0 // 1 GiB of device mappings - .quad .L_BLOCK_MEM | 0x40000000 // 1 GiB of DRAM - .fill 254, 8, 0x0 // 254 GiB of unmapped VA space - .quad .L_BLOCK_DEV | 0x4000000000 // 1 GiB of device mappings - .fill 255, 8, 0x0 // 255 GiB of remaining VA space diff --git a/src/exercises/bare-metal/rtc/src/logger.rs b/src/exercises/bare-metal/rtc/src/logger.rs index e27ee2b9..aa64782e 100644 --- a/src/exercises/bare-metal/rtc/src/logger.rs +++ b/src/exercises/bare-metal/rtc/src/logger.rs @@ -13,7 +13,7 @@ // limitations under the License. // ANCHOR: main -use crate::pl011::Uart; +use arm_pl011_uart::Uart; use core::fmt::Write; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use spin::mutex::SpinMutex; @@ -21,7 +21,7 @@ use spin::mutex::SpinMutex; static LOGGER: Logger = Logger { uart: SpinMutex::new(None) }; struct Logger { - uart: SpinMutex>, + uart: SpinMutex>>, } impl Log for Logger { @@ -43,7 +43,10 @@ impl Log for Logger { } /// Initialises UART logger. -pub fn init(uart: Uart, max_level: LevelFilter) -> Result<(), SetLoggerError> { +pub fn init( + uart: Uart<'static>, + max_level: LevelFilter, +) -> Result<(), SetLoggerError> { LOGGER.uart.lock().replace(uart); log::set_logger(&LOGGER)?; diff --git a/src/exercises/bare-metal/rtc/src/main.rs b/src/exercises/bare-metal/rtc/src/main.rs index c4316cf0..55b813da 100644 --- a/src/exercises/bare-metal/rtc/src/main.rs +++ b/src/exercises/bare-metal/rtc/src/main.rs @@ -19,7 +19,6 @@ mod exceptions; mod logger; -mod pl011; // ANCHOR_END: top mod pl031; @@ -28,10 +27,13 @@ use arm_gic::{irq_enable, wfi, IntId, Trigger}; use chrono::{TimeZone, Utc}; use core::hint::spin_loop; // ANCHOR: imports -use crate::pl011::Uart; +use aarch64_paging::paging::Attributes; +use aarch64_rt::{entry, initial_pagetable, InitialPagetable}; use arm_gic::gicv3::registers::{Gicd, GicrSgi}; use arm_gic::gicv3::GicV3; +use arm_pl011_uart::{PL011Registers, Uart, UniqueMmioPointer}; use core::panic::PanicInfo; +use core::ptr::NonNull; use log::{error, info, trace, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; @@ -41,7 +43,32 @@ const GICD_BASE_ADDRESS: *mut Gicd = 0x800_0000 as _; const GICR_BASE_ADDRESS: *mut GicrSgi = 0x80A_0000 as _; /// Base address of the primary PL011 UART. -const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; +const PL011_BASE_ADDRESS: NonNull = + NonNull::new(0x900_0000 as _).unwrap(); + +/// Attributes to use for device memory in the initial identity map. +const DEVICE_ATTRIBUTES: Attributes = Attributes::VALID + .union(Attributes::ATTRIBUTE_INDEX_0) + .union(Attributes::ACCESSED) + .union(Attributes::UXN); + +/// Attributes to use for normal memory in the initial identity map. +const MEMORY_ATTRIBUTES: Attributes = Attributes::VALID + .union(Attributes::ATTRIBUTE_INDEX_1) + .union(Attributes::INNER_SHAREABLE) + .union(Attributes::ACCESSED) + .union(Attributes::NON_GLOBAL); + +initial_pagetable!({ + let mut idmap = [0; 512]; + // 1 GiB of device memory. + idmap[0] = DEVICE_ATTRIBUTES.bits(); + // 1 GiB of normal memory. + idmap[1] = MEMORY_ATTRIBUTES.bits() | 0x40000000; + // Another 1 GiB of device memory starting at 256 GiB. + idmap[256] = DEVICE_ATTRIBUTES.bits() | 0x4000000000; + InitialPagetable(idmap) +}); // ANCHOR_END: imports /// Base address of the PL031 RTC. @@ -50,12 +77,11 @@ const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _; const PL031_IRQ: IntId = IntId::spi(2); // ANCHOR: main -// SAFETY: There is no other global function of this name. -#[unsafe(no_mangle)] -extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { +entry!(main); +fn main(x0: u64, x1: u64, x2: u64, x3: u64) -> ! { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. - let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; + let uart = unsafe { Uart::new(UniqueMmioPointer::new(PL011_BASE_ADDRESS)) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3); @@ -123,6 +149,7 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // ANCHOR: main_end system_off::().unwrap(); + panic!("system_off returned"); } #[panic_handler] diff --git a/src/exercises/bare-metal/rtc/src/pl011.rs b/src/exercises/bare-metal/rtc/src/pl011.rs deleted file mode 100644 index f38c3c50..00000000 --- a/src/exercises/bare-metal/rtc/src/pl011.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![allow(unused)] - -use core::fmt::{self, Write}; - -// ANCHOR: Flags -use bitflags::bitflags; - -bitflags! { - /// Flags from the UART flag register. - #[repr(transparent)] - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - struct Flags: u16 { - /// Clear to send. - const CTS = 1 << 0; - /// Data set ready. - const DSR = 1 << 1; - /// Data carrier detect. - const DCD = 1 << 2; - /// UART busy transmitting data. - const BUSY = 1 << 3; - /// Receive FIFO is empty. - const RXFE = 1 << 4; - /// Transmit FIFO is full. - const TXFF = 1 << 5; - /// Receive FIFO is full. - const RXFF = 1 << 6; - /// Transmit FIFO is empty. - const TXFE = 1 << 7; - /// Ring indicator. - const RI = 1 << 8; - } -} -// ANCHOR_END: Flags - -bitflags! { - /// Flags from the UART Receive Status Register / Error Clear Register. - #[repr(transparent)] - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - struct ReceiveStatus: u16 { - /// Framing error. - const FE = 1 << 0; - /// Parity error. - const PE = 1 << 1; - /// Break error. - const BE = 1 << 2; - /// Overrun error. - const OE = 1 << 3; - } -} - -// ANCHOR: Registers -#[repr(C, align(4))] -struct Registers { - dr: u16, - _reserved0: [u8; 2], - rsr: ReceiveStatus, - _reserved1: [u8; 19], - fr: Flags, - _reserved2: [u8; 6], - ilpr: u8, - _reserved3: [u8; 3], - ibrd: u16, - _reserved4: [u8; 2], - fbrd: u8, - _reserved5: [u8; 3], - lcr_h: u8, - _reserved6: [u8; 3], - cr: u16, - _reserved7: [u8; 3], - ifls: u8, - _reserved8: [u8; 3], - imsc: u16, - _reserved9: [u8; 2], - ris: u16, - _reserved10: [u8; 2], - mis: u16, - _reserved11: [u8; 2], - icr: u16, - _reserved12: [u8; 2], - dmacr: u8, - _reserved13: [u8; 3], -} -// ANCHOR_END: Registers - -// ANCHOR: Uart -/// Driver for a PL011 UART. -#[derive(Debug)] -pub struct Uart { - registers: *mut Registers, -} - -impl Uart { - /// Constructs a new instance of the UART driver for a PL011 device at the - /// given base address. - /// - /// # Safety - /// - /// The given base address must point to the MMIO control registers of a - /// PL011 device, which must be mapped into the address space of the process - /// as device memory and not have any other aliases. - pub unsafe fn new(base_address: *mut u32) -> Self { - Self { registers: base_address as *mut Registers } - } - - /// Writes a single byte to the UART. - pub fn write_byte(&self, byte: u8) { - // Wait until there is room in the TX buffer. - while self.read_flag_register().contains(Flags::TXFF) {} - - // SAFETY: We know that self.registers points to the control registers - // of a PL011 device which is appropriately mapped. - unsafe { - // Write to the TX buffer. - (&raw mut (*self.registers).dr).write_volatile(byte.into()); - } - - // Wait until the UART is no longer busy. - while self.read_flag_register().contains(Flags::BUSY) {} - } - - /// Reads and returns a pending byte, or `None` if nothing has been - /// received. - pub fn read_byte(&self) -> Option { - if self.read_flag_register().contains(Flags::RXFE) { - None - } else { - // SAFETY: We know that self.registers points to the control - // registers of a PL011 device which is appropriately mapped. - let data = unsafe { (&raw const (*self.registers).dr).read_volatile() }; - // TODO: Check for error conditions in bits 8-11. - Some(data as u8) - } - } - - fn read_flag_register(&self) -> Flags { - // SAFETY: We know that self.registers points to the control registers - // of a PL011 device which is appropriately mapped. - unsafe { (&raw const (*self.registers).fr).read_volatile() } - } -} -// ANCHOR_END: Uart - -impl Write for Uart { - fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.as_bytes() { - self.write_byte(*c); - } - Ok(()) - } -} - -// Safe because it just contains a pointer to device memory, which can be -// accessed from any context. -unsafe impl Send for Uart {}