Merge "[rkp] Return error code when requestAttestation fails" into main
diff --git a/microdroid/init.rc b/microdroid/init.rc
index f5f3f15..4cc0475 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -30,6 +30,11 @@
     # We don't directly exec the binary to specify stdio_to_kmsg.
     exec_start init_debug_policy
 
+    # Wait for ueventd to have finished cold boot.
+    # This is needed by prng-seeder (at least).
+    # (In Android this happens inside apexd-bootstrap.)
+    wait_for_prop ro.cold_boot_done true
+
 on init
     mkdir /mnt/apk 0755 root root
     mkdir /mnt/extra-apk 0755 root root
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 7ba54f8..1b41e58 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -34,7 +34,7 @@
 
 use crate::dice::dice_derivation;
 use crate::dice_driver::DiceDriver;
-use crate::instance::{ApexData, InstanceDisk, MicrodroidData};
+use crate::instance::{InstanceDisk, MicrodroidData};
 use crate::verify::verify_payload;
 use crate::vm_payload_service::register_vm_payload_service;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
@@ -42,10 +42,10 @@
 use keystore2_crypto::ZVec;
 use libc::VMADDR_CID_HOST;
 use log::{error, info};
-use microdroid_metadata::{write_metadata, PayloadMetadata};
+use microdroid_metadata::PayloadMetadata;
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use nix::sys::signal::Signal;
-use payload::{load_metadata, to_metadata};
+use payload::load_metadata;
 use rpcbinder::RpcSession;
 use rustutils::sockets::android_get_control_socket;
 use rustutils::system_properties;
@@ -143,15 +143,6 @@
     Ok(())
 }
 
-fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
-    // The host is running a VirtualMachineService for this VM on a port equal
-    // to the CID of this VM.
-    let port = vsock::get_local_cid().context("Could not determine local CID")?;
-    RpcSession::new()
-        .setup_vsock_client(VMADDR_CID_HOST, port)
-        .context("Could not connect to IVirtualMachineService")
-}
-
 fn main() -> Result<()> {
     // If debuggable, print full backtrace to console log with stdio_to_kmsg
     if is_debuggable()? {
@@ -174,25 +165,6 @@
     })
 }
 
-/// Prepares a socket file descriptor for the vm payload service.
-///
-/// # Safety
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
-    let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
-
-    // Creating OwnedFd for stdio FDs is not safe.
-    if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
-        bail!("File descriptor {raw_fd} is standard I/O descriptor");
-    }
-    // SAFETY: Initializing OwnedFd for a RawFd created by the init.
-    // We checked that the integer value corresponds to a valid FD and that the caller
-    // ensures that this is the only place to claim its ownership.
-    Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
 fn try_main() -> Result<()> {
     android_logger::init_once(
         android_logger::Config::default()
@@ -245,71 +217,6 @@
     }
 }
 
-fn post_payload_work() -> Result<()> {
-    // Sync the encrypted storage filesystem (flushes the filesystem caches).
-    if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
-        let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
-
-        // SAFETY: `mountpoint` is a valid C string. `syncfs` and `close` are safe for any parameter
-        // values.
-        let ret = unsafe {
-            let dirfd = libc::open(
-                mountpoint.as_ptr(),
-                libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
-            );
-            ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
-            let ret = libc::syncfs(dirfd);
-            libc::close(dirfd);
-            ret
-        };
-        if ret != 0 {
-            error!("failed to sync encrypted storage.");
-            return Err(anyhow!(std::io::Error::last_os_error()));
-        }
-    }
-    Ok(())
-}
-
-fn is_strict_boot() -> bool {
-    Path::new(AVF_STRICT_BOOT).exists()
-}
-
-fn is_new_instance() -> bool {
-    Path::new(AVF_NEW_INSTANCE).exists()
-}
-
-fn is_verified_boot() -> bool {
-    !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
-}
-
-fn is_debuggable() -> Result<bool> {
-    Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
-}
-
-fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
-    match config.export_tombstones {
-        Some(b) => b,
-        None => is_debuggable().unwrap_or(false),
-    }
-}
-
-/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
-fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
-    let mut file = match File::open(path) {
-        Ok(dp) => dp,
-        Err(e) => {
-            info!(
-                "Assumes that debug policy is disabled because failed to read debug policy ({e:?})"
-            );
-            return Ok(Some(false));
-        }
-    };
-    let mut log: [u8; 4] = Default::default();
-    file.read_exact(&mut log).context("Malformed data in {path}")?;
-    // DT spec uses big endian although Android is always little endian.
-    Ok(Some(u32::from_be_bytes(log) == 1))
-}
-
 fn try_run_payload(
     service: &Strong<dyn IVirtualMachineService>,
     vm_payload_service_fd: OwnedFd,
@@ -377,6 +284,13 @@
     let dice_artifacts = dice_derivation(dice, &verified_data, &payload_metadata)?;
     let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
 
+    if cfg!(dice_changes) {
+        // Now that the DICE derivation is done, it's ok to allow payload code to run.
+
+        // Start apexd to activate APEXes. This may allow code within them to run.
+        system_properties::write("ctl.start", "apexd-vm")?;
+    }
+
     // Run encryptedstore binary to prepare the storage
     let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
         info!("Preparing encryptedstore ...");
@@ -419,10 +333,12 @@
     );
     mount_extra_apks(&config, &mut zipfuse)?;
 
