xref: /freebsd/usr.sbin/bhyve/bhyverun.c (revision f80330a8209163c502d7cc20a3a10653e6625fea)
1e285ef8dSPeter Grehan /*-
2e285ef8dSPeter Grehan  * Copyright (c) 2011 NetApp, Inc.
3e285ef8dSPeter Grehan  * All rights reserved.
4e285ef8dSPeter Grehan  *
5e285ef8dSPeter Grehan  * Redistribution and use in source and binary forms, with or without
6e285ef8dSPeter Grehan  * modification, are permitted provided that the following conditions
7e285ef8dSPeter Grehan  * are met:
8e285ef8dSPeter Grehan  * 1. Redistributions of source code must retain the above copyright
9e285ef8dSPeter Grehan  *    notice, this list of conditions and the following disclaimer.
10e285ef8dSPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
11e285ef8dSPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
12e285ef8dSPeter Grehan  *    documentation and/or other materials provided with the distribution.
13e285ef8dSPeter Grehan  *
14e285ef8dSPeter Grehan  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15e285ef8dSPeter Grehan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16e285ef8dSPeter Grehan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17e285ef8dSPeter Grehan  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18e285ef8dSPeter Grehan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19e285ef8dSPeter Grehan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20e285ef8dSPeter Grehan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21e285ef8dSPeter Grehan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22e285ef8dSPeter Grehan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23e285ef8dSPeter Grehan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24e285ef8dSPeter Grehan  * SUCH DAMAGE.
25e285ef8dSPeter Grehan  *
26e285ef8dSPeter Grehan  * $FreeBSD$
27e285ef8dSPeter Grehan  */
28e285ef8dSPeter Grehan 
29e285ef8dSPeter Grehan #include <sys/cdefs.h>
30e285ef8dSPeter Grehan __FBSDID("$FreeBSD$");
31e285ef8dSPeter Grehan 
32e285ef8dSPeter Grehan #include <sys/types.h>
33e285ef8dSPeter Grehan #include <sys/mman.h>
34e285ef8dSPeter Grehan #include <sys/time.h>
35e285ef8dSPeter Grehan 
361c052192SNeel Natu #include <machine/atomic.h>
37e285ef8dSPeter Grehan #include <machine/segments.h>
38e285ef8dSPeter Grehan 
39e285ef8dSPeter Grehan #include <stdio.h>
40e285ef8dSPeter Grehan #include <stdlib.h>
41b5331f4dSNeel Natu #include <string.h>
42200758f1SNeel Natu #include <err.h>
43e285ef8dSPeter Grehan #include <libgen.h>
44e285ef8dSPeter Grehan #include <unistd.h>
45e285ef8dSPeter Grehan #include <assert.h>
46e285ef8dSPeter Grehan #include <errno.h>
47e285ef8dSPeter Grehan #include <pthread.h>
48e285ef8dSPeter Grehan #include <pthread_np.h>
49200758f1SNeel Natu #include <sysexits.h>
50e285ef8dSPeter Grehan 
51e285ef8dSPeter Grehan #include <machine/vmm.h>
52e285ef8dSPeter Grehan #include <vmmapi.h>
53e285ef8dSPeter Grehan 
54e285ef8dSPeter Grehan #include "bhyverun.h"
55e285ef8dSPeter Grehan #include "acpi.h"
56e285ef8dSPeter Grehan #include "inout.h"
57e285ef8dSPeter Grehan #include "dbgport.h"
58ea7f1c8cSNeel Natu #include "legacy_irq.h"
59e285ef8dSPeter Grehan #include "mem.h"
60e285ef8dSPeter Grehan #include "mevent.h"
61e285ef8dSPeter Grehan #include "mptbl.h"
62e285ef8dSPeter Grehan #include "pci_emul.h"
63ea7f1c8cSNeel Natu #include "pci_lpc.h"
64e285ef8dSPeter Grehan #include "xmsr.h"
65e285ef8dSPeter Grehan #include "spinup_ap.h"
669d6be09fSPeter Grehan #include "rtc.h"
67e285ef8dSPeter Grehan 
68e285ef8dSPeter Grehan #define GUEST_NIO_PORT		0x488	/* guest upcalls via i/o port */
69e285ef8dSPeter Grehan 
70e285ef8dSPeter Grehan #define	VMEXIT_SWITCH		0	/* force vcpu switch in mux mode */
71e285ef8dSPeter Grehan #define	VMEXIT_CONTINUE		1	/* continue from next instruction */
72e285ef8dSPeter Grehan #define	VMEXIT_RESTART		2	/* restart current instruction */
73e285ef8dSPeter Grehan #define	VMEXIT_ABORT		3	/* abort the vm run loop */
74e285ef8dSPeter Grehan #define	VMEXIT_RESET		4	/* guest machine has reset */
75e285ef8dSPeter Grehan 
76e285ef8dSPeter Grehan #define MB		(1024UL * 1024)
77e285ef8dSPeter Grehan #define GB		(1024UL * MB)
78e285ef8dSPeter Grehan 
79e285ef8dSPeter Grehan typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu);
80e285ef8dSPeter Grehan 
81e285ef8dSPeter Grehan char *vmname;
82e285ef8dSPeter Grehan 
83e285ef8dSPeter Grehan int guest_ncpus;
84e285ef8dSPeter Grehan 
85e285ef8dSPeter Grehan static int pincpu = -1;
86e285ef8dSPeter Grehan static int guest_vmexit_on_hlt, guest_vmexit_on_pause, disable_x2apic;
87062b878fSPeter Grehan static int virtio_msix = 1;
88e285ef8dSPeter Grehan 
89e285ef8dSPeter Grehan static int strictio;
90851d84f1SNeel Natu static int strictmsr = 1;
91e285ef8dSPeter Grehan 
92e285ef8dSPeter Grehan static int acpi;
93e285ef8dSPeter Grehan 
94e285ef8dSPeter Grehan static char *progname;
95e285ef8dSPeter Grehan static const int BSP = 0;
96e285ef8dSPeter Grehan 
97e285ef8dSPeter Grehan static int cpumask;
98e285ef8dSPeter Grehan 
99e285ef8dSPeter Grehan static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip);
100e285ef8dSPeter Grehan 
101e285ef8dSPeter Grehan struct vm_exit vmexit[VM_MAXCPU];
102e285ef8dSPeter Grehan 
10394c3b3bfSPeter Grehan struct bhyvestats {
104e285ef8dSPeter Grehan         uint64_t        vmexit_bogus;
105e285ef8dSPeter Grehan         uint64_t        vmexit_bogus_switch;
106e285ef8dSPeter Grehan         uint64_t        vmexit_hlt;
107e285ef8dSPeter Grehan         uint64_t        vmexit_pause;
108e285ef8dSPeter Grehan         uint64_t        vmexit_mtrap;
109318224bbSNeel Natu         uint64_t        vmexit_inst_emul;
110e285ef8dSPeter Grehan         uint64_t        cpu_switch_rotate;
111e285ef8dSPeter Grehan         uint64_t        cpu_switch_direct;
112e285ef8dSPeter Grehan         int             io_reset;
113e285ef8dSPeter Grehan } stats;
114e285ef8dSPeter Grehan 
115e285ef8dSPeter Grehan struct mt_vmm_info {
116e285ef8dSPeter Grehan 	pthread_t	mt_thr;
117e285ef8dSPeter Grehan 	struct vmctx	*mt_ctx;
118e285ef8dSPeter Grehan 	int		mt_vcpu;
119e285ef8dSPeter Grehan } mt_vmm_info[VM_MAXCPU];
120e285ef8dSPeter Grehan 
121e285ef8dSPeter Grehan static void
122e285ef8dSPeter Grehan usage(int code)
123e285ef8dSPeter Grehan {
124e285ef8dSPeter Grehan 
125e285ef8dSPeter Grehan         fprintf(stderr,
126851d84f1SNeel Natu                 "Usage: %s [-aehwAHIPW] [-g <gdb port>] [-s <pci>] [-S <pci>]\n"
127ea7f1c8cSNeel Natu 		"       %*s [-c vcpus] [-p pincpu] [-m mem] [-l <lpc>] <vm>\n"
128e285ef8dSPeter Grehan 		"       -a: local apic is in XAPIC mode (default is X2APIC)\n"
129e285ef8dSPeter Grehan 		"       -A: create an ACPI table\n"
1304a06a0feSNeel Natu 		"       -g: gdb port\n"
131e285ef8dSPeter Grehan 		"       -c: # cpus (default 1)\n"
132e285ef8dSPeter Grehan 		"       -p: pin vcpu 'n' to host cpu 'pincpu + n'\n"
133e285ef8dSPeter Grehan 		"       -H: vmexit from the guest on hlt\n"
134e285ef8dSPeter Grehan 		"       -P: vmexit from the guest on pause\n"
135062b878fSPeter Grehan 		"       -W: force virtio to use single-vector MSI\n"
136b5331f4dSNeel Natu 		"       -e: exit on unhandled I/O access\n"
137e285ef8dSPeter Grehan 		"       -h: help\n"
138e285ef8dSPeter Grehan 		"       -s: <slot,driver,configinfo> PCI slot config\n"
139e285ef8dSPeter Grehan 		"       -S: <slot,driver,configinfo> legacy PCI slot config\n"
140ea7f1c8cSNeel Natu 		"       -l: LPC device configuration\n"
141851d84f1SNeel Natu 		"       -m: memory size in MB\n"
142851d84f1SNeel Natu 		"       -w: ignore unimplemented MSRs\n",
143b5331f4dSNeel Natu 		progname, (int)strlen(progname), "");
14494c3b3bfSPeter Grehan 
145e285ef8dSPeter Grehan 	exit(code);
146e285ef8dSPeter Grehan }
147e285ef8dSPeter Grehan 
148e285ef8dSPeter Grehan void *
149b060ba50SNeel Natu paddr_guest2host(struct vmctx *ctx, uintptr_t gaddr, size_t len)
150e285ef8dSPeter Grehan {
151e285ef8dSPeter Grehan 
152b060ba50SNeel Natu 	return (vm_map_gpa(ctx, gaddr, len));
153e285ef8dSPeter Grehan }
154e285ef8dSPeter Grehan 
155e285ef8dSPeter Grehan int
156e285ef8dSPeter Grehan fbsdrun_disable_x2apic(void)
157e285ef8dSPeter Grehan {
158e285ef8dSPeter Grehan 
159e285ef8dSPeter Grehan 	return (disable_x2apic);
160e285ef8dSPeter Grehan }
161e285ef8dSPeter Grehan 
162e285ef8dSPeter Grehan int
163e285ef8dSPeter Grehan fbsdrun_vmexit_on_pause(void)
164e285ef8dSPeter Grehan {
165e285ef8dSPeter Grehan 
166e285ef8dSPeter Grehan 	return (guest_vmexit_on_pause);
167e285ef8dSPeter Grehan }
168e285ef8dSPeter Grehan 
169e285ef8dSPeter Grehan int
170e285ef8dSPeter Grehan fbsdrun_vmexit_on_hlt(void)
171e285ef8dSPeter Grehan {
172e285ef8dSPeter Grehan 
173e285ef8dSPeter Grehan 	return (guest_vmexit_on_hlt);
174e285ef8dSPeter Grehan }
175e285ef8dSPeter Grehan 
176062b878fSPeter Grehan int
177062b878fSPeter Grehan fbsdrun_virtio_msix(void)
178062b878fSPeter Grehan {
179062b878fSPeter Grehan 
180062b878fSPeter Grehan 	return (virtio_msix);
181062b878fSPeter Grehan }
182062b878fSPeter Grehan 
183e285ef8dSPeter Grehan static void *
184e285ef8dSPeter Grehan fbsdrun_start_thread(void *param)
185e285ef8dSPeter Grehan {
186e285ef8dSPeter Grehan 	char tname[MAXCOMLEN + 1];
187e285ef8dSPeter Grehan 	struct mt_vmm_info *mtp;
188e285ef8dSPeter Grehan 	int vcpu;
189e285ef8dSPeter Grehan 
190e285ef8dSPeter Grehan 	mtp = param;
191e285ef8dSPeter Grehan 	vcpu = mtp->mt_vcpu;
192e285ef8dSPeter Grehan 
1937f5487acSPeter Grehan 	snprintf(tname, sizeof(tname), "vcpu %d", vcpu);
194e285ef8dSPeter Grehan 	pthread_set_name_np(mtp->mt_thr, tname);
195e285ef8dSPeter Grehan 
196e285ef8dSPeter Grehan 	vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip);
197e285ef8dSPeter Grehan 
198e285ef8dSPeter Grehan 	/* not reached */
199e285ef8dSPeter Grehan 	exit(1);
200e285ef8dSPeter Grehan 	return (NULL);
201e285ef8dSPeter Grehan }
202e285ef8dSPeter Grehan 
203e285ef8dSPeter Grehan void
204e285ef8dSPeter Grehan fbsdrun_addcpu(struct vmctx *ctx, int vcpu, uint64_t rip)
205e285ef8dSPeter Grehan {
206e285ef8dSPeter Grehan 	int error;
207e285ef8dSPeter Grehan 
208e285ef8dSPeter Grehan 	if (cpumask & (1 << vcpu)) {
209e285ef8dSPeter Grehan 		fprintf(stderr, "addcpu: attempting to add existing cpu %d\n",
210e285ef8dSPeter Grehan 		    vcpu);
211e285ef8dSPeter Grehan 		exit(1);
212e285ef8dSPeter Grehan 	}
213e285ef8dSPeter Grehan 
2141c052192SNeel Natu 	atomic_set_int(&cpumask, 1 << vcpu);
215e285ef8dSPeter Grehan 
216e285ef8dSPeter Grehan 	/*
217e285ef8dSPeter Grehan 	 * Set up the vmexit struct to allow execution to start
218e285ef8dSPeter Grehan 	 * at the given RIP
219e285ef8dSPeter Grehan 	 */
220e285ef8dSPeter Grehan 	vmexit[vcpu].rip = rip;
221e285ef8dSPeter Grehan 	vmexit[vcpu].inst_length = 0;
222e285ef8dSPeter Grehan 
223e285ef8dSPeter Grehan 	mt_vmm_info[vcpu].mt_ctx = ctx;
224e285ef8dSPeter Grehan 	mt_vmm_info[vcpu].mt_vcpu = vcpu;
225e285ef8dSPeter Grehan 
226e285ef8dSPeter Grehan 	error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL,
227e285ef8dSPeter Grehan 	    fbsdrun_start_thread, &mt_vmm_info[vcpu]);
228e285ef8dSPeter Grehan 	assert(error == 0);
229e285ef8dSPeter Grehan }
230e285ef8dSPeter Grehan 
231e285ef8dSPeter Grehan static int
2321c052192SNeel Natu fbsdrun_deletecpu(struct vmctx *ctx, int vcpu)
2331c052192SNeel Natu {
2341c052192SNeel Natu 
2351c052192SNeel Natu 	if ((cpumask & (1 << vcpu)) == 0) {
2361c052192SNeel Natu 		fprintf(stderr, "addcpu: attempting to delete unknown cpu %d\n",
2371c052192SNeel Natu 		    vcpu);
2381c052192SNeel Natu 		exit(1);
2391c052192SNeel Natu 	}
2401c052192SNeel Natu 
2411c052192SNeel Natu 	atomic_clear_int(&cpumask, 1 << vcpu);
2421c052192SNeel Natu 	return (cpumask == 0);
2431c052192SNeel Natu }
2441c052192SNeel Natu 
2451c052192SNeel Natu static int
246e285ef8dSPeter Grehan vmexit_catch_reset(void)
247e285ef8dSPeter Grehan {
248e285ef8dSPeter Grehan         stats.io_reset++;
249e285ef8dSPeter Grehan         return (VMEXIT_RESET);
250e285ef8dSPeter Grehan }
251e285ef8dSPeter Grehan 
252e285ef8dSPeter Grehan static int
253e285ef8dSPeter Grehan vmexit_catch_inout(void)
254e285ef8dSPeter Grehan {
255e285ef8dSPeter Grehan 	return (VMEXIT_ABORT);
256e285ef8dSPeter Grehan }
257e285ef8dSPeter Grehan 
258e285ef8dSPeter Grehan static int
259e285ef8dSPeter Grehan vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu,
260e285ef8dSPeter Grehan 		     uint32_t eax)
261e285ef8dSPeter Grehan {
26294c3b3bfSPeter Grehan #if BHYVE_DEBUG
26394c3b3bfSPeter Grehan 	/*
26494c3b3bfSPeter Grehan 	 * put guest-driven debug here
26594c3b3bfSPeter Grehan 	 */
266e285ef8dSPeter Grehan #endif
267e285ef8dSPeter Grehan         return (VMEXIT_CONTINUE);
268e285ef8dSPeter Grehan }
269e285ef8dSPeter Grehan 
270e285ef8dSPeter Grehan static int
271e285ef8dSPeter Grehan vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
272e285ef8dSPeter Grehan {
273e285ef8dSPeter Grehan 	int error;
274e285ef8dSPeter Grehan 	int bytes, port, in, out;
275e285ef8dSPeter Grehan 	uint32_t eax;
276e285ef8dSPeter Grehan 	int vcpu;
277e285ef8dSPeter Grehan 
278e285ef8dSPeter Grehan 	vcpu = *pvcpu;
279e285ef8dSPeter Grehan 
280e285ef8dSPeter Grehan 	port = vme->u.inout.port;
281e285ef8dSPeter Grehan 	bytes = vme->u.inout.bytes;
282e285ef8dSPeter Grehan 	eax = vme->u.inout.eax;
283e285ef8dSPeter Grehan 	in = vme->u.inout.in;
284e285ef8dSPeter Grehan 	out = !in;
285e285ef8dSPeter Grehan 
286e285ef8dSPeter Grehan 	/* We don't deal with these */
287e285ef8dSPeter Grehan 	if (vme->u.inout.string || vme->u.inout.rep)
288e285ef8dSPeter Grehan 		return (VMEXIT_ABORT);
289e285ef8dSPeter Grehan 
290e285ef8dSPeter Grehan 	/* Special case of guest reset */
291e285ef8dSPeter Grehan 	if (out && port == 0x64 && (uint8_t)eax == 0xFE)
292e285ef8dSPeter Grehan 		return (vmexit_catch_reset());
293e285ef8dSPeter Grehan 
294e285ef8dSPeter Grehan         /* Extra-special case of host notifications */
295e285ef8dSPeter Grehan         if (out && port == GUEST_NIO_PORT)
296e285ef8dSPeter Grehan                 return (vmexit_handle_notify(ctx, vme, pvcpu, eax));
297e285ef8dSPeter Grehan 
298e285ef8dSPeter Grehan 	error = emulate_inout(ctx, vcpu, in, port, bytes, &eax, strictio);
299e285ef8dSPeter Grehan 	if (error == 0 && in)
300e285ef8dSPeter Grehan 		error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, eax);
301e285ef8dSPeter Grehan 
302e285ef8dSPeter Grehan 	if (error == 0)
303e285ef8dSPeter Grehan 		return (VMEXIT_CONTINUE);
304e285ef8dSPeter Grehan 	else {
305e285ef8dSPeter Grehan 		fprintf(stderr, "Unhandled %s%c 0x%04x\n",
306e285ef8dSPeter Grehan 			in ? "in" : "out",
307e285ef8dSPeter Grehan 			bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port);
308e285ef8dSPeter Grehan 		return (vmexit_catch_inout());
309e285ef8dSPeter Grehan 	}
310e285ef8dSPeter Grehan }
311e285ef8dSPeter Grehan 
312e285ef8dSPeter Grehan static int
313e285ef8dSPeter Grehan vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
314e285ef8dSPeter Grehan {
315851d84f1SNeel Natu 	uint64_t val;
316851d84f1SNeel Natu 	uint32_t eax, edx;
317851d84f1SNeel Natu 	int error;
318851d84f1SNeel Natu 
319851d84f1SNeel Natu 	val = 0;
320851d84f1SNeel Natu 	error = emulate_rdmsr(ctx, *pvcpu, vme->u.msr.code, &val);
321851d84f1SNeel Natu 	if (error != 0) {
322851d84f1SNeel Natu 		fprintf(stderr, "rdmsr to register %#x on vcpu %d\n",
323851d84f1SNeel Natu 		    vme->u.msr.code, *pvcpu);
324851d84f1SNeel Natu 		if (strictmsr)
325e285ef8dSPeter Grehan 			return (VMEXIT_ABORT);
326e285ef8dSPeter Grehan 	}
327e285ef8dSPeter Grehan 
328851d84f1SNeel Natu 	eax = val;
329851d84f1SNeel Natu 	error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RAX, eax);
330851d84f1SNeel Natu 	assert(error == 0);
331851d84f1SNeel Natu 
332851d84f1SNeel Natu 	edx = val >> 32;
333851d84f1SNeel Natu 	error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RDX, edx);
334851d84f1SNeel Natu 	assert(error == 0);
335851d84f1SNeel Natu 
336851d84f1SNeel Natu 	return (VMEXIT_CONTINUE);
337851d84f1SNeel Natu }
338851d84f1SNeel Natu 
339e285ef8dSPeter Grehan static int
340e285ef8dSPeter Grehan vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
341e285ef8dSPeter Grehan {
342851d84f1SNeel Natu 	int error;
343e285ef8dSPeter Grehan 
344851d84f1SNeel Natu 	error = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code, vme->u.msr.wval);
345851d84f1SNeel Natu 	if (error != 0) {
346851d84f1SNeel Natu 		fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n",
347851d84f1SNeel Natu 		    vme->u.msr.code, vme->u.msr.wval, *pvcpu);
348851d84f1SNeel Natu 		if (strictmsr)
349851d84f1SNeel Natu 			return (VMEXIT_ABORT);
350851d84f1SNeel Natu 	}
351851d84f1SNeel Natu 	return (VMEXIT_CONTINUE);
352e285ef8dSPeter Grehan }
353e285ef8dSPeter Grehan 
354e285ef8dSPeter Grehan static int
355e285ef8dSPeter Grehan vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
356e285ef8dSPeter Grehan {
357e285ef8dSPeter Grehan 	int newcpu;
358e285ef8dSPeter Grehan 	int retval = VMEXIT_CONTINUE;
359e285ef8dSPeter Grehan 
360e285ef8dSPeter Grehan 	newcpu = spinup_ap(ctx, *pvcpu,
361e285ef8dSPeter Grehan 			   vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip);
362e285ef8dSPeter Grehan 
363e285ef8dSPeter Grehan 	return (retval);
364e285ef8dSPeter Grehan }
365e285ef8dSPeter Grehan 
366e285ef8dSPeter Grehan static int
3671c052192SNeel Natu vmexit_spindown_cpu(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
3681c052192SNeel Natu {
3691c052192SNeel Natu 	int lastcpu;
3701c052192SNeel Natu 
3711c052192SNeel Natu 	lastcpu = fbsdrun_deletecpu(ctx, *pvcpu);
3721c052192SNeel Natu 	if (!lastcpu)
3731c052192SNeel Natu 		pthread_exit(NULL);
3741c052192SNeel Natu 	return (vmexit_catch_reset());
3751c052192SNeel Natu }
3761c052192SNeel Natu 
3771c052192SNeel Natu static int
378e285ef8dSPeter Grehan vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
379e285ef8dSPeter Grehan {
380e285ef8dSPeter Grehan 
381e285ef8dSPeter Grehan 	fprintf(stderr, "vm exit[%d]\n", *pvcpu);
382e285ef8dSPeter Grehan 	fprintf(stderr, "\treason\t\tVMX\n");
383e285ef8dSPeter Grehan 	fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip);
384e285ef8dSPeter Grehan 	fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length);
385e285ef8dSPeter Grehan 	fprintf(stderr, "\terror\t\t%d\n", vmexit->u.vmx.error);
386e285ef8dSPeter Grehan 	fprintf(stderr, "\texit_reason\t%u\n", vmexit->u.vmx.exit_reason);
387e285ef8dSPeter Grehan 	fprintf(stderr, "\tqualification\t0x%016lx\n",
388e285ef8dSPeter Grehan 	    vmexit->u.vmx.exit_qualification);
389e285ef8dSPeter Grehan 
390e285ef8dSPeter Grehan 	return (VMEXIT_ABORT);
391e285ef8dSPeter Grehan }
392e285ef8dSPeter Grehan 
393e285ef8dSPeter Grehan static int
394e285ef8dSPeter Grehan vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
395e285ef8dSPeter Grehan {
39694c3b3bfSPeter Grehan 
397e285ef8dSPeter Grehan 	stats.vmexit_bogus++;
398e285ef8dSPeter Grehan 
399e285ef8dSPeter Grehan 	return (VMEXIT_RESTART);
400e285ef8dSPeter Grehan }
401e285ef8dSPeter Grehan 
402e285ef8dSPeter Grehan static int
403e285ef8dSPeter Grehan vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
404e285ef8dSPeter Grehan {
40594c3b3bfSPeter Grehan 
406e285ef8dSPeter Grehan 	stats.vmexit_hlt++;
40794c3b3bfSPeter Grehan 
408e285ef8dSPeter Grehan 	/*
409e285ef8dSPeter Grehan 	 * Just continue execution with the next instruction. We use
410e285ef8dSPeter Grehan 	 * the HLT VM exit as a way to be friendly with the host
411e285ef8dSPeter Grehan 	 * scheduler.
412e285ef8dSPeter Grehan 	 */
413e285ef8dSPeter Grehan 	return (VMEXIT_CONTINUE);
414e285ef8dSPeter Grehan }
415e285ef8dSPeter Grehan 
416e285ef8dSPeter Grehan static int
417e285ef8dSPeter Grehan vmexit_pause(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
418e285ef8dSPeter Grehan {
41994c3b3bfSPeter Grehan 
420e285ef8dSPeter Grehan 	stats.vmexit_pause++;
421e285ef8dSPeter Grehan 
422e285ef8dSPeter Grehan 	return (VMEXIT_CONTINUE);
423e285ef8dSPeter Grehan }
424e285ef8dSPeter Grehan 
425e285ef8dSPeter Grehan static int
426e285ef8dSPeter Grehan vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
427e285ef8dSPeter Grehan {
42894c3b3bfSPeter Grehan 
429e285ef8dSPeter Grehan 	stats.vmexit_mtrap++;
430e285ef8dSPeter Grehan 
431e285ef8dSPeter Grehan 	return (VMEXIT_RESTART);
432e285ef8dSPeter Grehan }
433e285ef8dSPeter Grehan 
434e285ef8dSPeter Grehan static int
435318224bbSNeel Natu vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
436e285ef8dSPeter Grehan {
437e285ef8dSPeter Grehan 	int err;
438318224bbSNeel Natu 	stats.vmexit_inst_emul++;
439e285ef8dSPeter Grehan 
440318224bbSNeel Natu 	err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa,
441318224bbSNeel Natu 			  &vmexit->u.inst_emul.vie);
442e285ef8dSPeter Grehan 
443e285ef8dSPeter Grehan 	if (err) {
444e285ef8dSPeter Grehan 		if (err == EINVAL) {
445e285ef8dSPeter Grehan 			fprintf(stderr,
446e285ef8dSPeter Grehan 			    "Failed to emulate instruction at 0x%lx\n",
447e285ef8dSPeter Grehan 			    vmexit->rip);
448e285ef8dSPeter Grehan 		} else if (err == ESRCH) {
449e285ef8dSPeter Grehan 			fprintf(stderr, "Unhandled memory access to 0x%lx\n",
450318224bbSNeel Natu 			    vmexit->u.inst_emul.gpa);
451e285ef8dSPeter Grehan 		}
452e285ef8dSPeter Grehan 
453e285ef8dSPeter Grehan 		return (VMEXIT_ABORT);
454e285ef8dSPeter Grehan 	}
455e285ef8dSPeter Grehan 
456e285ef8dSPeter Grehan 	return (VMEXIT_CONTINUE);
457e285ef8dSPeter Grehan }
458e285ef8dSPeter Grehan 
459e285ef8dSPeter Grehan static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
460e285ef8dSPeter Grehan 	[VM_EXITCODE_INOUT]  = vmexit_inout,
461e285ef8dSPeter Grehan 	[VM_EXITCODE_VMX]    = vmexit_vmx,
462e285ef8dSPeter Grehan 	[VM_EXITCODE_BOGUS]  = vmexit_bogus,
463e285ef8dSPeter Grehan 	[VM_EXITCODE_RDMSR]  = vmexit_rdmsr,
464e285ef8dSPeter Grehan 	[VM_EXITCODE_WRMSR]  = vmexit_wrmsr,
465e285ef8dSPeter Grehan 	[VM_EXITCODE_MTRAP]  = vmexit_mtrap,
466318224bbSNeel Natu 	[VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
467e285ef8dSPeter Grehan 	[VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap,
4681c052192SNeel Natu 	[VM_EXITCODE_SPINDOWN_CPU] = vmexit_spindown_cpu,
469e285ef8dSPeter Grehan };
470e285ef8dSPeter Grehan 
471e285ef8dSPeter Grehan static void
472e285ef8dSPeter Grehan vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
473e285ef8dSPeter Grehan {
474485b3300SNeel Natu 	cpuset_t mask;
475e285ef8dSPeter Grehan 	int error, rc, prevcpu;
4768b271170SPeter Grehan 	enum vm_exitcode exitcode;
477e285ef8dSPeter Grehan 
478e285ef8dSPeter Grehan 	if (pincpu >= 0) {
479485b3300SNeel Natu 		CPU_ZERO(&mask);
480485b3300SNeel Natu 		CPU_SET(pincpu + vcpu, &mask);
481485b3300SNeel Natu 		error = pthread_setaffinity_np(pthread_self(),
482485b3300SNeel Natu 					       sizeof(mask), &mask);
483e285ef8dSPeter Grehan 		assert(error == 0);
484e285ef8dSPeter Grehan 	}
485e285ef8dSPeter Grehan 
486e285ef8dSPeter Grehan 	while (1) {
487e285ef8dSPeter Grehan 		error = vm_run(ctx, vcpu, rip, &vmexit[vcpu]);
488*f80330a8SNeel Natu 		if (error != 0)
489e285ef8dSPeter Grehan 			break;
490e285ef8dSPeter Grehan 
491e285ef8dSPeter Grehan 		prevcpu = vcpu;
4928b271170SPeter Grehan 
4938b271170SPeter Grehan 		exitcode = vmexit[vcpu].exitcode;
4948b271170SPeter Grehan 		if (exitcode >= VM_EXITCODE_MAX || handler[exitcode] == NULL) {
4958b271170SPeter Grehan 			fprintf(stderr, "vm_loop: unexpected exitcode 0x%x\n",
4968b271170SPeter Grehan 			    exitcode);
4978b271170SPeter Grehan 			exit(1);
4988b271170SPeter Grehan 		}
4998b271170SPeter Grehan 
5008b271170SPeter Grehan                 rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu);
5018b271170SPeter Grehan 
502e285ef8dSPeter Grehan 		switch (rc) {
503e285ef8dSPeter Grehan 		case VMEXIT_CONTINUE:
504e285ef8dSPeter Grehan                         rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length;
505e285ef8dSPeter Grehan 			break;
506e285ef8dSPeter Grehan 		case VMEXIT_RESTART:
507e285ef8dSPeter Grehan                         rip = vmexit[vcpu].rip;
508e285ef8dSPeter Grehan 			break;
509e285ef8dSPeter Grehan 		case VMEXIT_RESET:
510e285ef8dSPeter Grehan 			exit(0);
511e285ef8dSPeter Grehan 		default:
512e285ef8dSPeter Grehan 			exit(1);
513e285ef8dSPeter Grehan 		}
514e285ef8dSPeter Grehan 	}
515e285ef8dSPeter Grehan 	fprintf(stderr, "vm_run error %d, errno %d\n", error, errno);
516e285ef8dSPeter Grehan }
517e285ef8dSPeter Grehan 
5185f0677d3SNeel Natu static int
5195f0677d3SNeel Natu num_vcpus_allowed(struct vmctx *ctx)
5205f0677d3SNeel Natu {
5215f0677d3SNeel Natu 	int tmp, error;
5225f0677d3SNeel Natu 
5235f0677d3SNeel Natu 	error = vm_get_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, &tmp);
5245f0677d3SNeel Natu 
5255f0677d3SNeel Natu 	/*
5265f0677d3SNeel Natu 	 * The guest is allowed to spinup more than one processor only if the
5275f0677d3SNeel Natu 	 * UNRESTRICTED_GUEST capability is available.
5285f0677d3SNeel Natu 	 */
5295f0677d3SNeel Natu 	if (error == 0)
5305f0677d3SNeel Natu 		return (VM_MAXCPU);
5315f0677d3SNeel Natu 	else
5325f0677d3SNeel Natu 		return (1);
5335f0677d3SNeel Natu }
534e285ef8dSPeter Grehan 
53549cc03daSNeel Natu void
53649cc03daSNeel Natu fbsdrun_set_capabilities(struct vmctx *ctx, int cpu)
53749cc03daSNeel Natu {
53849cc03daSNeel Natu 	int err, tmp;
53949cc03daSNeel Natu 
54049cc03daSNeel Natu 	if (fbsdrun_vmexit_on_hlt()) {
54149cc03daSNeel Natu 		err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp);
54249cc03daSNeel Natu 		if (err < 0) {
54349cc03daSNeel Natu 			fprintf(stderr, "VM exit on HLT not supported\n");
54449cc03daSNeel Natu 			exit(1);
54549cc03daSNeel Natu 		}
54649cc03daSNeel Natu 		vm_set_capability(ctx, cpu, VM_CAP_HALT_EXIT, 1);
54749cc03daSNeel Natu 		if (cpu == BSP)
54849cc03daSNeel Natu 			handler[VM_EXITCODE_HLT] = vmexit_hlt;
54949cc03daSNeel Natu 	}
55049cc03daSNeel Natu 
55149cc03daSNeel Natu         if (fbsdrun_vmexit_on_pause()) {
55249cc03daSNeel Natu 		/*
55349cc03daSNeel Natu 		 * pause exit support required for this mode
55449cc03daSNeel Natu 		 */
55549cc03daSNeel Natu 		err = vm_get_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, &tmp);
55649cc03daSNeel Natu 		if (err < 0) {
55749cc03daSNeel Natu 			fprintf(stderr,
55849cc03daSNeel Natu 			    "SMP mux requested, no pause support\n");
55949cc03daSNeel Natu 			exit(1);
56049cc03daSNeel Natu 		}
56149cc03daSNeel Natu 		vm_set_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, 1);
56249cc03daSNeel Natu 		if (cpu == BSP)
56349cc03daSNeel Natu 			handler[VM_EXITCODE_PAUSE] = vmexit_pause;
56449cc03daSNeel Natu         }
56549cc03daSNeel Natu 
56649cc03daSNeel Natu 	if (fbsdrun_disable_x2apic())
56749cc03daSNeel Natu 		err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED);
56849cc03daSNeel Natu 	else
56949cc03daSNeel Natu 		err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED);
57049cc03daSNeel Natu 
57149cc03daSNeel Natu 	if (err) {
57249cc03daSNeel Natu 		fprintf(stderr, "Unable to set x2apic state (%d)\n", err);
57349cc03daSNeel Natu 		exit(1);
57449cc03daSNeel Natu 	}
57549cc03daSNeel Natu 
57649cc03daSNeel Natu 	vm_set_capability(ctx, cpu, VM_CAP_ENABLE_INVPCID, 1);
57749cc03daSNeel Natu }
57849cc03daSNeel Natu 
579e285ef8dSPeter Grehan int
580e285ef8dSPeter Grehan main(int argc, char *argv[])
581e285ef8dSPeter Grehan {
582a1a4cbeaSNeel Natu 	int c, error, gdb_port, err, bvmcons;
5835f0677d3SNeel Natu 	int max_vcpus;
584e285ef8dSPeter Grehan 	struct vmctx *ctx;
585e285ef8dSPeter Grehan 	uint64_t rip;
586b060ba50SNeel Natu 	size_t memsize;
587e285ef8dSPeter Grehan 
588e285ef8dSPeter Grehan 	bvmcons = 0;
589e285ef8dSPeter Grehan 	progname = basename(argv[0]);
5904a06a0feSNeel Natu 	gdb_port = 0;
591e285ef8dSPeter Grehan 	guest_ncpus = 1;
592b060ba50SNeel Natu 	memsize = 256 * MB;
593e285ef8dSPeter Grehan 
594851d84f1SNeel Natu 	while ((c = getopt(argc, argv, "abehwAHIPWp:g:c:s:S:m:l:")) != -1) {
595e285ef8dSPeter Grehan 		switch (c) {
596e285ef8dSPeter Grehan 		case 'a':
597e285ef8dSPeter Grehan 			disable_x2apic = 1;
598e285ef8dSPeter Grehan 			break;
599e285ef8dSPeter Grehan 		case 'A':
600e285ef8dSPeter Grehan 			acpi = 1;
601e285ef8dSPeter Grehan 			break;
602e285ef8dSPeter Grehan 		case 'b':
603e285ef8dSPeter Grehan 			bvmcons = 1;
604e285ef8dSPeter Grehan 			break;
605e285ef8dSPeter Grehan 		case 'p':
606e285ef8dSPeter Grehan 			pincpu = atoi(optarg);
607e285ef8dSPeter Grehan 			break;
608e285ef8dSPeter Grehan                 case 'c':
609e285ef8dSPeter Grehan 			guest_ncpus = atoi(optarg);
610e285ef8dSPeter Grehan 			break;
611e285ef8dSPeter Grehan 		case 'g':
612e285ef8dSPeter Grehan 			gdb_port = atoi(optarg);
613e285ef8dSPeter Grehan 			break;
614ea7f1c8cSNeel Natu 		case 'l':
615ea7f1c8cSNeel Natu 			if (lpc_device_parse(optarg) != 0) {
616ea7f1c8cSNeel Natu 				errx(EX_USAGE, "invalid lpc device "
617ea7f1c8cSNeel Natu 				    "configuration '%s'", optarg);
618ea7f1c8cSNeel Natu 			}
619ea7f1c8cSNeel Natu 			break;
620e285ef8dSPeter Grehan 		case 's':
621b05c77ffSNeel Natu 			if (pci_parse_slot(optarg, 0) != 0)
622b05c77ffSNeel Natu 				exit(1);
623b05c77ffSNeel Natu 			else
624e285ef8dSPeter Grehan 				break;
625e285ef8dSPeter Grehan 		case 'S':
626b05c77ffSNeel Natu 			if (pci_parse_slot(optarg, 1) != 0)
627b05c77ffSNeel Natu 				exit(1);
628b05c77ffSNeel Natu 			else
629e285ef8dSPeter Grehan 				break;
630e285ef8dSPeter Grehan                 case 'm':
631200758f1SNeel Natu 			error = vm_parse_memsize(optarg, &memsize);
632200758f1SNeel Natu 			if (error)
633200758f1SNeel Natu 				errx(EX_USAGE, "invalid memsize '%s'", optarg);
634e285ef8dSPeter Grehan 			break;
635e285ef8dSPeter Grehan 		case 'H':
636e285ef8dSPeter Grehan 			guest_vmexit_on_hlt = 1;
637e285ef8dSPeter Grehan 			break;
638e285ef8dSPeter Grehan 		case 'I':
639a1a4cbeaSNeel Natu 			/*
640a1a4cbeaSNeel Natu 			 * The "-I" option was used to add an ioapic to the
641a1a4cbeaSNeel Natu 			 * virtual machine.
642a1a4cbeaSNeel Natu 			 *
643a1a4cbeaSNeel Natu 			 * An ioapic is now provided unconditionally for each
644a1a4cbeaSNeel Natu 			 * virtual machine and this option is now deprecated.
645a1a4cbeaSNeel Natu 			 */
646e285ef8dSPeter Grehan 			break;
647e285ef8dSPeter Grehan 		case 'P':
648e285ef8dSPeter Grehan 			guest_vmexit_on_pause = 1;
649e285ef8dSPeter Grehan 			break;
650e285ef8dSPeter Grehan 		case 'e':
651e285ef8dSPeter Grehan 			strictio = 1;
652e285ef8dSPeter Grehan 			break;
653851d84f1SNeel Natu 		case 'w':
654851d84f1SNeel Natu 			strictmsr = 0;
655851d84f1SNeel Natu 			break;
656062b878fSPeter Grehan 		case 'W':
657062b878fSPeter Grehan 			virtio_msix = 0;
658062b878fSPeter Grehan 			break;
659e285ef8dSPeter Grehan 		case 'h':
660e285ef8dSPeter Grehan 			usage(0);
661e285ef8dSPeter Grehan 		default:
662e285ef8dSPeter Grehan 			usage(1);
663e285ef8dSPeter Grehan 		}
664e285ef8dSPeter Grehan 	}
665e285ef8dSPeter Grehan 	argc -= optind;
666e285ef8dSPeter Grehan 	argv += optind;
667e285ef8dSPeter Grehan 
668e285ef8dSPeter Grehan 	if (argc != 1)
669e285ef8dSPeter Grehan 		usage(1);
670e285ef8dSPeter Grehan 
671e285ef8dSPeter Grehan 	vmname = argv[0];
672e285ef8dSPeter Grehan 
673e285ef8dSPeter Grehan 	ctx = vm_open(vmname);
674e285ef8dSPeter Grehan 	if (ctx == NULL) {
675e285ef8dSPeter Grehan 		perror("vm_open");
676e285ef8dSPeter Grehan 		exit(1);
677e285ef8dSPeter Grehan 	}
678e285ef8dSPeter Grehan 
6795f0677d3SNeel Natu 	max_vcpus = num_vcpus_allowed(ctx);
6805f0677d3SNeel Natu 	if (guest_ncpus > max_vcpus) {
6815f0677d3SNeel Natu 		fprintf(stderr, "%d vCPUs requested but only %d available\n",
6825f0677d3SNeel Natu 			guest_ncpus, max_vcpus);
6835f0677d3SNeel Natu 		exit(1);
6845f0677d3SNeel Natu 	}
6855f0677d3SNeel Natu 
68649cc03daSNeel Natu 	fbsdrun_set_capabilities(ctx, BSP);
687e285ef8dSPeter Grehan 
688b060ba50SNeel Natu 	err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL);
689b060ba50SNeel Natu 	if (err) {
690b060ba50SNeel Natu 		fprintf(stderr, "Unable to setup memory (%d)\n", err);
691b060ba50SNeel Natu 		exit(1);
692e285ef8dSPeter Grehan 	}
693e285ef8dSPeter Grehan 
6940e2ca4e6SNeel Natu 	init_mem();
695e285ef8dSPeter Grehan 	init_inout();
696ea7f1c8cSNeel Natu 	legacy_irq_init();
697a38e2a64SPeter Grehan 
6989d6be09fSPeter Grehan 	rtc_init(ctx);
6999d6be09fSPeter Grehan 
700a38e2a64SPeter Grehan 	/*
701a38e2a64SPeter Grehan 	 * Exit if a device emulation finds an error in it's initilization
702a38e2a64SPeter Grehan 	 */
703a38e2a64SPeter Grehan 	if (init_pci(ctx) != 0)
704a38e2a64SPeter Grehan 		exit(1);
705a38e2a64SPeter Grehan 
706e285ef8dSPeter Grehan 	if (gdb_port != 0)
707e285ef8dSPeter Grehan 		init_dbgport(gdb_port);
708e285ef8dSPeter Grehan 
709e285ef8dSPeter Grehan 	if (bvmcons)
710e285ef8dSPeter Grehan 		init_bvmcons();
711e285ef8dSPeter Grehan 
712e285ef8dSPeter Grehan 	error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip);
713e285ef8dSPeter Grehan 	assert(error == 0);
714e285ef8dSPeter Grehan 
715e285ef8dSPeter Grehan 	/*
716e285ef8dSPeter Grehan 	 * build the guest tables, MP etc.
717e285ef8dSPeter Grehan 	 */
718a1a4cbeaSNeel Natu 	mptable_build(ctx, guest_ncpus);
719e285ef8dSPeter Grehan 
720e285ef8dSPeter Grehan 	if (acpi) {
721a1a4cbeaSNeel Natu 		error = acpi_build(ctx, guest_ncpus);
722e285ef8dSPeter Grehan 		assert(error == 0);
723e285ef8dSPeter Grehan 	}
724e285ef8dSPeter Grehan 
725e285ef8dSPeter Grehan 	/*
7267f5487acSPeter Grehan 	 * Change the proc title to include the VM name.
7277f5487acSPeter Grehan 	 */
7287f5487acSPeter Grehan 	setproctitle("%s", vmname);
7297f5487acSPeter Grehan 
7307f5487acSPeter Grehan 	/*
731e285ef8dSPeter Grehan 	 * Add CPU 0
732e285ef8dSPeter Grehan 	 */
733e285ef8dSPeter Grehan 	fbsdrun_addcpu(ctx, BSP, rip);
734e285ef8dSPeter Grehan 
735e285ef8dSPeter Grehan 	/*
736e285ef8dSPeter Grehan 	 * Head off to the main event dispatch loop
737e285ef8dSPeter Grehan 	 */
738e285ef8dSPeter Grehan 	mevent_dispatch();
739e285ef8dSPeter Grehan 
740e285ef8dSPeter Grehan 	exit(1);
741e285ef8dSPeter Grehan }
742