Merge changes from topic "avb_testkey_rsa4096_pub_bin"

* changes:
  pvmfw: Use pvmfw_embedded_key from Rust
  pvmfw: Introduce pvmfw_embedded_key
diff --git a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
index 5e9073a..641b566 100644
--- a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
+++ b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.platform.test.annotations.RootPermissionTest;
 
 import com.android.fs.common.AuthFsTestRule;
@@ -71,11 +73,13 @@
 
     @Before
     public void setUp() throws Exception {
+        assumeTrue(AuthFsTestRule.getDevice().supportsMicrodroid(/*protectedVm=*/ true));
         String metricsPrefix =
                 MetricsProcessor.getMetricPrefix(
                         getDevice().getProperty("debug.hypervisor.metrics_tag"));
         mMetricsProcessor = new MetricsProcessor(metricsPrefix + "authfs/");
-        AuthFsTestRule.startMicrodroid();
+        // TODO(b/236123069): Run benchmark tests in both protected and unprotected VMs.
+        AuthFsTestRule.startMicrodroid(/*protectedVm=*/ true);
     }
 
     @After
diff --git a/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java b/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
index 994f23b..2220169 100644
--- a/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
+++ b/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
@@ -24,7 +24,6 @@
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.util.PollingCheck;
@@ -131,7 +130,7 @@
         return sMicrodroidDevice;
     }
 
-    public static void startMicrodroid() throws DeviceNotAvailableException {
+    public static void startMicrodroid(boolean protectedVm) throws DeviceNotAvailableException {
         CLog.i("Starting the shared VM");
         assertThat(sMicrodroidDevice).isNull();
         sMicrodroidDevice =
@@ -139,6 +138,7 @@
                                 findTestFile(sTestInfo.getBuildInfo(), TEST_APK_NAME),
                                 VM_CONFIG_PATH_IN_APK)
                         .debugLevel("full")
+                        .protectedVm(protectedVm)
                         .build(getDevice());
 
         // From this point on, we need to tear down the Microdroid instance
@@ -152,10 +152,11 @@
     }
 
     public static void shutdownMicrodroid() throws DeviceNotAvailableException {
-        assertNotNull(sMicrodroidDevice);
-        getDevice().shutdownMicrodroid(sMicrodroidDevice);
-        sMicrodroidDevice = null;
-        sMicrodroid = null;
+        if (sMicrodroidDevice != null) {
+            getDevice().shutdownMicrodroid(sMicrodroidDevice);
+            sMicrodroidDevice = null;
+            sMicrodroid = null;
+        }
     }
 
     @Override
@@ -224,7 +225,7 @@
         }
     }
 
-    private static TestDevice getDevice() {
+    public static TestDevice getDevice() {
         return (TestDevice) sTestInfo.getDevice();
     }
 
@@ -245,7 +246,6 @@
     }
 
     private void setUpTest() throws Exception {
-        assumeTrue(getDevice().supportsMicrodroid());
         sAndroid.run("mkdir -p " + TEST_OUTPUT_DIR);
     }
 
diff --git a/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
index 3157dfd..967d104 100644
--- a/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.RootPermissionTest;
 
@@ -74,7 +75,8 @@
     @BeforeClassWithInfo
     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
         AuthFsTestRule.setUpAndroid(testInfo);
-        AuthFsTestRule.startMicrodroid();
+        assumeTrue(AuthFsTestRule.getDevice().supportsMicrodroid(/*protectedVm=*/ true));
+        AuthFsTestRule.startMicrodroid(/*protectedVm=*/ true);
         sAndroid = AuthFsTestRule.getAndroid();
         sMicrodroid = AuthFsTestRule.getMicrodroid();
     }
diff --git a/microdroid/vm_payload/Android.bp b/microdroid/vm_payload/Android.bp
index 925928e..eeca1c1 100644
--- a/microdroid/vm_payload/Android.bp
+++ b/microdroid/vm_payload/Android.bp
@@ -13,7 +13,9 @@
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
+        "liblazy_static",
         "liblog_rust",
