xref: /illumos-gate/usr/src/cmd/bhyve/task_switch.c (revision e0c0d44e)
14c87aefeSPatrick Mooney /*-
24c87aefeSPatrick Mooney  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
34c87aefeSPatrick Mooney  *
44c87aefeSPatrick Mooney  * Copyright (c) 2014 Neel Natu <neel@freebsd.org>
54c87aefeSPatrick Mooney  * All rights reserved.
64c87aefeSPatrick Mooney  *
74c87aefeSPatrick Mooney  * Redistribution and use in source and binary forms, with or without
84c87aefeSPatrick Mooney  * modification, are permitted provided that the following conditions
94c87aefeSPatrick Mooney  * are met:
104c87aefeSPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
114c87aefeSPatrick Mooney  *    notice, this list of conditions and the following disclaimer.
124c87aefeSPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
134c87aefeSPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
144c87aefeSPatrick Mooney  *    documentation and/or other materials provided with the distribution.
154c87aefeSPatrick Mooney  *
164c87aefeSPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
174c87aefeSPatrick Mooney  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184c87aefeSPatrick Mooney  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194c87aefeSPatrick Mooney  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204c87aefeSPatrick Mooney  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214c87aefeSPatrick Mooney  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224c87aefeSPatrick Mooney  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234c87aefeSPatrick Mooney  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244c87aefeSPatrick Mooney  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254c87aefeSPatrick Mooney  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264c87aefeSPatrick Mooney  * SUCH DAMAGE.
274c87aefeSPatrick Mooney  */
28*e0c0d44eSPatrick Mooney /*
29*e0c0d44eSPatrick Mooney  * This file and its contents are supplied under the terms of the
30*e0c0d44eSPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
31*e0c0d44eSPatrick Mooney  * You may only use this file in accordance with the terms of version
32*e0c0d44eSPatrick Mooney  * 1.0 of the CDDL.
33*e0c0d44eSPatrick Mooney  *
34*e0c0d44eSPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
35*e0c0d44eSPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
36*e0c0d44eSPatrick Mooney  * http://www.illumos.org/license/CDDL.
37*e0c0d44eSPatrick Mooney  *
38*e0c0d44eSPatrick Mooney  * Copyright 2020 Oxide Computer Company
39*e0c0d44eSPatrick Mooney  */
404c87aefeSPatrick Mooney 
414c87aefeSPatrick Mooney #include <sys/cdefs.h>
424c87aefeSPatrick Mooney __FBSDID("$FreeBSD$");
434c87aefeSPatrick Mooney 
444c87aefeSPatrick Mooney #include <sys/param.h>
454c87aefeSPatrick Mooney #include <sys/_iovec.h>
464c87aefeSPatrick Mooney #include <sys/mman.h>
474c87aefeSPatrick Mooney 
484c87aefeSPatrick Mooney #include <x86/psl.h>
494c87aefeSPatrick Mooney #include <x86/segments.h>
504c87aefeSPatrick Mooney #include <x86/specialreg.h>
514c87aefeSPatrick Mooney #include <machine/vmm.h>
524c87aefeSPatrick Mooney 
534c87aefeSPatrick Mooney #include <assert.h>
544c87aefeSPatrick Mooney #include <errno.h>
554c87aefeSPatrick Mooney #include <stdbool.h>
564c87aefeSPatrick Mooney #include <stdio.h>
574c87aefeSPatrick Mooney #include <stdlib.h>
584c87aefeSPatrick Mooney 
594c87aefeSPatrick Mooney #include <vmmapi.h>
604c87aefeSPatrick Mooney 
614c87aefeSPatrick Mooney #include "bhyverun.h"
62154972afSPatrick Mooney #include "debug.h"
634c87aefeSPatrick Mooney 
644c87aefeSPatrick Mooney /*
654c87aefeSPatrick Mooney  * Using 'struct i386tss' is tempting but causes myriad sign extension
664c87aefeSPatrick Mooney  * issues because all of its fields are defined as signed integers.
674c87aefeSPatrick Mooney  */
684c87aefeSPatrick Mooney struct tss32 {
694c87aefeSPatrick Mooney 	uint16_t	tss_link;
704c87aefeSPatrick Mooney 	uint16_t	rsvd1;
714c87aefeSPatrick Mooney 	uint32_t	tss_esp0;
724c87aefeSPatrick Mooney 	uint16_t	tss_ss0;
734c87aefeSPatrick Mooney 	uint16_t	rsvd2;
744c87aefeSPatrick Mooney 	uint32_t	tss_esp1;
754c87aefeSPatrick Mooney 	uint16_t	tss_ss1;
764c87aefeSPatrick Mooney 	uint16_t	rsvd3;
774c87aefeSPatrick Mooney 	uint32_t	tss_esp2;
784c87aefeSPatrick Mooney 	uint16_t	tss_ss2;
794c87aefeSPatrick Mooney 	uint16_t	rsvd4;
804c87aefeSPatrick Mooney 	uint32_t	tss_cr3;
814c87aefeSPatrick Mooney 	uint32_t	tss_eip;
824c87aefeSPatrick Mooney 	uint32_t	tss_eflags;
834c87aefeSPatrick Mooney 	uint32_t	tss_eax;
844c87aefeSPatrick Mooney 	uint32_t	tss_ecx;
854c87aefeSPatrick Mooney 	uint32_t	tss_edx;
864c87aefeSPatrick Mooney 	uint32_t	tss_ebx;
874c87aefeSPatrick Mooney 	uint32_t	tss_esp;
884c87aefeSPatrick Mooney 	uint32_t	tss_ebp;
894c87aefeSPatrick Mooney 	uint32_t	tss_esi;
904c87aefeSPatrick Mooney 	uint32_t	tss_edi;
914c87aefeSPatrick Mooney 	uint16_t	tss_es;
924c87aefeSPatrick Mooney 	uint16_t	rsvd5;
934c87aefeSPatrick Mooney 	uint16_t	tss_cs;
944c87aefeSPatrick Mooney 	uint16_t	rsvd6;
954c87aefeSPatrick Mooney 	uint16_t	tss_ss;
964c87aefeSPatrick Mooney 	uint16_t	rsvd7;
974c87aefeSPatrick Mooney 	uint16_t	tss_ds;
984c87aefeSPatrick Mooney 	uint16_t	rsvd8;
994c87aefeSPatrick Mooney 	uint16_t	tss_fs;
1004c87aefeSPatrick Mooney 	uint16_t	rsvd9;
1014c87aefeSPatrick Mooney 	uint16_t	tss_gs;
1024c87aefeSPatrick Mooney 	uint16_t	rsvd10;
1034c87aefeSPatrick Mooney 	uint16_t	tss_ldt;
1044c87aefeSPatrick Mooney 	uint16_t	rsvd11;
1054c87aefeSPatrick Mooney 	uint16_t	tss_trap;
1064c87aefeSPatrick Mooney 	uint16_t	tss_iomap;
1074c87aefeSPatrick Mooney };
1084c87aefeSPatrick Mooney static_assert(sizeof(struct tss32) == 104, "compile-time assertion failed");
1094c87aefeSPatrick Mooney 
1104c87aefeSPatrick Mooney #define	SEL_START(sel)	(((sel) & ~0x7))
1114c87aefeSPatrick Mooney #define	SEL_LIMIT(sel)	(((sel) | 0x7))
1124c87aefeSPatrick Mooney #define	TSS_BUSY(type)	(((type) & 0x2) != 0)
1134c87aefeSPatrick Mooney 
1144c87aefeSPatrick Mooney static uint64_t
GETREG(struct vmctx * ctx,int vcpu,int reg)1154c87aefeSPatrick Mooney GETREG(struct vmctx *ctx, int vcpu, int reg)
1164c87aefeSPatrick Mooney {
1174c87aefeSPatrick Mooney 	uint64_t val;
1184c87aefeSPatrick Mooney 	int error;
1194c87aefeSPatrick Mooney 
1204c87aefeSPatrick Mooney 	error = vm_get_register(ctx, vcpu, reg, &val);
1214c87aefeSPatrick Mooney 	assert(error == 0);
1224c87aefeSPatrick Mooney 	return (val);
1234c87aefeSPatrick Mooney }
1244c87aefeSPatrick Mooney 
1254c87aefeSPatrick Mooney static void
SETREG(struct vmctx * ctx,int vcpu,int reg,uint64_t val)1264c87aefeSPatrick Mooney SETREG(struct vmctx *ctx, int vcpu, int reg, uint64_t val)
1274c87aefeSPatrick Mooney {
1284c87aefeSPatrick Mooney 	int error;
1294c87aefeSPatrick Mooney 
1304c87aefeSPatrick Mooney 	error = vm_set_register(ctx, vcpu, reg, val);
1314c87aefeSPatrick Mooney 	assert(error == 0);
1324c87aefeSPatrick Mooney }
1334c87aefeSPatrick Mooney 
1344c87aefeSPatrick Mooney static struct seg_desc
usd_to_seg_desc(struct user_segment_descriptor * usd)1354c87aefeSPatrick Mooney usd_to_seg_desc(struct user_segment_descriptor *usd)
1364c87aefeSPatrick Mooney {
1374c87aefeSPatrick Mooney 	struct seg_desc seg_desc;
1384c87aefeSPatrick Mooney 
1394c87aefeSPatrick Mooney 	seg_desc.base = (u_int)USD_GETBASE(usd);
1404c87aefeSPatrick Mooney 	if (usd->sd_gran)
1414c87aefeSPatrick Mooney 		seg_desc.limit = (u_int)(USD_GETLIMIT(usd) << 12) | 0xfff;
1424c87aefeSPatrick Mooney 	else
1434c87aefeSPatrick Mooney 		seg_desc.limit = (u_int)USD_GETLIMIT(usd);
1444c87aefeSPatrick Mooney 	seg_desc.access = usd->sd_type | usd->sd_dpl << 5 | usd->sd_p << 7;
1454c87aefeSPatrick Mooney 	seg_desc.access |= usd->sd_xx << 12;
1464c87aefeSPatrick Mooney 	seg_desc.access |= usd->sd_def32 << 14;
1474c87aefeSPatrick Mooney 	seg_desc.access |= usd->sd_gran << 15;
1484c87aefeSPatrick Mooney 
1494c87aefeSPatrick Mooney 	return (seg_desc);
1504c87aefeSPatrick Mooney }
1514c87aefeSPatrick Mooney 
1524c87aefeSPatrick Mooney /*
1534c87aefeSPatrick Mooney  * Inject an exception with an error code that is a segment selector.
1544c87aefeSPatrick Mooney  * The format of the error code is described in section 6.13, "Error Code",
1554c87aefeSPatrick Mooney  * Intel SDM volume 3.
1564c87aefeSPatrick Mooney  *
1574c87aefeSPatrick Mooney  * Bit 0 (EXT) denotes whether the exception occurred during delivery
1584c87aefeSPatrick Mooney  * of an external event like an interrupt.
1594c87aefeSPatrick Mooney  *
1604c87aefeSPatrick Mooney  * Bit 1 (IDT) indicates whether the selector points to a gate descriptor
1614c87aefeSPatrick Mooney  * in the IDT.
1624c87aefeSPatrick Mooney  *
1634c87aefeSPatrick Mooney  * Bit 2(GDT/LDT) has the usual interpretation of Table Indicator (TI).
1644c87aefeSPatrick Mooney  */
1654c87aefeSPatrick Mooney static void
sel_exception(struct vmctx * ctx,int vcpu,int vector,uint16_t sel,int ext)1664c87aefeSPatrick Mooney sel_exception(struct vmctx *ctx, int vcpu, int vector, uint16_t sel, int ext)
1674c87aefeSPatrick Mooney {
1684c87aefeSPatrick Mooney 	/*
1694c87aefeSPatrick Mooney 	 * Bit 2 from the selector is retained as-is in the error code.
1704c87aefeSPatrick Mooney 	 *
1714c87aefeSPatrick Mooney 	 * Bit 1 can be safely cleared because none of the selectors
1724c87aefeSPatrick Mooney 	 * encountered during task switch emulation refer to a task
1734c87aefeSPatrick Mooney 	 * gate in the IDT.
1744c87aefeSPatrick Mooney 	 *
1754c87aefeSPatrick Mooney 	 * Bit 0 is set depending on the value of 'ext'.
1764c87aefeSPatrick Mooney 	 */
1774c87aefeSPatrick Mooney 	sel &= ~0x3;
1784c87aefeSPatrick Mooney 	if (ext)
1794c87aefeSPatrick Mooney 		sel |= 0x1;
1804c87aefeSPatrick Mooney 	vm_inject_fault(ctx, vcpu, vector, 1, sel);
1814c87aefeSPatrick Mooney }
1824c87aefeSPatrick Mooney 
1834c87aefeSPatrick Mooney /*
1844c87aefeSPatrick Mooney  * Return 0 if the selector 'sel' in within the limits of the GDT/LDT
1854c87aefeSPatrick Mooney  * and non-zero otherwise.
1864c87aefeSPatrick Mooney  */
1874c87aefeSPatrick Mooney static int
desc_table_limit_check(struct vmctx * ctx,int vcpu,uint16_t sel)1884c87aefeSPatrick Mooney desc_table_limit_check(struct vmctx *ctx, int vcpu, uint16_t sel)
1894c87aefeSPatrick Mooney {
1904c87aefeSPatrick Mooney 	uint64_t base;
1914c87aefeSPatrick Mooney 	uint32_t limit, access;
1924c87aefeSPatrick Mooney 	int error, reg;
1934c87aefeSPatrick Mooney 
1944c87aefeSPatrick Mooney 	reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR;
1954c87aefeSPatrick Mooney 	error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access);
1964c87aefeSPatrick Mooney 	assert(error == 0);
1974c87aefeSPatrick Mooney 
1984c87aefeSPatrick Mooney 	if (reg == VM_REG_GUEST_LDTR) {
1994c87aefeSPatrick Mooney 		if (SEG_DESC_UNUSABLE(access) || !SEG_DESC_PRESENT(access))
2004c87aefeSPatrick Mooney 			return (-1);
2014c87aefeSPatrick Mooney 	}
2024c87aefeSPatrick Mooney 
2034c87aefeSPatrick Mooney 	if (limit < SEL_LIMIT(sel))
2044c87aefeSPatrick Mooney 		return (-1);
2054c87aefeSPatrick Mooney 	else
2064c87aefeSPatrick Mooney 		return (0);
2074c87aefeSPatrick Mooney }
2084c87aefeSPatrick Mooney 
2094c87aefeSPatrick Mooney /*
2104c87aefeSPatrick Mooney  * Read/write the segment descriptor 'desc' into the GDT/LDT slot referenced
2114c87aefeSPatrick Mooney  * by the selector 'sel'.
2124c87aefeSPatrick Mooney  *
2134c87aefeSPatrick Mooney  * Returns 0 on success.
2144c87aefeSPatrick Mooney  * Returns 1 if an exception was injected into the guest.
2154c87aefeSPatrick Mooney  * Returns -1 otherwise.
2164c87aefeSPatrick Mooney  */
2174c87aefeSPatrick Mooney static int
desc_table_rw(struct vmctx * ctx,int vcpu,struct vm_guest_paging * paging,uint16_t sel,struct user_segment_descriptor * desc,bool doread,int * faultptr)2184c87aefeSPatrick Mooney desc_table_rw(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
2194c87aefeSPatrick Mooney     uint16_t sel, struct user_segment_descriptor *desc, bool doread,
2204c87aefeSPatrick Mooney     int *faultptr)
2214c87aefeSPatrick Mooney {
2224c87aefeSPatrick Mooney 	struct iovec iov[2];
2234c87aefeSPatrick Mooney 	uint64_t base;
2244c87aefeSPatrick Mooney 	uint32_t limit, access;
2254c87aefeSPatrick Mooney 	int error, reg;
2264c87aefeSPatrick Mooney 
2274c87aefeSPatrick Mooney 	reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR;
2284c87aefeSPatrick Mooney 	error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access);
2294c87aefeSPatrick Mooney 	assert(error == 0);
2304c87aefeSPatrick Mooney 	assert(limit >= SEL_LIMIT(sel));
2314c87aefeSPatrick Mooney 
2324c87aefeSPatrick Mooney 	error = vm_copy_setup(ctx, vcpu, paging, base + SEL_START(sel),
2334c87aefeSPatrick Mooney 	    sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov),
2344c87aefeSPatrick Mooney 	    faultptr);
2354c87aefeSPatrick Mooney 	if (error || *faultptr)
2364c87aefeSPatrick Mooney 		return (error);
2374c87aefeSPatrick Mooney 
2384c87aefeSPatrick Mooney 	if (doread)
2394c87aefeSPatrick Mooney 		vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc));
2404c87aefeSPatrick Mooney 	else
2414c87aefeSPatrick Mooney 		vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc));
2424c87aefeSPatrick Mooney 	return (0);
2434c87aefeSPatrick Mooney }
2444c87aefeSPatrick Mooney 
2454c87aefeSPatrick Mooney static int
desc_table_read(struct vmctx * ctx,int vcpu,struct vm_guest_paging * paging,uint16_t sel,struct user_segment_descriptor * desc,int * faultptr)2464c87aefeSPatrick Mooney desc_table_read(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
2474c87aefeSPatrick Mooney     uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
2484c87aefeSPatrick Mooney {
2494c87aefeSPatrick Mooney 	return (desc_table_rw(ctx, vcpu, paging, sel, desc, true, faultptr));
2504c87aefeSPatrick Mooney }
2514c87aefeSPatrick Mooney 
2524c87aefeSPatrick Mooney static int
desc_table_write(struct vmctx * ctx,int vcpu,struct vm_guest_paging * paging,uint16_t sel,struct user_segment_descriptor * desc,int * faultptr)2534c87aefeSPatrick Mooney desc_table_write(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
2544c87aefeSPatrick Mooney     uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
2554c87aefeSPatrick Mooney {
2564c87aefeSPatrick Mooney 	return (desc_table_rw(ctx, vcpu, paging, sel, desc, false, faultptr));
2574c87aefeSPatrick Mooney }
2584c87aefeSPatrick Mooney 
2594c87aefeSPatrick Mooney /*
2604c87aefeSPatrick Mooney  * Read the TSS descriptor referenced by 'sel' into 'desc'.
2614c87aefeSPatrick Mooney  *
2624c87aefeSPatrick Mooney  * Returns 0 on success.
2634c87aefeSPatrick Mooney  * Returns 1 if an exception was injected into the guest.
2644c87aefeSPatrick Mooney  * Returns -1 otherwise.
2654c87aefeSPatrick Mooney  */
2664c87aefeSPatrick Mooney static int
read_tss_descriptor(struct vmctx * ctx,int vcpu,struct vm_task_switch * ts,uint16_t sel,struct user_segment_descriptor * desc,int * faultptr)2674c87aefeSPatrick Mooney read_tss_descriptor(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
2684c87aefeSPatrick Mooney     uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
2694c87aefeSPatrick Mooney {
2704c87aefeSPatrick Mooney 	struct vm_guest_paging sup_paging;
2714c87aefeSPatrick Mooney 	int error;
2724c87aefeSPatrick Mooney 
2734c87aefeSPatrick Mooney 	assert(!ISLDT(sel));
2744c87aefeSPatrick Mooney 	assert(IDXSEL(sel) != 0);
2754c87aefeSPatrick Mooney 
2764c87aefeSPatrick Mooney 	/* Fetch the new TSS descriptor */
2774c87aefeSPatrick Mooney 	if (desc_table_limit_check(ctx, vcpu, sel)) {
2784c87aefeSPatrick Mooney 		if (ts->reason == TSR_IRET)
2794c87aefeSPatrick Mooney 			sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
2804c87aefeSPatrick Mooney 		else
2814c87aefeSPatrick Mooney 			sel_exception(ctx, vcpu, IDT_GP, sel, ts->ext);
2824c87aefeSPatrick Mooney 		return (1);
2834c87aefeSPatrick Mooney 	}
2844c87aefeSPatrick Mooney 
2854c87aefeSPatrick Mooney 	sup_paging = ts->paging;
2864c87aefeSPatrick Mooney 	sup_paging.cpl = 0;		/* implicit supervisor mode */
2874c87aefeSPatrick Mooney 	error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc, faultptr);
2884c87aefeSPatrick Mooney 	return (error);
2894c87aefeSPatrick Mooney }
2904c87aefeSPatrick Mooney 
2914c87aefeSPatrick Mooney static bool
code_desc(int sd_type)2924c87aefeSPatrick Mooney code_desc(int sd_type)
2934c87aefeSPatrick Mooney {
2944c87aefeSPatrick Mooney 	/* code descriptor */
2954c87aefeSPatrick Mooney 	return ((sd_type & 0x18) == 0x18);
2964c87aefeSPatrick Mooney }
2974c87aefeSPatrick Mooney 
2984c87aefeSPatrick Mooney static bool
stack_desc(int sd_type)2994c87aefeSPatrick Mooney stack_desc(int sd_type)
3004c87aefeSPatrick Mooney {
3014c87aefeSPatrick Mooney 	/* writable data descriptor */
3024c87aefeSPatrick Mooney 	return ((sd_type & 0x1A) == 0x12);
3034c87aefeSPatrick Mooney }
3044c87aefeSPatrick Mooney 
3054c87aefeSPatrick Mooney static bool
data_desc(int sd_type)3064c87aefeSPatrick Mooney data_desc(int sd_type)
3074c87aefeSPatrick Mooney {
3084c87aefeSPatrick Mooney 	/* data descriptor or a readable code descriptor */
3094c87aefeSPatrick Mooney 	return ((sd_type & 0x18) == 0x10 || (sd_type & 0x1A) == 0x1A);
3104c87aefeSPatrick Mooney }
3114c87aefeSPatrick Mooney 
3124c87aefeSPatrick Mooney static bool
ldt_desc(int sd_type)3134c87aefeSPatrick Mooney ldt_desc(int sd_type)
3144c87aefeSPatrick Mooney {
3154c87aefeSPatrick Mooney 
3164c87aefeSPatrick Mooney 	return (sd_type == SDT_SYSLDT);
3174c87aefeSPatrick Mooney }
3184c87aefeSPatrick Mooney 
3194c87aefeSPatrick Mooney /*
3204c87aefeSPatrick Mooney  * Validate the descriptor 'seg_desc' associated with 'segment'.
3214c87aefeSPatrick Mooney  */
3224c87aefeSPatrick Mooney static int
validate_seg_desc(struct vmctx * ctx,int vcpu,struct vm_task_switch * ts,int segment,struct seg_desc * seg_desc,int * faultptr)3234c87aefeSPatrick Mooney validate_seg_desc(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
3244c87aefeSPatrick Mooney     int segment, struct seg_desc *seg_desc, int *faultptr)
3254c87aefeSPatrick Mooney {
3264c87aefeSPatrick Mooney 	struct vm_guest_paging sup_paging;
3274c87aefeSPatrick Mooney 	struct user_segment_descriptor usd;
3284c87aefeSPatrick Mooney 	int error, idtvec;
3294c87aefeSPatrick Mooney 	int cpl, dpl, rpl;
3304c87aefeSPatrick Mooney 	uint16_t sel, cs;
3314c87aefeSPatrick Mooney 	bool ldtseg, codeseg, stackseg, dataseg, conforming;
3324c87aefeSPatrick Mooney 
3334c87aefeSPatrick Mooney 	ldtseg = codeseg = stackseg = dataseg = false;
3344c87aefeSPatrick Mooney 	switch (segment) {
3354c87aefeSPatrick Mooney 	case VM_REG_GUEST_LDTR:
3364c87aefeSPatrick Mooney 		ldtseg = true;
3374c87aefeSPatrick Mooney 		break;
3384c87aefeSPatrick Mooney 	case VM_REG_GUEST_CS:
3394c87aefeSPatrick Mooney 		codeseg = true;
3404c87aefeSPatrick Mooney 		break;
3414c87aefeSPatrick Mooney 	case VM_REG_GUEST_SS:
3424c87aefeSPatrick Mooney 		stackseg = true;
3434c87aefeSPatrick Mooney 		break;
3444c87aefeSPatrick Mooney 	case VM_REG_GUEST_DS:
3454c87aefeSPatrick Mooney 	case VM_REG_GUEST_ES:
3464c87aefeSPatrick Mooney 	case VM_REG_GUEST_FS:
3474c87aefeSPatrick Mooney 	case VM_REG_GUEST_GS:
3484c87aefeSPatrick Mooney 		dataseg = true;
3494c87aefeSPatrick Mooney 		break;
3504c87aefeSPatrick Mooney 	default:
3514c87aefeSPatrick Mooney 		assert(0);
3524c87aefeSPatrick Mooney 	}
3534c87aefeSPatrick Mooney 
3544c87aefeSPatrick Mooney 	/* Get the segment selector */
3554c87aefeSPatrick Mooney 	sel = GETREG(ctx, vcpu, segment);
3564c87aefeSPatrick Mooney 
3574c87aefeSPatrick Mooney 	/* LDT selector must point into the GDT */
3584c87aefeSPatrick Mooney 	if (ldtseg && ISLDT(sel)) {
3594c87aefeSPatrick Mooney 		sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
3604c87aefeSPatrick Mooney 		return (1);
3614c87aefeSPatrick Mooney 	}
3624c87aefeSPatrick Mooney 
3634c87aefeSPatrick Mooney 	/* Descriptor table limit check */
3644c87aefeSPatrick Mooney 	if (desc_table_limit_check(ctx, vcpu, sel)) {
3654c87aefeSPatrick Mooney 		sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
3664c87aefeSPatrick Mooney 		return (1);
3674c87aefeSPatrick Mooney 	}
3684c87aefeSPatrick Mooney 
3694c87aefeSPatrick Mooney 	/* NULL selector */
3704c87aefeSPatrick Mooney 	if (IDXSEL(sel) == 0) {
3714c87aefeSPatrick Mooney 		/* Code and stack segment selectors cannot be NULL */
3724c87aefeSPatrick Mooney 		if (codeseg || stackseg) {
3734c87aefeSPatrick Mooney 			sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
3744c87aefeSPatrick Mooney 			return (1);
3754c87aefeSPatrick Mooney 		}
3764c87aefeSPatrick Mooney 		seg_desc->base = 0;
3774c87aefeSPatrick Mooney 		seg_desc->limit = 0;
3784c87aefeSPatrick Mooney 		seg_desc->access = 0x10000;	/* unusable */
3794c87aefeSPatrick Mooney 		return (0);
3804c87aefeSPatrick Mooney 	}
3814c87aefeSPatrick Mooney 
3824c87aefeSPatrick Mooney 	/* Read the descriptor from the GDT/LDT */
3834c87aefeSPatrick Mooney 	sup_paging = ts->paging;
3844c87aefeSPatrick Mooney 	sup_paging.cpl = 0;	/* implicit supervisor mode */
3854c87aefeSPatrick Mooney 	error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd, faultptr);
3864c87aefeSPatrick Mooney 	if (error || *faultptr)
3874c87aefeSPatrick Mooney 		return (error);
3884c87aefeSPatrick Mooney 
3894c87aefeSPatrick Mooney 	/* Verify that the descriptor type is compatible with the segment */
3904c87aefeSPatrick Mooney 	if ((ldtseg && !ldt_desc(usd.sd_type)) ||
3914c87aefeSPatrick Mooney 	    (codeseg && !code_desc(usd.sd_type)) ||
3924c87aefeSPatrick Mooney 	    (dataseg && !data_desc(usd.sd_type)) ||
3934c87aefeSPatrick Mooney 	    (stackseg && !stack_desc(usd.sd_type))) {
3944c87aefeSPatrick Mooney 		sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
3954c87aefeSPatrick Mooney 		return (1);
3964c87aefeSPatrick Mooney 	}
3974c87aefeSPatrick Mooney 
3984c87aefeSPatrick Mooney 	/* Segment must be marked present */
3994c87aefeSPatrick Mooney 	if (!usd.sd_p) {
4004c87aefeSPatrick Mooney 		if (ldtseg)
4014c87aefeSPatrick Mooney 			idtvec = IDT_TS;
4024c87aefeSPatrick Mooney 		else if (stackseg)
4034c87aefeSPatrick Mooney 			idtvec = IDT_SS;
4044c87aefeSPatrick Mooney 		else
4054c87aefeSPatrick Mooney 			idtvec = IDT_NP;
4064c87aefeSPatrick Mooney 		sel_exception(ctx, vcpu, idtvec, sel, ts->ext);
4074c87aefeSPatrick Mooney 		return (1);
4084c87aefeSPatrick Mooney 	}
4094c87aefeSPatrick Mooney 
4104c87aefeSPatrick Mooney 	cs = GETREG(ctx, vcpu, VM_REG_GUEST_CS);
4114c87aefeSPatrick Mooney 	cpl = cs & SEL_RPL_MASK;
4124c87aefeSPatrick Mooney 	rpl = sel & SEL_RPL_MASK;
4134c87aefeSPatrick Mooney 	dpl = usd.sd_dpl;
4144c87aefeSPatrick Mooney 
4154c87aefeSPatrick Mooney 	if (stackseg && (rpl != cpl || dpl != cpl)) {
4164c87aefeSPatrick Mooney 		sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
4174c87aefeSPatrick Mooney 		return (1);
4184c87aefeSPatrick Mooney 	}
4194c87aefeSPatrick Mooney 
4204c87aefeSPatrick Mooney 	if (codeseg) {
4214c87aefeSPatrick Mooney 		conforming = (usd.sd_type & 0x4) ? true : false;
4224c87aefeSPatrick Mooney 		if ((conforming && (cpl < dpl)) ||
4234c87aefeSPatrick Mooney 		    (!conforming && (cpl != dpl))) {
4244c87aefeSPatrick Mooney 			sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
4254c87aefeSPatrick Mooney 			return (1);
4264c87aefeSPatrick Mooney 		}
4274c87aefeSPatrick Mooney 	}
4284c87aefeSPatrick Mooney 
4294c87aefeSPatrick Mooney 	if (dataseg) {
4304c87aefeSPatrick Mooney 		/*
4314c87aefeSPatrick Mooney 		 * A data segment is always non-conforming except when it's
4324c87aefeSPatrick Mooney 		 * descriptor is a readable, conforming code segment.
4334c87aefeSPatrick Mooney 		 */
4344c87aefeSPatrick Mooney 		if (code_desc(usd.sd_type) && (usd.sd_type & 0x4) != 0)
4354c87aefeSPatrick Mooney 			conforming = true;
4364c87aefeSPatrick Mooney 		else
4374c87aefeSPatrick Mooney 			conforming = false;
4384c87aefeSPatrick Mooney 
4394c87aefeSPatrick Mooney 		if (!conforming && (rpl > dpl || cpl > dpl)) {
4404c87aefeSPatrick Mooney 			sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
4414c87aefeSPatrick Mooney 			return (1);
4424c87aefeSPatrick Mooney 		}
4434c87aefeSPatrick Mooney 	}
4444c87aefeSPatrick Mooney 	*seg_desc = usd_to_seg_desc(&usd);
4454c87aefeSPatrick Mooney 	return (0);
4464c87aefeSPatrick Mooney }
4474c87aefeSPatrick Mooney 
4484c87aefeSPatrick Mooney static void
tss32_save(struct vmctx * ctx,int vcpu,struct vm_task_switch * task_switch,uint32_t eip,struct tss32 * tss,struct iovec * iov)4494c87aefeSPatrick Mooney tss32_save(struct vmctx *ctx, int vcpu, struct vm_task_switch *task_switch,
4504c87aefeSPatrick Mooney     uint32_t eip, struct tss32 *tss, struct iovec *iov)
4514c87aefeSPatrick Mooney {
4524c87aefeSPatrick Mooney 
4534c87aefeSPatrick Mooney 	/* General purpose registers */
4544c87aefeSPatrick Mooney 	tss->tss_eax = GETREG(ctx, vcpu, VM_REG_GUEST_RAX);
4554c87aefeSPatrick Mooney 	tss->tss_ecx = GETREG(ctx, vcpu, VM_REG_GUEST_RCX);
4564c87aefeSPatrick Mooney 	tss->tss_edx = GETREG(ctx, vcpu, VM_REG_GUEST_RDX);
4574c87aefeSPatrick Mooney 	tss->tss_ebx = GETREG(ctx, vcpu, VM_REG_GUEST_RBX);
4584c87aefeSPatrick Mooney 	tss->tss_esp = GETREG(ctx, vcpu, VM_REG_GUEST_RSP);
4594c87aefeSPatrick Mooney 	tss->tss_ebp = GETREG(ctx, vcpu, VM_REG_GUEST_RBP);
4604c87aefeSPatrick Mooney 	tss->tss_esi = GETREG(ctx, vcpu, VM_REG_GUEST_RSI);
4614c87aefeSPatrick Mooney 	tss->tss_edi = GETREG(ctx, vcpu, VM_REG_GUEST_RDI);
4624c87aefeSPatrick Mooney 
4634c87aefeSPatrick Mooney 	/* Segment selectors */
4644c87aefeSPatrick Mooney 	tss->tss_es = GETREG(ctx, vcpu, VM_REG_GUEST_ES);
4654c87aefeSPatrick Mooney 	tss->tss_cs = GETREG(ctx, vcpu, VM_REG_GUEST_CS);
4664c87aefeSPatrick Mooney 	tss->tss_ss = GETREG(ctx, vcpu, VM_REG_GUEST_SS);
4674c87aefeSPatrick Mooney 	tss->tss_ds = GETREG(ctx, vcpu, VM_REG_GUEST_DS);
4684c87aefeSPatrick Mooney 	tss->tss_fs = GETREG(ctx, vcpu, VM_REG_GUEST_FS);
4694c87aefeSPatrick Mooney 	tss->tss_gs = GETREG(ctx, vcpu, VM_REG_GUEST_GS);
4704c87aefeSPatrick Mooney 
4714c87aefeSPatrick Mooney 	/* eflags and eip */
4724c87aefeSPatrick Mooney 	tss->tss_eflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS);
4734c87aefeSPatrick Mooney 	if (task_switch->reason == TSR_IRET)
4744c87aefeSPatrick Mooney 		tss->tss_eflags &= ~PSL_NT;
4754c87aefeSPatrick Mooney 	tss->tss_eip = eip;
476