Support amending vendor hashtree digest when virtmgr constructs DTBO based on host DT

Bug: 325555638
Test: atest MicrodroidTests#configuringVendorDiskImageRequiresCustomPermission
Test: atest MicrodroidTests#bootsWithVendorPartition
Test: atest MicrodroidTests#bootsWithCustomVendorPartitionForNonPvm
Test: atest MicrodroidTests#bootFailsWithCustomVendorPartitionForPvm
Test: atest MicrodroidTests#creationFailsWithUnsignedVendorPartition
Test: atest virtualizationmanager_device_test

Change-Id: I3a008c4bbb34d7673d843c18c0b479212b925065
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 88700ec..c32960b 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -87,6 +87,7 @@
 use std::os::unix::raw::pid_t;
 use std::path::{Path, PathBuf};
 use std::sync::{Arc, Mutex, Weak};
+use vbmeta::VbMetaImage;
 use vmconfig::VmConfig;
 use vsock::VsockStream;
 use zip::ZipArchive;
@@ -381,6 +382,23 @@
             None
         };
 
+        let vendor_hashtree_digest = extract_vendor_hashtree_digest(config)
+            .context("Failed to extract vendor hashtree digest")
+            .or_service_specific_exception(-1)?;
+
+        let trusted_props = if let Some(ref vendor_hashtree_digest) = vendor_hashtree_digest {
+            info!(
+                "Passing vendor hashtree digest to pvmfw. This will be rejected if it doesn't \
+                match the trusted digest in the pvmfw config, causing the VM to fail to start."
+            );
+            vec![(
+                cstr!("vendor_hashtree_descriptor_root_digest"),
+                vendor_hashtree_digest.as_slice(),
+            )]
+        } else {
+            vec![]
+        };
+
         let untrusted_props = if cfg!(llpvm_changes) {
             // TODO(b/291213394): Replace this with a per-VM instance Id.
             let instance_id = b"sixtyfourbyteslonghardcoded_indeed_sixtyfourbyteslonghardcoded_h";
@@ -389,17 +407,23 @@
             vec![]
         };
 
-        let device_tree_overlay = if host_ref_dt.is_some() || !untrusted_props.is_empty() {
-            let dt_output = temporary_directory.join(VM_DT_OVERLAY_PATH);
-            let mut data = [0_u8; VM_DT_OVERLAY_MAX_SIZE];
-            let fdt = create_device_tree_overlay(&mut data, host_ref_dt, &untrusted_props)
+        let device_tree_overlay =
+            if host_ref_dt.is_some() || !untrusted_props.is_empty() || !trusted_props.is_empty() {
+                let dt_output = temporary_directory.join(VM_DT_OVERLAY_PATH);
+                let mut data = [0_u8; VM_DT_OVERLAY_MAX_SIZE];
+                let fdt = create_device_tree_overlay(
+                    &mut data,
+                    host_ref_dt,
+                    &untrusted_props,
+                    &trusted_props,
+                )
                 .map_err(|e| anyhow!("Failed to create DT overlay, {e:?}"))
                 .or_service_specific_exception(-1)?;
-            fs::write(&dt_output, fdt.as_slice()).or_service_specific_exception(-1)?;
-            Some(File::open(dt_output).or_service_specific_exception(-1)?)
-        } else {
-            None
-        };
+                fs::write(&dt_output, fdt.as_slice()).or_service_specific_exception(-1)?;
+                Some(File::open(dt_output).or_service_specific_exception(-1)?)
+            } else {
+                None
+            };
 
         let debug_level = match config {
             VirtualMachineConfig::AppConfig(config) => config.debugLevel,
@@ -601,6 +625,32 @@
     }
 }
 
+fn extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>> {
+    let VirtualMachineConfig::AppConfig(config) = config else {
+        return Ok(None);
+    };
+    let Some(custom_config) = &config.customConfig else {
+        return Ok(None);
+    };
+    let Some(file) = custom_config.vendorImage.as_ref() else {
+        return Ok(None);
+    };
+
+    let file = clone_file(file)?;
+    let size =
+        file.metadata().context("Failed to get metadata from microdroid vendor image")?.len();
+    let vbmeta = VbMetaImage::verify_reader_region(&file, 0, size)
+        .context("Failed to get vbmeta from microdroid-vendor.img")?;
+
+    for descriptor in vbmeta.descriptors()?.iter() {
+        if let vbmeta::Descriptor::Hashtree(_) = descriptor {
+            let root_digest = hex::encode(descriptor.to_hashtree()?.root_digest());
+            return Ok(Some(root_digest.as_bytes().to_vec()));
+        }
+    }
+    Err(anyhow!("No hashtree digest is extracted from microdroid vendor image"))
+}
+
 fn write_zero_filler(zero_filler_path: &Path) -> Result<()> {
     let file = OpenOptions::new()
         .create_new(true)