Merge "Don't allow vendor APEXes in Microdroid" into main
diff --git a/apex/Android.bp b/apex/Android.bp
index fedcfcd..7ef2c79 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -106,6 +106,7 @@
     ],
     host_required: [
         "vm_shell",
+        "prepare_device_vfio",
     ],
     apps: [
         "EmptyPayloadApp",
@@ -136,6 +137,12 @@
     installable: false,
 }
 
+sh_binary_host {
+    name: "prepare_device_vfio",
+    src: "prepare_device_vfio.sh",
+    filename: "prepare_device_vfio.sh",
+}
+
 // Virt apex needs a custom signer for its payload
 python_binary_host {
     name: "sign_virt_apex",
diff --git a/apex/prepare_device_vfio.sh b/apex/prepare_device_vfio.sh
new file mode 100755
index 0000000..de2d502
--- /dev/null
+++ b/apex/prepare_device_vfio.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# prepare_device_vfio.sh: prepares a device for VFIO assignment by binding a VFIO driver to it
+
+adb="${ADB:="adb"}" # ADB command to use
+vfio_dir="/dev/vfio"
+platform_bus="/sys/bus/platform"
+vfio_reset_required="/sys/module/vfio_platform/parameters/reset_required"
+vfio_noiommu_param="/sys/module/vfio/parameters/enable_unsafe_noiommu_mode"
+vfio_unsafe_interrupts_param="/sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts"
+
+function print_help() {
+    echo "prepare_device_vfio.sh prepares a device for VFIO assignment"
+    echo ""
+    echo " Usage:"
+    echo "    $0 DEVICE_NAME"
+    echo "      Prepare device DEVICE_NAME for VFIO assignment."
+    echo ""
+    echo "    help - prints this help message"
+}
+
+function cmd() {
+    $adb shell $@
+}
+
+function tcmd() {
+    trap "echo \"Error: adb shell command '$@' failed\" ; exit 1" ERR
+    $adb shell $@
+}
+
+function ensure_root() {
+    # Check user id
+    if [ $(cmd "id -u") != 0 ]; then
+        read -p "Must run as root; restart ADBD? [y/n] " answer
+        case $answer in
+            [Yy]* )
+                $adb root && $adb wait-for-device && sleep 3 || exit 1
+                ;;
+            * )
+                exit 1
+        esac
+    fi
+}
+
+function check_vfio() {
+    cmd "[ -c $vfio_dir/vfio ]"
+    if [ $? -ne 0 ]; then
+        echo "cannot find $vfio_dir/vfio"
+        exit 1
+    fi
+
+    cmd "[ -d $platform_bus/drivers/vfio-platform ]"
+    if [ $? -ne 0 ]; then
+        echo "VFIO-platform is not supported"
+        exit 1
+    fi
+}
+
+function check_device() {
+    cmd "[ -d $device_sys ]"
+    if [ $? -ne 0 ]; then
+        echo "no device $device ($device_sys)"
+        exit 1
+    fi
+}
+
+function get_device_iommu_group() {
+    local group=$(cmd "basename \$(readlink \"$device_sys/iommu_group\")")
+    if [ $? -eq 0 ]; then
+        echo $group
+    else
+        echo ""
+    fi
+}
+
+function misc_setup() {
+    # VFIO NOIOMMU check
+    if [ -z "$group" ]; then
+        echo "$device_sys does not have an IOMMU group - setting $vfio_noiommu_param"
+        tcmd "echo y > \"$vfio_noiommu_param\""
+    fi
+
+    # Disable SELinux to allow virtualizationmanager and crosvm to access sysfs
+    echo "[*WARN*] setenforce=0: SELinux is disabled"
+    tcmd "setenforce 0"
+
+    # Samsung IOMMU does not report interrupt remapping support, so enable unsafe uinterrupts
+    if [ -n "$group" ]; then
+        local iommu_drv=$(cmd "basename \$(readlink \"$device_sys/iommu/device/driver\")")
+        if [ "$iommu_drv" = "samsung-sysmmu-v9" ]; then
+            tcmd "echo y > \"$vfio_unsafe_interrupts_param\""
+        fi
+    fi
+}
+
+function bind_vfio_driver() {
+    # Check if non-VFIO driver is currently bound, ie unbinding is needed
+    cmd "[ -e \"$device_driver\" ] && \
+        [ ! \$(basename \$(readlink \"$device_driver\")) = \"vfio-platform\" ]"
+            if [ $? -eq 0 ]; then
+                # Unbind current driver
+                tcmd "echo \"$device\" > \"$device_driver/unbind\""
+            fi
+
+    # Bind to VFIO driver
+    cmd "[ ! -e \"$device_driver\" ]"
+    if [ $? -eq 0 ]; then
+        # Bind vfio-platform driver
+        tcmd "echo \"vfio-platform\" > \"$device_sys/driver_override\""
+        tcmd "echo \"$device\" > \"$platform_bus/drivers_probe\""
+        sleep 2
+    fi
+}
+
+function verify_vfio_driver() {
+    # Verify new VFIO file structure
+    group=$(get_device_iommu_group)
+    if [ -z "$group" ]; then
+        echo "cannot setup VFIO-NOIOMMU for $device_sys"
+        exit 1
+    fi
+
+    cmd "[ ! -c \"$vfio_dir/$group\" ] || \
+        [ ! -e \"$device_driver\" ] || \
+        [ ! \$(basename \$(readlink \"$device_driver\")) = \"vfio-platform\" ]"
+    if [ $? -eq 0 ]; then
+        echo "could not bind $device to VFIO platform driver"
+
+        if [ $(cmd "cat $vfio_reset_required") = Y ]; then
+            echo "VFIO device reset handler must be registered. Either unset $vfio_reset_required, \
+or register a reset handler for $device_sys"
+        fi
+        exit 1
+    fi
+}
+
+function prepare_device() {
+    device="$1"
+    device_sys="/sys/bus/platform/devices/$device"
+    device_driver="$device_sys/driver"
+
+    ensure_root
+    check_vfio
+    check_device
+    group=$(get_device_iommu_group)
+    misc_setup
+
+    bind_vfio_driver
+    verify_vfio_driver
+
+    echo "Device: $device_sys"
+    echo "IOMMU group: $group"
+    echo "VFIO group file: $vfio_dir/$group"
+    echo "Ready!"
+}
+
+cmd=$1
+
+case $cmd in
+    ""|help) print_help ;;
+    *) prepare_device "$cmd" $@ ;;
+esac
diff --git a/authfs/tests/benchmarks/Android.bp b/authfs/tests/benchmarks/Android.bp
index 110d000..cea5a81 100644
--- a/authfs/tests/benchmarks/Android.bp
+++ b/authfs/tests/benchmarks/Android.bp
@@ -17,12 +17,10 @@
     test_suites: ["general-tests"],
     data_device_bins_first: [
         "open_then_run",
-        "fsverity",
     ],
     per_testcase_directory: true,
     data: [
         ":authfs_test_files",
-        ":CtsApkVerityTestPrebuiltFiles",
         ":MicrodroidTestApp",
     ],
     required: ["MicrodroidTestPreparer"],
