Merge "Make microdroid_init_debug_policy buildable for AIDEGen" into main
diff --git a/Android.bp b/Android.bp
index 4fa696f..22581b0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,7 @@
         "release_avf_enable_dice_changes",
         "release_avf_enable_llpvm_changes",
         "release_avf_enable_multi_tenant_microdroid_vm",
+        "release_avf_enable_remote_attestation",
         "release_avf_enable_vendor_modules",
     ],
     properties: [
@@ -55,6 +56,9 @@
         release_avf_enable_multi_tenant_microdroid_vm: {
             cfgs: ["payload_not_root"],
         },
+        release_avf_enable_remote_attestation: {
+            cfgs: ["remote_attestation"],
+        },
         release_avf_enable_vendor_modules: {
             cfgs: ["vendor_modules"],
         },
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index a42f5ec..8257aae 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -231,6 +231,9 @@
         if additional_descriptors:
             for image in additional_descriptors:
                 cmd.extend(['--include_descriptors_from_image', image])
+
+        if 'Rollback Index' in info:
+            cmd.extend(['--rollback_index', info['Rollback Index']])
         RunCommand(args, cmd)
 
 
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 00831dd..bac93a4 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -409,7 +409,19 @@
     },
 }
 
-avb_add_hash_footer {
+soong_config_module_type {
+    name: "flag_aware_avb_add_hash_footer",
+    module_type: "avb_add_hash_footer",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_avf_enable_llpvm_changes",
+    ],
+    properties: [
+        "rollback_index",
+    ],
+}
+
+flag_aware_avb_add_hash_footer {
     name: "microdroid_kernel_signed",
     src: ":empty_file",
     filename: "microdroid_kernel",
@@ -431,6 +443,12 @@
         ":microdroid_initrd_normal_hashdesc",
         ":microdroid_initrd_debug_hashdesc",
     ],
+    // Below are properties that are conditionally set depending on value of build flags.
+    soong_config_variables: {
+        release_avf_enable_llpvm_changes: {
+            rollback_index: 1,
+        },
+    },
 }
 
 prebuilt_etc {
@@ -447,7 +465,7 @@
     },
 }
 
-avb_add_hash_footer {
+flag_aware_avb_add_hash_footer {
     name: "microdroid_kernel_with_modules_signed",
     src: ":empty_file",
     filename: "microdroid_kernel_with_modules",
@@ -465,6 +483,12 @@
         ":microdroid_initrd_normal_hashdesc",
         ":microdroid_initrd_debug_hashdesc",
     ],
+    // Below are properties that are conditionally set depending on value of build flags.
+    soong_config_variables: {
+        release_avf_enable_llpvm_changes: {
+            rollback_index: 1,
+        },
+    },
 }
 
 prebuilt_etc {
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index e9cb0ec..a496d53 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -144,18 +144,9 @@
         Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
     };
 
