#!/usr/bin/env bash # Copyright 2016 The Fuchsia Authors # # Use of this source code is governed by a MIT-style # license that can be found in the LICENSE file or at # https://opensource.org/licenses/MIT function HELP { echo "help:" echo "-a : arm64, or x64" echo "-b : build first" echo "-c : add item to kernel commandline" echo "-C : use Clang build" echo "-A : use ASan build" echo "-P : use profile build" echo "-L : use LTO build" echo "-l : use ThinLTO build" echo "-d : run with emulated disk" echo "-D : specify disk file or device path on host, default is blk.bin" echo "--disktype[=] : should be one of (ahci, virtio, nvme), default is ahci" echo "--diskfmt[=] : disk format (raw, qcow2, etc), default is raw" echo "-g : use graphical console" echo "-G : use GIC v2 or v3" echo "-I : network interface name, default is qemu." echo "-k : use KVM" echo "-m : memory size, default is ${MEMSIZE_DEFAULT}MB" echo "-n : run with emulated nic" echo "-N : run with emulated nic via tun/tap" echo "-o : build directory" echo "-q : location of qemu, defaults to looking in prebuilt/downloads/qemu/bin, then \$PATH" echo "-r : run release build" echo "-s : number of cpus, 1 for uniprocessor, default is 4" echo "-t : use as the QEMU->ZBI trampoline" echo "-u : execute qemu startUp script, default is no script" echo "-V : try to use virtio devices" echo "-z : boot specified complete ZBI via trampoline" echo "--audio[=] : use Intel HD Audio" echo " : should be one of (alsa, pa, wav, none)" echo "--ahci= : run with disk image file as raw ahci drive" echo "--build-debug : build an image for use with gdb (equivalent to -d to build-zircon)" echo "--debugger : Enable gdb stub and wait for connection" echo "--no-serial : Disable writing out to the guest's serial port" echo "--vnc= : use vnc based display" echo "--wavfile= : When audio host_drv == wav, output to the specified WAV file" echo "-h for help" echo "all arguments after -- are passed to qemu directly" exit 1 } DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" AHCI=() ARCH= ASAN=0 AUDIO= AUDIO_WAVFILE="/tmp/qemu.wav" BUILD=0 CLANG=0 DEBUGGER=0 BUILD_DEBUG=0 DISK=0 DISKFILE="blk.bin" DISKTYPE= DISKFMT="raw" BUILDDIR= GIC=3 GRAPHICS=0 DO_KVM=0 LTO=0 THINLTO=0 PROFILE=0 MEMSIZE_DEFAULT=2048 MEMSIZE=$MEMSIZE_DEFAULT NET=0 QEMUDIR= RELEASE=0 UPSCRIPT=no VNC= VIRTIO=0 SERIAL=1 SMP=4 CMDLINE="" QEMU_KERNEL= QEMU_INITRD= if [[ "$(uname -s)" == "Darwin" ]]; then IFNAME="tap0" else IFNAME="qemu" fi # QEMU looks for its own files in its current directory before looking in its # data directory (.../share/qemu/). So a file in the current directory that # happens to match one of those internal files' names will be used instead of # the proper file and make things go awry. There's no way to tell QEMU not to # look in the current directory first. So to make it safe to have files by any # name in the current directory, we cd to / before running QEMU (on the more # reasonable presumption that / won't contain any files by those names). Hence, # we have to convert any relative file names we're passing to QEMU to absolute. abspath() { local path="$1" case "$path" in /*) echo "$path";; *) echo "`pwd`/$path";; esac } while getopts "Aa:bc:CdD:gG:I:kLlm:nNo:Pq:rs:t::u:Vz:h-:" FLAG; do case $FLAG in A) ASAN=1;; a) ARCH=$OPTARG;; b) BUILD=1;; c) CMDLINE+="$OPTARG ";; C) CLANG=1;; d) DISK=1;; D) DISKFILE="$(abspath "$OPTARG")";; g) GRAPHICS=1;; G) GIC=$OPTARG;; I) IFNAME=$OPTARG;; k) DO_KVM=1;; L) LTO=1;; l) THINLTO=1;; m) MEMSIZE=$OPTARG;; n) NET=1;; N) NET=2;; o) BUILDDIR=$OPTARG;; P) PROFILE=1;; q) QEMUDIR=${OPTARG}/;; r) RELEASE=1;; s) SMP=$OPTARG;; t) QEMU_KERNEL="$(abspath "$OPTARG")";; u) UPSCRIPT="$(abspath "$OPTARG")";; V) VIRTIO=1;; z) QEMU_INITRD="$(abspath "$OPTARG")";; h) HELP;; \?) echo unrecognized option HELP ;; -) case $OPTARG in ahci=*) AHCI+=("$(abspath "${OPTARG#*=}")");; audio) AUDIO=none;; audio=*) AUDIO=${OPTARG#*=};; wavfile=*) AUDIO_WAVFILE="$(abspath "${OPTARG#*=}")";; build-debug) BUILD_DEBUG=1;; debugger) DEBUGGER=1;; disktype=*) DISKTYPE=${OPTARG#*=};; diskfmt=*) DISKFMT=${OPTARG#*=};; no-serial) SERIAL=0;; vnc=*) VNC=${OPTARG#*=};; *) echo unrecognized long option HELP ;; esac ;; esac done shift $((OPTIND-1)) # arch argument is non optional if [[ -z $ARCH ]]; then echo must specify arch HELP fi PROJECT="$ARCH" BUILDDIR_SUFFIX= BUILD_ARGS= if (( $ASAN )); then BUILDDIR_SUFFIX+=-asan BUILD_ARGS+=' -A' elif (( $LTO )); then BUILDDIR_SUFFIX+=-lto BUILD_ARGS+=' -L' elif (( $THINLTO )); then BUILDDIR_SUFFIX+=-thinlto BUILD_ARGS+=' -l' elif (( $PROFILE )); then BUILDDIR_SUFFIX+=-profile BUILD_ARGS+=' -P' elif (( $CLANG )); then BUILDDIR_SUFFIX+=-clang BUILD_ARGS+=' -C' fi if (( $RELEASE )); then BUILDDIR_SUFFIX+=-release BUILD_ARGS+=' -r' fi if (( $BUILD_DEBUG )); then BUILD=1 BUILD_ARGS+=' -d' fi # build the project if asked for if (( $BUILD )); then # DIR is zircon/scripts, we need to make inside zircon. $DIR/build-zircon -a $ARCH $BUILD_ARGS -- -C "$DIR/.." || exit 1 fi # by default use the qemu binary located in the fuchsia buildtools # repo if we can find it, but allow -q to override it for people # who want to use their own. case "$(uname -s)" in Darwin) readonly HOST_PLATFORM="mac-x64" ;; Linux) readonly HOST_PLATFORM="linux-x64" ;; esac if [[ -z $QEMUDIR && -d "$DIR/../prebuilt/downloads/qemu/bin" ]]; then QEMUDIR="$DIR/../prebuilt/downloads/qemu/bin/" fi if [[ -z $BUILDDIR ]]; then BUILDDIR="$(dirname "$DIR")/build-$PROJECT$BUILDDIR_SUFFIX" fi if [[ -z "$QEMU_INITRD" ]]; then QEMU_INITRD="$(abspath "$BUILDDIR/zircon.zbi")" fi if [[ -z "$QEMU_KERNEL" ]]; then case $ARCH in arm64) QEMU_KERNEL="$(abspath "$BUILDDIR/qemu-boot-shim.bin")" ;; x64) QEMU_KERNEL="$(abspath "$BUILDDIR/multiboot.bin")" ;; *) echo >&2 "No QEMU trampoline for $ARCH" exit 2 ;; esac fi if (( $BUILD )); then if [[ -n "$QEMU_INITRD" && "$QEMU_INITRD" != $BUILDDIR/* ]]; then echo >&2 "WARNING: ZBI $QEMU_INITRD not in $BUILDDIR just built" fi if [[ -n "$QEMU_KERNEL" && "$QEMU_KERNEL" != $BUILDDIR/* ]]; then echo >&2 "WARNING: trampoline $QEMU_KERNEL not in $BUILDDIR just built" fi fi # construct the args for qemu ARGS=" -m $MEMSIZE" if [[ -n $VNC ]]; then ARGS+=" -vnc $VNC" fi if (( !$GRAPHICS )); then ARGS+=" -nographic" else ARGS+=" -serial stdio" if [[ "$ARCH" == "x64" && $VIRTIO == 0 ]]; then # Enable Bochs VBE device, which Zircon has a device for ARGS+=" -vga std" else # use the virtio gpu for display ARGS+=" -vga none" ARGS+=" -device virtio-gpu-pci" fi fi if (( $DISK )); then # if disktype wasn't set on the command line, default to ahci unless VIRTIO is set if [[ -z $DISKTYPE ]]; then if (( $VIRTIO )); then DISKTYPE="virtio" else DISKTYPE="ahci" fi fi ARGS+=" -drive file=${DISKFILE},format=${DISKFMT},if=none,id=mydisk" if [[ "$DISKTYPE" == "virtio" ]]; then ARGS+=" -device virtio-blk-pci,drive=mydisk" elif [[ "$DISKTYPE" == "ahci" ]]; then ARGS+=" -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0" elif [[ "$DISKTYPE" == "nvme" ]]; then ARGS+=" -device nvme,drive=mydisk,serial=zircon" else echo unrecognized disk type \"$DISKTYPE\" exit fi fi ahcinum=1 for ahcifile in ${AHCI[@]}; do ARGS+=" -drive file=${ahcifile},format=raw,if=none,id=ahcidisk${ahcinum}" ARGS+=" -device ich9-ahci,id=ahci${ahcinum}" ARGS+=" -device ide-drive,drive=ahcidisk${ahcinum},bus=ahci.${ahcinum}" ahcinum=$((ahcinum + 1)) done if (( !$NET )); then ARGS+=" -net none" fi if [[ $NET == 1 ]]; then ARGS+=" -netdev type=user,hostname=$IFNAME,id=net0" fi if [[ $NET == 2 ]]; then if [[ "$(uname -s)" == "Darwin" ]]; then if [[ ! -c "/dev/$IFNAME" ]]; then echo "To use qemu 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 ARGS+=" -netdev type=tap,ifname=$IFNAME,script=$UPSCRIPT,downscript=no,id=net0" else CHECK=$(tunctl -b -u $USER -t $IFNAME 2>/dev/null) if [[ "$CHECK" != "$IFNAME" ]]; then echo "To use qemu with networking on Linux, configure tun/tap:" if [[ ! -x "/usr/sbin/tunctl" ]]; then echo "sudo apt-get install uml-utilities" fi echo "sudo tunctl -u $USER -t $IFNAME" echo "sudo ifconfig $IFNAME up" exit 1 fi ARGS+=" -netdev type=tap,ifname=$IFNAME,script=$UPSCRIPT,downscript=no,id=net0" fi fi if (( $NET )); then MAC="" if [[ $NET == 2 ]]; then HASH=$(echo $IFNAME | shasum) SUFFIX=$(for i in {0..2}; do echo -n :${HASH:$(( 2 * $i )):2}; done) MAC=",mac=52:54:00$SUFFIX" fi if [[ "$ARCH" == "x64" ]] && [[ $VIRTIO == 0 ]]; then ARGS+=" -device e1000,netdev=net0${MAC}" else ARGS+=" -device virtio-net-pci,netdev=net0${MAC}" fi fi if [[ -n $AUDIO ]]; then ARGS+=" -soundhw hda" export QEMU_AUDIO_DRV=$AUDIO export QEMU_AUDIO_DAC_FIXED_FREQ=48000 export QEMU_AUDIO_TIMER_PERIOD=20 case $AUDIO in none) ;; alsa) ;; pa) ;; wav) export QEMU_WAV_FREQUENCY=48000 export QEMU_WAV_PATH=${AUDIO_WAVFILE} ;; *) echo unrecognized QEMU host audio driver \"$AUDIO\" exit ;; esac fi if [[ $SMP != 1 ]]; then ARGS+=" -smp $SMP" if [[ "$ARCH" == "x64" ]]; then ARGS+=",threads=2" fi fi # start a few extra harmless virtio devices that can be ignored if (( $VIRTIO )); then ARGS+=" -device virtio-serial-pci" ARGS+=" -device virtio-rng-pci" ARGS+=" -device virtio-mouse-pci" ARGS+=" -device virtio-keyboard-pci" fi if (( $DEBUGGER )); then ARGS+=" -s -S" fi case $ARCH in arm64) QEMU=${QEMUDIR}qemu-system-aarch64 if (( $DO_KVM )); then ARGS+=" -enable-kvm -cpu host" GIC=host else ARGS+=" -machine virtualization=true -cpu cortex-a53" fi ARGS+=" -machine virt" # append a gic version to the machine specifier if [[ $GIC != 0 ]]; then ARGS+=",gic_version=${GIC}" fi if (( !$SERIAL )); then CMDLINE+="kernel.serial=none " fi ;; x64) QEMU=${QEMUDIR}qemu-system-x86_64 ARGS+=" -machine q35" ARGS+=" -device isa-debug-exit,iobase=0xf4,iosize=0x04" if (( $DO_KVM )); then ARGS+=" -enable-kvm -cpu host,migratable=no,+invtsc" else ARGS+=" -cpu Haswell,+smap,-check,-fsgsbase" fi if (( $SERIAL )); then CMDLINE+="kernel.serial=legacy " else CMDLINE+="kernel.serial=none " fi ;; *) echo unsupported arch HELP ;; esac # Propagate our TERM environment variable as a kernel command line # argument. This is last so that an explicit -c TERM=foo argument # goes into CMDLINE first. Kernel command line words become environment # variables, and the first variable in the list wins for getenv calls. if [[ -n $TERM ]]; then CMDLINE+="TERM=$TERM " fi # 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 " CMDLINE="`echo "$CMDLINE" | sed 's/,/,,/g'`" # run qemu echo CMDLINE: $CMDLINE cd / set -x exec $QEMU -kernel "$QEMU_KERNEL" -initrd "$QEMU_INITRD" \ $ARGS -append "$CMDLINE" "$@"