xref: /freebsd/sys/amd64/vmm/amd/vmcb.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
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$");
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  */
57 static struct vmcb_segment *
58 vmcb_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 static int
115 vmcb_access(struct svm_softc *softc, int vcpu, int write, int ident,
116 	uint64_t *val)
117 {
118 	struct vmcb *vmcb;
119 	int off, bytes;
120 	char *ptr;
121 
122 	vmcb	= svm_get_vmcb(softc, vcpu);
123 	off	= VMCB_ACCESS_OFFSET(ident);
124 	bytes	= VMCB_ACCESS_BYTES(ident);
125 
126 	if ((off + bytes) >= sizeof (struct vmcb))
127 		return (EINVAL);
128 
129 	ptr = (char *)vmcb;
130 
131 	if (!write)
132 		*val = 0;
133 
134 	switch (bytes) {
135 	case 8:
136 	case 4:
137 	case 2:
138 		if (write)
139 			memcpy(ptr + off, val, bytes);
140 		else
141 			memcpy(val, ptr + off, bytes);
142 		break;
143 	default:
144 		VCPU_CTR1(softc->vm, vcpu,
145 		    "Invalid size %d for VMCB access: %d", bytes);
146 		return (EINVAL);
147 	}
148 
149 	/* Invalidate all VMCB state cached by h/w. */
150 	if (write)
151 		svm_set_dirty(softc, vcpu, 0xffffffff);
152 
153 	return (0);
154 }
155 
156 /*
157  * Read from segment selector, control and general purpose register of VMCB.
158  */
159 int
160 vmcb_read(struct svm_softc *sc, int vcpu, int ident, uint64_t *retval)
161 {
162 	struct vmcb *vmcb;
163 	struct vmcb_state *state;
164 	struct vmcb_segment *seg;
165 	int err;
166 
167 	vmcb = svm_get_vmcb(sc, vcpu);
168 	state = &vmcb->state;
169 	err = 0;
170 
171 	if (VMCB_ACCESS_OK(ident))
172 		return (vmcb_access(sc, vcpu, 0, ident, retval));
173 
174 	switch (ident) {
175 	case VM_REG_GUEST_CR0:
176 		*retval = state->cr0;
177 		break;
178 
179 	case VM_REG_GUEST_CR2:
180 		*retval = state->cr2;
181 		break;
182 
183 	case VM_REG_GUEST_CR3:
184 		*retval = state->cr3;
185 		break;
186 
187 	case VM_REG_GUEST_CR4:
188 		*retval = state->cr4;
189 		break;
190 
191 	case VM_REG_GUEST_DR7:
192 		*retval = state->dr7;
193 		break;
194 
195 	case VM_REG_GUEST_EFER:
196 		*retval = state->efer;
197 		break;
198 
199 	case VM_REG_GUEST_RAX:
200 		*retval = state->rax;
201 		break;
202 
203 	case VM_REG_GUEST_RFLAGS:
204 		*retval = state->rflags;
205 		break;
206 
207 	case VM_REG_GUEST_RIP:
208 		*retval = state->rip;
209 		break;
210 
211 	case VM_REG_GUEST_RSP:
212 		*retval = state->rsp;
213 		break;
214 
215 	case VM_REG_GUEST_CS:
216 	case VM_REG_GUEST_DS:
217 	case VM_REG_GUEST_ES:
218 	case VM_REG_GUEST_FS:
219 	case VM_REG_GUEST_GS:
220 	case VM_REG_GUEST_SS:
221 	case VM_REG_GUEST_LDTR:
222 	case VM_REG_GUEST_TR:
223 		seg = vmcb_segptr(vmcb, ident);
224 		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
225 		    __func__, ident));
226 		*retval = seg->selector;
227 		break;
228 
229 	case VM_REG_GUEST_GDTR:
230 	case VM_REG_GUEST_IDTR:
231 		/* GDTR and IDTR don't have segment selectors */
232 		err = EINVAL;
233 		break;
234 	default:
235 		err =  EINVAL;
236 		break;
237 	}
238 
239 	return (err);
240 }
241 
242 /*
243  * Write to segment selector, control and general purpose register of VMCB.
244  */
245 int
246 vmcb_write(struct svm_softc *sc, int vcpu, int ident, uint64_t val)
247 {
248 	struct vmcb *vmcb;
249 	struct vmcb_state *state;
250 	struct vmcb_segment *seg;
251 	int err, dirtyseg;
252 
253 	vmcb = svm_get_vmcb(sc, vcpu);
254 	state = &vmcb->state;
255 	dirtyseg = 0;
256 	err = 0;
257 
258 	if (VMCB_ACCESS_OK(ident))
259 		return (vmcb_access(sc, vcpu, 1, ident, &val));
260 
261 	switch (ident) {
262 	case VM_REG_GUEST_CR0:
263 		state->cr0 = val;
264 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
265 		break;
266 
267 	case VM_REG_GUEST_CR2:
268 		state->cr2 = val;
269 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR2);
270 		break;
271 
272 	case VM_REG_GUEST_CR3:
273 		state->cr3 = val;
274 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
275 		break;
276 
277 	case VM_REG_GUEST_CR4:
278 		state->cr4 = val;
279 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
280 		break;
281 
282 	case VM_REG_GUEST_DR7:
283 		state->dr7 = val;
284 		break;
285 
286 	case VM_REG_GUEST_EFER:
287 		/* EFER_SVM must always be set when the guest is executing */
288 		state->efer = val | EFER_SVM;
289 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
290 		break;
291 
292 	case VM_REG_GUEST_RAX:
293 		state->rax = val;
294 		break;
295 
296 	case VM_REG_GUEST_RFLAGS:
297 		state->rflags = val;
298 		break;
299 
300 	case VM_REG_GUEST_RIP:
301 		state->rip = val;
302 		break;
303 
304 	case VM_REG_GUEST_RSP:
305 		state->rsp = val;
306 		break;
307 
308 	case VM_REG_GUEST_CS:
309 	case VM_REG_GUEST_DS:
310 	case VM_REG_GUEST_ES:
311 	case VM_REG_GUEST_SS:
312 		dirtyseg = 1;		/* FALLTHROUGH */
313 	case VM_REG_GUEST_FS:
314 	case VM_REG_GUEST_GS:
315 	case VM_REG_GUEST_LDTR:
316 	case VM_REG_GUEST_TR:
317 		seg = vmcb_segptr(vmcb, ident);
318 		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
319 		    __func__, ident));
320 		seg->selector = val;
321 		if (dirtyseg)
322 			svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
323 		break;
324 
325 	case VM_REG_GUEST_GDTR:
326 	case VM_REG_GUEST_IDTR:
327 		/* GDTR and IDTR don't have segment selectors */
328 		err = EINVAL;
329 		break;
330 	default:
331 		err = EINVAL;
332 		break;
333 	}
334 
335 	return (err);
336 }
337 
338 int
339 vmcb_seg(struct vmcb *vmcb, int ident, struct vmcb_segment *seg2)
340 {
341 	struct vmcb_segment *seg;
342 
343 	seg = vmcb_segptr(vmcb, ident);
344 	if (seg != NULL) {
345 		bcopy(seg, seg2, sizeof(struct vmcb_segment));
346 		return (0);
347 	} else {
348 		return (EINVAL);
349 	}
350 }
351 
352 int
353 vmcb_setdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
354 {
355 	struct vmcb *vmcb;
356 	struct svm_softc *sc;
357 	struct vmcb_segment *seg;
358 	uint16_t attrib;
359 
360 	sc = arg;
361 	vmcb = svm_get_vmcb(sc, vcpu);
362 
363 	seg = vmcb_segptr(vmcb, reg);
364 	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
365 	    __func__, reg));
366 
367 	seg->base = desc->base;
368 	seg->limit = desc->limit;
369 	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
370 		/*
371 		 * Map seg_desc access to VMCB attribute format.
372 		 *
373 		 * SVM uses the 'P' bit in the segment attributes to indicate a
374 		 * NULL segment so clear it if the segment is marked unusable.
375 		 */
376 		attrib = ((desc->access & 0xF000) >> 4) | (desc->access & 0xFF);
377 		if (SEG_DESC_UNUSABLE(desc->access)) {
378 			attrib &= ~0x80;
379 		}
380 		seg->attrib = attrib;
381 	}
382 
383 	VCPU_CTR4(sc->vm, vcpu, "Setting desc %d: base (%#lx), limit (%#x), "
384 	    "attrib (%#x)", reg, seg->base, seg->limit, seg->attrib);
385 
386 	switch (reg) {
387 	case VM_REG_GUEST_CS:
388 	case VM_REG_GUEST_DS:
389 	case VM_REG_GUEST_ES:
390 	case VM_REG_GUEST_SS:
391 		svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
392 		break;
393 	case VM_REG_GUEST_GDTR:
394 	case VM_REG_GUEST_IDTR:
395 		svm_set_dirty(sc, vcpu, VMCB_CACHE_DT);
396 		break;
397 	default:
398 		break;
399 	}
400 
401 	return (0);
402 }
403 
404 int
405 vmcb_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
406 {
407 	struct vmcb *vmcb;
408 	struct svm_softc *sc;
409 	struct vmcb_segment *seg;
410 
411 	sc = arg;
412 	vmcb = svm_get_vmcb(sc, vcpu);
413 	seg = vmcb_segptr(vmcb, reg);
414 	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
415 	    __func__, reg));
416 
417 	desc->base = seg->base;
418 	desc->limit = seg->limit;
419 	desc->access = 0;
420 
421 	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
422 		/* Map seg_desc access to VMCB attribute format */
423 		desc->access = ((seg->attrib & 0xF00) << 4) |
424 		    (seg->attrib & 0xFF);
425 
426 		/*
427 		 * VT-x uses bit 16 to indicate a segment that has been loaded
428 		 * with a NULL selector (aka unusable). The 'desc->access'
429 		 * field is interpreted in the VT-x format by the
430 		 * processor-independent code.
431 		 *
432 		 * SVM uses the 'P' bit to convey the same information so
433 		 * convert it into the VT-x format. For more details refer to
434 		 * section "Segment State in the VMCB" in APMv2.
435 		 */
436 		if (reg != VM_REG_GUEST_CS && reg != VM_REG_GUEST_TR) {
437 			if ((desc->access & 0x80) == 0)
438 				desc->access |= 0x10000;  /* Unusable segment */
439 		}
440 	}
441 
442 	return (0);
443 }
444