1189251Ssam/*-
2214734Srpaulo * Copyright (c) 2011 NetApp, Inc.
3252726Srpaulo * All rights reserved.
4189251Ssam *
5252726Srpaulo * Redistribution and use in source and binary forms, with or without
6252726Srpaulo * modification, are permitted provided that the following conditions
7189251Ssam * are met:
8189251Ssam * 1. Redistributions of source code must retain the above copyright
9189251Ssam *    notice, this list of conditions and the following disclaimer.
10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
11189251Ssam *    notice, this list of conditions and the following disclaimer in the
12189251Ssam *    documentation and/or other materials provided with the distribution.
13189251Ssam *
14189251Ssam * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24189251Ssam * SUCH DAMAGE.
25189251Ssam *
26189251Ssam * $FreeBSD$
27189251Ssam */
28189251Ssam
29189251Ssam#include <sys/cdefs.h>
30189251Ssam__FBSDID("$FreeBSD$");
31189251Ssam
32189251Ssam#include <sys/types.h>
33189251Ssam
34189251Ssam#include <machine/specialreg.h>
35189251Ssam#include <machine/segments.h>
36189251Ssam#include <machine/vmm.h>
37189251Ssam
38189251Ssam#include <errno.h>
39189251Ssam#include <string.h>
40189251Ssam
41189251Ssam#include "vmmapi.h"
42189251Ssam
43189251Ssam#define	I386_TSS_SIZE		104
44189251Ssam
45189251Ssam#define	DESC_PRESENT		0x00000080
46189251Ssam#define	DESC_LONGMODE		0x00002000
47189251Ssam#define	DESC_DEF32		0x00004000
48189251Ssam#define	DESC_GRAN		0x00008000
49189251Ssam#define	DESC_UNUSABLE		0x00010000
50189251Ssam
51189251Ssam#define	GUEST_NULL_SEL		0
52189251Ssam#define	GUEST_CODE_SEL		1
53189251Ssam#define	GUEST_DATA_SEL		2
54189251Ssam#define	GUEST_TSS_SEL		3
55189251Ssam#define	GUEST_GDTR_LIMIT64	(3 * 8 - 1)
56189251Ssam
57189251Ssamstatic struct segment_descriptor i386_gdt[] = {
58189251Ssam	{},						/* NULL */
59189251Ssam	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMER,	/* CODE */
60189251Ssam	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
61189251Ssam	{ .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW,	/* DATA */
62189251Ssam	  .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 },
63189251Ssam	{ .sd_lolimit = I386_TSS_SIZE - 1,		/* TSS */
64189251Ssam	  .sd_type = SDT_SYS386TSS, .sd_p = 1 }
65189251Ssam};
66189251Ssam
67189251Ssam/*
68189251Ssam * Setup the 'vcpu' register set such that it will begin execution at
69189251Ssam * 'eip' in flat mode.
70189251Ssam */
71189251Ssamint
72189251Ssamvm_setup_freebsd_registers_i386(struct vmctx *vmctx, int vcpu, uint32_t eip,
73189251Ssam				uint32_t gdtbase, uint32_t esp)
74189251Ssam{
75189251Ssam	uint64_t cr0, rflags, desc_base;
76189251Ssam	uint32_t desc_access, desc_limit, tssbase;
77189251Ssam	uint16_t gsel;
78189251Ssam	struct segment_descriptor *gdt;
79189251Ssam	int error, tmp;
80189251Ssam
81189251Ssam	/* A 32-bit guest requires unrestricted mode. */
82189251Ssam	error = vm_get_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp);
83189251Ssam	if (error)
84189251Ssam		goto done;
85189251Ssam	error = vm_set_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
86189251Ssam	if (error)
87189251Ssam		goto done;
88189251Ssam
89189251Ssam	cr0 = CR0_PE | CR0_NE;
90189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
91189251Ssam		goto done;
92189251Ssam
93189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, 0)) != 0)
94189251Ssam		goto done;
95189251Ssam
96189251Ssam	/*
97189251Ssam	 * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest
98189251Ssam	 * mode" entry control.
99189251Ssam	 */
100189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, 0)))
101189251Ssam		goto done;
102189251Ssam
103189251Ssam	gdt = vm_map_gpa(vmctx, gdtbase, 0x1000);
104189251Ssam	if (gdt == NULL)
105189251Ssam		return (EFAULT);
106189251Ssam	memcpy(gdt, i386_gdt, sizeof(i386_gdt));
107189251Ssam	desc_base = gdtbase;
108189251Ssam	desc_limit = sizeof(i386_gdt) - 1;
109189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR,
110189251Ssam			    desc_base, desc_limit, 0);
111189251Ssam	if (error != 0)
112189251Ssam		goto done;
113189251Ssam
114189251Ssam	/* Place the TSS one page above the GDT. */
115189251Ssam	tssbase = gdtbase + 0x1000;
116189251Ssam	gdt[3].sd_lobase = tssbase;
117189251Ssam
118189251Ssam	rflags = 0x2;
119189251Ssam	error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags);
120189251Ssam	if (error)
121189251Ssam		goto done;
122189251Ssam
123189251Ssam	desc_base = 0;
124189251Ssam	desc_limit = 0xffffffff;
125189251Ssam	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA;
126189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS,
127189251Ssam			    desc_base, desc_limit, desc_access);
128189251Ssam
129189251Ssam	desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA;
130189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS,
131189251Ssam			    desc_base, desc_limit, desc_access);
132189251Ssam	if (error)
133189251Ssam		goto done;
134189251Ssam
135189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES,
136189251Ssam			    desc_base, desc_limit, desc_access);
137189251Ssam	if (error)
138189251Ssam		goto done;
139189251Ssam
140189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS,
141189251Ssam			    desc_base, desc_limit, desc_access);
142189251Ssam	if (error)
143189251Ssam		goto done;
144189251Ssam
145189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS,
146189251Ssam			    desc_base, desc_limit, desc_access);
147189251Ssam	if (error)
148189251Ssam		goto done;
149189251Ssam
150189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS,
151189251Ssam			    desc_base, desc_limit, desc_access);
152189251Ssam	if (error)
153189251Ssam		goto done;
154189251Ssam
155189251Ssam	desc_base = tssbase;
156189251Ssam	desc_limit = I386_TSS_SIZE - 1;
157189251Ssam	desc_access = DESC_PRESENT | SDT_SYS386BSY;
158189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR,
159189251Ssam			    desc_base, desc_limit, desc_access);
160189251Ssam	if (error)
161189251Ssam		goto done;
162189251Ssam
163189251Ssam
164189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0,
165189251Ssam			    DESC_UNUSABLE);
166189251Ssam	if (error)
167189251Ssam		goto done;
168189251Ssam
169189251Ssam	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
170189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0)
171189251Ssam		goto done;
172189251Ssam
173189251Ssam	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
174189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0)
175189251Ssam		goto done;
176189251Ssam
177189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0)
178189251Ssam		goto done;
179189251Ssam
180189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0)
181189251Ssam		goto done;
182189251Ssam
183189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0)
184189251Ssam		goto done;
185189251Ssam
186189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0)
187189251Ssam		goto done;
188189251Ssam
189189251Ssam	gsel = GSEL(GUEST_TSS_SEL, SEL_KPL);
190189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, gsel)) != 0)
191189251Ssam		goto done;
192189251Ssam
193189251Ssam	/* LDTR is pointing to the null selector */
194189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
195189251Ssam		goto done;
196189251Ssam
197189251Ssam	/* entry point */
198189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, eip)) != 0)
199189251Ssam		goto done;
200189251Ssam
201189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, esp)) != 0)
202189251Ssam		goto done;
203189251Ssam
204189251Ssam	error = 0;
205189251Ssamdone:
206189251Ssam	return (error);
207189251Ssam}
208252726Srpaulo
209252726Srpaulovoid
210252726Srpaulovm_setup_freebsd_gdt(uint64_t *gdtr)
211189251Ssam{
212189251Ssam	gdtr[GUEST_NULL_SEL] = 0;
213189251Ssam	gdtr[GUEST_CODE_SEL] = 0x0020980000000000;
214189251Ssam	gdtr[GUEST_DATA_SEL] = 0x0000900000000000;
215189251Ssam}
216189251Ssam
217189251Ssam/*
218189251Ssam * Setup the 'vcpu' register set such that it will begin execution at
219189251Ssam * 'rip' in long mode.
220189251Ssam */
221189251Ssamint
222189251Ssamvm_setup_freebsd_registers(struct vmctx *vmctx, int vcpu,
223189251Ssam			   uint64_t rip, uint64_t cr3, uint64_t gdtbase,
224189251Ssam			   uint64_t rsp)
225189251Ssam{
226189251Ssam	int error;
227189251Ssam	uint64_t cr0, cr4, efer, rflags, desc_base;
228189251Ssam	uint32_t desc_access, desc_limit;
229189251Ssam	uint16_t gsel;
230189251Ssam
231189251Ssam	cr0 = CR0_PE | CR0_PG | CR0_NE;
232189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0)
233189251Ssam		goto done;
234189251Ssam
235189251Ssam	cr4 = CR4_PAE;
236189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, cr4)) != 0)
237189251Ssam		goto done;
238189251Ssam
239189251Ssam	efer = EFER_LME | EFER_LMA;
240189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, efer)))
241189251Ssam		goto done;
242189251Ssam
243189251Ssam	rflags = 0x2;
244189251Ssam	error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags);
245189251Ssam	if (error)
246189251Ssam		goto done;
247189251Ssam
248189251Ssam	desc_base = 0;
249189251Ssam	desc_limit = 0;
250189251Ssam	desc_access = 0x0000209B;
251189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS,
252189251Ssam			    desc_base, desc_limit, desc_access);
253189251Ssam	if (error)
254189251Ssam		goto done;
255189251Ssam
256189251Ssam	desc_access = 0x00000093;
257189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS,
258189251Ssam			    desc_base, desc_limit, desc_access);
259189251Ssam	if (error)
260189251Ssam		goto done;
261189251Ssam
262189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES,
263189251Ssam			    desc_base, desc_limit, desc_access);
264189251Ssam	if (error)
265189251Ssam		goto done;
266189251Ssam
267189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS,
268189251Ssam			    desc_base, desc_limit, desc_access);
269189251Ssam	if (error)
270189251Ssam		goto done;
271189251Ssam
272189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS,
273189251Ssam			    desc_base, desc_limit, desc_access);
274189251Ssam	if (error)
275189251Ssam		goto done;
276189251Ssam
277189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS,
278189251Ssam			    desc_base, desc_limit, desc_access);
279189251Ssam	if (error)
280189251Ssam		goto done;
281189251Ssam
282189251Ssam	/*
283189251Ssam	 * XXX TR is pointing to null selector even though we set the
284189251Ssam	 * TSS segment to be usable with a base address and limit of 0.
285189251Ssam	 */
286189251Ssam	desc_access = 0x0000008b;
287189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 0, 0, desc_access);
288189251Ssam	if (error)
289189251Ssam		goto done;
290189251Ssam
291189251Ssam	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0,
292189251Ssam			    DESC_UNUSABLE);
293189251Ssam	if (error)
294189251Ssam		goto done;
295189251Ssam
296189251Ssam	gsel = GSEL(GUEST_CODE_SEL, SEL_KPL);
297189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0)
298189251Ssam		goto done;
299189251Ssam
300189251Ssam	gsel = GSEL(GUEST_DATA_SEL, SEL_KPL);
301189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0)
302189251Ssam		goto done;
303189251Ssam
304189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0)
305189251Ssam		goto done;
306189251Ssam
307189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0)
308189251Ssam		goto done;
309189251Ssam
310189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0)
311189251Ssam		goto done;
312189251Ssam
313189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0)
314189251Ssam		goto done;
315189251Ssam
316189251Ssam	/* XXX TR is pointing to the null selector */
317189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, 0)) != 0)
318189251Ssam		goto done;
319189251Ssam
320189251Ssam	/* LDTR is pointing to the null selector */
321189251Ssam	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0)
322189251Ssam		goto done;
323189251Ssam
324214734Srpaulo	/* entry point */
325214734Srpaulo	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, rip)) != 0)
326214734Srpaulo		goto done;
327214734Srpaulo
328189251Ssam	/* page table base */
329252726Srpaulo	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR3, cr3)) != 0)
330252726Srpaulo		goto done;
331252726Srpaulo
332252726Srpaulo	desc_base = gdtbase;
333252726Srpaulo	desc_limit = GUEST_GDTR_LIMIT64;
334252726Srpaulo	error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR,
335252726Srpaulo			    desc_base, desc_limit, 0);
336252726Srpaulo	if (error != 0)
337252726Srpaulo		goto done;
338252726Srpaulo
339252726Srpaulo	if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, rsp)) != 0)
340189251Ssam		goto done;
341214734Srpaulo
342214734Srpaulo	error = 0;
343214734Srpaulodone:
344214734Srpaulo	return (error);
345189251Ssam}
346189251Ssam