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