1#!/bin/sh 2# 3# SPDX-License-Identifier: BSD-2-Clause 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# 30 31LOADER=/usr/sbin/bhyveload 32BHYVECTL=/usr/sbin/bhyvectl 33FBSDRUN=/usr/sbin/bhyve 34 35DEFAULT_MEMSIZE=512M 36DEFAULT_CPUS=2 37DEFAULT_TAPDEV=tap0 38DEFAULT_CONSOLE=stdio 39 40DEFAULT_NIC=virtio-net 41DEFAULT_DISK=virtio-blk 42DEFAULT_VIRTIO_DISK="./diskdev" 43DEFAULT_ISOFILE="./release.iso" 44 45DEFAULT_VNCHOST="127.0.0.1" 46DEFAULT_VNCPORT=5900 47DEFAULT_VNCSIZE="w=1024,h=768" 48 49errmsg() { 50 echo "*** $1" 51} 52 53usage() { 54 local msg=$1 55 56 echo "Usage: vmrun.sh [-aAEhiTuvw] [-c <CPUs>] [-C <console>]" \ 57 "[-d <disk file>]" 58 echo " [-e <name=value>] [-f <path of firmware>]" \ 59 "[-F <size>]" 60 echo " [-G [w][address:]port] [-H <directory>]" 61 echo " [-I <location of installation iso>] [-l <loader>]" 62 echo " [-L <VNC IP for UEFI framebuffer>]" 63 echo " [-m <memsize>]" \ 64 "[-n <network adapter emulation type>]" 65 echo " [-p <pcidev|bus/slot/func>]" 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 (amd64 only)" 76 echo " -f: Use a specific boot firmware (e.g., EDK2, U-Boot)" 77 echo " -F: Use a custom UEFI GOP framebuffer size" \ 78 "(default: ${DEFAULT_VNCSIZE}) (amd64 only)" 79 echo " -G: bind the GDB stub to the specified address" 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) (amd64 only)" 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 (e.g ppt0 or" \ 91 "bus/slot/func) (amd64 only)" 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) (amd64 only)" 95 echo " -u: RTC keeps UTC time" 96 echo " -v: Wait for VNC client connection before booting VM" 97 echo " -w: ignore unimplemented MSRs (amd64 only)" 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 114platform=$(uname -m) 115if [ "${platform}" != amd64 -a "${platform}" != arm64 ]; then 116 errmsg "This script is only supported on amd64 and arm64 platforms" 117 exit 1 118fi 119 120force_install=0 121isofile=${DEFAULT_ISOFILE} 122memsize=${DEFAULT_MEMSIZE} 123console=${DEFAULT_CONSOLE} 124cpus=${DEFAULT_CPUS} 125nic=${DEFAULT_NIC} 126tap_total=0 127disk_total=0 128disk_emulation=${DEFAULT_DISK} 129loader_opt="" 130pass_total=0 131 132# EFI-specific options 133efi_mode=0 134efi_firmware="/usr/local/share/uefi-firmware/BHYVE_UEFI.fd" 135vncwait="" 136vnchost=${DEFAULT_VNCHOST} 137vncport=${DEFAULT_VNCPORT} 138vncsize=${DEFAULT_VNCSIZE} 139tablet="" 140 141# arm64 only 142uboot_firmware="/usr/local/share/u-boot/u-boot-bhyve-arm64/u-boot.bin" 143 144case ${platform} in 145amd64) 146 bhyverun_opt="-H -P" 147 opts="aAc:C:d:e:Ef:F:G:hH:iI:l:L:m:n:p:P:t:Tuvw" 148 ;; 149arm64) 150 bhyverun_opt="" 151 opts="aAc:C:d:e:f:F:G:hH:iI:L:m:n:P:t:uv" 152 ;; 153esac 154 155while getopts $opts c ; do 156 case $c in 157 a) 158 bhyverun_opt="${bhyverun_opt} -a" 159 ;; 160 A) 161 disk_emulation="ahci-hd" 162 ;; 163 c) 164 cpus=${OPTARG} 165 ;; 166 C) 167 console=${OPTARG} 168 ;; 169 d) 170 disk_dev=${OPTARG%%,*} 171 disk_opts=${OPTARG#${disk_dev}} 172 eval "disk_dev${disk_total}=\"${disk_dev}\"" 173 eval "disk_opts${disk_total}=\"${disk_opts}\"" 174 disk_total=$(($disk_total + 1)) 175 ;; 176 e) 177 loader_opt="${loader_opt} -e ${OPTARG}" 178 ;; 179 E) 180 efi_mode=1 181 ;; 182 f) 183 firmware="${OPTARG}" 184 ;; 185 F) 186 vncsize="${OPTARG}" 187 ;; 188 G) 189 bhyverun_opt="${bhyverun_opt} -G ${OPTARG}" 190 ;; 191 H) 192 host_base=`realpath ${OPTARG}` 193 ;; 194 i) 195 force_install=1 196 ;; 197 I) 198 isofile=${OPTARG} 199 ;; 200 l) 201 loader_opt="${loader_opt} -l ${OPTARG}" 202 ;; 203 L) 204 vnchost="${OPTARG}" 205 ;; 206 m) 207 memsize=${OPTARG} 208 ;; 209 n) 210 nic=${OPTARG} 211 ;; 212 p) 213 eval "pass_dev${pass_total}=\"${OPTARG}\"" 214 pass_total=$(($pass_total + 1)) 215 ;; 216 P) 217 vncport="${OPTARG}" 218 ;; 219 t) 220 eval "tap_dev${tap_total}=\"${OPTARG}\"" 221 tap_total=$(($tap_total + 1)) 222 ;; 223 T) 224 tablet="-s 30,xhci,tablet" 225 ;; 226 u) 227 bhyverun_opt="${bhyverun_opt} -u" 228 ;; 229 v) 230 vncwait=",wait" 231 ;; 232 w) 233 bhyverun_opt="${bhyverun_opt} -w" 234 ;; 235 *) 236 usage 237 ;; 238 esac 239done 240 241if [ $tap_total -eq 0 ] ; then 242 tap_total=1 243 tap_dev0="${DEFAULT_TAPDEV}" 244fi 245if [ $disk_total -eq 0 ] ; then 246 disk_total=1 247 disk_dev0="${DEFAULT_VIRTIO_DISK}" 248 249fi 250 251shift $((${OPTIND} - 1)) 252 253if [ $# -ne 1 ]; then 254 usage "virtual machine name not specified" 255fi 256 257vmname="$1" 258if [ -n "${host_base}" ]; then 259 loader_opt="${loader_opt} -h ${host_base}" 260fi 261 262# If PCI passthru devices are configured then guest memory must be wired 263if [ ${pass_total} -gt 0 ]; then 264 loader_opt="${loader_opt} -S" 265 bhyverun_opt="${bhyverun_opt} -S" 266fi 267 268if [ -z "$firmware" ]; then 269 case ${platform} in 270 amd64) 271 firmware="${efi_firmware}" 272 firmware_pkg="edk2-bhyve" 273 ;; 274 arm64) 275 firmware="${uboot_firmware}" 276 firmware_pkg="u-boot-bhyve-arm64" 277 ;; 278 esac 279fi 280 281if [ -n "${firmware}" -a ! -f "${firmware}" ]; then 282 echo "Error: Firmware file ${firmware} doesn't exist." 283 if [ -n "${firmware_pkg}" ]; then 284 echo " Try: pkg install ${firmware_pkg}" 285 fi 286 exit 1 287fi 288 289make_and_check_diskdev() 290{ 291 local virtio_diskdev="$1" 292 # Create the virtio diskdev file if needed 293 if [ ! -e ${virtio_diskdev} ]; then 294 echo "virtio disk device file \"${virtio_diskdev}\" does not exist." 295 echo "Creating it ..." 296 truncate -s 8G ${virtio_diskdev} > /dev/null 297 fi 298 299 if [ ! -r ${virtio_diskdev} ]; then 300 echo "virtio disk device file \"${virtio_diskdev}\" is not readable" 301 exit 1 302 fi 303 304 if [ ! -w ${virtio_diskdev} ]; then 305 echo "virtio disk device file \"${virtio_diskdev}\" is not writable" 306 exit 1 307 fi 308} 309 310echo "Launching virtual machine \"$vmname\" ..." 311 312first_diskdev="$disk_dev0" 313 314${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1 315 316while [ 1 ]; do 317 318 file -s ${first_diskdev} | grep "boot sector" > /dev/null 319 rc=$? 320 if [ $rc -ne 0 ]; then 321 file -s ${first_diskdev} | \ 322 grep ": Unix Fast File sys" > /dev/null 323 rc=$? 324 fi 325 if [ $rc -ne 0 ]; then 326 need_install=1 327 else 328 need_install=0 329 fi 330 331 if [ $force_install -eq 1 -o $need_install -eq 1 ]; then 332 if [ ! -r ${isofile} ]; then 333 echo -n "Installation CDROM image \"${isofile}\" " 334 echo "is not readable" 335 exit 1 336 fi 337 BOOTDISKS="-d ${isofile}" 338 installer_opt="-s 31:0,ahci-cd,${isofile}" 339 else 340 BOOTDISKS="" 341 i=0 342 while [ $i -lt $disk_total ] ; do 343 eval "disk=\$disk_dev${i}" 344 if [ -r ${disk} ] ; then 345 BOOTDISKS="$BOOTDISKS -d ${disk} " 346 fi 347 i=$(($i + 1)) 348 done 349 installer_opt="" 350 fi 351 352 if [ ${platform} = amd64 -a ${efi_mode} -eq 0 ]; then 353 ${LOADER} -c ${console} -m ${memsize} ${BOOTDISKS} \ 354 ${loader_opt} ${vmname} 355 bhyve_exit=$? 356 if [ $bhyve_exit -ne 0 ]; then 357 break 358 fi 359 fi 360 361 # 362 # Build up args for additional tap and disk devices now. 363 # 364 devargs="-s 0:0,hostbridge" # accumulate disk/tap args here 365 case ${platform} in 366 amd64) 367 console_opt="-l com1,${console}" 368 devargs="$devargs -s 1:0,lpc " 369 nextslot=2 # slot 0 is hostbridge, slot 1 is lpc 370 ;; 371 arm64) 372 console_opt="-o console=${console}" 373 devargs="$devargs -o bootrom=${firmware} " 374 nextslot=1 # slot 0 is hostbridge 375 ;; 376 esac 377 378 i=0 379 while [ $i -lt $disk_total ] ; do 380 eval "disk=\$disk_dev${i}" 381 eval "opts=\$disk_opts${i}" 382 make_and_check_diskdev "${disk}" 383 devargs="$devargs -s $nextslot:0,$disk_emulation,${disk}${opts} " 384 nextslot=$(($nextslot + 1)) 385 i=$(($i + 1)) 386 done 387 388 i=0 389 while [ $i -lt $tap_total ] ; do 390 eval "tapname=\$tap_dev${i}" 391 devargs="$devargs -s $nextslot:0,${nic},${tapname} " 392 nextslot=$(($nextslot + 1)) 393 i=$(($i + 1)) 394 done 395 396 i=0 397 while [ $i -lt $pass_total ] ; do 398 eval "pass=\$pass_dev${i}" 399 bsfform="$(echo "${pass}" | grep "^[0-9]\+/[0-9]\+/[0-9]\+$")" 400 if [ -z "${bsfform}" ]; then 401 bsf="$(pciconf -l "${pass}" 2>/dev/null)" 402 if [ $? -ne 0 ]; then 403 errmsg "${pass} is not a host PCI device" 404 exit 1 405 fi 406 bsf="$(echo "${bsf}" | awk -F: '{print $2"/"$3"/"$4}')" 407 else 408 bsf="${pass}" 409 fi 410 devargs="$devargs -s $nextslot:0,passthru,${bsf} " 411 nextslot=$(($nextslot + 1)) 412 i=$(($i + 1)) 413 done 414 415 efiargs="" 416 if [ ${efi_mode} -gt 0 ]; then 417 efiargs="-s 29,fbuf,tcp=${vnchost}:${vncport}," 418 efiargs="${efiargs}${vncsize}${vncwait}" 419 efiargs="${efiargs} -l bootrom,${firmware}" 420 efiargs="${efiargs} ${tablet}" 421 fi 422 423 ${FBSDRUN} -c ${cpus} -m ${memsize} ${bhyverun_opt} \ 424 ${efiargs} \ 425 ${devargs} \ 426 ${console_opt} \ 427 ${installer_opt} \ 428 ${vmname} 429 430 bhyve_exit=$? 431 # bhyve returns the following status codes: 432 # 0 - VM has been reset 433 # 1 - VM has been powered off 434 # 2 - VM has been halted 435 # 3 - VM generated a triple fault 436 # all other non-zero status codes are errors 437 # 438 if [ $bhyve_exit -ne 0 ]; then 439 break 440 fi 441done 442 443 444case $bhyve_exit in 445 0|1|2) 446 # Cleanup /dev/vmm entry when bhyve did not exit 447 # due to an error. 448 ${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1 449 ;; 450esac 451 452exit $bhyve_exit 453