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