vmcb.c revision 271939
1/*-
2 * Copyright (c) 2013 Anish Gupta (akgupt3@gmail.com)
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 unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: projects/bhyve_svm/sys/amd64/vmm/amd/vmcb.c 271939 2014-09-21 23:42:54Z neel $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/cpuset.h>
33
34#include <machine/segments.h>
35#include <machine/specialreg.h>
36#include <machine/vmm.h>
37
38#include "vmm_ktr.h"
39
40#include "vmcb.h"
41#include "svm.h"
42#include "svm_softc.h"
43
44/*
45 * The VMCB aka Virtual Machine Control Block is a 4KB aligned page
46 * in memory that describes the virtual machine.
47 *
48 * The VMCB contains:
49 * - instructions or events in the guest to intercept
50 * - control bits that modify execution environment of the guest
51 * - guest processor state (e.g. general purpose registers)
52 */
53
54/*
55 * Return VMCB segment area.
56 */
57static struct vmcb_segment *
58vmcb_segptr(struct vmcb *vmcb, int type)
59{
60	struct vmcb_state *state;
61	struct vmcb_segment *seg;
62
63	state = &vmcb->state;
64
65	switch (type) {
66	case VM_REG_GUEST_CS:
67		seg = &state->cs;
68		break;
69
70	case VM_REG_GUEST_DS:
71		seg = &state->ds;
72		break;
73
74	case VM_REG_GUEST_ES:
75		seg = &state->es;
76		break;
77
78	case VM_REG_GUEST_FS:
79		seg = &state->fs;
80		break;
81
82	case VM_REG_GUEST_GS:
83		seg = &state->gs;
84		break;
85
86	case VM_REG_GUEST_SS:
87		seg = &state->ss;
88		break;
89
90	case VM_REG_GUEST_GDTR:
91		seg = &state->gdt;
92		break;
93
94	case VM_REG_GUEST_IDTR:
95		seg = &state->idt;
96		break;
97
98	case VM_REG_GUEST_LDTR:
99		seg = &state->ldt;
100		break;
101
102	case VM_REG_GUEST_TR:
103		seg = &state->tr;
104		break;
105
106	default:
107		seg = NULL;
108		break;
109	}
110
111	return (seg);
112}
113
114/*
115 * Read from segment selector, control and general purpose register of VMCB.
116 */
117int
118vmcb_read(struct svm_softc *sc, int vcpu, int ident, uint64_t *retval)
119{
120	struct vmcb *vmcb;
121	struct vmcb_state *state;
122	struct vmcb_segment *seg;
123	int err;
124
125	vmcb = svm_get_vmcb(sc, vcpu);
126	state = &vmcb->state;
127	err = 0;
128
129	switch (ident) {
130	case VM_REG_GUEST_CR0:
131		*retval = state->cr0;
132		break;
133
134	case VM_REG_GUEST_CR2:
135		*retval = state->cr2;
136		break;
137
138	case VM_REG_GUEST_CR3:
139		*retval = state->cr3;
140		break;
141
142	case VM_REG_GUEST_CR4:
143		*retval = state->cr4;
144		break;
145
146	case VM_REG_GUEST_DR7:
147		*retval = state->dr7;
148		break;
149
150	case VM_REG_GUEST_EFER:
151		*retval = state->efer;
152		break;
153
154	case VM_REG_GUEST_RAX:
155		*retval = state->rax;
156		break;
157
158	case VM_REG_GUEST_RFLAGS:
159		*retval = state->rflags;
160		break;
161
162	case VM_REG_GUEST_RIP:
163		*retval = state->rip;
164		break;
165
166	case VM_REG_GUEST_RSP:
167		*retval = state->rsp;
168		break;
169
170	case VM_REG_GUEST_CS:
171	case VM_REG_GUEST_DS:
172	case VM_REG_GUEST_ES:
173	case VM_REG_GUEST_FS:
174	case VM_REG_GUEST_GS:
175	case VM_REG_GUEST_SS:
176	case VM_REG_GUEST_LDTR:
177	case VM_REG_GUEST_TR:
178		seg = vmcb_segptr(vmcb, ident);
179		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
180		    __func__, ident));
181		*retval = seg->selector;
182		break;
183
184	case VM_REG_GUEST_GDTR:
185	case VM_REG_GUEST_IDTR:
186		/* GDTR and IDTR don't have segment selectors */
187		err = EINVAL;
188		break;
189	default:
190		err =  EINVAL;
191		break;
192	}
193
194	return (err);
195}
196
197/*
198 * Write to segment selector, control and general purpose register of VMCB.
199 */
200int
201vmcb_write(struct svm_softc *sc, int vcpu, int ident, uint64_t val)
202{
203	struct vmcb *vmcb;
204	struct vmcb_state *state;
205	struct vmcb_segment *seg;
206	int err, dirtyseg;
207
208	vmcb = svm_get_vmcb(sc, vcpu);
209	state = &vmcb->state;
210	dirtyseg = 0;
211	err = 0;
212
213	switch (ident) {
214	case VM_REG_GUEST_CR0:
215		state->cr0 = val;
216		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
217		break;
218
219	case VM_REG_GUEST_CR2:
220		state->cr2 = val;
221		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR2);
222		break;
223
224	case VM_REG_GUEST_CR3:
225		state->cr3 = val;
226		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
227		break;
228
229	case VM_REG_GUEST_CR4:
230		state->cr4 = val;
231		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
232		break;
233
234	case VM_REG_GUEST_DR7:
235		state->dr7 = val;
236		break;
237
238	case VM_REG_GUEST_EFER:
239		/* EFER_SVM must always be set when the guest is executing */
240		state->efer = val | EFER_SVM;
241		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
242		break;
243
244	case VM_REG_GUEST_RAX:
245		state->rax = val;
246		break;
247
248	case VM_REG_GUEST_RFLAGS:
249		state->rflags = val;
250		break;
251
252	case VM_REG_GUEST_RIP:
253		state->rip = val;
254		break;
255
256	case VM_REG_GUEST_RSP:
257		state->rsp = val;
258		break;
259
260	case VM_REG_GUEST_CS:
261	case VM_REG_GUEST_DS:
262	case VM_REG_GUEST_ES:
263	case VM_REG_GUEST_SS:
264		dirtyseg = 1;		/* FALLTHROUGH */
265	case VM_REG_GUEST_FS:
266	case VM_REG_GUEST_GS:
267	case VM_REG_GUEST_LDTR:
268	case VM_REG_GUEST_TR:
269		seg = vmcb_segptr(vmcb, ident);
270		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
271		    __func__, ident));
272		seg->selector = val;
273		if (dirtyseg)
274			svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
275		break;
276
277	case VM_REG_GUEST_GDTR:
278	case VM_REG_GUEST_IDTR:
279		/* GDTR and IDTR don't have segment selectors */
280		err = EINVAL;
281		break;
282	default:
283		err = EINVAL;
284		break;
285	}
286
287	return (err);
288}
289
290int
291vmcb_seg(struct vmcb *vmcb, int ident, struct vmcb_segment *seg2)
292{
293	struct vmcb_segment *seg;
294
295	seg = vmcb_segptr(vmcb, ident);
296	if (seg != NULL) {
297		bcopy(seg, seg2, sizeof(struct vmcb_segment));
298		return (0);
299	} else {
300		return (EINVAL);
301	}
302}
303
304int
305vmcb_setdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
306{
307	struct vmcb *vmcb;
308	struct svm_softc *sc;
309	struct vmcb_segment *seg;
310	uint16_t attrib;
311
312	sc = arg;
313	vmcb = svm_get_vmcb(sc, vcpu);
314
315	seg = vmcb_segptr(vmcb, reg);
316	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
317	    __func__, reg));
318
319	seg->base = desc->base;
320	seg->limit = desc->limit;
321	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
322		/*
323		 * Map seg_desc access to VMCB attribute format.
324		 *
325		 * SVM uses the 'P' bit in the segment attributes to indicate a
326		 * NULL segment so clear it if the segment is marked unusable.
327		 */
328		attrib = ((desc->access & 0xF000) >> 4) | (desc->access & 0xFF);
329		if (SEG_DESC_UNUSABLE(desc->access)) {
330			attrib &= ~0x80;
331		}
332		seg->attrib = attrib;
333	}
334
335	VCPU_CTR4(sc->vm, vcpu, "Setting desc %d: base (%#lx), limit (%#x), "
336	    "attrib (%#x)", reg, seg->base, seg->limit, seg->attrib);
337
338	switch (reg) {
339	case VM_REG_GUEST_CS:
340	case VM_REG_GUEST_DS:
341	case VM_REG_GUEST_ES:
342	case VM_REG_GUEST_SS:
343		svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
344	case VM_REG_GUEST_GDTR:
345	case VM_REG_GUEST_IDTR:
346		svm_set_dirty(sc, vcpu, VMCB_CACHE_DT);
347		break;
348	default:
349		break;
350	}
351
352	return (0);
353}
354
355int
356vmcb_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
357{
358	struct vmcb *vmcb;
359	struct svm_softc *sc;
360	struct vmcb_segment *seg;
361
362	sc = arg;
363	vmcb = svm_get_vmcb(sc, vcpu);
364	seg = vmcb_segptr(vmcb, reg);
365	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
366	    __func__, reg));
367
368	desc->base = seg->base;
369	desc->limit = seg->limit;
370	desc->access = 0;
371
372	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
373		/* Map seg_desc access to VMCB attribute format */
374		desc->access = ((seg->attrib & 0xF00) << 4) |
375		    (seg->attrib & 0xFF);
376
377		/*
378		 * VT-x uses bit 16 to indicate a segment that has been loaded
379		 * with a NULL selector (aka unusable). The 'desc->access'
380		 * field is interpreted in the VT-x format by the
381		 * processor-independent code.
382		 *
383		 * SVM uses the 'P' bit to convey the same information so
384		 * convert it into the VT-x format. For more details refer to
385		 * section "Segment State in the VMCB" in APMv2.
386		 */
387		if (reg != VM_REG_GUEST_CS && reg != VM_REG_GUEST_TR) {
388			if ((desc->access & 0x80) == 0)
389				desc->access |= 0x10000;  /* Unusable segment */
390		}
391	}
392
393	return (0);
394}
395