+        "librpcbinder_rs",
     ],
     apex_available: [
         "com.android.compos",
diff --git a/microdroid/vm_payload/include/vm_payload.h b/microdroid/vm_payload/include/vm_payload.h
index 6e065a5..2aeb44e 100644
--- a/microdroid/vm_payload/include/vm_payload.h
+++ b/microdroid/vm_payload/include/vm_payload.h
@@ -25,6 +25,9 @@
 extern "C" {
 #endif
 
+struct AIBinder;
+typedef struct AIBinder AIBinder;
+
 /**
  * Notifies the host that the payload is ready.
  *
@@ -33,6 +36,27 @@
 bool AVmPayload_notifyPayloadReady(void);
 
 /**
+ * Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
+ * port.
+ *
+ * If and when the server is ready for connections (it is listening on the port), `on_ready` is
+ * called to allow appropriate action to be taken - e.g. to notify clients that they may now
+ * attempt to connect with `AVmPayload_notifyPayloadReady`.
+ *
+ * The current thread is joined to the binder thread pool to handle incoming messages.
+ *
+ * \param service the service to bind to the given port.
+ * \param port vsock port.
+ * \param on_ready the callback to execute once the server is ready for connections. The callback
+ *                 will be called at most once.
+ * \param param param for the `on_ready` callback.
+ *
+ * \return true if the server has shutdown normally, false if it failed in some way.
+ */
+bool AVmPayload_runVsockRpcServer(AIBinder *service, unsigned int port,
+                                  void (*on_ready)(void *param), void *param);
+
+/**
  * Get a secret that is uniquely bound to this VM instance. The secrets are 32-byte values and the
  * value associated with an identifier will not change over the lifetime of the VM instance.
  *
@@ -72,6 +96,17 @@
  */
 bool AVmPayload_getDiceAttestationCdi(void *data, size_t size, size_t *total);
 
+/**
+ * Gets the path to the APK contents. It is a directory, under which are
+ * the unzipped contents of the APK containing the payload, all read-only
+ * but accessible to the payload.
+ *
+ * \return the path to the APK contents. The returned string should not be
+ * deleted or freed by the application. The string remains valid for the
+ * lifetime of the VM.
+ */
+const char *AVmPayload_getApkContentsPath(void);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/microdroid/vm_payload/src/vm_payload_service.rs b/microdroid/vm_payload/src/vm_payload_service.rs
index 44013c9..b0dd891 100644
--- a/microdroid/vm_payload/src/vm_payload_service.rs
+++ b/microdroid/vm_payload/src/vm_payload_service.rs
@@ -15,10 +15,19 @@
 //! This module handles the interaction with virtual machine payload service.
 
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
-    IVmPayloadService, VM_PAYLOAD_SERVICE_NAME};
+    IVmPayloadService, VM_PAYLOAD_SERVICE_NAME, VM_APK_CONTENTS_PATH};
 use anyhow::{Context, Result};
-use binder::{wait_for_interface, Strong};
+use binder::{wait_for_interface, Strong, unstable_api::{AIBinder, new_spibinder}};
+use lazy_static::lazy_static;
 use log::{error, info, Level};
+use rpcbinder::run_vsock_rpc_server;
+use std::ffi::CString;
+use std::os::raw::{c_char, c_void};
+
+lazy_static! {
+    static ref VM_APK_CONTENTS_PATH_C: CString =
+        CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed");
+}
 
 /// Notifies the host that the payload is ready.
 /// Returns true if the notification succeeds else false.
@@ -42,6 +51,44 @@
     get_vm_payload_service()?.notifyPayloadReady().context("Cannot notify payload ready")
 }
 
+/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
+/// port.
+///
+/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
+/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
+/// attempt to connect.
+///
+/// The current thread is joined to the binder thread pool to handle incoming messages.
+///
+/// Returns true if the server has shutdown normally, false if it failed in some way.
+///
+/// # Safety
+///
+/// The `on_ready` callback is only called inside `run_vsock_rpc_server`, within the lifetime of
+/// `ReadyNotifier` (the last parameter of `run_vsock_rpc_server`). If `on_ready` is called with
+/// wrong param, the callback execution could go wrong.
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_runVsockRpcServer(
+    service: *mut AIBinder,
+    port: u32,
+    on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
+    param: *mut c_void,
+) -> bool {
+    // SAFETY: AIBinder returned has correct reference count, and the ownership can
+    // safely be taken by new_spibinder.
+    let service = new_spibinder(service);
+    if let Some(service) = service {
+        run_vsock_rpc_server(service, port, || {
+            if let Some(on_ready) = on_ready {
+                on_ready(param);
+            }
+        })
+    } else {
+        error!("Failed to convert the given service from AIBinder to SpIBinder.");
+        false
+    }
+}
+
 /// Get a secret that is uniquely bound to this VM instance.
 ///
 /// # Safety