-    let death_reason_bytes = death_reason.as_bytes();
-    let mut sent_total = 0;
-    while sent_total < death_reason_bytes.len() {
+    for chunk in death_reason.as_bytes().chunks(16) {
         // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
-        let begin = sent_total;
-        let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
-        OpenOptions::new()
-            .read(false)
-            .write(true)
-            .open(FAILURE_SERIAL_DEVICE)?
-            .write_all(&death_reason_bytes[begin..end])?;
-        sent_total = end;
+        OpenOptions::new().read(false).write(true).open(FAILURE_SERIAL_DEVICE)?.write_all(chunk)?;
     }
 
     Ok(())
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 244b192..61de423 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -732,6 +732,7 @@
     strict_boot: bool,
     debug_policy: Option<&mut [u8]>,
     debuggable: bool,
+    kaslr_seed: u64,
 ) -> libfdt::Result<()> {
     if let Some(debug_policy) = debug_policy {
         let backup = Vec::from(fdt.as_slice());
@@ -753,6 +754,7 @@
     if let Some(mut chosen) = fdt.chosen_mut()? {
         empty_or_delete_prop(&mut chosen, cstr!("avf,strict-boot"), strict_boot)?;
         empty_or_delete_prop(&mut chosen, cstr!("avf,new-instance"), new_instance)?;
+        chosen.setprop_inplace(cstr!("kaslr-seed"), &kaslr_seed.to_be_bytes())?;
     };
     if !debuggable {
         if let Some(bootargs) = read_bootargs_from(fdt)? {
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index c6aa309..d39d51c 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -52,6 +52,7 @@
 use vmbase::heap;
 use vmbase::memory::flush;
 use vmbase::memory::MEMORY;
+use vmbase::rand;
 use vmbase::virtio::pci;
 
 const NEXT_BCC_SIZE: usize = GUEST_PAGE_SIZE;
@@ -154,12 +155,24 @@
     })?;
     flush(next_bcc);
 
+    let kaslr_seed = u64::from_ne_bytes(rand::random_array().map_err(|e| {
+        error!("Failed to generated guest KASLR seed: {e}");
+        RebootReason::InternalError
+    })?);
     let strict_boot = true;
-    modify_for_next_stage(fdt, next_bcc, new_instance, strict_boot, debug_policy, debuggable)
-        .map_err(|e| {
-            error!("Failed to configure device tree: {e}");
-            RebootReason::InternalError
-        })?;
+    modify_for_next_stage(
+        fdt,
+        next_bcc,
+        new_instance,
+        strict_boot,
+        debug_policy,
+        debuggable,
+        kaslr_seed,
+    )
+    .map_err(|e| {
+        error!("Failed to configure device tree: {e}");
+        RebootReason::InternalError
+    })?;
 
     info!("Starting payload...");
 
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 90ba575..80fdff7 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -28,6 +28,7 @@
     compile_multilib: "64",
     required: ["perf-setup"],
     host_required: ["MicrodroidTestPreparer"],
+    data: [":test_microdroid_vendor_image"],
 }
 
 cc_library_shared {
diff --git a/tests/benchmark/AndroidTest.xml b/tests/benchmark/AndroidTest.xml
index 8c8bfbe..11b17f4 100644
--- a/tests/benchmark/AndroidTest.xml
+++ b/tests/benchmark/AndroidTest.xml
@@ -30,6 +30,14 @@
         <option name="post-push" value="chmod 755 /data/local/tmp/perf-setup.sh;/data/local/tmp/perf-setup.sh" />
         <option name="cleanup" value="true" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/microdroid-bench" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/microdroid-bench" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="test_microdroid_vendor_image.img->/data/local/tmp/microdroid-bench/microdroid_vendor_image.img" />
+    </target_preparer>
     <target_preparer class="com.android.microdroid.test.preparer.DisableMicrodroidDebugPolicyPreparer" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.microdroid.benchmark" />
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 625f26a..1917654 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -38,6 +38,7 @@
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.system.Os;
+import android.system.virtualmachine.VirtualMachineManager;
 import android.util.Log;
 
 import com.android.microdroid.test.common.MetricsProcessor;
@@ -260,6 +261,26 @@
     }
 
     @Test
+    public void testMicrodroidDebugBootTime_withVendorPartition() throws Exception {
+        assumeFeatureEnabled(VirtualMachineManager.FEATURE_VENDOR_MODULES);
+
+        File vendorDiskImage =
+                new File("/data/local/tmp/microdroid-bench/microdroid_vendor_image.img");
+        BootTimeStats stats =
+                runBootTimeTest(
+                        "test_vm_boot_time_debug_with_vendor_partition",
+                        (builder) ->
+                                builder.setDebugLevel(DEBUG_LEVEL_FULL)
+                                        .setVmOutputCaptured(true)
+                                        .setVendorDiskImage(vendorDiskImage));
+        reportMetrics(stats.get(BootTimeMetric.TOTAL), "boot_time", "ms");
+        reportMetrics(stats.get(BootTimeMetric.VM_START), "vm_starting_time", "ms");
+        reportMetrics(stats.get(BootTimeMetric.BOOTLOADER), "bootloader_time", "ms");
+        reportMetrics(stats.get(BootTimeMetric.KERNEL), "kernel_boot_time", "ms");
+        reportMetrics(stats.get(BootTimeMetric.USERSPACE), "userspace_boot_time", "ms");
+    }
+
+    @Test
     public void testMicrodroidImageSize() throws IOException {
         Bundle bundle = new Bundle();
         for (File file : new File(APEX_ETC_FS).listFiles()) {
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 9f03ab7..e2795ec 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
@@ -20,6 +20,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.TruthJUnit.assume;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
@@ -545,4 +547,12 @@
     protected interface RunTestsAgainstTestService {
         void runTests(ITestService testService, TestResults testResults) throws Exception;
     }
+
+    protected void assumeFeatureEnabled(String featureName) throws Exception {
+        assumeTrue(featureName + " not enabled", isFeatureEnabled(featureName));
+    }
+
+    protected boolean isFeatureEnabled(String featureName) throws Exception {
+        return getVirtualMachineManager().isFeatureEnabled(featureName);
+    }
 }
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 6301a57..e3d9cbe 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -16,6 +16,7 @@
     static_libs: [
         "MicrodroidHostTestHelper",
         "compatibility-host-util",
+        "cts-host-utils",
         "cts-statsd-atom-host-test-utils",
         "microdroid_payload_metadata",
     ],
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 21960b4..d861761 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -30,6 +30,8 @@
 
 import static java.util.stream.Collectors.toList;
 
+import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
+import android.cts.host.utils.DeviceJUnit4Parameterized;
 import android.cts.statsdatom.lib.ConfigUtils;
 import android.cts.statsdatom.lib.ReportUtils;
 
@@ -42,7 +44,6 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.TestDevice;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.FileUtil;
@@ -58,6 +59,8 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.DefaultHandler;
 
@@ -67,6 +70,7 @@
 import java.io.PipedOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -77,7 +81,8 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
-@RunWith(DeviceJUnit4ClassRunner.class)
+@RunWith(DeviceJUnit4Parameterized.class)
+@UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
 public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
     private static final String APK_NAME = "MicrodroidTestApp.apk";
     private static final String PACKAGE_NAME = "com.android.microdroid.test";
@@ -97,6 +102,14 @@
         }
     }
 
+    @Parameterized.Parameters(name = "protectedVm={0}")
+    public static Collection<Object[]> params() {
+        return List.of(new Object[] {true}, new Object[] {false});
+    }
+
+    @Parameterized.Parameter(0)
+    public boolean mProtectedVm;
+
     @Rule public TestLogData mTestLogs = new TestLogData();
     @Rule public TestName mTestName = new TestName();
     @Rule public TestMetrics mMetrics = new TestMetrics();
@@ -259,9 +272,11 @@
         File virtApexEtcDir = new File(virtApexDir, "etc");
         // We need only etc/ directory for images
         assertWithMessage("Failed to mkdir " + virtApexEtcDir)
-                .that(virtApexEtcDir.mkdirs()).isTrue();
+                .that(virtApexEtcDir.mkdirs())
+                .isTrue();
         assertWithMessage("Failed to pull " + VIRT_APEX + "etc")
-                .that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir)).isTrue();
+                .that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir))
+                .isTrue();
 
         resignVirtApex(virtApexDir, key, keyOverrides, updateBootconfigs);
 
@@ -320,10 +335,12 @@
         final String initrdPath = TEST_ROOT + "etc/microdroid_initrd_debuggable.img";
         config.put("initrd", initrdPath);
         // Add instance image as a partition in disks[1]
-        disks.put(new JSONObject()
-                .put("writable", true)
-                .put("partitions",
-                        new JSONArray().put(newPartition("vm-instance", instanceImgPath))));
+        disks.put(
+                new JSONObject()
+                        .put("writable", true)
+                        .put(
+                                "partitions",
+                                new JSONArray().put(newPartition("vm-instance", instanceImgPath))));
         // Add payload image disk with partitions:
         // - payload-metadata
         // - apexes: com.android.os.statsd, com.android.adbd, [sharedlib apex](optional)
