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