Merge "Fix st_blksize type for riscv64" into main
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index fae7e99..8429263 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -40,7 +40,7 @@
name: "apkdmverity.test",
defaults: [
"apkdmverity.defaults",
- "ignorabletest.defaults",
+ "rdroidtest.defaults",
],
test_suites: ["general-tests"],
compile_multilib: "first",
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index 55953a9..d9e9e2b 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -153,12 +153,12 @@
}
#[cfg(test)]
-ignorabletest::test_main!();
+rdroidtest::test_main!();
#[cfg(test)]
mod tests {
use crate::*;
- use ignorabletest::test;
+ use rdroidtest::test;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::ops::Deref;
diff --git a/libs/devicemapper/Android.bp b/libs/devicemapper/Android.bp
index b7cdedc..29f2f5f 100644
--- a/libs/devicemapper/Android.bp
+++ b/libs/devicemapper/Android.bp
@@ -33,7 +33,7 @@
name: "libdm_rust.test",
defaults: [
"libdm_rust.defaults",
- "ignorabletest.defaults",
+ "rdroidtest.defaults",
],
test_suites: ["general-tests"],
rustlibs: [
diff --git a/libs/devicemapper/src/lib.rs b/libs/devicemapper/src/lib.rs
index 0170795..868ac5a 100644
--- a/libs/devicemapper/src/lib.rs
+++ b/libs/devicemapper/src/lib.rs
@@ -233,13 +233,13 @@
}
#[cfg(test)]
-ignorabletest::test_main!();
+rdroidtest::test_main!();
#[cfg(test)]
mod tests {
use super::*;
use crypt::{CipherType, DmCryptTargetBuilder};
- use ignorabletest::test;
+ use rdroidtest::test;
use rustutils::system_properties;
use std::fs::{read, File, OpenOptions};
use std::io::Write;
diff --git a/libs/hyp/src/error.rs b/libs/hyp/src/error.rs
index b8498ca..3fdad70 100644
--- a/libs/hyp/src/error.rs
+++ b/libs/hyp/src/error.rs
@@ -26,7 +26,7 @@
#[derive(Debug, Clone)]
pub enum Error {
/// MMIO guard is not supported.
- MmioGuardNotsupported,
+ MmioGuardNotSupported,
/// Failed to invoke a certain KVM HVC function.
KvmError(KvmError, u32),
/// Failed to invoke GenieZone HVC function.
@@ -40,7 +40,7 @@
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
- Self::MmioGuardNotsupported => write!(f, "MMIO guard is not supported"),
+ Self::MmioGuardNotSupported => write!(f, "MMIO guard is not supported"),
Self::KvmError(e, function_id) => {
write!(f, "Failed to invoke the HVC function with function ID {function_id}: {e}")
}
diff --git a/libs/hyp/src/hypervisor/common.rs b/libs/hyp/src/hypervisor/common.rs
index 7c030a1..70fdd0a 100644
--- a/libs/hyp/src/hypervisor/common.rs
+++ b/libs/hyp/src/hypervisor/common.rs
@@ -14,7 +14,7 @@
//! This module regroups some common traits shared by all the hypervisors.
-use crate::error::Result;
+use crate::error::{Error, Result};
use crate::util::SIZE_4KB;
/// Expected MMIO guard granule size, validated during MMIO guard initialization.
@@ -34,10 +34,9 @@
}
pub trait MmioGuardedHypervisor {
- /// Initializes the hypervisor by enrolling a MMIO guard and checking the memory granule size.
- /// By enrolling, all MMIO will be blocked unless allow-listed with `mmio_guard_map`.
- /// Protected VMs are auto-enrolled.
- fn init(&self) -> Result<()>;
+ /// Enrolls with the MMIO guard so that all MMIO will be blocked unless allow-listed with
+ /// `MmioGuardedHypervisor::map`.
+ fn enroll(&self) -> Result<()>;
/// Maps a page containing the given memory address to the hypervisor MMIO guard.
/// The page size corresponds to the MMIO guard granule size.
@@ -46,6 +45,18 @@
/// Unmaps a page containing the given memory address from the hypervisor MMIO guard.
/// The page size corresponds to the MMIO guard granule size.
fn unmap(&self, addr: usize) -> Result<()>;
+
+ /// Returns the MMIO guard granule size in bytes.
+ fn granule(&self) -> Result<usize>;
+
+ // TODO(ptosi): Fully move granule validation to client code.
+ /// Validates the MMIO guard granule size.
+ fn validate_granule(&self) -> Result<()> {
+ match self.granule()? {
+ MMIO_GUARD_GRANULE_SIZE => Ok(()),
+ granule => Err(Error::UnsupportedMmioGuardGranule(granule)),
+ }
+ }
}
pub trait MemSharingHypervisor {
diff --git a/libs/hyp/src/hypervisor/geniezone.rs b/libs/hyp/src/hypervisor/geniezone.rs
index 24eb89e..ad18e17 100644
--- a/libs/hyp/src/hypervisor/geniezone.rs
+++ b/libs/hyp/src/hypervisor/geniezone.rs
@@ -14,9 +14,7 @@
//! Wrappers around calls to the GenieZone hypervisor.
-use super::common::{
- Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor, MMIO_GUARD_GRANULE_SIZE,
-};
+use super::common::{Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
use crate::error::{Error, Result};
use crate::util::page_address;
use core::fmt::{self, Display, Formatter};
@@ -96,13 +94,15 @@
}
impl MmioGuardedHypervisor for GeniezoneHypervisor {
- fn init(&self) -> Result<()> {
- mmio_guard_enroll()?;
- let mmio_granule = mmio_guard_granule()?;
- if mmio_granule != MMIO_GUARD_GRANULE_SIZE {
- return Err(Error::UnsupportedMmioGuardGranule(mmio_granule));
+ fn enroll(&self) -> Result<()> {
+ let args = [0u64; 17];
+ match success_or_error_64(hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
+ Ok(()) => Ok(()),
+ Err(GeniezoneError::NotSupported) | Err(GeniezoneError::NotRequired) => {
+ Err(Error::MmioGuardNotSupported)
+ }
+ Err(e) => Err(Error::GeniezoneError(e, VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID)),
}
- Ok(())
}
fn map(&self, addr: usize) -> Result<()> {
@@ -118,6 +118,12 @@
checked_hvc64_expect_zero(VENDOR_HYP_GZVM_MMIO_GUARD_UNMAP_FUNC_ID, args)
}
+
+ fn granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
+ Ok(granule.try_into().unwrap())
+ }
}
impl MemSharingHypervisor for GeniezoneHypervisor {
@@ -142,23 +148,6 @@
}
}
-fn mmio_guard_granule() -> Result<usize> {
- let args = [0u64; 17];
-
- let granule = checked_hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
- Ok(granule.try_into().unwrap())
-}
-
-fn mmio_guard_enroll() -> Result<()> {
- let args = [0u64; 17];
- match success_or_error_64(hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
- Ok(_) => Ok(()),
- Err(GeniezoneError::NotSupported) => Err(Error::MmioGuardNotsupported),
- Err(GeniezoneError::NotRequired) => Err(Error::MmioGuardNotsupported),
- Err(e) => Err(Error::GeniezoneError(e, VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID)),
- }
-}
-
fn checked_hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<()> {
success_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::GeniezoneError(e, function))
}
diff --git a/libs/hyp/src/hypervisor/kvm.rs b/libs/hyp/src/hypervisor/kvm.rs
index a95b8de..5835346 100644
--- a/libs/hyp/src/hypervisor/kvm.rs
+++ b/libs/hyp/src/hypervisor/kvm.rs
@@ -14,9 +14,7 @@
//! Wrappers around calls to the KVM hypervisor.
-use super::common::{
- Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor, MMIO_GUARD_GRANULE_SIZE,
-};
+use super::common::{Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
use crate::error::{Error, Result};
use crate::util::page_address;
use core::fmt::{self, Display, Formatter};
@@ -95,13 +93,13 @@
}
impl MmioGuardedHypervisor for ProtectedKvmHypervisor {
- fn init(&self) -> Result<()> {
- mmio_guard_enroll()?;
- let mmio_granule = mmio_guard_granule()?;
- if mmio_granule != MMIO_GUARD_GRANULE_SIZE {
- return Err(Error::UnsupportedMmioGuardGranule(mmio_granule));
+ fn enroll(&self) -> Result<()> {
+ let args = [0u64; 17];
+ match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
+ Ok(()) => Ok(()),
+ Err(KvmError::NotSupported) => Err(Error::MmioGuardNotSupported),
+ Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID)),
}
- Ok(())
}
fn map(&self, addr: usize) -> Result<()> {
@@ -125,6 +123,12 @@
Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID)),
}
}
+
+ fn granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
+ Ok(granule.try_into().unwrap())
+ }
}
impl MemSharingHypervisor for ProtectedKvmHypervisor {
@@ -149,22 +153,6 @@
}
}
-fn mmio_guard_granule() -> Result<usize> {
- let args = [0u64; 17];
-
- let granule = checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
- Ok(granule.try_into().unwrap())
-}
-
-fn mmio_guard_enroll() -> Result<()> {
- let args = [0u64; 17];
- match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
- Ok(_) => Ok(()),
- Err(KvmError::NotSupported) => Err(Error::MmioGuardNotsupported),
- Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID)),
- }
-}
-
fn checked_hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<()> {
success_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::KvmError(e, function))
}
diff --git a/libs/hyp/src/hypervisor/mod.rs b/libs/hyp/src/hypervisor/mod.rs
index bc9e406..309f967 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -60,8 +60,10 @@
GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
RegularKvmHypervisor::UUID => {
- // Protected KVM has the same UUID so differentiate based on MEM_SHARE.
- match ProtectedKvmHypervisor.as_mem_sharer().unwrap().granule() {
+ // Protected KVM has the same UUID as "regular" KVM so issue an HVC that is assumed
+ // to only be supported by pKVM: if it returns SUCCESS, deduce that this is pKVM
+ // and if it returns NOT_SUPPORTED assume that it is "regular" KVM.
+ match ProtectedKvmHypervisor.as_mmio_guard().unwrap().granule() {
Ok(_) => Ok(HypervisorBackend::ProtectedKvm),
Err(Error::KvmError(KvmError::NotSupported, _)) => {
Ok(HypervisorBackend::RegularKvm)
@@ -101,7 +103,7 @@
}
fn detect_hypervisor() -> HypervisorBackend {
- query_vendor_hyp_call_uid().try_into().expect("Unknown hypervisor")
+ query_vendor_hyp_call_uid().try_into().expect("Failed to detect hypervisor")
}
/// Gets the hypervisor singleton.
diff --git a/libs/ignorabletest/Android.bp b/libs/ignorabletest/Android.bp
deleted file mode 100644
index 4ae89af..0000000
--- a/libs/ignorabletest/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-rust_library {
- name: "libignorabletest",
- host_supported: true,
- crate_name: "ignorabletest",
- cargo_env_compat: true,
- cargo_pkg_version: "0.1.0",
- srcs: ["src/lib.rs"],
- edition: "2021",
- rustlibs: [
- "liblibtest_mimic",
- "liblinkme",
- "liblog_rust",
- "liblogger",
- ],
- proc_macros: ["libpaste"],
- apex_available: [
- "//apex_available:platform",
- "//apex_available:anyapex",
- ],
-}
-
-rust_defaults {
- name: "ignorabletest.defaults",
- test_harness: false,
- cfgs: ["test"],
- rustlibs: [
- "libignorabletest",
- "liblinkme",
- ],
- // Without this flag we get linker errors saying to add it. See
- // https://github.com/dtolnay/linkme/issues/49 and related issues.
- ld_flags: [
- "-z",
- "nostart-stop-gc",
- ],
-}
diff --git a/libs/ignorabletest/README.md b/libs/ignorabletest/README.md
deleted file mode 100644
index 77140bd..0000000
--- a/libs/ignorabletest/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# ignorabletest
-
-This is a custom Rust test harness which allows tests to be ignored at runtime based on arbitrary
-criteria. The built-in Rust test harness only allows tests to be ignored at compile time, but this
-is often not enough on Android, where we want to ignore tests based on system properties or other
-characteristics of the device on which the test is being run, which are not known at build time.
-
-## Usage
-
-Unfortunately without the built-in support that rustc provides to the standard test harness, this
-one is slightly more cumbersome to use. Firstly, add it to the `rust_test` build rule in your
-`Android.bp` by adding the defaults provided:
-
-```soong
-rust_test {
- name: "mycrate.test",
- defaults: ["ignorabletest.defaults"],
- // ...
-}
-```
-
-If you are testing a binary that has a `main` function, you'll need to remove it from the test
-build:
-
-```rust
-#[cfg(not(test))]
-fn main() {
- // ...
-}
-```
-
-(If you're testing a library or anything else which doesn't have a `main` function, you don't need
-to worry about this.)
-
-Each test case should be marked with the `ignorabletest::test!` macro, rather than the standard
-`#[test]` attribute:
-
-```rust
-use ignorabletest::test;
-
-test!(one_plus_one);
-fn one_plus_one {
- assert_eq!(1 + 1, 2);
-}
-```
-
-To ignore a test, you can add an `ignore_if` clause with a boolean expression:
-
-```rust
-use ignorabletest::test;
-
-test!(clap_hands, ignore_if: !feeling_happy());
-fn clap_hands {
- assert!(HANDS.clap().is_ok());
-}
-```
-
-Somewhere in your main module, you need to use the `test_main` macro to generate an entry point for
-the test harness:
-
-```rust
-#[cfg(test)]
-ignorabletest::test_main!();
-```
-
-You can then run your tests as usual with `atest`.
diff --git a/libs/ignorabletest/src/lib.rs b/libs/ignorabletest/src/lib.rs
deleted file mode 100644
index c7243e6..0000000
--- a/libs/ignorabletest/src/lib.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-//! Test harness which supports ignoring tests at runtime.
-
-pub mod runner;
-
-#[doc(hidden)]
-pub use libtest_mimic as _libtest_mimic;
-#[doc(hidden)]
-pub use linkme as _linkme;
-#[doc(hidden)]
-pub use paste as _paste;
-
-/// Macro to generate the main function for the test harness.
-#[macro_export]
-macro_rules! test_main {
- () => {
- #[cfg(test)]
- fn main() {
- ignorabletest::runner::main()
- }
- };
-}
-
-/// Macro to generate a wrapper function for a single test.
-///
-/// # Usage
-///
-/// ```
-/// test!(test_string_equality);
-/// fn test_string_equality() {
-/// assert_eq!("", "");
-/// }
-/// ```
-#[macro_export]
-macro_rules! test {
- ($test_name:ident) => {
- $crate::_paste::paste!(
- #[$crate::_linkme::distributed_slice($crate::runner::IGNORABLETEST_TESTS)]
- fn [< __test_ $test_name >]() -> $crate::_libtest_mimic::Trial {
- $crate::_libtest_mimic::Trial::test(
- ::std::stringify!($test_name),
- move || ignorabletest::runner::run($test_name),
- )
- }
- );
- };
- ($test_name:ident, ignore_if: $ignore_expr:expr) => {
- $crate::_paste::paste!(
- #[$crate::_linkme::distributed_slice($crate::runner::IGNORABLETEST_TESTS)]
- fn [< __test_ $test_name >]() -> $crate::_libtest_mimic::Trial {
- $crate::_libtest_mimic::Trial::test(
- ::std::stringify!($test_name),
- move || ignorabletest::runner::run($test_name),
- ).with_ignored_flag($ignore_expr)
- }
- );
- };
-}
diff --git a/libs/ignorabletest/src/runner.rs b/libs/ignorabletest/src/runner.rs
deleted file mode 100644
index fdac406..0000000
--- a/libs/ignorabletest/src/runner.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Test runner.
-
-use core::ops::{Deref, FnOnce};
-use libtest_mimic::{Arguments, Failed, Trial};
-use linkme::distributed_slice;
-use log::Level;
-use std::env;
-
-/// Command-line arguments to ignore, because they are not supported by libtest-mimic.
-const IGNORED_ARGS: [&str; 2] = ["-Zunstable-options", "--report-time"];
-
-/// The collection of all tests to run.
-#[doc(hidden)]
-#[distributed_slice]
-pub static IGNORABLETEST_TESTS: [fn() -> Trial] = [..];
-
-/// Runs all tests.
-pub fn main() {
- logger::init(logger::Config::default().with_min_level(Level::Debug));
- let args = Arguments::from_iter(env::args().filter(|arg| !IGNORED_ARGS.contains(&arg.deref())));
- let tests = IGNORABLETEST_TESTS.iter().map(|test| test()).collect();
- libtest_mimic::run(&args, tests).exit();
-}
-
-/// Runs the given test.
-pub fn run(test: impl FnOnce()) -> Result<(), Failed> {
- test();
- Ok(())
-}
diff --git a/microdroid/kdump/Android.bp b/microdroid/kdump/Android.bp
index cc681a7..b9a18fe 100644
--- a/microdroid/kdump/Android.bp
+++ b/microdroid/kdump/Android.bp
@@ -18,6 +18,9 @@
static_executable: true,
installable: false,
compile_multilib: "64",
+ sanitize: {
+ hwaddress: false, // HWASAN setup fails when run as init process
+ },
}
android_filesystem {
diff --git a/pvmfw/platform.dts b/pvmfw/platform.dts
index 74439d9..cb8e30d 100644
--- a/pvmfw/platform.dts
+++ b/pvmfw/platform.dts
@@ -225,6 +225,8 @@
0x3000 0x0 0x0 1 &intc 0 0 GIC_SPI (IRQ_BASE + 5) IRQ_TYPE_LEVEL_HIGH
0x3800 0x0 0x0 1 &intc 0 0 GIC_SPI (IRQ_BASE + 6) IRQ_TYPE_LEVEL_HIGH
0x4000 0x0 0x0 1 &intc 0 0 GIC_SPI (IRQ_BASE + 7) IRQ_TYPE_LEVEL_HIGH
+ 0x4800 0x0 0x0 1 &intc 0 0 GIC_SPI (IRQ_BASE + 8) IRQ_TYPE_LEVEL_HIGH
+ 0x5000 0x0 0x0 1 &intc 0 0 GIC_SPI (IRQ_BASE + 9) IRQ_TYPE_LEVEL_HIGH
>;
interrupt-map-mask = <0xf800 0x0 0x0 0x7
0xf800 0x0 0x0 0x7
@@ -233,6 +235,8 @@
0xf800 0x0 0x0 0x7
0xf800 0x0 0x0 0x7
0xf800 0x0 0x0 0x7
+ 0xf800 0x0 0x0 0x7
+ 0xf800 0x0 0x0 0x7
0xf800 0x0 0x0 0x7>;
};
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 4e41331..244b192 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -209,7 +209,7 @@
impl PciInfo {
const IRQ_MASK_CELLS: usize = 4;
const IRQ_MAP_CELLS: usize = 10;
- const MAX_IRQS: usize = 8;
+ const MAX_IRQS: usize = 10;
}
type PciAddrRange = AddressRange<(u32, u64), u64, u64>;
@@ -248,14 +248,22 @@
let range1 = ranges.next().ok_or(FdtError::NotFound)?;
let irq_masks = node.getprop_cells(cstr!("interrupt-map-mask"))?.ok_or(FdtError::NotFound)?;
- let irq_masks = CellChunkIterator::<{ PciInfo::IRQ_MASK_CELLS }>::new(irq_masks);
- let irq_masks: ArrayVec<[PciIrqMask; PciInfo::MAX_IRQS]> =
- irq_masks.take(PciInfo::MAX_IRQS).collect();
+ let mut chunks = CellChunkIterator::<{ PciInfo::IRQ_MASK_CELLS }>::new(irq_masks);
+ let irq_masks = (&mut chunks).take(PciInfo::MAX_IRQS).collect();
+
+ if chunks.next().is_some() {
+ warn!("Input DT has more than {} PCI entries!", PciInfo::MAX_IRQS);
+ return Err(FdtError::NoSpace);
+ }
let irq_maps = node.getprop_cells(cstr!("interrupt-map"))?.ok_or(FdtError::NotFound)?;
- let irq_maps = CellChunkIterator::<{ PciInfo::IRQ_MAP_CELLS }>::new(irq_maps);
- let irq_maps: ArrayVec<[PciIrqMap; PciInfo::MAX_IRQS]> =
- irq_maps.take(PciInfo::MAX_IRQS).collect();
+ let mut chunks = CellChunkIterator::<{ PciInfo::IRQ_MAP_CELLS }>::new(irq_maps);
+ let irq_maps = (&mut chunks).take(PciInfo::MAX_IRQS).collect();
+
+ if chunks.next().is_some() {
+ warn!("Input DT has more than {} PCI entries!", PciInfo::MAX_IRQS);
+ return Err(FdtError::NoSpace);
+ }
Ok(PciInfo { ranges: [range0, range1], irq_masks, irq_maps })
}
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 9c512bf..90ba575 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -26,6 +26,7 @@
sdk_version: "test_current",
use_embedded_native_libs: true,
compile_multilib: "64",
+ required: ["perf-setup"],
host_required: ["MicrodroidTestPreparer"],
}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index aed28a8..625f26a 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -78,6 +78,7 @@
private static final String TAG = "MicrodroidBenchmarks";
private static final String METRIC_NAME_PREFIX = getMetricPrefix() + "microdroid/";
private static final int IO_TEST_TRIAL_COUNT = 5;
+ private static final int TEST_TRIAL_COUNT = 5;
private static final long ONE_MEBI = 1024 * 1024;
@Rule public Timeout globalTimeout = Timeout.seconds(300);
@@ -767,4 +768,35 @@
}
reportMetrics(requestLatencies, "latency/vsock", "us");
}
+
+ @Test
+ public void testVmKillTime() throws Exception {
+ VirtualMachineConfig config =
+ newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_io.json")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .build();
+ List<Double> vmKillTime = new ArrayList<>(TEST_TRIAL_COUNT);
+
+ for (int i = 0; i < TEST_TRIAL_COUNT; ++i) {
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_kill_time" + i, config);
+ VmEventListener listener =
+ new VmEventListener() {
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ long start = System.nanoTime();
+ try {
+ vm.stop();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in vm.stop():" + e);
+ throw new RuntimeException(e);
+ }
+ vmKillTime.add((double) (System.nanoTime() - start) / NANO_TO_MICRO);
+ super.onPayloadReady(vm);
+ }
+ };
+ listener.runToFinish(TAG, vm);
+ }
+ reportMetrics(vmKillTime, "vm_kill_time", "microsecond");
+ }
}
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index 8c6218c..f98d1d9 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -60,9 +60,6 @@
// Files that define the "test" instance of CompOS
private static final String COMPOS_TEST_ROOT = "/data/misc/apexdata/com.android.compos/test/";
- private static final String SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME =
- "dalvik.vm.systemservercompilerfilter";
-
private static final String BOOTLOADER_TIME_PROP_NAME = "ro.boot.boottime";
private static final String BOOTLOADER_PREFIX = "bootloader-";
private static final String BOOTLOADER_TIME = "bootloader_time";
@@ -74,7 +71,6 @@
private static final int COMPILE_STAGED_APEX_RETRY_INTERVAL_MS = 10 * 1000;
private static final int COMPILE_STAGED_APEX_TIMEOUT_SEC = 540;
private static final int BOOT_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000;
- private static final double NANOS_IN_SEC = 1_000_000_000.0;
private static final int ROUND_COUNT = 5;
private static final int ROUND_IGNORE_STARTUP_TIME = 3;
private static final String APK_NAME = "MicrodroidTestApp.apk";
@@ -262,32 +258,34 @@
.memoryMib(vm_mem_mb)
.cpuTopology("match_host")
.build(device);
- microdroidDevice.waitForBootComplete(30000);
- microdroidDevice.enableAdbRoot();
-
- CommandRunner microdroid = new CommandRunner(microdroidDevice);
-
- microdroid.run("mkdir -p /mnt/ramdisk && chmod 777 /mnt/ramdisk");
- microdroid.run("mount -t tmpfs -o size=32G tmpfs /mnt/ramdisk");
-
- // Allocate memory for the VM until it fails and make sure that we touch
- // the allocated memory in the guest to be able to create stage2 fragmentation.
try {
- microdroid.tryRun(
- String.format(
- "cd /mnt/ramdisk && truncate -s %dM sprayMemory"
- + " && dd if=/dev/zero of=sprayMemory bs=1MB count=%d",
- vm_mem_mb, vm_mem_mb));
- } catch (Exception ex) {
- }
+ microdroidDevice.waitForBootComplete(30000);
+ microdroidDevice.enableAdbRoot();
- // Run the app during the VM run and collect cold startup time.
- for (int i = 0; i < ROUND_COUNT; i++) {
- AmStartupTimeCmdParser duringVmStartApp = getColdRunStartupTimes(android, pkgName);
- metricColector.addStartupTimeMetricDuringVmRun(duringVmStartApp);
- }
+ CommandRunner microdroid = new CommandRunner(microdroidDevice);
- device.shutdownMicrodroid(microdroidDevice);
+ microdroid.run("mkdir -p /mnt/ramdisk && chmod 777 /mnt/ramdisk");
+ microdroid.run("mount -t tmpfs -o size=32G tmpfs /mnt/ramdisk");
+
+ // Allocate memory for the VM until it fails and make sure that we touch
+ // the allocated memory in the guest to be able to create stage2 fragmentation.
+ try {
+ microdroid.tryRun(
+ String.format(
+ "cd /mnt/ramdisk && truncate -s %dM sprayMemory"
+ + " && dd if=/dev/zero of=sprayMemory bs=1MB count=%d",
+ vm_mem_mb, vm_mem_mb));
+ } catch (Exception expected) {
+ }
+
+ // Run the app during the VM run and collect cold startup time.
+ for (int i = 0; i < ROUND_COUNT; i++) {
+ AmStartupTimeCmdParser duringVmStartApp = getColdRunStartupTimes(android, pkgName);
+ metricColector.addStartupTimeMetricDuringVmRun(duringVmStartApp);
+ }
+ } finally {
+ device.shutdownMicrodroid(microdroidDevice);
+ }
// Run the app after the VM run and collect cold startup time.
for (int i = 0; i < ROUND_COUNT; i++) {
@@ -304,12 +302,12 @@
String[] lines = startAppLog.split("[\r\n]+");
mTotalTime = mWaitTime = 0;
- for (int i = 0; i < lines.length; i++) {
- if (lines[i].contains("TotalTime:")) {
- mTotalTime = Integer.parseInt(lines[i].replaceAll("\\D+", ""));
+ for (String line : lines) {
+ if (line.contains("TotalTime:")) {
+ mTotalTime = Integer.parseInt(line.replaceAll("\\D+", ""));
}
- if (lines[i].contains("WaitTime:")) {
- mWaitTime = Integer.parseInt(lines[i].replaceAll("\\D+", ""));
+ if (line.contains("WaitTime:")) {
+ mWaitTime = Integer.parseInt(line.replaceAll("\\D+", ""));
}
}
}
@@ -365,9 +363,9 @@
String content = android.runForResult("cat /proc/meminfo").getStdout().trim();
String[] lines = content.split("[\r\n]+");
- for (int i = 0; i < lines.length; i++) {
- if (lines[i].contains("MemFree:")) {
- freeMemory = Integer.parseInt(lines[i].replaceAll("\\D+", "")) / 1024;
+ for (String line : lines) {
+ if (line.contains("MemFree:")) {
+ freeMemory = Integer.parseInt(line.replaceAll("\\D+", "")) / 1024;
return freeMemory;
}
}
@@ -416,7 +414,7 @@
CommandRunner android = new CommandRunner(getDevice());
String result = android.run("dmesg");
- Pattern pattern = Pattern.compile("\\[(.*)\\].*sys.boot_completed=1.*");
+ Pattern pattern = Pattern.compile("\\[(.*)].*sys.boot_completed=1.*");
for (String line : result.split("[\r\n]+")) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
@@ -568,7 +566,7 @@
private void compileStagedApex(int timeoutSec) throws Exception {
long timeStart = System.currentTimeMillis();
- long timeEnd = timeStart + timeoutSec * 1000;
+ long timeEnd = timeStart + timeoutSec * 1000L;
while (true) {
@@ -599,7 +597,7 @@
private void reInstallApex(int timeoutSec) throws Exception {
long timeStart = System.currentTimeMillis();
- long timeEnd = timeStart + timeoutSec * 1000;
+ long timeEnd = timeStart + timeoutSec * 1000L;
while (true) {
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index deaf08a..092325e 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -42,7 +42,6 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.TestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
import com.android.tradefed.util.CommandResult;
@@ -62,12 +61,8 @@
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
@@ -75,7 +70,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -95,15 +89,11 @@
private static final int BOOT_COMPLETE_TIMEOUT = 30000; // 30 seconds
- private static final Pattern sCIDPattern = Pattern.compile("with CID (\\d+)");
-
private static class VmInfo {
final Process mProcess;
- final String mCid;
- VmInfo(Process process, String cid) {
+ VmInfo(Process process) {
mProcess = process;
- mCid = cid;
}
}
@@ -376,44 +366,14 @@
PipedInputStream pis = new PipedInputStream();
Process process = RunUtil.getDefault().runCmdInBackground(args, new PipedOutputStream(pis));
- return new VmInfo(process, extractCidFrom(pis));
- }
-
- private static Optional<String> tryExtractCidFrom(String str) {
- Matcher matcher = sCIDPattern.matcher(str);
- if (matcher.find()) {
- return Optional.of(matcher.group(1));
- }
- return Optional.empty();
- }
-
- private static String extractCidFrom(InputStream input) throws IOException {
- String cid = null;
- String line;
- try (BufferedReader out = new BufferedReader(new InputStreamReader(input))) {
- while ((line = out.readLine()) != null) {
- CLog.i("VM output: " + line);
- Optional<String> result = tryExtractCidFrom(line);
- if (result.isPresent()) {
- cid = result.get();
- break;
- }
- }
- }
- assertWithMessage("The output does not contain the expected pattern for CID.")
- .that(cid)
- .isNotNull();
- return cid;
+ return new VmInfo(process);
}
@Test
@CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
public void protectedVmRunsPvmfw() throws Exception {
// Arrange
- boolean protectedVm = true;
- assumeTrue(
- "Skip if protected VMs are not supported",
- getAndroidDevice().supportsMicrodroid(protectedVm));
+ assumeProtectedVmSupported();
final String configPath = "assets/vm_config_apex.json";
// Act
@@ -422,7 +382,7 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(protectedVm)
+ .protectedVm(true)
.build(getAndroidDevice());
// Assert
@@ -441,16 +401,16 @@
@CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
public void protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() throws Exception {
// Arrange
- boolean protectedVm = true;
- assumeTrue(
- "Skip if protected VMs are not supported",
- getAndroidDevice().supportsMicrodroid(protectedVm));
+ assumeProtectedVmSupported();
File key = findTestFile("test.com.android.virt.pem");
// Act
VmInfo vmInfo =
runMicrodroidWithResignedImages(
- key, /*keyOverrides=*/ Map.of(), protectedVm, /*updateBootconfigs=*/ true);
+ key,
+ /*keyOverrides=*/ Map.of(),
+ /*isProtected=*/ true,
+ /*updateBootconfigs=*/ true);
// Assert
vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
@@ -465,6 +425,7 @@
@CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
throws Exception {
+ assumeNonProtectedVmSupported();
File key = findTestFile("test.com.android.virt.pem");
Map<String, File> keyOverrides = Map.of();
VmInfo vmInfo =
@@ -481,6 +442,8 @@
@Test
@CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig() throws Exception {
+ // protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() is the protected case.
+ assumeNonProtectedVmSupported();
// Sign everything with key1 except vbmeta
File key = findTestFile("test.com.android.virt.pem");
// To be able to stop it, it should be a daemon.
@@ -558,10 +521,22 @@
}
@Test
- public void testTombstonesAreGeneratedUponUserspaceCrash() throws Exception {
+ public void testTombstonesAreGeneratedUponUserspaceCrashOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ testTombstonesAreGeneratedUponUserspaceCrash(false);
+ }
+
+ @Test
+ public void testTombstonesAreGeneratedUponUserspaceCrashOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ testTombstonesAreGeneratedUponUserspaceCrash(true);
+ }
+
+ private void testTombstonesAreGeneratedUponUserspaceCrash(boolean protectedVm)
+ throws Exception {
assertThat(
isTombstoneGeneratedWithCmd(
- false,
+ protectedVm,
"assets/vm_config.json",
"kill",
"-SIGSEGV",
@@ -570,10 +545,24 @@
}
@Test
- public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash() throws Exception {
+ public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnNonPvm()
+ throws Exception {
+ assumeNonProtectedVmSupported();
+ testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(false);
+ }
+
+ @Test
+ public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnPvm()
+ throws Exception {
+ assumeProtectedVmSupported();
+ testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(true);
+ }
+
+ private void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(boolean protectedVm)
+ throws Exception {
assertThat(
isTombstoneGeneratedWithCmd(
- false,
+ protectedVm,
"assets/vm_config_no_tombstone.json",
"kill",
"-SIGSEGV",
@@ -597,19 +586,18 @@
@Test
public void testTombstonesAreGeneratedUponKernelCrashOnNonPvm() throws Exception {
- testTombstonesAreGeneratedUponKernelCrash(false);
+ assumeNonProtectedVmSupported();
+ testTombstonesAreGeneratedUponKernelCrash(/* protectedVm=*/ false);
}
@Test
public void testTombstonesAreGeneratedUponKernelCrashOnPvm() throws Exception {
- assumeTrue(
- "Protected VMs are not supported",
- getAndroidDevice().supportsMicrodroid(/*protectedVm=*/ true));
- testTombstonesAreGeneratedUponKernelCrash(true);
+ assumeProtectedVmSupported();
+ testTombstonesAreGeneratedUponKernelCrash(/* protectedVm=*/ true);
}
- private boolean isTombstoneGeneratedWithVmRunApp(boolean debuggable, String... additionalArgs)
- throws Exception {
+ private boolean isTombstoneGeneratedWithVmRunApp(
+ boolean protectedVm, boolean debuggable, String... additionalArgs) throws Exception {
// we can't use microdroid builder as it wants ADB connection (debuggable)
CommandRunner android = new CommandRunner(getDevice());
String testStartTime = android.runWithTimeout(1000, "date", "'+%Y-%m-%d %H:%M:%S.%N'");
@@ -630,44 +618,114 @@
apkPath,
idsigPath,
instanceImgPath));
+ if (protectedVm) {
+ cmd.add("--protected");
+ }
Collections.addAll(cmd, additionalArgs);
android.run(cmd.toArray(new String[0]));
return isTombstoneReceivedFromHostLogcat(testStartTime);
}
- private boolean isTombstoneGeneratedWithCrashPayload(boolean debuggable) throws Exception {
+ private boolean isTombstoneGeneratedWithCrashPayload(boolean protectedVm, boolean debuggable)
+ throws Exception {
return isTombstoneGeneratedWithVmRunApp(
- debuggable, "--payload-binary-name", "MicrodroidCrashNativeLib.so");
+ protectedVm, debuggable, "--payload-binary-name", "MicrodroidCrashNativeLib.so");
}
@Test
- public void testTombstonesAreGeneratedWithCrashPayload() throws Exception {
- assertThat(isTombstoneGeneratedWithCrashPayload(true /* debuggable */)).isTrue();
+ public void testTombstonesAreGeneratedWithCrashPayloadOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashPayload(
+ /*protectedVm=*/ true, /*debuggable=*/ true))
+ .isTrue();
}
@Test
- public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggable() throws Exception {
- assertThat(isTombstoneGeneratedWithCrashPayload(false /* debuggable */)).isFalse();
+ public void testTombstonesAreGeneratedWithCrashPayloadOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashPayload(
+ /*protectedVm=*/ false, /*debuggable=*/ true))
+ .isTrue();
}
- private boolean isTombstoneGeneratedWithCrashConfig(boolean debuggable) throws Exception {
+ @Test
+ public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnPvm()
+ throws Exception {
+ assumeProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashPayload(
+ /*protectedVm=*/ true, /*debuggable=*/ false))
+ .isFalse();
+ }
+
+ @Test
+ public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnNonPvm()
+ throws Exception {
+ assumeNonProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashPayload(
+ /*protectedVm=*/ false, /*debuggable=*/ false))
+ .isFalse();
+ }
+
+ private boolean isTombstoneGeneratedWithCrashConfig(boolean protectedVm, boolean debuggable)
+ throws Exception {
return isTombstoneGeneratedWithVmRunApp(
- debuggable, "--config-path", "assets/vm_config_crash.json");
+ protectedVm, debuggable, "--config-path", "assets/vm_config_crash.json");
}
@Test
- public void testTombstonesAreGeneratedWithCrashConfig() throws Exception {
- assertThat(isTombstoneGeneratedWithCrashConfig(true /* debuggable */)).isTrue();
+ public void testTombstonesAreGeneratedWithCrashConfigOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ assertThat(isTombstoneGeneratedWithCrashConfig(/*protectedVm=*/ true, /*debuggable=*/ true))
+ .isTrue();
}
@Test
- public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggable() throws Exception {
- assertThat(isTombstoneGeneratedWithCrashConfig(false /* debuggable */)).isFalse();
+ public void testTombstonesAreGeneratedWithCrashConfigOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashConfig(
+ /*protectedVm=*/ false, /*debuggable=*/ true))
+ .isTrue();
}
@Test
- public void testTelemetryPushedAtoms() throws Exception {
+ public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnPvm()
+ throws Exception {
+ assumeProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashConfig(
+ /*protectedVm=*/ true, /*debuggable=*/ false))
+ .isFalse();
+ }
+
+ @Test
+ public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnNonPvm()
+ throws Exception {
+ assumeNonProtectedVmSupported();
+ assertThat(
+ isTombstoneGeneratedWithCrashConfig(
+ /*protectedVm=*/ false, /*debuggable=*/ false))
+ .isFalse();
+ }
+
+ @Test
+ public void testTelemetryPushedAtomsOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ testTelemetryPushedAtoms(false);
+ }
+
+ @Test
+ public void testTelemetryPushedAtomsOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ testTelemetryPushedAtoms(true);
+ }
+
+ private void testTelemetryPushedAtoms(boolean protectedVm) throws Exception {
// Reset statsd config and report before the test
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
@@ -688,6 +746,7 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
+ .protectedVm(protectedVm)
.build(device);
microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
device.shutdownMicrodroid(microdroid);
@@ -714,7 +773,7 @@
data.get(0).getAtom().getVmCreationRequested();
assertThat(atomVmCreationRequested.getHypervisor())
.isEqualTo(AtomsProto.VmCreationRequested.Hypervisor.PKVM);
- assertThat(atomVmCreationRequested.getIsProtected()).isFalse();
+ assertThat(atomVmCreationRequested.getIsProtected()).isEqualTo(protectedVm);
assertThat(atomVmCreationRequested.getCreationSucceeded()).isTrue();
assertThat(atomVmCreationRequested.getBinderExceptionCode()).isEqualTo(0);
assertThat(atomVmCreationRequested.getVmIdentifier()).isEqualTo("VmRunApp");
@@ -743,7 +802,19 @@
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
- public void testMicrodroidBoots() throws Exception {
+ public void testMicrodroidBootsOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ testMicrodroidBoots(true);
+ }
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
+ public void testMicrodroidBootsOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ testMicrodroidBoots(false);
+ }
+
+ private void testMicrodroidBoots(boolean protectedVm) throws Exception {
CommandRunner android = new CommandRunner(getDevice());
final String configPath = "assets/vm_config.json"; // path inside the APK
@@ -752,6 +823,7 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
+ .protectedVm(protectedVm)
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
@@ -810,13 +882,25 @@
}
@Test
- public void testMicrodroidRamUsage() throws Exception {
+ public void testMicrodroidRamUsageOnPvm() throws Exception {
+ assumeProtectedVmSupported();
+ testMicrodroidRamUsage(true);
+ }
+
+ @Test
+ public void testMicrodroidRamUsageOnNonPvm() throws Exception {
+ assumeNonProtectedVmSupported();
+ testMicrodroidRamUsage(false);
+ }
+
+ private void testMicrodroidRamUsage(boolean protectedVm) throws Exception {
final String configPath = "assets/vm_config.json";
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
+ .protectedVm(protectedVm)
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
@@ -990,6 +1074,18 @@
"android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
}
+ private void assumeProtectedVmSupported() throws Exception {
+ assumeTrue(
+ "Test skipped because protected VMs are not supported",
+ getAndroidDevice().supportsMicrodroid(true));
+ }
+
+ private void assumeNonProtectedVmSupported() throws Exception {
+ assumeTrue(
+ "Test skipped because non-protected VMs are not supported",
+ getAndroidDevice().supportsMicrodroid(false));
+ }
+
private TestDevice getAndroidDevice() {
TestDevice androidDevice = (TestDevice) getDevice();
assertThat(androidDevice).isNotNull();
diff --git a/vm/src/main.rs b/vm/src/main.rs
index d7c2c4d..0c99acb 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -82,8 +82,8 @@
#[clap(long)]
log: Option<PathBuf>,
- /// Debug level of the VM. Supported values: "none" (default), and "full".
- #[clap(long, default_value = "none", value_parser = parse_debug_level)]
+ /// Debug level of the VM. Supported values: "full" (default), and "none".
+ #[clap(long, default_value = "full", value_parser = parse_debug_level)]
debug: DebugLevel,
/// Run VM in protected mode.
@@ -154,7 +154,7 @@
#[clap(long)]
log: Option<PathBuf>,
- /// Debug level of the VM. Supported values: "none" (default), and "full".
+ /// Debug level of the VM. Supported values: "full" (default), and "none".
#[clap(long, default_value = "full", value_parser = parse_debug_level)]
debug: DebugLevel,
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index 46f4937..71b9e76 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -84,6 +84,7 @@
"libspin_nostd",
"libtinyvec_nostd",
"libvirtio_drivers",
+ "libzerocopy_nostd",
"libzeroize_nostd",
],
whole_static_libs: [
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
index 2ce0e83..29fa0ff 100644
--- a/vmbase/src/bionic.rs
+++ b/vmbase/src/bionic.rs
@@ -22,6 +22,7 @@
use core::str;
use crate::console;
+use crate::cstr;
use crate::eprintln;
use crate::read_sysreg;
@@ -79,6 +80,11 @@
unsafe { ERRNO = value };
}
+fn get_errno() -> c_int {
+ // SAFETY: vmbase is currently single-threaded.
+ unsafe { ERRNO }
+}
+
/// Reports a fatal error detected by Bionic.
///
/// # Safety
@@ -154,142 +160,168 @@
#[no_mangle]
extern "C" fn strerror(n: c_int) -> *mut c_char {
- // Messages taken from errno(1).
- let s = match n {
- 0 => "Success",
- 1 => "Operation not permitted",
- 2 => "No such file or directory",
- 3 => "No such process",
- 4 => "Interrupted system call",
- 5 => "Input/output error",
- 6 => "No such device or address",
- 7 => "Argument list too long",
- 8 => "Exec format error",
- 9 => "Bad file descriptor",
- 10 => "No child processes",
- 11 => "Resource temporarily unavailable",
- 12 => "Cannot allocate memory",
- 13 => "Permission denied",
- 14 => "Bad address",
- 15 => "Block device required",
- 16 => "Device or resource busy",
- 17 => "File exists",
- 18 => "Invalid cross-device link",
- 19 => "No such device",
- 20 => "Not a directory",
- 21 => "Is a directory",
- 22 => "Invalid argument",
- 23 => "Too many open files in system",
- 24 => "Too many open files",
- 25 => "Inappropriate ioctl for device",
- 26 => "Text file busy",
- 27 => "File too large",
- 28 => "No space left on device",
- 29 => "Illegal seek",
- 30 => "Read-only file system",
- 31 => "Too many links",
- 32 => "Broken pipe",
- 33 => "Numerical argument out of domain",
- 34 => "Numerical result out of range",
- 35 => "Resource deadlock avoided",
- 36 => "File name too long",
- 37 => "No locks available",
- 38 => "Function not implemented",
- 39 => "Directory not empty",
- 40 => "Too many levels of symbolic links",
- 42 => "No message of desired type",
- 43 => "Identifier removed",
- 44 => "Channel number out of range",
- 45 => "Level 2 not synchronized",
- 46 => "Level 3 halted",
- 47 => "Level 3 reset",
- 48 => "Link number out of range",
- 49 => "Protocol driver not attached",
- 50 => "No CSI structure available",
- 51 => "Level 2 halted",
- 52 => "Invalid exchange",
- 53 => "Invalid request descriptor",
- 54 => "Exchange full",
- 55 => "No anode",
- 56 => "Invalid request code",
- 57 => "Invalid slot",
- 59 => "Bad font file format",
- 60 => "Device not a stream",
- 61 => "No data available",
- 62 => "Timer expired",
- 63 => "Out of streams resources",
- 64 => "Machine is not on the network",
- 65 => "Package not installed",
- 66 => "Object is remote",
- 67 => "Link has been severed",
- 68 => "Advertise error",
- 69 => "Srmount error",
- 70 => "Communication error on send",
- 71 => "Protocol error",
- 72 => "Multihop attempted",
- 73 => "RFS specific error",
- 74 => "Bad message",
- 75 => "Value too large for defined data type",
- 76 => "Name not unique on network",
- 77 => "File descriptor in bad state",
- 78 => "Remote address changed",
- 79 => "Can not access a needed shared library",
- 80 => "Accessing a corrupted shared library",
- 81 => ".lib section in a.out corrupted",
- 82 => "Attempting to link in too many shared libraries",
- 83 => "Cannot exec a shared library directly",
- 84 => "Invalid or incomplete multibyte or wide character",
- 85 => "Interrupted system call should be restarted",
- 86 => "Streams pipe error",
- 87 => "Too many users",
- 88 => "Socket operation on non-socket",
- 89 => "Destination address required",
- 90 => "Message too long",
- 91 => "Protocol wrong type for socket",
- 92 => "Protocol not available",
- 93 => "Protocol not supported",
- 94 => "Socket type not supported",
- 95 => "Operation not supported",
- 96 => "Protocol family not supported",
- 97 => "Address family not supported by protocol",
- 98 => "Address already in use",
- 99 => "Cannot assign requested address",
- 100 => "Network is down",
- 101 => "Network is unreachable",
- 102 => "Network dropped connection on reset",
- 103 => "Software caused connection abort",
- 104 => "Connection reset by peer",
- 105 => "No buffer space available",
- 106 => "Transport endpoint is already connected",
- 107 => "Transport endpoint is not connected",
- 108 => "Cannot send after transport endpoint shutdown",
- 109 => "Too many references: cannot splice",
- 110 => "Connection timed out",
- 111 => "Connection refused",
- 112 => "Host is down",
- 113 => "No route to host",
- 114 => "Operation already in progress",
- 115 => "Operation now in progress",
- 116 => "Stale file handle",
- 117 => "Structure needs cleaning",
- 118 => "Not a XENIX named type file",
- 119 => "No XENIX semaphores available",
- 120 => "Is a named type file",
- 121 => "Remote I/O error",
- 122 => "Disk quota exceeded",
- 123 => "No medium found",
- 124 => "Wrong medium type",
- 125 => "Operation canceled",
- 126 => "Required key not available",
- 127 => "Key has expired",
- 128 => "Key has been revoked",
- 129 => "Key was rejected by service",
- 130 => "Owner died",
- 131 => "State not recoverable",
- 132 => "Operation not possible due to RF-kill",
- 133 => "Memory page has hardware error",
- _ => "Unknown errno value",
+ cstr_error(n).as_ptr().cast_mut().cast()
+}
+
+#[no_mangle]
+extern "C" fn perror(s: *const c_char) {
+ let prefix = if s.is_null() {
+ None
+ } else {
+ // SAFETY: Just like libc, we need to assume that `s` is a valid NULL-terminated string.
+ let c_str = unsafe { CStr::from_ptr(s) };
+ // TODO(Rust 1.71): if c_str.is_empty() {
+ if c_str.to_bytes().is_empty() {
+ None
+ } else {
+ Some(c_str.to_str().unwrap())
+ }
};
- s.as_ptr().cast_mut().cast()
+ let error = cstr_error(get_errno()).to_str().unwrap();
+
+ if let Some(prefix) = prefix {
+ eprintln!("{prefix}: {error}");
+ } else {
+ eprintln!("{error}");
+ }
+}
+
+fn cstr_error(n: c_int) -> &'static CStr {
+ // Messages taken from errno(1).
+ match n {
+ 0 => cstr!("Success"),
+ 1 => cstr!("Operation not permitted"),
+ 2 => cstr!("No such file or directory"),
+ 3 => cstr!("No such process"),
+ 4 => cstr!("Interrupted system call"),
+ 5 => cstr!("Input/output error"),
+ 6 => cstr!("No such device or address"),
+ 7 => cstr!("Argument list too long"),
+ 8 => cstr!("Exec format error"),
+ 9 => cstr!("Bad file descriptor"),
+ 10 => cstr!("No child processes"),
+ 11 => cstr!("Resource temporarily unavailable"),
+ 12 => cstr!("Cannot allocate memory"),
+ 13 => cstr!("Permission denied"),
+ 14 => cstr!("Bad address"),
+ 15 => cstr!("Block device required"),
+ 16 => cstr!("Device or resource busy"),
+ 17 => cstr!("File exists"),
+ 18 => cstr!("Invalid cross-device link"),
+ 19 => cstr!("No such device"),
+ 20 => cstr!("Not a directory"),
+ 21 => cstr!("Is a directory"),
+ 22 => cstr!("Invalid argument"),
+ 23 => cstr!("Too many open files in system"),
+ 24 => cstr!("Too many open files"),
+ 25 => cstr!("Inappropriate ioctl for device"),
+ 26 => cstr!("Text file busy"),
+ 27 => cstr!("File too large"),
+ 28 => cstr!("No space left on device"),
+ 29 => cstr!("Illegal seek"),
+ 30 => cstr!("Read-only file system"),
+ 31 => cstr!("Too many links"),
+ 32 => cstr!("Broken pipe"),
+ 33 => cstr!("Numerical argument out of domain"),
+ 34 => cstr!("Numerical result out of range"),
+ 35 => cstr!("Resource deadlock avoided"),
+ 36 => cstr!("File name too long"),
+ 37 => cstr!("No locks available"),
+ 38 => cstr!("Function not implemented"),
+ 39 => cstr!("Directory not empty"),
+ 40 => cstr!("Too many levels of symbolic links"),
+ 42 => cstr!("No message of desired type"),
+ 43 => cstr!("Identifier removed"),
+ 44 => cstr!("Channel number out of range"),
+ 45 => cstr!("Level 2 not synchronized"),
+ 46 => cstr!("Level 3 halted"),
+ 47 => cstr!("Level 3 reset"),
+ 48 => cstr!("Link number out of range"),
+ 49 => cstr!("Protocol driver not attached"),
+ 50 => cstr!("No CSI structure available"),
+ 51 => cstr!("Level 2 halted"),
+ 52 => cstr!("Invalid exchange"),
+ 53 => cstr!("Invalid request descriptor"),
+ 54 => cstr!("Exchange full"),
+ 55 => cstr!("No anode"),
+ 56 => cstr!("Invalid request code"),
+ 57 => cstr!("Invalid slot"),
+ 59 => cstr!("Bad font file format"),
+ 60 => cstr!("Device not a stream"),
+ 61 => cstr!("No data available"),
+ 62 => cstr!("Timer expired"),
+ 63 => cstr!("Out of streams resources"),
+ 64 => cstr!("Machine is not on the network"),
+ 65 => cstr!("Package not installed"),
+ 66 => cstr!("Object is remote"),
+ 67 => cstr!("Link has been severed"),
+ 68 => cstr!("Advertise error"),
+ 69 => cstr!("Srmount error"),
+ 70 => cstr!("Communication error on send"),
+ 71 => cstr!("Protocol error"),
+ 72 => cstr!("Multihop attempted"),
+ 73 => cstr!("RFS specific error"),
+ 74 => cstr!("Bad message"),
+ 75 => cstr!("Value too large for defined data type"),
+ 76 => cstr!("Name not unique on network"),
+ 77 => cstr!("File descriptor in bad state"),
+ 78 => cstr!("Remote address changed"),
+ 79 => cstr!("Can not access a needed shared library"),
+ 80 => cstr!("Accessing a corrupted shared library"),
+ 81 => cstr!(".lib section in a.out corrupted"),
+ 82 => cstr!("Attempting to link in too many shared libraries"),
+ 83 => cstr!("Cannot exec a shared library directly"),
+ 84 => cstr!("Invalid or incomplete multibyte or wide character"),
+ 85 => cstr!("Interrupted system call should be restarted"),
+ 86 => cstr!("Streams pipe error"),
+ 87 => cstr!("Too many users"),
+ 88 => cstr!("Socket operation on non-socket"),
+ 89 => cstr!("Destination address required"),
+ 90 => cstr!("Message too long"),
+ 91 => cstr!("Protocol wrong type for socket"),
+ 92 => cstr!("Protocol not available"),
+ 93 => cstr!("Protocol not supported"),
+ 94 => cstr!("Socket type not supported"),
+ 95 => cstr!("Operation not supported"),
+ 96 => cstr!("Protocol family not supported"),
+ 97 => cstr!("Address family not supported by protocol"),
+ 98 => cstr!("Address already in use"),
+ 99 => cstr!("Cannot assign requested address"),
+ 100 => cstr!("Network is down"),
+ 101 => cstr!("Network is unreachable"),
+ 102 => cstr!("Network dropped connection on reset"),
+ 103 => cstr!("Software caused connection abort"),
+ 104 => cstr!("Connection reset by peer"),
+ 105 => cstr!("No buffer space available"),
+ 106 => cstr!("Transport endpoint is already connected"),
+ 107 => cstr!("Transport endpoint is not connected"),
+ 108 => cstr!("Cannot send after transport endpoint shutdown"),
+ 109 => cstr!("Too many references: cannot splice"),
+ 110 => cstr!("Connection timed out"),
+ 111 => cstr!("Connection refused"),
+ 112 => cstr!("Host is down"),
+ 113 => cstr!("No route to host"),
+ 114 => cstr!("Operation already in progress"),
+ 115 => cstr!("Operation now in progress"),
+ 116 => cstr!("Stale file handle"),
+ 117 => cstr!("Structure needs cleaning"),
+ 118 => cstr!("Not a XENIX named type file"),
+ 119 => cstr!("No XENIX semaphores available"),
+ 120 => cstr!("Is a named type file"),
+ 121 => cstr!("Remote I/O error"),
+ 122 => cstr!("Disk quota exceeded"),
+ 123 => cstr!("No medium found"),
+ 124 => cstr!("Wrong medium type"),
+ 125 => cstr!("Operation canceled"),
+ 126 => cstr!("Required key not available"),
+ 127 => cstr!("Key has expired"),
+ 128 => cstr!("Key has been revoked"),
+ 129 => cstr!("Key was rejected by service"),
+ 130 => cstr!("Owner died"),
+ 131 => cstr!("State not recoverable"),
+ 132 => cstr!("Operation not possible due to RF-kill"),
+ 133 => cstr!("Memory page has hardware error"),
+ _ => cstr!("Unknown errno value"),
+ }
}
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index 24b5035..2ff66cc 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -26,7 +26,8 @@
console::init();
if let Some(mmio_guard) = get_mmio_guard() {
- mmio_guard.init()?;
+ mmio_guard.enroll()?;
+ mmio_guard.validate_granule()?;
mmio_guard.map(console::BASE_ADDRESS)?;
}
diff --git a/vmbase/src/hvc.rs b/vmbase/src/hvc.rs
index ebd1625..1197143 100644
--- a/vmbase/src/hvc.rs
+++ b/vmbase/src/hvc.rs
@@ -37,7 +37,7 @@
(version as u32 as i32).try_into()
}
-pub type TrngRng64Entropy = (u64, u64, u64);
+pub type TrngRng64Entropy = [u64; 3];
pub fn trng_rnd64(nbits: u64) -> trng::Result<TrngRng64Entropy> {
let mut args = [0u64; 17];
@@ -46,7 +46,7 @@
let regs = hvc64(ARM_SMCCC_TRNG_RND64, args);
success_or_error_64::<Error>(regs[0])?;
- Ok((regs[1], regs[2], regs[3]))
+ Ok([regs[1], regs[2], regs[3]])
}
pub fn trng_features(fid: u32) -> trng::Result<u64> {
diff --git a/vmbase/src/memory/mod.rs b/vmbase/src/memory/mod.rs
index 898aa10..2f72fc4 100644
--- a/vmbase/src/memory/mod.rs
+++ b/vmbase/src/memory/mod.rs
@@ -23,10 +23,12 @@
pub use error::MemoryTrackerError;
pub use page_table::PageTable;
pub use shared::{
- alloc_shared, dealloc_shared, handle_permission_fault, handle_translation_fault, MemoryRange,
- MemoryTracker, MEMORY,
+ handle_permission_fault, handle_translation_fault, MemoryRange, MemoryTracker, MEMORY,
};
pub use util::{
- flush, flushed_zeroize, min_dcache_line_size, page_4kb_of, phys_to_virt, virt_to_phys,
- PAGE_SIZE, SIZE_128KB, SIZE_2MB, SIZE_4KB, SIZE_4MB, SIZE_64KB,
+ flush, flushed_zeroize, min_dcache_line_size, page_4kb_of, PAGE_SIZE, SIZE_128KB, SIZE_2MB,
+ SIZE_4KB, SIZE_4MB, SIZE_64KB,
};
+
+pub(crate) use shared::{alloc_shared, dealloc_shared};
+pub(crate) use util::{phys_to_virt, virt_to_phys};
diff --git a/vmbase/src/memory/shared.rs b/vmbase/src/memory/shared.rs
index 173c0ec..064fb6d 100644
--- a/vmbase/src/memory/shared.rs
+++ b/vmbase/src/memory/shared.rs
@@ -27,6 +27,7 @@
use alloc::vec::Vec;
use buddy_system_allocator::{FrameAllocator, LockedFrameAllocator};
use core::alloc::Layout;
+use core::cmp::max;
use core::mem::size_of;
use core::num::NonZeroUsize;
use core::ops::Range;
@@ -343,7 +344,7 @@
/// Allocates a memory range of at least the given size and alignment that is shared with the host.
/// Returns a pointer to the buffer.
-pub fn alloc_shared(layout: Layout) -> hyp::Result<NonNull<u8>> {
+pub(crate) fn alloc_shared(layout: Layout) -> hyp::Result<NonNull<u8>> {
assert_ne!(layout.size(), 0);
let Some(buffer) = try_shared_alloc(layout) else {
handle_alloc_error(layout);
@@ -359,7 +360,11 @@
if let Some(buffer) = shared_pool.alloc_aligned(layout) {
Some(NonNull::new(buffer as _).unwrap())
} else if let Some(shared_memory) = SHARED_MEMORY.lock().as_mut() {
- shared_memory.refill(&mut shared_pool, layout);
+ // Adjusts the layout size to the max of the next power of two and the alignment,
+ // as this is the actual size of the memory allocated in `alloc_aligned()`.
+ let size = max(layout.size().next_power_of_two(), layout.align());
+ let refill_layout = Layout::from_size_align(size, layout.align()).unwrap();
+ shared_memory.refill(&mut shared_pool, refill_layout);
shared_pool.alloc_aligned(layout).map(|buffer| NonNull::new(buffer as _).unwrap())
} else {
None
@@ -374,7 +379,7 @@
///
/// The memory must have been allocated by `alloc_shared` with the same layout, and not yet
/// deallocated.
-pub unsafe fn dealloc_shared(vaddr: NonNull<u8>, layout: Layout) -> hyp::Result<()> {
+pub(crate) unsafe fn dealloc_shared(vaddr: NonNull<u8>, layout: Layout) -> hyp::Result<()> {
SHARED_POOL.get().unwrap().lock().dealloc_aligned(vaddr.as_ptr() as usize, layout);
trace!("Deallocated shared buffer at {vaddr:?} with {layout:?}");
diff --git a/vmbase/src/memory/util.rs b/vmbase/src/memory/util.rs
index 48d4c55..2b75414 100644
--- a/vmbase/src/memory/util.rs
+++ b/vmbase/src/memory/util.rs
@@ -88,7 +88,7 @@
///
/// As we use identity mapping for everything, this is just a cast, but it's useful to use it to be
/// explicit about where we are converting from virtual to physical address.
-pub fn virt_to_phys(vaddr: NonNull<u8>) -> usize {
+pub(crate) fn virt_to_phys(vaddr: NonNull<u8>) -> usize {
vaddr.as_ptr() as _
}
@@ -96,6 +96,6 @@
/// physical address.
///
/// Panics if `paddr` is 0.
-pub fn phys_to_virt(paddr: usize) -> NonNull<u8> {
+pub(crate) fn phys_to_virt(paddr: usize) -> NonNull<u8> {
NonNull::new(paddr as _).unwrap()
}
diff --git a/vmbase/src/rand.rs b/vmbase/src/rand.rs
index 6b8d7e0..2acc390 100644
--- a/vmbase/src/rand.rs
+++ b/vmbase/src/rand.rs
@@ -14,10 +14,13 @@
//! Functions and drivers for obtaining true entropy.
-use crate::hvc::{self, TrngRng64Entropy};
+use crate::hvc;
use core::fmt;
use core::mem::size_of;
use smccc::{self, Hvc};
+use zerocopy::AsBytes as _;
+
+type Entropy = [u8; size_of::<u64>() * 3];
/// Error type for rand operations.
pub enum Error {
@@ -95,46 +98,55 @@
/// 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::<TrngRng64Entropy>();
+ const MAX_BYTES_PER_CALL: usize = size_of::<Entropy>();
- let (aligned, remainder) = s.split_at_mut(s.len() - s.len() % MAX_BYTES_PER_CALL);
-
- for chunk in aligned.chunks_exact_mut(MAX_BYTES_PER_CALL) {
- let (r, s, t) = repeat_trng_rnd(chunk.len())?;
-
- let mut words = chunk.chunks_exact_mut(size_of::<u64>());
- words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
- words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
- words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
- }
-
- if !remainder.is_empty() {
- let mut entropy = [0; MAX_BYTES_PER_CALL];
- let (r, s, t) = repeat_trng_rnd(remainder.len())?;
-
- let mut words = entropy.chunks_exact_mut(size_of::<u64>());
- words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
- words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
- words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
-
- remainder.clone_from_slice(&entropy[..remainder.len()]);
+ for chunk in s.chunks_mut(MAX_BYTES_PER_CALL) {
+ let entropy = repeat_trng_rnd(chunk.len())?;
+ chunk.clone_from_slice(&entropy[..chunk.len()]);
}
Ok(())
}
-fn repeat_trng_rnd(n_bytes: usize) -> Result<TrngRng64Entropy> {
- let bits = usize::try_from(u8::BITS).unwrap();
- let n_bits = (n_bytes * bits).try_into().unwrap();
+/// 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 {
- match hvc::trng_rnd64(n_bits) {
- Ok(entropy) => return Ok(entropy),
- Err(hvc::trng::Error::NoEntropy) => (),
- Err(e) => return Err(e.into()),
+ 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/zipfuse/src/inode.rs b/zipfuse/src/inode.rs
index ea63422..ef48389 100644
--- a/zipfuse/src/inode.rs
+++ b/zipfuse/src/inode.rs
@@ -210,7 +210,7 @@
parent = found;
// Update the mode if this is a directory leaf.
if !is_file && is_leaf {
- let mut inode = table.get_mut(parent).unwrap();
+ let inode = table.get_mut(parent).unwrap();
inode.mode = file.unix_mode().unwrap_or(DEFAULT_DIR_MODE);
}
continue;