Merge changes I5ac315ed,I57d5a764,I82360003,I4ff012ac,Ibac59fc1, ... into main
* changes:
vmbase: Move Trng internals to arch::aarch64
vmbase: Export VirtualAddress from arch
vmbase: Separate console form aarch64 UART implementation
vmbase: uart: Create abstraction layer over register write
pvmfw: Move aarch64 fn `jump_to_payload` to a separate module
pvmfw: Move aarch64 exceptions handling to new `arch` module
vmbase: Move exceptions to aarch64 directories
vmbase: Move hvc to aarch64
vmbase: Move dbm to aarch64
vmbase: Move linker to aarch64
vmbase: Move page table to aarch64
vmbase: Move crosvm layout to aarch64
vmbase: aarch64: Move power to platform
libvmbase: Separate aarch64 asm files
pvmfw: Compile aarch64 specifics conditionally
vmbase: Use aarch64 specific libs conditionally
diff --git a/guest/pvmfw/Android.bp b/guest/pvmfw/Android.bp
index 4ef57a6..6f113c8 100644
--- a/guest/pvmfw/Android.bp
+++ b/guest/pvmfw/Android.bp
@@ -11,7 +11,6 @@
"legacy",
],
rustlibs: [
- "libaarch64_paging",
"libbssl_avf_nostd",
"libcbor_util_nostd",
"libciborium_nostd",
@@ -24,8 +23,6 @@
"libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
"libpvmfw_fdt_template",
- "libservice_vm_version",
- "libsmccc",
"libstatic_assertions",
"libtinyvec_nostd",
"libuuid_nostd",
@@ -34,6 +31,15 @@
"libzerocopy_nostd",
"libzeroize_nostd",
],
+ target: {
+ android_arm64: {
+ rustlibs: [
+ "libaarch64_paging",
+ "libsmccc",
+ "libservice_vm_version",
+ ],
+ },
+ },
}
// Generates an empty file.
@@ -270,17 +276,21 @@
cc_binary {
name: "pvmfw",
defaults: ["vmbase_elf_defaults"],
- srcs: [
- "idmap.S",
- ],
static_libs: [
"libpvmfw",
"libvmbase_dice_clear_memory",
],
- linker_scripts: [
- "image.ld",
- ":vmbase_sections",
- ],
+ target: {
+ android_arm64: {
+ srcs: [
+ "asm/aarch64/idmap.S",
+ ],
+ linker_scripts: [
+ "asm/aarch64/image.ld",
+ ":vmbase_sections",
+ ],
+ },
+ },
// `installable: false` is inherited from vmbase_elf_defaults, and that
// hides this module from Make, which makes it impossible for the Make world
// to place the unstripped binary to the symbols directory. Marking back as
diff --git a/guest/pvmfw/idmap.S b/guest/pvmfw/asm/aarch64/idmap.S
similarity index 100%
rename from guest/pvmfw/idmap.S
rename to guest/pvmfw/asm/aarch64/idmap.S
diff --git a/guest/pvmfw/image.ld b/guest/pvmfw/asm/aarch64/image.ld
similarity index 100%
rename from guest/pvmfw/image.ld
rename to guest/pvmfw/asm/aarch64/image.ld
diff --git a/guest/pvmfw/src/arch.rs b/guest/pvmfw/src/arch.rs
new file mode 100644
index 0000000..1bbf4d4
--- /dev/null
+++ b/guest/pvmfw/src/arch.rs
@@ -0,0 +1,21 @@
+// Copyright 2024, The Android Open Source Project
+//
+// 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.
+
+//! Module providing Low-level platform specific implementations
+
+#[cfg(target_arch = "aarch64")]
+mod aarch64;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::payload;
diff --git a/guest/pvmfw/src/arch/aarch64.rs b/guest/pvmfw/src/arch/aarch64.rs
new file mode 100644
index 0000000..171ba64
--- /dev/null
+++ b/guest/pvmfw/src/arch/aarch64.rs
@@ -0,0 +1,18 @@
+// Copyright 2024, The Android Open Source Project
+//
+// 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.
+
+//! aarch64 platform specific code
+
+pub mod exceptions;
+pub mod payload;
diff --git a/guest/pvmfw/src/exceptions.rs b/guest/pvmfw/src/arch/aarch64/exceptions.rs
similarity index 93%
rename from guest/pvmfw/src/exceptions.rs
rename to guest/pvmfw/src/arch/aarch64/exceptions.rs
index c16e637..4c867fb 100644
--- a/guest/pvmfw/src/exceptions.rs
+++ b/guest/pvmfw/src/arch/aarch64/exceptions.rs
@@ -15,10 +15,10 @@
//! Exception handlers.
use vmbase::{
- eprintln,
- exceptions::{handle_permission_fault, handle_translation_fault},
- exceptions::{ArmException, Esr, HandleExceptionError},
- logger,
+ arch::aarch64::exceptions::{
+ handle_permission_fault, handle_translation_fault, ArmException, Esr, HandleExceptionError,
+ },
+ eprintln, logger,
power::reboot,
read_sysreg,
};
diff --git a/guest/pvmfw/src/arch/aarch64/payload.rs b/guest/pvmfw/src/arch/aarch64/payload.rs
new file mode 100644
index 0000000..0da8297
--- /dev/null
+++ b/guest/pvmfw/src/arch/aarch64/payload.rs
@@ -0,0 +1,177 @@
+// Copyright 2022, The Android Open Source Project
+//
+// 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.
+
+//! ARM64 low-level payload entry point
+
+use crate::memory::MemorySlices;
+use core::arch::asm;
+use core::mem::size_of;
+use vmbase::util::RangeExt as _;
+use vmbase::{arch::aarch64::min_dcache_line_size, layout, memory::deactivate_dynamic_page_tables};
+
+/// Function boot payload after cleaning all secret from pvmfw memory
+pub fn jump_to_payload(entrypoint: usize, slices: &MemorySlices) -> ! {
+ let fdt_address = slices.fdt.as_ptr() as usize;
+ let bcc = slices
+ .dice_chain
+ .map(|slice| {
+ let r = slice.as_ptr_range();
+ (r.start as usize)..(r.end as usize)
+ })
+ .expect("Missing DICE chain");
+
+ deactivate_dynamic_page_tables();
+
+ const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
+ const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
+ // Stage 1 instruction access cacheability is unaffected.
+ const SCTLR_EL1_I: u64 = 0b1 << 12;
+ // SETEND instruction disabled at EL0 in aarch32 mode.
+ const SCTLR_EL1_SED: u64 = 0b1 << 8;
+ // Various IT instructions are disabled at EL0 in aarch32 mode.
+ const SCTLR_EL1_ITD: u64 = 0b1 << 7;
+
+ const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
+
+ let scratch = layout::data_bss_range();
+
+ assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
+ assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
+ assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
+
+ assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
+ assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
+ assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
+
+ let stack = layout::stack_range();
+
+ assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
+ assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
+ assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
+
+ let eh_stack = layout::eh_stack_range();
+
+ assert_ne!(eh_stack.end - eh_stack.start, 0, "EH stack region is empty.");
+ assert_eq!(eh_stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
+ assert_eq!(eh_stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
+
+ // Zero all memory that could hold secrets and that can't be safely written to from Rust.
+ // Disable the exception vector, caches and page table and then jump to the payload at the
+ // given address, passing it the given FDT pointer.
+ //
+ // SAFETY: We're exiting pvmfw by passing the register values we need to a noreturn asm!().
+ unsafe {
+ asm!(
+ "cmp {scratch}, {bcc}",
+ "b.hs 1f",
+
+ // Zero .data & .bss until BCC.
+ "0: stp xzr, xzr, [{scratch}], 16",
+ "cmp {scratch}, {bcc}",
+ "b.lo 0b",
+
+ "1:",
+ // Skip BCC.
+ "mov {scratch}, {bcc_end}",
+ "cmp {scratch}, {scratch_end}",
+ "b.hs 1f",
+
+ // Keep zeroing .data & .bss.
+ "0: stp xzr, xzr, [{scratch}], 16",
+ "cmp {scratch}, {scratch_end}",
+ "b.lo 0b",
+
+ "1:",
+ // Flush d-cache over .data & .bss (including BCC).
+ "0: dc cvau, {cache_line}",
+ "add {cache_line}, {cache_line}, {dcache_line_size}",
+ "cmp {cache_line}, {scratch_end}",
+ "b.lo 0b",
+
+ "mov {cache_line}, {stack}",
+ // Zero stack region.
+ "0: stp xzr, xzr, [{stack}], 16",
+ "cmp {stack}, {stack_end}",
+ "b.lo 0b",
+
+ // Flush d-cache over stack region.
+ "0: dc cvau, {cache_line}",
+ "add {cache_line}, {cache_line}, {dcache_line_size}",
+ "cmp {cache_line}, {stack_end}",
+ "b.lo 0b",
+
+ "mov {cache_line}, {eh_stack}",
+ // Zero EH stack region.
+ "0: stp xzr, xzr, [{eh_stack}], 16",
+ "cmp {eh_stack}, {eh_stack_end}",
+ "b.lo 0b",
+
+ // Flush d-cache over EH stack region.
+ "0: dc cvau, {cache_line}",
+ "add {cache_line}, {cache_line}, {dcache_line_size}",
+ "cmp {cache_line}, {eh_stack_end}",
+ "b.lo 0b",
+
+ "msr sctlr_el1, {sctlr_el1_val}",
+ "isb",
+ "mov x1, xzr",
+ "mov x2, xzr",
+ "mov x3, xzr",
+ "mov x4, xzr",
+ "mov x5, xzr",
+ "mov x6, xzr",
+ "mov x7, xzr",
+ "mov x8, xzr",
+ "mov x9, xzr",
+ "mov x10, xzr",
+ "mov x11, xzr",
+ "mov x12, xzr",
+ "mov x13, xzr",
+ "mov x14, xzr",
+ "mov x15, xzr",
+ "mov x16, xzr",
+ "mov x17, xzr",
+ "mov x18, xzr",
+ "mov x19, xzr",
+ "mov x20, xzr",
+ "mov x21, xzr",
+ "mov x22, xzr",
+ "mov x23, xzr",
+ "mov x24, xzr",
+ "mov x25, xzr",
+ "mov x26, xzr",
+ "mov x27, xzr",
+ "mov x28, xzr",
+ "mov x29, xzr",
+ "msr ttbr0_el1, xzr",
+ // Ensure that CMOs have completed before entering payload.
+ "dsb nsh",
+ "br x30",
+ sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
+ bcc = in(reg) u64::try_from(bcc.start).unwrap(),
+ bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
+ cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
+ scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
+ scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
+ stack = in(reg) u64::try_from(stack.start.0).unwrap(),
+ stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
+ eh_stack = in(reg) u64::try_from(eh_stack.start.0).unwrap(),
+ eh_stack_end = in(reg) u64::try_from(eh_stack.end.0).unwrap(),
+ dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
+ in("x0") u64::try_from(fdt_address).unwrap(),
+ in("x30") u64::try_from(entrypoint).unwrap(),
+ options(noreturn),
+ );
+ };
+}
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 862fb1d..8ada6a1 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -14,21 +14,18 @@
//! Low-level entry and exit points of pvmfw.
+use crate::arch::payload::jump_to_payload;
use crate::config;
use crate::memory::MemorySlices;
-use core::arch::asm;
-use core::mem::size_of;
use core::slice;
use log::error;
use log::warn;
use log::LevelFilter;
-use vmbase::util::RangeExt as _;
use vmbase::{
- arch::aarch64::min_dcache_line_size,
- configure_heap, console_writeln, layout, limit_stack_size, main,
+ configure_heap, console_writeln, limit_stack_size, main,
memory::{
- deactivate_dynamic_page_tables, map_image_footer, unshare_all_memory,
- unshare_all_mmio_except_uart, unshare_uart, MemoryTrackerError, SIZE_128KB, SIZE_4KB,
+ map_image_footer, unshare_all_memory, unshare_all_mmio_except_uart, unshare_uart,
+ MemoryTrackerError, SIZE_128KB, SIZE_4KB,
},
power::reboot,
};
@@ -173,161 +170,6 @@
}
}
-fn jump_to_payload(entrypoint: usize, slices: &MemorySlices) -> ! {
- let fdt_address = slices.fdt.as_ptr() as usize;
- let bcc = slices
- .dice_chain
- .map(|slice| {
- let r = slice.as_ptr_range();
- (r.start as usize)..(r.end as usize)
- })
- .expect("Missing DICE chain");
-
- deactivate_dynamic_page_tables();
-
- const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
- const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
- // Stage 1 instruction access cacheability is unaffected.
- const SCTLR_EL1_I: u64 = 0b1 << 12;
- // SETEND instruction disabled at EL0 in aarch32 mode.
- const SCTLR_EL1_SED: u64 = 0b1 << 8;
- // Various IT instructions are disabled at EL0 in aarch32 mode.
- const SCTLR_EL1_ITD: u64 = 0b1 << 7;
-
- const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
-
- let scratch = layout::data_bss_range();
-
- assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
- assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
- assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
-
- assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
- assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
- assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
-
- let stack = layout::stack_range();
-
- assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
- assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
- assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
-
- let eh_stack = layout::eh_stack_range();
-
- assert_ne!(eh_stack.end - eh_stack.start, 0, "EH stack region is empty.");
- assert_eq!(eh_stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
- assert_eq!(eh_stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region.");
-
- // Zero all memory that could hold secrets and that can't be safely written to from Rust.
- // Disable the exception vector, caches and page table and then jump to the payload at the
- // given address, passing it the given FDT pointer.
- //
- // SAFETY: We're exiting pvmfw by passing the register values we need to a noreturn asm!().
- unsafe {
- asm!(
- "cmp {scratch}, {bcc}",
- "b.hs 1f",
-
- // Zero .data & .bss until BCC.
- "0: stp xzr, xzr, [{scratch}], 16",
- "cmp {scratch}, {bcc}",
- "b.lo 0b",
-
- "1:",
- // Skip BCC.
- "mov {scratch}, {bcc_end}",
- "cmp {scratch}, {scratch_end}",
- "b.hs 1f",
-
- // Keep zeroing .data & .bss.
- "0: stp xzr, xzr, [{scratch}], 16",
- "cmp {scratch}, {scratch_end}",
- "b.lo 0b",
-
- "1:",
- // Flush d-cache over .data & .bss (including BCC).
- "0: dc cvau, {cache_line}",
- "add {cache_line}, {cache_line}, {dcache_line_size}",
- "cmp {cache_line}, {scratch_end}",
- "b.lo 0b",
-
- "mov {cache_line}, {stack}",
- // Zero stack region.
- "0: stp xzr, xzr, [{stack}], 16",
- "cmp {stack}, {stack_end}",
- "b.lo 0b",
-
- // Flush d-cache over stack region.
- "0: dc cvau, {cache_line}",
- "add {cache_line}, {cache_line}, {dcache_line_size}",
- "cmp {cache_line}, {stack_end}",
- "b.lo 0b",
-
- "mov {cache_line}, {eh_stack}",
- // Zero EH stack region.
- "0: stp xzr, xzr, [{eh_stack}], 16",
- "cmp {eh_stack}, {eh_stack_end}",
- "b.lo 0b",
-
- // Flush d-cache over EH stack region.
- "0: dc cvau, {cache_line}",
- "add {cache_line}, {cache_line}, {dcache_line_size}",
- "cmp {cache_line}, {eh_stack_end}",
- "b.lo 0b",
-
- "msr sctlr_el1, {sctlr_el1_val}",
- "isb",
- "mov x1, xzr",
- "mov x2, xzr",
- "mov x3, xzr",
- "mov x4, xzr",
- "mov x5, xzr",
- "mov x6, xzr",
- "mov x7, xzr",
- "mov x8, xzr",
- "mov x9, xzr",
- "mov x10, xzr",
- "mov x11, xzr",
- "mov x12, xzr",
- "mov x13, xzr",
- "mov x14, xzr",
- "mov x15, xzr",
- "mov x16, xzr",
- "mov x17, xzr",
- "mov x18, xzr",
- "mov x19, xzr",
- "mov x20, xzr",
- "mov x21, xzr",
- "mov x22, xzr",
- "mov x23, xzr",
- "mov x24, xzr",
- "mov x25, xzr",
- "mov x26, xzr",
- "mov x27, xzr",
- "mov x28, xzr",
- "mov x29, xzr",
- "msr ttbr0_el1, xzr",
- // Ensure that CMOs have completed before entering payload.
- "dsb nsh",
- "br x30",
- sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
- bcc = in(reg) u64::try_from(bcc.start).unwrap(),
- bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
- cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
- scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
- scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
- stack = in(reg) u64::try_from(stack.start.0).unwrap(),
- stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
- eh_stack = in(reg) u64::try_from(eh_stack.start.0).unwrap(),
- eh_stack_end = in(reg) u64::try_from(eh_stack.end.0).unwrap(),
- dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
- in("x0") u64::try_from(fdt_address).unwrap(),
- in("x30") u64::try_from(entrypoint).unwrap(),
- options(noreturn),
- );
- };
-}
-
fn get_appended_data_slice() -> Result<&'static mut [u8], MemoryTrackerError> {
let range = map_image_footer()?;
// SAFETY: This region was just mapped for the first time (as map_image_footer() didn't fail)
diff --git a/guest/pvmfw/src/main.rs b/guest/pvmfw/src/main.rs
index afa64e0..9c67be8 100644
--- a/guest/pvmfw/src/main.rs
+++ b/guest/pvmfw/src/main.rs
@@ -19,13 +19,13 @@
extern crate alloc;
+mod arch;
mod bcc;
mod bootargs;
mod config;
mod device_assignment;
mod dice;
mod entry;
-mod exceptions;
mod fdt;
mod gpt;
mod instance;
diff --git a/guest/rialto/src/exceptions.rs b/guest/rialto/src/exceptions.rs
index 8899796..467a3a6 100644
--- a/guest/rialto/src/exceptions.rs
+++ b/guest/rialto/src/exceptions.rs
@@ -15,10 +15,10 @@
//! Exception handlers.
use vmbase::{
- eprintln,
- exceptions::{handle_permission_fault, handle_translation_fault},
- exceptions::{ArmException, Esr, HandleExceptionError},
- logger,
+ arch::aarch64::exceptions::{
+ handle_permission_fault, handle_translation_fault, ArmException, Esr, HandleExceptionError,
+ },
+ eprintln, logger,
power::reboot,
read_sysreg,
};
diff --git a/guest/vmbase_example/src/main.rs b/guest/vmbase_example/src/main.rs
index b7d2f95..8723a55 100644
--- a/guest/vmbase_example/src/main.rs
+++ b/guest/vmbase_example/src/main.rs
@@ -30,11 +30,12 @@
use log::{debug, error, info, trace, warn, LevelFilter};
use spin::mutex::SpinMutex;
use vmbase::{
+ arch::linker,
bionic, configure_heap,
fdt::pci::PciInfo,
generate_image_header,
layout::crosvm::FDT_MAX_SIZE,
- linker, logger, main,
+ logger, main,
memory::{deactivate_dynamic_page_tables, map_data, SIZE_64KB},
};
diff --git a/libs/libvmbase/Android.bp b/libs/libvmbase/Android.bp
index 7465508..2d0294e 100644
--- a/libs/libvmbase/Android.bp
+++ b/libs/libvmbase/Android.bp
@@ -77,14 +77,12 @@
crate_name: "vmbase",
srcs: ["src/lib.rs"],
rustlibs: [
- "libaarch64_paging",
"libbuddy_system_allocator",
"libcfg_if",
"libhypervisor_backends",
"liblibfdt_nostd",
"liblog_rust_nostd",
"libonce_cell_nostd",
- "libsmccc",
"libspin_nostd",
"libstatic_assertions",
"libthiserror_nostd",
@@ -97,21 +95,34 @@
whole_static_libs: [
"librust_baremetal",
],
- // TODO(b/277859415, b/277860860): Drop "compat_android_13".
- features: [
- "compat_android_13",
- "cpu_feat_hafdbs",
- ],
+ target: {
+ android_arm64: {
+ rustlibs: [
+ "libaarch64_paging",
+ "libsmccc",
+ ],
+ // TODO(b/277859415, b/277860860): Drop "compat_android_13".
+ features: [
+ "compat_android_13",
+ "cpu_feat_hafdbs",
+ ],
+ },
+ },
}
cc_library_static {
name: "libvmbase_entry",
defaults: ["vmbase_cc_defaults"],
- srcs: [
- "entry.S",
- "exceptions.S",
- "exceptions_panic.S",
- ],
+ srcs: [],
+ target: {
+ android_arm64: {
+ srcs: [
+ "asm/aarch64/entry.S",
+ "asm/aarch64/exceptions.S",
+ "asm/aarch64/exceptions_panic.S",
+ ],
+ },
+ },
}
filegroup {
diff --git a/libs/libvmbase/common.h b/libs/libvmbase/asm/aarch64/common.h
similarity index 100%
rename from libs/libvmbase/common.h
rename to libs/libvmbase/asm/aarch64/common.h
diff --git a/libs/libvmbase/entry.S b/libs/libvmbase/asm/aarch64/entry.S
similarity index 99%
rename from libs/libvmbase/entry.S
rename to libs/libvmbase/asm/aarch64/entry.S
index 9177a4a..6dffbab 100644
--- a/libs/libvmbase/entry.S
+++ b/libs/libvmbase/asm/aarch64/entry.S
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <common.h>
+#include "common.h"
.set .L_MAIR_DEV_nGnRE, 0x04
.set .L_MAIR_MEM_WBWA, 0xff
diff --git a/libs/libvmbase/exceptions.S b/libs/libvmbase/asm/aarch64/exceptions.S
similarity index 100%
rename from libs/libvmbase/exceptions.S
rename to libs/libvmbase/asm/aarch64/exceptions.S
diff --git a/libs/libvmbase/exceptions_panic.S b/libs/libvmbase/asm/aarch64/exceptions_panic.S
similarity index 98%
rename from libs/libvmbase/exceptions_panic.S
rename to libs/libvmbase/asm/aarch64/exceptions_panic.S
index 54735b2..d064c8d 100644
--- a/libs/libvmbase/exceptions_panic.S
+++ b/libs/libvmbase/asm/aarch64/exceptions_panic.S
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <common.h>
+#include "common.h"
/**
* The following table is intended to trap any fault resulting from the very
diff --git a/libs/libvmbase/src/arch.rs b/libs/libvmbase/src/arch.rs
index 0348800..cc42939 100644
--- a/libs/libvmbase/src/arch.rs
+++ b/libs/libvmbase/src/arch.rs
@@ -17,6 +17,27 @@
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::platform;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::layout;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::linker;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::dbm;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::rand;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::uart;
+
+#[cfg(target_arch = "aarch64")]
+pub use aarch64_paging::paging::VirtualAddress;
+
/// Write with well-defined compiled behavior.
///
/// See https://github.com/rust-lang/rust/issues/131894
@@ -44,7 +65,6 @@
let line_size = aarch64::min_dcache_line_size();
let end = start + size;
let start = crate::util::unchecked_align_down(start, line_size);
-
for line in (start..end).step_by(line_size) {
crate::dc!("cvau", line);
}
diff --git a/libs/libvmbase/src/arch/aarch64.rs b/libs/libvmbase/src/arch/aarch64.rs
index 5006aca..9c4d256 100644
--- a/libs/libvmbase/src/arch/aarch64.rs
+++ b/libs/libvmbase/src/arch/aarch64.rs
@@ -14,6 +14,16 @@
//! Wrappers of assembly calls.
+pub mod dbm;
+pub mod exceptions;
+pub mod hvc;
+pub mod layout;
+pub mod linker;
+pub mod page_table;
+pub mod platform;
+pub mod rand;
+pub mod uart;
+
/// Reads a value from a system register.
#[macro_export]
macro_rules! read_sysreg {
diff --git a/libs/libvmbase/src/memory/dbm.rs b/libs/libvmbase/src/arch/aarch64/dbm.rs
similarity index 93%
rename from libs/libvmbase/src/memory/dbm.rs
rename to libs/libvmbase/src/arch/aarch64/dbm.rs
index de43403..1c2190c 100644
--- a/libs/libvmbase/src/memory/dbm.rs
+++ b/libs/libvmbase/src/arch/aarch64/dbm.rs
@@ -14,14 +14,14 @@
//! Hardware management of the access flag and dirty state.
-use super::page_table::PageTable;
+use crate::arch::aarch64::page_table::PageTable;
use crate::arch::flush_region;
use crate::{dsb, isb, read_sysreg, tlbi, write_sysreg};
use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion};
/// Sets whether the hardware management of access and dirty state is enabled with
/// the given boolean.
-pub(super) fn set_dbm_enabled(enabled: bool) {
+pub fn set_dbm_enabled(enabled: bool) {
if !dbm_available() {
return;
}
@@ -49,8 +49,9 @@
read_sysreg!("id_aa64mmfr1_el1") & DBM_AVAILABLE != 0
}
+#[allow(clippy::result_unit_err)]
/// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
-pub(super) fn flush_dirty_range(
+pub fn flush_dirty_range(
va_range: &MemoryRegion,
desc: &Descriptor,
_level: usize,
@@ -62,9 +63,10 @@
Ok(())
}
+#[allow(clippy::result_unit_err)]
/// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
/// in software to handle permission faults on read-only descriptors.
-pub(super) fn mark_dirty_block(
+pub fn mark_dirty_block(
va_range: &MemoryRegion,
desc: &mut Descriptor,
_level: usize,
diff --git a/libs/libvmbase/src/exceptions.rs b/libs/libvmbase/src/arch/aarch64/exceptions.rs
similarity index 97%
rename from libs/libvmbase/src/exceptions.rs
rename to libs/libvmbase/src/arch/aarch64/exceptions.rs
index b04cb16..1868bf7 100644
--- a/libs/libvmbase/src/exceptions.rs
+++ b/libs/libvmbase/src/arch/aarch64/exceptions.rs
@@ -14,13 +14,11 @@
//! Helper functions and structs for exception handlers.
+use crate::memory::{MemoryTrackerError, MEMORY};
use crate::{
- eprintln,
- layout::UART_PAGE_ADDR,
- memory::{page_4kb_of, MemoryTrackerError, MEMORY},
+ arch::aarch64::layout::UART_PAGE_ADDR, arch::VirtualAddress, eprintln, memory::page_4kb_of,
read_sysreg,
};
-use aarch64_paging::paging::VirtualAddress;
use core::fmt;
use core::result;
@@ -99,6 +97,7 @@
}
}
}
+
/// A struct representing an Armv8 exception.
pub struct ArmException {
/// The value of the exception syndrome register.
diff --git a/libs/libvmbase/src/hvc.rs b/libs/libvmbase/src/arch/aarch64/hvc.rs
similarity index 83%
rename from libs/libvmbase/src/hvc.rs
rename to libs/libvmbase/src/arch/aarch64/hvc.rs
index 1197143..b20f62d 100644
--- a/libs/libvmbase/src/hvc.rs
+++ b/libs/libvmbase/src/arch/aarch64/hvc.rs
@@ -14,6 +14,7 @@
//! Wrappers around calls to the hypervisor.
+/// TRNG ARM specific module
pub mod trng;
use self::trng::Error;
use smccc::{
@@ -21,12 +22,18 @@
hvc64,
};
+/// ARM HVC call number that will return TRNG version
const ARM_SMCCC_TRNG_VERSION: u32 = 0x8400_0050;
+/// ARM TRNG feature hypercall number
const ARM_SMCCC_TRNG_FEATURES: u32 = 0x8400_0051;
#[allow(dead_code)]
+/// ARM SMCC TRNG get uuid hypercall number
const ARM_SMCCC_TRNG_GET_UUID: u32 = 0x8400_0052;
#[allow(dead_code)]
+/// ARM SMCC TRNG 32BIT random hypercall number
const ARM_SMCCC_TRNG_RND32: u32 = 0x8400_0053;
+
+/// ARM SMCCC 64BIT random hypercall number
pub const ARM_SMCCC_TRNG_RND64: u32 = 0xc400_0053;
/// Returns the (major, minor) version tuple, as defined by the SMCCC TRNG.
@@ -37,8 +44,10 @@
(version as u32 as i32).try_into()
}
+/// Buffer for random number
pub type TrngRng64Entropy = [u64; 3];
+/// Return hardware backed entropy from TRNG
pub fn trng_rnd64(nbits: u64) -> trng::Result<TrngRng64Entropy> {
let mut args = [0u64; 17];
args[0] = nbits;
@@ -49,6 +58,7 @@
Ok([regs[1], regs[2], regs[3]])
}
+/// Return TRNG feature
pub fn trng_features(fid: u32) -> trng::Result<u64> {
let mut args = [0u64; 17];
args[0] = fid as u64;
diff --git a/libs/libvmbase/src/hvc/trng.rs b/libs/libvmbase/src/arch/aarch64/hvc/trng.rs
similarity index 97%
rename from libs/libvmbase/src/hvc/trng.rs
rename to libs/libvmbase/src/arch/aarch64/hvc/trng.rs
index efb86f6..26a515e 100644
--- a/libs/libvmbase/src/hvc/trng.rs
+++ b/libs/libvmbase/src/arch/aarch64/hvc/trng.rs
@@ -54,12 +54,15 @@
}
}
+/// Local result alias
pub type Result<T> = result::Result<T, Error>;
/// A version of the SMCCC TRNG interface.
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Version {
+ /// Version majon number
pub major: u16,
+ /// Version minor number
pub minor: u16,
}
diff --git a/libs/libvmbase/src/layout/crosvm.rs b/libs/libvmbase/src/arch/aarch64/layout.rs
similarity index 62%
rename from libs/libvmbase/src/layout/crosvm.rs
rename to libs/libvmbase/src/arch/aarch64/layout.rs
index 39a8147..fe72919 100644
--- a/libs/libvmbase/src/layout/crosvm.rs
+++ b/libs/libvmbase/src/arch/aarch64/layout.rs
@@ -16,7 +16,9 @@
//!
//! https://crosvm.dev/book/appendix/memory_layout.html#common-layout
+use crate::memory::page_4kb_of;
use core::ops::Range;
+use static_assertions::const_assert_eq;
/// The start address of MMIO space.
pub const MMIO_START: usize = 0x0;
@@ -33,3 +35,18 @@
/// Size of the FDT region as defined by crosvm, both in kernel and BIOS modes.
pub const FDT_MAX_SIZE: usize = 2 << 20;
+
+/// First address that can't be translated by a level 1 TTBR0_EL1.
+pub const MAX_VIRT_ADDR: usize = 1 << 40;
+
+/// Base memory-mapped addresses of the UART devices.
+///
+/// See SERIAL_ADDR in https://crosvm.dev/book/appendix/memory_layout.html#common-layout.
+pub const UART_ADDRESSES: [usize; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
+
+/// Address of the single page containing all the UART devices.
+pub const UART_PAGE_ADDR: usize = 0;
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[0]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[1]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[2]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[3]));
diff --git a/libs/libvmbase/src/linker.rs b/libs/libvmbase/src/arch/aarch64/linker.rs
similarity index 100%
rename from libs/libvmbase/src/linker.rs
rename to libs/libvmbase/src/arch/aarch64/linker.rs
diff --git a/libs/libvmbase/src/memory/page_table.rs b/libs/libvmbase/src/arch/aarch64/page_table.rs
similarity index 98%
rename from libs/libvmbase/src/memory/page_table.rs
rename to libs/libvmbase/src/arch/aarch64/page_table.rs
index 62b52ae..5f3ed0a 100644
--- a/libs/libvmbase/src/memory/page_table.rs
+++ b/libs/libvmbase/src/arch/aarch64/page_table.rs
@@ -23,7 +23,7 @@
use core::result;
/// Software bit used to indicate a device that should be lazily mapped.
-pub(super) const MMIO_LAZY_MAP_FLAG: Attributes = Attributes::SWFLAG_0;
+pub const MMIO_LAZY_MAP_FLAG: Attributes = Attributes::SWFLAG_0;
/// We assume that MAIR_EL1.Attr0 = "Device-nGnRE memory" (0b0000_0100)
const DEVICE_NGNRE: Attributes = Attributes::ATTRIBUTE_INDEX_0;
diff --git a/libs/libvmbase/src/arch/aarch64/platform.rs b/libs/libvmbase/src/arch/aarch64/platform.rs
new file mode 100644
index 0000000..6a62f8c
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/platform.rs
@@ -0,0 +1,137 @@
+// Copyright 2022, The Android Open Source Project
+//
+// 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.
+
+//! Definition of platform
+
+use crate::{
+ arch::aarch64::{
+ layout::{UART_ADDRESSES, UART_PAGE_ADDR},
+ uart::Uart,
+ },
+ memory::{SIZE_16KB, SIZE_4KB},
+};
+use smccc::{
+ psci::{system_off, system_reset},
+ Hvc,
+};
+use spin::{mutex::SpinMutex, once::Once};
+use static_assertions::const_assert_eq;
+
+// Arbitrary limit on the number of consoles that can be registered.
+//
+// Matches the UART count in crosvm.
+const MAX_CONSOLES: usize = 4;
+
+static CONSOLES: [Once<SpinMutex<Uart>>; MAX_CONSOLES] =
+ [Once::new(), Once::new(), Once::new(), Once::new()];
+static ADDRESSES: [Once<usize>; MAX_CONSOLES] =
+ [Once::new(), Once::new(), Once::new(), Once::new()];
+
+/// Index of the console used by default for logging.
+pub const DEFAULT_CONSOLE_INDEX: usize = 0;
+
+/// Index of the console used by default for emergency logging.
+pub const DEFAULT_EMERGENCY_CONSOLE_INDEX: usize = DEFAULT_CONSOLE_INDEX;
+
+/// Initialises the global instance(s) of the UART driver.
+///
+/// # Safety
+///
+/// This must be called before using the `print!` and `println!` macros.
+/// The only safe place to execute this function is in rust initialization code.
+///
+/// This must be called once with the bases of UARTs, mapped as device memory and (if necessary)
+/// shared with the host as MMIO, to which no other references must be held.
+pub unsafe fn init_all_uart(base_addresses: &[usize]) {
+ for (i, &base_address) in base_addresses.iter().enumerate() {
+ // Remember the valid address, for emergency console accesses.
+ ADDRESSES[i].call_once(|| base_address);
+
+ // Initialize the console driver, for normal console accesses.
+ assert!(!CONSOLES[i].is_completed(), "console::init() called more than once");
+ // SAFETY: The caller promised that base_address is the base of a mapped UART with no
+ // aliases.
+ CONSOLES[i].call_once(|| SpinMutex::new(unsafe { Uart::new(base_address) }));
+ }
+}
+
+/// Initialize console by mapping MMIO memory
+pub fn map_uarts_mmio() -> Result<(), hypervisor_backends::Error> {
+ if let Some(mmio_guard) = hypervisor_backends::get_mmio_guard() {
+ mmio_guard.enroll()?;
+
+ // TODO(ptosi): Use MmioSharer::share() to properly track this MMIO_GUARD_MAP.
+ //
+ // The following call shares the UART but also anything else present in 0..granule.
+ //
+ // For 4KiB, that's only the UARTs. For 16KiB, it also covers the RTC and watchdog but, as
+ // neither is used by vmbase clients (and as both are outside of the UART page), they
+ // will never have valid stage-1 mappings to those devices. As a result, this
+ // MMIO_GUARD_MAP isn't affected by the granule size in any visible way. Larger granule
+ // sizes will need to be checked separately, if needed.
+ assert!({
+ let granule = mmio_guard.granule()?;
+ granule == SIZE_4KB || granule == SIZE_16KB
+ });
+ // Validate the assumption above by ensuring that the UART is not moved to another page:
+ const_assert_eq!(UART_PAGE_ADDR, 0);
+ mmio_guard.map(UART_PAGE_ADDR)?;
+ }
+ Ok(())
+}
+
+/// Initialize platform specific device drivers. If this function fails the reboot is issued.
+pub fn init_console() {
+ if map_uarts_mmio().is_err() {
+ // UART mapping failed platform can't provide any output.
+ // Reboot to prevent printing any message.
+ reboot()
+ }
+ // SAFETY: UART_PAGE is mapped at stage-1 (see entry.S) and was just MMIO-guarded.
+ unsafe { init_all_uart(&UART_ADDRESSES) };
+}
+
+/// Return platform uart with specific index
+///
+/// Panics if console was not initialized by calling [`init`] first.
+pub fn uart(id: usize) -> &'static spin::mutex::SpinMutex<Uart> {
+ return CONSOLES[id].get().unwrap();
+}
+
+/// Reinitializes the emergency UART driver and returns it.
+///
+/// This is intended for use in situations where the UART may be in an unknown state or the global
+/// instance may be locked, such as in an exception handler or panic handler.
+pub fn emergency_uart() -> Uart {
+ // SAFETY: Initialization of UART using dedicated const address.
+ unsafe { Uart::new(UART_ADDRESSES[DEFAULT_EMERGENCY_CONSOLE_INDEX]) }
+}
+
+/// Makes a `PSCI_SYSTEM_OFF` call to shutdown the VM.
+///
+/// Panics if it returns an error.
+pub fn shutdown() -> ! {
+ system_off::<Hvc>().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
+
+/// Makes a `PSCI_SYSTEM_RESET` call to shutdown the VM abnormally.
+///
+/// Panics if it returns an error.
+pub fn reboot() -> ! {
+ system_reset::<Hvc>().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
diff --git a/libs/libvmbase/src/arch/aarch64/rand.rs b/libs/libvmbase/src/arch/aarch64/rand.rs
new file mode 100644
index 0000000..4fd1905
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/rand.rs
@@ -0,0 +1,131 @@
+// Copyright 2025, The Android Open Source Project
+//
+// 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.
+
+//! Random number generator implementation for aarch64 platforms using TRNG
+
+use crate::arch::aarch64::hvc;
+use crate::rand::{Entropy, Error, Result};
+use core::fmt;
+use core::mem::size_of;
+use smccc::{self, Hvc};
+use zerocopy::IntoBytes as _;
+
+/// Error type for rand operations.
+pub enum PlatformError {
+ /// Error during architectural SMCCC call.
+ Smccc(smccc::arch::Error),
+ /// Error during SMCCC TRNG call.
+ Trng(hvc::trng::Error),
+ /// Unsupported SMCCC version.
+ UnsupportedSmcccVersion(smccc::arch::Version),
+ /// Unsupported SMCCC TRNG version.
+ UnsupportedTrngVersion(hvc::trng::Version),
+}
+
+impl From<smccc::arch::Error> for Error {
+ fn from(e: smccc::arch::Error) -> Self {
+ Self::Platform(PlatformError::Smccc(e))
+ }
+}
+
+impl From<hvc::trng::Error> for Error {
+ fn from(e: hvc::trng::Error) -> Self {
+ Self::Platform(PlatformError::Trng(e))
+ }
+}
+
+impl fmt::Display for PlatformError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Smccc(e) => write!(f, "Architectural SMCCC error: {e}"),
+ Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
+ Self::UnsupportedSmcccVersion(v) => write!(f, "Unsupported SMCCC version {v}"),
+ Self::UnsupportedTrngVersion(v) => write!(f, "Unsupported SMCCC TRNG version {v}"),
+ }
+ }
+}
+
+impl fmt::Debug for PlatformError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{self}")
+ }
+}
+
+pub(crate) const MAX_BYTES_PER_CALL: usize = size_of::<u64>() * 3;
+
+/// Configure the source of entropy.
+pub(crate) fn init() -> Result<()> {
+ // SMCCC TRNG requires SMCCC v1.1.
+ match smccc::arch::version::<Hvc>()? {
+ smccc::arch::Version { major: 1, minor } if minor >= 1 => (),
+ version => return Err(PlatformError::UnsupportedSmcccVersion(version).into()),
+ }
+
+ // TRNG_RND requires SMCCC TRNG v1.0.
+ match hvc::trng_version()? {
+ hvc::trng::Version { major: 1, minor: _ } => (),
+ version => return Err(PlatformError::UnsupportedTrngVersion(version).into()),
+ }
+
+ // TRNG_RND64 doesn't define any special capabilities so ignore the successful result.
+ let _ = hvc::trng_features(hvc::ARM_SMCCC_TRNG_RND64).map_err(|e| {
+ if e == hvc::trng::Error::NotSupported {
+ // SMCCC TRNG is currently our only source of entropy.
+ Error::NoEntropySource
+ } else {
+ e.into()
+ }
+ })?;
+
+ Ok(())
+}
+
+/// Returns an array where the first `n_bytes` bytes hold entropy.
+///
+/// The rest of the array should be ignored.
+pub(crate) fn platform_entropy(n_bytes: usize) -> Result<Entropy> {
+ loop {
+ if let Some(entropy) = rnd64(n_bytes)? {
+ return Ok(entropy);
+ }
+ }
+}
+
+/// Returns an array where the first `n_bytes` bytes hold entropy, if available.
+///
+/// The rest of the array should be ignored.
+fn rnd64(n_bytes: usize) -> Result<Option<Entropy>> {
+ let bits = usize::try_from(u8::BITS).unwrap();
+ let result = hvc::trng_rnd64((n_bytes * bits).try_into().unwrap());
+ let entropy = if matches!(result, Err(hvc::trng::Error::NoEntropy)) {
+ None
+ } else {
+ let r = result?;
+ // From the SMCCC TRNG:
+ //
+ // A MAX_BITS-bits wide value (Entropy) is returned across X1 to X3.
+ // The requested conditioned entropy is returned in Entropy[N-1:0].
+ //
+ // X1 Entropy[191:128]
+ // X2 Entropy[127:64]
+ // X3 Entropy[63:0]
+ //
+ // The bits in Entropy[MAX_BITS-1:N] are 0.
+ let reordered = [r[2].to_le(), r[1].to_le(), r[0].to_le()];
+
+ Some(reordered.as_bytes().try_into().unwrap())
+ };
+
+ Ok(entropy)
+}
diff --git a/libs/libvmbase/src/arch/aarch64/uart.rs b/libs/libvmbase/src/arch/aarch64/uart.rs
new file mode 100644
index 0000000..2ef7d7e
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/uart.rs
@@ -0,0 +1,66 @@
+// Copyright 2025, The Android Open Source Project
+//
+// 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.
+
+//! Uart driver with backend for aarch64 using MMIO
+
+use crate::arch::write_volatile_u8;
+use crate::uart::UartBackend;
+
+/// Alias for default Uart for aarch64 backend with [`MmioBackend`]
+pub type Uart = crate::uart::Uart<MmioBackend>;
+
+/// Backend for [`crate::uart::Uart`] that uses [`crate::arch::write_volatile_u8`] for writing to
+/// hardware registers.
+pub struct MmioBackend {
+ base_address: *mut u8,
+}
+
+impl MmioBackend {
+ /// Constructs a new instance of the UART driver backend for a device at the given base address.
+ ///
+ /// # Safety
+ ///
+ /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+ /// 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: usize) -> Self {
+ Self { base_address: base_address as *mut u8 }
+ }
+}
+
+impl UartBackend for MmioBackend {
+ fn write_register_u8(&self, offset: usize, byte: u8) {
+ // SAFETY: We know that the base address points to the control registers of a UART device
+ // which is appropriately mapped.
+ unsafe { write_volatile_u8(self.base_address.add(offset), byte) }
+ }
+}
+
+impl Uart {
+ /// Constructs a new instance of the UART driver for a device at the given base address.
+ ///
+ /// # Safety
+ ///
+ /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+ /// 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: usize) -> Self {
+ // SAFETY: Delegated to caller
+ unsafe { Self::create(MmioBackend::new(base_address)) }
+ }
+}
+
+// SAFETY: `MmioBackend` just contains a pointer to device memory, which can be accessed from any
+// context.
+unsafe impl Send for MmioBackend {}
diff --git a/libs/libvmbase/src/console.rs b/libs/libvmbase/src/console.rs
index 7b01bb6..6d9a4fe 100644
--- a/libs/libvmbase/src/console.rs
+++ b/libs/libvmbase/src/console.rs
@@ -14,67 +14,25 @@
//! Console driver for 8250 UART.
-use crate::uart::Uart;
+use crate::arch::platform;
use core::fmt::{write, Arguments, Write};
-use spin::{mutex::SpinMutex, Once};
-
-// Arbitrary limit on the number of consoles that can be registered.
-//
-// Matches the UART count in crosvm.
-const MAX_CONSOLES: usize = 4;
-
-static CONSOLES: [Once<SpinMutex<Uart>>; MAX_CONSOLES] =
- [Once::new(), Once::new(), Once::new(), Once::new()];
-static ADDRESSES: [Once<usize>; MAX_CONSOLES] =
- [Once::new(), Once::new(), Once::new(), Once::new()];
-
-/// Index of the console used by default for logging.
-pub const DEFAULT_CONSOLE_INDEX: usize = 0;
-
-/// Index of the console used by default for emergency logging.
-pub const DEFAULT_EMERGENCY_CONSOLE_INDEX: usize = DEFAULT_CONSOLE_INDEX;
-
-/// Initialises the global instance(s) of the UART driver.
-///
-/// This must be called before using the `print!` and `println!` macros.
-///
-/// # Safety
-///
-/// This must be called once with the bases of UARTs, mapped as device memory and (if necessary)
-/// shared with the host as MMIO, to which no other references must be held.
-pub unsafe fn init(base_addresses: &[usize]) {
- for (i, &base_address) in base_addresses.iter().enumerate() {
- // Remember the valid address, for emergency console accesses.
- ADDRESSES[i].call_once(|| base_address);
-
- // Initialize the console driver, for normal console accesses.
- assert!(!CONSOLES[i].is_completed(), "console::init() called more than once");
- // SAFETY: The caller promised that base_address is the base of a mapped UART with no
- // aliases.
- CONSOLES[i].call_once(|| SpinMutex::new(unsafe { Uart::new(base_address) }));
- }
-}
/// Writes a formatted string followed by a newline to the n-th console.
///
/// Panics if the n-th console was not initialized by calling [`init`] first.
pub fn writeln(n: usize, format_args: Arguments) {
- let uart = &mut *CONSOLES[n].get().unwrap().lock();
-
+ let uart = &mut *platform::uart(n).lock();
write(uart, format_args).unwrap();
let _ = uart.write_str("\n");
}
-/// Reinitializes the n-th UART driver and writes a formatted string followed by a newline to it.
+/// Reinitializes the emergency UART driver and writes a formatted string followed by a newline to
+/// it.
///
/// This is intended for use in situations where the UART may be in an unknown state or the global
/// instance may be locked, such as in an exception handler or panic handler.
-pub fn ewriteln(n: usize, format_args: Arguments) {
- let Some(addr) = ADDRESSES[n].get() else { return };
-
- // SAFETY: addr contains the base of a mapped UART, passed in init().
- let mut uart = unsafe { Uart::new(*addr) };
-
+pub fn ewriteln(format_args: Arguments) {
+ let mut uart = platform::emergency_uart();
let _ = write(&mut uart, format_args);
let _ = uart.write_str("\n");
}
@@ -98,7 +56,7 @@
/// use `eprintln!` instead.
macro_rules! println {
($($arg:tt)*) => ({
- $crate::console::console_writeln!($crate::console::DEFAULT_CONSOLE_INDEX, $($arg)*)
+ $crate::console::console_writeln!($crate::arch::platform::DEFAULT_CONSOLE_INDEX, $($arg)*)
})
}
@@ -111,6 +69,6 @@
#[macro_export]
macro_rules! eprintln {
($($arg:tt)*) => ({
- $crate::console::ewriteln($crate::console::DEFAULT_EMERGENCY_CONSOLE_INDEX, format_args!($($arg)*))
+ $crate::console::ewriteln(format_args!($($arg)*))
})
}
diff --git a/libs/libvmbase/src/entry.rs b/libs/libvmbase/src/entry.rs
index b681aea..5c74753 100644
--- a/libs/libvmbase/src/entry.rs
+++ b/libs/libvmbase/src/entry.rs
@@ -15,54 +15,20 @@
//! Rust entry point.
use crate::{
- bionic, console, heap,
- layout::{UART_ADDRESSES, UART_PAGE_ADDR},
- logger,
- memory::{switch_to_dynamic_page_tables, PAGE_SIZE, SIZE_16KB, SIZE_4KB},
- power::{reboot, shutdown},
+ arch::platform,
+ bionic, heap, logger,
+ memory::{switch_to_dynamic_page_tables, PAGE_SIZE, SIZE_4KB},
+ power::shutdown,
rand,
};
use core::mem::size_of;
-use hypervisor_backends::{get_mmio_guard, Error};
-use static_assertions::const_assert_eq;
-
-fn try_console_init() -> Result<(), Error> {
- if let Some(mmio_guard) = get_mmio_guard() {
- mmio_guard.enroll()?;
-
- // TODO(ptosi): Use MmioSharer::share() to properly track this MMIO_GUARD_MAP.
- //
- // The following call shares the UART but also anything else present in 0..granule.
- //
- // For 4KiB, that's only the UARTs. For 16KiB, it also covers the RTC and watchdog but, as
- // neither is used by vmbase clients (and as both are outside of the UART page), they
- // will never have valid stage-1 mappings to those devices. As a result, this
- // MMIO_GUARD_MAP isn't affected by the granule size in any visible way. Larger granule
- // sizes will need to be checked separately, if needed.
- assert!({
- let granule = mmio_guard.granule()?;
- granule == SIZE_4KB || granule == SIZE_16KB
- });
- // Validate the assumption above by ensuring that the UART is not moved to another page:
- const_assert_eq!(UART_PAGE_ADDR, 0);
- mmio_guard.map(UART_PAGE_ADDR)?;
- }
-
- // SAFETY: UART_PAGE is mapped at stage-1 (see entry.S) and was just MMIO-guarded.
- unsafe { console::init(&UART_ADDRESSES) };
-
- Ok(())
-}
/// This is the entry point to the Rust code, called from the binary entry point in `entry.S`.
#[no_mangle]
extern "C" fn rust_entry(x0: u64, x1: u64, x2: u64, x3: u64) -> ! {
heap::init();
-
- if try_console_init().is_err() {
- // Don't panic (or log) here to avoid accessing the console.
- reboot()
- }
+ // Initialize platform drivers
+ platform::init_console();
logger::init().expect("Failed to initialize the logger");
// We initialize the logger to Off (like the log crate) and clients should log::set_max_level.
@@ -72,7 +38,7 @@
// We keep a null byte at the top of the stack guard to act as a string terminator.
let random_guard = &mut stack_guard[..(SIZE_OF_STACK_GUARD - 1)];
- if let Err(e) = rand::init() {
+ if let Err(e) = crate::arch::rand::init() {
panic!("Failed to initialize a source of entropy: {e}");
}
diff --git a/libs/libvmbase/src/layout.rs b/libs/libvmbase/src/layout.rs
index 4c45eb2..8bc2319 100644
--- a/libs/libvmbase/src/layout.rs
+++ b/libs/libvmbase/src/layout.rs
@@ -16,34 +16,24 @@
#![allow(unused_unsafe)]
-pub mod crosvm;
-
-use crate::linker::__stack_chk_guard;
-use crate::memory::{max_stack_size, page_4kb_of, PAGE_SIZE};
-use aarch64_paging::paging::VirtualAddress;
+#[cfg(target_arch = "aarch64")]
+use crate::arch::aarch64::linker::__stack_chk_guard;
+use crate::arch::VirtualAddress;
+use crate::memory::{max_stack_size, PAGE_SIZE};
use core::ops::Range;
-use static_assertions::const_assert_eq;
+
+#[cfg(target_arch = "aarch64")]
+pub use crate::arch::aarch64::layout as crosvm;
/// First address that can't be translated by a level 1 TTBR0_EL1.
pub const MAX_VIRT_ADDR: usize = 1 << 40;
-/// Base memory-mapped addresses of the UART devices.
-///
-/// See SERIAL_ADDR in https://crosvm.dev/book/appendix/memory_layout.html#common-layout.
-pub const UART_ADDRESSES: [usize; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
-
-/// Address of the single page containing all the UART devices.
-pub const UART_PAGE_ADDR: usize = 0;
-const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[0]));
-const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[1]));
-const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[2]));
-const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[3]));
-
/// Get an address from a linker-defined symbol.
#[macro_export]
macro_rules! linker_addr {
($symbol:ident) => {{
- let addr = (&raw const $crate::linker::$symbol) as usize;
+ #[cfg(target_arch = "aarch64")]
+ let addr = (&raw const $crate::arch::aarch64::linker::$symbol) as usize;
VirtualAddress(addr)
}};
}
@@ -54,7 +44,6 @@
($begin:ident,$end:ident) => {{
let start = linker_addr!($begin);
let end = linker_addr!($end);
-
start..end
}};
}
@@ -110,8 +99,9 @@
}
/// Range of the page at UART_PAGE_ADDR of PAGE_SIZE.
+#[cfg(target_arch = "aarch64")]
pub fn console_uart_page() -> Range<VirtualAddress> {
- VirtualAddress(UART_PAGE_ADDR)..VirtualAddress(UART_PAGE_ADDR + PAGE_SIZE)
+ VirtualAddress(crosvm::UART_PAGE_ADDR)..VirtualAddress(crosvm::UART_PAGE_ADDR + PAGE_SIZE)
}
/// Read-write data (original).
diff --git a/libs/libvmbase/src/lib.rs b/libs/libvmbase/src/lib.rs
index 431e899..d254038 100644
--- a/libs/libvmbase/src/lib.rs
+++ b/libs/libvmbase/src/lib.rs
@@ -22,12 +22,9 @@
pub mod bionic;
pub mod console;
mod entry;
-pub mod exceptions;
pub mod fdt;
pub mod heap;
-mod hvc;
pub mod layout;
-pub mod linker;
pub mod logger;
pub mod memory;
pub mod power;
diff --git a/libs/libvmbase/src/memory.rs b/libs/libvmbase/src/memory.rs
index 9153706..afd70aa 100644
--- a/libs/libvmbase/src/memory.rs
+++ b/libs/libvmbase/src/memory.rs
@@ -14,22 +14,23 @@
//! Memory management.
-mod dbm;
mod error;
-mod page_table;
mod shared;
mod stack;
mod tracker;
mod util;
pub use error::MemoryTrackerError;
-pub use page_table::PageTable;
pub use shared::MemoryRange;
pub use tracker::{
deactivate_dynamic_page_tables, init_shared_pool, map_data, map_data_noflush, map_device,
map_image_footer, map_rodata, map_rodata_outside_main_memory, resize_available_memory,
unshare_all_memory, unshare_all_mmio_except_uart, unshare_uart,
};
+
+#[cfg(target_arch = "aarch64")]
+pub use crate::arch::aarch64::page_table::PageTable;
+
pub use util::{
flush, flushed_zeroize, page_4kb_of, PAGE_SIZE, SIZE_128KB, SIZE_16KB, SIZE_2MB, SIZE_4KB,
SIZE_4MB, SIZE_64KB,
diff --git a/libs/libvmbase/src/memory/shared.rs b/libs/libvmbase/src/memory/shared.rs
index 7e5e7e9..ce57793 100644
--- a/libs/libvmbase/src/memory/shared.rs
+++ b/libs/libvmbase/src/memory/shared.rs
@@ -16,9 +16,10 @@
use super::error::MemoryTrackerError;
use super::util::virt_to_phys;
+use crate::arch::VirtualAddress;
use crate::layout;
use crate::util::unchecked_align_down;
-use aarch64_paging::paging::{MemoryRegion as VaRange, VirtualAddress, PAGE_SIZE};
+use aarch64_paging::paging::{MemoryRegion as VaRange, PAGE_SIZE};
use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error};
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
@@ -74,7 +75,7 @@
let base = unchecked_align_down(phys, self.granule);
// TODO(ptosi): Share the UART using this method and remove the hardcoded check.
- if self.frames.contains(&base) || base == layout::UART_PAGE_ADDR {
+ if self.frames.contains(&base) || base == layout::crosvm::UART_PAGE_ADDR {
return Err(MemoryTrackerError::DuplicateMmioShare(base));
}
diff --git a/libs/libvmbase/src/memory/tracker.rs b/libs/libvmbase/src/memory/tracker.rs
index bbff254..cdaae55 100644
--- a/libs/libvmbase/src/memory/tracker.rs
+++ b/libs/libvmbase/src/memory/tracker.rs
@@ -14,15 +14,16 @@
//! Memory management.
-use super::dbm::{flush_dirty_range, mark_dirty_block, set_dbm_enabled};
use super::error::MemoryTrackerError;
-use super::page_table::{PageTable, MMIO_LAZY_MAP_FLAG};
use super::shared::{SHARED_MEMORY, SHARED_POOL};
+use crate::arch::aarch64::page_table::{PageTable, MMIO_LAZY_MAP_FLAG};
+use crate::arch::dbm::{flush_dirty_range, mark_dirty_block, set_dbm_enabled};
+use crate::arch::VirtualAddress;
use crate::dsb;
use crate::layout;
use crate::memory::shared::{MemoryRange, MemorySharer, MmioSharer};
use crate::util::RangeExt as _;
-use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange, VirtualAddress};
+use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange};
use alloc::boxed::Box;
use buddy_system_allocator::LockedFrameAllocator;
use core::mem::size_of;
@@ -119,7 +120,7 @@
/// Unshare the UART page, previously shared with the host.
pub fn unshare_uart() -> Result<()> {
let Some(mmio_guard) = get_mmio_guard() else { return Ok(()) };
- Ok(mmio_guard.unmap(layout::UART_PAGE_ADDR)?)
+ Ok(mmio_guard.unmap(layout::crosvm::UART_PAGE_ADDR)?)
}
/// Map the provided range as normal memory, with R/W permissions.
diff --git a/libs/libvmbase/src/power.rs b/libs/libvmbase/src/power.rs
index 9240acf..e3461c5 100644
--- a/libs/libvmbase/src/power.rs
+++ b/libs/libvmbase/src/power.rs
@@ -13,26 +13,18 @@
// limitations under the License.
//! Functions for shutting down the VM.
+use crate::arch::platform;
-use smccc::{
- psci::{system_off, system_reset},
- Hvc,
-};
-
-/// Makes a `PSCI_SYSTEM_OFF` call to shutdown the VM.
+/// Call shutdown VM using platform specific code.
///
/// Panics if it returns an error.
pub fn shutdown() -> ! {
- system_off::<Hvc>().unwrap();
- #[allow(clippy::empty_loop)]
- loop {}
+ platform::shutdown();
}
-/// Makes a `PSCI_SYSTEM_RESET` call to shutdown the VM abnormally.
+/// Call reboot VM using platform specific code.
///
/// Panics if it returns an error.
pub fn reboot() -> ! {
- system_reset::<Hvc>().unwrap();
- #[allow(clippy::empty_loop)]
- loop {}
+ platform::reboot();
}
diff --git a/libs/libvmbase/src/rand.rs b/libs/libvmbase/src/rand.rs
index 16c7b6a..e4b8c63 100644
--- a/libs/libvmbase/src/rand.rs
+++ b/libs/libvmbase/src/rand.rs
@@ -14,37 +14,23 @@
//! Functions and drivers for obtaining true entropy.
-use crate::hvc;
+use crate::arch::rand::{platform_entropy, PlatformError, MAX_BYTES_PER_CALL};
use core::fmt;
-use core::mem::size_of;
-use smccc::{self, Hvc};
-use zerocopy::IntoBytes as _;
-type Entropy = [u8; size_of::<u64>() * 3];
+pub(crate) type Entropy = [u8; MAX_BYTES_PER_CALL];
/// Error type for rand operations.
pub enum Error {
/// No source of entropy found.
NoEntropySource,
- /// Error during architectural SMCCC call.
- Smccc(smccc::arch::Error),
- /// Error during SMCCC TRNG call.
- Trng(hvc::trng::Error),
- /// Unsupported SMCCC version.
- UnsupportedSmcccVersion(smccc::arch::Version),
- /// Unsupported SMCCC TRNG version.
- UnsupportedTrngVersion(hvc::trng::Version),
+
+ /// Platform specific error
+ Platform(PlatformError),
}
-impl From<smccc::arch::Error> for Error {
- fn from(e: smccc::arch::Error) -> Self {
- Self::Smccc(e)
- }
-}
-
-impl From<hvc::trng::Error> for Error {
- fn from(e: hvc::trng::Error) -> Self {
- Self::Trng(e)
+impl From<PlatformError> for Error {
+ fn from(e: PlatformError) -> Self {
+ Error::Platform(e)
}
}
@@ -55,10 +41,7 @@
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::NoEntropySource => write!(f, "No source of entropy available"),
- Self::Smccc(e) => write!(f, "Architectural SMCCC error: {e}"),
- Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
- Self::UnsupportedSmcccVersion(v) => write!(f, "Unsupported SMCCC version {v}"),
- Self::UnsupportedTrngVersion(v) => write!(f, "Unsupported SMCCC TRNG version {v}"),
+ Self::Platform(e) => write!(f, "Platform error: {e}"),
}
}
}
@@ -69,84 +52,16 @@
}
}
-/// Configure the source of entropy.
-pub(crate) fn init() -> Result<()> {
- // SMCCC TRNG requires SMCCC v1.1.
- match smccc::arch::version::<Hvc>()? {
- smccc::arch::Version { major: 1, minor } if minor >= 1 => (),
- version => return Err(Error::UnsupportedSmcccVersion(version)),
- }
-
- // TRNG_RND requires SMCCC TRNG v1.0.
- match hvc::trng_version()? {
- hvc::trng::Version { major: 1, minor: _ } => (),
- version => return Err(Error::UnsupportedTrngVersion(version)),
- }
-
- // TRNG_RND64 doesn't define any special capabilities so ignore the successful result.
- let _ = hvc::trng_features(hvc::ARM_SMCCC_TRNG_RND64).map_err(|e| {
- if e == hvc::trng::Error::NotSupported {
- // SMCCC TRNG is currently our only source of entropy.
- Error::NoEntropySource
- } else {
- e.into()
- }
- })?;
-
- Ok(())
-}
-
/// Fills a slice of bytes with true entropy.
pub fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
- const MAX_BYTES_PER_CALL: usize = size_of::<Entropy>();
-
for chunk in s.chunks_mut(MAX_BYTES_PER_CALL) {
- let entropy = repeat_trng_rnd(chunk.len())?;
+ let entropy = platform_entropy(chunk.len())?;
chunk.clone_from_slice(&entropy[..chunk.len()]);
}
Ok(())
}
-/// Returns an array where the first `n_bytes` bytes hold entropy.
-///
-/// The rest of the array should be ignored.
-fn repeat_trng_rnd(n_bytes: usize) -> Result<Entropy> {
- loop {
- if let Some(entropy) = rnd64(n_bytes)? {
- return Ok(entropy);
- }
- }
-}
-
-/// Returns an array where the first `n_bytes` bytes hold entropy, if available.
-///
-/// The rest of the array should be ignored.
-fn rnd64(n_bytes: usize) -> Result<Option<Entropy>> {
- let bits = usize::try_from(u8::BITS).unwrap();
- let result = hvc::trng_rnd64((n_bytes * bits).try_into().unwrap());
- let entropy = if matches!(result, Err(hvc::trng::Error::NoEntropy)) {
- None
- } else {
- let r = result?;
- // From the SMCCC TRNG:
- //
- // A MAX_BITS-bits wide value (Entropy) is returned across X1 to X3.
- // The requested conditioned entropy is returned in Entropy[N-1:0].
- //
- // X1 Entropy[191:128]
- // X2 Entropy[127:64]
- // X3 Entropy[63:0]
- //
- // The bits in Entropy[MAX_BITS-1:N] are 0.
- let reordered = [r[2].to_le(), r[1].to_le(), r[0].to_le()];
-
- Some(reordered.as_bytes().try_into().unwrap())
- };
-
- Ok(entropy)
-}
-
/// Generate an array of fixed-size initialized with true-random bytes.
pub fn random_array<const N: usize>() -> Result<[u8; N]> {
let mut arr = [0; N];
diff --git a/libs/libvmbase/src/uart.rs b/libs/libvmbase/src/uart.rs
index 427499b..857a22e 100644
--- a/libs/libvmbase/src/uart.rs
+++ b/libs/libvmbase/src/uart.rs
@@ -15,36 +15,33 @@
//! Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
//! provided by crosvm, and won't work with real hardware.
-use crate::arch::write_volatile_u8;
use core::fmt::{self, Write};
+/// The backend for [`Uart`] that abstracts the access to 8250 register map
+pub trait UartBackend {
+ /// Writes a byte value on the offset to the hardware registers.
+ fn write_register_u8(&self, offset: usize, byte: u8);
+}
+
/// Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
/// provided by crosvm, and won't work with real hardware.
-pub struct Uart {
- base_address: *mut u8,
+pub struct Uart<Backend: UartBackend> {
+ backend: Backend,
}
-impl Uart {
- /// Constructs a new instance of the UART driver for a device at the given base address.
- ///
- /// # Safety
- ///
- /// The given base address must point to the 8 MMIO control registers of an appropriate UART
- /// 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: usize) -> Self {
- Self { base_address: base_address as *mut u8 }
+impl<Backend: UartBackend> Uart<Backend> {
+ /// Constructs a new instance of the UART driver with given backend.
+ pub(crate) fn create(backend: Backend) -> Self {
+ Self { backend }
}
/// Writes a single byte to the UART.
pub fn write_byte(&self, byte: u8) {
- // SAFETY: We know that the base address points to the control registers of a UART device
- // which is appropriately mapped.
- unsafe { write_volatile_u8(self.base_address, byte) }
+ self.backend.write_register_u8(0, byte)
}
}
-impl Write for Uart {
+impl<Backend: UartBackend> Write for Uart<Backend> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.as_bytes() {
self.write_byte(*c);
@@ -52,6 +49,3 @@
Ok(())
}
}
-
-// SAFETY: `Uart` just contains a pointer to device memory, which can be accessed from any context.
-unsafe impl Send for Uart {}