xref: /freebsd/share/examples/bhyve/vmrun.sh (revision 4657548d18877f64bd02be888406aa5b02bf9b06)
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 [-ahi] [-c <CPUs>] [-C <console>] [-d <disk file>]"
50	echo "                [-e <name=value>] [-g <gdbport> ] [-H <directory>]"
51	echo "                [-I <location of installation iso>] [-l <loader>]"
52	echo "                [-m <memsize>] [-t <tapdev>] <vmname>"
53	echo ""
54	echo "       -h: display this help message"
55	echo "       -a: force memory mapped local APIC access"
56	echo "       -c: number of virtual cpus (default is ${DEFAULT_CPUS})"
57	echo "       -C: console device (default is ${DEFAULT_CONSOLE})"
58	echo "       -d: virtio diskdev file (default is ${DEFAULT_VIRTIO_DISK})"
59	echo "       -e: set FreeBSD loader environment variable"
60	echo "       -g: listen for connection from kgdb at <gdbport>"
61	echo "       -H: host filesystem to export to the loader"
62	echo "       -i: force boot of the Installation CDROM image"
63	echo "       -I: Installation CDROM image location (default is ${DEFAULT_ISOFILE})"
64	echo "       -l: the OS loader to use (default is /boot/userboot.so)"
65	echo "       -m: memory size (default is ${DEFAULT_MEMSIZE})"
66	echo "       -p: pass-through a host PCI device at bus/slot/func (e.g. 10/0/0)"
67	echo "       -t: tap device for virtio-net (default is $DEFAULT_TAPDEV)"
68	echo "       -u: RTC keeps UTC time"
69	echo "       -w: ignore unimplemented MSRs"
70	echo ""
71	[ -n "$msg" ] && errmsg "$msg"
72	exit 1
73}
74
75if [ `id -u` -ne 0 ]; then
76	errmsg "This script must be executed with superuser privileges"
77	exit 1
78fi
79
80kldstat -n vmm > /dev/null 2>&1
81if [ $? -ne 0 ]; then
82	errmsg "vmm.ko is not loaded"
83	exit 1
84fi
85
86force_install=0
87isofile=${DEFAULT_ISOFILE}
88memsize=${DEFAULT_MEMSIZE}
89console=${DEFAULT_CONSOLE}
90cpus=${DEFAULT_CPUS}
91tap_total=0
92disk_total=0
93gdbport=0
94loader_opt=""
95bhyverun_opt="-H -A -P"
96pass_total=0
97
98while getopts ac:C:d:e:g:hH:iI:l:m:p:t:uw c ; do
99	case $c in
100	a)
101		bhyverun_opt="${bhyverun_opt} -a"
102		;;
103	c)
104		cpus=${OPTARG}
105		;;
106	C)
107		console=${OPTARG}
108		;;
109	d)
110		disk_dev=${OPTARG%%,*}
111		disk_opts=${OPTARG#${disk_dev}}
112		eval "disk_dev${disk_total}=\"${disk_dev}\""
113		eval "disk_opts${disk_total}=\"${disk_opts}\""
114		disk_total=$(($disk_total + 1))
115		;;
116	e)
117		loader_opt="${loader_opt} -e ${OPTARG}"
118		;;
119	g)
120		gdbport=${OPTARG}
121		;;
122	H)
123		host_base=`realpath ${OPTARG}`
124		;;
125	i)
126		force_install=1
127		;;
128	I)
129		isofile=${OPTARG}
130		;;
131	l)
132		loader_opt="${loader_opt} -l ${OPTARG}"
133		;;
134	m)
135		memsize=${OPTARG}
136		;;
137	p)
138		eval "pass_dev${pass_total}=\"${OPTARG}\""
139		pass_total=$(($pass_total + 1))
140		;;
141	t)
142		eval "tap_dev${tap_total}=\"${OPTARG}\""
143		tap_total=$(($tap_total + 1))
144		;;
145	u)
146		bhyverun_opt="${bhyverun_opt} -u"
147		;;
148	w)
149		bhyverun_opt="${bhyverun_opt} -w"
150		;;
151	*)
152		usage
153		;;
154	esac
155done
156
157if [ $tap_total -eq 0 ] ; then
158    tap_total=1
159    tap_dev0="${DEFAULT_TAPDEV}"
160fi
161if [ $disk_total -eq 0 ] ; then
162    disk_total=1
163    disk_dev0="${DEFAULT_VIRTIO_DISK}"
164
165fi
166
167shift $((${OPTIND} - 1))
168
169if [ $# -ne 1 ]; then
170	usage "virtual machine name not specified"
171fi
172
173vmname="$1"
174if [ -n "${host_base}" ]; then
175	loader_opt="${loader_opt} -h ${host_base}"
176fi
177
178# If PCI passthru devices are configured then guest memory must be wired
179if [ ${pass_total} -gt 0 ]; then
180	loader_opt="${loader_opt} -S"
181	bhyverun_opt="${bhyverun_opt} -S"
182fi
183
184make_and_check_diskdev()
185{
186    local virtio_diskdev="$1"
187    # Create the virtio diskdev file if needed
188    if [ ! -e ${virtio_diskdev} ]; then
189	    echo "virtio disk device file \"${virtio_diskdev}\" does not exist."
190	    echo "Creating it ..."
191	    truncate -s 8G ${virtio_diskdev} > /dev/null
192    fi
193
194    if [ ! -r ${virtio_diskdev} ]; then
195	    echo "virtio disk device file \"${virtio_diskdev}\" is not readable"
196	    exit 1
197    fi
198
199    if [ ! -w ${virtio_diskdev} ]; then
200	    echo "virtio disk device file \"${virtio_diskdev}\" is not writable"
201	    exit 1
202    fi
203}
204
205echo "Launching virtual machine \"$vmname\" ..."
206
207first_diskdev="$disk_dev0"
208
209${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1
210
211while [ 1 ]; do
212
213	file -s ${first_diskdev} | grep "boot sector" > /dev/null
214	rc=$?
215	if [ $rc -ne 0 ]; then
216		file -s ${first_diskdev} | grep ": Unix Fast File sys" > /dev/null
217		rc=$?
218	fi
219	if [ $rc -ne 0 ]; then
220		need_install=1
221	else
222		need_install=0
223	fi
224
225	if [ $force_install -eq 1 -o $need_install -eq 1 ]; then
226		if [ ! -r ${isofile} ]; then
227			echo -n "Installation CDROM image \"${isofile}\" "
228			echo    "is not readable"
229			exit 1
230		fi
231		BOOTDISKS="-d ${isofile}"
232		installer_opt="-s 31:0,ahci-cd,${isofile}"
233	else
234		BOOTDISKS=""
235		i=0
236		while [ $i -lt $disk_total ] ; do
237			eval "disk=\$disk_dev${i}"
238			if [ -r ${disk} ] ; then
239				BOOTDISKS="$BOOTDISKS -d ${disk} "
240			fi
241			i=$(($i + 1))
242		done
243		installer_opt=""
244	fi
245
246	${LOADER} -c ${console} -m ${memsize} ${BOOTDISKS} ${loader_opt} \
247		${vmname}
248	bhyve_exit=$?
249	if [ $bhyve_exit -ne 0 ]; then
250		break
251	fi
252
253	#
254	# Build up args for additional tap and disk devices now.
255	#
256	nextslot=2  # slot 0 is hostbridge, slot 1 is lpc
257	devargs=""  # accumulate disk/tap args here
258	i=0
259	while [ $i -lt $tap_total ] ; do
260	    eval "tapname=\$tap_dev${i}"
261	    devargs="$devargs -s $nextslot:0,virtio-net,${tapname} "
262	    nextslot=$(($nextslot + 1))
263	    i=$(($i + 1))
264	done
265
266	i=0
267	while [ $i -lt $disk_total ] ; do
268	    eval "disk=\$disk_dev${i}"
269	    eval "opts=\$disk_opts${i}"
270	    make_and_check_diskdev "${disk}"
271	    devargs="$devargs -s $nextslot:0,virtio-blk,${disk}${opts} "
272	    nextslot=$(($nextslot + 1))
273	    i=$(($i + 1))
274	done
275
276	i=0
277	while [ $i -lt $pass_total ] ; do
278	    eval "pass=\$pass_dev${i}"
279	    devargs="$devargs -s $nextslot:0,passthru,${pass} "
280	    nextslot=$(($nextslot + 1))
281	    i=$(($i + 1))
282        done
283
284	${FBSDRUN} -c ${cpus} -m ${memsize} ${bhyverun_opt}		\
285		-g ${gdbport}						\
286		-s 0:0,hostbridge					\
287		-s 1:0,lpc						\
288		${devargs}						\
289		-l com1,${console}					\
290		${installer_opt}					\
291		${vmname}
292
293	bhyve_exit=$?
294	# bhyve returns the following status codes:
295	#  0 - VM has been reset
296	#  1 - VM has been powered off
297	#  2 - VM has been halted
298	#  3 - VM generated a triple fault
299	#  all other non-zero status codes are errors
300	#
301	if [ $bhyve_exit -ne 0 ]; then
302		break
303	fi
304done
305
306
307case $bhyve_exit in
308	0|1|2)
309		# Cleanup /dev/vmm entry when bhyve did not exit
310		# due to an error.
311		${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1
312		;;
313esac
314
315exit $bhyve_exit
316