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:
parent
3b6306885d
commit
2bb13e5098
@ -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)
|
||||
|
15
src/bare-metal/aps/aarch64-rt.md
Normal file
15
src/bare-metal/aps/aarch64-rt.md
Normal 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}}
|
||||
```
|
@ -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.
|
||||
|
||||
|
110
src/bare-metal/aps/examples/Cargo.lock
generated
110
src/bare-metal/aps/examples/Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
@ -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"));
|
@ -16,6 +16,7 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
mod asm;
|
||||
mod exceptions;
|
||||
mod pl011_minimal;
|
||||
|
||||
|
77
src/bare-metal/aps/examples/src/main_rt.rs
Normal file
77
src/bare-metal/aps/examples/src/main_rt.rs
Normal 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 {}
|
||||
}
|
@ -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 -->
|
||||
|
85
src/exercises/bare-metal/rtc/Cargo.lock
generated
85
src/exercises/bare-metal/rtc/Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
@ -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"
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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)?;
|
||||
|
@ -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]
|
||||
|
@ -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 {}
|
Loading…
x
Reference in New Issue
Block a user