xref: /freebsd/sys/amd64/vmm/amd/vmcb.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Anish Gupta (akgupt3@gmail.com)
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 unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_bhyve_snapshot.h"
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 
37 #include <machine/segments.h>
38 #include <machine/specialreg.h>
39 #include <machine/vmm.h>
40 #include <machine/vmm_snapshot.h>
41 
42 #include "vmm_ktr.h"
43 
44 #include "vmcb.h"
45 #include "svm.h"
46 #include "svm_softc.h"
47 
48 /*
49  * The VMCB aka Virtual Machine Control Block is a 4KB aligned page
50  * in memory that describes the virtual machine.
51  *
52  * The VMCB contains:
53  * - instructions or events in the guest to intercept
54  * - control bits that modify execution environment of the guest
55  * - guest processor state (e.g. general purpose registers)
56  */
57 
58 /*
59  * Return VMCB segment area.
60  */
61 static struct vmcb_segment *
62 vmcb_segptr(struct vmcb *vmcb, int type)
63 {
64 	struct vmcb_state *state;
65 	struct vmcb_segment *seg;
66 
67 	state = &vmcb->state;
68 
69 	switch (type) {
70 	case VM_REG_GUEST_CS:
71 		seg = &state->cs;
72 		break;
73 
74 	case VM_REG_GUEST_DS:
75 		seg = &state->ds;
76 		break;
77 
78 	case VM_REG_GUEST_ES:
79 		seg = &state->es;
80 		break;
81 
82 	case VM_REG_GUEST_FS:
83 		seg = &state->fs;
84 		break;
85 
86 	case VM_REG_GUEST_GS:
87 		seg = &state->gs;
88 		break;
89 
90 	case VM_REG_GUEST_SS:
91 		seg = &state->ss;
92 		break;
93 
94 	case VM_REG_GUEST_GDTR:
95 		seg = &state->gdt;
96 		break;
97 
98 	case VM_REG_GUEST_IDTR:
99 		seg = &state->idt;
100 		break;
101 
102 	case VM_REG_GUEST_LDTR:
103 		seg = &state->ldt;
104 		break;
105 
106 	case VM_REG_GUEST_TR:
107 		seg = &state->tr;
108 		break;
109 
110 	default:
111 		seg = NULL;
112 		break;
113 	}
114 
115 	return (seg);
116 }
117 
118 static int
119 vmcb_access(struct svm_softc *softc, int vcpu, int write, int ident,
120 	uint64_t *val)
121 {
122 	struct vmcb *vmcb;
123 	int off, bytes;
124 	char *ptr;
125 
126 	vmcb	= svm_get_vmcb(softc, vcpu);
127 	off	= VMCB_ACCESS_OFFSET(ident);
128 	bytes	= VMCB_ACCESS_BYTES(ident);
129 
130 	if ((off + bytes) >= sizeof (struct vmcb))
131 		return (EINVAL);
132 
133 	ptr = (char *)vmcb;
134 
135 	if (!write)
136 		*val = 0;
137 
138 	switch (bytes) {
139 	case 8:
140 	case 4:
141 	case 2:
142 	case 1:
143 		if (write)
144 			memcpy(ptr + off, val, bytes);
145 		else
146 			memcpy(val, ptr + off, bytes);
147 		break;
148 	default:
149 		VCPU_CTR1(softc->vm, vcpu,
150 		    "Invalid size %d for VMCB access: %d", bytes);
151 		return (EINVAL);
152 	}
153 
154 	/* Invalidate all VMCB state cached by h/w. */
155 	if (write)
156 		svm_set_dirty(softc, vcpu, 0xffffffff);
157 
158 	return (0);
159 }
160 
161 /*
162  * Read from segment selector, control and general purpose register of VMCB.
163  */
164 int
165 vmcb_read(struct svm_softc *sc, int vcpu, int ident, uint64_t *retval)
166 {
167 	struct vmcb *vmcb;
168 	struct vmcb_state *state;
169 	struct vmcb_segment *seg;
170 	int err;
171 
172 	vmcb = svm_get_vmcb(sc, vcpu);
173 	state = &vmcb->state;
174 	err = 0;
175 
176 	if (VMCB_ACCESS_OK(ident))
177 		return (vmcb_access(sc, vcpu, 0, ident, retval));
178 
179 	switch (ident) {
180 	case VM_REG_GUEST_CR0:
181 		*retval = state->cr0;
182 		break;
183 
184 	case VM_REG_GUEST_CR2:
185 		*retval = state->cr2;
186 		break;
187 
188 	case VM_REG_GUEST_CR3:
189 		*retval = state->cr3;
190 		break;
191 
192 	case VM_REG_GUEST_CR4:
193 		*retval = state->cr4;
194 		break;
195 
196 	case VM_REG_GUEST_DR6:
197 		*retval = state->dr6;
198 		break;
199 
200 	case VM_REG_GUEST_DR7:
201 		*retval = state->dr7;
202 		break;
203 
204 	case VM_REG_GUEST_EFER:
205 		*retval = state->efer;
206 		break;
207 
208 	case VM_REG_GUEST_RAX:
209 		*retval = state->rax;
210 		break;
211 
212 	case VM_REG_GUEST_RFLAGS:
213 		*retval = state->rflags;
214 		break;
215 
216 	case VM_REG_GUEST_RIP:
217 		*retval = state->rip;
218 		break;
219 
220 	case VM_REG_GUEST_RSP:
221 		*retval = state->rsp;
222 		break;
223 
224 	case VM_REG_GUEST_CS:
225 	case VM_REG_GUEST_DS:
226 	case VM_REG_GUEST_ES:
227 	case VM_REG_GUEST_FS:
228 	case VM_REG_GUEST_GS:
229 	case VM_REG_GUEST_SS:
230 	case VM_REG_GUEST_LDTR:
231 	case VM_REG_GUEST_TR:
232 		seg = vmcb_segptr(vmcb, ident);
233 		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
234 		    __func__, ident));
235 		*retval = seg->selector;
236 		break;
237 
238 	case VM_REG_GUEST_GDTR:
239 	case VM_REG_GUEST_IDTR:
240 		/* GDTR and IDTR don't have segment selectors */
241 		err = EINVAL;
242 		break;
243 	default:
244 		err =  EINVAL;
245 		break;
246 	}
247 
248 	return (err);
249 }
250 
251 /*
252  * Write to segment selector, control and general purpose register of VMCB.
253  */
254 int
255 vmcb_write(struct svm_softc *sc, int vcpu, int ident, uint64_t val)
256 {
257 	struct vmcb *vmcb;
258 	struct vmcb_state *state;
259 	struct vmcb_segment *seg;
260 	int err, dirtyseg;
261 
262 	vmcb = svm_get_vmcb(sc, vcpu);
263 	state = &vmcb->state;
264 	dirtyseg = 0;
265 	err = 0;
266 
267 	if (VMCB_ACCESS_OK(ident))
268 		return (vmcb_access(sc, vcpu, 1, ident, &val));
269 
270 	switch (ident) {
271 	case VM_REG_GUEST_CR0:
272 		state->cr0 = val;
273 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
274 		break;
275 
276 	case VM_REG_GUEST_CR2:
277 		state->cr2 = val;
278 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR2);
279 		break;
280 
281 	case VM_REG_GUEST_CR3:
282 		state->cr3 = val;
283 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
284 		break;
285 
286 	case VM_REG_GUEST_CR4:
287 		state->cr4 = val;
288 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
289 		break;
290 
291 	case VM_REG_GUEST_DR6:
292 		state->dr6 = val;
293 		svm_set_dirty(sc, vcpu, VMCB_CACHE_DR);
294 		break;
295 
296 	case VM_REG_GUEST_DR7:
297 		state->dr7 = val;
298 		svm_set_dirty(sc, vcpu, VMCB_CACHE_DR);
299 		break;
300 
301 	case VM_REG_GUEST_EFER:
302 		/* EFER_SVM must always be set when the guest is executing */
303 		state->efer = val | EFER_SVM;
304 		svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
305 		break;
306 
307 	case VM_REG_GUEST_RAX:
308 		state->rax = val;
309 		break;
310 
311 	case VM_REG_GUEST_RFLAGS:
312 		state->rflags = val;
313 		break;
314 
315 	case VM_REG_GUEST_RIP:
316 		state->rip = val;
317 		break;
318 
319 	case VM_REG_GUEST_RSP:
320 		state->rsp = val;
321 		break;
322 
323 	case VM_REG_GUEST_CS:
324 	case VM_REG_GUEST_DS:
325 	case VM_REG_GUEST_ES:
326 	case VM_REG_GUEST_SS:
327 		dirtyseg = 1;		/* FALLTHROUGH */
328 	case VM_REG_GUEST_FS:
329 	case VM_REG_GUEST_GS:
330 	case VM_REG_GUEST_LDTR:
331 	case VM_REG_GUEST_TR:
332 		seg = vmcb_segptr(vmcb, ident);
333 		KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
334 		    __func__, ident));
335 		seg->selector = val;
336 		if (dirtyseg)
337 			svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
338 		break;
339 
340 	case VM_REG_GUEST_GDTR:
341 	case VM_REG_GUEST_IDTR:
342 		/* GDTR and IDTR don't have segment selectors */
343 		err = EINVAL;
344 		break;
345 	default:
346 		err = EINVAL;
347 		break;
348 	}
349 
350 	return (err);
351 }
352 
353 int
354 vmcb_seg(struct vmcb *vmcb, int ident, struct vmcb_segment *seg2)
355 {
356 	struct vmcb_segment *seg;
357 
358 	seg = vmcb_segptr(vmcb, ident);
359 	if (seg != NULL) {
360 		bcopy(seg, seg2, sizeof(struct vmcb_segment));
361 		return (0);
362 	} else {
363 		return (EINVAL);
364 	}
365 }
366 
367 int
368 vmcb_setdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
369 {
370 	struct vmcb *vmcb;
371 	struct svm_softc *sc;
372 	struct vmcb_segment *seg;
373 	uint16_t attrib;
374 
375 	sc = arg;
376 	vmcb = svm_get_vmcb(sc, vcpu);
377 
378 	seg = vmcb_segptr(vmcb, reg);
379 	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
380 	    __func__, reg));
381 
382 	seg->base = desc->base;
383 	seg->limit = desc->limit;
384 	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
385 		/*
386 		 * Map seg_desc access to VMCB attribute format.
387 		 *
388 		 * SVM uses the 'P' bit in the segment attributes to indicate a
389 		 * NULL segment so clear it if the segment is marked unusable.
390 		 */
391 		attrib = ((desc->access & 0xF000) >> 4) | (desc->access & 0xFF);
392 		if (SEG_DESC_UNUSABLE(desc->access)) {
393 			attrib &= ~0x80;
394 		}
395 		seg->attrib = attrib;
396 	}
397 
398 	VCPU_CTR4(sc->vm, vcpu, "Setting desc %d: base (%#lx), limit (%#x), "
399 	    "attrib (%#x)", reg, seg->base, seg->limit, seg->attrib);
400 
401 	switch (reg) {
402 	case VM_REG_GUEST_CS:
403 	case VM_REG_GUEST_DS:
404 	case VM_REG_GUEST_ES:
405 	case VM_REG_GUEST_SS:
406 		svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
407 		break;
408 	case VM_REG_GUEST_GDTR:
409 	case VM_REG_GUEST_IDTR:
410 		svm_set_dirty(sc, vcpu, VMCB_CACHE_DT);
411 		break;
412 	default:
413 		break;
414 	}
415 
416 	return (0);
417 }
418 
419 int
420 vmcb_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
421 {
422 	struct vmcb *vmcb;
423 	struct svm_softc *sc;
424 	struct vmcb_segment *seg;
425 
426 	sc = arg;
427 	vmcb = svm_get_vmcb(sc, vcpu);
428 	seg = vmcb_segptr(vmcb, reg);
429 	KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
430 	    __func__, reg));
431 
432 	desc->base = seg->base;
433 	desc->limit = seg->limit;
434 	desc->access = 0;
435 
436 	if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
437 		/* Map seg_desc access to VMCB attribute format */
438 		desc->access = ((seg->attrib & 0xF00) << 4) |
439 		    (seg->attrib & 0xFF);
440 
441 		/*
442 		 * VT-x uses bit 16 to indicate a segment that has been loaded
443 		 * with a NULL selector (aka unusable). The 'desc->access'
444 		 * field is interpreted in the VT-x format by the
445 		 * processor-independent code.
446 		 *
447 		 * SVM uses the 'P' bit to convey the same information so
448 		 * convert it into the VT-x format. For more details refer to
449 		 * section "Segment State in the VMCB" in APMv2.
450 		 */
451 		if (reg != VM_REG_GUEST_CS && reg != VM_REG_GUEST_TR) {
452 			if ((desc->access & 0x80) == 0)
453 				desc->access |= 0x10000;  /* Unusable segment */
454 		}
455 	}
456 
457 	return (0);
458 }
459 
460 #ifdef BHYVE_SNAPSHOT
461 int
462 vmcb_getany(struct svm_softc *sc, int vcpu, int ident, uint64_t *val)
463 {
464 	int error = 0;
465 
466 	if (vcpu < 0 || vcpu >= VM_MAXCPU) {
467 		error = EINVAL;
468 		goto err;
469 	}
470 
471 	if (ident >= VM_REG_LAST) {
472 		error = EINVAL;
473 		goto err;
474 	}
475 
476 	error = vmcb_read(sc, vcpu, ident, val);
477 
478 err:
479 	return (error);
480 }
481 
482 int
483 vmcb_setany(struct svm_softc *sc, int vcpu, int ident, uint64_t val)
484 {
485 	int error = 0;
486 
487 	if (vcpu < 0 || vcpu >= VM_MAXCPU) {
488 		error = EINVAL;
489 		goto err;
490 	}
491 
492 	if (ident >= VM_REG_LAST) {
493 		error = EINVAL;
494 		goto err;
495 	}
496 
497 	error = vmcb_write(sc, vcpu, ident, val);
498 
499 err:
500 	return (error);
501 }
502 
503 int
504 vmcb_snapshot_desc(void *arg, int vcpu, int reg, struct vm_snapshot_meta *meta)
505 {
506 	int ret;
507 	struct seg_desc desc;
508 
509 	if (meta->op == VM_SNAPSHOT_SAVE) {
510 		ret = vmcb_getdesc(arg, vcpu, reg, &desc);
511 		if (ret != 0)
512 			goto done;
513 
514 		SNAPSHOT_VAR_OR_LEAVE(desc.base, meta, ret, done);
515 		SNAPSHOT_VAR_OR_LEAVE(desc.limit, meta, ret, done);
516 		SNAPSHOT_VAR_OR_LEAVE(desc.access, meta, ret, done);
517 	} else if (meta->op == VM_SNAPSHOT_RESTORE) {
518 		SNAPSHOT_VAR_OR_LEAVE(desc.base, meta, ret, done);
519 		SNAPSHOT_VAR_OR_LEAVE(desc.limit, meta, ret, done);
520 		SNAPSHOT_VAR_OR_LEAVE(desc.access, meta, ret, done);
521 
522 		ret = vmcb_setdesc(arg, vcpu, reg, &desc);
523 		if (ret != 0)
524 			goto done;
525 	} else {
526 		ret = EINVAL;
527 		goto done;
528 	}
529 
530 done:
531 	return (ret);
532 }
533 
534 int
535 vmcb_snapshot_any(struct svm_softc *sc, int vcpu, int ident,
536 		  struct vm_snapshot_meta *meta)
537 {
538 	int ret;
539 	uint64_t val;
540 
541 	if (meta->op == VM_SNAPSHOT_SAVE) {
542 		ret = vmcb_getany(sc, vcpu, ident, &val);
543 		if (ret != 0)
544 			goto done;
545 
546 		SNAPSHOT_VAR_OR_LEAVE(val, meta, ret, done);
547 	} else if (meta->op == VM_SNAPSHOT_RESTORE) {
548 		SNAPSHOT_VAR_OR_LEAVE(val, meta, ret, done);
549 
550 		ret = vmcb_setany(sc, vcpu, ident, val);
551 		if (ret != 0)
552 			goto done;
553 	} else {
554 		ret = EINVAL;
555 		goto done;
556 	}
557 
558 done:
559 	return (ret);
560 }
561 #endif
562