@@ -373,7 +390,7 @@
     @CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
     public void protectedVmRunsPvmfw() throws Exception {
         // Arrange
-        assumeProtectedVmSupported();
+        assumeProtectedVm();
         final String configPath = "assets/vm_config_apex.json";
 
         // Act
@@ -401,16 +418,16 @@
     @CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
     public void protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() throws Exception {
         // Arrange
-        assumeProtectedVmSupported();
+        assumeProtectedVm();
         File key = findTestFile("test.com.android.virt.pem");
 
         // Act
         VmInfo vmInfo =
                 runMicrodroidWithResignedImages(
                         key,
-                        /*keyOverrides=*/ Map.of(),
-                        /*isProtected=*/ true,
-                        /*updateBootconfigs=*/ true);
+                        /* keyOverrides= */ Map.of(),
+                        /* isProtected= */ true,
+                        /* updateBootconfigs= */ true);
 
         // Assert
         vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
@@ -425,12 +442,12 @@
     @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
     public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
             throws Exception {
-        assumeNonProtectedVmSupported();
+        assumeNonProtectedVm();
         File key = findTestFile("test.com.android.virt.pem");
         Map<String, File> keyOverrides = Map.of();
         VmInfo vmInfo =
                 runMicrodroidWithResignedImages(
-                        key, keyOverrides, /*isProtected=*/ false, /*updateBootconfigs=*/ true);
+                        key, keyOverrides, /* isProtected= */ false, /* updateBootconfigs= */ true);
         assertThatEventually(
                 100000,
                 () -> getDevice().pullFileContents(LOG_PATH),
@@ -443,13 +460,13 @@
     @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
     public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig() throws Exception {
         // protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() is the protected case.
-        assumeNonProtectedVmSupported();
+        assumeNonProtectedVm();
         // Sign everything with key1 except vbmeta
         File key = findTestFile("test.com.android.virt.pem");
         // To be able to stop it, it should be a daemon.
         VmInfo vmInfo =
                 runMicrodroidWithResignedImages(
-                        key, Map.of(), /*isProtected=*/ false, /*updateBootconfigs=*/ false);
+                        key, Map.of(), /* isProtected= */ false, /* updateBootconfigs= */ false);
         // Wait so that init can print errors to console (time in cuttlefish >> in real device)
         assertThatEventually(
                 100000,
@@ -521,26 +538,12 @@
     }
 
     @Test
-    public void testTombstonesAreGeneratedUponUserspaceCrashOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
+    public void testTombstonesAreGeneratedUponUserspaceCrash() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        testTombstonesAreGeneratedUponUserspaceCrash(false);
-    }
-
-    @Test
-    public void testTombstonesAreGeneratedUponUserspaceCrashOnPvm() throws Exception {
-        assumeProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        testTombstonesAreGeneratedUponUserspaceCrash(true);
-    }
-
-    private void testTombstonesAreGeneratedUponUserspaceCrash(boolean protectedVm)
-            throws Exception {
         assertThat(
                         isTombstoneGeneratedWithCmd(
-                                protectedVm,
+                                mProtectedVm,
                                 "assets/vm_config.json",
                                 "kill",
                                 "-SIGSEGV",
@@ -549,28 +552,12 @@
     }
 
     @Test
-    public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnNonPvm()
-            throws Exception {
-        assumeNonProtectedVmSupported();
+    public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(false);
-    }
-
-    @Test
-    public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnPvm()
-            throws Exception {
-        assumeProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(true);
-    }
-
-    private void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(boolean protectedVm)
-            throws Exception {
         assertThat(
                         isTombstoneGeneratedWithCmd(
-                                protectedVm,
+                                mProtectedVm,
                                 "assets/vm_config_no_tombstone.json",
                                 "kill",
                                 "-SIGSEGV",
@@ -578,12 +565,13 @@
                 .isFalse();
     }
 
-    private void testTombstonesAreGeneratedUponKernelCrash(boolean protectedVm) throws Exception {
+    @Test
+    public void testTombstonesAreGeneratedUponKernelCrash() throws Exception {
         assumeFalse("Cuttlefish is not supported", isCuttlefish());
         assumeFalse("Skipping test because ramdump is disabled on user build", isUserBuild());
         assertThat(
                         isTombstoneGeneratedWithCmd(
-                                protectedVm,
+                                mProtectedVm,
                                 "assets/vm_config.json",
                                 "echo",
                                 "c",
@@ -592,18 +580,6 @@
                 .isTrue();
     }
 
-    @Test
-    public void testTombstonesAreGeneratedUponKernelCrashOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
-        testTombstonesAreGeneratedUponKernelCrash(/* protectedVm=*/ false);
-    }
-
-    @Test
-    public void testTombstonesAreGeneratedUponKernelCrashOnPvm() throws Exception {
-        assumeProtectedVmSupported();
-        testTombstonesAreGeneratedUponKernelCrash(/* protectedVm=*/ true);
-    }
-
     private boolean isTombstoneGeneratedWithVmRunApp(
             boolean protectedVm, boolean debuggable, String... additionalArgs) throws Exception {
         // we can't use microdroid builder as it wants ADB connection (debuggable)
@@ -642,48 +618,18 @@
     }
 
     @Test
-    public void testTombstonesAreGeneratedWithCrashPayloadOnPvm() throws Exception {
-        assumeProtectedVmSupported();
+    public void testTombstonesAreGeneratedWithCrashPayload() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashPayload(
-                                /*protectedVm=*/ true, /*debuggable=*/ true))
+        assertThat(isTombstoneGeneratedWithCrashPayload(mProtectedVm, /* debuggable= */ true))
                 .isTrue();
     }
 
     @Test
-    public void testTombstonesAreGeneratedWithCrashPayloadOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
+    public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggable() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashPayload(
-                                /*protectedVm=*/ false, /*debuggable=*/ true))
-                .isTrue();
-    }
-
-    @Test
-    public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnPvm()
-            throws Exception {
-        assumeProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashPayload(
-                                /*protectedVm=*/ true, /*debuggable=*/ false))
-                .isFalse();
-    }
-
-    @Test
-    public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnNonPvm()
-            throws Exception {
-        assumeNonProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashPayload(
-                                /*protectedVm=*/ false, /*debuggable=*/ false))
+        assertThat(isTombstoneGeneratedWithCrashPayload(mProtectedVm, /* debuggable= */ false))
                 .isFalse();
     }
 
@@ -694,62 +640,23 @@
     }
 
     @Test
-    public void testTombstonesAreGeneratedWithCrashConfigOnPvm() throws Exception {
-        assumeProtectedVmSupported();
+    public void testTombstonesAreGeneratedWithCrashConfig() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(isTombstoneGeneratedWithCrashConfig(/*protectedVm=*/ true, /*debuggable=*/ true))
+        assertThat(isTombstoneGeneratedWithCrashConfig(mProtectedVm, /* debuggable= */ true))
                 .isTrue();
     }
 
     @Test
-    public void testTombstonesAreGeneratedWithCrashConfigOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
+    public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggable() throws Exception {
         // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
         assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashConfig(
-                                /*protectedVm=*/ false, /*debuggable=*/ true))
-                .isTrue();
-    }
-
-    @Test
-    public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnPvm()
-            throws Exception {
-        assumeProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashConfig(
-                                /*protectedVm=*/ true, /*debuggable=*/ false))
+        assertThat(isTombstoneGeneratedWithCrashConfig(mProtectedVm, /* debuggable= */ false))
                 .isFalse();
     }
 
     @Test
