Merge "Revert "Disabled audio on arm64 host""
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index f5c12c6..8b21f0c 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -319,6 +319,15 @@
   }
 }
 
+SharedFD SharedFD::Mkstemp(std::string* path) {
+  int fd = mkstemp(path->data());
+  if (fd == -1) {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
+  } else {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
+  }
+}
+
 SharedFD SharedFD::ErrorFD(int error) {
   return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(-1, error)));
 }
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index ad962de..e712ab9 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -129,6 +129,7 @@
   static bool Pipe(SharedFD* fd0, SharedFD* fd1);
   static SharedFD Event(int initval = 0, int flags = 0);
   static SharedFD MemfdCreate(const std::string& name, unsigned int flags = 0);
+  static SharedFD Mkstemp(std::string* path);
   static bool SocketPair(int domain, int type, int protocol, SharedFD* fd0,
                          SharedFD* fd1);
   static SharedFD Socket(int domain, int socket_type, int protocol);
diff --git a/common/libs/utils/environment.cpp b/common/libs/utils/environment.cpp
index 5c0de51..ec27290 100644
--- a/common/libs/utils/environment.cpp
+++ b/common/libs/utils/environment.cpp
@@ -21,6 +21,8 @@
 #include <stdio.h>
 #include <iostream>
 
