blob: 9eac1552bd37ca1f9fb05df0de5aa98970aa1083 [file] [log] [blame]
#!/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: linux: no script, macos: tap ifup 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
DEVSHELL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
source "${DEVSHELL_DIR}"/lib/image_build_vars.sh || exit $?
source "${DEVSHELL_DIR}"/lib/fvm.sh || exit $?
ACCEL="auto"
NET=0
DEBUGGER=0
IFNAME=""
AUDIO=0
HEADLESS=0
AEMU="emulator"
AEMU_DIR=""
UPSCRIPT=
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 [[ -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
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
if [[ ! -w "/dev/kvm" ]]; then
echo "To use KVM acceleration, adjust permissions to /dev/kvm using:"
echo
echo "sudo chmod 666 /dev/kvm"
exit 1
fi
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
if [[ "$FUCHSIA_ARCH" == "arm64" ]]; then
if ! "$EXPERIMENT_ARM64"; then
fx-error "arm64 support is still experimental, requires --experiment-arm64"
exit 1
fi
OPTIONS+=("-avd-arch" "arm64")
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 [[ "$IFNAME" != "fakenetwork" && ! -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 [[ "$IFNAME" != "fakenetwork" && ! -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
if [[ -z "${UPSCRIPT}" ]]; then
echo "sudo follows to configure the tap interface:"
UPSCRIPT="${DEVSHELL_DIR}"/lib/emu-ifup-macos.sh
fi
else
if [[ -z "$IFNAME" ]]; then
IFNAME="qemu"
fi
TAP_IFS=$(ip tuntap show 2>/dev/null)
if [[ "$IFNAME" != "fakenetwork" && ! "$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
if [[ -z "${UPSCRIPT}" ]]; then
UPSCRIPT=no
fi
fi
if [[ -n "${UPSCRIPT}" && "${UPSCRIPT}" != "no" && ! -x "${UPSCRIPT}" ]]; then
echo "The upscript ${UPSCRIPT} is not a valid executable"
exit 1
fi
ARGS+=("-netdev" "type=tap,ifname=$IFNAME,id=net0${UPSCRIPT:+,script=${UPSCRIPT}}")
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
# Check for X11 on Linux
if [[ "$(uname -s)" == "Linux" ]] && (( ! HEADLESS )); then
if [[ -z "$DISPLAY" ]]; then
echo "To use emulator on Linux, start X, or use a virtual framebuffer:"
echo
echo "xvfb-run fx emu"
exit 1
else
if [[ "$DISPLAY" != "fakedisplay" ]] && ! xset q >/dev/null 2>&1; then
echo "clients must be allowed to connect to DISPLAY. Please run:"
echo
echo "DISPLAY=$DISPLAY XAUTHORITY=/var/run/lightdm/root/$DISPLAY sudo xhost +"
exit 1
fi
fi
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" "$@"