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