xref: /freebsd/share/examples/bhyve/vmrun.sh (revision d8a0fe102c0cfdfcd5b818f850eff09d8536c9bc)
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_VIRTIO_DISK="./diskdev"
42DEFAULT_ISOFILE="./release.iso"
43
44errmsg() {
45	echo "*** $1"
46}
47
48usage() {
49	local msg=$1
50
51	echo "Usage: vmrun.sh [-aAEhiTv] [-c <CPUs>] [-C <console>] [-d <disk file>]"
52	echo "                [-e <name=value>] [-f <path of firmware>] [-F <size>]"
53	echo "                [-g <gdbport> ] [-H <directory>]"
54	echo "                [-I <location of installation iso>] [-l <loader>]"
55	echo "                [-L <VNC IP for UEFI framebuffer>]"
56	echo "                [-m <memsize>] [-P <port>] [-t <tapdev>] <vmname>"
57	echo ""
58	echo "       -h: display this help message"
59	echo "       -a: force memory mapped local APIC access"
60	echo "       -A: use AHCI disk emulation instead of virtio"
61	echo "       -c: number of virtual cpus (default is ${DEFAULT_CPUS})"
62	echo "       -C: console device (default is ${DEFAULT_CONSOLE})"
63	echo "       -d: virtio diskdev file (default is ${DEFAULT_VIRTIO_DISK})"
64	echo "       -e: set FreeBSD loader environment variable"
65	echo "       -E: Use UEFI mode"
66	echo "       -f: Use a specific UEFI firmware"
67	echo "       -F: Use a custom UEFI GOP framebuffer size (default: w=1024,h=768)"
68	echo "       -g: listen for connection from kgdb at <gdbport>"
69	echo "       -H: host filesystem to export to the loader"
70	echo "       -i: force boot of the Installation CDROM image"
71	echo "       -I: Installation CDROM image location (default is ${DEFAULT_ISOFILE})"
72	echo "       -l: the OS loader to use (default is /boot/userboot.so)"
73	echo "       -L: IP address for UEFI GOP VNC server (default: 127.0.0.1)"
74	echo "       -m: memory size (default is ${DEFAULT_MEMSIZE})"
75	echo "       -p: pass-through a host PCI device at bus/slot/func (e.g. 10/0/0)"
76	echo "       -P: UEFI GOP VNC port (default: 5900)"
77	echo "       -t: tap device for virtio-net (default is $DEFAULT_TAPDEV)"
78	echo "       -T: Enable tablet device (for UEFI GOP)"
79	echo "       -u: RTC keeps UTC time"
80	echo "       -v: Wait for VNC client connection before booting VM"
81	echo "       -w: ignore unimplemented MSRs"
82	echo ""
83	[ -n "$msg" ] && errmsg "$msg"
84	exit 1
85}
86
87if [ `id -u` -ne 0 ]; then
88	errmsg "This script must be executed with superuser privileges"
89	exit 1
90fi
91
92kldstat -n vmm > /dev/null 2>&1
93if [ $? -ne 0 ]; then
94	errmsg "vmm.ko is not loaded"
95	exit 1
96fi
97
98force_install=0
99isofile=${DEFAULT_ISOFILE}
100memsize=${DEFAULT_MEMSIZE}
101console=${DEFAULT_CONSOLE}
102cpus=${DEFAULT_CPUS}
103tap_total=0
104disk_total=0
105disk_emulation="virtio-blk"
106gdbport=0
107loader_opt=""
108bhyverun_opt="-H -A -P"
109pass_total=0
110
111# EFI-specific options
112efi_mode=0
113efi_firmware="/usr/local/share/uefi-firmware/BHYVE_UEFI.fd"
114vncwait=""
115vnchost="127.0.0.1"
116vncport=5900
117fbsize="w=1024,h=768"
118tablet=""
119
120while getopts aAc:C:d:e:Ef:F:g:hH:iI:l:m:p:P:t:Tuvw c ; do
121	case $c in
122	a)
123		bhyverun_opt="${bhyverun_opt} -a"
124		;;
125	A)
126		disk_emulation="ahci-hd"
127		;;
128	c)
129		cpus=${OPTARG}
130		;;
131	C)
132		console=${OPTARG}
133		;;
134	d)
135		disk_dev=${OPTARG%%,*}
136		disk_opts=${OPTARG#${disk_dev}}
137		eval "disk_dev${disk_total}=\"${disk_dev}\""
138		eval "disk_opts${disk_total}=\"${disk_opts}\""
139		disk_total=$(($disk_total + 1))
140		;;
141	e)
142		loader_opt="${loader_opt} -e ${OPTARG}"
143		;;
144	E)
145		efi_mode=1
146		;;
147	f)
148		efi_firmware="${OPTARG}"
149		;;
150	F)
151		fbsize="${OPTARG}"
152		;;
153	g)
154		gdbport=${OPTARG}
155		;;
156	H)
157		host_base=`realpath ${OPTARG}`
158		;;
159	i)
160		force_install=1
161		;;
162	I)
163		isofile=${OPTARG}
164		;;
165	l)
166		loader_opt="${loader_opt} -l ${OPTARG}"
167		;;
168	L)
169		vnchost="${OPTARG}"
170		;;
171	m)
172		memsize=${OPTARG}
173		;;
174	p)
175		eval "pass_dev${pass_total}=\"${OPTARG}\""
176		pass_total=$(($pass_total + 1))
177		;;
178	P)
179		vncport="${OPTARG}"
180		;;
181	t)
182		eval "tap_dev${tap_total}=\"${OPTARG}\""
183		tap_total=$(($tap_total + 1))
184		;;
185	T)
186		tablet="-s 30,xhci,tablet"
187		;;
188	u)
189		bhyverun_opt="${bhyverun_opt} -u"
190		;;
191	v)
192		vncwait=",wait"
193		;;
194	w)
195		bhyverun_opt="${bhyverun_opt} -w"
196		;;
197	*)
198		usage
199		;;
200	esac
201done
202
203if [ $tap_total -eq 0 ] ; then
204    tap_total=1
205    tap_dev0="${DEFAULT_TAPDEV}"
206fi
207if [ $disk_total -eq 0 ] ; then
208    disk_total=1
209    disk_dev0="${DEFAULT_VIRTIO_DISK}"
210
211fi
212
213shift $((${OPTIND} - 1))
214
215if [ $# -ne 1 ]; then
216	usage "virtual machine name not specified"
217fi
218
219vmname="$1"
220if [ -n "${host_base}" ]; then
221	loader_opt="${loader_opt} -h ${host_base}"
222fi
223
224# If PCI passthru devices are configured then guest memory must be wired
225if [ ${pass_total} -gt 0 ]; then
226	loader_opt="${loader_opt} -S"
227	bhyverun_opt="${bhyverun_opt} -S"
228fi
229
230if [ ${efi_mode} -gt 0 ]; then
231	if [ ! -f ${efi_firmware} ]; then
232		echo "Error: EFI Firmware ${efi_firmware} doesn't exist. Try: pkg install uefi-edk2-bhyve"
233		exit 1
234	fi
235fi
236
237make_and_check_diskdev()
238{
239    local virtio_diskdev="$1"
240    # Create the virtio diskdev file if needed
241    if [ ! -e ${virtio_diskdev} ]; then
242	    echo "virtio disk device file \"${virtio_diskdev}\" does not exist."
243	    echo "Creating it ..."
244	    truncate -s 8G ${virtio_diskdev} > /dev/null
245    fi
246
247    if [ ! -r ${virtio_diskdev} ]; then
248	    echo "virtio disk device file \"${virtio_diskdev}\" is not readable"
249	    exit 1
250    fi
251
252    if [ ! -w ${virtio_diskdev} ]; then
253	    echo "virtio disk device file \"${virtio_diskdev}\" is not writable"
254	    exit 1
255    fi
256}
257
258echo "Launching virtual machine \"$vmname\" ..."
259
260first_diskdev="$disk_dev0"
261
262${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1
263
264while [ 1 ]; do
265
266	file -s ${first_diskdev} | grep "boot sector" > /dev/null
267	rc=$?
268	if [ $rc -ne 0 ]; then
269		file -s ${first_diskdev} | grep ": Unix Fast File sys" > /dev/null
270		rc=$?
271	fi
272	if [ $rc -ne 0 ]; then
273		need_install=1
274	else
275		need_install=0
276	fi
277
278	if [ $force_install -eq 1 -o $need_install -eq 1 ]; then
279		if [ ! -r ${isofile} ]; then
280			echo -n "Installation CDROM image \"${isofile}\" "
281			echo    "is not readable"
282			exit 1
283		fi
284		BOOTDISKS="-d ${isofile}"
285		installer_opt="-s 31:0,ahci-cd,${isofile}"
286	else
287		BOOTDISKS=""
288		i=0
289		while [ $i -lt $disk_total ] ; do
290			eval "disk=\$disk_dev${i}"
291			if [ -r ${disk} ] ; then
292				BOOTDISKS="$BOOTDISKS -d ${disk} "
293			fi
294			i=$(($i + 1))
295		done
296		installer_opt=""
297	fi
298
299	if [ ${efi_mode} -eq 0 ]; then
300		${LOADER} -c ${console} -m ${memsize} ${BOOTDISKS} ${loader_opt} \
301			${vmname}
302		bhyve_exit=$?
303		if [ $bhyve_exit -ne 0 ]; then
304			break
305		fi
306	fi
307
308	#
309	# Build up args for additional tap and disk devices now.
310	#
311	nextslot=2  # slot 0 is hostbridge, slot 1 is lpc
312	devargs=""  # accumulate disk/tap args here
313	i=0
314	while [ $i -lt $tap_total ] ; do
315	    eval "tapname=\$tap_dev${i}"
316	    devargs="$devargs -s $nextslot:0,virtio-net,${tapname} "
317	    nextslot=$(($nextslot + 1))
318	    i=$(($i + 1))
319	done
320
321	i=0
322	while [ $i -lt $disk_total ] ; do
323	    eval "disk=\$disk_dev${i}"
324	    eval "opts=\$disk_opts${i}"
325	    make_and_check_diskdev "${disk}"
326	    devargs="$devargs -s $nextslot:0,$disk_emulation,${disk}${opts} "
327	    nextslot=$(($nextslot + 1))
328	    i=$(($i + 1))
329	done
330
331	i=0
332	while [ $i -lt $pass_total ] ; do
333	    eval "pass=\$pass_dev${i}"
334	    devargs="$devargs -s $nextslot:0,passthru,${pass} "
335	    nextslot=$(($nextslot + 1))
336	    i=$(($i + 1))
337        done
338
339	efiargs=""
340	if [ ${efi_mode} -gt 0 ]; then
341		efiargs="-s 29,fbuf,tcp=${vnchost}:${vncport},${fbsize}${vncwait}"
342		efiargs="${efiargs} -l bootrom,${efi_firmware}"
343		efiargs="${efiargs} ${tablet}"
344	fi
345
346	${FBSDRUN} -c ${cpus} -m ${memsize} ${bhyverun_opt}		\
347		-g ${gdbport}						\
348		-s 0:0,hostbridge					\
349		-s 1:0,lpc						\
350		${efiargs}						\
351		${devargs}						\
352		-l com1,${console}					\
353		${installer_opt}					\
354		${vmname}
355
356	bhyve_exit=$?
357	# bhyve returns the following status codes:
358	#  0 - VM has been reset
359	#  1 - VM has been powered off
360	#  2 - VM has been halted
361	#  3 - VM generated a triple fault
362	#  all other non-zero status codes are errors
363	#
364	if [ $bhyve_exit -ne 0 ]; then
365		break
366	fi
367done
368
369
370case $bhyve_exit in
371	0|1|2)
372		# Cleanup /dev/vmm entry when bhyve did not exit
373		# due to an error.
374		${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1
375		;;
376esac
377
378exit $bhyve_exit
379