diff --git a/authfs/tests/benchmarks/AndroidTest.xml b/authfs/tests/benchmarks/AndroidTest.xml
index 9216006..715f352 100644
--- a/authfs/tests/benchmarks/AndroidTest.xml
+++ b/authfs/tests/benchmarks/AndroidTest.xml
@@ -34,32 +34,16 @@
 
         <!-- Test executable -->
         <option name="push-file" key="open_then_run" value="/data/local/tmp/open_then_run" />
-        <option name="push-file" key="fsverity" value="/data/local/tmp/fsverity" />
 
         <!-- Test data files -->
         <option name="push-file" key="cert.der" value="/data/local/tmp/authfs/cert.der" />
         <option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.4m" />
         <option name="push-file" key="input.4m.fsv_meta"
             value="/data/local/tmp/authfs/input.4m.fsv_meta" />
-
-        <!-- Just pick a file with signature that can be trused on the device. -->
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk"
-            value="/data/local/tmp/authfs/input.apk" />
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk.fsv_sig"
-            value="/data/local/tmp/authfs/input.apk.fsv_sig" />
     </target_preparer>
 
     <target_preparer class="com.android.microdroid.test.preparer.DisableMicrodroidDebugPolicyPreparer" />
 
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="throw-if-cmd-fail" value="true" />
-        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file.
-             It works because the signature is trusted on all CTS compatible devices. -->
-        <option name="run-command"
-            value="cd /data/local/tmp/authfs;
-                   ../fsverity enable input.apk --signature=input.apk.fsv_sig" />
-    </target_preparer>
-
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="AuthFsBenchmarks.jar" />
     </test>
diff --git a/authfs/tests/hosttests/Android.bp b/authfs/tests/hosttests/Android.bp
index 4b8151d..83ef853 100644
--- a/authfs/tests/hosttests/Android.bp
+++ b/authfs/tests/hosttests/Android.bp
@@ -20,7 +20,6 @@
     per_testcase_directory: true,
     data: [
         ":authfs_test_files",
-        ":CtsApkVerityTestPrebuiltFiles",
         ":MicrodroidTestApp",
     ],
 }
diff --git a/authfs/tests/hosttests/AndroidTest.xml b/authfs/tests/hosttests/AndroidTest.xml
index 2ccc45f..5920630 100644
--- a/authfs/tests/hosttests/AndroidTest.xml
+++ b/authfs/tests/hosttests/AndroidTest.xml
@@ -50,18 +50,13 @@
         <option name="push-file" key="input.4m.fsv_meta.bad_merkle"
             value="/data/local/tmp/authfs/input.4m.fsv_meta.bad_merkle" />
 
