emulator-arm64: ARM64 wrapper for QEMU executable.

This patch adds two new binaries (emulator-arm64 and emulator64-arm64)
which act as a wrapper for QEMU-based emulation engines for ARM64
emulation (and ARM64 _only_).

The (prebuilt) qemu binary must be located under:

  $PROGRAM_DIR/qemu/$SYSTEM/qemu-system-aarch64$EXE

Where:

  $PROGRAM_DIR is the location of the 'emulator-arm64' binary.
  $SYSTEM is a host system tag (e.g. linux-x86 or linux-x86_64)
  $EXE is the host executable extension.

The program is in charge of parsing command-line arguments,
computing AVD configuration, then calling the QEMU program
with a (very long) list of configuration arguments.

See https://qemu-android.googlesource.com/qemu-ranchu/ for
scripts used to rebuild the QEMU prebuilt binary from
sources.

Change-Id: Ifcb9dd50fc5d83a864a1f957d12b1c199e0504fb
diff --git a/Makefile.android b/Makefile.android
index 350b72f..600be7c 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -379,6 +379,9 @@
 EMULATOR_TARGET_ARCH := mips
 include $(LOCAL_PATH)/Makefile.target
 
+EMULATOR_TARGET_ARCH := arm64
+include $(LOCAL_PATH)/Makefile.qemu-launcher
+
 ##############################################################################
 ##############################################################################
 ###
diff --git a/Makefile.qemu-launcher b/Makefile.qemu-launcher
new file mode 100644
index 0000000..7be48d7
--- /dev/null
+++ b/Makefile.qemu-launcher
@@ -0,0 +1,42 @@
+##############################################################################
+##############################################################################
+###
+###  emulator-$ARCH: Standalone launcher for QEMU executable.
+###
+###
+EMULATOR_TARGET_CPU := $(EMULATOR_TARGET_ARCH)
+ifeq ($(EMULATOR_TARGET_CPU),x86)
+  EMULATOR_TARGET_CPU := i386
+endif
+
+ifeq ($(EMULATOR_TARGET_CPU),arm64)
+
+qemu_launcher_SOURCES := \
+    android/qemu-launcher/emulator-qemu.cpp \
+    android/cmdline-option.c \
+    android/help.c \
+    android/main-common.c \
+
+qemu_launcher_CFLAGS := -DNO_SKIN=1
+
+qemu_launcher_LDLIBS := -lstdc++
+
+$(call start-emulator-program, emulator-arm64)
+LOCAL_SRC_FILES := $(qemu_launcher_SOURCES)
+LOCAL_CFLAGS := $(qemu_launcher_CFLAGS)
+LOCAL_STATIC_LIBRARIES := \
+    emulator-common
+LOCAL_LDLIBS := $(qemu_launcher_LDLIBS)
+$(call gen-hw-config-defs)
+$(call end-emulator-program)
+
+$(call start-emulator64-program, emulator64-arm64)
+LOCAL_SRC_FILES := $(qemu_launcher_SOURCES)
+LOCAL_CFLAGS := $(qemu_launcher_CFLAGS)
+LOCAL_STATIC_LIBRARIES := \
+    emulator64-common
+LOCAL_LDLIBS := $(qemu_launcher_LDLIBS)
+$(call gen-hw-config-defs)
+$(call end-emulator-program)
+
+endif  # EMULATOR_TARGET_CPU == arm64
diff --git a/android/avd/util.c b/android/avd/util.c
index cbd6e9c..51236ac 100644
--- a/android/avd/util.c
+++ b/android/avd/util.c
@@ -338,6 +338,7 @@
         { "x86", "x86" },
         { "x86_64", "x86" },
         { "mips", "mips" },
+        { "arm64", "arm" },
         // Add more if needed here.
     };
     size_t n;
diff --git a/android/avd/util_unittest.cpp b/android/avd/util_unittest.cpp
index 4197b0a..d73474f 100644
--- a/android/avd/util_unittest.cpp
+++ b/android/avd/util_unittest.cpp
@@ -19,10 +19,10 @@
   EXPECT_STREQ("x86", emulator_getBackendSuffix("x86"));
   EXPECT_STREQ("x86", emulator_getBackendSuffix("x86_64"));
   EXPECT_STREQ("mips", emulator_getBackendSuffix("mips"));