+#include <android-base/logging.h>
+
 namespace cuttlefish {
 
 std::string StringFromEnv(const std::string& varname,
@@ -39,7 +41,7 @@
  *
  * @return arch string on success, "" on failure
  */
-std::string HostArch() {
+std::string HostArchStr() {
   static std::string arch;
   static bool cached = false;
 
@@ -83,6 +85,29 @@
   return arch;
 }
 
+Arch HostArch() {
+  std::string arch_str = HostArchStr();
+  if (arch_str == "aarch64") {
+    return Arch::Arm64;
+  } else if (arch_str == "arm") {
+    return Arch::Arm;
+  } else if (arch_str == "x86_64") {
+    return Arch::X86_64;
+  } else if (arch_str.size() == 4 && arch_str[0] == 'i' && arch_str[2] == '8' &&
+             arch_str[3] == '6') {
+    return Arch::X86;
+  } else {
+    LOG(FATAL) << "Unknown host architecture: " << arch_str;
+    return Arch::X86;
+  }
+}
+
+bool IsHostCompatible(Arch arch) {
+  Arch host_arch = HostArch();
+  return arch == host_arch || (arch == Arch::Arm && host_arch == Arch::Arm64) ||
+         (arch == Arch::X86 && host_arch == Arch::X86_64);
+}
+
 static bool IsRunningInDocker() {
   // if /.dockerenv exists, it's inside a docker container
   static std::string docker_env_path("/.dockerenv");
diff --git a/common/libs/utils/environment.h b/common/libs/utils/environment.h
index 60bcbb6..004a849 100644
--- a/common/libs/utils/environment.h
+++ b/common/libs/utils/environment.h
@@ -19,10 +19,19 @@
 
 namespace cuttlefish {
 
+enum class Arch {
+  Arm,
+  Arm64,
+  X86,
+  X86_64,
+};
+
 std::string StringFromEnv(const std::string& varname,
                           const std::string& defval);
 
-std::string HostArch();
+std::string HostArchStr();
+Arch HostArch();
+bool IsHostCompatible(Arch arch);
 
 bool IsRunningInContainer();
 
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index df91c23..d1f5f59 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -96,7 +96,7 @@
     return tap_fd;
   }
 
-  if (HostArch() == "aarch64") {
+  if (HostArch() == Arch::Arm64) {
     auto tapsetiff_path = DefaultHostArtifactsPath("bin/tapsetiff");
     Command cmd(tapsetiff_path);
     cmd.AddParameter(tap_fd);
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index fec1ecf..63a6b4f 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -102,7 +102,7 @@
 #endif
 
 const CuttlefishConfig* InitFilesystemAndCreateConfig(
-    FetcherConfig fetcher_config) {
+    FetcherConfig fetcher_config, KernelConfig kernel_config) {
   std::string assembly_dir_parent = AbsolutePath(FLAGS_assembly_dir);
   while (assembly_dir_parent[assembly_dir_parent.size() - 1] == '/') {
     assembly_dir_parent =
@@ -131,7 +131,7 @@
     // two operations, as those will assume they can read the config object from
     // disk.
     auto config = InitializeCuttlefishConfiguration(
-        FLAGS_assembly_dir, FLAGS_instance_dir, FLAGS_modem_simulator_count);
+        FLAGS_instance_dir, FLAGS_modem_simulator_count, kernel_config);
     std::set<std::string> preserving;
     if (FLAGS_resume && ShouldCreateAllCompositeDisks(config)) {
       LOG(INFO) << "Requested resuming a previous session (the default behavior) "
@@ -227,9 +227,10 @@
   }
   std::vector<std::string> input_files = android::base::Split(input_files_str, "\n");
 
-  CHECK(ParseCommandLineFlags(&argc, &argv)) << "Failed to parse arguments";
+  KernelConfig kernel_config;
+  CHECK(ParseCommandLineFlags(&argc, &argv, &kernel_config)) << "Failed to parse arguments";
 
-  auto config = InitFilesystemAndCreateConfig(FindFetcherConfig(input_files));
+  auto config = InitFilesystemAndCreateConfig(FindFetcherConfig(input_files), kernel_config);
 
   std::cout << GetConfigFilePath(*config) << "\n";
   std::cout << std::flush;
diff --git a/host/commands/assemble_cvd/boot_config.cc b/host/commands/assemble_cvd/boot_config.cc
index a6d41dd..b433940 100644
--- a/host/commands/assemble_cvd/boot_config.cc
+++ b/host/commands/assemble_cvd/boot_config.cc
@@ -50,9 +50,6 @@
   if (!config.boot_slot().empty()) {
       env << "android_slot_suffix=_" << config.boot_slot() << '\0';
   }
-  // Points to the misc partition.
-  // Note that the 0 index points to the GPT table.
-  env << "bootdevice=0:2" << '\0';
 
   if(FLAGS_pause_in_bootloader) {
     env << "bootdelay=-1" << '\0';
@@ -60,9 +57,10 @@
     env << "bootdelay=0" << '\0';
   }
 
-  env << "bootcmd=boot_android virtio -" << '\0';
+  // Note that the 0 index points to the GPT table.
+  env << "bootcmd=boot_android virtio 0#misc" << '\0';
   if (FLAGS_vm_manager == CrosvmManager::name() &&
-          HostArch() == "aarch64") {
+      config.target_arch() == Arch::Arm64) {
     env << "fdtaddr=0x80000000" << '\0';
   } else {
     env << "fdtaddr=0x40000000" << '\0';
diff --git a/host/commands/assemble_cvd/boot_image_utils.cc b/host/commands/assemble_cvd/boot_image_utils.cc
index 5654f93..a222f85 100644
--- a/host/commands/assemble_cvd/boot_image_utils.cc
+++ b/host/commands/assemble_cvd/boot_image_utils.cc
@@ -179,14 +179,6 @@
 
 }  // namespace
 
-std::string ExtractKernelFromBootImage(const std::string& boot_image_path,
-                                       const std::string& unpack_dir) {
-  if (UnpackBootImage(boot_image_path, unpack_dir)) {
-    return unpack_dir + "/kernel";
-  } else {
-    return "";
-  }
-}
 bool RepackBootImage(const std::string& new_kernel_path,
                      const std::string& boot_image_path,
                      const std::string& new_boot_image_path,
diff --git a/host/commands/assemble_cvd/boot_image_utils.h b/host/commands/assemble_cvd/boot_image_utils.h
index ae28761..fafb514 100644
--- a/host/commands/assemble_cvd/boot_image_utils.h
+++ b/host/commands/assemble_cvd/boot_image_utils.h
@@ -19,8 +19,6 @@
 #include <vector>
 
 namespace cuttlefish {
-std::string ExtractKernelFromBootImage(const std::string& boot_image_path,
-                                       const std::string& unpack_dir);
 bool RepackBootImage(const std::string& new_kernel_path,
                      const std::string& boot_image_path,
                      const std::string& new_boot_image_path,
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index fd9a1dd..774e5ba 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -330,32 +330,6 @@
   return true;
 }
 
-static bool IsBootconfigSupported(const std::string& tmp_dir) {
-  const std::string kernel_image_path =
-      FLAGS_kernel_path.size()
-          ? FLAGS_kernel_path
-          : ExtractKernelFromBootImage(FLAGS_boot_image, tmp_dir);
-  const std::string ikconfig_path = tmp_dir + "/ikconfig";
-
-  Command ikconfig_cmd(HostBinaryPath("extract-ikconfig"));
-  ikconfig_cmd.AddParameter(kernel_image_path);
-
-  std::string current_path = StringFromEnv("PATH", "");
-  std::string bin_folder = DefaultHostArtifactsPath("bin");
-  ikconfig_cmd.SetEnvironment({"PATH=" + current_path + ":" + bin_folder});
-
-  auto ikconfig_fd = SharedFD::Creat(ikconfig_path, 0666);
-  CHECK(ikconfig_fd->IsOpen())
-      << "Unable to create ikconfig file: " << ikconfig_fd->StrError();
-  ikconfig_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, ikconfig_fd);
-
-  auto ikconfig_proc = ikconfig_cmd.Start();
-  CHECK(ikconfig_proc.Started() && ikconfig_proc.Wait() == 0)
-      << "Failed to extract ikconfig from " << kernel_image_path;
-
-  return ReadFile(ikconfig_path).find("BOOT_CONFIG=y") != std::string::npos;
-}
-
 const std::string kKernelDefaultPath = "kernel";
 const std::string kInitramfsImg = "initramfs.img";
 static void ExtractKernelParamsFromFetcherConfig(
@@ -391,8 +365,6 @@
                                  google::FlagSettingMode::SET_FLAGS_DEFAULT);
   }
 
-  const bool bootconfig_supported =
-      IsBootconfigSupported(config->assembly_dir());
   for (auto instance : config->Instances()) {
     const std::string new_vendor_boot_image_path =
         instance.vendor_boot_image_path();
@@ -404,7 +376,8 @@
         bool success = RepackVendorBootImage(
             FLAGS_initramfs_path, FLAGS_vendor_boot_image,
             new_vendor_boot_image_path, config->assembly_dir(),
-            instance.instance_dir(), boot_config_vector, bootconfig_supported);
+            instance.instance_dir(), boot_config_vector,
+            config->bootconfig_supported());
         CHECK(success) << "Failed to regenerate the vendor boot image with the "
                           "new ramdisk";
       } else {
@@ -414,7 +387,7 @@
         bool success = RepackVendorBootImageWithEmptyRamdisk(
             FLAGS_vendor_boot_image, new_vendor_boot_image_path,
             config->assembly_dir(), instance.instance_dir(), boot_config_vector,
-            bootconfig_supported);
+            config->bootconfig_supported());
         CHECK(success)
             << "Failed to regenerate the vendor boot image without a ramdisk";
       }
@@ -424,7 +397,7 @@
       bool success = RepackVendorBootImage(
           std::string(), FLAGS_vendor_boot_image, new_vendor_boot_image_path,
           config->assembly_dir(), instance.instance_dir(), boot_config_vector,
-          bootconfig_supported);
+          config->bootconfig_supported());
       CHECK(success) << "Failed to regenerate the vendor boot image";
     }
   }
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 8dc71b0..c23d1fc 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -72,9 +72,8 @@
               "Serial number to use for the device");
 DEFINE_bool(use_random_serial, false,
             "Whether to use random serial for the device.");
-DEFINE_string(
-    vm_manager, CrosvmManager::name(),
-    "What virtual machine manager to use, one of {qemu_cli, crosvm}");
+DEFINE_string(vm_manager, "",
+              "What virtual machine manager to use, one of {qemu_cli, crosvm}");
 DEFINE_string(gpu_mode, cuttlefish::kGpuModeAuto,
               "What gpu configuration to use, one of {auto, drm_virgl, "
               "gfxstream, guest_swiftshader}");
@@ -120,7 +119,7 @@
             "Enable crosvm sandbox. Use this when you are sure about what you are doing.");
 
 static const std::string kSeccompDir =
