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