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