-        <!-- Just pick a file with signature that can be trused on the device. -->
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk"
-            value="/data/local/tmp/authfs/input.apk" />
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk.fsv_sig"
-            value="/data/local/tmp/authfs/input.apk.fsv_sig" />
+        <option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.file" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="throw-if-cmd-fail" value="true" />
-        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file.
-             It works because the signature is trusted on all CTS compatible devices. -->
-        <option name="run-command" value="cd /data/local/tmp/authfs; ../fsverity enable input.apk --signature=input.apk.fsv_sig" />
+        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file. -->
+        <option name="run-command" value="cd /data/local/tmp/authfs; ../fsverity enable input.file" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
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 440f5ca..d0a7c66 100644
--- a/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
@@ -145,17 +145,17 @@
 
     @Test
     public void testReadWithFsverityVerification_FdServerUsesRealFsverityData() throws Exception {
-        // Setup (fs-verity is enabled for input.apk in AndroidTest.xml)
-        runFdServerOnAndroid("--open-ro 3:input.apk", "--ro-fds 3");
-        String expectedDigest = sAndroid.run(
-                FSVERITY_BIN + " digest --compact " + TEST_DIR + "/input.apk");
+        // Setup (fs-verity is enabled for input.file in AndroidTest.xml)
+        runFdServerOnAndroid("--open-ro 3:input.file", "--ro-fds 3");
+        String expectedDigest =
+                sAndroid.run(FSVERITY_BIN + " digest --compact " + TEST_DIR + "/input.file");
         runAuthFsOnMicrodroid("--remote-ro-file 3:sha256-" + expectedDigest);
 
         // Action
         String actualHash = computeFileHash(sMicrodroid, MOUNT_DIR + "/3");
 
         // Verify
-        String expectedHash = computeFileHash(sAndroid, TEST_DIR + "/input.apk");
+        String expectedHash = computeFileHash(sAndroid, TEST_DIR + "/input.file");
         assertEquals("Inconsistent hash from /authfs/3: ", expectedHash, actualHash);
     }
 
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index 23f8ca6..2ea748b 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
@@ -28,10 +28,12 @@
 
     private static final String KEY_VENDOR_DEVICE = "ro.product.vendor.device";
     private static final String KEY_BUILD_TYPE = "ro.build.type";
+    private static final String KEY_PRODUCT_NAME = "ro.product.name";
     private static final String KEY_METRICS_TAG = "debug.hypervisor.metrics_tag";
 
     private static final String CUTTLEFISH_DEVICE_PREFIX = "vsoc_";
     private static final String USER_BUILD_TYPE = "user";
+    private static final String HWASAN_SUFFIX = "_hwasan";
 
     private final PropertyGetter mPropertyGetter;
 
@@ -53,6 +55,14 @@
     }
 
     /**
+     * @return whether the build is HWASAN.
+     */
+    public boolean isHwasan() {
+        String productName = getProperty(KEY_PRODUCT_NAME);
+        return productName != null && productName.contains(HWASAN_SUFFIX);
+    }
+
+    /**
      * @return whether the device is user build.
      */
     public boolean isUserBuild() {
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 81ccec7..98327a9 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -89,6 +89,10 @@
         return DeviceProperties.create(getDevice()::getProperty).isCuttlefish();
     }
 
+    protected boolean isHwasan() {
+        return DeviceProperties.create(getDevice()::getProperty).isHwasan();
+    }
+
     protected String getMetricPrefix() {
         return MetricsProcessor.getMetricPrefix(
                 DeviceProperties.create(getDevice()::getProperty).getMetricsTag());
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 092325e..82d8571 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -523,12 +523,16 @@
     @Test
     public void testTombstonesAreGeneratedUponUserspaceCrashOnNonPvm() throws Exception {
         assumeNonProtectedVmSupported();
+        // 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);
     }
 
@@ -548,6 +552,8 @@
     public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnNonPvm()
             throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(false);
     }
 
@@ -555,6 +561,8 @@
     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);
     }
 
@@ -636,6 +644,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashPayloadOnPvm() 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=*/ true))
@@ -645,6 +655,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashPayloadOnNonPvm() 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=*/ true))
@@ -655,6 +667,8 @@
     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))
@@ -665,6 +679,8 @@
     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))
@@ -680,6 +696,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashConfigOnPvm() 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=*/ true))
                 .isTrue();
     }
@@ -687,6 +705,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashConfigOnNonPvm() 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=*/ true))
@@ -697,6 +717,8 @@
     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))
@@ -707,6 +729,8 @@
     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))