Merge changes from topic "vmnic_delete_tap" into main

* changes:
  Implement deleting TAP interface in vmnic
  Expose network support configuration into AVF Java Framework API
diff --git a/apex/Android.bp b/apex/Android.bp
index 43819dc..17b1f9e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -157,6 +157,7 @@
         "microdroid.json",
         "microdroid_kernel",
         "com.android.virt.init.rc",
+        "android_bootloader_crosvm_aarch64",
     ],
     host_required: [
         "vm_shell",
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
index bd011fa..b31f4f3 100644
--- a/compos/tests/java/android/compos/test/ComposTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -24,11 +24,13 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.RootPermissionTest;
 
 import com.android.microdroid.test.host.CommandRunner;
 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
+import com.android.tradefed.device.TestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.LogDataType;
@@ -85,6 +87,8 @@
         assumeDeviceIsCapable(getDevice());
         // Test takes too long to run on Cuttlefish (b/292824951).
         assumeFalse("Skipping test on Cuttlefish", isCuttlefish());
+        // CompOS requires a protected VM
+        assumeTrue(((TestDevice) getDevice()).supportsMicrodroid(/*protectedVm*/ true));
 
         String value = getDevice().getProperty(SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME);
         if (value == null) {
diff --git a/docs/custom_vm.md b/docs/custom_vm.md
index 5511758..d52aa95 100644
--- a/docs/custom_vm.md
+++ b/docs/custom_vm.md
@@ -194,7 +194,8 @@
     "protected": false,
     "cpu_topology": "match_host",
     "platform_version": "~1.0",
-    "memory_mib" : 8096
+    "memory_mib" : 8096,
+    "console_input_device": "ttyS0"
 }
 ```
 
@@ -296,4 +297,16 @@
 * DNS: 8.8.8.8 (or any DNS server you know)
 
 These settings are persistent; stored in chromiumos_test_image.bin. So you
-don’t have to repeat this next time.`
+don’t have to repeat this next time.
+
+### Debugging
+
+To see console log, check
+`/data/data/com.android.virtualization.vmlauncher/files/console.log`
+
+For ChromiumOS, you can ssh-in. Use following commands after network setup.
+
+```shell
+$ adb kill-server ; adb start-server; adb forward tcp:9222 tcp:9222
+$ ssh -oProxyCommand=none -o UserKnownHostsFile=/dev/null root@localhost -p 9222
+```
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
index 428d3e3..ef6a2fc 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -78,6 +78,7 @@
     private static final String TAG = "VirtualMachineConfig";
 
     private static String[] EMPTY_STRING_ARRAY = {};
+    private static final String U_BOOT_PREBUILT_PATH = "/apex/com.android.virt/etc/u-boot.bin";
 
     // These define the schema of the config file persisted on disk.
     // Please bump up the version number when adding a new key.
@@ -674,6 +675,11 @@
                 Optional.ofNullable(customImageConfig.getBootloaderPath())
                         .map((path) -> openOrNull(new File(path), MODE_READ_ONLY))
                         .orElse(null);
+
+        if (config.kernel == null && config.bootloader == null) {
+            config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH), MODE_READ_ONLY);
+        }
+
         config.params =
                 Optional.ofNullable(customImageConfig.getParams())
                         .map((params) -> TextUtils.join(" ", params))