@@ -145,6 +192,12 @@
     }
 }
 
+/// Gets the path to the APK contents.
+#[no_mangle]
+pub extern "C" fn AVmPayload_getApkContentsPath() -> *const c_char {
+    (*VM_APK_CONTENTS_PATH_C).as_ptr()
+}
+
 fn try_get_dice_attestation_cdi() -> Result<Vec<u8>> {
     get_vm_payload_service()?.getDiceAttestationCdi().context("Cannot get attestation CDI")
 }
diff --git a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
index 4dd3db6..4823bb8 100644
--- a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
+++ b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
@@ -24,6 +24,9 @@
     /** Name of the service IVmPayloadService. */
     const String VM_PAYLOAD_SERVICE_NAME = "virtual_machine_payload_service";
 
+    /** Path to the APK contents path. */
+    const String VM_APK_CONTENTS_PATH = "/mnt/apk";
+
     /** Notifies that the payload is ready to serve. */
     void notifyPayloadReady();
 
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 00c3dce..b8e85e7 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -33,12 +33,14 @@
     VirtualMachineCpuStatus::VirtualMachineCpuStatus,
     VirtualMachineMemStatus::VirtualMachineMemStatus,
 };
+use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::VM_APK_CONTENTS_PATH;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify, V4Signature};
 use binder::{ProcessState, Strong};
 use diced_utils::cbor::{encode_header, encode_number};
 use glob::glob;
 use itertools::sorted;
+use libc::VMADDR_CID_HOST;
 use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
@@ -62,6 +64,7 @@
 use vsock::VsockStream;
 
 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
+const SENDING_VM_STATUS_CYCLE_PERIOD: Duration = Duration::from_secs(60);
 const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
 const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
 const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
@@ -75,9 +78,6 @@
 const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
     "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
 
-/// The CID representing the host VM
-const VMADDR_CID_HOST: u32 = 2;
-
 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
 const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
 const APK_MOUNT_DONE_PROP: &str = "microdroid_manager.apk.mounted";
@@ -97,39 +97,39 @@
     InvalidConfig(String),
 }
 
-fn send_vm_status() -> Result<()> {
+fn send_vm_status(service: &Strong<dyn IVirtualMachineService>) -> Result<()> {
+    // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
+    let cpu_time = get_cpu_time()?;
+    let vm_cpu_status = VirtualMachineCpuStatus {
+        cpu_time_user: cpu_time.user,
+        cpu_time_nice: cpu_time.nice,
+        cpu_time_sys: cpu_time.sys,
+        cpu_time_idle: cpu_time.idle,
+    };
+    service.notifyCpuStatus(&vm_cpu_status).expect("Can't send information about VM CPU status");
+
+    // Collect VM memory information and creating VmMemStatus atom for metrics.
+    let mem_info = get_mem_info()?;
+    let vm_mem_status = VirtualMachineMemStatus {
+        mem_total: mem_info.total,
+        mem_free: mem_info.free,
+        mem_available: mem_info.available,
+        mem_buffer: mem_info.buffer,
+        mem_cached: mem_info.cached,
+    };
+    service.notifyMemStatus(&vm_mem_status).expect("Can't send information about VM memory status");
+
+    Ok(())
+}
+
+fn send_vm_status_periodically() -> Result<()> {
     let service = get_vms_rpc_binder()
         .context("cannot connect to VirtualMachineService")
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
 
-    let one_second = Duration::from_millis(1000);
     loop {
-        // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
-        let cpu_time = get_cpu_time()?;
-        let vm_cpu_status = VirtualMachineCpuStatus {
-            cpu_time_user: cpu_time.user,
-            cpu_time_nice: cpu_time.nice,
-            cpu_time_sys: cpu_time.sys,
-            cpu_time_idle: cpu_time.idle,
-        };
-        service
-            .notifyCpuStatus(&vm_cpu_status)
-            .expect("Can't send information about VM CPU status");
-
-        // Collect VM memory information and creating VmMemStatus atom for metrics.
-        let mem_info = get_mem_info()?;
-        let vm_mem_status = VirtualMachineMemStatus {
-            mem_total: mem_info.total,
-            mem_free: mem_info.free,
-            mem_available: mem_info.available,
-            mem_buffer: mem_info.buffer,
-            mem_cached: mem_info.cached,
-        };
-        service
-            .notifyMemStatus(&vm_mem_status)
-            .expect("Can't send information about VM memory status");
-
-        thread::sleep(one_second);
+        send_vm_status(&service)?;
+        thread::sleep(SENDING_VM_STATUS_CYCLE_PERIOD);
     }
 }
 
@@ -226,7 +226,7 @@
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
 
     thread::spawn(move || {
-        if let Err(e) = send_vm_status() {
+        if let Err(e) = send_vm_status_periodically() {
             error!("failed to get virtual machine status: {:?}", e);
         }
     });
@@ -399,7 +399,7 @@
         MountForExec::Allowed,
         "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
         Path::new("/dev/block/mapper/microdroid-apk"),
-        Path::new("/mnt/apk"),
+        Path::new(VM_APK_CONTENTS_PATH),
         Some(APK_MOUNT_DONE_PROP),
     )
     .context("Failed to run zipfuse")?;
@@ -450,6 +450,7 @@
     ProcessState::start_thread_pool();
 
     system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
+    send_vm_status(service)?;
 
     exec_task(task, service).context("Failed to run payload")
 }
