| #!/bin/bash | |
| # Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| #### CATEGORY=Run, inspect and debug | |
| ### start fuchsia in an emulator | |
| ## usage: fx emu [-a <mode>] [-c <text>] [-N [-I <ifname>]] [-u <path>] [-g <port> [-r <fps>] [-t <cmd>]] [-x <port> [-X <directory>]] [-e <directory>] [-w <size>] [-s <cpus>] [-k <authorized_keys_file>] [-K <kernel_image>] [-z <zbi_image>] [-f <fvm_image>] [--audio] [--headless] [--software-gpu] [--debugger] | |
| ## -a <mode> acceleration mode (auto, off, kvm, hvf, hax), default is auto | |
| ## -c <text> add item to kernel command line | |
| ## -ds <size> extends the fvm image size to <size> bytes. Default is twice the original size | |
| ## -N run with emulated nic via tun/tap | |
| ## -I <ifname> uses the tun/tap interface named ifname | |
| ## -u <path> execute emu if-up script, default is no script | |
| ## -e <directory> location of emulator, defaults to looking in prebuilt/third_party/aemu/PLATFORM | |
| ## -g <port> enable gRPC service on port to control the emulator, default is 5556 when WebRTC service is enabled | |
| ## -r <fps> webrtc frame rate when using gRPC service, default is 30 | |
| ## -t <cmd> execute the given command to obtain turn configuration | |
| ## -x <port> enable WebRTC HTTP service on port | |
| ## -X <directory> location of grpcwebproxy, defaults to looking in prebuilt/third_party/grpcwebproxy | |
| ## -w <size> window size, default is 1280x800 | |
| ## -s <cpus> number of cpus, 1 for uniprocessor, default is 4 | |
| ## -k <authorized_keys_file> SSH authorized keys file, otherwise defaults to //.ssh/authorized_keys | |
| ## -K <kernel_image> path to image to use as kernel, defaults to kernel generated by the current build. | |
| ## -z <zbi_image> path to image to use as ZBI, defaults to zircon-a generated by the current build. | |
| ## -f <fvm_image> path to full FVM image, defaults to image generated by the current build. | |
| ## --audio run with audio hardware added to the virtual machine | |
| ## --headless run in headless mode | |
| ## --host-gpu run with host GPU acceleration | |
| ## --software-gpu run without host GPU acceleration | |
| ## --debugger pause on launch and wait for a debugger process to attach before resuming | |
| set -e | |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/image_build_vars.sh || exit $? | |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/fvm.sh || exit $? | |
| ACCEL="auto" | |
| NET=0 | |
| DEBUGGER=0 | |
| IFNAME="" | |
| AUDIO=0 | |
| HEADLESS=0 | |
| AEMU="emulator" | |
| AEMU_DIR="" | |
| UPSCRIPT=no | |
| WINDOW_SIZE="1280x800" | |
| GRPC= | |
| RTCFPS="30" | |
| TURNCFG="" | |
| GPU="auto" | |
| VULKAN=1 | |
| HTTP=0 | |
| GRPCWEBPROXY_DIR="" | |
| CMDLINE="" | |
| SMP=4 | |
| AUTHORIZED_KEYS="${FUCHSIA_DIR}/.ssh/authorized_keys" | |
| IMAGE_SIZE= | |
| EXPERIMENT_ARM64=false | |
| KERNEL_IMAGE="${FUCHSIA_BUILD_DIR}/${IMAGE_QEMU_KERNEL_RAW}" | |
| ZBI_IMAGE="${FUCHSIA_BUILD_DIR}/${IMAGE_ZIRCONA_ZBI}" | |
| if [[ -n "${IMAGE_FVM_RAW}" ]]; then | |
| FVM_IMAGE="${FUCHSIA_BUILD_DIR}/${IMAGE_FVM_RAW}" | |
| else | |
| FVM_IMAGE="" | |
| fi | |
| case $(uname -m) in | |
| x86_64) | |
| HOST_ARCH=x64 | |
| ;; | |
| aarch64) | |
| HOST_ARCH=arm64 | |
| ;; | |
| *) | |
| fx-error "Unsupported host architecture: $(uname -m)" | |
| exit 1 | |
| esac | |
| if [[ "$FUCHSIA_ARCH" != "$HOST_ARCH" ]]; then | |
| ACCEL=off | |
| fi | |
| # Propagate our TERM environment variable as a kernel command line | |
| # argument. This is first so that an explicit -c TERM=foo argument | |
| # goes into CMDLINE later and overrides this. | |
| if [[ -n $TERM ]]; then | |
| CMDLINE+="TERM=$TERM " | |
| fi | |
| while [[ $# -ge 1 ]]; do | |
| case "$1" in | |
| -h|--help) | |
| fx-command-help | |
| exit 0 | |
| ;; | |
| -a) | |
| shift | |
| ACCEL="$1" | |
| ;; | |
| -c) | |
| shift | |
| CMDLINE+="$1 " | |
| ;; | |
| -ds) | |
| shift | |
| IMAGE_SIZE="$1" | |
| ;; | |
| -N) | |
| NET=1 | |
| ;; | |
| -I) | |
| shift | |
| IFNAME="$1" | |
| ;; | |
| -u) | |
| shift | |
| UPSCRIPT="$1" | |
| ;; | |
| -e) | |
| shift | |
| AEMU_DIR="$1" | |
| ;; | |
| -x) | |
| shift | |
| HTTP="$1" | |
| ;; | |
| -X) | |
| shift | |
| GRPCWEBPROXY_DIR="$1" | |
| ;; | |
| -g) | |
| shift | |
| GRPC="$1" | |
| ;; | |
| -r) | |
| shift | |
| RTCFPS="$1" | |
| ;; | |
| -t) | |
| shift | |
| TURNCFG="$1" | |
| ;; | |
| -w) | |
| shift | |
| WINDOW_SIZE="$1" | |
| ;; | |
| -s) | |
| shift | |
| SMP="$1" | |
| ;; | |
| -k) | |
| shift | |
| AUTHORIZED_KEYS="$1" | |
| ;; | |
| -K) | |
| shift | |
| KERNEL_IMAGE="$1" | |
| ;; | |
| -z) | |
| shift | |
| ZBI_IMAGE="$1" | |
| ;; | |
| -f) | |
| shift | |
| FVM_IMAGE="$1" | |
| ;; | |
| --audio) | |
| AUDIO=1 | |
| ;; | |
| --headless) | |
| HEADLESS=1 | |
| ;; | |
| --debugger) | |
| DEBUGGER=1 | |
| ;; | |
| --host-gpu) | |
| GPU="host" | |
| ;; | |
| --software-gpu) | |
| GPU="swiftshader_indirect" | |
| ;; | |
| --experiment-arm64) | |
| EXPERIMENT_ARM64=true | |
| ;; | |
| *) | |
| break | |
| esac | |
| shift | |
| done | |
| if [[ "$FUCHSIA_ARCH" == "arm64" ]]; then | |
| if ! "$EXPERIMENT_ARM64"; then | |
| fx-error "arm64 support is still experimental, requires --experiment-arm64" | |
| exit 1 | |
| fi | |
| fi | |
| if [[ -z "$AEMU_DIR" && -d "$PREBUILT_AEMU_DIR" ]]; then | |
| AEMU_DIR="$PREBUILT_AEMU_DIR" | |
| fi | |
| if [[ -z "$GRPCWEBPROXY_DIR" && -d "$PREBUILT_GRPCWEBPROXY_DIR" ]]; then | |
| GRPCWEBPROXY_DIR="$PREBUILT_GRPCWEBPROXY_DIR" | |
| fi | |
| # construct the args for aemu | |
| ARGS=() | |
| ARGS+=("-m" "2048") | |
| ARGS+=("-serial" "stdio") | |
| ARGS+=("-vga" "none") | |
| ARGS+=("-device" "virtio-keyboard-pci") | |
| ARGS+=("-device" "virtio_input_multi_touch_pci_1") | |
| if [[ $SMP != 1 ]]; then | |
| ARGS+=("-smp" "${SMP},threads=2") | |
| fi | |
| # TODO(raggi): do we want to use hda on arm64? | |
| if (( AUDIO )); then | |
| ARGS+=("-soundhw" "hda") | |
| fi | |
| FEATURES="VirtioInput,RefCountPipe" | |
| if [[ "$ACCEL" == "auto" ]]; then | |
| if [[ "$(uname -s)" == "Darwin" ]]; then | |
| ACCEL="hvf" | |
| else | |
| ACCEL="kvm" | |
| fi | |
| fi | |
| if [[ "$GPU" == "auto" ]]; then | |
| if [[ "$(uname -s)" == "Darwin" ]]; then | |
| GPU="swiftshader_indirect" | |
| else | |
| GPU="host" | |
| fi | |
| fi | |
| case "$FUCHSIA_ARCH" in | |
| x64) | |
| ARGS+=("-machine" "q35") | |
| ARGS+=("-device" "isa-debug-exit,iobase=0xf4,iosize=0x04") | |
| ;; | |
| arm64) | |
| ARGS+=("-machine" "virt") | |
| ;; | |
| esac | |
| if [[ "$ACCEL" == "kvm" ]]; then | |
| ARGS+=("-enable-kvm" "-cpu" "host,migratable=no,+invtsc") | |
| FEATURES+=",KVM,GLDirectMem" | |
| elif [[ "$ACCEL" == "hvf" ]]; then | |
| ARGS+=("-enable-hvf" "-cpu" "Haswell") | |
| FEATURES+=",HVF,GLDirectMem" | |
| elif [[ "$ACCEL" == "hax" ]]; then | |
| ARGS+=("-enable-hax" "-cpu" "Haswell") | |
| FEATURES+=",HAXM,GLDirectMem" | |
| elif [[ "$ACCEL" == "off" ]]; then | |
| case "$FUCHSIA_ARCH" in | |
| x64) | |
| ARGS+=("-cpu" "Haswell,+smap,-check,-fsgsbase") | |
| ;; | |
| arm64) | |
| ARGS+=("-cpu" "cortex-a53") | |
| ;; | |
| esac | |
| # disable vulkan as not useful without kvm,hvf,hax and support | |
| # for coherent host visible memory. | |
| VULKAN=0 | |
| FEATURES+=",-GLDirectMem" | |
| else | |
| fx-error Unsupported acceleration mode | |
| exit 1 | |
| fi | |
| if (( VULKAN )); then | |
| FEATURES+=",Vulkan" | |
| else | |
| FEATURES+=",-Vulkan" | |
| fi | |
| OPTIONS=() | |
| OPTIONS+=("-feature" "$FEATURES") | |
| OPTIONS+=("-window-size" "$WINDOW_SIZE") | |
| OPTIONS+=("-gpu" "$GPU") | |
| if (( DEBUGGER )); then | |
| OPTIONS+=("-wait-for-debugger") | |
| fi | |
| if (( HEADLESS )); then | |
| OPTIONS+=("-no-window") | |
| fi | |
| # use port 5556 by default | |
| if (( HTTP )); then | |
| GRPC="${GRPC:-5556}" | |
| fi | |
| if (( GRPC )); then | |
| OPTIONS+=("-grpc" "$GRPC") | |
| OPTIONS+=("-rtcfps" "$RTCFPS") | |
| if [[ -n "$TURNCFG" ]]; then | |
| OPTIONS+=("-turncfg" "$TURNCFG") | |
| fi | |
| fi | |
| if (( NET )); then | |
| if [[ "$(uname -s)" == "Darwin" ]]; then | |
| if [[ -z "$IFNAME" ]]; then | |
| IFNAME="tap0" | |
| fi | |
| if [[ ! -c "/dev/$IFNAME" ]]; then | |
| echo "To use emu with networking on macOS, install the tun/tap driver:" | |
| echo "http://tuntaposx.sourceforge.net/download.xhtml" | |
| exit 1 | |
| fi | |
| if [[ ! -w "/dev/$IFNAME" ]]; then | |
| echo "For networking /dev/$IFNAME must be owned by $USER. Please run:" | |
| echo " sudo chown $USER /dev/$IFNAME" | |
| exit 1 | |
| fi | |
| else | |
| if [[ -z "$IFNAME" ]]; then | |
| IFNAME="qemu" | |
| fi | |
| TAP_IFS=$(ip tuntap show 2>/dev/null) | |
| if [[ ! "$TAP_IFS" =~ ${IFNAME}: ]]; then | |
| echo "To use emu with networking on Linux, configure tun/tap:" | |
| echo | |
| echo "sudo ip tuntap add dev $IFNAME mode tap user $USER && \\" | |
| echo "sudo ip link set $IFNAME up" | |
| exit 1 | |
| fi | |
| # Try to detect if a firewall is active. There are only few ways to do that | |
| # without being root. Unfortunately, using systemd or systemctl does not work | |
| # (at least on Debian), so use the following hack instead: | |
| if (command -v ufw && grep -q "^ENABLED=yes" /etc/ufw/ufw.conf) >/dev/null 2>&1; then | |
| fx-warn "Active firewall detected: If this emulator is unreachable, run: fx setup-ufw" | |
| fi | |
| fi | |
| ARGS+=("-netdev" "type=tap,ifname=$IFNAME,script=$UPSCRIPT,downscript=no,id=net0") | |
| HASH=$(echo $IFNAME | shasum) | |
| SUFFIX=$(for i in {0..2}; do echo -n ":${HASH:$(( 2 * i )):2}"; done) | |
| MAC=",mac=52:54:00$SUFFIX" | |
| ARGS+=("-device" "e1000,netdev=net0${MAC}") | |
| else | |
| ARGS+=("-net" "none") | |
| fi | |
| # Construction of a qcow image prevents aemu from writing back to the | |
| # build-produced image file, which could cause timestamp issues with that file. | |
| # Construction of the new ZBI adds //.ssh/authorized_keys for SSH access. | |
| img_dir="$(mktemp -d)" | |
| if [[ ! -d "${img_dir}" ]]; then | |
| fx-error "Failed to create temporary directory" | |
| exit 1 | |
| fi | |
| trap 'rm -Rf "${img_dir}" ; [[ "${GRPCWEBPROXY_PID}" ]] && kill "${GRPCWEBPROXY_PID}"; stty sane' EXIT | |
| KERNEL_ZBI="${img_dir}/fuchsia-ssh.zbi" | |
| fx-zbi -o "${KERNEL_ZBI}" "${ZBI_IMAGE}" \ | |
| --entry "data/ssh/authorized_keys=${AUTHORIZED_KEYS}" | |
| if [[ -n "$FVM_IMAGE" ]]; then | |
| fvmimg="${img_dir}/fvm.blk" | |
| fx-fvm-extend-image "${FVM_IMAGE}" "${fvmimg}" "${IMAGE_SIZE}" | |
| ARGS+=("-drive" "file=${fvmimg},format=raw,if=none,id=vdisk") | |
| ARGS+=("-device" "virtio-blk-pci,drive=vdisk") | |
| fi | |
| # Start gRPC web proxy if HTTP port is set | |
| if (( HTTP )); then | |
| "${GRPCWEBPROXY_DIR}/grpcwebproxy" \ | |
| --backend_addr localhost:"$GRPC" --server_http_debug_port "$HTTP" \ | |
| --backend_tls=false --run_tls_server=false --allow_all_origins & | |
| GRPCWEBPROXY_PID=$! | |
| fi | |
| # construct the kernel cmd line for aemu | |
| CMDLINE+="kernel.serial=legacy " | |
| # Add entropy to the kernel | |
| CMDLINE+="kernel.entropy-mixin=$(head -c 32 /dev/urandom | shasum -a 256 | awk '{ print $1 }') " | |
| # Don't 'reboot' the emulator if the kernel crashes | |
| CMDLINE+="kernel.halt-on-panic=true " | |
| # run aemu | |
| set -x | |
| "${AEMU_DIR}/${AEMU}" "${OPTIONS[@]}" -fuchsia \ | |
| -kernel "${KERNEL_IMAGE}" \ | |
| -initrd "$KERNEL_ZBI" "${ARGS[@]}" -append "$CMDLINE" "$@" |