-    // Wait until apex config is done. (e.g. linker configuration for apexes)
-    wait_for_apex_config_done()?;
-
-    setup_config_sysprops(&config)?;
+    register_vm_payload_service(
+        allow_restricted_apis,
+        service.clone(),
+        vm_secret,
+        vm_payload_service_fd,
+    )?;
 
     // Set export_tombstones if enabled
     if should_export_tombstones(&config) {
@@ -431,16 +347,20 @@
             .context("set microdroid_manager.export_tombstones.enabled")?;
     }
 
+    // Wait until apex config is done. (e.g. linker configuration for apexes)
+    wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")?;
+
+    // Trigger init post-fs-data. This will start authfs if we wask it to.
+    if config.enable_authfs {
+        system_properties::write("microdroid_manager.authfs.enabled", "1")
+            .context("failed to write microdroid_manager.authfs.enabled")?;
+    }
+    system_properties::write("microdroid_manager.config_done", "1")
+        .context("failed to write microdroid_manager.config_done")?;
+
     // Wait until zipfuse has mounted the APKs so we can access the payload
     zipfuse.wait_until_done()?;
 
-    register_vm_payload_service(
-        allow_restricted_apis,
-        service.clone(),
-        vm_secret,
-        vm_payload_service_fd,
-    )?;
-
     // Wait for encryptedstore to finish mounting the storage (if enabled) before setting
     // microdroid_manager.init_done. Reason is init stops uneventd after that.
     // Encryptedstore, however requires ueventd
@@ -449,7 +369,10 @@
         ensure!(exitcode.success(), "Unable to prepare encrypted storage. Exitcode={}", exitcode);
     }
 
+    // Wait for init to have finished booting.
     wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
+
+    // And then tell it we're done so unnecessary services can be shut down.
     system_properties::write("microdroid_manager.init_done", "1")
         .context("set microdroid_manager.init_done")?;
 
@@ -457,6 +380,120 @@
     exec_task(task, service).context("Failed to run payload")
 }
 
