Merge "Initialise logger for tests."
diff --git a/launcher/main.cpp b/launcher/main.cpp
index c3f9988..5ae9956 100644
--- a/launcher/main.cpp
+++ b/launcher/main.cpp
@@ -61,7 +61,7 @@
return EXIT_FAILURE;
}
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ android::base::InitLogging(argv);
const char* libname = argv[1];
auto handle = load(libname);
diff --git a/libs/hyp/src/error.rs b/libs/hyp/src/error.rs
index 408150e..b8498ca 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::GeniezoneError;
use crate::KvmError;
use core::{fmt, result};
use uuid::Uuid;
@@ -28,7 +29,9 @@
MmioGuardNotsupported,
/// Failed to invoke a certain KVM HVC function.
KvmError(KvmError, u32),
- /// Unsupported Hypervisor.
+ /// Failed to invoke GenieZone HVC function.
+ GeniezoneError(GeniezoneError, u32),
+ /// Unsupported Hypervisor
UnsupportedHypervisorUuid(Uuid),
/// The MMIO_GUARD granule used by the hypervisor is not supported.
UnsupportedMmioGuardGranule(usize),
@@ -41,6 +44,12 @@
Self::KvmError(e, function_id) => {
write!(f, "Failed to invoke the HVC function with function ID {function_id}: {e}")
}
+ Self::GeniezoneError(e, function_id) => {
+ write!(
+ f,
+ "Failed to invoke GenieZone HVC function with function ID {function_id}: {e}"
+ )
+ }
Self::UnsupportedHypervisorUuid(u) => {
write!(f, "Unsupported Hypervisor UUID {u}")
}
diff --git a/libs/hyp/src/hypervisor/geniezone.rs b/libs/hyp/src/hypervisor/geniezone.rs
new file mode 100644
index 0000000..0741978
--- /dev/null
+++ b/libs/hyp/src/hypervisor/geniezone.rs
@@ -0,0 +1,159 @@
+// 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.
+
+//! Wrappers around calls to the GenieZone hypervisor.
+
+use super::common::{Hypervisor, HypervisorCap, MMIO_GUARD_GRANULE_SIZE};
+use crate::error::{Error, Result};
+use crate::util::page_address;
+use core::fmt::{self, Display, Formatter};
+use smccc::{
+ error::{positive_or_error_64, success_or_error_64},
+ hvc64,
+};
+use uuid::{uuid, Uuid};
+
+pub(super) struct GeniezoneHypervisor;
+
+const ARM_SMCCC_GZVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
+const ARM_SMCCC_GZVM_FUNC_MEM_SHARE: u32 = 0xc6000003;
+const ARM_SMCCC_GZVM_FUNC_MEM_UNSHARE: u32 = 0xc6000004;
+
+const VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
+const VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID: u32 = 0xc6000006;
+const VENDOR_HYP_GZVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
+const VENDOR_HYP_GZVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
+
+impl GeniezoneHypervisor {
+ // We generate this uuid by ourselves to identify GenieZone hypervisor
+ // and share the same identification along with guest VMs.
+ // The previous uuid was removed due to duplication elsewhere.
+ pub const UUID: Uuid = uuid!("7e134ed0-3b82-488d-8cee-69c19211dbe7");
+ const CAPABILITIES: HypervisorCap = HypervisorCap::DYNAMIC_MEM_SHARE;
+}
+
+/// Error from a GenieZone HVC call.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum GeniezoneError {
+ /// The call is not supported by the implementation.
+ NotSupported,
+ /// The call is not required to implement.
+ NotRequired,
+ /// One of the call parameters has a invalid value.
+ InvalidParameter,
+ /// There was an unexpected return value.
+ Unknown(i64),
+}
+
+impl From<i64> for GeniezoneError {
+ fn from(value: i64) -> Self {
+ match value {
+ -1 => GeniezoneError::NotSupported,
+ -2 => GeniezoneError::NotRequired,
+ -3 => GeniezoneError::InvalidParameter,
+ _ => GeniezoneError::Unknown(value),
+ }
+ }
+}
+
+impl From<i32> for GeniezoneError {
+ fn from(value: i32) -> Self {
+ i64::from(value).into()
+ }
+}
+
+impl Display for GeniezoneError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::NotSupported => write!(f, "GenieZone call not supported"),
+ Self::NotRequired => write!(f, "GenieZone call not required"),
+ Self::InvalidParameter => write!(f, "GenieZone call received invalid value"),
+ Self::Unknown(e) => write!(f, "Unknown return value from GenieZone {} ({0:#x})", e),
+ }
+ }
+}
+
+impl Hypervisor for GeniezoneHypervisor {
+ fn mmio_guard_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));
+ }
+ Ok(())
+ }
+
+ fn mmio_guard_map(&self, addr: usize) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = page_address(addr);
+
+ checked_hvc64_expect_zero(VENDOR_HYP_GZVM_MMIO_GUARD_MAP_FUNC_ID, args)
+ }
+
+ fn mmio_guard_unmap(&self, addr: usize) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = page_address(addr);
+
+ checked_hvc64_expect_zero(VENDOR_HYP_GZVM_MMIO_GUARD_UNMAP_FUNC_ID, args)
+ }
+
+ fn mem_share(&self, base_ipa: u64) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = base_ipa;
+
+ checked_hvc64_expect_zero(ARM_SMCCC_GZVM_FUNC_MEM_SHARE, args)
+ }
+
+ fn mem_unshare(&self, base_ipa: u64) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = base_ipa;
+
+ checked_hvc64_expect_zero(ARM_SMCCC_GZVM_FUNC_MEM_UNSHARE, args)
+ }
+
+ fn memory_protection_granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(ARM_SMCCC_GZVM_FUNC_HYP_MEMINFO, args)?;
+ Ok(granule.try_into().unwrap())
+ }
+
+ fn has_cap(&self, cap: HypervisorCap) -> bool {
+ Self::CAPABILITIES.contains(cap)
+ }
+}
+
+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))
+}
+
+fn checked_hvc64(function: u32, args: [u64; 17]) -> Result<u64> {
+ positive_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::GeniezoneError(e, function))
+}
diff --git a/libs/hyp/src/hypervisor/mod.rs b/libs/hyp/src/hypervisor/mod.rs
index 923a21d..93d53fe 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -17,6 +17,7 @@
extern crate alloc;
mod common;
+mod geniezone;
mod gunyah;
mod kvm;
@@ -25,6 +26,8 @@
pub use common::Hypervisor;
pub use common::HypervisorCap;
pub use common::MMIO_GUARD_GRANULE_SIZE;
+pub use geniezone::GeniezoneError;
+use geniezone::GeniezoneHypervisor;
use gunyah::GunyahHypervisor;
pub use kvm::KvmError;
use kvm::KvmHypervisor;
@@ -35,6 +38,7 @@
enum HypervisorBackend {
Kvm,
Gunyah,
+ Geniezone,
}
impl HypervisorBackend {
@@ -42,6 +46,7 @@
match self {
Self::Kvm => &KvmHypervisor,
Self::Gunyah => &GunyahHypervisor,
+ Self::Geniezone => &GeniezoneHypervisor,
}
}
}
@@ -51,6 +56,7 @@
fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
match uuid {
+ GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
KvmHypervisor::UUID => Ok(HypervisorBackend::Kvm),
u => Err(Error::UnsupportedHypervisorUuid(u)),
diff --git a/libs/hyp/src/lib.rs b/libs/hyp/src/lib.rs
index 2c2d1d6..32a59d1 100644
--- a/libs/hyp/src/lib.rs
+++ b/libs/hyp/src/lib.rs
@@ -22,3 +22,5 @@
pub use error::{Error, Result};
pub use hypervisor::{get_hypervisor, Hypervisor, HypervisorCap, KvmError, MMIO_GUARD_GRANULE_SIZE};
+
+use hypervisor::GeniezoneError;
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 37f68a2..5440695 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -55,10 +55,8 @@
"microdroid_ueventd_rc",
"microdroid_launcher",
- "libbinder",
"libbinder_ndk",
"libstdc++",
- "secilc",
// "com.android.adbd" requires these,
"libadbd_auth",
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 495d3bb..d854d54 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -13,6 +13,7 @@
"android.system.virtualizationservice-rust",
"android.system.virtualmachineservice-rust",
"android.system.virtualization.payload-rust",
+ "libandroid_logger",
"libanyhow",
"libapexutil_rust",
"libapkverify",
@@ -25,7 +26,6 @@
"libglob",
"libhex",
"libitertools",
- "libkernlog",
"libkeystore2_crypto_rust",
"liblibc",
"liblog_rust",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 8fa2807..9c19feb 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -211,7 +211,11 @@
}
fn try_main() -> Result<()> {
- let _ignored = kernlog::init();
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("microdroid_manager")
+ .with_min_level(log::Level::Info),
+ );
info!("started.");
// SAFETY: This is the only place we take the ownership of the fd of the vm payload service.
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index 5353a21..49c4717 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -2,8 +2,8 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-rust_defaults {
- name: "libpvmfw_avb_nostd_defaults",
+rust_library_rlib {
+ name: "libpvmfw_avb_nostd",
crate_name: "pvmfw_avb",
srcs: ["src/lib.rs"],
prefer_rlib: true,
@@ -16,11 +16,6 @@
whole_static_libs: [
"libavb_baremetal",
],
-}
-
-rust_library_rlib {
- name: "libpvmfw_avb_nostd",
- defaults: ["libpvmfw_avb_nostd_defaults"],
no_stdlibs: true,
stdlibs: [
"libcore.rust_sysroot",
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 0d2dfda..2582d55 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -15,6 +15,7 @@
//! Low-level entry and exit points of pvmfw.
use crate::config;
+use crate::configure_global_allocator_size;
use crate::crypto;
use crate::fdt;
use crate::heap;
@@ -36,7 +37,7 @@
console,
layout::{self, crosvm},
logger, main,
- memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_4KB},
+ memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
power::reboot,
};
use zeroize::Zeroize;
@@ -62,6 +63,7 @@
}
main!(start);
+configure_global_allocator_size!(SIZE_128KB);
/// Entry point for pVM firmware.
pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
diff --git a/pvmfw/src/heap.rs b/pvmfw/src/heap.rs
index 151049e..a28a02c 100644
--- a/pvmfw/src/heap.rs
+++ b/pvmfw/src/heap.rs
@@ -27,14 +27,30 @@
use buddy_system_allocator::LockedHeap;
-/// 128 KiB
-const HEAP_SIZE: usize = 0x20000;
-static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
+/// Configures the size of the global allocator.
+#[macro_export]
+macro_rules! configure_global_allocator_size {
+ ($len:expr) => {
+ static mut __HEAP_ARRAY: [u8; $len] = [0; $len];
+ #[export_name = "HEAP"]
+ // SAFETY - HEAP will only be accessed once as mut, from init().
+ static mut __HEAP: &'static mut [u8] = unsafe { &mut __HEAP_ARRAY };
+ };
+}
+
+extern "Rust" {
+ /// Slice used by the global allocator, configured using configure_global_allocator_size!().
+ static mut HEAP: &'static mut [u8];
+}
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
-/// SAFETY: Must be called no more than once.
+/// Initialize the global allocator.
+///
+/// # Safety
+///
+/// Must be called no more than once.
pub unsafe fn init() {
// SAFETY: Nothing else accesses this memory, and we hand it over to the heap to manage and
// never touch it again. The heap is locked, so there cannot be any races.
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index f58ce81..7e6080f 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -160,7 +160,7 @@
private StringBuilder mLogOutput = new StringBuilder();
private boolean mProcessedBootTimeMetrics = false;
- private void processBootTimeMetrics(String log) {
+ private synchronized void processBootTimeMetrics(String log) {
if (!mVcpuStartedNanoTime.isPresent()) {
mVcpuStartedNanoTime = OptionalLong.of(System.nanoTime());
}
@@ -177,12 +177,8 @@
}
private void logVmOutputAndMonitorBootTimeMetrics(
- String tag,
- InputStream vmOutputStream,
- String name,
- StringBuilder result,
- boolean monitorEvents) {
- mProcessedBootTimeMetrics |= monitorEvents;
+ String tag, InputStream vmOutputStream, String name, StringBuilder result) {
+ mProcessedBootTimeMetrics = true;
new Thread(
() -> {
try {
@@ -192,7 +188,7 @@
String line;
while ((line = reader.readLine()) != null
&& !Thread.interrupted()) {
- if (monitorEvents) processBootTimeMetrics(line);
+ processBootTimeMetrics(line);
Log.i(tag, name + ": " + line);
result.append(line + "\n");
}
@@ -203,17 +199,6 @@
.start();
}
- private void logVmOutputAndMonitorBootTimeMetrics(
- String tag, InputStream vmOutputStream, String name, StringBuilder result) {
- logVmOutputAndMonitorBootTimeMetrics(tag, vmOutputStream, name, result, true);
- }
-
- /** Copy output from the VM to logcat. This is helpful when things go wrong. */
- protected void logVmOutput(
- String tag, InputStream vmOutputStream, String name, StringBuilder result) {
- logVmOutputAndMonitorBootTimeMetrics(tag, vmOutputStream, name, result, false);
- }
-
public void runToFinish(String logTag, VirtualMachine vm)
throws VirtualMachineException, InterruptedException {
vm.setCallback(mExecutorService, this);
@@ -221,7 +206,7 @@
if (vm.getConfig().isVmOutputCaptured()) {
logVmOutputAndMonitorBootTimeMetrics(
logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
- logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
+ logVmOutputAndMonitorBootTimeMetrics(logTag, vm.getLogOutput(), "Log", mLogOutput);
}
mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 42a3085..1f5fcee 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -55,6 +55,7 @@
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -472,7 +473,7 @@
key, keyOverrides, /*isProtected=*/ false, /*updateBootconfigs=*/ true);
assertThatEventually(
100000,
- () -> getDevice().pullFileContents(CONSOLE_PATH),
+ () -> getDevice().pullFileContents(LOG_PATH),
containsString("boot completed, time to run payload"));
vmInfo.mProcess.destroy();
@@ -845,43 +846,6 @@
}
@Test
- public void testCustomVirtualMachinePermission() throws Exception {
- assumeTrue(
- "Protected VMs are not supported",
- getAndroidDevice().supportsMicrodroid(/*protectedVm=*/ true));
- assumeTrue("Test requires adb unroot", getDevice().disableAdbRoot());
- CommandRunner android = new CommandRunner(getDevice());
-
- // Pull etc/microdroid.json
- File virtApexDir = FileUtil.createTempDir("virt_apex");
- File microdroidConfigFile = new File(virtApexDir, "microdroid.json");
- assertThat(getDevice().pullFile(VIRT_APEX + "etc/microdroid.json", microdroidConfigFile))
- .isTrue();
- JSONObject config = new JSONObject(FileUtil.readStringFromFile(microdroidConfigFile));
-
- // USE_CUSTOM_VIRTUAL_MACHINE is enforced only on protected mode
- config.put("protected", true);
-
- // Write updated config
- final String configPath = TEST_ROOT + "raw_config.json";
- getDevice().pushString(config.toString(), configPath);
-
- // temporarily revoke the permission
- android.run(
- "pm",
- "revoke",
- SHELL_PACKAGE_NAME,
- "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
- final String ret =
- android.runForResult(VIRT_APEX + "bin/vm run", configPath).getStderr().trim();
-
- assertThat(ret)
- .contains(
- "does not have the android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
- + " permission");
- }
-
- @Test
public void testPathToBinaryIsRejected() throws Exception {
CommandRunner android = new CommandRunner(getDevice());
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 0ddafeb..a64b62a 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1251,7 +1251,7 @@
assertThat(payloadStarted.getNow(false)).isTrue();
assertThat(exitCodeFuture.getNow(0)).isNotEqualTo(0);
- assertThat(listener.getConsoleOutput()).contains(reason);
+ assertThat(listener.getLogOutput()).contains(reason);
}
@Test
@@ -1609,7 +1609,7 @@
.command(
"logcat",
"-e",
- "virtualizationmanager::aidl: Console.*executing main task",
+ "virtualizationmanager::aidl: Log.*executing main task",
"-t",
time)
.start();
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 86c8596..9cd70e6 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -116,6 +116,20 @@
.context("failed to create idsig")?;
let mut output = clone_file(idsig_fd)?;
+
+ // Optimization. We don't have to update idsig file whenever a VM is started. Don't update it,
+ // if the idsig file already has the same APK digest.
+ if output.metadata()?.len() > 0 {
+ if let Ok(out_sig) = V4Signature::from_idsig(&mut output) {
+ if out_sig.signing_info.apk_digest == sig.signing_info.apk_digest {
+ debug!("idsig {:?} is up-to-date with apk {:?}.", output, input);
+ return Ok(());
+ }
+ }
+ // if we fail to read v4signature from output, that's fine. User can pass a random file.
+ // We will anyway overwrite the file to the v4signature generated from input_fd.
+ }
+
output.set_len(0).context("failed to set_len on the idsig output")?;
sig.write_into(&mut output).context("failed to write idsig")?;
Ok(())
@@ -236,9 +250,6 @@
input_fd: &ParcelFileDescriptor,
idsig_fd: &ParcelFileDescriptor,
) -> binder::Result<()> {
- // TODO(b/193504400): do this only when (1) idsig_fd is empty or (2) the APK digest in
- // idsig_fd is different from APK digest in input_fd
-
check_manage_access()?;
create_or_update_idsig_file(input_fd, idsig_fd)
@@ -1300,4 +1311,30 @@
assert!(ret.is_err(), "should fail");
Ok(())
}
+
+ #[test]
+ fn test_create_or_update_idsig_does_not_update_if_already_valid() -> Result<()> {
+ use std::io::Seek;
+
+ // Pick any APK
+ let mut apk = File::open("/system/priv-app/Shell/Shell.apk").unwrap();
+ let mut idsig = tempfile::tempfile().unwrap();
+
+ create_or_update_idsig_file(
+ &ParcelFileDescriptor::new(apk.try_clone()?),
+ &ParcelFileDescriptor::new(idsig.try_clone()?),
+ )?;
+ let modified_orig = idsig.metadata()?.modified()?;
+ apk.rewind()?;
+ idsig.rewind()?;
+
+ // Call the function again
+ create_or_update_idsig_file(
+ &ParcelFileDescriptor::new(apk.try_clone()?),
+ &ParcelFileDescriptor::new(idsig.try_clone()?),
+ )?;
+ let modified_new = idsig.metadata()?.modified()?;
+ assert!(modified_orig == modified_new, "idsig file was updated unnecessarily");
+ Ok(())
+ }
}
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index 384a9c1..6abe66e 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -25,7 +25,7 @@
pci::{bus::PciRoot, virtio_device_type, PciTransport},
DeviceType, Transport,
},
- BufferDirection, Hal, PhysAddr, PAGE_SIZE,
+ BufferDirection, Error, Hal, PhysAddr, PAGE_SIZE,
};
/// The standard sector size of a VirtIO block device, in bytes.
@@ -36,6 +36,7 @@
pub fn check_pci(pci_root: &mut PciRoot) {
let mut checked_virtio_device_count = 0;
+ let mut block_device_count = 0;
for (device_function, info) in pci_root.enumerate_bus(0) {
let (status, command) = pci_root.get_status_command(device_function);
info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
@@ -47,24 +48,31 @@
transport.device_type(),
transport.read_device_features(),
);
- if check_virtio_device(transport, virtio_type) {
- checked_virtio_device_count += 1;
+ match virtio_type {
+ DeviceType::Block => {
+ check_virtio_block_device(transport, block_device_count);
+ block_device_count += 1;
+ checked_virtio_device_count += 1;
+ }
+ DeviceType::Console => {
+ check_virtio_console_device(transport);
+ checked_virtio_device_count += 1;
+ }
+ _ => {}
}
}
}
- assert_eq!(checked_virtio_device_count, 4);
+ assert_eq!(checked_virtio_device_count, 5);
+ assert_eq!(block_device_count, 2);
}
-/// Checks the given VirtIO device, if we know how to.
-///
-/// Returns true if the device was checked, or false if it was ignored.
-fn check_virtio_device(transport: impl Transport, device_type: DeviceType) -> bool {
- match device_type {
- DeviceType::Block => {
- let mut blk =
- VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
- info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
+/// Checks the given VirtIO block device.
+fn check_virtio_block_device(transport: impl Transport, index: usize) {
+ let mut blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
+ info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
+ match index {
+ 0 => {
assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
for i in 0..EXPECTED_SECTOR_COUNT {
@@ -75,22 +83,27 @@
assert_eq!(chunk, &(i as u32).to_le_bytes());
}
info!("Read expected data from block device.");
- true
}
- DeviceType::Console => {
- let mut console = VirtIOConsole::<HalImpl, _>::new(transport)
- .expect("Failed to create VirtIO console driver");
- info!("Found console device: {:?}", console.info());
- for &c in b"Hello VirtIO console\n" {
- console.send(c).expect("Failed to send character to VirtIO console device");
- }
- info!("Wrote to VirtIO console.");
- true
+ 1 => {
+ assert_eq!(blk.capacity(), 0);
+ let mut data = [0; SECTOR_SIZE_BYTES];
+ assert_eq!(blk.read_block(0, &mut data), Err(Error::IoError));
}
- _ => false,
+ _ => panic!("Unexpected VirtIO block device index {}.", index),
}
}
+/// Checks the given VirtIO console device.
+fn check_virtio_console_device(transport: impl Transport) {
+ let mut console = VirtIOConsole::<HalImpl, _>::new(transport)
+ .expect("Failed to create VirtIO console driver");
+ info!("Found console device: {:?}", console.info());
+ for &c in b"Hello VirtIO console\n" {
+ console.send(c).expect("Failed to send character to VirtIO console device");
+ }
+ info!("Wrote to VirtIO console.");
+}
+
/// Gets the memory region in which BARs are allocated.
pub fn get_bar_region(pci_info: &PciInfo) -> MemoryRegion {
MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index 9088f1a..085a620 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -35,6 +35,8 @@
const VMBASE_EXAMPLE_PATH: &str =
"/data/local/tmp/vmbase_example.integration_test/arm64/vmbase_example.bin";
const TEST_DISK_IMAGE_PATH: &str = "/data/local/tmp/vmbase_example.integration_test/test_disk.img";
+const EMPTY_DISK_IMAGE_PATH: &str =
+ "/data/local/tmp/vmbase_example.integration_test/empty_disk.img";
/// Runs the vmbase_example VM as an unprotected VM via VirtualizationService.
#[test]
@@ -76,13 +78,25 @@
let test_image = ParcelFileDescriptor::new(test_image);
let disk_image = DiskImage { image: Some(test_image), writable: false, partitions: vec![] };
+ // Make file for empty test disk image.
+ let empty_image = File::options()
+ .create(true)
+ .read(true)
+ .write(true)
+ .truncate(true)
+ .open(EMPTY_DISK_IMAGE_PATH)
+ .with_context(|| format!("Failed to open empty disk image {}", EMPTY_DISK_IMAGE_PATH))?;
+ let empty_image = ParcelFileDescriptor::new(empty_image);
+ let empty_disk_image =
+ DiskImage { image: Some(empty_image), writable: false, partitions: vec![] };
+
let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
name: String::from("VmBaseTest"),
kernel: None,
initrd: None,
params: None,
bootloader: Some(bootloader),
- disks: vec![disk_image],
+ disks: vec![disk_image, empty_disk_image],
protectedVm: false,
memoryMib: 300,
cpuTopology: CpuTopology::ONE_CPU,
diff --git a/vmbase/src/memory/mod.rs b/vmbase/src/memory/mod.rs
index 9f14691..6bc600d 100644
--- a/vmbase/src/memory/mod.rs
+++ b/vmbase/src/memory/mod.rs
@@ -25,5 +25,5 @@
pub use shared::{alloc_shared, dealloc_shared, 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_2MB, SIZE_4KB, SIZE_4MB,
+ PAGE_SIZE, SIZE_128KB, SIZE_2MB, SIZE_4KB, SIZE_4MB,
};
diff --git a/vmbase/src/memory/util.rs b/vmbase/src/memory/util.rs
index 04d42cd..48007f3 100644
--- a/vmbase/src/memory/util.rs
+++ b/vmbase/src/memory/util.rs
@@ -22,6 +22,8 @@
/// The size of a 4KB memory in bytes.
pub const SIZE_4KB: usize = 4 << 10;
+/// The size of a 128KB memory in bytes.
+pub const SIZE_128KB: usize = 128 << 10;
/// The size of a 2MB memory in bytes.
pub const SIZE_2MB: usize = 2 << 20;
/// The size of a 4MB memory in bytes.