-    std::string("usr/share/crosvm/") + cuttlefish::HostArch() + "-linux-gnu/seccomp";
+    std::string("usr/share/crosvm/") + cuttlefish::HostArchStr() + "-linux-gnu/seccomp";
 DEFINE_string(seccomp_policy_dir, DefaultHostArtifactsPath(kSeccompDir),
               "With sandbox'ed crosvm, overrieds the security comp policy directory");
 
@@ -210,9 +209,8 @@
 DEFINE_string(setupwizard_mode, "DISABLED",
             "One of DISABLED,OPTIONAL,REQUIRED");
 
-DEFINE_string(qemu_binary,
-              "/usr/bin/qemu-system-x86_64",
-              "The qemu binary to use");
+DEFINE_string(qemu_binary_dir, "/usr/bin",
+              "Path to the directory containing the qemu binary to use");
 DEFINE_string(crosvm_binary, HostBinaryPath("crosvm"),
               "The Crosvm binary to use");
 DEFINE_string(tpm_device, "", "A host TPM device to pass through commands to.");
@@ -290,11 +288,13 @@
 
 DEFINE_bool(protected_vm, false, "Boot in Protected VM mode");
 
-DECLARE_string(system_image_dir);
-
-DEFINE_bool(enable_audio, cuttlefish::HostArch() != "aarch64",
+DEFINE_bool(enable_audio, cuttlefish::HostArch() != cuttlefish::Arch::Arm64,
             "Whether to play or capture audio");
 
+DECLARE_string(assembly_dir);
+DECLARE_string(boot_image);
+DECLARE_string(system_image_dir);
+
 namespace cuttlefish {
 using vm_manager::QemuManager;
 using vm_manager::GetVmManager;
@@ -332,17 +332,60 @@
   return stream.str();
 }
 
+void ReadKernelConfig(KernelConfig* kernel_config) {
+  const std::string kernel_image_path =
+      FLAGS_kernel_path.size() ? FLAGS_kernel_path : FLAGS_boot_image;
+
+  Command ikconfig_cmd(HostBinaryPath("extract-ikconfig"));
+  ikconfig_cmd.AddParameter(kernel_image_path);
+
+  std::string current_path = StringFromEnv("PATH", "");
+  std::string bin_folder = DefaultHostArtifactsPath("bin");
+  ikconfig_cmd.SetEnvironment({"PATH=" + current_path + ":" + bin_folder});
+
+  std::string ikconfig_path =
+      StringFromEnv("TEMP", "/tmp") + "/ikconfig.XXXXXX";
+  auto ikconfig_fd = SharedFD::Mkstemp(&ikconfig_path);
+  CHECK(ikconfig_fd->IsOpen())
+      << "Unable to create ikconfig file: " << ikconfig_fd->StrError();
+  ikconfig_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, ikconfig_fd);
+
+  auto ikconfig_proc = ikconfig_cmd.Start();
+  CHECK(ikconfig_proc.Started() && ikconfig_proc.Wait() == 0)
+      << "Failed to extract ikconfig from " << kernel_image_path;
+
+  std::string config = ReadFile(ikconfig_path);
+
+  if (config.find("\nCONFIG_ARM=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::Arm;
+  } else if (config.find("\nCONFIG_ARM64=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::Arm64;
+  } else if (config.find("\nCONFIG_X86_64=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::X86_64;
+  } else if (config.find("\nCONFIG_X86=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::X86;
+  } else {
+    LOG(FATAL) << "Unknown target architecture";
+  }
+  kernel_config->bootconfig_supported =
+      config.find("\nCONFIG_BOOT_CONFIG=y") != std::string::npos;
+
+  unlink(ikconfig_path.c_str());
+}
+
 } // namespace
 
 CuttlefishConfig InitializeCuttlefishConfiguration(
-    const std::string& assembly_dir, const std::string& instance_dir,
-    int modem_simulator_count) {
+    const std::string& instance_dir, int modem_simulator_count,
+    KernelConfig kernel_config) {
   // At most one streamer can be started.
   CHECK(NumStreamers() <= 1);
 
   CuttlefishConfig tmp_config_obj;
-  tmp_config_obj.set_assembly_dir(assembly_dir);
-  auto vmm = GetVmManager(FLAGS_vm_manager);
+  tmp_config_obj.set_assembly_dir(FLAGS_assembly_dir);
+  tmp_config_obj.set_target_arch(kernel_config.target_arch);
+  tmp_config_obj.set_bootconfig_supported(kernel_config.bootconfig_supported);
+  auto vmm = GetVmManager(FLAGS_vm_manager, kernel_config.target_arch);
   if (!vmm) {
     LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
   }
@@ -441,7 +484,7 @@
 
   tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
 
-  tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
+  tmp_config_obj.set_qemu_binary_dir(FLAGS_qemu_binary_dir);
   tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
   tmp_config_obj.set_tpm_device(FLAGS_tpm_device);
 
@@ -801,7 +844,7 @@
 
   // for now, we support only x86_64 by default
   bool default_enable_sandbox = false;
-  std::set<const std::string> supported_archs{std::string("x86_64")};
+  std::set<Arch> supported_archs{Arch::X86_64};
   if (supported_archs.find(HostArch()) != supported_archs.end()) {
     if (DirectoryExists(kCrosvmVarEmptyDir)) {
       default_enable_sandbox = IsDirectoryEmpty(kCrosvmVarEmptyDir);
@@ -821,10 +864,24 @@
                                SET_FLAGS_DEFAULT);
 }
 
-bool ParseCommandLineFlags(int* argc, char*** argv) {
+bool ParseCommandLineFlags(int* argc, char*** argv, KernelConfig* kernel_config) {
   google::ParseCommandLineNonHelpFlags(argc, argv, true);
   SetDefaultFlagsFromConfigPreset();
   bool invalid_manager = false;
+
+  if (!ResolveInstanceFiles()) {
+    return false;
+  }
+
+  ReadKernelConfig(kernel_config);
+  if (FLAGS_vm_manager == "") {
+    if (IsHostCompatible(kernel_config->target_arch)) {
+      FLAGS_vm_manager = CrosvmManager::name();
+    } else {
+      FLAGS_vm_manager = QemuManager::name();
+    }
+  }
+
   if (FLAGS_vm_manager == QemuManager::name()) {
     SetDefaultFlagsForQemu();
   } else if (FLAGS_vm_manager == CrosvmManager::name()) {
@@ -846,7 +903,7 @@
   // Set the env variable to empty (in case the caller passed a value for it).
   unsetenv(kCuttlefishConfigEnvVarName);
 
-  return ResolveInstanceFiles();
+  return true;
 }
 
 std::string GetConfigFilePath(const CuttlefishConfig& config) {
diff --git a/host/commands/assemble_cvd/flags.h b/host/commands/assemble_cvd/flags.h
index 3dca9e3..3c02d92 100644
--- a/host/commands/assemble_cvd/flags.h
+++ b/host/commands/assemble_cvd/flags.h
@@ -3,16 +3,23 @@
 #include <cstdint>
 #include <optional>
 
+#include "common/libs/utils/environment.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/config/fetcher_config.h"
 
 namespace cuttlefish {
 
-bool ParseCommandLineFlags(int* argc, char*** argv);
+struct KernelConfig {
+  Arch target_arch;
+  bool bootconfig_supported;
+};
+
+bool ParseCommandLineFlags(int* argc, char*** argv,
+                           KernelConfig* kernel_config);
 // Must be called after ParseCommandLineFlags.
 CuttlefishConfig InitializeCuttlefishConfiguration(
-    const std::string& assembly_dir, const std::string& instance_dir,
-    int modem_simulator_count);
+    const std::string& instance_dir, int modem_simulator_count,
+    KernelConfig kernel_config);
 
 std::string GetConfigFilePath(const CuttlefishConfig& config);
 std::string GetCuttlefishEnvPath();
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 8945da0..8e61d66 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -437,7 +437,7 @@
     LOG(ERROR) << "Ethernet TAP device already in use";
   }
 
-  auto vm_manager = GetVmManager(config->vm_manager());
+  auto vm_manager = GetVmManager(config->vm_manager(), config->target_arch());
 
 #ifndef __ANDROID__
   // Check host configuration
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index 867dcac..31ec7fe 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -151,6 +151,9 @@
 std::unique_ptr<Streamer> Streamer::Create(
     const StreamerConfig& cfg,
     std::shared_ptr<ConnectionObserverFactory> connection_observer_factory) {
+
+  rtc::LogMessage::LogToDebug(rtc::LS_ERROR);
+
   std::unique_ptr<Streamer::Impl> impl(new Streamer::Impl());
   impl->config_ = cfg;
   impl->connection_observer_factory_ = connection_observer_factory;
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index b7c7f7a..b0a3b3f 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -83,7 +83,7 @@
   std::vector<std::string> bootconfig_args;
 
   AppendVector(&bootconfig_args, VmManagerBootconfig(config));
-  auto vmm = vm_manager::GetVmManager(config.vm_manager());
+  auto vmm = vm_manager::GetVmManager(config.vm_manager(), config.target_arch());
   AppendVector(&bootconfig_args,
                vmm->ConfigureBootDevices(instance.virtual_disk_paths().size()));
   AppendVector(&bootconfig_args, vmm->ConfigureGpuMode(config.gpu_mode()));
@@ -155,6 +155,12 @@
 
   bootconfig_args.push_back("androidboot.verifiedbootstate=orange");
 
+  // Non-native architecture implies a significantly slower execution speed, so
+  // set a large timeout multiplier.
+  if (!IsHostCompatible(config.target_arch())) {
+    bootconfig_args.push_back("androidboot.hw_timeout_multiplier=50");
+  }
+
   // TODO(b/173815685): Create an extra_bootconfig flag and add it to bootconfig
 
   return bootconfig_args;
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index c1978bf..b9b04e5 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -259,12 +259,12 @@
   (*dictionary_)[kSetupWizardMode] = mode;
 }
 
-static constexpr char kQemuBinary[] = "qemu_binary";
-std::string CuttlefishConfig::qemu_binary() const {
-  return (*dictionary_)[kQemuBinary].asString();
+static constexpr char kQemuBinaryDir[] = "qemu_binary_dir";
+std::string CuttlefishConfig::qemu_binary_dir() const {
+  return (*dictionary_)[kQemuBinaryDir].asString();
 }
-void CuttlefishConfig::set_qemu_binary(const std::string& qemu_binary) {
-  (*dictionary_)[kQemuBinary] = qemu_binary;
+void CuttlefishConfig::set_qemu_binary_dir(const std::string& qemu_binary_dir) {
+  (*dictionary_)[kQemuBinaryDir] = qemu_binary_dir;
 }
 
 static constexpr char kCrosvmBinary[] = "crosvm_binary";
@@ -676,7 +676,8 @@
     console_dev = "hvc1";
   } else {
     // crosvm ARM does not support ttyAMA. ttyAMA is a part of ARM arch.
-    if (HostArch() == "aarch64" &&
+    Arch target = target_arch();
+    if ((target == Arch::Arm64 || target == Arch::Arm) &&
         vm_manager() != vm_manager::CrosvmManager::name()) {
       console_dev = "ttyAMA0";
     } else {
@@ -734,6 +735,22 @@
   return (*dictionary_)[kProtectedVm].asBool();
 }
 
+static constexpr char kTargetArch[] = "target_arch";
+void CuttlefishConfig::set_target_arch(Arch target_arch) {
+  (*dictionary_)[kTargetArch] = static_cast<int>(target_arch);
+}
+Arch CuttlefishConfig::target_arch() const {
+  return static_cast<Arch>((*dictionary_)[kTargetArch].asInt());
+}
+
+static constexpr char kBootconfigSupported[] = "bootconfig_supported";
+bool CuttlefishConfig::bootconfig_supported() const {
+  return (*dictionary_)[kBootconfigSupported].asBool();
+}
+void CuttlefishConfig::set_bootconfig_supported(bool bootconfig_supported) {
+  (*dictionary_)[kBootconfigSupported] = bootconfig_supported;
+}
+
 // Creates the (initially empty) config object and populates it with values from
 // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
 // Returns nullptr if there was an error loading from file
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 9aa94b0..c2a024f 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -25,6 +25,7 @@
 #include <set>
 #include <vector>
 
+#include "common/libs/utils/environment.h"
 #include "host/libs/config/custom_actions.h"
 
 namespace Json {
@@ -132,8 +133,8 @@
   void set_setupwizard_mode(const std::string& title);
   std::string setupwizard_mode() const;
 
-  void set_qemu_binary(const std::string& qemu_binary);
-  std::string qemu_binary() const;
+  void set_qemu_binary_dir(const std::string& qemu_binary_dir);
+  std::string qemu_binary_dir() const;
 
   void set_crosvm_binary(const std::string& crosvm_binary);
   std::string crosvm_binary() const;
@@ -308,6 +309,12 @@
   void set_protected_vm(bool protected_vm);
   bool protected_vm() const;
 
+  void set_target_arch(Arch target_arch);
+  Arch target_arch() const;
+
+  void set_bootconfig_supported(bool bootconfig_supported);
+  bool bootconfig_supported() const;
+
   class InstanceSpecific;
   class MutableInstanceSpecific;
 
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 7a9a40e..421950a 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -46,7 +46,8 @@
     // crosvm sets up the console= earlycon= panic= flags for us if booting straight to
     // the kernel, but QEMU and the bootloader via crosvm does not.
     AppendVector(&vm_manager_cmdline, {"console=hvc0", "panic=-1"});
-    if (HostArch() == "aarch64") {
+    Arch target_arch = config.target_arch();
+    if (target_arch == Arch::Arm64 || target_arch == Arch::Arm) {
       if (config.vm_manager() == QemuManager::name()) {
         // To update the pl011 address:
         // $ qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine dumpdtb=virt.dtb
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 58ae199..0d5b5c7 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -138,7 +138,7 @@
 
 std::vector<std::string> CrosvmManager::ConfigureBootDevices(int num_disks) {
   // TODO There is no way to control this assignment with crosvm (yet)
-  if (HostArch() == "x86_64") {
+  if (HostArch() == Arch::X86_64) {
     // crosvm has an additional PCI device for an ISA bridge
     std::stringstream stream;
     stream << std::setfill('0') << std::setw(2) << std::hex
diff --git a/host/libs/vm_manager/crosvm_manager.h b/host/libs/vm_manager/crosvm_manager.h
index d6e0e01..a2865f7 100644
--- a/host/libs/vm_manager/crosvm_manager.h
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -31,7 +31,7 @@
 class CrosvmManager : public VmManager {
  public:
   static std::string name() { return "crosvm"; }
-  CrosvmManager() = default;
+  CrosvmManager(Arch arch) : VmManager(arch) {}
   virtual ~CrosvmManager() = default;
 
   bool IsSupported() override;
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index dff471d..92da009 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -121,11 +121,22 @@
 }
 
 std::vector<std::string> QemuManager::ConfigureBootDevices(int num_disks) {
-  // QEMU has additional PCI devices for an ISA bridge and PIIX4
-  std::stringstream stream;
-  stream << std::setfill('0') << std::setw(2) << std::hex
-         << 2 + VmManager::kDefaultNumHvcs + VmManager::kMaxDisks - num_disks;
-  return {"androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() + ".0"};
+  switch (arch_) {
+    case Arch::X86:
+    case Arch::X86_64: {
+      // QEMU has additional PCI devices for an ISA bridge and PIIX4
+      std::stringstream stream;
+      stream << std::setfill('0') << std::setw(2) << std::hex
+             << 2 + VmManager::kDefaultNumHvcs + VmManager::kMaxDisks -
+                    num_disks;
+      return {"androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() +
+              ".0"};
+    }
+    case Arch::Arm:
+      return {"androidboot.boot_devices=3f000000.pcie"};
+    case Arch::Arm64:
+      return {"androidboot.boot_devices=4010000000.pcie"};
+  }
 }
 
 std::vector<Command> QemuManager::StartCommands(
@@ -141,7 +152,22 @@
                   << "attempting to KILL";
     return KillSubprocess(proc);
   };
-  Command qemu_cmd(config.qemu_binary(), stop);
+  std::string qemu_binary = config.qemu_binary_dir();
+  switch (arch_) {
+    case Arch::Arm:
+      qemu_binary += "/qemu-system-arm";
+      break;
+    case Arch::Arm64:
+      qemu_binary += "/qemu-system-aarch64";
+      break;
+    case Arch::X86:
+      qemu_binary += "/qemu-system-i386";
+      break;
+    case Arch::X86_64:
+      qemu_binary += "/qemu-system-x86_64";
+      break;
+  }
+  Command qemu_cmd(qemu_binary, stop);
 
   int hvc_num = 0;
   int serial_num = 0;
@@ -207,7 +233,7 @@
     hvc_num++;
   };
 
-  bool is_arm = android::base::EndsWith(config.qemu_binary(), "system-aarch64");
+  bool is_arm = arch_ == Arch::Arm || arch_ == Arch::Arm64;
 
   auto access_kregistry_size_bytes = 0;
   if (FileExists(instance.access_kregistry_path())) {
@@ -229,7 +255,8 @@
   qemu_cmd.AddParameter("guest=", instance.instance_name(), ",debug-threads=on");
 
   qemu_cmd.AddParameter("-machine");
-  auto machine = is_arm ? "virt,gic-version=2" : "pc-i440fx-2.8,accel=kvm,nvdimm=on";
+  auto machine = is_arm ? "virt,gic-version=2,mte=on"
+                        : "pc-i440fx-2.8,accel=kvm,nvdimm=on";
   qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off");
 
   qemu_cmd.AddParameter("-m");
@@ -428,7 +455,7 @@
   qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
 
   qemu_cmd.AddParameter("-cpu");
-  qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host");
+  qemu_cmd.AddParameter(IsHostCompatible(arch_) ? "host" : "max");
 
   qemu_cmd.AddParameter("-msg");
   qemu_cmd.AddParameter("timestamp=on");
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 3535246..21455f7 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -31,7 +31,7 @@
  public:
   static std::string name() { return "qemu_cli"; }
 
-  QemuManager() = default;
+  QemuManager(Arch arch) : VmManager(arch) {}
   virtual ~QemuManager() = default;
 
   bool IsSupported() override;
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index 30aeee7..3a5a41d 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -27,12 +27,12 @@
 namespace cuttlefish {
 namespace vm_manager {
 
-std::unique_ptr<VmManager> GetVmManager(const std::string& name) {
+std::unique_ptr<VmManager> GetVmManager(const std::string& name, Arch arch) {
   std::unique_ptr<VmManager> vmm;
   if (name == QemuManager::name()) {
-    vmm.reset(new QemuManager());
+    vmm.reset(new QemuManager(arch));
   } else if (name == CrosvmManager::name()) {
-    vmm.reset(new CrosvmManager());
+    vmm.reset(new CrosvmManager(arch));
   }
   if (!vmm) {
     LOG(ERROR) << "Invalid VM manager: " << name;
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index b482b2c..74f5725 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -26,6 +26,9 @@
 
 // Superclass of every guest VM manager.
 class VmManager {
+ protected:
+  const Arch arch_;
+
  public:
   // This is the number of HVC virtual console ports that should be configured
   // by the VmManager. Because crosvm currently allocates these ports as the
@@ -48,6 +51,7 @@
   // assigned virtual disk PCI ID (i.e. 2 disks = 7 hvcs, 1 disks = 8 hvcs)
   static const int kMaxDisks = 3;
 
+  VmManager(Arch arch) : arch_(arch) {}
   virtual ~VmManager() = default;
 
   virtual bool IsSupported() = 0;
@@ -62,7 +66,7 @@
       const CuttlefishConfig& config) = 0;
 };
 
-std::unique_ptr<VmManager> GetVmManager(const std::string&);
+std::unique_ptr<VmManager> GetVmManager(const std::string&, Arch arch);
 
 } // namespace vm_manager
 } // namespace cuttlefish
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 27114bf..7fa67f5 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -187,7 +187,8 @@
 BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_PARTITION_LIST := product system system_ext
 BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_SIZE := 5368709120  # 5GiB
 BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_PARTITION_LIST := odm vendor vendor_dlkm odm_dlkm
-BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_SIZE := 1073741824
+# 1020MiB, reserve 4MiB for dynamic partition metadata
+BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_SIZE := 1069547520
 BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true
 BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE := true
 TARGET_RELEASETOOLS_EXTENSIONS := device/google/cuttlefish/shared
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 3cd5a02..184833f 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -9,6 +9,7 @@
     setprop ro.hardware.hwcomposer ${ro.boot.hardware.hwcomposer}
     setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
     setprop ro.cpuvulkan.version ${ro.boot.cpuvulkan.version}
+    setprop ro.hw_timeout_multiplier ${ro.boot.hw_timeout_multiplier}
 
     # start module load in the background
     start vendor.insmod_sh