Deleted Added
full compact
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 ---