1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-22 18:30:33 +02:00

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.
This commit is contained in:
Andrew Walbran 2025-05-16 14:34:45 +01:00 committed by GitHub
parent 3b6306885d
commit 2bb13e5098
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 319 additions and 662 deletions

View File

@ -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)

View File

@ -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}}
```

View File

@ -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}}
```
<details>
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.

View File

@ -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",
]

View File

@ -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"

View File

@ -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");
}

View File

@ -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"));

View File

@ -16,6 +16,7 @@
#![no_main]
#![no_std]
mod asm;
mod exceptions;
mod pl011_minimal;

View File

@ -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<PL011Registers> =
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::<Hvc>().unwrap();
panic!("system_off returned");
}
// ANCHOR_END: main
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("{info}");
system_off::<Hvc>().unwrap();
loop {}
}

View File

@ -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):
<!-- File src/pl011.rs -->
<!-- mdbook-xgettext: skip -->
```rust,compile_fail
{{#include rtc/src/pl011.rs}}
```
_Cargo.toml_ (you shouldn't need to change this):
<!-- File Cargo.toml -->
@ -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):
<!-- File build.rs -->
<!-- mdbook-xgettext: skip -->
```rust,compile_fail
{{#include rtc/build.rs}}
```
_entry.S_ (you shouldn't need to change this):
<!-- File entry.S -->
<!-- mdbook-xgettext: skip -->
```armasm
{{#include rtc/entry.S}}
```
_exceptions.S_ (you shouldn't need to change this):
<!-- File exceptions.S -->
<!-- mdbook-xgettext: skip -->
```armasm
{{#include rtc/exceptions.S}}
```
_idmap.S_ (you shouldn't need to change this):
<!-- File idmap.S -->
<!-- mdbook-xgettext: skip -->
```armasm
{{#include rtc/idmap.S}}
```
_image.ld_ (you shouldn't need to change this):
<!-- File image.ld -->

View File

@ -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",
]

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<Option<Uart>>,
uart: SpinMutex<Option<Uart<'static>>>,
}
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)?;

View File

@ -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<PL011Registers> =
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::<Hvc>().unwrap();
panic!("system_off returned");
}
#[panic_handler]

View File

@ -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<u8> {
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 {}