+  EXPECT_STREQ("arm", emulator_getBackendSuffix("arm64"));
 
   // TODO(digit): Add support for these CPU architectures to the emulator
   // to change these to EXPECT_STREQ() calls.
-  EXPECT_FALSE(emulator_getBackendSuffix("arm64"));
   EXPECT_FALSE(emulator_getBackendSuffix("mips64"));
 
   EXPECT_FALSE(emulator_getBackendSuffix(NULL));
diff --git a/android/main-emulator.c b/android/main-emulator.c
index 3f09c33..f9f2976 100644
--- a/android/main-emulator.c
+++ b/android/main-emulator.c
@@ -332,6 +332,22 @@
         }
     }
 
+    // Special case: for arm64, first try to find emulator-arm64 before
+    // looking for emulator-arm.
+    if (!strcmp(avdArch, "arm64")) {
+        emulatorSuffix = "arm64";
+
+        D("Looking for emulator backend for %s CPU\n", avdArch);
+
+        result = probeTargetEmulatorPath(progDir,
+                                         emulatorSuffix,
+                                         search_for_64bit_emulator,
+                                         try_current_path);
+        if (result) {
+            return result;
+        }
+    }
+
     // Now for the regular case.
     emulatorSuffix = emulator_getBackendSuffix(avdArch);
     if (!emulatorSuffix) {
diff --git a/android/qemu-launcher/emulator-qemu.cpp b/android/qemu-launcher/emulator-qemu.cpp
new file mode 100644
index 0000000..899cc9d
--- /dev/null
+++ b/android/qemu-launcher/emulator-qemu.cpp
@@ -0,0 +1,815 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// This source file implements emulator-arm64 and emulator64-arm64
+// which are used to launch QEMU binaries located under
+// $PROGRAM_DIR/qemu/<host>/qemu-system-aarch64<exe>
+
+#include "android/base/containers/StringVector.h"
+#include "android/base/files/PathUtils.h"
+#include "android/base/Limits.h"
+#include "android/base/Log.h"
+#include "android/base/String.h"
+#include "android/base/StringFormat.h"
+
+#include "android/cmdline-option.h"
+#include "android/globals.h"
+#include "android/help.h"
+#include "android/kernel/kernel_utils.h"
+#include "android/main-common.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+#include "android/utils/path.h"
+#include "android/utils/stralloc.h"
+#include "android/utils/win32_cmdline_quote.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define  STRINGIFY(x)   _STRINGIFY(x)
+#define  _STRINGIFY(x)  #x
+
+#ifdef ANDROID_SDK_TOOLS_REVISION
+#  define  VERSION_STRING  STRINGIFY(ANDROID_SDK_TOOLS_REVISION)".0"
+#else
+#  define  VERSION_STRING  "standalone"
+#endif
+
+#define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
+
+/* The execv() definition in older mingw is slightly bogus.
+ * It takes a second argument of type 'const char* const*'
+ * while POSIX mandates char** instead.
+ *
+ * To avoid compiler warnings, define the safe_execv macro
+ * to perform an explicit cast with mingw.
+ */
+#if defined(_WIN32) && !ANDROID_GCC_PREREQ(4,4)
+#  define safe_execv(_filepath,_argv)  execv((_filepath),(const char* const*)(_argv))
+#else
+#  define safe_execv(_filepath,_argv)  execv((_filepath),(_argv))
+#endif
+
+using namespace android::base;
+
+namespace {
+
+// The host CPU architecture.
+#ifdef __i386__
+const char kHostArch[] = "x86";
+#elif defined(__x86_64__)
+const char kHostArch[] = "x86_64";
+#else
+#error "Your host CPU is not supported!"
+#endif
+
+// The host operating system name.
+#ifdef __linux__
+static const char kHostOs[] = "linux";
+#elif defined(__APPLE__)
+static const char kHostOs[] = "darwin";
+#elif defined(_WIN32)
+static const char kHostOs[] = "windows";
+#endif
+
+// The target CPU architecture.
+const char kTargetArch[] = "aarch64";
+
+// Return the path of the QEMU executable
+String getQemuExecutablePath(const char* programPath) {
+    StringVector path = PathUtils::decompose(programPath);
+    if (path.size() < 1) {
+        return String();
+    }
+    // Remove program from path.
+    path.resize(path.size() - 1U);
+
+    // Add sub-directories.
+    path.append(String("qemu"));
+
+    String host = kHostOs;
+    host += "-";
+    host += kHostArch;
+    path.append(host);
+
+    String qemuProgram = "qemu-system-";
+    qemuProgram += kTargetArch;
+#ifdef _WIN32
+    qemuProgram += ".exe";
+#endif
+    path.append(qemuProgram);
+
+    return PathUtils::recompose(path);
+}
+
+void emulator_help( void ) {
+    STRALLOC_DEFINE(out);
+    android_help_main(out);
+    printf("%.*s", out->n, out->s);
+    stralloc_reset(out);
+    exit(1);
+}
+
+/* TODO: Put in shared source file */
+char* _getFullFilePath(const char* rootPath, const char* fileName) {
+    if (path_is_absolute(fileName)) {
+        return ASTRDUP(fileName);
+    } else {
+        char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+        p = bufprint(temp, end, "%s/%s", rootPath, fileName);
+        if (p >= end) {
+            return NULL;
+        }
+        return ASTRDUP(temp);
+    }
+}
+
+uint64_t _adjustPartitionSize(const char*  description,
+                              uint64_t     imageBytes,
+                              uint64_t     defaultBytes,
+                              int          inAndroidBuild ) {
+    char      temp[64];
+    unsigned  imageMB;
+    unsigned  defaultMB;
+
+    if (imageBytes <= defaultBytes)
+        return defaultBytes;
+
+    imageMB   = convertBytesToMB(imageBytes);
+    defaultMB = convertBytesToMB(defaultBytes);
+
+    if (imageMB > defaultMB) {
+        snprintf(temp, sizeof temp, "(%d MB > %d MB)", imageMB, defaultMB);
+    } else {
+        snprintf(temp, sizeof temp, "(%" PRIu64 "  bytes > %" PRIu64 " bytes)", imageBytes, defaultBytes);
+    }
+
+    if (inAndroidBuild) {
+        dwarning("%s partition size adjusted to match image file %s\n", description, temp);
+    }
+
+    return convertMBToBytes(imageMB);
+}
+
+bool android_op_wipe_data;
+
+}  // namespace
+
+extern "C" int main(int argc, char **argv, char **envp) {
+    if (argc < 1) {
+        fprintf(stderr, "Invalid invokation (no program path)\n");
+        return 1;
+    }
+
+    AndroidOptions opts[1];
+
+    if (android_parse_options(&argc, &argv, opts) < 0) {
+        return 1;
+    }
+
+    // TODO(digit): This code is very similar to the one in main.c,
+    // refactor everything so that it fits into a single shared source
+    // file, if possible, with the least amount of dependencies.
+
+    while (argc-- > 1) {
+        const char* opt = (++argv)[0];
+
+        if(!strcmp(opt, "-qemu")) {
+            argc--;
+            argv++;
+            break;
+        }
+
+        if (!strcmp(opt, "-help")) {
+            emulator_help();
+        }
+
+        if (!strncmp(opt, "-help-",6)) {
+            STRALLOC_DEFINE(out);
+            opt += 6;
+
+            if (!strcmp(opt, "all")) {
+                android_help_all(out);
+            }
+            else if (android_help_for_option(opt, out) == 0) {
+                /* ok */
+            }
+            else if (android_help_for_topic(opt, out) == 0) {
+                /* ok */
+            }
+            if (out->n > 0) {
+                printf("\n%.*s", out->n, out->s);
+                exit(0);
+            }
+
+            fprintf(stderr, "unknown option: -help-%s\n", opt);
+            fprintf(stderr, "please use -help for a list of valid topics\n");
+            exit(1);
+        }
+
+        if (opt[0] == '-') {
+            fprintf(stderr, "unknown option: %s\n", opt);
+            fprintf(stderr, "please use -help for a list of valid options\n");
+            exit(1);
+        }
+
+        fprintf(stderr, "invalid command-line parameter: %s.\n", opt);
+        fprintf(stderr, "Hint: use '@foo' to launch a virtual device named 'foo'.\n");
+        fprintf(stderr, "please use -help for more information\n");
+        exit(1);
+    }
+
+    if (opts->version) {
+        printf("Android emulator version %s\n"
+               "Copyright (C) 2006-2011 The Android Open Source Project and many others.\n"
+               "This program is a derivative of the QEMU CPU emulator (www.qemu.org).\n\n",
+#if defined ANDROID_BUILD_ID
+               VERSION_STRING " (build_id " STRINGIFY(ANDROID_BUILD_ID) ")" );
+#else
+               VERSION_STRING);
+#endif
+        printf("  This software is licensed under the terms of the GNU General Public\n"
+               "  License version 2, as published by the Free Software Foundation, and\n"
+               "  may be copied, distributed, and modified under those terms.\n\n"
+               "  This program is distributed in the hope that it will be useful,\n"
+               "  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+               "  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+               "  GNU General Public License for more details.\n\n");
+
+        exit(0);
+    }
+
+    sanitizeOptions(opts);
+
+    // Ignore snapshot storage here.
+
+    int inAndroidBuild = 0;
+    AvdInfo* avd = createAVD(opts, &inAndroidBuild);
+
+    // Ignore skin options here.
+
+    // Read hardware configuration, apply overrides from options.
+    AndroidHwConfig* hw = android_hw;
+    if (avdInfo_initHwConfig(avd, hw) < 0) {
+        derror("could not read hardware configuration ?");
+        exit(1);
+    }
+
+    /* Update CPU architecture for HW configs created from build dir. */
+    if (inAndroidBuild) {
+#if defined(TARGET_ARM)
+        reassign_string(&android_hw->hw_cpu_arch, "arm");
+#elif defined(TARGET_I386)
+        reassign_string(&android_hw->hw_cpu_arch, "x86");
+#elif defined(TARGET_MIPS)
+        reassign_string(&android_hw->hw_cpu_arch, "mips");
+#elif defined(TARGET_ARM64)
+        reassign_string(&android_hw->hw_cpu_arch, "arm64");
+#endif
+    }
+
+    /* generate arguments for the underlying qemu main() */
+    {
+        char*  kernelFile    = opts->kernel;
+
+        if (kernelFile == NULL) {
+            kernelFile = avdInfo_getKernelPath(avd);
+            if (kernelFile == NULL) {
+                derror( "This AVD's configuration is missing a kernel file!!" );
+                exit(2);
+            }
+            D("autoconfig: -kernel %s", kernelFile);
+        }
+        if (!path_exists(kernelFile)) {
+            derror( "Invalid or missing kernel image file: %s", kernelFile );
+            exit(2);
+        }
+
+        hw->kernel_path = kernelFile;
+    }
+
+    KernelType kernelType = KERNEL_TYPE_LEGACY;
+    if (!android_pathProbeKernelType(hw->kernel_path, &kernelType)) {
+        D("WARNING: Could not determine kernel device naming scheme. Assuming legacy\n"
+            "If this AVD doesn't boot, and uses a recent kernel (3.10 or above) try setting\n"
+            "'kernel.newDeviceNaming' to 'yes' in its configuration.\n");
+    }
+
+    // Auto-detect kernel device naming scheme if needed.
+    if (androidHwConfig_getKernelDeviceNaming(hw) < 0) {
+        const char* newDeviceNaming = "no";
+        if (kernelType == KERNEL_TYPE_3_10_OR_ABOVE) {
+            D("Auto-detect: Kernel image requires new device naming scheme.");
+            newDeviceNaming = "yes";
+        } else {
+            D("Auto-detect: Kernel image requires legacy device naming scheme.");
+        }
+        AFREE(hw->kernel_newDeviceNaming);
+        hw->kernel_newDeviceNaming = ASTRDUP(newDeviceNaming);
+    }
+
+    // Auto-detect YAFFS2 partition support if needed.
+    if (androidHwConfig_getKernelYaffs2Support(hw) < 0) {
+        // Essentially, anything before API level 20 supports Yaffs2
+        const char* newYaffs2Support = "no";
+        if (avdInfo_getApiLevel(avd) < 20) {
+            newYaffs2Support = "yes";
+            D("Auto-detect: Kernel does support YAFFS2 partitions.");
+        } else {
+            D("Auto-detect: Kernel does not support YAFFS2 partitions.");
+        }
+        AFREE(hw->kernel_supportsYaffs2);
+        hw->kernel_supportsYaffs2 = ASTRDUP(newYaffs2Support);
+    }
+
+
+    /* opts->ramdisk is never NULL (see createAVD) here */
+    if (opts->ramdisk) {
+        reassign_string(&hw->disk_ramdisk_path, opts->ramdisk);
+    }
+    else if (!hw->disk_ramdisk_path[0]) {
+        hw->disk_ramdisk_path = avdInfo_getRamdiskPath(avd);
+        D("autoconfig: -ramdisk %s", hw->disk_ramdisk_path);
+    }
+
+    /* -partition-size is used to specify the max size of both the system
+     * and data partition sizes.
+     */
+    uint64_t defaultPartitionSize = convertMBToBytes(200);
+
+    if (opts->partition_size) {
+        char*  end;
+        long   sizeMB = strtol(opts->partition_size, &end, 0);
+        long   minSizeMB = 10;
+        long   maxSizeMB = LONG_MAX / ONE_MB;
+
+        if (sizeMB < 0 || *end != 0) {
+            derror( "-partition-size must be followed by a positive integer" );
+            exit(1);
+        }
+        if (sizeMB < minSizeMB || sizeMB > maxSizeMB) {
+            derror( "partition-size (%d) must be between %dMB and %dMB",
+                    sizeMB, minSizeMB, maxSizeMB );
+            exit(1);
+        }
+        defaultPartitionSize = (uint64_t) sizeMB * ONE_MB;
+    }
+
+    /** SYSTEM PARTITION **/
+
+    if (opts->sysdir == NULL) {
+        if (avdInfo_inAndroidBuild(avd)) {
+            opts->sysdir = ASTRDUP(avdInfo_getContentPath(avd));
+            D("autoconfig: -sysdir %s", opts->sysdir);
+        }
+    }
+
+    if (opts->sysdir != NULL) {
+        if (!path_exists(opts->sysdir)) {
+            derror("Directory does not exist: %s", opts->sysdir);
+            exit(1);
+        }
+    }
+
+    {
+        char*  rwImage   = NULL;
+        char*  initImage = NULL;
+
+        do {
+            if (opts->system == NULL) {
+                /* If -system is not used, try to find a runtime system image
+                * (i.e. system-qemu.img) in the content directory.
+                */
+                rwImage = avdInfo_getSystemImagePath(avd);
+                if (rwImage != NULL) {
+                    break;
+                }
+                /* Otherwise, try to find the initial system image */
+                initImage = avdInfo_getSystemInitImagePath(avd);
+                if (initImage == NULL) {
+                    derror("No initial system image for this configuration!");
+                    exit(1);
+                }
+                break;
+            }
+
+            /* If -system <name> is used, use it to find the initial image */
+            if (opts->sysdir != NULL && !path_exists(opts->system)) {
+                initImage = _getFullFilePath(opts->sysdir, opts->system);
+            } else {
+                initImage = ASTRDUP(opts->system);
+            }
+            if (!path_exists(initImage)) {
+                derror("System image file doesn't exist: %s", initImage);
+                exit(1);
+            }
+
+        } while (0);
+
+        if (rwImage != NULL) {
+            /* Use the read/write image file directly */
+            hw->disk_systemPartition_path     = rwImage;
+            hw->disk_systemPartition_initPath = NULL;
+            D("Using direct system image: %s", rwImage);
+        } else if (initImage != NULL) {
+            hw->disk_systemPartition_path = NULL;
+            hw->disk_systemPartition_initPath = initImage;
+            D("Using initial system image: %s", initImage);
+        }
+
+        /* Check the size of the system partition image.
+        * If we have an AVD, it must be smaller than
+        * the disk.systemPartition.size hardware property.
+        *
+        * Otherwise, we need to adjust the systemPartitionSize
+        * automatically, and print a warning.
+        *
+        */
+        const char* systemImage = hw->disk_systemPartition_path;
+        uint64_t    systemBytes;
+
+        if (systemImage == NULL)
+            systemImage = hw->disk_systemPartition_initPath;
+
+        if (path_get_size(systemImage, &systemBytes) < 0) {
+            derror("Missing system image: %s", systemImage);
+            exit(1);
+        }
+
+        hw->disk_systemPartition_size =
+            _adjustPartitionSize("system", systemBytes, defaultPartitionSize,
+                                 avdInfo_inAndroidBuild(avd));
+    }
+
+    /** DATA PARTITION **/
+
+    if (opts->datadir) {
+        if (!path_exists(opts->datadir)) {
+            derror("Invalid -datadir directory: %s", opts->datadir);
+        }
+    }
+
+    {
+        char*  dataImage = NULL;
+        char*  initImage = NULL;
+
+        do {
+            if (!opts->data) {
+                dataImage = avdInfo_getDataImagePath(avd);
+                if (dataImage != NULL) {
+                    D("autoconfig: -data %s", dataImage);
+                    break;
+                }
+                dataImage = avdInfo_getDefaultDataImagePath(avd);
+                if (dataImage == NULL) {
+                    derror("No data image path for this configuration!");
+                    exit (1);
+                }
+                opts->wipe_data = 1;
+                break;
+            }
+
+            if (opts->datadir) {
+                dataImage = _getFullFilePath(opts->datadir, opts->data);
+            } else {
+                dataImage = ASTRDUP(opts->data);
+            }
+        } while (0);
+
+        if (opts->initdata != NULL) {
+            initImage = ASTRDUP(opts->initdata);
+            if (!path_exists(initImage)) {
+                derror("Invalid initial data image path: %s", initImage);
+                exit(1);
+            }
+        } else {
+            initImage = avdInfo_getDataInitImagePath(avd);
+            D("autoconfig: -initdata %s", initImage);
+        }
+
+        hw->disk_dataPartition_path = dataImage;
+        if (opts->wipe_data) {
+            hw->disk_dataPartition_initPath = initImage;
+        } else {
+            hw->disk_dataPartition_initPath = NULL;
+        }
+        android_op_wipe_data = opts->wipe_data;
+
+        uint64_t     defaultBytes =
+                hw->disk_dataPartition_size == 0 ?
+                defaultPartitionSize :
+                hw->disk_dataPartition_size;
+        uint64_t     dataBytes;
+        const char*  dataPath = hw->disk_dataPartition_initPath;
+
+        if (dataPath == NULL)
+            dataPath = hw->disk_dataPartition_path;
+
+        path_get_size(dataPath, &dataBytes);
+
+        hw->disk_dataPartition_size =
+            _adjustPartitionSize("data", dataBytes, defaultBytes,
+                                 avdInfo_inAndroidBuild(avd));
+    }
+
+    /** CACHE PARTITION **/
+
+    if (opts->no_cache) {
+        /* No cache partition at all */
+        hw->disk_cachePartition = 0;
+    }
+    else if (!hw->disk_cachePartition) {
+        if (opts->cache) {
+            dwarning( "Emulated hardware doesn't support a cache partition. -cache option ignored!" );
+            opts->cache = NULL;
+        }
+    }
+    else
+    {
+        if (!opts->cache) {
+            /* Find the current cache partition file */
+            opts->cache = avdInfo_getCachePath(avd);
+            if (opts->cache == NULL) {
+                /* The file does not exists, we will force its creation
+                 * if we are not in the Android build system. Otherwise,
+                 * a temporary file will be used.
+                 */
+                if (!avdInfo_inAndroidBuild(avd)) {
+                    opts->cache = avdInfo_getDefaultCachePath(avd);
+                }
+            }
+            if (opts->cache) {
+                D("autoconfig: -cache %s", opts->cache);
+            }
+        }
+
+        if (opts->cache) {
+            hw->disk_cachePartition_path = ASTRDUP(opts->cache);
+        }
+    }
+
+    if (hw->disk_cachePartition_path && opts->cache_size) {
+        /* Set cache partition size per user options. */
+        char*  end;
+        long   sizeMB = strtol(opts->cache_size, &end, 0);
+
+        if (sizeMB < 0 || *end != 0) {
+            derror( "-cache-size must be followed by a positive integer" );
+            exit(1);
+        }
+        hw->disk_cachePartition_size = (uint64_t) sizeMB * ONE_MB;
+    }
+
+    /** SD CARD PARTITION */
+
+    if (!hw->hw_sdCard) {
+        /* No SD Card emulation, so -sdcard will be ignored */
+        if (opts->sdcard) {
+            dwarning( "Emulated hardware doesn't support SD Cards. -sdcard option ignored." );
+            opts->sdcard = NULL;
+        }
+    } else {
+        /* Auto-configure -sdcard if it is not available */
+        if (!opts->sdcard) {
+            do {
+                /* If -datadir <path> is used, look for a sdcard.img file here */
+                if (opts->datadir) {
+                    char tmp[PATH_MAX], *tmpend = tmp + sizeof(tmp);
+                    bufprint(tmp, tmpend, "%s/%s", opts->datadir, "system.img");
+                    if (path_exists(tmp)) {
+                        opts->sdcard = strdup(tmp);
+                        break;
+                    }
+                }
+
+                /* Otherwise, look at the AVD's content */
+                opts->sdcard = avdInfo_getSdCardPath(avd);
+                if (opts->sdcard != NULL) {
+                    break;
+                }
+
+                /* Nothing */
+            } while (0);
+
+            if (opts->sdcard) {
+                D("autoconfig: -sdcard %s", opts->sdcard);
+            }
+        }
+    }
+
+    if(opts->sdcard) {
+        uint64_t  size;
+        if (path_get_size(opts->sdcard, &size) == 0) {
+            /* see if we have an sdcard image.  get its size if it exists */
+            /* due to what looks like limitations of the MMC protocol, one has
+             * to use an SD Card image that is equal or larger than 9 MB
+             */
+            if (size < 9*1024*1024ULL) {
+                fprintf(stderr, "### WARNING: SD Card files must be at least 9MB, ignoring '%s'\n", opts->sdcard);
+            } else {
+                hw->hw_sdCard_path = ASTRDUP(opts->sdcard);
+            }
+        } else {
+            dwarning("no SD Card image at '%s'", opts->sdcard);
+        }
+    }
+
+    if (opts->memory) {
+        char*  end;
+        long   ramSize = strtol(opts->memory, &end, 0);
+        if (ramSize < 0 || *end != 0) {
+            derror( "-memory must be followed by a positive integer" );
+            exit(1);
+        }
+        if (ramSize < 32 || ramSize > 4096) {
+            derror( "physical memory size must be between 32 and 4096 MB" );
+            exit(1);
+        }
+        hw->hw_ramSize = ramSize;
+    } else {
+        int ramSize = hw->hw_ramSize;
+        if (ramSize <= 0) {
+#if 1
+            // For ARM64, use 1GB by default.
+            ramSize = 1024 * 1024ULL;
+#else
+            /* Compute the default RAM size based on the size of screen.
+             * This is only used when the skin doesn't provide the ram
+             * size through its hardware.ini (i.e. legacy ones) or when
+             * in the full Android build system.
+             */
+            int64_t pixels  = hw->hw_lcd_width * hw->hw_lcd_height;
+            /* The following thresholds are a bit liberal, but we
+             * essentially want to ensure the following mappings:
+             *
+             *   320x480 -> 96
+             *   800x600 -> 128
+             *  1024x768 -> 256
+             *
+             * These are just simple heuristics, they could change in
+             * the future.
+             */
+            if (pixels <= 250000)
+                ramSize = 96;
+            else if (pixels <= 500000)
+                ramSize = 128;
+            else
+                ramSize = 256;
+        }
+#endif
+        hw->hw_ramSize = ramSize;
+    }
+
+    D("Physical RAM size: %dMB\n", hw->hw_ramSize);
+
+    if (opts->gpu) {
+        const char* gpu = opts->gpu;
+        if (!strcmp(gpu,"on") || !strcmp(gpu,"enable")) {
+            hw->hw_gpu_enabled = 1;
+        } else if (!strcmp(gpu,"off") || !strcmp(gpu,"disable")) {
+            hw->hw_gpu_enabled = 0;
+        } else if (!strcmp(gpu,"auto")) {
+            /* Nothing to do */
+        } else {
+            derror("Invalid value for -gpu <mode> parameter: %s\n", gpu);
+            derror("Valid values are: on, off or auto\n");
+            exit(1);
+        }
+    }
+
+    hw->avd_name = ASTRDUP(avdInfo_getName(avd));
+
+    String qemuExecutable = getQemuExecutablePath(argv[0]);
+    D("QEMU EXECUTABLE=%s\n", qemuExecutable.c_str());
+
+    // Now build the QEMU parameters.
+    const char* args[128];
+    int n = 0;
+
+    args[n++] = qemuExecutable.c_str();
+
+    args[n++] = "-cpu";
+    args[n++] = "cortex-a57";
+    args[n++] = "-machine";
+    args[n++] = "type=ranchu";
+
+    // Memory size
+    args[n++] = "-m";
+    String memorySize = StringFormat("%ld", hw->hw_ramSize);
+    args[n++] = memorySize.c_str();
+
+    // Command-line
+    args[n++] = "-append";
+    String kernelCommandLine =
+            "console=ttyAMA0,38400 keep_bootcon earlyprintk=ttyAMA0";
+    args[n++] = kernelCommandLine.c_str();
+
+    args[n++] = "-serial";
+    args[n++] = "mon:stdio";
+
+    // Kernel image
+    args[n++] = "-kernel";
+    args[n++] = hw->kernel_path;
+
+    // Ramdisk
+    args[n++] = "-initrd";
+    args[n++] = hw->disk_ramdisk_path;
+
+    // Data partition.
+    args[n++] = "-drive";
+    String userDataParam =
+            StringFormat("index=2,id=userdata,file=%s",
+                         hw->disk_dataPartition_path);
+    args[n++] = userDataParam.c_str();
+    args[n++] = "-device";
+    args[n++] = "virtio-blk-device,drive=userdata";
+
+    // Cache partition.
+    args[n++] = "-drive";
+    String cacheParam =
+            StringFormat("index=1,id=cache,file=%s",
+                         hw->disk_cachePartition_path);
+    args[n++] = cacheParam.c_str();
+    args[n++] = "-device";
+    args[n++] = "virtio-blk-device,drive=cache";
+
+    // System partition.
+    args[n++] = "-drive";
+    String systemParam =
+            StringFormat("index=0,id=system,file=%s",
+                         hw->disk_systemPartition_initPath);
+    args[n++] = systemParam.c_str();
+    args[n++] = "-device";
+    args[n++] = "virtio-blk-device,drive=system";
+
+    // Network
+    args[n++] = "-netdev";
+    args[n++] = "user,id=mynet";
+    args[n++] = "-device";
+    args[n++] = "virtio-net-device,netdev=mynet";
+    args[n++] = "-show-cursor";
+
+    if(VERBOSE_CHECK(init)) {
+        int i;
+        printf("QEMU options list:\n");
+        for(i = 0; i < n; i++) {
+            printf("emulator: argv[%02d] = \"%s\"\n", i, args[i]);
+        }
+        /* Dump final command-line option to make debugging the core easier */
+        printf("Concatenated QEMU options:\n");
+        for (i = 0; i < n; i++) {
+            /* To make it easier to copy-paste the output to a command-line,
+             * quote anything that contains spaces.
+             */
+            if (strchr(args[i], ' ') != NULL) {
+                printf(" '%s'", args[i]);
+            } else {
+                printf(" %s", args[i]);
+            }
+        }
+        printf("\n");
+    }
+
+    if (!path_exists(qemuExecutable.c_str())) {
+        fprintf(stderr, "Missing QEMU executable: %s\n",
+                qemuExecutable.c_str());
+        return 1;
+    }
+
+    // Now launch executable.
+#ifdef _WIN32
+    // Take care of quoting all parameters before sending them to execv().
+    // See the "Eveyone quotes command line arguments the wrong way" on
+    // MSDN.
+    int i;
+    for (i = 0; i < n; ++i) {
+        // Technically, this leaks the quoted strings, but we don't care
+        // since this process will terminate after the execv() anyway.
+        args[i] = win32_cmdline_quote(args[i]);
+        D("Quoted param: [%s]\n", args[i]);
+    }
+#endif
+
+    safe_execv(qemuExecutable.c_str(), (char* const*)args);
+
+    fprintf(stderr,
+            "Could not launch QEMU [%s]: %s\n",
+            qemuExecutable.c_str(),
+            strerror(errno));
+
+    return errno;
+}
+}
+}
+}
diff --git a/android/utils/system.h b/android/utils/system.h
index a6b84d1..94c1581 100644
--- a/android/utils/system.h
+++ b/android/utils/system.h
@@ -14,7 +14,9 @@
 
 #include <string.h>
 #include <stdint.h>
+#ifndef __STDC_FORMAT_MACROS
 #define __STDC_FORMAT_MACROS 1
+#endif
 #include <inttypes.h>  /* for PRId64 et al. */
 #include "android/utils/assert.h"
 #include "android/utils/compiler.h"