-    public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnNonPvm()
-            throws Exception {
-        assumeNonProtectedVmSupported();
-        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
-        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
-        assertThat(
-                        isTombstoneGeneratedWithCrashConfig(
-                                /*protectedVm=*/ false, /*debuggable=*/ false))
-                .isFalse();
-    }
-
-    @Test
-    public void testTelemetryPushedAtomsOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
-        testTelemetryPushedAtoms(false);
-    }
-
-    @Test
-    public void testTelemetryPushedAtomsOnPvm() throws Exception {
-        assumeProtectedVmSupported();
-        testTelemetryPushedAtoms(true);
-    }
-
-    private void testTelemetryPushedAtoms(boolean protectedVm) throws Exception {
+    public void testTelemetryPushedAtoms() throws Exception {
         // Reset statsd config and report before the test
         ConfigUtils.removeConfig(getDevice());
         ReportUtils.clearReports(getDevice());
@@ -770,7 +677,7 @@
                         .debugLevel("full")
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
-                        .protectedVm(protectedVm)
+                        .protectedVm(mProtectedVm)
                         .build(device);
         microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         device.shutdownMicrodroid(microdroid);
@@ -797,7 +704,7 @@
                 data.get(0).getAtom().getVmCreationRequested();
         assertThat(atomVmCreationRequested.getHypervisor())
                 .isEqualTo(AtomsProto.VmCreationRequested.Hypervisor.PKVM);
-        assertThat(atomVmCreationRequested.getIsProtected()).isEqualTo(protectedVm);
+        assertThat(atomVmCreationRequested.getIsProtected()).isEqualTo(mProtectedVm);
         assertThat(atomVmCreationRequested.getCreationSucceeded()).isTrue();
         assertThat(atomVmCreationRequested.getBinderExceptionCode()).isEqualTo(0);
         assertThat(atomVmCreationRequested.getVmIdentifier()).isEqualTo("VmRunApp");
@@ -826,19 +733,7 @@
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
-    public void testMicrodroidBootsOnPvm() throws Exception {
-        assumeProtectedVmSupported();
-        testMicrodroidBoots(true);
-    }
-
-    @Test
-    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
-    public void testMicrodroidBootsOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
-        testMicrodroidBoots(false);
-    }
-
-    private void testMicrodroidBoots(boolean protectedVm) throws Exception {
+    public void testMicrodroidBoots() throws Exception {
         CommandRunner android = new CommandRunner(getDevice());
 
         final String configPath = "assets/vm_config.json"; // path inside the APK
@@ -847,7 +742,7 @@
                         .debugLevel("full")
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
-                        .protectedVm(protectedVm)
+                        .protectedVm(mProtectedVm)
                         .build(getAndroidDevice());
         mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
@@ -906,25 +801,14 @@
     }
 
     @Test
-    public void testMicrodroidRamUsageOnPvm() throws Exception {
-        assumeProtectedVmSupported();
-        testMicrodroidRamUsage(true);
-    }
-
-    @Test
-    public void testMicrodroidRamUsageOnNonPvm() throws Exception {
-        assumeNonProtectedVmSupported();
-        testMicrodroidRamUsage(false);
-    }
-
-    private void testMicrodroidRamUsage(boolean protectedVm) throws Exception {
+    public void testMicrodroidRamUsage() throws Exception {
         final String configPath = "assets/vm_config.json";
         mMicrodroidDevice =
                 MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
                         .debugLevel("full")
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
-                        .protectedVm(protectedVm)
+                        .protectedVm(mProtectedVm)
                         .build(getAndroidDevice());
         mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         mMicrodroidDevice.enableAdbRoot();
@@ -949,8 +833,7 @@
         for (Map.Entry<Integer, String> proc :
                 ProcessUtil.getProcessMap(microdroidExec).entrySet()) {
             for (Map.Entry<String, Long> stat :
-                    ProcessUtil.getProcessSmapsRollup(proc.getKey(), microdroidExec)
-                            .entrySet()) {
+                    ProcessUtil.getProcessSmapsRollup(proc.getKey(), microdroidExec).entrySet()) {
                 String name = stat.getKey().toLowerCase();
                 mMetrics.addTestMetric(
                         mMetricPrefix + "smaps/" + name + "/" + proc.getValue(),
@@ -1066,8 +949,8 @@
     }
 
     @Test
-    public void testDevcieAssignment() throws Exception {
-        assumeProtectedVmSupported();
+    public void testDeviceAssignment() throws Exception {
+        assumeProtectedVm();
         assumeVfioPlatformSupported();
 
         List<String> devices = getAssignableDevices();
@@ -1095,6 +978,11 @@
         prepareVirtualizationTestSetup(getDevice());
 
         getDevice().installPackage(findTestFile(APK_NAME), /* reinstall */ false);
+
+        // Skip test if given device doesn't support protected or non-protected VM.
+        assumeTrue(
+                "Microdroid is not supported for specific VM protection type",
+                getAndroidDevice().supportsMicrodroid(mProtectedVm));
     }
 
     @After
@@ -1119,16 +1007,12 @@
                         "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
     }
 
-    private void assumeProtectedVmSupported() throws Exception {
-        assumeTrue(
-                "Test skipped because protected VMs are not supported",
-                getAndroidDevice().supportsMicrodroid(true));
+    private void assumeProtectedVm() throws Exception {
+        assumeTrue("This test is only for protected VM.", mProtectedVm);
     }
 
-    private void assumeNonProtectedVmSupported() throws Exception {
-        assumeTrue(
-                "Test skipped because non-protected VMs are not supported",
-                getAndroidDevice().supportsMicrodroid(false));
+    private void assumeNonProtectedVm() throws Exception {
+        assumeFalse("This test is only for non-protected VM.", mProtectedVm);
     }
 
     private void assumeVfioPlatformSupported() throws Exception {
diff --git a/tests/pvmfw/Android.bp b/tests/pvmfw/Android.bp
index 61667f3..474c62e 100644
--- a/tests/pvmfw/Android.bp
+++ b/tests/pvmfw/Android.bp
@@ -9,6 +9,20 @@
 }
 
 genrule {
+    name: "test_avf_debug_policy_with_ramdump",
+    defaults: ["test_avf_dts_to_dtb"],
+    srcs: ["assets/avf_debug_policy_with_ramdump.dts"],
+    out: ["avf_debug_policy_with_ramdump.dtbo"],
+}
+
+genrule {
+    name: "test_avf_debug_policy_without_ramdump",
+    defaults: ["test_avf_dts_to_dtb"],
+    srcs: ["assets/avf_debug_policy_without_ramdump.dts"],
+    out: ["avf_debug_policy_without_ramdump.dtbo"],
+}
+
+genrule {
     name: "test_avf_debug_policy_with_adb",
     defaults: ["test_avf_dts_to_dtb"],
     srcs: ["assets/avf_debug_policy_with_adb.dts"],
@@ -39,6 +53,8 @@
     data: [
         ":MicrodroidTestApp",
         ":pvmfw_test",
+        ":test_avf_debug_policy_with_ramdump",
+        ":test_avf_debug_policy_without_ramdump",
         ":test_avf_debug_policy_with_adb",
         ":test_avf_debug_policy_without_adb",
         "assets/bcc.dat",
diff --git a/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts b/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts
new file mode 100644
index 0000000..139d28e
--- /dev/null
+++ b/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+    fragment@avf {
+        target-path = "/";
+
+        __overlay__ {
+            avf {
+                guest {
+                    common {
+                        ramdump = <1>;
+                    };
+                    microdroid {
+                        adb = <1>; // adb is required to check VM's bootargs.
+                    };
+                };
+            };
+        };
+    };
+};
+
diff --git a/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts b/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts
new file mode 100644
index 0000000..8e0e44c
--- /dev/null
+++ b/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+    fragment@avf {
+        target-path = "/";
+
+        __overlay__ {
+            avf {
+                guest {
+                    common {
+                        ramdump = <0>;
+                    };
+                    microdroid {
+                        adb = <1>; // adb is required to check VM's bootargs.
+                    };
+                };
+            };
+        };
+    };
+};
+
diff --git a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
index 410e6e0..7d0faa4 100644
--- a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
@@ -192,6 +192,43 @@
         launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
     }
 
+    @Test
+    public void testRamdumpInDebugPolicy_withDebugLevelNone_hasRamdumpArgs() throws Exception {
+        prepareCustomDebugPolicy("avf_debug_policy_with_ramdump.dtbo");
+        mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_NONE);
+
+        assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH)).contains("crashkernel=");
+        assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
+                .contains("crashkernel=");
+        assertThat(readMicrodroidFileAsHexString(MICRODROID_DT_RAMDUMP_PATH))
+                .isEqualTo(HEX_STRING_ONE);
+    }
+
+    @Test
+    public void testNoRamdumpInDebugPolicy_withDebugLevelNone_noRamdumpArgs() throws Exception {
+        prepareCustomDebugPolicy("avf_debug_policy_without_ramdump.dtbo");
+        mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_NONE);
+
+        assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH))
+                .doesNotContain("crashkernel=");
+        assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
+                .doesNotContain("crashkernel=");
+        assertThat(readMicrodroidFileAsHexString(MICRODROID_DT_RAMDUMP_PATH))
+                .isEqualTo(HEX_STRING_ZERO);
+    }
+
+    @Test
+    public void testNoRamdumpInDebugPolicy_withDebugLevelFull_hasRamdumpArgs() throws Exception {
+        prepareCustomDebugPolicy("avf_debug_policy_without_ramdump.dtbo");
+        mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
+
+        assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH)).contains("crashkernel=");
+        assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
+                .contains("crashkernel=");
+        assertThat(readMicrodroidFileAsHexString(MICRODROID_DT_RAMDUMP_PATH))
+                .isEqualTo(HEX_STRING_ZERO);
+    }
+
     private boolean isDebugPolicyEnabled(@NonNull String dtPropertyPath)
             throws DeviceNotAvailableException {
         CommandRunner runner = new CommandRunner(mAndroidDevice);
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 9fe5614..4b9f803 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -2254,12 +2254,4 @@
                 .that(KERNEL_VERSION)
                 .isNotEqualTo("5.4");
     }