+fn post_payload_work() -> Result<()> {
+    // Sync the encrypted storage filesystem (flushes the filesystem caches).
+    if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
+        let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
+
+        // SAFETY: `mountpoint` is a valid C string. `syncfs` and `close` are safe for any parameter
+        // values.
+        let ret = unsafe {
+            let dirfd = libc::open(
+                mountpoint.as_ptr(),
+                libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
+            );
+            ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
+            let ret = libc::syncfs(dirfd);
+            libc::close(dirfd);
+            ret
+        };
+        if ret != 0 {
+            error!("failed to sync encrypted storage.");
+            return Err(anyhow!(std::io::Error::last_os_error()));
+        }
+    }
+    Ok(())
+}
+
+fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
+    // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
+    for i in 0..config.extra_apks.len() {
+        let mount_dir = format!("/mnt/extra-apk/{i}");
+        create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
+
+        let mount_for_exec =
+            if cfg!(multi_tenant) { MountForExec::Allowed } else { MountForExec::Disallowed };
+        // These run asynchronously in parallel - we wait later for them to complete.
+        zipfuse.mount(
+            mount_for_exec,
+            "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
+            Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
+            Path::new(&mount_dir),
+            format!("microdroid_manager.extra_apk.mounted.{i}"),
+        )?;
+    }
+
+    Ok(())
+}
+
+fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
+    // The host is running a VirtualMachineService for this VM on a port equal
+    // to the CID of this VM.
+    let port = vsock::get_local_cid().context("Could not determine local CID")?;
+    RpcSession::new()
+        .setup_vsock_client(VMADDR_CID_HOST, port)
+        .context("Could not connect to IVirtualMachineService")
+}
+
+/// Prepares a socket file descriptor for the vm payload service.
+///
+/// # Safety
+///
+/// The caller must ensure that this function is the only place that claims ownership
+/// of the file descriptor and it is called only once.
+unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
+    let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
+
+    // Creating OwnedFd for stdio FDs is not safe.
+    if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
+        bail!("File descriptor {raw_fd} is standard I/O descriptor");
+    }
+    // SAFETY: Initializing OwnedFd for a RawFd created by the init.
+    // We checked that the integer value corresponds to a valid FD and that the caller
+    // ensures that this is the only place to claim its ownership.
+    Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
+}
+
+fn is_strict_boot() -> bool {
+    Path::new(AVF_STRICT_BOOT).exists()
+}
+
+fn is_new_instance() -> bool {
+    Path::new(AVF_NEW_INSTANCE).exists()
+}
+
+fn is_verified_boot() -> bool {
+    !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
+}
+
+fn is_debuggable() -> Result<bool> {
+    Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
+}
+
+fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
+    match config.export_tombstones {
+        Some(b) => b,
+        None => is_debuggable().unwrap_or(false),
+    }
+}
+
+/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
+fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
+    let mut file = match File::open(path) {
+        Ok(dp) => dp,
+        Err(e) => {
+            info!(
+                "Assumes that debug policy is disabled because failed to read debug policy ({e:?})"
+            );
+            return Ok(Some(false));
+        }
+    };
+    let mut log: [u8; 4] = Default::default();
+    file.read_exact(&mut log).context("Malformed data in {path}")?;
+    // DT spec uses big endian although Android is always little endian.
+    Ok(Some(u32::from_be_bytes(log) == 1))
+}
+
 enum MountForExec {
     Allowed,
     Disallowed,
@@ -504,65 +541,6 @@
     }
 }
 
-fn write_apex_payload_data(
-    saved_data: Option<&MicrodroidData>,
-    apex_data_from_payload: &[ApexData],
-) -> Result<()> {
-    if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
-        // We don't support APEX updates. (assuming that update will change root digest)
-        ensure!(
-            saved_apex_data == apex_data_from_payload,
-            MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
-        );
-        let apex_metadata = to_metadata(apex_data_from_payload);
-        // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
-        // metadata instead of the default one (/dev/block/by-name/payload-metadata)
-        OpenOptions::new()
-            .create_new(true)
-            .write(true)
-            .open("/apex/vm-payload-metadata")
-            .context("Failed to open /apex/vm-payload-metadata")
-            .and_then(|f| write_metadata(&apex_metadata, f))?;
-    }
-    Ok(())
-}
-
-fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
-    // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
-    for i in 0..config.extra_apks.len() {
-        let mount_dir = format!("/mnt/extra-apk/{i}");
-        create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
-
-        let mount_for_exec =
-            if cfg!(multi_tenant) { MountForExec::Allowed } else { MountForExec::Disallowed };
-        // These run asynchronously in parallel - we wait later for them to complete.
-        zipfuse.mount(
-            mount_for_exec,
-            "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
-            Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
-            Path::new(&mount_dir),
-            format!("microdroid_manager.extra_apk.mounted.{i}"),
-        )?;
-    }
-
-    Ok(())
-}
-
-fn setup_config_sysprops(config: &VmPayloadConfig) -> Result<()> {
-    if config.enable_authfs {
-        system_properties::write("microdroid_manager.authfs.enabled", "1")
-            .context("failed to write microdroid_manager.authfs.enabled")?;
-    }
-    system_properties::write("microdroid_manager.config_done", "1")
-        .context("failed to write microdroid_manager.config_done")?;
-    Ok(())
-}
-
-// Waits until linker config is generated
-fn wait_for_apex_config_done() -> Result<()> {
-    wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")
-}
-
 fn wait_for_property_true(property_name: &str) -> Result<()> {
     let mut prop = PropertyWatcher::new(property_name)?;
     loop {
diff --git a/microdroid_manager/src/verify.rs b/microdroid_manager/src/verify.rs
index 22f3414..e63530b 100644
--- a/microdroid_manager/src/verify.rs
+++ b/microdroid_manager/src/verify.rs
@@ -12,18 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::instance::{ApkData, MicrodroidData, RootHash};
-use crate::payload::get_apex_data_from_payload;
-use crate::{is_strict_boot, is_verified_boot, write_apex_payload_data, MicrodroidError};
+use crate::instance::{ApexData, ApkData, MicrodroidData, RootHash};
+use crate::payload::{get_apex_data_from_payload, to_metadata};
+use crate::{is_strict_boot, is_verified_boot, MicrodroidError};
 use anyhow::{anyhow, ensure, Context, Result};
 use apkmanifest::get_manifest_info;
 use apkverify::{get_public_key_der, verify, V4Signature};
 use glob::glob;
 use itertools::sorted;
 use log::{info, warn};
-use microdroid_metadata::Metadata;
+use microdroid_metadata::{write_metadata, Metadata};
 use rand::Fill;
 use rustutils::system_properties;
+use std::fs::OpenOptions;
 use std::path::Path;
 use std::process::{Child, Command};
 use std::str;
@@ -134,8 +135,10 @@
         write_apex_payload_data(saved_data, &apex_data_from_payload)?;
     }
 
-    // Start apexd to activate APEXes
-    system_properties::write("ctl.start", "apexd-vm")?;
+    if cfg!(not(dice_changes)) {
+        // Start apexd to activate APEXes
+        system_properties::write("ctl.start", "apexd-vm")?;
+    }
 
     // TODO(inseob): add timeout
     apkdmverity_child.wait()?;
@@ -207,6 +210,29 @@
     })
 }
 
