| #!/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 <arch> : arm64, or x64" |
| echo "-b : build first" |
| echo "-c <text> : 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 <disk file|device>: specify disk file or device path on host, default is blk.bin" |
| echo "--disktype[=<type>] : should be one of (ahci, virtio, nvme), default is ahci" |
| echo "--diskfmt[=<format>] : disk format (raw, qcow2, etc), default is raw" |
| echo "-g : use graphical console" |
| echo "-G <version> : use GIC v2 or v3" |
| echo "-I <interface name> : network interface name, default is qemu." |
| echo "-k : use KVM" |
| echo "-m <memory in MB> : memory size, default is ${MEMSIZE_DEFAULT}MB" |
| echo "-n : run with emulated nic" |
| echo "-N : run with emulated nic via tun/tap" |
| echo "-o <dir> : build directory" |
| echo "-q <directory> : location of qemu, defaults to looking in prebuilt/downloads/qemu/bin, then \$PATH" |
| echo "-r : run release build" |
| echo "-s <number of cpus> : number of cpus, 1 for uniprocessor, default is 4" |
| echo "-t <binary> : use <binary> as the QEMU->ZBI trampoline" |
| echo "-u <path> : execute qemu startUp script, default is no script" |
| echo "-V : try to use virtio devices" |
| echo "-x <bootdata> : DEPRECATED: use specified bootdata" |
| echo "-z <zbi> : boot specified complete ZBI via trampoline" |
| echo "--audio[=<host_drv>] : use Intel HD Audio" |
| echo " : <host_drv> should be one of (alsa, pa, wav, none)" |
| echo "--ahci=<disk image> : 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=<display> : use vnc based display" |
| echo "--wavfile=<file> : 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 |
| DEPRECATED_INITRD= |
| 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:Vx:z: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;; |
| x) DEPRECATED_INITRD="$(abspath "$OPTARG")";; |
| 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 [[ -n "$QEMU_INITRD" ]]; then |
| if [[ -n "$DEPRECATED_INITRD" ]]; then |
| echo >&2 "ZBI booting with -z is incompatible with deprecated -x" |
| exit 2 |
| fi |
| elif [[ -z "$DEPRECATED_INITRD" ]]; then |
| QEMU_INITRD="$(abspath "$BUILDDIR/zircon.zbi")" |
| fi |
| |
| if [[ -n "$QEMU_INITRD" && -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 |
| DEPRECATED_KERNEL="$BUILDDIR/qemu-zircon.bin" |
| 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 |
| DEPRECATED_KERNEL="$BUILDDIR/zircon.bin" |
| 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 |
| |
| if [[ -n "$DEPRECATED_INITRD" ]]; then |
| QEMU_KERNEL="$(abspath "$DEPRECATED_KERNEL")" |
| QEMU_INITRD="$(abspath "$DEPRECATED_INITRD")" |
| fi |
| |
| # 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" "$@" |