-
-    private void assumeFeatureEnabled(String featureName) throws Exception {
-        assumeTrue(featureName + " not enabled", isFeatureEnabled(featureName));
-    }
-
-    private boolean isFeatureEnabled(String featureName) throws Exception {
-        return getVirtualMachineManager().isFeatureEnabled(featureName);
-    }
 }
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index c660414..12d8724 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -82,6 +82,8 @@
         "libtempfile",
     ],
     data: [
+        ":test_avf_debug_policy_with_ramdump",
+        ":test_avf_debug_policy_without_ramdump",
         ":test_avf_debug_policy_with_adb",
         ":test_avf_debug_policy_without_adb",
     ],
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 790cdb5..8456888 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -18,7 +18,7 @@
 use crate::atom::{
     write_vm_booted_stats, write_vm_creation_stats};
 use crate::composite::make_composite_image;
-use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
+use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VfioDevice, VmContext, VmInstance, VmState};
 use crate::debug_config::DebugConfig;
 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
 use crate::selinux::{getfilecon, SeContext};
@@ -458,7 +458,7 @@
             }
         };
 
-        if !config.devices.is_empty() {
+        let vfio_devices = if !config.devices.is_empty() {
             let mut set = HashSet::new();
             for device in config.devices.iter() {
                 let path = canonicalize(device)
@@ -469,8 +469,17 @@
                         .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
                 }
             }
-            GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
-        }
+            GLOBAL_SERVICE
+                .bindDevicesToVfioDriver(&config.devices)?
+                .into_iter()
+                .map(|x| VfioDevice {
+                    sysfs_path: PathBuf::from(&x.sysfsPath),
+                    dtbo_node: x.dtboNode,
+                })
+                .collect::<Vec<_>>()
+        } else {
+            vec![]
+        };
 
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
@@ -495,7 +504,7 @@
             platform_version: parse_platform_version_req(&config.platformVersion)?,
             detect_hangup: is_app_config,
             gdb_port,