@@ -782,12 +783,11 @@
     service.notifyPayloadStarted()?;
 
     let exit_status = command.spawn()?.wait()?;
+    send_vm_status(service)?;
     exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
 }
 
 fn build_command(task: &Task) -> Result<Command> {
-    const VMADDR_CID_HOST: u32 = 2;
-
     let mut command = match task.type_ {
         TaskType::Executable => Command::new(&task.command),
         TaskType::MicrodroidLauncher => {
@@ -825,7 +825,7 @@
     let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
     let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
     let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
-    let path = format!("/mnt/apk/lib/{}/{}", abi, name);
+    let path = format!("{}/lib/{}/{}", VM_APK_CONTENTS_PATH, abi, name);
 
     let metadata = fs::metadata(&path).with_context(|| format!("Unable to access {}", path))?;
     if !metadata.is_file() {
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index ebb2bcf..e8c435f 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -33,4 +33,7 @@
 
     /* get the VM's boot certificate chain (BCC). */
     byte[] getBcc();
+
+    /* get the APK contents path. */
+    String getApkContentsPath();
 }
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 88e4d41..2ba3881 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -45,7 +45,6 @@
         "com.android.microdroid.testservice-ndk",
         "libbase",
         "libbinder_ndk",
-        "libbinder_rpc_unstable",
         "liblog",
         "libvm_payload",
     ],
diff --git a/tests/benchmark/src/native/benchmarkbinary.cpp b/tests/benchmark/src/native/benchmarkbinary.cpp
index c394756..24712b1 100644
--- a/tests/benchmark/src/native/benchmarkbinary.cpp
+++ b/tests/benchmark/src/native/benchmarkbinary.cpp
@@ -29,7 +29,6 @@
 #include <vm_main.h>
 #include <vm_payload.h>
 
-#include <binder_rpc_unstable.hpp>
 #include <fstream>
 #include <random>
 #include <string>
@@ -164,9 +163,8 @@
             abort();
         }
     };
-
-    if (!RunVsockRpcServerCallback(test_service->asBinder().get(), test_service->SERVICE_PORT,
-                                   callback, nullptr)) {
+    if (!AVmPayload_runVsockRpcServer(test_service->asBinder().get(), test_service->SERVICE_PORT,
+                                      callback, nullptr)) {
         return Error() << "RPC Server failed to run";
     }
     return {};
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index c9df624..33788ed 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -485,7 +485,7 @@
     }
 
     @Test
