Merge "Revert "[vs] Extract writable partition initialization into a ne...""
diff --git a/libs/hyp/Android.bp b/libs/hyp/Android.bp
index bc66190..e4353c8 100644
--- a/libs/hyp/Android.bp
+++ b/libs/hyp/Android.bp
@@ -8,7 +8,7 @@
srcs: ["src/lib.rs"],
prefer_rlib: true,
rustlibs: [
- "libsmccc",
+ "libpsci",
],
no_stdlibs: true,
stdlibs: [
diff --git a/libs/hyp/src/error.rs b/libs/hyp/src/error.rs
index 20a0cff..4e25e7f 100644
--- a/libs/hyp/src/error.rs
+++ b/libs/hyp/src/error.rs
@@ -14,6 +14,7 @@
//! Error and Result types for hypervisor.
+use crate::KvmError;
use core::{fmt, result};
/// Result type with hypervisor error.
@@ -24,8 +25,8 @@
pub enum Error {
/// MMIO guard is not supported.
MmioGuardNotsupported,
- /// Failed to invoke a certain HVC function.
- HvcError(smccc::Error, u32),
+ /// Failed to invoke a certain KVM HVC function.
+ KvmError(KvmError, u32),
/// The MMIO_GUARD granule used by the hypervisor is not supported.
UnsupportedMmioGuardGranule(usize),
}
@@ -34,7 +35,7 @@
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::MmioGuardNotsupported => write!(f, "MMIO guard is not supported"),
- Self::HvcError(e, function_id) => {
+ Self::KvmError(e, function_id) => {
write!(f, "Failed to invoke the HVC function with function ID {function_id}: {e}")
}
Self::UnsupportedMmioGuardGranule(g) => {
diff --git a/libs/hyp/src/hypervisor/common.rs b/libs/hyp/src/hypervisor/common.rs
index 550fb2e..87d35b2 100644
--- a/libs/hyp/src/hypervisor/common.rs
+++ b/libs/hyp/src/hypervisor/common.rs
@@ -23,10 +23,12 @@
/// Protected VMs are auto-enrolled.
fn mmio_guard_init(&self) -> Result<()>;
- /// Maps a memory address to the hypervisor MMIO guard.
+ /// Maps a page containing the given memory address to the hypervisor MMIO guard.
+ /// The page size corresponds to the MMIO guard granule size.
fn mmio_guard_map(&self, addr: usize) -> Result<()>;
- /// Unmaps a memory address from the hypervisor MMIO guard.
+ /// Unmaps a page containing the given memory address from the hypervisor MMIO guard.
+ /// The page size corresponds to the MMIO guard granule size.
fn mmio_guard_unmap(&self, addr: usize) -> Result<()>;
/// Shares a region of memory with host, granting it read, write and execute permissions.
diff --git a/libs/hyp/src/hypervisor/kvm.rs b/libs/hyp/src/hypervisor/kvm.rs
index 615c75a..c0c1ac9 100644
--- a/libs/hyp/src/hypervisor/kvm.rs
+++ b/libs/hyp/src/hypervisor/kvm.rs
@@ -17,6 +17,48 @@
use super::common::Hypervisor;
use crate::error::{Error, Result};
use crate::util::{page_address, SIZE_4KB};
+use core::fmt::{self, Display, Formatter};
+use psci::smccc::{
+ error::{positive_or_error_64, success_or_error_32, success_or_error_64},
+ hvc64,
+};
+
+/// Error from a KVM HVC call.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum KvmError {
+ /// The call is not supported by the implementation.
+ NotSupported,
+ /// One of the call parameters has a non-supported value.
+ InvalidParameter,
+ /// There was an unexpected return value.
+ Unknown(i64),
+}
+
+impl From<i64> for KvmError {
+ fn from(value: i64) -> Self {
+ match value {
+ -1 => KvmError::NotSupported,
+ -3 => KvmError::InvalidParameter,
+ _ => KvmError::Unknown(value),
+ }
+ }
+}
+
+impl From<i32> for KvmError {
+ fn from(value: i32) -> Self {
+ i64::from(value).into()
+ }
+}
+
+impl Display for KvmError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::NotSupported => write!(f, "KVM call not supported"),
+ Self::InvalidParameter => write!(f, "KVM call received non-supported value"),
+ Self::Unknown(e) => write!(f, "Unknown return value from KVM {} ({0:#x})", e),
+ }
+ }
+}
const ARM_SMCCC_KVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
const ARM_SMCCC_KVM_FUNC_MEM_SHARE: u32 = 0xc6000003;
@@ -45,17 +87,8 @@
// TODO(b/277859415): pKVM returns a i32 instead of a i64 in T.
// Drop this hack once T reaches EoL.
- let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
- match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
- Err(smccc::Error::Unexpected(e)) if is_i32_error_code(e) => match e as u32 as i32 {
- -1 => Err(smccc::Error::NotSupported),
- -2 => Err(smccc::Error::NotRequired),
- -3 => Err(smccc::Error::InvalidParameter),
- ret => Err(smccc::Error::Unknown(ret as i64)),
- },
- res => res,
- }
- .map_err(|e| Error::HvcError(e, VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID))
+ success_or_error_32(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args)[0] as u32)
+ .map_err(|e| Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID))
}
fn mmio_guard_unmap(&self, addr: usize) -> Result<()> {
@@ -64,9 +97,9 @@
// TODO(b/277860860): pKVM returns NOT_SUPPORTED for SUCCESS in T.
// Drop this hack once T reaches EoL.
- match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
- Err(smccc::Error::NotSupported) | Ok(_) => Ok(()),
- Err(e) => Err(Error::HvcError(e, VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID)),
+ match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args)[0]) {
+ Err(KvmError::NotSupported) | Ok(_) => Ok(()),
+ Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID)),
}
}
@@ -100,17 +133,17 @@
fn mmio_guard_enroll() -> Result<()> {
let args = [0u64; 17];
- match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args) {
+ match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
Ok(_) => Ok(()),
- Err(smccc::Error::NotSupported) => Err(Error::MmioGuardNotsupported),
- Err(e) => Err(Error::HvcError(e, VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID)),
+ 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<()> {
- smccc::checked_hvc64_expect_zero(function, args).map_err(|e| Error::HvcError(e, function))
+ success_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::KvmError(e, function))
}
fn checked_hvc64(function: u32, args: [u64; 17]) -> Result<u64> {
- smccc::checked_hvc64(function, args).map_err(|e| Error::HvcError(e, function))
+ positive_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 87925d2..a694029 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -18,6 +18,7 @@
mod kvm;
pub use common::Hypervisor;
+pub use kvm::KvmError;
use kvm::KvmHypervisor;
static HYPERVISOR: HypervisorBackend = HypervisorBackend::Kvm;
diff --git a/libs/hyp/src/lib.rs b/libs/hyp/src/lib.rs
index 66c78f4..6db6ba8 100644
--- a/libs/hyp/src/lib.rs
+++ b/libs/hyp/src/lib.rs
@@ -21,4 +21,4 @@
mod util;
pub use error::{Error, Result};
-pub use hypervisor::{get_hypervisor, Hypervisor};
+pub use hypervisor::{get_hypervisor, Hypervisor, KvmError};
diff --git a/libs/smccc/Android.bp b/libs/smccc/Android.bp
deleted file mode 100644
index 96943d8..0000000
--- a/libs/smccc/Android.bp
+++ /dev/null
@@ -1,18 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_library_rlib {
- name: "libsmccc",
- crate_name: "smccc",
- srcs: ["src/lib.rs"],
- prefer_rlib: true,
- rustlibs: [
- "libpsci",
- ],
- no_stdlibs: true,
- stdlibs: [
- "libcore.rust_sysroot",
- ],
- apex_available: ["com.android.virt"],
-}
diff --git a/libs/smccc/src/lib.rs b/libs/smccc/src/lib.rs
deleted file mode 100644
index 2cd31dc..0000000
--- a/libs/smccc/src/lib.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2023, 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.
-
-//! Structs and functions for making SMCCC calls following the SMC Calling
-//! Convention version 1.4.
-
-#![no_std]
-
-mod smccc;
-
-pub use smccc::{checked_hvc64, checked_hvc64_expect_zero, hvc64, Error, Result};
diff --git a/libs/smccc/src/smccc.rs b/libs/smccc/src/smccc.rs
deleted file mode 100644
index c0070e0..0000000
--- a/libs/smccc/src/smccc.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.
-
-//! Structs and functions for making SMCCC calls.
-
-use core::{fmt, result};
-// Ideally, smccc shouldn't depend on psci. Smccc isn't split as a separate
-// upstream crate currently mostly for maintenance consideration.
-// See b/245889995 for more context.
-pub use psci::smccc::hvc64;
-
-/// Standard SMCCC error values as described in DEN 0028E.
-#[derive(Debug, Clone)]
-pub enum Error {
- /// The call is not supported by the implementation.
- NotSupported,
- /// The call is deemed not required by the implementation.
- NotRequired,
- /// One of the call parameters has a non-supported value.
- InvalidParameter,
- /// Negative values indicate error.
- Unknown(i64),
- /// The call returned a positive value when 0 was expected.
- Unexpected(u64),
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::NotSupported => write!(f, "SMCCC call not supported"),
- Self::NotRequired => write!(f, "SMCCC call not required"),
- Self::InvalidParameter => write!(f, "SMCCC call received non-supported value"),
- Self::Unexpected(v) => write!(f, "Unexpected SMCCC return value {} ({0:#x})", v),
- Self::Unknown(e) => write!(f, "Unknown SMCCC return value {} ({0:#x})", e),
- }
- }
-}
-
-/// Result type with smccc::Error.
-pub type Result<T> = result::Result<T, Error>;
-
-/// Makes a checked HVC64 call to the hypervisor, following the SMC Calling Convention version 1.4.
-/// Returns Ok only when the return code is 0.
-pub fn checked_hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<()> {
- match checked_hvc64(function, args)? {
- 0 => Ok(()),
- v => Err(Error::Unexpected(v)),
- }
-}
-
-/// Makes a checked HVC64 call to the hypervisor, following the SMC Calling Convention version 1.4.
-/// Returns Ok with the return code only when the return code >= 0.
-pub fn checked_hvc64(function: u32, args: [u64; 17]) -> Result<u64> {
- match hvc64(function, args)[0] as i64 {
- ret if ret >= 0 => Ok(ret as u64),
- -1 => Err(Error::NotSupported),
- -2 => Err(Error::NotRequired),
- -3 => Err(Error::InvalidParameter),
- ret => Err(Error::Unknown(ret)),
- }
-}
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index de06d01..1092476 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -426,7 +426,7 @@
avb_add_hash_footer {
name: "microdroid_kernel_signed",
- src: "empty_kernel",
+ src: ":empty_file",
filename: "microdroid_kernel",
partition_name: "boot",
private_key: ":microdroid_sign_key",
@@ -450,7 +450,7 @@
prebuilt_etc {
name: "microdroid_kernel",
- src: "empty_kernel",
+ src: ":empty_file",
relative_install_path: "fs",
arch: {
arm64: {
diff --git a/microdroid/empty_kernel b/microdroid/empty_kernel
deleted file mode 100644
index e69de29..0000000
--- a/microdroid/empty_kernel
+++ /dev/null
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 0571c36..8be5f7d 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -21,10 +21,10 @@
"liblibfdt",
"liblog_rust_nostd",
"libonce_cell_nostd",
+ "libpsci",
"libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
"libpvmfw_fdt_template",
- "libsmccc",
"libstatic_assertions",
"libtinyvec_nostd",
"libuuid_nostd",
diff --git a/pvmfw/src/hvc.rs b/pvmfw/src/hvc.rs
index 6c5017f..1e2bca2 100644
--- a/pvmfw/src/hvc.rs
+++ b/pvmfw/src/hvc.rs
@@ -15,6 +15,11 @@
//! Wrappers around calls to the hypervisor.
pub mod trng;
+use self::trng::Error;
+use psci::smccc::{
+ error::{positive_or_error_64, success_or_error_64},
+ hvc64,
+};
// TODO(b/272226230): Move all the trng functions to trng module
const ARM_SMCCC_TRNG_VERSION: u32 = 0x8400_0050;
@@ -30,7 +35,7 @@
pub fn trng_version() -> trng::Result<(u16, u16)> {
let args = [0u64; 17];
- let version = trng::hvc64(ARM_SMCCC_TRNG_VERSION, args)?[0];
+ let version = positive_or_error_64::<Error>(hvc64(ARM_SMCCC_TRNG_VERSION, args)[0])?;
Ok(((version >> 16) as u16, version as u16))
}
@@ -40,7 +45,8 @@
let mut args = [0u64; 17];
args[0] = nbits;
- let regs = trng::hvc64_expect_zero(ARM_SMCCC_TRNG_RND64, args)?;
+ let regs = hvc64(ARM_SMCCC_TRNG_RND64, args);
+ success_or_error_64::<Error>(regs[0])?;
Ok((regs[1], regs[2], regs[3]))
}
diff --git a/pvmfw/src/hvc/trng.rs b/pvmfw/src/hvc/trng.rs
index 05ecc6b..6331d66 100644
--- a/pvmfw/src/hvc/trng.rs
+++ b/pvmfw/src/hvc/trng.rs
@@ -42,23 +42,16 @@
}
}
+impl From<i64> for Error {
+ fn from(value: i64) -> Self {
+ match value {
+ -1 => Error::NotSupported,
+ -2 => Error::InvalidParameter,
+ -3 => Error::NoEntropy,
+ _ if value < 0 => Error::Unknown(value),
+ _ => Error::Unexpected(value as u64),
+ }
+ }
+}
+
pub type Result<T> = result::Result<T, Error>;
-
-pub fn hvc64(function: u32, args: [u64; 17]) -> Result<[u64; 18]> {
- let res = smccc::hvc64(function, args);
- match res[0] as i64 {
- ret if ret >= 0 => Ok(res),
- -1 => Err(Error::NotSupported),
- -2 => Err(Error::InvalidParameter),
- -3 => Err(Error::NoEntropy),
- ret => Err(Error::Unknown(ret)),
- }
-}
-
-pub fn hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<[u64; 18]> {
- let res = hvc64(function, args)?;
- match res[0] {
- 0 => Ok(res),
- v => Err(Error::Unexpected(v)),
- }
-}
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 429d910..18728ad 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -25,8 +25,6 @@
<option name="force-root" value="false"/>
</target_preparer>
- <target_preparer class="com.android.microdroid.test.preparer.DisableMicrodroidDebugPolicyPreparer" />
-
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="MicrodroidHostTestCases.jar" />
</test>
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index e0b78ba..3888df2 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -26,13 +26,13 @@
IVirtualizationServiceInternal::IVirtualizationServiceInternal,
};
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, ensure, Context, Result};
use binder::{self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong};
use libc::VMADDR_CID_HOST;
use log::{error, info, warn};
use rustutils::system_properties;
use std::collections::HashMap;
-use std::fs::{create_dir, read_dir, remove_dir, remove_file, set_permissions, Permissions};
+use std::fs::{create_dir, remove_dir_all, set_permissions, Permissions};
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::os::unix::raw::{pid_t, uid_t};
@@ -268,20 +268,10 @@
/// Removes a directory owned by a different user by first changing its owner back
/// to VirtualizationService.
pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
- if !path.as_path().is_dir() {
- bail!("Path {:?} is not a directory", path);
- }
+ ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
chown(path, Some(Uid::current()), None)?;
set_permissions(path, Permissions::from_mode(0o700))?;
- remove_temporary_files(path)?;
- remove_dir(path)?;
- Ok(())
-}
-
-pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
- for dir_entry in read_dir(path)? {
- remove_file(dir_entry?.path())?;
- }
+ remove_dir_all(path)?;
Ok(())
}
diff --git a/vmbase/README.md b/vmbase/README.md
index 552ac31..7f621fb 100644
--- a/vmbase/README.md
+++ b/vmbase/README.md
@@ -51,12 +51,14 @@
entry point. Instead, `vmbase` provides a macro to specify your main function:
```rust
-use vmbase::{main, println};
+use vmbase::{logger, main};
+use log::{info, LevelFilter};
main!(main);
pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) {
- println!("Hello world");
+ logger::init(LevelFilter::Info).unwrap();
+ info!("Hello world");
}
```
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index 26be51b..dc9a090 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -18,7 +18,6 @@
"libvirtio_drivers",
"libvmbase",
],
- apex_available: ["com.android.virt"],
}
cc_binary {
@@ -34,7 +33,6 @@
"image.ld",
":vmbase_sections",
],
- apex_available: ["com.android.virt"],
}
raw_binary {
diff --git a/vmbase/example/src/layout.rs b/vmbase/example/src/layout.rs
index 4c3af6d..e660c5f 100644
--- a/vmbase/example/src/layout.rs
+++ b/vmbase/example/src/layout.rs
@@ -17,8 +17,8 @@
use aarch64_paging::paging::{MemoryRegion, VirtualAddress};
use core::arch::asm;
use core::ops::Range;
+use log::info;
use vmbase::layout;
-use vmbase::println;
use vmbase::STACK_CHK_GUARD;
/// The first 1 GiB of memory are used for MMIO.
@@ -74,14 +74,14 @@
pub fn print_addresses() {
let dtb = dtb_range();
- println!("dtb: {}..{} ({} bytes)", dtb.start, dtb.end, dtb.end - dtb.start);
+ info!("dtb: {}..{} ({} bytes)", dtb.start, dtb.end, dtb.end - dtb.start);
let text = text_range();
- println!("text: {}..{} ({} bytes)", text.start, text.end, text.end - text.start);
+ info!("text: {}..{} ({} bytes)", text.start, text.end, text.end - text.start);
let rodata = rodata_range();
- println!("rodata: {}..{} ({} bytes)", rodata.start, rodata.end, rodata.end - rodata.start);
- println!("binary end: {}", binary_end());
+ info!("rodata: {}..{} ({} bytes)", rodata.start, rodata.end, rodata.end - rodata.start);
+ info!("binary end: {}", binary_end());
let data = data_range();
- println!(
+ info!(
"data: {}..{} ({} bytes, loaded at {})",
data.start,
data.end,
@@ -89,9 +89,9 @@
data_load_address(),
);
let bss = bss_range();
- println!("bss: {}..{} ({} bytes)", bss.start, bss.end, bss.end - bss.start);
+ info!("bss: {}..{} ({} bytes)", bss.start, bss.end, bss.end - bss.start);
let boot_stack = boot_stack_range();
- println!(
+ info!(
"boot_stack: {}..{} ({} bytes)",
boot_stack.start,
boot_stack.end,
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 9ec2dc4..ed0275b 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -34,8 +34,8 @@
use core::ffi::CStr;
use fdtpci::PciInfo;
use libfdt::Fdt;
-use log::{debug, info, trace, LevelFilter};
-use vmbase::{logger, main, println};
+use log::{debug, error, info, trace, warn, LevelFilter};
+use vmbase::{logger, main};
static INITIALISED_DATA: [u32; 4] = [1, 2, 3, 4];
static mut ZEROED_DATA: [u32; 10] = [0; 10];
@@ -55,7 +55,7 @@
pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) {
logger::init(LevelFilter::Debug).unwrap();
- println!("Hello world");
+ info!("Hello world");
info!("x0={:#018x}, x1={:#018x}, x2={:#018x}, x3={:#018x}", arg0, arg1, arg2, arg3);
print_addresses();
assert_eq!(arg0, dtb_range().start.0 as u64);
@@ -127,6 +127,8 @@
let mut pci_root = unsafe { pci_info.make_pci_root() };
check_pci(&mut pci_root);
+
+ emit_suppressed_log();
}
fn check_stack_guard() {
@@ -236,3 +238,21 @@
]
);
}
+
+macro_rules! log_all_levels {
+ ($msg:literal) => {{
+ error!($msg);
+ warn!($msg);
+ info!($msg);
+ debug!($msg);
+ trace!($msg);
+ }};
+}
+
+fn emit_suppressed_log() {
+ {
+ let _guard = logger::suppress();
+ log_all_levels!("Suppressed message");
+ }
+ log_all_levels!("Unsuppressed message");
+}
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index 8f0eaa5..9088f1a 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -24,6 +24,7 @@
use anyhow::{Context, Error};
use log::info;
use std::{
+ collections::{HashSet, VecDeque},
fs::File,
io::{self, BufRead, BufReader, Read, Write},
os::unix::io::FromRawFd,
@@ -89,7 +90,7 @@
taskProfiles: vec![],
gdbPort: 0, // no gdb
});
- let console = android_log_fd()?;
+ let (handle, console) = android_log_fd()?;
let (mut log_reader, log_writer) = pipe()?;
let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log_writer), None)
.context("Failed to create VM")?;
@@ -99,6 +100,7 @@
// Wait for VM to finish, and check that it shut down cleanly.
let death_reason = vm.wait_for_death();
assert_eq!(death_reason, DeathReason::Shutdown);
+ handle.join().unwrap();
// Check that the expected string was written to the log VirtIO console device.
let expected = "Hello VirtIO console\n";
@@ -109,15 +111,10 @@
Ok(())
}
-fn android_log_fd() -> io::Result<File> {
+fn android_log_fd() -> Result<(thread::JoinHandle<()>, File), io::Error> {
let (reader, writer) = pipe()?;
-
- thread::spawn(|| {
- for line in BufReader::new(reader).lines() {
- info!("{}", line.unwrap());
- }
- });
- Ok(writer)
+ let handle = thread::spawn(|| VmLogProcessor::new(reader).run().unwrap());
+ Ok((handle, writer))
}
fn pipe() -> io::Result<(File, File)> {
@@ -129,3 +126,51 @@
Ok((reader, writer))
}
+
+struct VmLogProcessor {
+ reader: Option<File>,
+ expected: VecDeque<String>,
+ unexpected: HashSet<String>,
+ had_unexpected: bool,
+}
+
+impl VmLogProcessor {
+ fn messages() -> (VecDeque<String>, HashSet<String>) {
+ let mut expected = VecDeque::new();
+ let mut unexpected = HashSet::new();
+ for log_lvl in ["[ERROR]", "[WARN]", "[INFO]", "[DEBUG]"] {
+ expected.push_back(format!("{log_lvl} Unsuppressed message"));
+ unexpected.insert(format!("{log_lvl} Suppressed message"));
+ }
+ (expected, unexpected)
+ }
+
+ fn new(reader: File) -> Self {
+ let (expected, unexpected) = Self::messages();
+ Self { reader: Some(reader), expected, unexpected, had_unexpected: false }
+ }
+
+ fn verify(&mut self, msg: &str) {
+ if self.expected.front() == Some(&msg.to_owned()) {
+ self.expected.pop_front();
+ }
+ if !self.had_unexpected && self.unexpected.contains(msg) {
+ self.had_unexpected = true;
+ }
+ }
+
+ fn run(mut self) -> Result<(), &'static str> {
+ for line in BufReader::new(self.reader.take().unwrap()).lines() {
+ let msg = line.unwrap();
+ info!("{msg}");
+ self.verify(&msg);
+ }
+ if !self.expected.is_empty() {
+ Err("missing expected log message")
+ } else if self.had_unexpected {
+ Err("unexpected log message")
+ } else {
+ Ok(())
+ }
+ }
+}
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
index fabea91..7c8ddf6 100644
--- a/vmbase/src/console.rs
+++ b/vmbase/src/console.rs
@@ -40,14 +40,14 @@
/// Writes a string to the console.
///
/// Panics if [`init`] was not called first.
-pub fn write_str(s: &str) {
+pub(crate) fn write_str(s: &str) {
CONSOLE.lock().as_mut().unwrap().write_str(s).unwrap();
}
/// Writes a formatted string to the console.
///
/// Panics if [`init`] was not called first.
-pub fn write_args(format_args: Arguments) {
+pub(crate) fn write_args(format_args: Arguments) {
write(CONSOLE.lock().as_mut().unwrap(), format_args).unwrap();
}
@@ -69,20 +69,10 @@
let _ = write(&mut uart, format_args);
}
-/// Prints the given string to the console.
-///
-/// Panics if the console has not yet been initialised. May hang if used in an exception context;
-/// use `eprint!` instead.
-#[macro_export]
-macro_rules! print {
- ($($arg:tt)*) => ($crate::console::write_args(format_args!($($arg)*)));
-}
-
/// Prints the given formatted string to the console, followed by a newline.
///
/// Panics if the console has not yet been initialised. May hang if used in an exception context;
/// use `eprintln!` instead.
-#[macro_export]
macro_rules! println {
() => ($crate::console::write_str("\n"));
($($arg:tt)*) => ({
@@ -91,6 +81,8 @@
);
}
+pub(crate) use println; // Make it available in this crate.
+
/// Prints the given string to the console in an emergency, such as an exception handler.
///
/// Never panics.
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index 1510ae2..8cdfe77 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -36,12 +36,14 @@
/// Example:
///
/// ```rust
-/// use vmbase::main;
+/// use vmbase::{logger, main};
+/// use log::{info, LevelFilter};
///
/// main!(my_main);
///
/// fn my_main() {
-/// println!("Hello world");
+/// logger::init(LevelFilter::Info).unwrap();
+/// info!("Hello world");
/// }
/// ```
#[macro_export]
diff --git a/vmbase/src/logger.rs b/vmbase/src/logger.rs
index 5f0f1c2..94dc880 100644
--- a/vmbase/src/logger.rs
+++ b/vmbase/src/logger.rs
@@ -20,27 +20,71 @@
extern crate log;
-use super::println;
+use crate::console::println;
+use core::sync::atomic::{AtomicBool, Ordering};
use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
-struct Logger;
-static LOGGER: Logger = Logger;
+struct Logger {
+ is_enabled: AtomicBool,
+}
+static mut LOGGER: Logger = Logger::new();
+
+impl Logger {
+ const fn new() -> Self {
+ Self { is_enabled: AtomicBool::new(true) }
+ }
+
+ fn swap_enabled(&mut self, enabled: bool) -> bool {
+ self.is_enabled.swap(enabled, Ordering::Relaxed)
+ }
+}
impl Log for Logger {
fn enabled(&self, _metadata: &Metadata) -> bool {
- true
+ self.is_enabled.load(Ordering::Relaxed)
}
fn log(&self, record: &Record) {
- println!("[{}] {}", record.level(), record.args());
+ if self.enabled(record.metadata()) {
+ println!("[{}] {}", record.level(), record.args());
+ }
}
fn flush(&self) {}
}
+/// An RAII implementation of a log suppressor. When the instance is dropped, logging is re-enabled.
+pub struct SuppressGuard {
+ old_enabled: bool,
+}
+
+impl SuppressGuard {
+ fn new() -> Self {
+ // Safe because it modifies an atomic.
+ unsafe { Self { old_enabled: LOGGER.swap_enabled(false) } }
+ }
+}
+
+impl Drop for SuppressGuard {
+ fn drop(&mut self) {
+ // Safe because it modifies an atomic.
+ unsafe {
+ LOGGER.swap_enabled(self.old_enabled);
+ }
+ }
+}
+
/// Initialize vmbase logger with a given max logging level.
pub fn init(max_level: LevelFilter) -> Result<(), SetLoggerError> {
- log::set_logger(&LOGGER)?;
+ // Safe because it only sets the global logger.
+ unsafe {
+ log::set_logger(&LOGGER)?;
+ }
log::set_max_level(max_level);
Ok(())
}
+
+/// Suppress logging until the return value goes out of scope.
+pub fn suppress() -> SuppressGuard {
+ SuppressGuard::new()
+}