Fold constants and unify vnc variable names.
[freebsd.git] / share / examples / bhyve / vmrun.sh
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
32 LOADER=/usr/sbin/bhyveload
33 BHYVECTL=/usr/sbin/bhyvectl
34 FBSDRUN=/usr/sbin/bhyve
35
36 DEFAULT_MEMSIZE=512M
37 DEFAULT_CPUS=2
38 DEFAULT_TAPDEV=tap0
39 DEFAULT_CONSOLE=stdio
40
41 DEFAULT_NIC=virtio-net
42 DEFAULT_DISK=virtio-blk
43 DEFAULT_VIRTIO_DISK="./diskdev"
44 DEFAULT_ISOFILE="./release.iso"
45
46 DEFAULT_VNCHOST="127.0.0.1"
47 DEFAULT_VNCPORT=5900
48 DEFAULT_VNCSIZE="w=1024,h=768"
49
50 errmsg() {
51         echo "*** $1"
52 }
53
54 usage() {
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
103 if [ `id -u` -ne 0 ]; then
104         errmsg "This script must be executed with superuser privileges"
105         exit 1
106 fi
107
108 kldstat -n vmm > /dev/null 2>&1 
109 if [ $? -ne 0 ]; then
110         errmsg "vmm.ko is not loaded"
111         exit 1
112 fi
113
114 force_install=0
115 isofile=${DEFAULT_ISOFILE}
116 memsize=${DEFAULT_MEMSIZE}
117 console=${DEFAULT_CONSOLE}
118 cpus=${DEFAULT_CPUS}
119 nic=${DEFAULT_NIC}
120 tap_total=0
121 disk_total=0
122 disk_emulation=${DEFAULT_DISK}
123 gdbport=0
124 loader_opt=""
125 bhyverun_opt="-H -A -P"
126 pass_total=0
127
128 # EFI-specific options
129 efi_mode=0
130 efi_firmware="/usr/local/share/uefi-firmware/BHYVE_UEFI.fd"
131 vncwait=""
132 vnchost=${DEFAULT_VNCHOST}
133 vncport=${DEFAULT_VNCPORT}
134 vncsize=${DEFAULT_VNCSIZE}
135 tablet=""
136
137 while getopts aAc:C:d:e:Ef:F:g:hH:iI: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
221 done
222
223 if [ $tap_total -eq 0 ] ; then
224     tap_total=1
225     tap_dev0="${DEFAULT_TAPDEV}"
226 fi
227 if [ $disk_total -eq 0 ] ; then
228     disk_total=1
229     disk_dev0="${DEFAULT_VIRTIO_DISK}"
230
231 fi
232
233 shift $((${OPTIND} - 1))
234
235 if [ $# -ne 1 ]; then
236         usage "virtual machine name not specified"
237 fi
238
239 vmname="$1"
240 if [ -n "${host_base}" ]; then
241         loader_opt="${loader_opt} -h ${host_base}"
242 fi
243
244 # If PCI passthru devices are configured then guest memory must be wired
245 if [ ${pass_total} -gt 0 ]; then
246         loader_opt="${loader_opt} -S"
247         bhyverun_opt="${bhyverun_opt} -S"
248 fi
249
250 if [ ${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
256 fi
257
258 make_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
279 echo "Launching virtual machine \"$vmname\" ..."
280
281 first_diskdev="$disk_dev0"
282
283 ${BHYVECTL} --vm=${vmname} --destroy > /dev/null 2>&1
284
285 while [ 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
390 done
391
392
393 case $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                 ;;
399 esac
400
401 exit $bhyve_exit