-    public void testTelemetryPushedAtoms() throws Exception {
+    public void testTelemetryPushedAtomsOfEventMetrics() throws Exception {
         // Reset statsd config and report before the test
         ConfigUtils.removeConfig(getDevice());
         ReportUtils.clearReports(getDevice());
@@ -566,6 +566,49 @@
     }
 
     @Test
+    public void testTelemetryPushedAtomsOfValueMetrics() throws Exception {
+        // Reset statsd config and report before the test
+        ConfigUtils.removeConfig(getDevice());
+        ReportUtils.clearReports(getDevice());
+
+        // Setup statsd config
+        int[] atomIds = {
+            AtomsProto.Atom.VM_CPU_STATUS_REPORTED_FIELD_NUMBER,
+            AtomsProto.Atom.VM_MEM_STATUS_REPORTED_FIELD_NUMBER,
+        };
+        ConfigUtils.uploadConfigForPushedAtoms(getDevice(), PACKAGE_NAME, atomIds);
+
+        // Create VM with microdroid
+        final String configPath = "assets/vm_config_apex.json"; // path inside the APK
+        final String cid =
+                startMicrodroid(
+                        getDevice(),
+                        getBuild(),
+                        APK_NAME,
+                        PACKAGE_NAME,
+                        configPath,
+                        /* debug */ true,
+                        minMemorySize(),
+                        Optional.of(NUM_VCPUS));
+
+        // Boot VM with microdroid
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForBootComplete();
+
+        // Check VmCpuStatusReported and VmMemStatusReported atoms and clear the statsd report
+        List<StatsLog.EventMetricData> data;
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertThat(data.size() >= 2).isTrue();
+        assertThat(data.get(0).getAtom().getPushedCase().getNumber())
+                .isEqualTo(AtomsProto.Atom.VM_CPU_STATUS_REPORTED_FIELD_NUMBER);
+        assertThat(data.get(1).getAtom().getPushedCase().getNumber())
+                .isEqualTo(AtomsProto.Atom.VM_MEM_STATUS_REPORTED_FIELD_NUMBER);
+
+        // Shutdown VM with microdroid
+        shutdownMicrodroid(getDevice(), cid);
+    }
+
+    @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index bb17058..da2c626 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -38,7 +38,6 @@
         "com.android.microdroid.testservice-ndk",
         "libbase",
         "libbinder_ndk",
-        "libbinder_rpc_unstable",
         "MicrodroidTestNativeLibSub",
         "libvm_payload",
     ],
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 5c9cf42..c4296df 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -111,6 +111,7 @@
         assertThat(testResults.mAddInteger).isEqualTo(123 + 456);
         assertThat(testResults.mAppRunProp).isEqualTo("true");
         assertThat(testResults.mSublibRunProp).isEqualTo("true");
+        assertThat(testResults.mApkContentsPath).isEqualTo("/mnt/apk");
     }
 
     @Test
@@ -538,6 +539,7 @@
         String mAppRunProp;
         String mSublibRunProp;
         String mExtraApkTestProp;
+        String mApkContentsPath;
     }
 
     private TestResults runVmTestService(VirtualMachine vm) throws Exception {
@@ -557,6 +559,7 @@
                                     testService.readProperty("debug.microdroid.app.sublib.run");
                             testResults.mExtraApkTestProp =
                                     testService.readProperty("debug.microdroid.test.extra_apk");
+                            testResults.mApkContentsPath = testService.getApkContentsPath();
                         } catch (Exception e) {
                             testResults.mException = e;
                         }
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 5d6ca8b..1a3e940 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -30,7 +30,6 @@
 #include <vm_main.h>
 #include <vm_payload.h>
 
-#include <binder_rpc_unstable.hpp>
 #include <string>
 
 using android::base::ErrnoError;
@@ -113,6 +112,17 @@
             }
             return ndk::ScopedAStatus::ok();
         }
+
+        ndk::ScopedAStatus getApkContentsPath(std::string* out) override {
+            const char* path_c = AVmPayload_getApkContentsPath();
+            if (path_c == nullptr) {
+                return ndk::ScopedAStatus::
+                        fromServiceSpecificErrorWithMessage(0, "Failed to get APK contents path");
+            }
+            std::string path(path_c);
+            *out = path;
+            return ndk::ScopedAStatus::ok();
+        }
     };
     auto testService = ndk::SharedRefBase::make<TestService>();
 
@@ -122,8 +132,8 @@
             abort();
         }
     };
-    if (!RunVsockRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT,
-                                   callback, nullptr)) {
+    if (!AVmPayload_runVsockRpcServer(testService->asBinder().get(), testService->SERVICE_PORT,
+                                      callback, nullptr)) {
         return Error() << "RPC Server failed to run";
     }
 
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 0551229..26d41c9 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -32,6 +32,7 @@
         "libcommand_fds",
         "libdisk",
         "liblazy_static",
+        "liblibc",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 827dcb0..b4ce9d2 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -22,7 +22,6 @@
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
 use crate::selinux::{getfilecon, SeContext};
-use crate::{Cid, FIRST_GUEST_CID, SYSPROP_LAST_CID};
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
@@ -55,6 +54,7 @@
     SpIBinder, Status, StatusCode, Strong, ThreadState,
 };
 use disk::QcowFile;