+fn write_apex_payload_data(
+    saved_data: Option<&MicrodroidData>,
+    apex_data_from_payload: &[ApexData],
+) -> Result<()> {
+    if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
+        // We don't support APEX updates. (assuming that update will change root digest)
+        ensure!(
+            saved_apex_data == apex_data_from_payload,
+            MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
+        );
+        let apex_metadata = to_metadata(apex_data_from_payload);
+        // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
+        // metadata instead of the default one (/dev/block/by-name/payload-metadata)
+        OpenOptions::new()
+            .create_new(true)
+            .write(true)
+            .open("/apex/vm-payload-metadata")
+            .context("Failed to open /apex/vm-payload-metadata")
+            .and_then(|f| write_metadata(&apex_metadata, f))?;
+    }
+    Ok(())
+}
+
 fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
     Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
 }
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index a2816c4..7eae09f 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -28,32 +28,18 @@
 use core::mem;
 use libfdt::{Fdt, FdtError, FdtNode};
 
-// TODO(b/308694211): Move this to the vmbase
-macro_rules! const_cstr {
-    ($str:literal) => {{
-        #[allow(unused_unsafe)] // In case the macro is used within an unsafe block.
-        // SAFETY: Trailing null is guaranteed by concat!()
-        unsafe {
-            CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes())
-        }
-    }};
-}
-
 // TODO(b/308694211): Use cstr! from vmbase instead.
 macro_rules! cstr {
     ($str:literal) => {{
-        CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
+        const S: &str = concat!($str, "\0");
+        const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
+            Ok(v) => v,
+            Err(_) => panic!("string contains interior NUL"),
+        };
+        C
     }};
 }
 
-const FILTERED_VM_DTBO_PROP: [&CStr; 3] = [
-    const_cstr!("android,pvmfw,phy-reg"),
-    const_cstr!("android,pvmfw,phy-iommu"),
-    const_cstr!("android,pvmfw,phy-sid"),
-];
-
-const REG_PROP_NAME: &CStr = const_cstr!("reg");
-const INTERRUPTS_PROP_NAME: &CStr = const_cstr!("interrupts");
 // TODO(b/277993056): Keep constants derived from platform.dts in one place.
 const CELLS_PER_INTERRUPT: usize = 3; // from /intc node in platform.dts
 
