xref: /trueos/lib/libvmmapi/vmmapi_freebsd.c (revision 9a6c2fa2dad43a5cae7c895556a556d70fc66de9)
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$
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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 
57 static 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  */
71 int
vm_setup_freebsd_registers_i386(struct vmctx * vmctx,int vcpu,uint32_t eip,uint32_t gdtbase,uint32_t esp)72 vm_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;
205 done:
206 	return (error);
207 }
208 
209 void
vm_setup_freebsd_gdt(uint64_t * gdtr)210 vm_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  */
221 int
vm_setup_freebsd_registers(struct vmctx * vmctx,int vcpu,uint64_t rip,uint64_t cr3,uint64_t gdtbase,uint64_t rsp)222 vm_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;
343 done:
344 	return (error);
345 }
346