+use libc::VMADDR_CID_HOST;
 use log::{debug, error, info, warn};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use rpcbinder::run_vsock_rpc_server_with_factory;
@@ -73,13 +73,19 @@
 use vsock::{VsockListener, VsockStream};
 use zip::ZipArchive;
 
+/// The unique ID of a VM used (together with a port number) for vsock communication.
+pub type Cid = u32;
+
 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
 
 /// Directory in which to write disk image files used while running VMs.
 pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
 
-/// The CID representing the host VM
-const VMADDR_CID_HOST: u32 = 2;
+/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
+/// are reserved for the host or other usage.
+const FIRST_GUEST_CID: Cid = 10;
+
+const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
 
 /// The size of zero.img.
 /// Gaps in composite disk images are filled with a shared zero.img.
@@ -358,7 +364,7 @@
         let log_fd = log_fd.map(clone_file).transpose()?;
         let requester_uid = ThreadState::get_calling_uid();
         let requester_debug_pid = ThreadState::get_calling_pid();
-        let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
+        let cid = state.next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -969,27 +975,27 @@
         let vm = self.debug_held_vms.swap_remove(pos);
         Some(vm)
     }
-}
 
-/// Get the next available CID, or an error if we have run out. The last CID used is stored in
-/// a system property so that restart of virtualizationservice doesn't reuse CID while the host
-/// Android is up.
-fn next_cid() -> Result<Cid> {
-    let next = if let Some(val) = system_properties::read(SYSPROP_LAST_CID)? {
-        if let Ok(num) = val.parse::<u32>() {
-            num.checked_add(1).ok_or_else(|| anyhow!("run out of CID"))?
+    /// Get the next available CID, or an error if we have run out. The last CID used is stored in
+    /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
+    /// Android is up.
+    fn next_cid(&mut self) -> Result<Cid> {
+        let next = if let Some(val) = system_properties::read(SYSPROP_LAST_CID)? {
+            if let Ok(num) = val.parse::<u32>() {
+                num.checked_add(1).ok_or_else(|| anyhow!("run out of CID"))?
+            } else {
+                error!("Invalid last CID {}. Using {}", &val, FIRST_GUEST_CID);
+                FIRST_GUEST_CID
+            }
         } else {
-            error!("Invalid last CID {}. Using {}", &val, FIRST_GUEST_CID);
+            // First VM since the boot
             FIRST_GUEST_CID
-        }
-    } else {
-        // First VM since the boot
-        FIRST_GUEST_CID
-    };
-    // Persist the last value for next use
-    let str_val = format!("{}", next);
-    system_properties::write(SYSPROP_LAST_CID, &str_val)?;
-    Ok(next)
+        };
+        // Persist the last value for next use
+        let str_val = format!("{}", next);
+        system_properties::write(SYSPROP_LAST_CID, &str_val)?;
+        Ok(next)
+    }
 }
 
 /// Gets the `VirtualMachineState` of the given `VmInstance`.
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index f5c894a..6f646b7 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -14,9 +14,8 @@
 
 //! Functions for running instances of `crosvm`.
 
-use crate::aidl::VirtualMachineCallbacks;
+use crate::aidl::{Cid, VirtualMachineCallbacks};
 use crate::atom::write_vm_exited_stats;
-use crate::Cid;
 use anyhow::{anyhow, bail, Context, Error};
 use command_fds::CommandFdExt;
 use lazy_static::lazy_static;
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 828d3a2..cea2747 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -29,17 +29,8 @@
 use log::{info, Level};
 use std::fs::{remove_dir_all, remove_file, read_dir};
 
-/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
-/// are reserved for the host or other usage.
-const FIRST_GUEST_CID: Cid = 10;
-
-const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
-
 const LOG_TAG: &str = "VirtualizationService";
 