diff --git a/tests/ferrochrome/Android.bp b/tests/ferrochrome/Android.bp
new file mode 100644
index 0000000..889f41e
--- /dev/null
+++ b/tests/ferrochrome/Android.bp
@@ -0,0 +1,13 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+sh_test_host {
+    name: "ferrochrome-tests",
+    src: "ferrochrome.sh",
+    test_options: {
+        unit_test: false,
+    },
+    per_testcase_directory: true,
+    data: ["assets/vm_config.json"],
+}
diff --git a/tests/ferrochrome/AndroidTest.xml b/tests/ferrochrome/AndroidTest.xml
new file mode 100644
index 0000000..3f902ea
--- /dev/null
+++ b/tests/ferrochrome/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 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
+
+          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.
+-->
+<configuration description="Host driven tests for ferrochrome">
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <!-- 'adb root' to enable vmlauncher -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="force-root" value="true"/>
+    </target_preparer>
+
+    <!-- Check presence of vmlauncher here, because we can't skip tests in shell test -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="throw-if-cmd-fail" value="true" />
+        <option name="run-command" value="pm list packages | grep vmlauncher" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="ferrochrome-tests" />
+        <option name="runtime-hint" value="10m" />
+        <option name="per-binary-timeout" value="20m" />
+    </test>
+</configuration>
+
diff --git a/tests/ferrochrome/assets/vm_config.json b/tests/ferrochrome/assets/vm_config.json
new file mode 100644
index 0000000..f8a3099
--- /dev/null
+++ b/tests/ferrochrome/assets/vm_config.json
@@ -0,0 +1,18 @@
+{
+    "name": "cros",
+    "kernel": "/data/local/tmp/ferrochrome/vmlinuz",
+    "disks": [
+        {
+            "image": "/data/local/tmp/ferrochrome/chromiumos_test_image.bin",
+            "partitions": [],
+            "writable": true
+        }
+    ],
+    "params": "root=/dev/vda3 rootwait noinitrd ro enforcing=0 cros_debug cros_secure",
+    "protected": false,
+    "cpu_topology": "match_host",
+    "platform_version": "~1.0",
+    "memory_mib" : 8096,
+    "console_input_device": "ttyS0"
+}
+
diff --git a/tests/ferrochrome/ferrochrome.sh b/tests/ferrochrome/ferrochrome.sh
new file mode 100755
index 0000000..4dde401
--- /dev/null
+++ b/tests/ferrochrome/ferrochrome.sh
@@ -0,0 +1,142 @@
+#!/bin/bash
+
+# Copyright 2024 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.
+
+## Booting tests for ferrochrome
+## Keep this file synced with docs/custom_vm.md
+
+set -e
+
+FECR_GS_URL="https://storage.googleapis.com/chromiumos-image-archive/ferrochrome-public"
+FECR_DEFAULT_VERSION="R127-15916.0.0"
+FECR_DEVICE_DIR="/data/local/tmp/ferrochrome"
+FECR_CONFIG_PATH="/data/local/tmp/vm_config.json"  # hardcoded at VmLauncherApp
+FECR_CONSOLE_LOG_PATH="/data/data/\${pkg_name}/files/console.log"
+FECR_BOOT_COMPLETED_LOG="Have fun and send patches!"
+FECR_BOOT_TIMEOUT="300" # 5 minutes (300 seconds)
+AOSP_PKG_NAME="com.android.virtualization.vmlauncher"
+SIGNED_PKG_NAME="com.google.android.virtualization.vmlauncher"
+
+fecr_clean_up() {
+  trap - INT
+
+  if [[ -d ${fecr_dir} && -z ${fecr_keep} ]]; then
+    rm -rf ${fecr_dir}
+  fi
+}
+
+print_usage() {
+  echo "ferochrome.sh: Launches ferrochrome image"
+  echo ""
+  echo "By default, this downloads ferrochrome image with version ${FECR_DEFAULT_VERSION},"
+  echo "launches, and waits for boot completed."
+  echo "When done, removes downloaded image."
+  echo ""
+  echo "Usage: ferrochrome.sh [options]"
+  echo ""
+  echo "Options"
+  echo "  --help or -h: This message"
+  echo "  --dir \${dir}: Use ferrochrome images at the dir instead of downloading"
+  echo "  --skip: Skipping downloading and/or pushing images"
+  echo "  --version \${version}: ferrochrome version to be downloaded"
+  echo "  --keep: Keep downloaded ferrochrome image"
+}
+
+
+fecr_version=""
+fecr_dir=""
+fecr_keep=""
+fecr_skip=""
+fecr_script_path=$(dirname ${0})
+
+# Parse parameters
+while (( "${#}" )); do
+  case "${1}" in
+    --version)
+      shift
+      fecr_version="${1}"
+      ;;
+    --dir)
+      shift
+      fecr_dir="${1}"
+      fecr_keep="true"
+      ;;
+    --keep)
+      fecr_keep="true"
+      ;;
+    --skip)
+      fecr_skip="true"
+      ;;
+    -h|--help)
+      print_usage
+      exit 0
+      ;;
+    *)
+      print_usage
+      exit 1
+      ;;
+  esac
+  shift
+done
+
+trap fecr_clean_up INT
+trap fecr_clean_up EXIT
+
+if [[ -z "${fecr_skip}" ]]; then
+  if [[ -z "${fecr_dir}" ]]; then
+    # Download fecr image archive, and extract necessary files
+    # DISCLAIMER: Image is too large (1.5G+ for compressed, 6.5G+ for uncompressed), so can't submit.
+    fecr_dir=$(mktemp -d)
+
+    echo "Downloading ferrochrome image to ${fecr_dir}"
+    fecr_version=${fecr_version:-${FECR_DEFAULT_VERSION}}
+    curl --output-dir ${fecr_dir} -O ${FECR_GS_URL}/${fecr_version}/image.zip
+  fi
+  if [[ ! -f "${fecr_dir}/chromiumos_test_image.bin" ]]; then
+    unzip ${fecr_dir}/image.zip chromiumos_test_image.bin boot_images/vmlinuz* -d ${fecr_dir} > /dev/null
+  fi
+
+  echo "Pushing ferrochrome image to ${FECR_DEVICE_DIR}"
+  adb shell mkdir -p ${FECR_DEVICE_DIR} > /dev/null || true
+  adb push ${fecr_dir}/chromiumos_test_image.bin ${FECR_DEVICE_DIR}
+  adb push ${fecr_dir}/boot_images/vmlinuz ${FECR_DEVICE_DIR}
+  adb push ${fecr_script_path}/assets/vm_config.json ${FECR_CONFIG_PATH}
+fi
+
+adb root > /dev/null
+adb shell pm list packages | grep ${AOSP_PKG_NAME} > /dev/null
+if [[ "${?}" == "0" ]]; then
+  pkg_name=${AOSP_PKG_NAME}
+else
+  pkg_name=${SIGNED_PKG_NAME}
+fi
+
+adb shell pm enable ${pkg_name}/${AOSP_PKG_NAME}.MainActivity > /dev/null
+adb shell pm grant ${pkg_name} android.permission.USE_CUSTOM_VIRTUAL_MACHINE > /dev/null
+adb shell pm clear ${pkg_name} > /dev/null
+
+echo "Starting ferrochrome"
+adb shell am start-activity ${pkg_name}/${AOSP_PKG_NAME}.MainActivity > /dev/null
+
+log_path="/data/data/${pkg_name}/files/console.log"
+fecr_start_time=${EPOCHSECONDS}
+
+while [[ $((EPOCHSECONDS - fecr_start_time)) -lt ${FECR_BOOT_TIMEOUT} ]]; do
+  adb shell grep -sF \""${FECR_BOOT_COMPLETED_LOG}"\" "${log_path}" && exit 0
+  sleep 10
+done
+
+echo "Ferrochrome failed to boot"
+exit 1
diff --git a/vmbase/src/uart.rs b/vmbase/src/uart.rs
index 09d747f..e35555d 100644
--- a/vmbase/src/uart.rs
+++ b/vmbase/src/uart.rs
@@ -16,7 +16,6 @@
 //! provided by crosvm, and won't work with real hardware.
 
 use core::fmt::{self, Write};
-use core::ptr::write_volatile;
 
 /// Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
 /// provided by crosvm, and won't work with real hardware.
@@ -41,7 +40,11 @@
         // SAFETY: We know that the base address points to the control registers of a UART device
         // which is appropriately mapped.
         unsafe {
-            write_volatile(self.base_address, byte);
+            core::arch::asm!(
+                "strb {value:w}, [{ptr}]",
+                value = in(reg) byte,
+                ptr = in(reg) self.base_address,
+            );
         }
     }
 }