task_switch.c (284894) | task_switch.c (284900) |
---|---|
1/*- 2 * Copyright (c) 2014 Neel Natu <neel@freebsd.org> 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 --- 11 unchanged lines hidden (view full) --- 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> | 1/*- 2 * Copyright (c) 2014 Neel Natu <neel@freebsd.org> 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 --- 11 unchanged lines hidden (view full) --- 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> |
28__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/task_switch.c 284894 2015-06-27 22:48:22Z neel $"); | 28__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/task_switch.c 284900 2015-06-28 03:22:26Z neel $"); |
29 30#include <sys/param.h> 31#include <sys/_iovec.h> 32#include <sys/mman.h> 33 34#include <x86/psl.h> 35#include <x86/segments.h> 36#include <x86/specialreg.h> --- 160 unchanged lines hidden (view full) --- 197 * by the selector 'sel'. 198 * 199 * Returns 0 on success. 200 * Returns 1 if an exception was injected into the guest. 201 * Returns -1 otherwise. 202 */ 203static int 204desc_table_rw(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, | 29 30#include <sys/param.h> 31#include <sys/_iovec.h> 32#include <sys/mman.h> 33 34#include <x86/psl.h> 35#include <x86/segments.h> 36#include <x86/specialreg.h> --- 160 unchanged lines hidden (view full) --- 197 * by the selector 'sel'. 198 * 199 * Returns 0 on success. 200 * Returns 1 if an exception was injected into the guest. 201 * Returns -1 otherwise. 202 */ 203static int 204desc_table_rw(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, |
205 uint16_t sel, struct user_segment_descriptor *desc, bool doread) | 205 uint16_t sel, struct user_segment_descriptor *desc, bool doread, 206 int *faultptr) |
206{ 207 struct iovec iov[2]; 208 uint64_t base; 209 uint32_t limit, access; 210 int error, reg; 211 212 reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR; 213 error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access); 214 assert(error == 0); 215 assert(limit >= SEL_LIMIT(sel)); 216 217 error = vm_copy_setup(ctx, vcpu, paging, base + SEL_START(sel), | 207{ 208 struct iovec iov[2]; 209 uint64_t base; 210 uint32_t limit, access; 211 int error, reg; 212 213 reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR; 214 error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access); 215 assert(error == 0); 216 assert(limit >= SEL_LIMIT(sel)); 217 218 error = vm_copy_setup(ctx, vcpu, paging, base + SEL_START(sel), |
218 sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov)); 219 if (error == 0) { 220 if (doread) 221 vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc)); 222 else 223 vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc)); 224 } 225 return (error); | 219 sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov), 220 faultptr); 221 if (error || *faultptr) 222 return (error); 223 224 if (doread) 225 vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc)); 226 else 227 vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc)); 228 return (0); |
226} 227 228static int 229desc_table_read(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, | 229} 230 231static int 232desc_table_read(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, |
230 uint16_t sel, struct user_segment_descriptor *desc) | 233 uint16_t sel, struct user_segment_descriptor *desc, int *faultptr) |
231{ | 234{ |
232 return (desc_table_rw(ctx, vcpu, paging, sel, desc, true)); | 235 return (desc_table_rw(ctx, vcpu, paging, sel, desc, true, faultptr)); |
233} 234 235static int 236desc_table_write(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, | 236} 237 238static int 239desc_table_write(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, |
237 uint16_t sel, struct user_segment_descriptor *desc) | 240 uint16_t sel, struct user_segment_descriptor *desc, int *faultptr) |
238{ | 241{ |
239 return (desc_table_rw(ctx, vcpu, paging, sel, desc, false)); | 242 return (desc_table_rw(ctx, vcpu, paging, sel, desc, false, faultptr)); |
240} 241 242/* 243 * Read the TSS descriptor referenced by 'sel' into 'desc'. 244 * 245 * Returns 0 on success. 246 * Returns 1 if an exception was injected into the guest. 247 * Returns -1 otherwise. 248 */ 249static int 250read_tss_descriptor(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, | 243} 244 245/* 246 * Read the TSS descriptor referenced by 'sel' into 'desc'. 247 * 248 * Returns 0 on success. 249 * Returns 1 if an exception was injected into the guest. 250 * Returns -1 otherwise. 251 */ 252static int 253read_tss_descriptor(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, |
251 uint16_t sel, struct user_segment_descriptor *desc) | 254 uint16_t sel, struct user_segment_descriptor *desc, int *faultptr) |
252{ 253 struct vm_guest_paging sup_paging; 254 int error; 255 256 assert(!ISLDT(sel)); 257 assert(IDXSEL(sel) != 0); 258 259 /* Fetch the new TSS descriptor */ 260 if (desc_table_limit_check(ctx, vcpu, sel)) { 261 if (ts->reason == TSR_IRET) 262 sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext); 263 else 264 sel_exception(ctx, vcpu, IDT_GP, sel, ts->ext); 265 return (1); 266 } 267 268 sup_paging = ts->paging; 269 sup_paging.cpl = 0; /* implicit supervisor mode */ | 255{ 256 struct vm_guest_paging sup_paging; 257 int error; 258 259 assert(!ISLDT(sel)); 260 assert(IDXSEL(sel) != 0); 261 262 /* Fetch the new TSS descriptor */ 263 if (desc_table_limit_check(ctx, vcpu, sel)) { 264 if (ts->reason == TSR_IRET) 265 sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext); 266 else 267 sel_exception(ctx, vcpu, IDT_GP, sel, ts->ext); 268 return (1); 269 } 270 271 sup_paging = ts->paging; 272 sup_paging.cpl = 0; /* implicit supervisor mode */ |
270 error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc); | 273 error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc, faultptr); |
271 return (error); 272} 273 274static bool 275code_desc(int sd_type) 276{ 277 /* code descriptor */ 278 return ((sd_type & 0x18) == 0x18); --- 17 unchanged lines hidden (view full) --- 296ldt_desc(int sd_type) 297{ 298 299 return (sd_type == SDT_SYSLDT); 300} 301 302/* 303 * Validate the descriptor 'seg_desc' associated with 'segment'. | 274 return (error); 275} 276 277static bool 278code_desc(int sd_type) 279{ 280 /* code descriptor */ 281 return ((sd_type & 0x18) == 0x18); --- 17 unchanged lines hidden (view full) --- 299ldt_desc(int sd_type) 300{ 301 302 return (sd_type == SDT_SYSLDT); 303} 304 305/* 306 * Validate the descriptor 'seg_desc' associated with 'segment'. |
304 * 305 * Returns 0 on success. 306 * Returns 1 if an exception was injected into the guest. 307 * Returns -1 otherwise. | |
308 */ 309static int 310validate_seg_desc(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, | 307 */ 308static int 309validate_seg_desc(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, |
311 int segment, struct seg_desc *seg_desc) | 310 int segment, struct seg_desc *seg_desc, int *faultptr) |
312{ 313 struct vm_guest_paging sup_paging; 314 struct user_segment_descriptor usd; 315 int error, idtvec; 316 int cpl, dpl, rpl; 317 uint16_t sel, cs; 318 bool ldtseg, codeseg, stackseg, dataseg, conforming; 319 --- 44 unchanged lines hidden (view full) --- 364 seg_desc->limit = 0; 365 seg_desc->access = 0x10000; /* unusable */ 366 return (0); 367 } 368 369 /* Read the descriptor from the GDT/LDT */ 370 sup_paging = ts->paging; 371 sup_paging.cpl = 0; /* implicit supervisor mode */ | 311{ 312 struct vm_guest_paging sup_paging; 313 struct user_segment_descriptor usd; 314 int error, idtvec; 315 int cpl, dpl, rpl; 316 uint16_t sel, cs; 317 bool ldtseg, codeseg, stackseg, dataseg, conforming; 318 --- 44 unchanged lines hidden (view full) --- 363 seg_desc->limit = 0; 364 seg_desc->access = 0x10000; /* unusable */ 365 return (0); 366 } 367 368 /* Read the descriptor from the GDT/LDT */ 369 sup_paging = ts->paging; 370 sup_paging.cpl = 0; /* implicit supervisor mode */ |
372 error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd); 373 if (error) | 371 error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd, faultptr); 372 if (error || *faultptr) |
374 return (error); 375 376 /* Verify that the descriptor type is compatible with the segment */ 377 if ((ldtseg && !ldt_desc(usd.sd_type)) || 378 (codeseg && !code_desc(usd.sd_type)) || 379 (dataseg && !data_desc(usd.sd_type)) || 380 (stackseg && !stack_desc(usd.sd_type))) { 381 sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext); --- 89 unchanged lines hidden (view full) --- 471 int error; 472 473 error = vm_set_desc(ctx, vcpu, reg, sd->base, sd->limit, sd->access); 474 assert(error == 0); 475} 476 477/* 478 * Update the vcpu registers to reflect the state of the new task. | 373 return (error); 374 375 /* Verify that the descriptor type is compatible with the segment */ 376 if ((ldtseg && !ldt_desc(usd.sd_type)) || 377 (codeseg && !code_desc(usd.sd_type)) || 378 (dataseg && !data_desc(usd.sd_type)) || 379 (stackseg && !stack_desc(usd.sd_type))) { 380 sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext); --- 89 unchanged lines hidden (view full) --- 470 int error; 471 472 error = vm_set_desc(ctx, vcpu, reg, sd->base, sd->limit, sd->access); 473 assert(error == 0); 474} 475 476/* 477 * Update the vcpu registers to reflect the state of the new task. |
479 * 480 * Returns 0 on success. 481 * Returns 1 if an exception was injected into the guest. 482 * Returns -1 otherwise. | |
483 */ 484static int 485tss32_restore(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, | 478 */ 479static int 480tss32_restore(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts, |
486 uint16_t ot_sel, struct tss32 *tss, struct iovec *iov) | 481 uint16_t ot_sel, struct tss32 *tss, struct iovec *iov, int *faultptr) |
487{ 488 struct seg_desc seg_desc, seg_desc2; 489 uint64_t *pdpte, maxphyaddr, reserved; 490 uint32_t eflags; 491 int error, i; 492 bool nested; 493 494 nested = false; --- 65 unchanged lines hidden (view full) --- 560 /* 561 * If this is a nested task then write out the new TSS to update 562 * the previous link field. 563 */ 564 if (nested) 565 vm_copyout(ctx, vcpu, tss, iov, sizeof(*tss)); 566 567 /* Validate segment descriptors */ | 482{ 483 struct seg_desc seg_desc, seg_desc2; 484 uint64_t *pdpte, maxphyaddr, reserved; 485 uint32_t eflags; 486 int error, i; 487 bool nested; 488 489 nested = false; --- 65 unchanged lines hidden (view full) --- 555 /* 556 * If this is a nested task then write out the new TSS to update 557 * the previous link field. 558 */ 559 if (nested) 560 vm_copyout(ctx, vcpu, tss, iov, sizeof(*tss)); 561 562 /* Validate segment descriptors */ |
568 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_LDTR, &seg_desc); 569 if (error) | 563 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_LDTR, &seg_desc, 564 faultptr); 565 if (error || *faultptr) |
570 return (error); 571 update_seg_desc(ctx, vcpu, VM_REG_GUEST_LDTR, &seg_desc); 572 573 /* 574 * Section "Checks on Guest Segment Registers", Intel SDM, Vol 3. 575 * 576 * The SS and CS attribute checks on VM-entry are inter-dependent so 577 * we need to make sure that both segments are valid before updating 578 * either of them. This ensures that the VMCS state can pass the 579 * VM-entry checks so the guest can handle any exception injected 580 * during task switch emulation. 581 */ | 566 return (error); 567 update_seg_desc(ctx, vcpu, VM_REG_GUEST_LDTR, &seg_desc); 568 569 /* 570 * Section "Checks on Guest Segment Registers", Intel SDM, Vol 3. 571 * 572 * The SS and CS attribute checks on VM-entry are inter-dependent so 573 * we need to make sure that both segments are valid before updating 574 * either of them. This ensures that the VMCS state can pass the 575 * VM-entry checks so the guest can handle any exception injected 576 * during task switch emulation. 577 */ |
582 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_CS, &seg_desc); 583 if (error) | 578 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_CS, &seg_desc, 579 faultptr); 580 if (error || *faultptr) |
584 return (error); | 581 return (error); |
585 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_SS, &seg_desc2); 586 if (error) | 582 583 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_SS, &seg_desc2, 584 faultptr); 585 if (error || *faultptr) |
587 return (error); 588 update_seg_desc(ctx, vcpu, VM_REG_GUEST_CS, &seg_desc); 589 update_seg_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc2); 590 ts->paging.cpl = tss->tss_cs & SEL_RPL_MASK; 591 | 586 return (error); 587 update_seg_desc(ctx, vcpu, VM_REG_GUEST_CS, &seg_desc); 588 update_seg_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc2); 589 ts->paging.cpl = tss->tss_cs & SEL_RPL_MASK; 590 |
592 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_DS, &seg_desc); 593 if (error) | 591 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_DS, &seg_desc, 592 faultptr); 593 if (error || *faultptr) |
594 return (error); 595 update_seg_desc(ctx, vcpu, VM_REG_GUEST_DS, &seg_desc); 596 | 594 return (error); 595 update_seg_desc(ctx, vcpu, VM_REG_GUEST_DS, &seg_desc); 596 |
597 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_ES, &seg_desc); 598 if (error) | 597 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_ES, &seg_desc, 598 faultptr); 599 if (error || *faultptr) |
599 return (error); 600 update_seg_desc(ctx, vcpu, VM_REG_GUEST_ES, &seg_desc); 601 | 600 return (error); 601 update_seg_desc(ctx, vcpu, VM_REG_GUEST_ES, &seg_desc); 602 |
602 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_FS, &seg_desc); 603 if (error) | 603 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_FS, &seg_desc, 604 faultptr); 605 if (error || *faultptr) |
604 return (error); 605 update_seg_desc(ctx, vcpu, VM_REG_GUEST_FS, &seg_desc); 606 | 606 return (error); 607 update_seg_desc(ctx, vcpu, VM_REG_GUEST_FS, &seg_desc); 608 |
607 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_GS, &seg_desc); 608 if (error) | 609 error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_GS, &seg_desc, 610 faultptr); 611 if (error || *faultptr) |
609 return (error); 610 update_seg_desc(ctx, vcpu, VM_REG_GUEST_GS, &seg_desc); 611 612 return (0); 613} 614 615/* 616 * Push an error code on the stack of the new task. This is needed if the 617 * task switch was triggered by a hardware exception that causes an error 618 * code to be saved (e.g. #PF). | 612 return (error); 613 update_seg_desc(ctx, vcpu, VM_REG_GUEST_GS, &seg_desc); 614 615 return (0); 616} 617 618/* 619 * Push an error code on the stack of the new task. This is needed if the 620 * task switch was triggered by a hardware exception that causes an error 621 * code to be saved (e.g. #PF). |
619 * 620 * Returns 0 on success. 621 * Returns 1 if an exception was injected into the guest. 622 * Returns -1 otherwise. | |
623 */ 624static int 625push_errcode(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, | 622 */ 623static int 624push_errcode(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, |
626 int task_type, uint32_t errcode) | 625 int task_type, uint32_t errcode, int *faultptr) |
627{ 628 struct iovec iov[2]; 629 struct seg_desc seg_desc; 630 int stacksize, bytes, error; 631 uint64_t gla, cr0, rflags; 632 uint32_t esp; 633 uint16_t stacksel; 634 | 626{ 627 struct iovec iov[2]; 628 struct seg_desc seg_desc; 629 int stacksize, bytes, error; 630 uint64_t gla, cr0, rflags; 631 uint32_t esp; 632 uint16_t stacksel; 633 |
634 *faultptr = 0; 635 |
|
635 cr0 = GETREG(ctx, vcpu, VM_REG_GUEST_CR0); 636 rflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS); 637 stacksel = GETREG(ctx, vcpu, VM_REG_GUEST_SS); 638 639 error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc.base, 640 &seg_desc.limit, &seg_desc.access); 641 assert(error == 0); 642 --- 18 unchanged lines hidden (view full) --- 661 stacksize = 2; 662 663 esp = GETREG(ctx, vcpu, VM_REG_GUEST_RSP); 664 esp -= bytes; 665 666 if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS, 667 &seg_desc, esp, bytes, stacksize, PROT_WRITE, &gla)) { 668 sel_exception(ctx, vcpu, IDT_SS, stacksel, 1); | 636 cr0 = GETREG(ctx, vcpu, VM_REG_GUEST_CR0); 637 rflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS); 638 stacksel = GETREG(ctx, vcpu, VM_REG_GUEST_SS); 639 640 error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc.base, 641 &seg_desc.limit, &seg_desc.access); 642 assert(error == 0); 643 --- 18 unchanged lines hidden (view full) --- 662 stacksize = 2; 663 664 esp = GETREG(ctx, vcpu, VM_REG_GUEST_RSP); 665 esp -= bytes; 666 667 if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS, 668 &seg_desc, esp, bytes, stacksize, PROT_WRITE, &gla)) { 669 sel_exception(ctx, vcpu, IDT_SS, stacksel, 1); |
669 return (1); | 670 *faultptr = 1; 671 return (0); |
670 } 671 672 if (vie_alignment_check(paging->cpl, bytes, cr0, rflags, gla)) { 673 vm_inject_ac(ctx, vcpu, 1); | 672 } 673 674 if (vie_alignment_check(paging->cpl, bytes, cr0, rflags, gla)) { 675 vm_inject_ac(ctx, vcpu, 1); |
674 return (1); | 676 *faultptr = 1; 677 return (0); |
675 } 676 677 error = vm_copy_setup(ctx, vcpu, paging, gla, bytes, PROT_WRITE, | 678 } 679 680 error = vm_copy_setup(ctx, vcpu, paging, gla, bytes, PROT_WRITE, |
678 iov, nitems(iov)); 679 if (error) | 681 iov, nitems(iov), faultptr); 682 if (error || *faultptr) |
680 return (error); 681 682 vm_copyout(ctx, vcpu, &errcode, iov, bytes); 683 SETREG(ctx, vcpu, VM_REG_GUEST_RSP, esp); 684 return (0); 685} 686 687/* 688 * Evaluate return value from helper functions and potentially return to 689 * the VM run loop. | 683 return (error); 684 685 vm_copyout(ctx, vcpu, &errcode, iov, bytes); 686 SETREG(ctx, vcpu, VM_REG_GUEST_RSP, esp); 687 return (0); 688} 689 690/* 691 * Evaluate return value from helper functions and potentially return to 692 * the VM run loop. |
690 * 0: success 691 * +1: an exception was injected into the guest vcpu 692 * -1: unrecoverable/programming error | |
693 */ | 693 */ |
694#define CHKERR(x) \ | 694#define CHKERR(error,fault) \ |
695 do { \ | 695 do { \ |
696 assert(((x) == 0) || ((x) == 1) || ((x) == -1)); \ 697 if ((x) == -1) \ | 696 assert((error == 0) || (error == EFAULT)); \ 697 if (error) \ |
698 return (VMEXIT_ABORT); \ | 698 return (VMEXIT_ABORT); \ |
699 else if ((x) == 1) \ | 699 else if (fault) \ |
700 return (VMEXIT_CONTINUE); \ 701 } while (0) 702 703int 704vmexit_task_switch(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 705{ 706 struct seg_desc nt; 707 struct tss32 oldtss, newtss; 708 struct vm_task_switch *task_switch; 709 struct vm_guest_paging *paging, sup_paging; 710 struct user_segment_descriptor nt_desc, ot_desc; 711 struct iovec nt_iov[2], ot_iov[2]; 712 uint64_t cr0, ot_base; 713 uint32_t eip, ot_lim, access; | 700 return (VMEXIT_CONTINUE); \ 701 } while (0) 702 703int 704vmexit_task_switch(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 705{ 706 struct seg_desc nt; 707 struct tss32 oldtss, newtss; 708 struct vm_task_switch *task_switch; 709 struct vm_guest_paging *paging, sup_paging; 710 struct user_segment_descriptor nt_desc, ot_desc; 711 struct iovec nt_iov[2], ot_iov[2]; 712 uint64_t cr0, ot_base; 713 uint32_t eip, ot_lim, access; |
714 int error, ext, minlimit, nt_type, ot_type, vcpu; | 714 int error, ext, fault, minlimit, nt_type, ot_type, vcpu; |
715 enum task_switch_reason reason; 716 uint16_t nt_sel, ot_sel; 717 718 task_switch = &vmexit->u.task_switch; 719 nt_sel = task_switch->tsssel; 720 ext = vmexit->u.task_switch.ext; 721 reason = vmexit->u.task_switch.reason; 722 paging = &vmexit->u.task_switch.paging; --- 11 unchanged lines hidden (view full) --- 734 * The following page table accesses are implicitly supervisor mode: 735 * - accesses to GDT or LDT to load segment descriptors 736 * - accesses to the task state segment during task switch 737 */ 738 sup_paging = *paging; 739 sup_paging.cpl = 0; /* implicit supervisor mode */ 740 741 /* Fetch the new TSS descriptor */ | 715 enum task_switch_reason reason; 716 uint16_t nt_sel, ot_sel; 717 718 task_switch = &vmexit->u.task_switch; 719 nt_sel = task_switch->tsssel; 720 ext = vmexit->u.task_switch.ext; 721 reason = vmexit->u.task_switch.reason; 722 paging = &vmexit->u.task_switch.paging; --- 11 unchanged lines hidden (view full) --- 734 * The following page table accesses are implicitly supervisor mode: 735 * - accesses to GDT or LDT to load segment descriptors 736 * - accesses to the task state segment during task switch 737 */ 738 sup_paging = *paging; 739 sup_paging.cpl = 0; /* implicit supervisor mode */ 740 741 /* Fetch the new TSS descriptor */ |
742 error = read_tss_descriptor(ctx, vcpu, task_switch, nt_sel, &nt_desc); 743 CHKERR(error); | 742 error = read_tss_descriptor(ctx, vcpu, task_switch, nt_sel, &nt_desc, 743 &fault); 744 CHKERR(error, fault); |
744 745 nt = usd_to_seg_desc(&nt_desc); 746 747 /* Verify the type of the new TSS */ 748 nt_type = SEG_DESC_TYPE(nt.access); 749 if (nt_type != SDT_SYS386BSY && nt_type != SDT_SYS386TSS && 750 nt_type != SDT_SYS286BSY && nt_type != SDT_SYS286TSS) { 751 sel_exception(ctx, vcpu, IDT_TS, nt_sel, ext); --- 35 unchanged lines hidden (view full) --- 787 */ 788 if (reason != TSR_IRET && TSS_BUSY(nt_type)) { 789 sel_exception(ctx, vcpu, IDT_GP, nt_sel, ext); 790 goto done; 791 } 792 793 /* Fetch the new TSS */ 794 error = vm_copy_setup(ctx, vcpu, &sup_paging, nt.base, minlimit + 1, | 745 746 nt = usd_to_seg_desc(&nt_desc); 747 748 /* Verify the type of the new TSS */ 749 nt_type = SEG_DESC_TYPE(nt.access); 750 if (nt_type != SDT_SYS386BSY && nt_type != SDT_SYS386TSS && 751 nt_type != SDT_SYS286BSY && nt_type != SDT_SYS286TSS) { 752 sel_exception(ctx, vcpu, IDT_TS, nt_sel, ext); --- 35 unchanged lines hidden (view full) --- 788 */ 789 if (reason != TSR_IRET && TSS_BUSY(nt_type)) { 790 sel_exception(ctx, vcpu, IDT_GP, nt_sel, ext); 791 goto done; 792 } 793 794 /* Fetch the new TSS */ 795 error = vm_copy_setup(ctx, vcpu, &sup_paging, nt.base, minlimit + 1, |
795 PROT_READ | PROT_WRITE, nt_iov, nitems(nt_iov)); 796 CHKERR(error); | 796 PROT_READ | PROT_WRITE, nt_iov, nitems(nt_iov), &fault); 797 CHKERR(error, fault); |
797 vm_copyin(ctx, vcpu, nt_iov, &newtss, minlimit + 1); 798 799 /* Get the old TSS selector from the guest's task register */ 800 ot_sel = GETREG(ctx, vcpu, VM_REG_GUEST_TR); 801 if (ISLDT(ot_sel) || IDXSEL(ot_sel) == 0) { 802 /* 803 * This might happen if a task switch was attempted without 804 * ever loading the task register with LTR. In this case the --- 8 unchanged lines hidden (view full) --- 813 error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_TR, &ot_base, &ot_lim, 814 &access); 815 assert(error == 0); 816 assert(!SEG_DESC_UNUSABLE(access) && SEG_DESC_PRESENT(access)); 817 ot_type = SEG_DESC_TYPE(access); 818 assert(ot_type == SDT_SYS386BSY || ot_type == SDT_SYS286BSY); 819 820 /* Fetch the old TSS descriptor */ | 798 vm_copyin(ctx, vcpu, nt_iov, &newtss, minlimit + 1); 799 800 /* Get the old TSS selector from the guest's task register */ 801 ot_sel = GETREG(ctx, vcpu, VM_REG_GUEST_TR); 802 if (ISLDT(ot_sel) || IDXSEL(ot_sel) == 0) { 803 /* 804 * This might happen if a task switch was attempted without 805 * ever loading the task register with LTR. In this case the --- 8 unchanged lines hidden (view full) --- 814 error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_TR, &ot_base, &ot_lim, 815 &access); 816 assert(error == 0); 817 assert(!SEG_DESC_UNUSABLE(access) && SEG_DESC_PRESENT(access)); 818 ot_type = SEG_DESC_TYPE(access); 819 assert(ot_type == SDT_SYS386BSY || ot_type == SDT_SYS286BSY); 820 821 /* Fetch the old TSS descriptor */ |
821 error = read_tss_descriptor(ctx, vcpu, task_switch, ot_sel, &ot_desc); 822 CHKERR(error); | 822 error = read_tss_descriptor(ctx, vcpu, task_switch, ot_sel, &ot_desc, 823 &fault); 824 CHKERR(error, fault); |
823 824 /* Get the old TSS */ 825 error = vm_copy_setup(ctx, vcpu, &sup_paging, ot_base, minlimit + 1, | 825 826 /* Get the old TSS */ 827 error = vm_copy_setup(ctx, vcpu, &sup_paging, ot_base, minlimit + 1, |
826 PROT_READ | PROT_WRITE, ot_iov, nitems(ot_iov)); 827 CHKERR(error); | 828 PROT_READ | PROT_WRITE, ot_iov, nitems(ot_iov), &fault); 829 CHKERR(error, fault); |
828 vm_copyin(ctx, vcpu, ot_iov, &oldtss, minlimit + 1); 829 830 /* 831 * Clear the busy bit in the old TSS descriptor if the task switch 832 * due to an IRET or JMP instruction. 833 */ 834 if (reason == TSR_IRET || reason == TSR_JMP) { 835 ot_desc.sd_type &= ~0x2; 836 error = desc_table_write(ctx, vcpu, &sup_paging, ot_sel, | 830 vm_copyin(ctx, vcpu, ot_iov, &oldtss, minlimit + 1); 831 832 /* 833 * Clear the busy bit in the old TSS descriptor if the task switch 834 * due to an IRET or JMP instruction. 835 */ 836 if (reason == TSR_IRET || reason == TSR_JMP) { 837 ot_desc.sd_type &= ~0x2; 838 error = desc_table_write(ctx, vcpu, &sup_paging, ot_sel, |
837 &ot_desc); 838 CHKERR(error); | 839 &ot_desc, &fault); 840 CHKERR(error, fault); |
839 } 840 841 if (nt_type == SDT_SYS286BSY || nt_type == SDT_SYS286TSS) { 842 fprintf(stderr, "Task switch to 16-bit TSS not supported\n"); 843 return (VMEXIT_ABORT); 844 } 845 846 /* Save processor state in old TSS */ 847 tss32_save(ctx, vcpu, task_switch, eip, &oldtss, ot_iov); 848 849 /* 850 * If the task switch was triggered for any reason other than IRET 851 * then set the busy bit in the new TSS descriptor. 852 */ 853 if (reason != TSR_IRET) { 854 nt_desc.sd_type |= 0x2; 855 error = desc_table_write(ctx, vcpu, &sup_paging, nt_sel, | 841 } 842 843 if (nt_type == SDT_SYS286BSY || nt_type == SDT_SYS286TSS) { 844 fprintf(stderr, "Task switch to 16-bit TSS not supported\n"); 845 return (VMEXIT_ABORT); 846 } 847 848 /* Save processor state in old TSS */ 849 tss32_save(ctx, vcpu, task_switch, eip, &oldtss, ot_iov); 850 851 /* 852 * If the task switch was triggered for any reason other than IRET 853 * then set the busy bit in the new TSS descriptor. 854 */ 855 if (reason != TSR_IRET) { 856 nt_desc.sd_type |= 0x2; 857 error = desc_table_write(ctx, vcpu, &sup_paging, nt_sel, |
856 &nt_desc); 857 CHKERR(error); | 858 &nt_desc, &fault); 859 CHKERR(error, fault); |
858 } 859 860 /* Update task register to point at the new TSS */ 861 SETREG(ctx, vcpu, VM_REG_GUEST_TR, nt_sel); 862 863 /* Update the hidden descriptor state of the task register */ 864 nt = usd_to_seg_desc(&nt_desc); 865 update_seg_desc(ctx, vcpu, VM_REG_GUEST_TR, &nt); --- 6 unchanged lines hidden (view full) --- 872 * We are now committed to the task switch. Any exceptions encountered 873 * after this point will be handled in the context of the new task and 874 * the saved instruction pointer will belong to the new task. 875 */ 876 error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, newtss.tss_eip); 877 assert(error == 0); 878 879 /* Load processor state from new TSS */ | 860 } 861 862 /* Update task register to point at the new TSS */ 863 SETREG(ctx, vcpu, VM_REG_GUEST_TR, nt_sel); 864 865 /* Update the hidden descriptor state of the task register */ 866 nt = usd_to_seg_desc(&nt_desc); 867 update_seg_desc(ctx, vcpu, VM_REG_GUEST_TR, &nt); --- 6 unchanged lines hidden (view full) --- 874 * We are now committed to the task switch. Any exceptions encountered 875 * after this point will be handled in the context of the new task and 876 * the saved instruction pointer will belong to the new task. 877 */ 878 error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, newtss.tss_eip); 879 assert(error == 0); 880 881 /* Load processor state from new TSS */ |
880 error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov); 881 CHKERR(error); | 882 error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov, 883 &fault); 884 CHKERR(error, fault); |
882 883 /* 884 * Section "Interrupt Tasks" in Intel SDM, Vol 3: if an exception 885 * caused an error code to be generated, this error code is copied 886 * to the stack of the new task. 887 */ 888 if (task_switch->errcode_valid) { 889 assert(task_switch->ext); 890 assert(task_switch->reason == TSR_IDT_GATE); 891 error = push_errcode(ctx, vcpu, &task_switch->paging, nt_type, | 885 886 /* 887 * Section "Interrupt Tasks" in Intel SDM, Vol 3: if an exception 888 * caused an error code to be generated, this error code is copied 889 * to the stack of the new task. 890 */ 891 if (task_switch->errcode_valid) { 892 assert(task_switch->ext); 893 assert(task_switch->reason == TSR_IDT_GATE); 894 error = push_errcode(ctx, vcpu, &task_switch->paging, nt_type, |
892 task_switch->errcode); 893 CHKERR(error); | 895 task_switch->errcode, &fault); 896 CHKERR(error, fault); |
894 } 895 896 /* 897 * Treatment of virtual-NMI blocking if NMI is delivered through 898 * a task gate. 899 * 900 * Section "Architectural State Before A VM Exit", Intel SDM, Vol3: 901 * If the virtual NMIs VM-execution control is 1, VM entry injects --- 35 unchanged lines hidden --- | 897 } 898 899 /* 900 * Treatment of virtual-NMI blocking if NMI is delivered through 901 * a task gate. 902 * 903 * Section "Architectural State Before A VM Exit", Intel SDM, Vol3: 904 * If the virtual NMIs VM-execution control is 1, VM entry injects --- 35 unchanged lines hidden --- |