-            vfio_devices: config.devices.iter().map(PathBuf::from).collect(),
+            vfio_devices,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 77dd76f..b053d99 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -115,7 +115,7 @@
     pub platform_version: VersionReq,
     pub detect_hangup: bool,
     pub gdb_port: Option<NonZeroU16>,
-    pub vfio_devices: Vec<PathBuf>,
+    pub vfio_devices: Vec<VfioDevice>,
 }
 
 /// A disk image to pass to crosvm for a VM.
@@ -125,6 +125,12 @@
     pub writable: bool,
 }
 
+#[derive(Clone, Debug)]
+pub struct VfioDevice {
+    pub sysfs_path: PathBuf,
+    pub dtbo_node: String,
+}
+
 /// The lifecycle state which the payload in the VM has reported itself to be in.
 ///
 /// Note that the order of enum variants is significant; only forward transitions are allowed by
@@ -144,7 +150,7 @@
     /// The VM has not yet tried to start.
     NotStarted {
         ///The configuration needed to start the VM, if it has not yet been started.
-        config: CrosvmConfig,
+        config: Box<CrosvmConfig>,
     },
     /// The VM has been started.
     Running {
@@ -171,7 +177,8 @@
 pub struct VmMetric {
     /// Recorded timestamp when the VM is started.
     pub start_timestamp: Option<SystemTime>,
-    /// Update most recent guest_time periodically from /proc/[crosvm pid]/stat while VM is running.
+    /// Update most recent guest_time periodically from /proc/[crosvm pid]/stat while VM is
+    /// running.
     pub cpu_guest_time: Option<i64>,
     /// Update maximum RSS values periodically from /proc/[crosvm pid]/smaps while VM is running.
     pub rss: Option<Rss>,
@@ -184,6 +191,7 @@
     fn start(&mut self, instance: Arc<VmInstance>) -> Result<(), Error> {
         let state = mem::replace(self, VmState::Failed);
         if let VmState::NotStarted { config } = state {
+            let config = *config;
             let detect_hangup = config.detect_hangup;
             let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
             let vfio_devices = config.vfio_devices.clone();
@@ -303,7 +311,7 @@
             .flatten()
             .map_or_else(|| format!("{}", requester_uid), |u| u.name);
         let instance = VmInstance {
-            vm_state: Mutex::new(VmState::NotStarted { config }),
+            vm_state: Mutex::new(VmState::NotStarted { config: Box::new(config) }),
             vm_context,
             cid,
             crosvm_control_socket_path: temporary_directory.join("crosvm.sock"),
@@ -342,7 +350,7 @@
         &self,
         child: Arc<SharedChild>,
         mut failure_pipe_read: File,
-        vfio_devices: Vec<PathBuf>,
+        vfio_devices: Vec<VfioDevice>,
     ) {
         let result = child.wait();
         match &result {
@@ -641,10 +649,10 @@
 }
 
 fn death_reason(result: &Result<ExitStatus, io::Error>, mut failure_reason: &str) -> DeathReason {
-    if let Some(position) = failure_reason.find('|') {
+    if let Some((reason, info)) = failure_reason.split_once('|') {
         // Separator indicates extra context information is present after the failure name.
-        error!("Failure info: {}", &failure_reason[(position + 1)..]);
-        failure_reason = &failure_reason[..position];
+        error!("Failure info: {info}");
+        failure_reason = reason;
     }
     if let Ok(status) = result {
         match failure_reason {
@@ -694,9 +702,9 @@
 const SYSFS_PLATFORM_DEVICES_PATH: &str = "/sys/devices/platform/";
 const VFIO_PLATFORM_DRIVER_PATH: &str = "/sys/bus/platform/drivers/vfio-platform";
 
-fn vfio_argument_for_platform_device(path: &Path) -> Result<String, Error> {
+fn vfio_argument_for_platform_device(device: &VfioDevice) -> Result<String, Error> {
     // Check platform device exists
-    let path = path.canonicalize()?;
+    let path = device.sysfs_path.canonicalize()?;
     if !path.starts_with(SYSFS_PLATFORM_DEVICES_PATH) {
         bail!("{path:?} is not a platform device");
     }
@@ -708,7 +716,7 @@
     }
 
     if let Some(p) = path.to_str() {
-        Ok(format!("--vfio={p},iommu=viommu"))
+        Ok(format!("--vfio={p},iommu=viommu,dt-symbol={0}", device.dtbo_node))
     } else {
         bail!("invalid path {path:?}");
     }
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index 9b13475..5d22f59 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -239,6 +239,38 @@
     }
 
     #[test]
+    fn test_read_avf_debug_policy_with_ramdump() -> Result<()> {
+        let debug_config = DebugConfig::from_custom_debug_overlay_policy(
+            DebugLevel::FULL,
+            "avf_debug_policy_with_ramdump.dtbo".as_ref(),
+        )
+        .unwrap();
+
+        assert_eq!(DebugLevel::FULL, debug_config.debug_level);
+        assert!(!debug_config.debug_policy_log);
+        assert!(debug_config.debug_policy_ramdump);
+        assert!(debug_config.debug_policy_adb);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_read_avf_debug_policy_without_ramdump() -> Result<()> {
+        let debug_config = DebugConfig::from_custom_debug_overlay_policy(
+            DebugLevel::FULL,
+            "avf_debug_policy_without_ramdump.dtbo".as_ref(),
+        )
+        .unwrap();
+
+        assert_eq!(DebugLevel::FULL, debug_config.debug_level);
+        assert!(!debug_config.debug_policy_log);
+        assert!(!debug_config.debug_policy_ramdump);
+        assert!(debug_config.debug_policy_adb);
+
+        Ok(())
+    }
+
+    #[test]
     fn test_read_avf_debug_policy_with_adb() -> Result<()> {
         let debug_config = DebugConfig::from_custom_debug_overlay_policy(
             DebugLevel::FULL,
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index 62d66c0..f3a7617 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -23,6 +23,10 @@
 import android.system.virtualizationservice_internal.IGlobalVmContext;
 
 interface IVirtualizationServiceInternal {
+    parcelable BoundDevice {
+        String sysfsPath;
+        String dtboNode;
+    }
     /**
      * Removes the memlock rlimit of the calling process.
      *
@@ -68,6 +72,7 @@
      * Bind given devices to vfio driver.
      *
      * @param devices paths of sysfs nodes of devices to assign.
+     * @return a list of pairs (sysfs path, DTBO node label) for devices.
      */
-    void bindDevicesToVfioDriver(in String[] devices);
+    BoundDevice[] bindDevicesToVfioDriver(in String[] devices);
 }
diff --git a/virtualizationservice/assignable_devices.xsd b/virtualizationservice/assignable_devices.xsd
index 842542e..8f43019 100644
--- a/virtualizationservice/assignable_devices.xsd
+++ b/virtualizationservice/assignable_devices.xsd
@@ -25,6 +25,7 @@
     </xs:element>
     <xs:complexType name="device">
         <xs:attribute name="kind" type="xs:string"/>
+        <xs:attribute name="dtbo_node" type="xs:string"/>
         <xs:attribute name="sysfs_path" type="xs:string"/>
     </xs:complexType>
 </xs:schema>
diff --git a/virtualizationservice/schema/current.txt b/virtualizationservice/schema/current.txt
index ed0763a..ef99294 100644
--- a/virtualizationservice/schema/current.txt
+++ b/virtualizationservice/schema/current.txt
@@ -3,8 +3,10 @@
 
   public class Device {
     ctor public Device();
+    method public String getDtbo_node();
     method public String getKind();
     method public String getSysfs_path();
+    method public void setDtbo_node(String);
     method public void setKind(String);
     method public void setSysfs_path(String);
   }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 6f5a487..ed5c513 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -28,6 +28,7 @@
     AtomVmCreationRequested::AtomVmCreationRequested,
     AtomVmExited::AtomVmExited,
     IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
+    IVirtualizationServiceInternal::BoundDevice::BoundDevice,
     IVirtualizationServiceInternal::IVirtualizationServiceInternal,
     IVfioHandler::{BpVfioHandler, IVfioHandler},
 };
@@ -160,53 +161,33 @@
     fn requestCertificate(&self, csr: &[u8]) -> binder::Result<Vec<u8>> {
         check_manage_access()?;
         info!("Received csr. Getting certificate...");
-        request_certificate(csr)
-            .context("Failed to get certificate")
+        if cfg!(remote_attestation) {
+            request_certificate(csr)
+                .context("Failed to get certificate")
+                .with_log()
+                .or_service_specific_exception(-1)
+        } else {
+            Err(Status::new_exception_str(
+                ExceptionCode::UNSUPPORTED_OPERATION,
+                Some(
+                    "requestCertificate is not supported with the remote_attestation feature disabled",
+                ),
+            ))
             .with_log()
-            .or_service_specific_exception(-1)
+        }
     }
 
     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
         check_use_custom_virtual_machine()?;
 
-        let mut ret = Vec::new();
-        let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
-        if !xml_path.exists() {
-            return Ok(ret);
-        }
-
-        let xml = fs::read(xml_path)
-            .context("Failed to read assignable_devices.xml")
-            .with_log()
-            .or_service_specific_exception(-1)?;
-
-        let xml = String::from_utf8(xml)
-            .context("assignable_devices.xml is not a valid UTF-8 file")
-            .with_log()
-            .or_service_specific_exception(-1)?;
-
-        let devices: Devices = serde_xml_rs::from_str(&xml)
-            .context("can't parse assignable_devices.xml")
-            .with_log()
-            .or_service_specific_exception(-1)?;
-
-        let mut device_set = HashSet::new();
-
-        for device in devices.device.into_iter() {
-            if device_set.contains(&device.sysfs_path) {
-                warn!("duplicated assignable device {device:?}; ignoring...")
-            } else if Path::new(&device.sysfs_path).exists() {
-                device_set.insert(device.sysfs_path.clone());
-                ret.push(AssignableDevice { kind: device.kind, node: device.sysfs_path });
-            } else {
-                warn!("assignable device {device:?} doesn't exist; ignoring...");
-            }
-        }
-
-        Ok(ret)
+        Ok(get_assignable_devices()?
+            .device
+            .into_iter()
+            .map(|x| AssignableDevice { node: x.sysfs_path, kind: x.kind })
+            .collect::<Vec<_>>())
     }
 
-    fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<()> {
+    fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<Vec<BoundDevice>> {
         check_use_custom_virtual_machine()?;
 
         let vfio_service: Strong<dyn IVfioHandler> =
@@ -223,7 +204,17 @@
             vfio_service.writeVmDtbo(&ParcelFileDescriptor::new(dtbo))?;
         }
 
-        Ok(())
+        Ok(get_assignable_devices()?
+            .device
+            .into_iter()
+            .filter_map(|x| {
+                if devices.contains(&x.sysfs_path) {
+                    Some(BoundDevice { sysfsPath: x.sysfs_path, dtboNode: x.dtbo_node })
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>())
     }
 }
 
@@ -231,14 +222,54 @@
 #[derive(Debug, Deserialize)]
 struct Device {
     kind: String,
+    dtbo_node: String,
     sysfs_path: String,
 }
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Default, Deserialize)]
 struct Devices {
     device: Vec<Device>,
 }
 
+fn get_assignable_devices() -> binder::Result<Devices> {
+    let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
+    if !xml_path.exists() {
+        return Ok(Devices { ..Default::default() });
+    }
+
+    let xml = fs::read(xml_path)
+        .context("Failed to read assignable_devices.xml")
+        .with_log()
+        .or_service_specific_exception(-1)?;
+
+    let xml = String::from_utf8(xml)
+        .context("assignable_devices.xml is not a valid UTF-8 file")
+        .with_log()
+        .or_service_specific_exception(-1)?;
+
+    let mut devices: Devices = serde_xml_rs::from_str(&xml)
+        .context("can't parse assignable_devices.xml")
+        .with_log()
+        .or_service_specific_exception(-1)?;
+
+    let mut device_set = HashSet::new();
+    devices.device.retain(move |device| {
+        if device_set.contains(&device.sysfs_path) {
+            warn!("duplicated assignable device {device:?}; ignoring...");
+            return false;
+        }
+
+        if !Path::new(&device.sysfs_path).exists() {
+            warn!("assignable device {device:?} doesn't exist; ignoring...");
+            return false;
+        }
+
+        device_set.insert(device.sysfs_path.clone());
+        true
+    });
+    Ok(devices)
+}
+
 #[derive(Debug, Default)]
 struct GlobalVmInstance {
     /// The unique CID assigned to the VM for vsock communication.
diff --git a/virtualizationservice/src/remote_provisioning.rs b/virtualizationservice/src/remote_provisioning.rs
index 06f8ad4..1c8d1e6 100644
--- a/virtualizationservice/src/remote_provisioning.rs
+++ b/virtualizationservice/src/remote_provisioning.rs
@@ -94,6 +94,16 @@
         keysToSign: &[MacedPublicKey],
         challenge: &[u8],
     ) -> BinderResult<Vec<u8>> {
+        const MAX_CHALLENGE_SIZE: usize = 64;
+        if challenge.len() > MAX_CHALLENGE_SIZE {
+            let message = format!(
+                "Challenge is too big. Actual: {:?}. Maximum: {:?}.",
+                challenge.len(),
+                MAX_CHALLENGE_SIZE
+            );
+            return Err(Status::new_service_specific_error_str(STATUS_FAILED, Some(message)))
+                .with_log();
+        }
         // TODO(b/299259624): Validate the MAC of the keys to certify.
         rkpvm::generate_certificate_request(keysToSign, challenge)
             .context("Failed to generate certificate request")
diff --git a/vmclient/src/error_code.rs b/vmclient/src/error_code.rs
index a7c442f..10e6d4d 100644
--- a/vmclient/src/error_code.rs
+++ b/vmclient/src/error_code.rs
@@ -20,8 +20,8 @@
     /// Error code for all other errors not listed below.
     Unknown,
 
-    /// Error code indicating that the payload can't be verified due to various reasons (e.g invalid
-    /// merkle tree, invalid formats, etc).
+    /// Error code indicating that the payload can't be verified due to various reasons (e.g
+    /// invalid merkle tree, invalid formats, etc).
     PayloadVerificationFailed,
 
     /// Error code indicating that the payload is verified, but has changed since the last boot.
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 7c0383b..9f1d7d1 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -35,8 +35,8 @@
         VirtualMachineState::VirtualMachineState,
     },
     binder::{
-        wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
-        ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
+        BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, ParcelFileDescriptor,
+        Result as BinderResult, StatusCode, Strong,
     },
 };
 use command_fds::CommandFdExt;
@@ -53,9 +53,6 @@
     time::Duration,
 };
 
-const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
-    "android.system.virtualizationservice";
-
 const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
 const VIRTMGR_THREADS: usize = 2;
 
@@ -128,11 +125,6 @@
     }
 }
 
-/// Connects to the VirtualizationService AIDL service.
-pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
-    wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
-}
-
 /// A virtual machine which has been started by the VirtualizationService.
 pub struct VmInstance {
     /// The `IVirtualMachine` Binder object representing the VM.