Merge "Default to arch-specific u-boot implementation for bootloader." into main
diff --git a/android/virtmgr/Android.bp b/android/virtmgr/Android.bp
index f0b6881..0148ff6 100644
--- a/android/virtmgr/Android.bp
+++ b/android/virtmgr/Android.bp
@@ -70,6 +70,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..0f41932 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -1045,8 +1045,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,
         ));
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/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/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");