1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $FreeBSD$
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/types.h>
35
36#include <machine/specialreg.h>
37#include <machine/segments.h>
38#include <machine/vmm.h>
39
40#include <errno.h>
41#include <string.h>
42
43#include "vmmapi.h"
44
45#define	I386_TSS_SIZE		104
46
47#define	DESC_PRESENT		0x00000080
48#define	DESC_LONGMODE		0x00002000
49#define	DESC_DEF32		0x00004000
50#define	DESC_GRAN		0x00008000
51#define	DESC_UNUSABLE		0x00010000
52
53#define	GUEST_NULL_SEL		0
54#define	GUEST_CODE_SEL		1
55#define	GUEST_DATA_SEL		2
56#define	GUEST_TSS_SEL		3
57#define	GUEST_GDTR_LIMIT64	(3 * 8 - 1)
58
59static struct segment_descriptor i386_gdt[] = {
60	{},						/* NULL */
61	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMER,	/* CODE */
62	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
63	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW,	/* DATA */
64	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
65	{ .sd_lolimit = I386_TSS_SIZE - 1,		/* TSS */
66	  .sd_type = SDT_SYS386TSS, .sd_p = 1 }
67};
68
69/*
70 * Setup the 'vcpu' register set such that it will begin execution at
71 * 'eip' in flat mode.
72 */
73int
74vm_setup_freebsd_registers_i386(struct vmctx *vmctx, int vcpu, uint32_t eip,
75				uint32_t gdtbase, uint32_t esp)
76{
77	uint64_t cr0, rflags, desc_base;
78	uint32_t desc_access, desc_limit, tssbase;
79	uint16_t gsel;
80	struct segment_descriptor *gdt;
81	int error, tmp;
82
83	/* A 32-bit guest requires unrestricted mode. */
84	error = vm_get_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp);
85	if (error)
86		goto done;
87	error = vm_set_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
88	if (error)
89		goto done;
90
91	cr0 = CR0_PE | CR0_NE;
92	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
93		goto done;
94
95	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, 0)) != 0)
96		goto done;
97
98	/*
99	 * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest
100	 * mode" entry control.
101	 */
102	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, 0)))
103		goto done;
104
105	gdt = vm_map_gpa(vmctx, gdtbase, 0x1000);
106	if (gdt == NULL)
107		return (EFAULT);
108	memcpy(gdt, i386_gdt, sizeof(i386_gdt));
109	desc_base = gdtbase;
110	desc_limit = sizeof(i386_gdt) - 1;
111	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR,
112			    desc_base, desc_limit, 0);
113	if (error != 0)
114		goto done;
115
116	/* Place the TSS one page above the GDT. */
117	tssbase = gdtbase + 0x1000;
118	gdt[3].sd_lobase = tssbase;
119
120	rflags = 0x2;
121	error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags);
122	if (error)
123		goto done;
124
125	desc_base = 0;
126	desc_limit = 0xffffffff;
127	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA;
128	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS,
129			    desc_base, desc_limit, desc_access);
130
131	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA;
132	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS,
133			    desc_base, desc_limit, desc_access);
134	if (error)
135		goto done;
136
137	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES,
138			    desc_base, desc_limit, desc_access);
139	if (error)
140		goto done;
141
142	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS,
143			    desc_base, desc_limit, desc_access);
144	if (error)
145		goto done;
146
147	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS,
148			    desc_base, desc_limit, desc_access);
149	if (error)
150		goto done;
151
152	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS,
153			    desc_base, desc_limit, desc_access);
154	if (error)
155		goto done;
156
157	desc_base = tssbase;
158	desc_limit = I386_TSS_SIZE - 1;
159	desc_access = DESC_PRESENT | SDT_SYS386BSY;
160	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR,
161			    desc_base, desc_limit, desc_access);
162	if (error)
163		goto done;
164
165
166	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0,
167			    DESC_UNUSABLE);
168	if (error)
169		goto done;
170
171	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
172	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0)
173		goto done;
174
175	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
176	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0)
177		goto done;
178
179	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0)
180		goto done;
181
182	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0)
183		goto done;
184
185	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0)
186		goto done;
187
188	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0)
189		goto done;
190
191	gsel = GSEL(GUEST_TSS_SEL, SEL_KPL);
192	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, gsel)) != 0)
193		goto done;
194
195	/* LDTR is pointing to the null selector */
196	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
197		goto done;
198
199	/* entry point */
200	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, eip)) != 0)
201		goto done;
202
203	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, esp)) != 0)
204		goto done;
205
206	error = 0;
207done:
208	return (error);
209}
210
211void
212vm_setup_freebsd_gdt(uint64_t *gdtr)
213{
214	gdtr[GUEST_NULL_SEL] = 0;
215	gdtr[GUEST_CODE_SEL] = 0x0020980000000000;
216	gdtr[GUEST_DATA_SEL] = 0x0000900000000000;
217}
218
219/*
220 * Setup the 'vcpu' register set such that it will begin execution at
221 * 'rip' in long mode.
222 */
223int
224vm_setup_freebsd_registers(struct vmctx *vmctx, int vcpu,
225			   uint64_t rip, uint64_t cr3, uint64_t gdtbase,
226			   uint64_t rsp)
227{
228	int error;
229	uint64_t cr0, cr4, efer, rflags, desc_base;
230	uint32_t desc_access, desc_limit;
231	uint16_t gsel;
232
233	cr0 = CR0_PE | CR0_PG | CR0_NE;
234	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
235		goto done;
236
237	cr4 = CR4_PAE;
238	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, cr4)) != 0)
239		goto done;
240
241	efer = EFER_LME | EFER_LMA;
242	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, efer)))
243		goto done;
244
245	rflags = 0x2;
246	error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags);
247	if (error)
248		goto done;
249
250	desc_base = 0;
251	desc_limit = 0;
252	desc_access = 0x0000209B;
253	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS,
254			    desc_base, desc_limit, desc_access);
255	if (error)
256		goto done;
257
258	desc_access = 0x00000093;
259	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS,
260			    desc_base, desc_limit, desc_access);
261	if (error)
262		goto done;
263
264	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES,
265			    desc_base, desc_limit, desc_access);
266	if (error)
267		goto done;
268
269	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS,
270			    desc_base, desc_limit, desc_access);
271	if (error)
272		goto done;
273
274	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS,
275			    desc_base, desc_limit, desc_access);
276	if (error)
277		goto done;
278
279	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS,
280			    desc_base, desc_limit, desc_access);
281	if (error)
282		goto done;
283
284	/*
285	 * XXX TR is pointing to null selector even though we set the
286	 * TSS segment to be usable with a base address and limit of 0.
287	 */
288	desc_access = 0x0000008b;
289	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 0, 0, desc_access);
290	if (error)
291		goto done;
292
293	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0,
294			    DESC_UNUSABLE);
295	if (error)
296		goto done;
297
298	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
299	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0)
300		goto done;
301
302	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
303	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0)
304		goto done;
305
306	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0)
307		goto done;
308
309	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0)
310		goto done;
311
312	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0)
313		goto done;
314
315	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0)
316		goto done;
317
318	/* XXX TR is pointing to the null selector */
319	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, 0)) != 0)
320		goto done;
321
322	/* LDTR is pointing to the null selector */
323	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
324		goto done;
325
326	/* entry point */
327	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, rip)) != 0)
328		goto done;
329
330	/* page table base */
331	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR3, cr3)) != 0)
332		goto done;
333
334	desc_base = gdtbase;
335	desc_limit = GUEST_GDTR_LIMIT64;
336	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR,
337			    desc_base, desc_limit, 0);
338	if (error != 0)
339		goto done;
340
341	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, rsp)) != 0)
342		goto done;
343
344	error = 0;
345done:
346	return (error);
347}
348