1#!/bin/sh 2# 3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4# 5# Copyright (c) 2013 NetApp, Inc. 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27# SUCH DAMAGE. 28# 29# $FreeBSD$ 30# 31 32LOADER=/usr/sbin/bhyveload 33BHYVECTL=/usr/sbin/bhyvectl 34FBSDRUN=/usr/sbin/bhyve 35 36DEFAULT_MEMSIZE=512M 37DEFAULT_CPUS=2 38DEFAULT_TAPDEV=tap0 39DEFAULT_CONSOLE=stdio 40 41DEFAULT_NIC=virtio-net 42DEFAULT_DISK=virtio-blk 43DEFAULT_VIRTIO_DISK="./diskdev" 44DEFAULT_ISOFILE="./release.iso" 45 46DEFAULT_VNCHOST="127.0.0.1" 47DEFAULT_VNCPORT=5900 48DEFAULT_VNCSIZE="w=1024,h=768" 49 50errmsg() { 51 echo "*** $1" 52} 53 54usage() { 55 local msg=$1 56 57 echo "Usage: vmrun.sh [-aAEhiTv] [-c <CPUs>] [-C <console>]" \ 58 "[-d <disk file>]" 59 echo " [-e <name=value>] [-f <path of firmware>]" \ 60 "[-F <size>]" 61 echo " [-g <gdbport> ] [-H <directory>]" 62 echo " [-I <location of installation iso>] [-l <loader>]" 63 echo " [-L <VNC IP for UEFI framebuffer>]" 64 echo " [-m <memsize>]" \ 65 "[-n <network adapter emulation type>]" 66 echo " [-P <port>] [-t <tapdev>] <vmname>" 67 echo "" 68 echo " -h: display this help message" 69 echo " -a: force memory mapped local APIC access" 70 echo " -A: use AHCI disk emulation instead of ${DEFAULT_DISK}" 71 echo " -c: number of virtual cpus (default: ${DEFAULT_CPUS})" 72 echo " -C: console device (default: ${DEFAULT_CONSOLE})" 73 echo " -d: virtio diskdev file (default: ${DEFAULT_VIRTIO_DISK})" 74 echo " -e: set FreeBSD loader environment variable" 75 echo " -E: Use UEFI mode" 76 echo " -f: Use a specific UEFI firmware" 77 echo " -F: Use a custom UEFI GOP framebuffer size" \ 78 "(default: ${DEFAULT_VNCSIZE})" 79 echo " -g: listen for connection from kgdb at <gdbport>" 80 echo " -H: host filesystem to export to the loader" 81 echo " -i: force boot of the Installation CDROM image" 82 echo " -I: Installation CDROM image location" \ 83 "(default: ${DEFAULT_ISOFILE})" 84 echo " -l: the OS loader to use (default: /boot/userboot.so)" 85 echo " -L: IP address for UEFI GOP VNC server" \ 86 "(default: ${DEFAULT_VNCHOST}" 87 echo " -m: memory size (default: ${DEFAULT_MEMSIZE})" 88 echo " -n: network adapter emulation type" \ 89 "(default: ${DEFAULT_NIC})" 90 echo " -p: pass-through a host PCI device at bus/slot/func" \ 91 "(e.g. 10/0/0)" 92 echo " -P: UEFI GOP VNC port (default: ${DEFAULT_VNCPORT})" 93 echo " -t: tap device for virtio-net (default: $DEFAULT_TAPDEV)" 94 echo " -T: Enable tablet device (for UEFI GOP)" 95 echo " -u: RTC keeps UTC time" 96 echo " -v: Wait for VNC client connection before booting VM" 97 echo " -w: ignore unimplemented MSRs" 98 echo "" 99 [ -n "$msg" ] && errmsg "$msg" 100 exit 1 101} 102 103if [ `id -u` -ne 0 ]; then 104 errmsg "This script must be executed with superuser privileges" 105 exit 1 106fi 107 108kldstat -n vmm > /dev/null 2>&1 109if [ $? -ne 0 ]; then 110 errmsg "vmm.ko is not loaded" 111 exit 1 112fi 113 114force_install=0 115isofile=${DEFAULT_ISOFILE} 116memsize=${DEFAULT_MEMSIZE} 117console=${DEFAULT_CONSOLE} 118cpus=${DEFAULT_CPUS} 119nic=${DEFAULT_NIC} 120tap_total=0 121disk_total=0 122disk_emulation=${DEFAULT_DISK} 123gdbport=0 124loader_opt="" 125bhyverun_opt="-H -A -P" 126pass_total=0 127 128# EFI-specific options 129efi_mode=0 130efi_firmware="/usr/local/share/uefi-firmware/BHYVE_UEFI.fd" 131vncwait="" 132vnchost=${DEFAULT_VNCHOST} 133vncport=${DEFAULT_VNCPORT} 134vncsize=${DEFAULT_VNCSIZE} 135tablet="" 136 137while getopts aAc:C:d:e:Ef:F:g:hH:iI:l:L:m:n:p:P:t:Tuvw c ; do 138 case $c in 139 a) 140 bhyverun_opt="${bhyverun_opt} -a" 141 ;; 142 A) 143 disk_emulation="ahci-hd" 144 ;; 145 c) 146 cpus=${OPTARG} 147 ;; 148 C) 149 console=${OPTARG} 150 ;; 151 d) 152 disk_dev=${OPTARG%%,*} 153 disk_opts=${OPTARG#${disk_dev}} 154 eval "disk_dev${disk_total}=\"${disk_dev}\"" 155 eval "disk_opts${disk_total}=\"${disk_opts}\"" 156 disk_total=$(($disk_total + 1)) 157 ;; 158 e) 159 loader_opt="${loader_opt} -e ${OPTARG}" 160 ;; 161 E) 162 efi_mode=1 163 ;; 164 f) 165 efi_firmware="${OPTARG}" 166 ;; 167 F) 168 vncsize="${OPTARG}" 169 ;; 170 g) 171 gdbport=${OPTARG} 172 ;; 173 H) 174 host_base=`realpath ${OPTARG}` 175 ;; 176 i) 177 force_install=1 178 ;; 179 I) 180 isofile=${OPTARG} 181 ;; 182 l) 183 loader_opt="${loader_opt} -l ${OPTARG}" 184 ;; 185 L) 186 vnchost="${OPTARG}" 187 ;; 188 m) 189 memsize=${OPTARG} 190 ;; 191 n) 192 nic=${OPTARG} 193 ;; 194 p) 195 eval "pass_dev${pass_total}=\"${OPTARG}\"" 196 pass_total=$(($pass_total + 1)) 197 ;; 198 P) 199 vncport="${OPTARG}" 200 ;; 201 t) 202 eval "tap_dev${tap_total}=\"${OPTARG}\"" 203 tap_total=$(($tap_total + 1)) 204 ;; 205 T) 206 tablet="-s 30,xhci,tablet" 207 ;; 208 u) 209 bhyverun_opt="${bhyverun_opt} -u" 210 ;; 211 v) 212 vncwait=",wait" 213 ;; 214 w) 215 bhyverun_opt="${bhyverun_opt} -w" 216 ;; 217 *) 218 usage 219 ;; 220 esac 221done 222 223if [ $tap_total -eq 0 ] ; then 224 tap_total=1 225 tap_dev0="${DEFAULT_TAPDEV}" 226fi 227if [ $disk_total -eq 0 ] ; then 228 disk_total=1 229 disk_dev0="${DEFAULT_VIRTIO_DISK}" 230 231fi 232 233shift $((${OPTIND} - 1)) 234 235if [ $# -ne 1 ]; then 236 usage "virtual machine name not specified" 237fi 238 239vmname="$1" 240if [ -n "${host_base}" ]; then 241 loader_opt="${loader_opt} -h ${host_base}" 242fi 243 244# If PCI passthru devices are configured then guest memory must be wired 245if [ ${pass_total} -gt 0 ]; then 246 loader_opt="${loader_opt} -S" 247 bhyverun_opt="${bhyverun_opt} -S" 248fi 249 250if [ ${efi_mode} -gt 0 ]; then 251 if [ ! -f ${efi_firmware} ]; then 252 echo "Error: EFI Firmware ${efi_firmware} doesn't exist." \ 253 "Try: pkg install uefi-edk2-bhyve" 254 exit 1 255 fi 256fi 257 258make_and_check_diskdev() 259{ 260 local virtio_diskdev="$1" 261 # Create the virtio diskdev file if needed 262 if [ ! -e ${virtio_diskdev} ]; then 263 echo "virtio disk device file \"${virtio_diskdev}\" does not exist." 264 echo "Creating it ..." 265 truncate -s 8G ${virtio_diskdev} > /dev/null 266 fi 267 268 if [ ! -r ${virtio_diskdev} ]; then 269 echo "virtio disk device file \"${virtio_diskdev}\" is not readable" 270 exit 1 271 fi 272 273 if [ ! -w ${virtio_diskdev} ]; then 274 echo "virtio disk device file \"${virtio_diskdev}\" is not writable" 275 exit 1 276 fi 277} 278 279echo "Launching virtual machine \"$vmname\" ..." 280 281first_diskdev="$disk_dev0" 282 283${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1 284 285while [ 1 ]; do 286 287 file -s ${first_diskdev} | grep "boot sector" > /dev/null 288 rc=$? 289 if [ $rc -ne 0 ]; then 290 file -s ${first_diskdev} | \ 291 grep ": Unix Fast File sys" > /dev/null 292 rc=$? 293 fi 294 if [ $rc -ne 0 ]; then 295 need_install=1 296 else 297 need_install=0 298 fi 299 300 if [ $force_install -eq 1 -o $need_install -eq 1 ]; then 301 if [ ! -r ${isofile} ]; then 302 echo -n "Installation CDROM image \"${isofile}\" " 303 echo "is not readable" 304 exit 1 305 fi 306 BOOTDISKS="-d ${isofile}" 307 installer_opt="-s 31:0,ahci-cd,${isofile}" 308 else 309 BOOTDISKS="" 310 i=0 311 while [ $i -lt $disk_total ] ; do 312 eval "disk=\$disk_dev${i}" 313 if [ -r ${disk} ] ; then 314 BOOTDISKS="$BOOTDISKS -d ${disk} " 315 fi 316 i=$(($i + 1)) 317 done 318 installer_opt="" 319 fi 320 321 if [ ${efi_mode} -eq 0 ]; then 322 ${LOADER} -c ${console} -m ${memsize} ${BOOTDISKS} \ 323 ${loader_opt} ${vmname} 324 bhyve_exit=$? 325 if [ $bhyve_exit -ne 0 ]; then 326 break 327 fi 328 fi 329 330 # 331 # Build up args for additional tap and disk devices now. 332 # 333 nextslot=2 # slot 0 is hostbridge, slot 1 is lpc 334 devargs="" # accumulate disk/tap args here 335 i=0 336 while [ $i -lt $tap_total ] ; do 337 eval "tapname=\$tap_dev${i}" 338 devargs="$devargs -s $nextslot:0,${nic},${tapname} " 339 nextslot=$(($nextslot + 1)) 340 i=$(($i + 1)) 341 done 342 343 i=0 344 while [ $i -lt $disk_total ] ; do 345 eval "disk=\$disk_dev${i}" 346 eval "opts=\$disk_opts${i}" 347 make_and_check_diskdev "${disk}" 348 devargs="$devargs -s $nextslot:0,$disk_emulation,${disk}${opts} " 349 nextslot=$(($nextslot + 1)) 350 i=$(($i + 1)) 351 done 352 353 i=0 354 while [ $i -lt $pass_total ] ; do 355 eval "pass=\$pass_dev${i}" 356 devargs="$devargs -s $nextslot:0,passthru,${pass} " 357 nextslot=$(($nextslot + 1)) 358 i=$(($i + 1)) 359 done 360 361 efiargs="" 362 if [ ${efi_mode} -gt 0 ]; then 363 efiargs="-s 29,fbuf,tcp=${vnchost}:${vncport}," 364 efiargs="${efiargs}${vncsize}${vncwait}" 365 efiargs="${efiargs} -l bootrom,${efi_firmware}" 366 efiargs="${efiargs} ${tablet}" 367 fi 368 369 ${FBSDRUN} -c ${cpus} -m ${memsize} ${bhyverun_opt} \ 370 -g ${gdbport} \ 371 -s 0:0,hostbridge \ 372 -s 1:0,lpc \ 373 ${efiargs} \ 374 ${devargs} \ 375 -l com1,${console} \ 376 ${installer_opt} \ 377 ${vmname} 378 379 bhyve_exit=$? 380 # bhyve returns the following status codes: 381 # 0 - VM has been reset 382 # 1 - VM has been powered off 383 # 2 - VM has been halted 384 # 3 - VM generated a triple fault 385 # all other non-zero status codes are errors 386 # 387 if [ $bhyve_exit -ne 0 ]; then 388 break 389 fi 390done 391 392 393case $bhyve_exit in 394 0|1|2) 395 # Cleanup /dev/vmm entry when bhyve did not exit 396 # due to an error. 397 ${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1 398 ;; 399esac 400 401exit $bhyve_exit 402