xref: /freebsd/lib/libvmmapi/amd64/vmmapi_freebsd_machdep.c (revision 967264cfcd6d1a42dc0c9f9916320afe90f1faa4)
1*967264cfSMark Johnston /*-
2*967264cfSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
3*967264cfSMark Johnston  *
4*967264cfSMark Johnston  * Copyright (c) 2011 NetApp, Inc.
5*967264cfSMark Johnston  * All rights reserved.
6*967264cfSMark Johnston  *
7*967264cfSMark Johnston  * Redistribution and use in source and binary forms, with or without
8*967264cfSMark Johnston  * modification, are permitted provided that the following conditions
9*967264cfSMark Johnston  * are met:
10*967264cfSMark Johnston  * 1. Redistributions of source code must retain the above copyright
11*967264cfSMark Johnston  *    notice, this list of conditions and the following disclaimer.
12*967264cfSMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
13*967264cfSMark Johnston  *    notice, this list of conditions and the following disclaimer in the
14*967264cfSMark Johnston  *    documentation and/or other materials provided with the distribution.
15*967264cfSMark Johnston  *
16*967264cfSMark Johnston  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17*967264cfSMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*967264cfSMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*967264cfSMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20*967264cfSMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*967264cfSMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*967264cfSMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*967264cfSMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*967264cfSMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*967264cfSMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*967264cfSMark Johnston  * SUCH DAMAGE.
27*967264cfSMark Johnston  */
28*967264cfSMark Johnston 
29*967264cfSMark Johnston #include <sys/types.h>
30*967264cfSMark Johnston 
31*967264cfSMark Johnston #include <machine/specialreg.h>
32*967264cfSMark Johnston #include <machine/segments.h>
33*967264cfSMark Johnston #include <machine/vmm.h>
34*967264cfSMark Johnston 
35*967264cfSMark Johnston #include <errno.h>
36*967264cfSMark Johnston #include <string.h>
37*967264cfSMark Johnston 
38*967264cfSMark Johnston #include "vmmapi.h"
39*967264cfSMark Johnston #include "internal.h"
40*967264cfSMark Johnston 
41*967264cfSMark Johnston #define	I386_TSS_SIZE		104
42*967264cfSMark Johnston 
43*967264cfSMark Johnston #define	DESC_PRESENT		0x00000080
44*967264cfSMark Johnston #define	DESC_LONGMODE		0x00002000
45*967264cfSMark Johnston #define	DESC_DEF32		0x00004000
46*967264cfSMark Johnston #define	DESC_GRAN		0x00008000
47*967264cfSMark Johnston #define	DESC_UNUSABLE		0x00010000
48*967264cfSMark Johnston 
49*967264cfSMark Johnston #define	GUEST_NULL_SEL		0
50*967264cfSMark Johnston #define	GUEST_CODE_SEL		1
51*967264cfSMark Johnston #define	GUEST_DATA_SEL		2
52*967264cfSMark Johnston #define	GUEST_TSS_SEL		3
53*967264cfSMark Johnston #define	GUEST_GDTR_LIMIT64	(3 * 8 - 1)
54*967264cfSMark Johnston 
55*967264cfSMark Johnston static struct segment_descriptor i386_gdt[] = {
56*967264cfSMark Johnston 	{},						/* NULL */
57*967264cfSMark Johnston 	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMER,	/* CODE */
58*967264cfSMark Johnston 	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
59*967264cfSMark Johnston 	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW,	/* DATA */
60*967264cfSMark Johnston 	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
61*967264cfSMark Johnston 	{ .sd_lolimit = I386_TSS_SIZE - 1,		/* TSS */
62*967264cfSMark Johnston 	  .sd_type = SDT_SYS386TSS, .sd_p = 1 }
63*967264cfSMark Johnston };
64*967264cfSMark Johnston 
65*967264cfSMark Johnston /*
66*967264cfSMark Johnston  * Setup the 'vcpu' register set such that it will begin execution at
67*967264cfSMark Johnston  * 'eip' in flat mode.
68*967264cfSMark Johnston  */
69*967264cfSMark Johnston int
70*967264cfSMark Johnston vm_setup_freebsd_registers_i386(struct vcpu *vcpu, uint32_t eip,
71*967264cfSMark Johnston 				uint32_t gdtbase, uint32_t esp)
72*967264cfSMark Johnston {
73*967264cfSMark Johnston 	uint64_t cr0, rflags, desc_base;
74*967264cfSMark Johnston 	uint32_t desc_access, desc_limit, tssbase;
75*967264cfSMark Johnston 	uint16_t gsel;
76*967264cfSMark Johnston 	struct segment_descriptor *gdt;
77*967264cfSMark Johnston 	int error, tmp;
78*967264cfSMark Johnston 
79*967264cfSMark Johnston 	/* A 32-bit guest requires unrestricted mode. */
80*967264cfSMark Johnston 	error = vm_get_capability(vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp);
81*967264cfSMark Johnston 	if (error)
82*967264cfSMark Johnston 		goto done;
83*967264cfSMark Johnston 	error = vm_set_capability(vcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
84*967264cfSMark Johnston 	if (error)
85*967264cfSMark Johnston 		goto done;
86*967264cfSMark Johnston 
87*967264cfSMark Johnston 	cr0 = CR0_PE | CR0_NE;
88*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
89*967264cfSMark Johnston 		goto done;
90*967264cfSMark Johnston 
91*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR4, 0)) != 0)
92*967264cfSMark Johnston 		goto done;
93*967264cfSMark Johnston 
94*967264cfSMark Johnston 	/*
95*967264cfSMark Johnston 	 * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest
96*967264cfSMark Johnston 	 * mode" entry control.
97*967264cfSMark Johnston 	 */
98*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_EFER, 0)))
99*967264cfSMark Johnston 		goto done;
100*967264cfSMark Johnston 
101*967264cfSMark Johnston 	gdt = vm_map_gpa(vcpu->ctx, gdtbase, 0x1000);
102*967264cfSMark Johnston 	if (gdt == NULL)
103*967264cfSMark Johnston 		return (EFAULT);
104*967264cfSMark Johnston 	memcpy(gdt, i386_gdt, sizeof(i386_gdt));
105*967264cfSMark Johnston 	desc_base = gdtbase;
106*967264cfSMark Johnston 	desc_limit = sizeof(i386_gdt) - 1;
107*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR,
108*967264cfSMark Johnston 			    desc_base, desc_limit, 0);
109*967264cfSMark Johnston 	if (error != 0)
110*967264cfSMark Johnston 		goto done;
111*967264cfSMark Johnston 
112*967264cfSMark Johnston 	/* Place the TSS one page above the GDT. */
113*967264cfSMark Johnston 	tssbase = gdtbase + 0x1000;
114*967264cfSMark Johnston 	gdt[3].sd_lobase = tssbase;
115*967264cfSMark Johnston 
116*967264cfSMark Johnston 	rflags = 0x2;
117*967264cfSMark Johnston 	error = vm_set_register(vcpu, VM_REG_GUEST_RFLAGS, rflags);
118*967264cfSMark Johnston 	if (error)
119*967264cfSMark Johnston 		goto done;
120*967264cfSMark Johnston 
121*967264cfSMark Johnston 	desc_base = 0;
122*967264cfSMark Johnston 	desc_limit = 0xffffffff;
123*967264cfSMark Johnston 	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA;
124*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_CS,
125*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
126*967264cfSMark Johnston 
127*967264cfSMark Johnston 	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA;
128*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_DS,
129*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
130*967264cfSMark Johnston 	if (error)
131*967264cfSMark Johnston 		goto done;
132*967264cfSMark Johnston 
133*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_ES,
134*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
135*967264cfSMark Johnston 	if (error)
136*967264cfSMark Johnston 		goto done;
137*967264cfSMark Johnston 
138*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_FS,
139*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
140*967264cfSMark Johnston 	if (error)
141*967264cfSMark Johnston 		goto done;
142*967264cfSMark Johnston 
143*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_GS,
144*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
145*967264cfSMark Johnston 	if (error)
146*967264cfSMark Johnston 		goto done;
147*967264cfSMark Johnston 
148*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_SS,
149*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
150*967264cfSMark Johnston 	if (error)
151*967264cfSMark Johnston 		goto done;
152*967264cfSMark Johnston 
153*967264cfSMark Johnston 	desc_base = tssbase;
154*967264cfSMark Johnston 	desc_limit = I386_TSS_SIZE - 1;
155*967264cfSMark Johnston 	desc_access = DESC_PRESENT | SDT_SYS386BSY;
156*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_TR,
157*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
158*967264cfSMark Johnston 	if (error)
159*967264cfSMark Johnston 		goto done;
160*967264cfSMark Johnston 
161*967264cfSMark Johnston 
162*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_LDTR, 0, 0,
163*967264cfSMark Johnston 			    DESC_UNUSABLE);
164*967264cfSMark Johnston 	if (error)
165*967264cfSMark Johnston 		goto done;
166*967264cfSMark Johnston 
167*967264cfSMark Johnston 	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
168*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CS, gsel)) != 0)
169*967264cfSMark Johnston 		goto done;
170*967264cfSMark Johnston 
171*967264cfSMark Johnston 	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
172*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_DS, gsel)) != 0)
173*967264cfSMark Johnston 		goto done;
174*967264cfSMark Johnston 
175*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_ES, gsel)) != 0)
176*967264cfSMark Johnston 		goto done;
177*967264cfSMark Johnston 
178*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_FS, gsel)) != 0)
179*967264cfSMark Johnston 		goto done;
180*967264cfSMark Johnston 
181*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_GS, gsel)) != 0)
182*967264cfSMark Johnston 		goto done;
183*967264cfSMark Johnston 
184*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_SS, gsel)) != 0)
185*967264cfSMark Johnston 		goto done;
186*967264cfSMark Johnston 
187*967264cfSMark Johnston 	gsel = GSEL(GUEST_TSS_SEL, SEL_KPL);
188*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_TR, gsel)) != 0)
189*967264cfSMark Johnston 		goto done;
190*967264cfSMark Johnston 
191*967264cfSMark Johnston 	/* LDTR is pointing to the null selector */
192*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
193*967264cfSMark Johnston 		goto done;
194*967264cfSMark Johnston 
195*967264cfSMark Johnston 	/* entry point */
196*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_RIP, eip)) != 0)
197*967264cfSMark Johnston 		goto done;
198*967264cfSMark Johnston 
199*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_RSP, esp)) != 0)
200*967264cfSMark Johnston 		goto done;
201*967264cfSMark Johnston 
202*967264cfSMark Johnston 	error = 0;
203*967264cfSMark Johnston done:
204*967264cfSMark Johnston 	return (error);
205*967264cfSMark Johnston }
206*967264cfSMark Johnston 
207*967264cfSMark Johnston void
208*967264cfSMark Johnston vm_setup_freebsd_gdt(uint64_t *gdtr)
209*967264cfSMark Johnston {
210*967264cfSMark Johnston 	gdtr[GUEST_NULL_SEL] = 0;
211*967264cfSMark Johnston 	gdtr[GUEST_CODE_SEL] = 0x0020980000000000;
212*967264cfSMark Johnston 	gdtr[GUEST_DATA_SEL] = 0x0000900000000000;
213*967264cfSMark Johnston }
214*967264cfSMark Johnston 
215*967264cfSMark Johnston /*
216*967264cfSMark Johnston  * Setup the 'vcpu' register set such that it will begin execution at
217*967264cfSMark Johnston  * 'rip' in long mode.
218*967264cfSMark Johnston  */
219*967264cfSMark Johnston int
220*967264cfSMark Johnston vm_setup_freebsd_registers(struct vcpu *vcpu,
221*967264cfSMark Johnston 			   uint64_t rip, uint64_t cr3, uint64_t gdtbase,
222*967264cfSMark Johnston 			   uint64_t rsp)
223*967264cfSMark Johnston {
224*967264cfSMark Johnston 	int error;
225*967264cfSMark Johnston 	uint64_t cr0, cr4, efer, rflags, desc_base;
226*967264cfSMark Johnston 	uint32_t desc_access, desc_limit;
227*967264cfSMark Johnston 	uint16_t gsel;
228*967264cfSMark Johnston 
229*967264cfSMark Johnston 	cr0 = CR0_PE | CR0_PG | CR0_NE;
230*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
231*967264cfSMark Johnston 		goto done;
232*967264cfSMark Johnston 
233*967264cfSMark Johnston 	cr4 = CR4_PAE;
234*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR4, cr4)) != 0)
235*967264cfSMark Johnston 		goto done;
236*967264cfSMark Johnston 
237*967264cfSMark Johnston 	efer = EFER_LME | EFER_LMA;
238*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_EFER, efer)))
239*967264cfSMark Johnston 		goto done;
240*967264cfSMark Johnston 
241*967264cfSMark Johnston 	rflags = 0x2;
242*967264cfSMark Johnston 	error = vm_set_register(vcpu, VM_REG_GUEST_RFLAGS, rflags);
243*967264cfSMark Johnston 	if (error)
244*967264cfSMark Johnston 		goto done;
245*967264cfSMark Johnston 
246*967264cfSMark Johnston 	desc_base = 0;
247*967264cfSMark Johnston 	desc_limit = 0;
248*967264cfSMark Johnston 	desc_access = 0x0000209B;
249*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_CS,
250*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
251*967264cfSMark Johnston 	if (error)
252*967264cfSMark Johnston 		goto done;
253*967264cfSMark Johnston 
254*967264cfSMark Johnston 	desc_access = 0x00000093;
255*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_DS,
256*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
257*967264cfSMark Johnston 	if (error)
258*967264cfSMark Johnston 		goto done;
259*967264cfSMark Johnston 
260*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_ES,
261*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
262*967264cfSMark Johnston 	if (error)
263*967264cfSMark Johnston 		goto done;
264*967264cfSMark Johnston 
265*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_FS,
266*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
267*967264cfSMark Johnston 	if (error)
268*967264cfSMark Johnston 		goto done;
269*967264cfSMark Johnston 
270*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_GS,
271*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
272*967264cfSMark Johnston 	if (error)
273*967264cfSMark Johnston 		goto done;
274*967264cfSMark Johnston 
275*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_SS,
276*967264cfSMark Johnston 			    desc_base, desc_limit, desc_access);
277*967264cfSMark Johnston 	if (error)
278*967264cfSMark Johnston 		goto done;
279*967264cfSMark Johnston 
280*967264cfSMark Johnston 	/*
281*967264cfSMark Johnston 	 * XXX TR is pointing to null selector even though we set the
282*967264cfSMark Johnston 	 * TSS segment to be usable with a base address and limit of 0.
283*967264cfSMark Johnston 	 */
284*967264cfSMark Johnston 	desc_access = 0x0000008b;
285*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_TR, 0, 0, desc_access);
286*967264cfSMark Johnston 	if (error)
287*967264cfSMark Johnston 		goto done;
288*967264cfSMark Johnston 
289*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_LDTR, 0, 0,
290*967264cfSMark Johnston 			    DESC_UNUSABLE);
291*967264cfSMark Johnston 	if (error)
292*967264cfSMark Johnston 		goto done;
293*967264cfSMark Johnston 
294*967264cfSMark Johnston 	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
295*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CS, gsel)) != 0)
296*967264cfSMark Johnston 		goto done;
297*967264cfSMark Johnston 
298*967264cfSMark Johnston 	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
299*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_DS, gsel)) != 0)
300*967264cfSMark Johnston 		goto done;
301*967264cfSMark Johnston 
302*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_ES, gsel)) != 0)
303*967264cfSMark Johnston 		goto done;
304*967264cfSMark Johnston 
305*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_FS, gsel)) != 0)
306*967264cfSMark Johnston 		goto done;
307*967264cfSMark Johnston 
308*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_GS, gsel)) != 0)
309*967264cfSMark Johnston 		goto done;
310*967264cfSMark Johnston 
311*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_SS, gsel)) != 0)
312*967264cfSMark Johnston 		goto done;
313*967264cfSMark Johnston 
314*967264cfSMark Johnston 	/* XXX TR is pointing to the null selector */
315*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_TR, 0)) != 0)
316*967264cfSMark Johnston 		goto done;
317*967264cfSMark Johnston 
318*967264cfSMark Johnston 	/* LDTR is pointing to the null selector */
319*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
320*967264cfSMark Johnston 		goto done;
321*967264cfSMark Johnston 
322*967264cfSMark Johnston 	/* entry point */
323*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_RIP, rip)) != 0)
324*967264cfSMark Johnston 		goto done;
325*967264cfSMark Johnston 
326*967264cfSMark Johnston 	/* page table base */
327*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR3, cr3)) != 0)
328*967264cfSMark Johnston 		goto done;
329*967264cfSMark Johnston 
330*967264cfSMark Johnston 	desc_base = gdtbase;
331*967264cfSMark Johnston 	desc_limit = GUEST_GDTR_LIMIT64;
332*967264cfSMark Johnston 	error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR,
333*967264cfSMark Johnston 			    desc_base, desc_limit, 0);
334*967264cfSMark Johnston 	if (error != 0)
335*967264cfSMark Johnston 		goto done;
336*967264cfSMark Johnston 
337*967264cfSMark Johnston 	if ((error = vm_set_register(vcpu, VM_REG_GUEST_RSP, rsp)) != 0)
338*967264cfSMark Johnston 		goto done;
339*967264cfSMark Johnston 
340*967264cfSMark Johnston 	error = 0;
341*967264cfSMark Johnston done:
342*967264cfSMark Johnston 	return (error);
343*967264cfSMark Johnston }
344