Merge "Add a script to build the debian guest image for AVF" into main
diff --git a/android/virtmgr/Android.bp b/android/virtmgr/Android.bp
index f0b6881..d0d7915 100644
--- a/android/virtmgr/Android.bp
+++ b/android/virtmgr/Android.bp
@@ -34,7 +34,6 @@
"libapkverify",
"libavf_features",
"libavflog",
- "libbase_rust",
"libbinder_rs",
"libcfg_if",
"libclap",
@@ -70,6 +69,7 @@
"liblibfdt",
"libfsfdt",
"libhypervisor_props",
+ "libzerocopy",
"libuuid",
// TODO(b/202115393) stabilize the interface
"packagemanager_aidl-rust",
diff --git a/android/virtmgr/src/composite.rs b/android/virtmgr/src/composite.rs
index 681ec59..1219150 100644
--- a/android/virtmgr/src/composite.rs
+++ b/android/virtmgr/src/composite.rs
@@ -15,13 +15,16 @@
//! Functions for creating a composite disk image.
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition;
-use anyhow::{anyhow, Context, Error};
-use disk::{
- create_composite_disk, create_disk_file, ImagePartitionType, PartitionInfo, MAX_NESTING_DEPTH,
-};
+use anyhow::{bail, Context, Error};
+use disk::{create_composite_disk, ImagePartitionType, PartitionInfo};
use std::fs::{File, OpenOptions};
+use std::io::ErrorKind;
+use std::os::unix::fs::FileExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
+use zerocopy::AsBytes;
+use zerocopy::FromBytes;
+use zerocopy::FromZeroes;
use uuid::Uuid;
@@ -98,7 +101,7 @@
.context("Failed to clone partition image file descriptor")?
.into();
let path = fd_path_for_file(&file);
- let size = get_partition_size(&file, &path)?;
+ let size = get_partition_size(&file)?;
files.push(file);
Ok(PartitionInfo {
@@ -122,16 +125,74 @@
/// Find the size of the partition image in the given file by parsing the header.
///
-/// This will work for raw, QCOW2, composite and Android sparse images.
-fn get_partition_size(partition: &File, path: &Path) -> Result<u64, Error> {
- // TODO: Use `context` once disk::Error implements std::error::Error.
- // TODO: Add check for is_sparse_file
- Ok(create_disk_file(
- partition.try_clone()?,
- /* is_sparse_file */ false,
- MAX_NESTING_DEPTH,
- path,
- )
- .map_err(|e| anyhow!("Failed to open partition image: {}", e))?
- .get_len()?)
+/// This will work for raw and Android sparse images. QCOW2 and composite images aren't supported.
+fn get_partition_size(file: &File) -> Result<u64, Error> {
+ match detect_image_type(file).context("failed to detect partition image type")? {
+ ImageType::Raw => Ok(file.metadata().context("failed to get metadata")?.len()),
+ ImageType::AndroidSparse => {
+ // Source: system/core/libsparse/sparse_format.h
+ #[repr(C)]
+ #[derive(Clone, Copy, Debug, AsBytes, FromZeroes, FromBytes)]
+ struct SparseHeader {
+ magic: u32,
+ major_version: u16,
+ minor_version: u16,
+ file_hdr_sz: u16,
+ chunk_hdr_size: u16,
+ blk_sz: u32,
+ total_blks: u32,
+ total_chunks: u32,
+ image_checksum: u32,
+ }
+ let mut header = SparseHeader::new_zeroed();
+ file.read_exact_at(header.as_bytes_mut(), 0)
+ .context("failed to read android sparse header")?;
+ let len = u64::from(header.total_blks)
+ .checked_mul(header.blk_sz.into())
+ .context("android sparse image len too big")?;
+ Ok(len)
+ }
+ t => bail!("unsupported partition image type: {t:?}"),
+ }
+}
+
+/// Image file types we can detect.
+#[derive(Debug, PartialEq, Eq)]
+enum ImageType {
+ Raw,
+ Qcow2,
+ CompositeDisk,
+ AndroidSparse,
+}
+
+/// Detect image type by looking for magic bytes.
+fn detect_image_type(file: &File) -> std::io::Result<ImageType> {
+ const CDISK_MAGIC: &str = "composite_disk\x1d";
+ const QCOW_MAGIC: u32 = 0x5146_49fb;
+ const SPARSE_HEADER_MAGIC: u32 = 0xed26ff3a;
+
+ let mut magic4 = [0u8; 4];
+ match file.read_exact_at(&mut magic4[..], 0) {
+ Ok(()) => {}
+ Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
+ Err(e) => return Err(e),
+ }
+ if magic4 == QCOW_MAGIC.to_be_bytes() {
+ return Ok(ImageType::Qcow2);
+ }
+ if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
+ return Ok(ImageType::AndroidSparse);
+ }
+
+ let mut buf = [0u8; CDISK_MAGIC.len()];
+ match file.read_exact_at(buf.as_bytes_mut(), 0) {
+ Ok(()) => {}
+ Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
+ Err(e) => return Err(e),
+ }
+ if buf == CDISK_MAGIC.as_bytes() {
+ return Ok(ImageType::CompositeDisk);
+ }
+
+ Ok(ImageType::Raw)
}
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 5886535..b2be736 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -57,7 +57,6 @@
use rpcbinder::RpcServer;
/// external/crosvm
-use base::UnixSeqpacketListener;
use vm_control::{BalloonControlCommand, VmRequest, VmResponse};
const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
@@ -1045,8 +1044,9 @@
}
for disk in config.disks {
+ // Disk file locking is disabled because of missing SELinux policies.
command.arg("--block").arg(format!(
- "path={},ro={}",
+ "path={},ro={},lock=false",
add_preserved_fd(&mut preserved_fds, disk.image),
!disk.writable,
));
@@ -1056,8 +1056,8 @@
command.arg(add_preserved_fd(&mut preserved_fds, kernel));
}
- let control_sock = UnixSeqpacketListener::bind(crosvm_control_socket_path)
- .context("failed to create control server")?;
+ let control_sock = create_crosvm_control_listener(crosvm_control_socket_path)
+ .context("failed to create control listener")?;
command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, control_sock));
if let Some(dt_overlay) = config.device_tree_overlay {
@@ -1271,3 +1271,22 @@
let (read_fd, write_fd) = pipe2(OFlag::O_CLOEXEC)?;
Ok((read_fd.into(), write_fd.into()))
}
+
+/// Creates and binds a unix seqpacket listening socket to be passed as crosvm's `--socket`
+/// argument. See `UnixSeqpacketListener::bind` in crosvm's code for reference.
+fn create_crosvm_control_listener(crosvm_control_socket_path: &Path) -> Result<OwnedFd> {
+ use nix::sys::socket;
+ let fd = socket::socket(
+ socket::AddressFamily::Unix,
+ socket::SockType::SeqPacket,
+ socket::SockFlag::empty(),
+ None,
+ )
+ .context("socket failed")?;
+ socket::bind(fd.as_raw_fd(), &socket::UnixAddr::new(crosvm_control_socket_path)?)
+ .context("bind failed")?;
+ // The exact backlog size isn't imporant. crosvm uses 128 internally. We use 127 here
+ // because of a `nix` bug.
+ socket::listen(&fd, socket::Backlog::new(127).unwrap()).context("listen failed")?;
+ Ok(fd)
+}
diff --git a/android/virtualizationservice/aidl/Android.bp b/android/virtualizationservice/aidl/Android.bp
index c1bff5e..79a9d40 100644
--- a/android/virtualizationservice/aidl/Android.bp
+++ b/android/virtualizationservice/aidl/Android.bp
@@ -29,6 +29,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
@@ -149,6 +150,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
index 11a2115..99dc648 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
@@ -20,7 +20,12 @@
/** A label for the partition. */
@utf8InCpp String label;
- /** The backing file descriptor of the partition image. */
+ /**
+ * The backing file descriptor of the partition image.
+ *
+ * The image file must either be a raw binary file, or an android-sparse
+ * formatted file.
+ */
ParcelFileDescriptor image;
/** Whether the partition should be writable by the VM. */
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index f493202..be62d18 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -107,6 +107,7 @@
filesystems: microdroid_filesystem_images,
prebuilts: [
"rialto_bin",
+ "android_bootloader_crosvm_aarch64",
],
},
x86_64: {
@@ -125,6 +126,9 @@
default: [],
}),
filesystems: microdroid_filesystem_images,
+ prebuilts: [
+ "android_bootloader_crosvm_x86_64",
+ ],
},
},
binaries: [
@@ -138,7 +142,6 @@
"microdroid.json",
"microdroid_kernel",
"com.android.virt.init.rc",
- "android_bootloader_crosvm_aarch64",
] + select(soong_config_variable("ANDROID", "avf_microdroid_guest_gki_version"), {
"android15_66": [
"microdroid_gki-android15-6.6_initrd_debuggable",
diff --git a/docs/device_assignment.md b/docs/device_assignment.md
index 4b2296c..6011d8f 100644
--- a/docs/device_assignment.md
+++ b/docs/device_assignment.md
@@ -205,6 +205,18 @@
* `<sysfs_path>`: Sysfs path of the device in host, used to bind to the VFIO
driver. Must be non-empty and unique in the XML.
+### List support assignable devices
+
+In order to query list of the devices that can be assigned to a pVM, run the
+following command:
+
+```bash
+adb shell /apex/com.android.virt/bin/vm info
+```
+
+All supported assignable devices will be located under the "Assignable devices:"
+section of the output.
+
## Boot with VM DTBO
Bootloader should provide VM DTBO to both Android and pvmfw.
diff --git a/docs/img/rkpvm-dice-chain.png b/docs/img/rkpvm-dice-chain.png
new file mode 100644
index 0000000..6847f7f
--- /dev/null
+++ b/docs/img/rkpvm-dice-chain.png
Binary files differ
diff --git a/docs/vm_remote_attestation.md b/docs/vm_remote_attestation.md
index 79f44b9..ee20591 100644
--- a/docs/vm_remote_attestation.md
+++ b/docs/vm_remote_attestation.md
@@ -46,17 +46,17 @@
spec.
[open-dice]: https://android.googlesource.com/platform/external/open-dice/+/main/docs/android.md
-[rkpvm-marker]: https://android.googlesource.com/platform/external/open-dice/+/main/docs/android.md#Configuration-descriptor
-[rkp-hal]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md
### pVM attestation
Once the RKP VM is successfully attested, it acts as a trusted platform to
attest pVMs. Leveraging its trusted status, the RKP VM validates the integrity
-of each pVM's DICE chain by comparing it against its own DICE chain. This
-validation process ensures that the pVMs are running in the expected VM
-environment and certifies the payload executed within each pVM. Currently, only
-Microdroid VMs are supported.
+of each [pVM DICE chain][pvm-dice-chain] by comparing it against its own DICE
+chain. This validation process ensures that the pVMs are running in the expected
+VM environment and certifies the payload executed within each pVM. Currently,
+only Microdroid VMs are supported.
+
+[pvm-dice-chain]: ./pvm_dice_chain.md
## API
@@ -113,13 +113,37 @@
## To Support It
-VM remote attestation is a strongly recommended feature from Android V. To support
-it, you only need to provide a valid VM DICE chain satisfying the following
-requirements:
+VM remote attestation is a strongly recommended feature from Android V. To
+support it, you only need to provide a valid VM DICE chain satisfying the
+following requirements:
-- The DICE chain must have a UDS-rooted public key registered at the RKP factory.
-- The DICE chain should have RKP VM markers that help identify RKP VM as required
- by the [remote provisioning HAL][rkp-hal-markers].
+- The DICE chain must have a UDS-rooted public key registered at the RKP
+ factory.
+- The DICE chain must use [RKP VM markers][rkpvm-marker] to help identify the
+ RKP VM as required by the [remote provisioning HAL][rkp-hal].
+
+### RKP VM marker
+
+To support VM remote attestation, vendors must include an RKP VM marker in their
+DICE certificates. This marker should be present from the early boot stage
+within the TEE and continue through to the last DICE certificate before
+[pvmfw][pvmfw] takes over.
+
+![RKP VM DICE chain][rkpvm-dice-chain]
+
+Pvmfw will add an RKP VM marker when it's launching an RKP VM. The __continuous
+presence__ of this marker throughout the chain allows the RKP server to clearly
+identify legitimate RKP VM DICE chains.
+
+This mechanism also serves as a security measure. If an attacker tries to launch
+a malicious guest OS or payload, their DICE chain will be rejected by the RKP
+server because it will lack the RKP VM marker that pvmfw would have added in a
+genuine RKP VM boot process.
+
+[pvmfw]: ../guest/pvmfw/README.md
+[rkpvm-dice-chain]: img/rkpvm-dice-chain.png
+
+## To Disable It
The feature is enabled by default. To disable it, you have two options:
@@ -133,4 +157,5 @@
If you don't set any of these variables, VM remote attestation will be enabled
by default.
-[rkp-hal-markers]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md#hal
+[rkpvm-marker]: https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#configuration-descriptor
+[rkp-hal]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md#hal
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index 7352a2c..8186e9d 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -654,7 +654,7 @@
if requested {
let status = Command::new("/system/bin/kexec_load").status()?;
if !status.success() {
- return Err(anyhow!("Failed to load crashkernel: {:?}", status));
+ return Err(anyhow!("Failed to load crashkernel: {status}"));
}
info!("ramdump is loaded: debuggable={debuggable}, ramdump={ramdump}");
}
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index cb21ccf..de1b081 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -78,7 +78,8 @@
private static final String TAG = "VirtualMachineConfig";
private static String[] EMPTY_STRING_ARRAY = {};
- private static final String U_BOOT_PREBUILT_PATH = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_ARM = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_X86 = "/apex/com.android.virt/etc/u-boot.rom";
// These define the schema of the config file persisted on disk.
// Please bump up the version number when adding a new key.
@@ -668,7 +669,11 @@
.orElse(null);
if (config.kernel == null && config.bootloader == null) {
- config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH), MODE_READ_ONLY);
+ if (Arrays.stream(Build.SUPPORTED_ABIS).anyMatch("x86_64"::equals)) {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_X86), MODE_READ_ONLY);
+ } else {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_ARM), MODE_READ_ONLY);
+ }
}
config.params =
diff --git a/libs/libinherited_fd/Android.bp b/libs/libinherited_fd/Android.bp
deleted file mode 100644
index 28ec2e5..0000000
--- a/libs/libinherited_fd/Android.bp
+++ /dev/null
@@ -1,44 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
- name: "libinherited_fd.defaults",
- crate_name: "inherited_fd",
- srcs: ["src/lib.rs"],
- edition: "2021",
- rustlibs: [
- "libnix",
- "libonce_cell",
- "libthiserror",
- ],
-}
-
-rust_library {
- name: "libinherited_fd",
- defaults: ["libinherited_fd.defaults"],
- apex_available: [
- "com.android.compos",
- "com.android.virt",
- ],
-}
-
-rust_test {
- name: "libinherited_fd.test",
- defaults: ["libinherited_fd.defaults"],
- rustlibs: [
- "libanyhow",
- "libtempfile",
- ],
- host_supported: true,
- test_suites: ["general-tests"],
- test_options: {
- unit_test: true,
- },
- // this is to run each test function in a separate process.
- // note that they still run in parallel.
- flags: [
- "-C panic=abort",
- "-Z panic_abort_tests",
- ],
-}
diff --git a/libs/libinherited_fd/src/lib.rs b/libs/libinherited_fd/src/lib.rs
deleted file mode 100644
index f5e2d6b..0000000
--- a/libs/libinherited_fd/src/lib.rs
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2024, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Library for safely obtaining `OwnedFd` for inherited file descriptors.
-
-use nix::fcntl::{fcntl, FdFlag, F_SETFD};
-use nix::libc;
-use std::collections::HashMap;
-use std::fs::canonicalize;
-use std::fs::read_dir;
-use std::os::fd::FromRawFd;
-use std::os::fd::OwnedFd;
-use std::os::fd::RawFd;
-use std::sync::Mutex;
-use std::sync::OnceLock;
-use thiserror::Error;
-
-/// Errors that can occur while taking an ownership of `RawFd`
-#[derive(Debug, PartialEq, Error)]
-pub enum Error {
- /// init_once() not called
- #[error("init_once() not called")]
- NotInitialized,
-
- /// Ownership already taken
- #[error("Ownership of FD {0} is already taken")]
- OwnershipTaken(RawFd),
-
- /// Not an inherited file descriptor
- #[error("FD {0} is either invalid file descriptor or not an inherited one")]
- FileDescriptorNotInherited(RawFd),
-
- /// Failed to set CLOEXEC
- #[error("Failed to set CLOEXEC on FD {0}")]
- FailCloseOnExec(RawFd),
-}
-
-static INHERITED_FDS: OnceLock<Mutex<HashMap<RawFd, Option<OwnedFd>>>> = OnceLock::new();
-
-/// Take ownership of all open file descriptors in this process, which later can be obtained by
-/// calling `take_fd_ownership`.
-///
-/// # Safety
-/// This function has to be called very early in the program before the ownership of any file
-/// descriptors (except stdin/out/err) is taken.
-pub unsafe fn init_once() -> Result<(), std::io::Error> {
- let mut fds = HashMap::new();
-
- let fd_path = canonicalize("/proc/self/fd")?;
-
- for entry in read_dir(&fd_path)? {
- let entry = entry?;
-
- // Files in /prod/self/fd are guaranteed to be numbers. So parsing is always successful.
- let file_name = entry.file_name();
- let raw_fd = file_name.to_str().unwrap().parse::<RawFd>().unwrap();
-
- // We don't take ownership of the stdio FDs as the Rust runtime owns them.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- continue;
- }
-
- // Exceptional case: /proc/self/fd/* may be a dir fd created by read_dir just above. Since
- // the file descriptor is owned by read_dir (and thus closed by it), we shouldn't take
- // ownership to it.
- if entry.path().read_link()? == fd_path {
- continue;
- }
-
- // SAFETY: /proc/self/fd/* are file descriptors that are open. If `init_once()` was called
- // at the very beginning of the program execution (as requested by the safety requirement
- // of this function), this is the first time to claim the ownership of these file
- // descriptors.
- let owned_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
- fds.insert(raw_fd, Some(owned_fd));
- }
-
- INHERITED_FDS
- .set(Mutex::new(fds))
- .or(Err(std::io::Error::other("Inherited fds were already initialized")))
-}
-
-/// Take the ownership of the given `RawFd` and returns `OwnedFd` for it. The returned FD is set
-/// CLOEXEC. `Error` is returned when the ownership was already taken (by a prior call to this
-/// function with the same `RawFd`) or `RawFd` is not an inherited file descriptor.
-pub fn take_fd_ownership(raw_fd: RawFd) -> Result<OwnedFd, Error> {
- let mut fds = INHERITED_FDS.get().ok_or(Error::NotInitialized)?.lock().unwrap();
-
- if let Some(value) = fds.get_mut(&raw_fd) {
- if let Some(owned_fd) = value.take() {
- fcntl(raw_fd, F_SETFD(FdFlag::FD_CLOEXEC)).or(Err(Error::FailCloseOnExec(raw_fd)))?;
- Ok(owned_fd)
- } else {
- Err(Error::OwnershipTaken(raw_fd))
- }
- } else {
- Err(Error::FileDescriptorNotInherited(raw_fd))
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use anyhow::Result;
- use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
- use nix::unistd::close;
- use std::os::fd::{AsRawFd, IntoRawFd};
- use tempfile::tempfile;
-
- struct Fixture {
- fds: Vec<RawFd>,
- }
-
- impl Fixture {
- fn setup(num_fds: usize) -> Result<Self> {
- let mut fds = Vec::new();
- for _ in 0..num_fds {
- fds.push(tempfile()?.into_raw_fd());
- }
- Ok(Fixture { fds })
- }
-
- fn open_new_file(&mut self) -> Result<RawFd> {
- let raw_fd = tempfile()?.into_raw_fd();
- self.fds.push(raw_fd);
- Ok(raw_fd)
- }
- }
-
- impl Drop for Fixture {
- fn drop(&mut self) {
- self.fds.iter().for_each(|fd| {
- let _ = close(*fd);
- });
- }
- }
-
- fn is_fd_opened(raw_fd: RawFd) -> bool {
- fcntl(raw_fd, F_GETFD).is_ok()
- }
-
- #[test]
- fn happy_case() -> Result<()> {
- let fixture = Fixture::setup(2)?;
- let f0 = fixture.fds[0];
- let f1 = fixture.fds[1];
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- let f0_owned = take_fd_ownership(f0)?;
- let f1_owned = take_fd_ownership(f1)?;
- assert_eq!(f0, f0_owned.as_raw_fd());
- assert_eq!(f1, f1_owned.as_raw_fd());
-
- drop(f0_owned);
- drop(f1_owned);
- assert!(!is_fd_opened(f0));
- assert!(!is_fd_opened(f1));
- Ok(())
- }
-
- #[test]
- fn access_non_inherited_fd() -> Result<()> {
- let mut fixture = Fixture::setup(2)?;
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- let f = fixture.open_new_file()?;
- assert_eq!(Some(Error::FileDescriptorNotInherited(f)), take_fd_ownership(f).err());
- Ok(())
- }
-
- #[test]
- fn call_init_once_multiple_times() -> Result<()> {
- let _ = Fixture::setup(2)?;
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- // SAFETY: for testing
- let res = unsafe { init_once() };
- assert!(res.is_err());
- Ok(())
- }
-
- #[test]
- fn access_without_init_once() -> Result<()> {
- let fixture = Fixture::setup(2)?;
-
- let f = fixture.fds[0];
- assert_eq!(Some(Error::NotInitialized), take_fd_ownership(f).err());
- Ok(())
- }
-
- #[test]
- fn double_ownership() -> Result<()> {
- let fixture = Fixture::setup(2)?;
- let f = fixture.fds[0];
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- let f_owned = take_fd_ownership(f)?;
- let f_double_owned = take_fd_ownership(f);
- assert_eq!(Some(Error::OwnershipTaken(f)), f_double_owned.err());
-
- // just to highlight that f_owned is kept alive when the second call to take_fd_ownership
- // is made.
- drop(f_owned);
- Ok(())
- }
-
- #[test]
- fn take_drop_retake() -> Result<()> {
- let fixture = Fixture::setup(2)?;
- let f = fixture.fds[0];
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- let f_owned = take_fd_ownership(f)?;
- drop(f_owned);
-
- let f_double_owned = take_fd_ownership(f);
- assert_eq!(Some(Error::OwnershipTaken(f)), f_double_owned.err());
- Ok(())
- }
-
- #[test]
- fn cloexec() -> Result<()> {
- let fixture = Fixture::setup(2)?;
- let f = fixture.fds[0];
-
- // SAFETY: assume files opened by Fixture are inherited ones
- unsafe {
- init_once()?;
- }
-
- // Intentionally cleaar cloexec to see if it is set by take_fd_ownership
- fcntl(f, F_SETFD(FdFlag::empty()))?;
-
- let f_owned = take_fd_ownership(f)?;
- let flags = fcntl(f_owned.as_raw_fd(), F_GETFD)?;
- assert_eq!(flags, FdFlag::FD_CLOEXEC.bits());
- Ok(())
- }
-}
diff --git a/libs/libvmclient/Android.bp b/libs/libvmclient/Android.bp
index 5bd59da..d318d0e 100644
--- a/libs/libvmclient/Android.bp
+++ b/libs/libvmclient/Android.bp
@@ -23,6 +23,7 @@
"com.android.compos",
"com.android.microfuchsia",
"com.android.virt",
+ "//apex_available:platform",
],
}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index bc9d683..ce7d5a5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -55,6 +55,7 @@
time::Duration,
};
+const EARLY_VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/early_virtmgr";
const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
const VIRTMGR_THREADS: usize = 2;
@@ -122,10 +123,20 @@
/// Spawns a new instance of virtmgr, a child process that will host
/// the VirtualizationService AIDL service.
pub fn new() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(VIRTMGR_PATH)
+ }
+
+ /// Spawns a new instance of early_virtmgr, a child process that will host
+ /// the VirtualizationService AIDL service for early VMs.
+ pub fn new_early() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(EARLY_VIRTMGR_PATH)
+ }
+
+ fn new_with_path(virtmgr_path: &str) -> Result<VirtualizationService, io::Error> {
let (wait_fd, ready_fd) = posix_pipe()?;
let (client_fd, server_fd) = posix_socketpair()?;
- let mut command = Command::new(VIRTMGR_PATH);
+ let mut command = Command::new(virtmgr_path);
// Can't use BorrowedFd as it doesn't implement Display
command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
index 3814cdd..8604553 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
@@ -78,9 +78,9 @@
/** This class provides utilities to interact with the hyp tracing subsystem */
public final class KvmHypTracer {
- private static final String HYP_TRACING_ROOT = "/sys/kernel/tracing/hyp/";
private static final int DEFAULT_BUF_SIZE_KB = 4 * 1024;
+ private final String mHypTracingRoot;
private final CommandRunner mRunner;
private final ITestDevice mDevice;
private final int mNrCpus;
@@ -88,17 +88,41 @@
private final ArrayList<File> mTraces;
- private void setNode(String node, int val) throws Exception {
- mRunner.run("echo " + val + " > " + HYP_TRACING_ROOT + node);
+ private static String getHypTracingRoot(ITestDevice device) throws Exception {
+ String legacy = "/sys/kernel/tracing/hyp/";
+ String path = "/sys/kernel/tracing/hypervisor/";
+
+ if (device.doesFileExist(path)) {
+ return path;
+ }
+
+ if (device.doesFileExist(legacy)) {
+ return legacy;
+ }
+
+ throw new Exception("Hypervisor tracing not found");
}
- private static String eventDir(String event) {
- return "events/hyp/" + event + "/";
+ private static String getHypEventsDir(String root) {
+ if (root.endsWith("/hypervisor/"))
+ return "events/hypervisor/";
+
+ return "events/hyp/";
}
public static boolean isSupported(ITestDevice device, String[] events) throws Exception {
- for (String event : events) {
- if (!device.doesFileExist(HYP_TRACING_ROOT + eventDir(event) + "/enable")) return false;
+ String dir;
+
+ try {
+ dir = getHypTracingRoot(device);
+ dir += getHypEventsDir(dir);
+ } catch (Exception e) {
+ return false;
+ }
+
+ for (String event: events) {
+ if (!device.doesFileExist(dir + event + "/enable"))
+ return false;
}
return true;
}
@@ -108,6 +132,7 @@
.that(isSupported(device, events))
.isTrue();
+ mHypTracingRoot = getHypTracingRoot(device);
mDevice = device;
mRunner = new CommandRunner(mDevice);
mTraces = new ArrayList<File>();
@@ -115,17 +140,25 @@
mHypEvents = events;
}
+ private void setNode(String node, int val) throws Exception {
+ mRunner.run("echo " + val + " > " + mHypTracingRoot + node);
+ }
+
public String run(String payload_cmd) throws Exception {
mTraces.clear();
setNode("tracing_on", 0);
- mRunner.run("echo 0 | tee " + HYP_TRACING_ROOT + "events/*/*/enable");
+ mRunner.run("echo 0 | tee " + mHypTracingRoot + "events/*/*/enable");
setNode("buffer_size_kb", DEFAULT_BUF_SIZE_KB);
- for (String event : mHypEvents) setNode(eventDir(event) + "/enable", 1);
+
+ for (String event: mHypEvents) {
+ setNode(getHypEventsDir(mHypTracingRoot) + event + "/enable", 1);
+ }
+
setNode("trace", 0);
/* Cat each per-cpu trace_pipe in its own tmp file in the background */
- String cmd = "cd " + HYP_TRACING_ROOT + ";";
+ String cmd = "cd " + mHypTracingRoot + ";";
String trace_pipes[] = new String[mNrCpus];
for (int i = 0; i < mNrCpus; i++) {
trace_pipes[i] = mRunner.run("mktemp -t trace_pipe.cpu" + i + ".XXXXXXXXXX");