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