@@ -102,10 +88,6 @@
 pub struct VmDtbo(Fdt);
 
 impl VmDtbo {
-    const OVERLAY_NODE_NAME: &CStr = const_cstr!("__overlay__");
-    const TARGET_PATH_PROP: &CStr = const_cstr!("target-path");
-    const SYMBOLS_NODE_PATH: &CStr = const_cstr!("/__symbols__");
-
     /// Wraps a mutable slice containing a VM DTBO.
     ///
     /// Fails if the VM DTBO does not pass validation.
@@ -150,7 +132,7 @@
 
         let fragment_node = node.supernode_at_depth(1)?;
         let target_path = fragment_node
-            .getprop_str(Self::TARGET_PATH_PROP)?
+            .getprop_str(cstr!("target-path"))?
             .ok_or(DeviceAssignmentError::InvalidDtbo)?;
         if target_path != cstr!("/") {
             return Err(DeviceAssignmentError::UnsupportedOverlayTarget);
@@ -161,7 +143,7 @@
             .filter(|&component| !component.is_empty())
             .skip(1);
         let overlay_node_name = components.next();
-        if overlay_node_name != Some(Self::OVERLAY_NODE_NAME.to_bytes()) {
+        if overlay_node_name != Some(b"__overlay__") {
             return Err(DeviceAssignmentError::InvalidDtbo);
         }
         let mut overlaid_path = Vec::with_capacity(dtbo_node_path_bytes.len());
@@ -206,7 +188,7 @@
         // Validation: Validate if interrupts cell numbers are multiple of #interrupt-cells.
         // We can't know how many interrupts would exist.
         let interrupts_cells = node
-            .getprop_cells(INTERRUPTS_PROP_NAME)?
+            .getprop_cells(cstr!("interrupts"))?
             .ok_or(DeviceAssignmentError::InvalidInterrupts)?
             .count();
         if interrupts_cells % CELLS_PER_INTERRUPT != 0 {
@@ -214,7 +196,7 @@
         }
 
         // Once validated, keep the raw bytes so patch can be done with setprop()
-        Ok(node.getprop(INTERRUPTS_PROP_NAME).unwrap().unwrap().into())
+        Ok(node.getprop(cstr!("interrupts")).unwrap().unwrap().into())
     }
 
     // TODO(b/277993056): Read and validate iommu
@@ -224,7 +206,7 @@
         let Some(node) = fdt.node(&node_path)? else { return Ok(None) };
 
         // TODO(b/277993056): Validate reg with HVC, and keep reg with FdtNode::reg()
-        let reg = node.getprop(REG_PROP_NAME).unwrap().unwrap();
+        let reg = node.getprop(cstr!("reg")).unwrap().unwrap();
 
         let interrupts = Self::parse_interrupts(&node)?;
 
@@ -238,8 +220,8 @@
 
     fn patch(&self, fdt: &mut Fdt) -> Result<()> {
         let mut dst = fdt.node_mut(&self.node_path)?.unwrap();
-        dst.setprop(REG_PROP_NAME, &self.reg)?;
-        dst.setprop(INTERRUPTS_PROP_NAME, &self.interrupts)?;
+        dst.setprop(cstr!("reg"), &self.reg)?;
+        dst.setprop(cstr!("interrupts"), &self.interrupts)?;
         // TODO(b/277993056): Read and patch iommu
         Ok(())
     }
@@ -275,7 +257,7 @@
                 filtered_dtbo_paths.push(dtbo_node_path.into());
             }
         }
-        filtered_dtbo_paths.push(VmDtbo::SYMBOLS_NODE_PATH.into());
+        filtered_dtbo_paths.push(CString::new("/__symbols__").unwrap());
 
         if assigned_devices.is_empty() {
             return Ok(None);
@@ -299,7 +281,12 @@
             node.nop()?;
         }
 
-        // Filters unused properties in assigned device node
+        // Filters pvmfw-specific properties in assigned device node.
+        const FILTERED_VM_DTBO_PROP: [&CStr; 3] = [
+            cstr!("android,pvmfw,phy-reg"),
+            cstr!("android,pvmfw,phy-iommu"),
+            cstr!("android,pvmfw,phy-sid"),
+        ];
         for assigned_device in &self.assigned_devices {
             let mut node = vm_dtbo.node_mut(&assigned_device.dtbo_node_path).unwrap().unwrap();
             for prop in FILTERED_VM_DTBO_PROP {