#!/bin/sh # Copyright (c) 2015-2019 Contributors as noted in the AUTHORS file # # This file is part of Solo5, a sandboxed execution environment. # # Permission to use, copy, modify, and/or distribute this software # for any purpose with or without fee is hereby granted, provided # that the above copyright notice and this permission notice appear # in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. usage () { cat <&2 Usage: solo5-virtio-run [ OPTIONS ] UNIKERNEL [ -- ] [ ARGUMENTS ... ] Launch the Solo5 UNIKERNEL (virtio target). Unikernel output is sent to stdout. Options: -d DISK: Attach virtio-blk device with DISK image file. -m MEM: Start guest with MEM megabytes of memory (default is 128). -n NETIF: Attach virtio-net device with NETIF tap interface. -q: Quiet mode. Don't print hypervisor incantations. -H HV: Use hypervisor HV (default is "best available"). EOM exit 1 } die () { echo solo5-virtio-run: error: "$@" 1>&2 exit 1 } hv_addargs () { if [ -z "${HVCMD}" ]; then HVCMD="$@" else HVCMD="${HVCMD} $@" fi } is_quiet () { [ -n "${QUIET}" ] } # Parse command line arguments. ARGS=$(getopt d:m:n:qH: $*) [ $? -ne 0 ] && usage set -- $ARGS MEM=128 HV=best NETIF= BLKIMG= QUIET= while true; do case "$1" in -d) BLKIMG=$(readlink -f $2) [ -f ${BLKIMG} ] || die "not found: ${BLKIMG}" shift; shift ;; -m) MEM="$2" shift; shift ;; -n) NETIF="$2" # Check dependencies type ip >/dev/null 2>&1 || type ifconfig >/dev/null 2>&1 || die "need ip or ifconfig installed" ip a show ${NETIF} >/dev/null 2>&1 || ifconfig ${NETIF} >/dev/null 2>&1 || die "no such network interface: ${NETIF}" shift; shift ;; -q) QUIET=1 shift ;; -H) HV="$2" shift; shift ;; --) shift; break ;; esac done [ $# -lt 1 ] && usage UNIKERNEL=$(readlink -f $1) [ -n "${UNIKERNEL}" -a -f "${UNIKERNEL}" ] || die "not found: $1}" shift VMNAME=vm$$ if [ "${HV}" = "best" ]; then SYS=$(uname -s) case ${SYS} in Linux) if [ -c /dev/kvm -a -w /dev/kvm ]; then HV=kvm else HV=qemu fi ;; FreeBSD) # XXX How to detect if bhyve is available on this machine? HV=bhyve [ $(id -u) -eq 0 ] || die "Root privileges required for bhyve" type grub-bhyve >/dev/null 2>&1 \ || die "Please install sysutils/grub2-bhyve from ports" ;; *) die "unsupported os: ${SYS}" ;; esac fi case ${HV} in kvm|qemu) hv_addargs qemu-system-x86_64 case ${HV} in kvm) hv_addargs -cpu host -enable-kvm ;; qemu) hv_addargs -cpu Westmere ;; esac hv_addargs -m ${MEM} # Kill all default devices provided by QEMU, we don't need them. hv_addargs -nodefaults -no-acpi # Console. We could use just "-nograhic -vga none", however that MUXes the # QEMU monitor on stdio which requires ^Ax to exit. This makes things look # more like a normal process (quit with ^C), consistent with bhyve and # solo5-hvt. hv_addargs -display none -serial stdio # Network if [ -n "${NETIF}" ]; then hv_addargs -device virtio-net,netdev=n0 hv_addargs -netdev tap,id=n0,ifname=${NETIF},script=no,downscript=no fi # Disk if [ -n "${BLKIMG}" ]; then hv_addargs -drive file=${BLKIMG},if=virtio,format=raw fi # Used by automated tests on QEMU (see kernel/virtio/platform.c). hv_addargs -device isa-debug-exit hv_addargs -kernel ${UNIKERNEL} # QEMU command line parsing is just stupid. ARGS= if [ "$#" -ge 1 ]; then ARGS="$(echo "$@" | sed -e s/,/,,/g)" is_quiet || set -x exec ${HVCMD} -append "${ARGS}" else is_quiet || set -x exec ${HVCMD} fi ;; bhyve) # Load the VM using grub-bhyve. Kill stdout as this is normal GRUB output. (is_quiet || set -x; \ printf -- "multiboot ${UNIKERNEL} placeholder %s\nboot\n" "$*" \ | grub-bhyve -M ${MEM} "${VMNAME}" >/dev/null) \ || die "Could not initialise VM" hv_addargs bhyve hv_addargs -u hv_addargs -m ${MEM} hv_addargs -H -s 0:0,hostbridge -s 1:0,lpc # Console. Bhyve insists on talking to a TTY so fake one in a way that we # can redirect output to a file (see below). TTYA=/dev/nmdm$$A TTYB=/dev/nmdm$$B hv_addargs -l com1,${TTYB} # Network if [ -n "${NETIF}" ]; then hv_addargs -s 2:0,virtio-net,${NETIF} fi # Disk if [ -n "${BLKIMG}" ]; then hv_addargs -s 3:0,virtio-blk,${BLKIMG} fi hv_addargs ${VMNAME} # Fake a console using nmdm(4). Open 'A' end first and keep it open, then # set some sane TTY parameters and launch bhyve on the 'B' end. # Bhyve will respond to interactive SIGINT correctly, the cat and VM itself # are cleaned up below. # XXX Can occasionally leak /dev/nmdm devices, oh well ... # sleep a bit to ensure that cat has opened /dev/nmdm before stty cat ${TTYA} & sleep 0.2 CAT=$! cleanup () { kill ${CAT} >/dev/null 2>&1 bhyvectl --destroy --vm=${VMNAME} >/dev/null 2>&1 } trap cleanup 0 INT TERM stty -f ${TTYB} >/dev/null stty -f ${TTYA} 115200 igncr # Can't do exec here since we need to clean up the 'cat' process. ( is_quiet || set -x; ${HVCMD} ) exit $? ;; best) die "Could not determine hypervisor, try with -H" ;; *) die "Unknown hypervisor: ${HV}" ;; esac