-/// The unique ID of a VM used (together with a port number) for vsock communication.
-type Cid = u32;
-
 fn main() {
     android_logger::init_once(
         Config::default()
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 967314b..de8f1c0 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -55,6 +55,8 @@
     task_profiles: Vec<String>,
     extra_idsigs: &[PathBuf],
 ) -> Result<(), Error> {
+    let apk_file = File::open(apk).context("Failed to open APK file")?;
+
     let extra_apks = parse_extra_apk_list(apk, config_path)?;
     if extra_apks.len() != extra_idsigs.len() {
         bail!(
@@ -70,7 +72,6 @@
         service.createOrUpdateIdsigFile(&extra_apk_fd, &extra_idsig_fd)?;
     }
 
-    let apk_file = File::open(apk).context("Failed to open APK file")?;
     let idsig_file = File::create(idsig).context("Failed to create idsig file")?;
 
     let apk_fd = ParcelFileDescriptor::new(apk_file);
diff --git a/vmbase/common.h b/vmbase/common.h
new file mode 100644
index 0000000..788dcf0
--- /dev/null
+++ b/vmbase/common.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 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
+ *
+ *     https://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.
+ */
+
+#pragma once
+
+#define PSCI_SYSTEM_RESET (0x84000009)
+
+.macro adr_l, reg:req, sym:req
+	adrp \reg, \sym
+	add \reg, \reg, :lo12:\sym
+.endm
+
+.macro mov_i, reg:req, imm:req
+	movz \reg, :abs_g3:\imm
+	movk \reg, :abs_g2_nc:\imm
+	movk \reg, :abs_g1_nc:\imm
+	movk \reg, :abs_g0_nc:\imm
+.endm
+
+.macro reset_or_hang
+	mov_i x0, PSCI_SYSTEM_RESET
+	hvc 0
+999:	wfi
+	b 999b
+.endm
diff --git a/vmbase/entry.S b/vmbase/entry.S
index 75ab90b..5f0a2ce 100644
--- a/vmbase/entry.S
+++ b/vmbase/entry.S
@@ -14,17 +14,7 @@
  * limitations under the License.
  */
 
-.macro adr_l, reg:req, sym:req
-	adrp \reg, \sym
-	add \reg, \reg, :lo12:\sym
-.endm
-
-.macro mov_i, reg:req, imm:req
-	movz \reg, :abs_g3:\imm
-	movk \reg, :abs_g2_nc:\imm
-	movk \reg, :abs_g1_nc:\imm
-	movk \reg, :abs_g0_nc:\imm
-.endm
+#include <common.h>
 
 .set .L_MAIR_DEV_nGnRE,	0x04
 .set .L_MAIR_MEM_WBWA,	0xff
@@ -95,6 +85,16 @@
 	adr x30, vector_table_panic
 	msr vbar_el1, x30
 
+	/*
+	 * Our load address is set by the host so validate it before proceeding.
+	 */
+	adr x30, entry
+	mov_i x29, entry
+	cmp x29, x30
+	b.eq 1f
+	reset_or_hang
+1:
+
 	adrp x30, idmap
 	msr ttbr0_el1, x30
 
diff --git a/vmbase/exceptions_panic.S b/vmbase/exceptions_panic.S
index 4a3f2db..54735b2 100644
--- a/vmbase/exceptions_panic.S
+++ b/vmbase/exceptions_panic.S
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <common.h>
+
 /**
  * The following table is intended to trap any fault resulting from the very
  * first memory accesses. They assume that PSCI v0.2 is available and provides
@@ -21,80 +23,69 @@
  * results in the core busy-looping.
  */
 
-.macro exception_panic
-	mov	x0, 0x84000000
-	movk	x0, 9
-	mov	x1, 0
-	mov	x2, 0
-	mov	x3, 0
-	hvc	0
-0:	wfi
-	b	0b
-.endm
-
 .section .text.vector_table_panic, "ax"
 .global vector_table_panic
 .balign 0x800
 vector_table_panic:
 sync_cur_sp0_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 irq_cur_sp0_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 fiq_cur_sp0_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 serr_cur_sp0_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 sync_cur_spx_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 irq_cur_spx_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 fiq_cur_spx_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 serr_cur_spx_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 sync_lower_64_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 irq_lower_64_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 fiq_lower_64_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 serr_lower_64_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 sync_lower_32_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 irq_lower_32_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 fiq_lower_32_panic:
-	exception_panic
+	reset_or_hang
 
 .balign 0x80
 serr_lower_32_panic:
-	exception_panic
+	reset_or_hang