xref: /illumos-gate/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c (revision 32640292339b07090f10ce34d455f98711077343)
17c8c0b82SPatrick Mooney /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
37c8c0b82SPatrick Mooney  *
47c8c0b82SPatrick Mooney  * Copyright (c) 2012 Sandvine, Inc.
57c8c0b82SPatrick Mooney  * Copyright (c) 2012 NetApp, Inc.
67c8c0b82SPatrick Mooney  * All rights reserved.
77c8c0b82SPatrick Mooney  *
87c8c0b82SPatrick Mooney  * Redistribution and use in source and binary forms, with or without
97c8c0b82SPatrick Mooney  * modification, are permitted provided that the following conditions
107c8c0b82SPatrick Mooney  * are met:
117c8c0b82SPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
127c8c0b82SPatrick Mooney  *    notice, this list of conditions and the following disclaimer.
137c8c0b82SPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
147c8c0b82SPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
157c8c0b82SPatrick Mooney  *    documentation and/or other materials provided with the distribution.
167c8c0b82SPatrick Mooney  *
177c8c0b82SPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
187c8c0b82SPatrick Mooney  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
197c8c0b82SPatrick Mooney  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
207c8c0b82SPatrick Mooney  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
217c8c0b82SPatrick Mooney  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
227c8c0b82SPatrick Mooney  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
237c8c0b82SPatrick Mooney  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
247c8c0b82SPatrick Mooney  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
257c8c0b82SPatrick Mooney  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
267c8c0b82SPatrick Mooney  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
277c8c0b82SPatrick Mooney  * SUCH DAMAGE.
287c8c0b82SPatrick Mooney  */
297c8c0b82SPatrick Mooney /*
307c8c0b82SPatrick Mooney  * This file and its contents are supplied under the terms of the
317c8c0b82SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
327c8c0b82SPatrick Mooney  * You may only use this file in accordance with the terms of version
337c8c0b82SPatrick Mooney  * 1.0 of the CDDL.
347c8c0b82SPatrick Mooney  *
357c8c0b82SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
367c8c0b82SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
377c8c0b82SPatrick Mooney  * http://www.illumos.org/license/CDDL.
387c8c0b82SPatrick Mooney  *
397c8c0b82SPatrick Mooney  * Copyright 2015 Pluribus Networks Inc.
407c8c0b82SPatrick Mooney  * Copyright 2018 Joyent, Inc.
417c8c0b82SPatrick Mooney  * Copyright 2021 Oxide Computer Company
427c8c0b82SPatrick Mooney  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
437c8c0b82SPatrick Mooney  */
447c8c0b82SPatrick Mooney 
457c8c0b82SPatrick Mooney #include <sys/cdefs.h>
467c8c0b82SPatrick Mooney 
477c8c0b82SPatrick Mooney #include <sys/param.h>
487c8c0b82SPatrick Mooney #include <sys/pcpu.h>
497c8c0b82SPatrick Mooney #include <sys/systm.h>
507c8c0b82SPatrick Mooney #include <sys/proc.h>
517c8c0b82SPatrick Mooney 
527c8c0b82SPatrick Mooney #include <machine/vmparam.h>
537c8c0b82SPatrick Mooney #include <machine/vmm.h>
547c8c0b82SPatrick Mooney #include <sys/vmm_kernel.h>
557c8c0b82SPatrick Mooney #include <sys/vmm_vm.h>
567c8c0b82SPatrick Mooney 
577c8c0b82SPatrick Mooney #include <sys/vmm_instruction_emul.h>
587c8c0b82SPatrick Mooney #include <x86/psl.h>
597c8c0b82SPatrick Mooney #include <x86/specialreg.h>
607c8c0b82SPatrick Mooney 
617c8c0b82SPatrick Mooney #include "vmm_ioport.h"
627c8c0b82SPatrick Mooney 
637c8c0b82SPatrick Mooney enum vie_status {
647c8c0b82SPatrick Mooney 	VIES_INIT		= (1U << 0),
657c8c0b82SPatrick Mooney 	VIES_MMIO		= (1U << 1),
667c8c0b82SPatrick Mooney 	VIES_INOUT		= (1U << 2),
677c8c0b82SPatrick Mooney 	VIES_OTHER		= (1U << 3),
687c8c0b82SPatrick Mooney 	VIES_INST_FETCH		= (1U << 4),
697c8c0b82SPatrick Mooney 	VIES_INST_DECODE	= (1U << 5),
707c8c0b82SPatrick Mooney 	VIES_PENDING_MMIO	= (1U << 6),
717c8c0b82SPatrick Mooney 	VIES_PENDING_INOUT	= (1U << 7),
727c8c0b82SPatrick Mooney 	VIES_REPEAT		= (1U << 8),
737c8c0b82SPatrick Mooney 	VIES_USER_FALLBACK	= (1U << 9),
747c8c0b82SPatrick Mooney 	VIES_COMPLETE		= (1U << 10),
757c8c0b82SPatrick Mooney };
767c8c0b82SPatrick Mooney 
777c8c0b82SPatrick Mooney /* State of request to perform emulated access (inout or MMIO) */
787c8c0b82SPatrick Mooney enum vie_req {
797c8c0b82SPatrick Mooney 	VR_NONE,
807c8c0b82SPatrick Mooney 	VR_PENDING,
817c8c0b82SPatrick Mooney 	VR_DONE,
827c8c0b82SPatrick Mooney };
837c8c0b82SPatrick Mooney 
847c8c0b82SPatrick Mooney struct vie_mmio {
857c8c0b82SPatrick Mooney 	uint64_t		data;
867c8c0b82SPatrick Mooney 	uint64_t		gpa;
877c8c0b82SPatrick Mooney 	uint8_t			bytes;
887c8c0b82SPatrick Mooney 	enum vie_req		state;
897c8c0b82SPatrick Mooney };
907c8c0b82SPatrick Mooney 
917c8c0b82SPatrick Mooney struct vie_op {
927c8c0b82SPatrick Mooney 	uint8_t		op_byte;	/* actual opcode byte */
937c8c0b82SPatrick Mooney 	uint8_t		op_type;	/* type of operation (e.g. MOV) */
947c8c0b82SPatrick Mooney 	uint16_t	op_flags;
957c8c0b82SPatrick Mooney };
967c8c0b82SPatrick Mooney 
977c8c0b82SPatrick Mooney #define	VIE_INST_SIZE	15
987c8c0b82SPatrick Mooney struct vie {
997c8c0b82SPatrick Mooney 	uint8_t		inst[VIE_INST_SIZE];	/* instruction bytes */
1007c8c0b82SPatrick Mooney 	uint8_t		num_valid;		/* size of the instruction */
1017c8c0b82SPatrick Mooney 	uint8_t		num_processed;
1027c8c0b82SPatrick Mooney 
1037c8c0b82SPatrick Mooney 	uint8_t		addrsize:4, opsize:4;	/* address and operand sizes */
1047c8c0b82SPatrick Mooney 	uint8_t		rex_w:1,		/* REX prefix */
1057c8c0b82SPatrick Mooney 			rex_r:1,
1067c8c0b82SPatrick Mooney 			rex_x:1,
1077c8c0b82SPatrick Mooney 			rex_b:1,
1087c8c0b82SPatrick Mooney 			rex_present:1,
1097c8c0b82SPatrick Mooney 			repz_present:1,		/* REP/REPE/REPZ prefix */
1107c8c0b82SPatrick Mooney 			repnz_present:1,	/* REPNE/REPNZ prefix */
1117c8c0b82SPatrick Mooney 			opsize_override:1,	/* Operand size override */
1127c8c0b82SPatrick Mooney 			addrsize_override:1,	/* Address size override */
1137c8c0b82SPatrick Mooney 			segment_override:1;	/* Segment override */
1147c8c0b82SPatrick Mooney 
1157c8c0b82SPatrick Mooney 	uint8_t		mod:2,			/* ModRM byte */
1167c8c0b82SPatrick Mooney 			reg:4,
1177c8c0b82SPatrick Mooney 			rm:4;
1187c8c0b82SPatrick Mooney 
1197c8c0b82SPatrick Mooney 	uint8_t		ss:2,			/* SIB byte */
1207c8c0b82SPatrick Mooney 			vex_present:1,		/* VEX prefixed */
1217c8c0b82SPatrick Mooney 			vex_l:1,		/* L bit */
1227c8c0b82SPatrick Mooney 			index:4,		/* SIB byte */
1237c8c0b82SPatrick Mooney 			base:4;			/* SIB byte */
1247c8c0b82SPatrick Mooney 
1257c8c0b82SPatrick Mooney 	uint8_t		disp_bytes;
1267c8c0b82SPatrick Mooney 	uint8_t		imm_bytes;
1277c8c0b82SPatrick Mooney 
1287c8c0b82SPatrick Mooney 	uint8_t		scale;
1297c8c0b82SPatrick Mooney 
1307c8c0b82SPatrick Mooney 	uint8_t		vex_reg:4,	/* vvvv: first source reg specifier */
1317c8c0b82SPatrick Mooney 			vex_pp:2,	/* pp */
1327c8c0b82SPatrick Mooney 			_sparebits:2;
1337c8c0b82SPatrick Mooney 
1347c8c0b82SPatrick Mooney 	uint8_t		_sparebytes[2];
1357c8c0b82SPatrick Mooney 
1367c8c0b82SPatrick Mooney 	int		base_register;		/* VM_REG_GUEST_xyz */
1377c8c0b82SPatrick Mooney 	int		index_register;		/* VM_REG_GUEST_xyz */
1387c8c0b82SPatrick Mooney 	int		segment_register;	/* VM_REG_GUEST_xyz */
1397c8c0b82SPatrick Mooney 
1407c8c0b82SPatrick Mooney 	int64_t		displacement;		/* optional addr displacement */
1417c8c0b82SPatrick Mooney 	int64_t		immediate;		/* optional immediate operand */
1427c8c0b82SPatrick Mooney 
1437c8c0b82SPatrick Mooney 	struct vie_op	op;			/* opcode description */
1447c8c0b82SPatrick Mooney 
1457c8c0b82SPatrick Mooney 	enum vie_status	status;
1467c8c0b82SPatrick Mooney 
1477c8c0b82SPatrick Mooney 	struct vm_guest_paging paging;		/* guest paging state */
1487c8c0b82SPatrick Mooney 
1497c8c0b82SPatrick Mooney 	uint64_t	mmio_gpa;		/* faulting GPA */
1507c8c0b82SPatrick Mooney 	struct vie_mmio	mmio_req_read;
1517c8c0b82SPatrick Mooney 	struct vie_mmio	mmio_req_write;
1527c8c0b82SPatrick Mooney 
1537c8c0b82SPatrick Mooney 	struct vm_inout	inout;			/* active in/out op */
1547c8c0b82SPatrick Mooney 	enum vie_req	inout_req_state;
1557c8c0b82SPatrick Mooney 	uint32_t	inout_req_val;		/* value from userspace */
1567c8c0b82SPatrick Mooney };
1577c8c0b82SPatrick Mooney 
1587c8c0b82SPatrick Mooney 
1597c8c0b82SPatrick Mooney /* struct vie_op.op_type */
1607c8c0b82SPatrick Mooney enum {
1617c8c0b82SPatrick Mooney 	VIE_OP_TYPE_NONE = 0,
1627c8c0b82SPatrick Mooney 	VIE_OP_TYPE_MOV,
1637c8c0b82SPatrick Mooney 	VIE_OP_TYPE_MOVSX,
1647c8c0b82SPatrick Mooney 	VIE_OP_TYPE_MOVZX,
1657c8c0b82SPatrick Mooney 	VIE_OP_TYPE_MOV_CR,
1667c8c0b82SPatrick Mooney 	VIE_OP_TYPE_AND,
1677c8c0b82SPatrick Mooney 	VIE_OP_TYPE_OR,
1687c8c0b82SPatrick Mooney 	VIE_OP_TYPE_SUB,
1697c8c0b82SPatrick Mooney 	VIE_OP_TYPE_TWO_BYTE,
1707c8c0b82SPatrick Mooney 	VIE_OP_TYPE_PUSH,
1717c8c0b82SPatrick Mooney 	VIE_OP_TYPE_CMP,
1727c8c0b82SPatrick Mooney 	VIE_OP_TYPE_POP,
1737c8c0b82SPatrick Mooney 	VIE_OP_TYPE_MOVS,
1747c8c0b82SPatrick Mooney 	VIE_OP_TYPE_GROUP1,
1757c8c0b82SPatrick Mooney 	VIE_OP_TYPE_STOS,
1767c8c0b82SPatrick Mooney 	VIE_OP_TYPE_BITTEST,
1777c8c0b82SPatrick Mooney 	VIE_OP_TYPE_TWOB_GRP15,
1787c8c0b82SPatrick Mooney 	VIE_OP_TYPE_ADD,
1797c8c0b82SPatrick Mooney 	VIE_OP_TYPE_TEST,
1807c8c0b82SPatrick Mooney 	VIE_OP_TYPE_BEXTR,
1817c8c0b82SPatrick Mooney 	VIE_OP_TYPE_CLTS,
1821fde93bfSAndy Fiddaman 	VIE_OP_TYPE_MUL,
1837c8c0b82SPatrick Mooney 	VIE_OP_TYPE_LAST
1847c8c0b82SPatrick Mooney };
1857c8c0b82SPatrick Mooney 
1867c8c0b82SPatrick Mooney /* struct vie_op.op_flags */
1877c8c0b82SPatrick Mooney #define	VIE_OP_F_IMM		(1 << 0)  /* 16/32-bit immediate operand */
1887c8c0b82SPatrick Mooney #define	VIE_OP_F_IMM8		(1 << 1)  /* 8-bit immediate operand */
1897c8c0b82SPatrick Mooney #define	VIE_OP_F_MOFFSET	(1 << 2)  /* 16/32/64-bit immediate moffset */
1907c8c0b82SPatrick Mooney #define	VIE_OP_F_NO_MODRM	(1 << 3)
1917c8c0b82SPatrick Mooney #define	VIE_OP_F_NO_GLA_VERIFICATION	(1 << 4)
1927c8c0b82SPatrick Mooney #define	VIE_OP_F_REG_REG	(1 << 5)  /* special-case for mov-cr */
1937c8c0b82SPatrick Mooney 
1947c8c0b82SPatrick Mooney static const struct vie_op three_byte_opcodes_0f38[256] = {
1957c8c0b82SPatrick Mooney 	[0xF7] = {
1967c8c0b82SPatrick Mooney 		.op_byte = 0xF7,
1977c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_BEXTR,
1987c8c0b82SPatrick Mooney 	},
1997c8c0b82SPatrick Mooney };
2007c8c0b82SPatrick Mooney 
2017c8c0b82SPatrick Mooney static const struct vie_op two_byte_opcodes[256] = {
2027c8c0b82SPatrick Mooney 	[0x06] = {
2037c8c0b82SPatrick Mooney 		.op_byte = 0x06,
2047c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_CLTS,
2057c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
2067c8c0b82SPatrick Mooney 	},
2077c8c0b82SPatrick Mooney 	[0x20] = {
2087c8c0b82SPatrick Mooney 		.op_byte = 0x20,
2097c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV_CR,
2107c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_REG_REG | VIE_OP_F_NO_GLA_VERIFICATION
2117c8c0b82SPatrick Mooney 	},
2127c8c0b82SPatrick Mooney 	[0x22] = {
2137c8c0b82SPatrick Mooney 		.op_byte = 0x22,
2147c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV_CR,
2157c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_REG_REG | VIE_OP_F_NO_GLA_VERIFICATION
2167c8c0b82SPatrick Mooney 	},
2177c8c0b82SPatrick Mooney 	[0xAE] = {
2187c8c0b82SPatrick Mooney 		.op_byte = 0xAE,
2197c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_TWOB_GRP15,
2207c8c0b82SPatrick Mooney 	},
2211fde93bfSAndy Fiddaman 	[0xAF] = {
2221fde93bfSAndy Fiddaman 		.op_byte = 0xAF,
2231fde93bfSAndy Fiddaman 		.op_type = VIE_OP_TYPE_MUL,
2241fde93bfSAndy Fiddaman 	},
2257c8c0b82SPatrick Mooney 	[0xB6] = {
2267c8c0b82SPatrick Mooney 		.op_byte = 0xB6,
2277c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOVZX,
2287c8c0b82SPatrick Mooney 	},
2297c8c0b82SPatrick Mooney 	[0xB7] = {
2307c8c0b82SPatrick Mooney 		.op_byte = 0xB7,
2317c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOVZX,
2327c8c0b82SPatrick Mooney 	},
2337c8c0b82SPatrick Mooney 	[0xBA] = {
2347c8c0b82SPatrick Mooney 		.op_byte = 0xBA,
2357c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_BITTEST,
2367c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
2377c8c0b82SPatrick Mooney 	},
2387c8c0b82SPatrick Mooney 	[0xBE] = {
2397c8c0b82SPatrick Mooney 		.op_byte = 0xBE,
2407c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOVSX,
2417c8c0b82SPatrick Mooney 	},
2427c8c0b82SPatrick Mooney };
2437c8c0b82SPatrick Mooney 
2447c8c0b82SPatrick Mooney static const struct vie_op one_byte_opcodes[256] = {
2457c8c0b82SPatrick Mooney 	[0x03] = {
2467c8c0b82SPatrick Mooney 		.op_byte = 0x03,
2477c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_ADD,
2487c8c0b82SPatrick Mooney 	},
2497c8c0b82SPatrick Mooney 	[0x0F] = {
2507c8c0b82SPatrick Mooney 		.op_byte = 0x0F,
2517c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_TWO_BYTE
2527c8c0b82SPatrick Mooney 	},
2537c8c0b82SPatrick Mooney 	[0x0B] = {
2547c8c0b82SPatrick Mooney 		.op_byte = 0x0B,
2557c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_OR,
2567c8c0b82SPatrick Mooney 	},
2577c8c0b82SPatrick Mooney 	[0x2B] = {
2587c8c0b82SPatrick Mooney 		.op_byte = 0x2B,
2597c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_SUB,
2607c8c0b82SPatrick Mooney 	},
2617c8c0b82SPatrick Mooney 	[0x39] = {
2627c8c0b82SPatrick Mooney 		.op_byte = 0x39,
2637c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_CMP,
2647c8c0b82SPatrick Mooney 	},
2657c8c0b82SPatrick Mooney 	[0x3B] = {
2667c8c0b82SPatrick Mooney 		.op_byte = 0x3B,
2677c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_CMP,
2687c8c0b82SPatrick Mooney 	},
2697c8c0b82SPatrick Mooney 	[0x88] = {
2707c8c0b82SPatrick Mooney 		.op_byte = 0x88,
2717c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2727c8c0b82SPatrick Mooney 	},
2737c8c0b82SPatrick Mooney 	[0x89] = {
2747c8c0b82SPatrick Mooney 		.op_byte = 0x89,
2757c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2767c8c0b82SPatrick Mooney 	},
2777c8c0b82SPatrick Mooney 	[0x8A] = {
2787c8c0b82SPatrick Mooney 		.op_byte = 0x8A,
2797c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2807c8c0b82SPatrick Mooney 	},
2817c8c0b82SPatrick Mooney 	[0x8B] = {
2827c8c0b82SPatrick Mooney 		.op_byte = 0x8B,
2837c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2847c8c0b82SPatrick Mooney 	},
2857c8c0b82SPatrick Mooney 	[0xA1] = {
2867c8c0b82SPatrick Mooney 		.op_byte = 0xA1,
2877c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2887c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_MOFFSET | VIE_OP_F_NO_MODRM,
2897c8c0b82SPatrick Mooney 	},
2907c8c0b82SPatrick Mooney 	[0xA3] = {
2917c8c0b82SPatrick Mooney 		.op_byte = 0xA3,
2927c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
2937c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_MOFFSET | VIE_OP_F_NO_MODRM,
2947c8c0b82SPatrick Mooney 	},
2957c8c0b82SPatrick Mooney 	[0xA4] = {
2967c8c0b82SPatrick Mooney 		.op_byte = 0xA4,
2977c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOVS,
2987c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
2997c8c0b82SPatrick Mooney 	},
3007c8c0b82SPatrick Mooney 	[0xA5] = {
3017c8c0b82SPatrick Mooney 		.op_byte = 0xA5,
3027c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOVS,
3037c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
3047c8c0b82SPatrick Mooney 	},
3057c8c0b82SPatrick Mooney 	[0xAA] = {
3067c8c0b82SPatrick Mooney 		.op_byte = 0xAA,
3077c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_STOS,
3087c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
3097c8c0b82SPatrick Mooney 	},
3107c8c0b82SPatrick Mooney 	[0xAB] = {
3117c8c0b82SPatrick Mooney 		.op_byte = 0xAB,
3127c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_STOS,
3137c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
3147c8c0b82SPatrick Mooney 	},
3157c8c0b82SPatrick Mooney 	[0xC6] = {
3167c8c0b82SPatrick Mooney 		/* XXX Group 11 extended opcode - not just MOV */
3177c8c0b82SPatrick Mooney 		.op_byte = 0xC6,
3187c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
3197c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
3207c8c0b82SPatrick Mooney 	},
3217c8c0b82SPatrick Mooney 	[0xC7] = {
3227c8c0b82SPatrick Mooney 		.op_byte = 0xC7,
3237c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV,
3247c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM,
3257c8c0b82SPatrick Mooney 	},
3267c8c0b82SPatrick Mooney 	[0x23] = {
3277c8c0b82SPatrick Mooney 		.op_byte = 0x23,
3287c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_AND,
3297c8c0b82SPatrick Mooney 	},
3307c8c0b82SPatrick Mooney 	[0x80] = {
3317c8c0b82SPatrick Mooney 		/* Group 1 extended opcode */
3327c8c0b82SPatrick Mooney 		.op_byte = 0x80,
3337c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_GROUP1,
3347c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
3357c8c0b82SPatrick Mooney 	},
3367c8c0b82SPatrick Mooney 	[0x81] = {
3377c8c0b82SPatrick Mooney 		/* Group 1 extended opcode */
3387c8c0b82SPatrick Mooney 		.op_byte = 0x81,
3397c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_GROUP1,
3407c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM,
3417c8c0b82SPatrick Mooney 	},
3427c8c0b82SPatrick Mooney 	[0x83] = {
3437c8c0b82SPatrick Mooney 		/* Group 1 extended opcode */
3447c8c0b82SPatrick Mooney 		.op_byte = 0x83,
3457c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_GROUP1,
3467c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
3477c8c0b82SPatrick Mooney 	},
3487c8c0b82SPatrick Mooney 	[0x8F] = {
3497c8c0b82SPatrick Mooney 		/* XXX Group 1A extended opcode - not just POP */
3507c8c0b82SPatrick Mooney 		.op_byte = 0x8F,
3517c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_POP,
3527c8c0b82SPatrick Mooney 	},
3537c8c0b82SPatrick Mooney 	[0xF6] = {
3547c8c0b82SPatrick Mooney 		/* XXX Group 3 extended opcode - not just TEST */
3557c8c0b82SPatrick Mooney 		.op_byte = 0xF6,
3567c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_TEST,
3577c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
3587c8c0b82SPatrick Mooney 	},
3597c8c0b82SPatrick Mooney 	[0xF7] = {
3607c8c0b82SPatrick Mooney 		/* XXX Group 3 extended opcode - not just TEST */
3617c8c0b82SPatrick Mooney 		.op_byte = 0xF7,
3627c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_TEST,
3637c8c0b82SPatrick Mooney 		.op_flags = VIE_OP_F_IMM,
3647c8c0b82SPatrick Mooney 	},
3657c8c0b82SPatrick Mooney 	[0xFF] = {
3667c8c0b82SPatrick Mooney 		/* XXX Group 5 extended opcode - not just PUSH */
3677c8c0b82SPatrick Mooney 		.op_byte = 0xFF,
3687c8c0b82SPatrick Mooney 		.op_type = VIE_OP_TYPE_PUSH,
3697c8c0b82SPatrick Mooney 	}
3707c8c0b82SPatrick Mooney };
3717c8c0b82SPatrick Mooney 
3727c8c0b82SPatrick Mooney /* struct vie.mod */
3737c8c0b82SPatrick Mooney #define	VIE_MOD_INDIRECT		0
3747c8c0b82SPatrick Mooney #define	VIE_MOD_INDIRECT_DISP8		1
3757c8c0b82SPatrick Mooney #define	VIE_MOD_INDIRECT_DISP32		2
3767c8c0b82SPatrick Mooney #define	VIE_MOD_DIRECT			3
3777c8c0b82SPatrick Mooney 
3787c8c0b82SPatrick Mooney /* struct vie.rm */
3797c8c0b82SPatrick Mooney #define	VIE_RM_SIB			4
3807c8c0b82SPatrick Mooney #define	VIE_RM_DISP32			5
3817c8c0b82SPatrick Mooney 
3827c8c0b82SPatrick Mooney #define	GB				(1024 * 1024 * 1024)
3837c8c0b82SPatrick Mooney 
3847c8c0b82SPatrick Mooney 
3857c8c0b82SPatrick Mooney /*
3867c8c0b82SPatrick Mooney  * Paging defines, previously pulled in from machine/pmap.h
3877c8c0b82SPatrick Mooney  */
3887c8c0b82SPatrick Mooney #define	PG_V	(1 << 0) /* Present */
3897c8c0b82SPatrick Mooney #define	PG_RW	(1 << 1) /* Read/Write */
3907c8c0b82SPatrick Mooney #define	PG_U	(1 << 2) /* User/Supervisor */
3917c8c0b82SPatrick Mooney #define	PG_A	(1 << 5) /* Accessed */
3927c8c0b82SPatrick Mooney #define	PG_M	(1 << 6) /* Dirty */
3937c8c0b82SPatrick Mooney #define	PG_PS	(1 << 7) /* Largepage */
3947c8c0b82SPatrick Mooney 
3957c8c0b82SPatrick Mooney /*
3967c8c0b82SPatrick Mooney  * Paging except defines, previously pulled in from machine/pmap.h
3977c8c0b82SPatrick Mooney  */
3987c8c0b82SPatrick Mooney #define	PGEX_P		(1 << 0) /* Non-present/Protection */
3997c8c0b82SPatrick Mooney #define	PGEX_W		(1 << 1) /* Read/Write */
4007c8c0b82SPatrick Mooney #define	PGEX_U		(1 << 2) /* User/Supervisor */
4017c8c0b82SPatrick Mooney #define	PGEX_RSV	(1 << 3) /* (Non-)Reserved */
4027c8c0b82SPatrick Mooney #define	PGEX_I		(1 << 4) /* Instruction */
4037c8c0b82SPatrick Mooney 
4047c8c0b82SPatrick Mooney 
4057c8c0b82SPatrick Mooney static enum vm_reg_name gpr_map[16] = {
4067c8c0b82SPatrick Mooney 	VM_REG_GUEST_RAX,
4077c8c0b82SPatrick Mooney 	VM_REG_GUEST_RCX,
4087c8c0b82SPatrick Mooney 	VM_REG_GUEST_RDX,
4097c8c0b82SPatrick Mooney 	VM_REG_GUEST_RBX,
4107c8c0b82SPatrick Mooney 	VM_REG_GUEST_RSP,
4117c8c0b82SPatrick Mooney 	VM_REG_GUEST_RBP,
4127c8c0b82SPatrick Mooney 	VM_REG_GUEST_RSI,
4137c8c0b82SPatrick Mooney 	VM_REG_GUEST_RDI,
4147c8c0b82SPatrick Mooney 	VM_REG_GUEST_R8,
4157c8c0b82SPatrick Mooney 	VM_REG_GUEST_R9,
4167c8c0b82SPatrick Mooney 	VM_REG_GUEST_R10,
4177c8c0b82SPatrick Mooney 	VM_REG_GUEST_R11,
4187c8c0b82SPatrick Mooney 	VM_REG_GUEST_R12,
4197c8c0b82SPatrick Mooney 	VM_REG_GUEST_R13,
4207c8c0b82SPatrick Mooney 	VM_REG_GUEST_R14,
4217c8c0b82SPatrick Mooney 	VM_REG_GUEST_R15
4227c8c0b82SPatrick Mooney };
4237c8c0b82SPatrick Mooney 
4241fde93bfSAndy Fiddaman static const char *gpr_name_map[][16] = {
4251fde93bfSAndy Fiddaman 	[1] = {
4261fde93bfSAndy Fiddaman 		"a[hl]", "c[hl]", "d[hl]", "b[hl]", "spl", "bpl", "sil", "dil",
4271fde93bfSAndy Fiddaman 		"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b",
4281fde93bfSAndy Fiddaman 	},
4291fde93bfSAndy Fiddaman 	[2] = {
4301fde93bfSAndy Fiddaman 		"ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
4311fde93bfSAndy Fiddaman 		"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
4321fde93bfSAndy Fiddaman 	},
4331fde93bfSAndy Fiddaman 	[4] = {
4341fde93bfSAndy Fiddaman 		"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
4351fde93bfSAndy Fiddaman 		"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
4361fde93bfSAndy Fiddaman 	},
4371fde93bfSAndy Fiddaman 	[8] = {
4381fde93bfSAndy Fiddaman 		"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
4391fde93bfSAndy Fiddaman 		"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
4401fde93bfSAndy Fiddaman 	},
4411fde93bfSAndy Fiddaman };
4421fde93bfSAndy Fiddaman 
4437c8c0b82SPatrick Mooney static enum vm_reg_name cr_map[16] = {
4447c8c0b82SPatrick Mooney 	VM_REG_GUEST_CR0,
4457c8c0b82SPatrick Mooney 	VM_REG_LAST,
4467c8c0b82SPatrick Mooney 	VM_REG_GUEST_CR2,
4477c8c0b82SPatrick Mooney 	VM_REG_GUEST_CR3,
4487c8c0b82SPatrick Mooney 	VM_REG_GUEST_CR4,
4497c8c0b82SPatrick Mooney 	VM_REG_LAST,
4507c8c0b82SPatrick Mooney 	VM_REG_LAST,
4517c8c0b82SPatrick Mooney 	VM_REG_LAST,
4527c8c0b82SPatrick Mooney 	VM_REG_LAST,
4537c8c0b82SPatrick Mooney 	VM_REG_LAST,
4547c8c0b82SPatrick Mooney 	VM_REG_LAST,
4557c8c0b82SPatrick Mooney 	VM_REG_LAST,
4567c8c0b82SPatrick Mooney 	VM_REG_LAST,
4577c8c0b82SPatrick Mooney 	VM_REG_LAST,
4587c8c0b82SPatrick Mooney 	VM_REG_LAST,
4597c8c0b82SPatrick Mooney 	VM_REG_LAST
4607c8c0b82SPatrick Mooney };
4617c8c0b82SPatrick Mooney 
4627c8c0b82SPatrick Mooney static uint64_t size2mask[] = {
4637c8c0b82SPatrick Mooney 	[1] = 0xff,
4647c8c0b82SPatrick Mooney 	[2] = 0xffff,
4657c8c0b82SPatrick Mooney 	[4] = 0xffffffff,
4667c8c0b82SPatrick Mooney 	[8] = 0xffffffffffffffff,
4677c8c0b82SPatrick Mooney };
4687c8c0b82SPatrick Mooney 
4697c8c0b82SPatrick Mooney 
4707c8c0b82SPatrick Mooney static int vie_mmio_read(struct vie *vie, struct vm *vm, int cpuid,
4717c8c0b82SPatrick Mooney     uint64_t gpa, uint64_t *rval, int bytes);
4727c8c0b82SPatrick Mooney static int vie_mmio_write(struct vie *vie, struct vm *vm, int cpuid,
4737c8c0b82SPatrick Mooney     uint64_t gpa, uint64_t wval, int bytes);
4747c8c0b82SPatrick Mooney static int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg,
4757c8c0b82SPatrick Mooney     struct seg_desc *desc, uint64_t offset, int length, int addrsize,
4767c8c0b82SPatrick Mooney     int prot, uint64_t *gla);
4777c8c0b82SPatrick Mooney static int vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla);
4787c8c0b82SPatrick Mooney static int vie_alignment_check(int cpl, int size, uint64_t cr0, uint64_t rf,
4797c8c0b82SPatrick Mooney     uint64_t gla);
4807c8c0b82SPatrick Mooney static uint64_t vie_size2mask(int size);
4817c8c0b82SPatrick Mooney 
4827c8c0b82SPatrick Mooney struct vie *
vie_alloc()4837c8c0b82SPatrick Mooney vie_alloc()
4847c8c0b82SPatrick Mooney {
4857c8c0b82SPatrick Mooney 	return (kmem_zalloc(sizeof (struct vie), KM_SLEEP));
4867c8c0b82SPatrick Mooney }
4877c8c0b82SPatrick Mooney 
4887c8c0b82SPatrick Mooney void
vie_free(struct vie * vie)4897c8c0b82SPatrick Mooney vie_free(struct vie *vie)
4907c8c0b82SPatrick Mooney {
4917c8c0b82SPatrick Mooney 	kmem_free(vie, sizeof (struct vie));
4927c8c0b82SPatrick Mooney }
4937c8c0b82SPatrick Mooney 
4947c8c0b82SPatrick Mooney enum vm_reg_name
vie_regnum_map(uint8_t regnum)4957c8c0b82SPatrick Mooney vie_regnum_map(uint8_t regnum)
4967c8c0b82SPatrick Mooney {
4977c8c0b82SPatrick Mooney 	VERIFY3U(regnum, <, 16);
4987c8c0b82SPatrick Mooney 	return (gpr_map[regnum]);
4997c8c0b82SPatrick Mooney }
5007c8c0b82SPatrick Mooney 
5011fde93bfSAndy Fiddaman const char *
vie_regnum_name(uint8_t regnum,uint8_t size)5021fde93bfSAndy Fiddaman vie_regnum_name(uint8_t regnum, uint8_t size)
5031fde93bfSAndy Fiddaman {
5041fde93bfSAndy Fiddaman 	VERIFY3U(regnum, <, 16);
5051fde93bfSAndy Fiddaman 	VERIFY(size == 1 || size == 2 || size == 4 || size == 8);
5061fde93bfSAndy Fiddaman 	return (gpr_name_map[size][regnum]);
5071fde93bfSAndy Fiddaman }
5081fde93bfSAndy Fiddaman 
5097c8c0b82SPatrick Mooney static void
vie_calc_bytereg(struct vie * vie,enum vm_reg_name * reg,int * lhbr)5107c8c0b82SPatrick Mooney vie_calc_bytereg(struct vie *vie, enum vm_reg_name *reg, int *lhbr)
5117c8c0b82SPatrick Mooney {
5127c8c0b82SPatrick Mooney 	*lhbr = 0;
5137c8c0b82SPatrick Mooney 	*reg = gpr_map[vie->reg];
5147c8c0b82SPatrick Mooney 
5157c8c0b82SPatrick Mooney 	/*
5167c8c0b82SPatrick Mooney 	 * 64-bit mode imposes limitations on accessing legacy high byte
5177c8c0b82SPatrick Mooney 	 * registers (lhbr).
5187c8c0b82SPatrick Mooney 	 *
5197c8c0b82SPatrick Mooney 	 * The legacy high-byte registers cannot be addressed if the REX
5207c8c0b82SPatrick Mooney 	 * prefix is present. In this case the values 4, 5, 6 and 7 of the
5217c8c0b82SPatrick Mooney 	 * 'ModRM:reg' field address %spl, %bpl, %sil and %dil respectively.
5227c8c0b82SPatrick Mooney 	 *
5237c8c0b82SPatrick Mooney 	 * If the REX prefix is not present then the values 4, 5, 6 and 7
5247c8c0b82SPatrick Mooney 	 * of the 'ModRM:reg' field address the legacy high-byte registers,
5257c8c0b82SPatrick Mooney 	 * %ah, %ch, %dh and %bh respectively.
5267c8c0b82SPatrick Mooney 	 */
5277c8c0b82SPatrick Mooney 	if (!vie->rex_present) {
5287c8c0b82SPatrick Mooney 		if (vie->reg & 0x4) {
5297c8c0b82SPatrick Mooney 			*lhbr = 1;
5307c8c0b82SPatrick Mooney 			*reg = gpr_map[vie->reg & 0x3];
5317c8c0b82SPatrick Mooney 		}
5327c8c0b82SPatrick Mooney 	}
5337c8c0b82SPatrick Mooney }
5347c8c0b82SPatrick Mooney 
5357c8c0b82SPatrick Mooney static int
vie_read_bytereg(struct vie * vie,struct vm * vm,int vcpuid,uint8_t * rval)5367c8c0b82SPatrick Mooney vie_read_bytereg(struct vie *vie, struct vm *vm, int vcpuid, uint8_t *rval)
5377c8c0b82SPatrick Mooney {
5387c8c0b82SPatrick Mooney 	uint64_t val;
5397c8c0b82SPatrick Mooney 	int error, lhbr;
5407c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
5417c8c0b82SPatrick Mooney 
5427c8c0b82SPatrick Mooney 	vie_calc_bytereg(vie, &reg, &lhbr);
5437c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, reg, &val);
5447c8c0b82SPatrick Mooney 
5457c8c0b82SPatrick Mooney 	/*
5467c8c0b82SPatrick Mooney 	 * To obtain the value of a legacy high byte register shift the
5477c8c0b82SPatrick Mooney 	 * base register right by 8 bits (%ah = %rax >> 8).
5487c8c0b82SPatrick Mooney 	 */
5497c8c0b82SPatrick Mooney 	if (lhbr)
5507c8c0b82SPatrick Mooney 		*rval = val >> 8;
5517c8c0b82SPatrick Mooney 	else
5527c8c0b82SPatrick Mooney 		*rval = val;
5537c8c0b82SPatrick Mooney 	return (error);
5547c8c0b82SPatrick Mooney }
5557c8c0b82SPatrick Mooney 
5567c8c0b82SPatrick Mooney static int
vie_write_bytereg(struct vie * vie,struct vm * vm,int vcpuid,uint8_t byte)5577c8c0b82SPatrick Mooney vie_write_bytereg(struct vie *vie, struct vm *vm, int vcpuid, uint8_t byte)
5587c8c0b82SPatrick Mooney {
5597c8c0b82SPatrick Mooney 	uint64_t origval, val, mask;
5607c8c0b82SPatrick Mooney 	int error, lhbr;
5617c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
5627c8c0b82SPatrick Mooney 
5637c8c0b82SPatrick Mooney 	vie_calc_bytereg(vie, &reg, &lhbr);
5647c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, reg, &origval);
5657c8c0b82SPatrick Mooney 	if (error == 0) {
5667c8c0b82SPatrick Mooney 		val = byte;
5677c8c0b82SPatrick Mooney 		mask = 0xff;
5687c8c0b82SPatrick Mooney 		if (lhbr) {
5697c8c0b82SPatrick Mooney 			/*
5707c8c0b82SPatrick Mooney 			 * Shift left by 8 to store 'byte' in a legacy high
5717c8c0b82SPatrick Mooney 			 * byte register.
5727c8c0b82SPatrick Mooney 			 */
5737c8c0b82SPatrick Mooney 			val <<= 8;
5747c8c0b82SPatrick Mooney 			mask <<= 8;
5757c8c0b82SPatrick Mooney 		}
5767c8c0b82SPatrick Mooney 		val |= origval & ~mask;
5777c8c0b82SPatrick Mooney 		error = vm_set_register(vm, vcpuid, reg, val);
5787c8c0b82SPatrick Mooney 	}
5797c8c0b82SPatrick Mooney 	return (error);
5807c8c0b82SPatrick Mooney }
5817c8c0b82SPatrick Mooney 
5827c8c0b82SPatrick Mooney static int
vie_update_register(struct vm * vm,int vcpuid,enum vm_reg_name reg,uint64_t val,int size)5837c8c0b82SPatrick Mooney vie_update_register(struct vm *vm, int vcpuid, enum vm_reg_name reg,
5847c8c0b82SPatrick Mooney     uint64_t val, int size)
5857c8c0b82SPatrick Mooney {
5867c8c0b82SPatrick Mooney 	int error;
5877c8c0b82SPatrick Mooney 	uint64_t origval;
5887c8c0b82SPatrick Mooney 
5897c8c0b82SPatrick Mooney 	switch (size) {
5907c8c0b82SPatrick Mooney 	case 1:
5917c8c0b82SPatrick Mooney 	case 2:
5927c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &origval);
5937c8c0b82SPatrick Mooney 		if (error)
5947c8c0b82SPatrick Mooney 			return (error);
5957c8c0b82SPatrick Mooney 		val &= size2mask[size];
5967c8c0b82SPatrick Mooney 		val |= origval & ~size2mask[size];
5977c8c0b82SPatrick Mooney 		break;
5987c8c0b82SPatrick Mooney 	case 4:
5997c8c0b82SPatrick Mooney 		val &= 0xffffffffUL;
6007c8c0b82SPatrick Mooney 		break;
6017c8c0b82SPatrick Mooney 	case 8:
6027c8c0b82SPatrick Mooney 		break;
6037c8c0b82SPatrick Mooney 	default:
6047c8c0b82SPatrick Mooney 		return (EINVAL);
6057c8c0b82SPatrick Mooney 	}
6067c8c0b82SPatrick Mooney 
6077c8c0b82SPatrick Mooney 	error = vm_set_register(vm, vcpuid, reg, val);
6087c8c0b82SPatrick Mooney 	return (error);
6097c8c0b82SPatrick Mooney }
6107c8c0b82SPatrick Mooney 
6117c8c0b82SPatrick Mooney static int
vie_repeat(struct vie * vie)6127c8c0b82SPatrick Mooney vie_repeat(struct vie *vie)
6137c8c0b82SPatrick Mooney {
6147c8c0b82SPatrick Mooney 	vie->status |= VIES_REPEAT;
6157c8c0b82SPatrick Mooney 
6167c8c0b82SPatrick Mooney 	/*
6177c8c0b82SPatrick Mooney 	 * Clear out any cached operation values so the repeated instruction can
6187c8c0b82SPatrick Mooney 	 * begin without using that stale state.  Other state, such as the
6197c8c0b82SPatrick Mooney 	 * decoding results, are kept around as it will not vary between
6207c8c0b82SPatrick Mooney 	 * iterations of a rep-prefixed instruction.
6217c8c0b82SPatrick Mooney 	 */
6227c8c0b82SPatrick Mooney 	if ((vie->status & VIES_MMIO) != 0) {
6237c8c0b82SPatrick Mooney 		vie->mmio_req_read.state = VR_NONE;
6247c8c0b82SPatrick Mooney 		vie->mmio_req_write.state = VR_NONE;
6257c8c0b82SPatrick Mooney 	} else if ((vie->status & VIES_INOUT) != 0) {
6267c8c0b82SPatrick Mooney 		vie->inout_req_state = VR_NONE;
6277c8c0b82SPatrick Mooney 	} else {
6287c8c0b82SPatrick Mooney 		panic("unexpected emulation state");
6297c8c0b82SPatrick Mooney 	}
6307c8c0b82SPatrick Mooney 
6317c8c0b82SPatrick Mooney 	return (EAGAIN);
6327c8c0b82SPatrick Mooney }
6337c8c0b82SPatrick Mooney 
6347c8c0b82SPatrick Mooney #define	RFLAGS_STATUS_BITS    (PSL_C | PSL_PF | PSL_AF | PSL_Z | PSL_N | PSL_V)
6357c8c0b82SPatrick Mooney 
6367c8c0b82SPatrick Mooney /*
6377c8c0b82SPatrick Mooney  * Return the status flags that would result from doing (x - y).
6387c8c0b82SPatrick Mooney  */
6397c8c0b82SPatrick Mooney /* BEGIN CSTYLED */
6407c8c0b82SPatrick Mooney #define	GETCC(sz)							\
6417c8c0b82SPatrick Mooney static ulong_t								\
6427c8c0b82SPatrick Mooney getcc##sz(uint##sz##_t x, uint##sz##_t y)				\
6437c8c0b82SPatrick Mooney {									\
6447c8c0b82SPatrick Mooney 	ulong_t rflags;							\
6457c8c0b82SPatrick Mooney 									\
6467c8c0b82SPatrick Mooney 	__asm __volatile("sub %2,%1; pushfq; popq %0" :			\
6477c8c0b82SPatrick Mooney 	    "=r" (rflags), "+r" (x) : "m" (y));				\
6487c8c0b82SPatrick Mooney 	return (rflags);						\
6497c8c0b82SPatrick Mooney } struct __hack
6507c8c0b82SPatrick Mooney /* END CSTYLED */
6517c8c0b82SPatrick Mooney 
6527c8c0b82SPatrick Mooney GETCC(8);
6537c8c0b82SPatrick Mooney GETCC(16);
6547c8c0b82SPatrick Mooney GETCC(32);
6557c8c0b82SPatrick Mooney GETCC(64);
6567c8c0b82SPatrick Mooney 
6577c8c0b82SPatrick Mooney static ulong_t
getcc(int opsize,uint64_t x,uint64_t y)6587c8c0b82SPatrick Mooney getcc(int opsize, uint64_t x, uint64_t y)
6597c8c0b82SPatrick Mooney {
6607c8c0b82SPatrick Mooney 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
6617c8c0b82SPatrick Mooney 	    ("getcc: invalid operand size %d", opsize));
6627c8c0b82SPatrick Mooney 
6637c8c0b82SPatrick Mooney 	if (opsize == 1)
6647c8c0b82SPatrick Mooney 		return (getcc8(x, y));
6657c8c0b82SPatrick Mooney 	else if (opsize == 2)
6667c8c0b82SPatrick Mooney 		return (getcc16(x, y));
6677c8c0b82SPatrick Mooney 	else if (opsize == 4)
6687c8c0b82SPatrick Mooney 		return (getcc32(x, y));
6697c8c0b82SPatrick Mooney 	else
6707c8c0b82SPatrick Mooney 		return (getcc64(x, y));
6717c8c0b82SPatrick Mooney }
6727c8c0b82SPatrick Mooney 
6737c8c0b82SPatrick Mooney /*
6747c8c0b82SPatrick Mooney  * Macro creation of functions getaddflags{8,16,32,64}
6757c8c0b82SPatrick Mooney  */
6767c8c0b82SPatrick Mooney /* BEGIN CSTYLED */
6777c8c0b82SPatrick Mooney #define	GETADDFLAGS(sz)							\
6787c8c0b82SPatrick Mooney static ulong_t								\
6797c8c0b82SPatrick Mooney getaddflags##sz(uint##sz##_t x, uint##sz##_t y)				\
6807c8c0b82SPatrick Mooney {									\
6817c8c0b82SPatrick Mooney 	ulong_t rflags;							\
6827c8c0b82SPatrick Mooney 									\
6837c8c0b82SPatrick Mooney 	__asm __volatile("add %2,%1; pushfq; popq %0" :			\
6847c8c0b82SPatrick Mooney 	    "=r" (rflags), "+r" (x) : "m" (y));				\
6857c8c0b82SPatrick Mooney 	return (rflags);						\
6867c8c0b82SPatrick Mooney } struct __hack
6877c8c0b82SPatrick Mooney /* END CSTYLED */
6887c8c0b82SPatrick Mooney 
6897c8c0b82SPatrick Mooney GETADDFLAGS(8);
6907c8c0b82SPatrick Mooney GETADDFLAGS(16);
6917c8c0b82SPatrick Mooney GETADDFLAGS(32);
6927c8c0b82SPatrick Mooney GETADDFLAGS(64);
6937c8c0b82SPatrick Mooney 
6947c8c0b82SPatrick Mooney static ulong_t
getaddflags(int opsize,uint64_t x,uint64_t y)6957c8c0b82SPatrick Mooney getaddflags(int opsize, uint64_t x, uint64_t y)
6967c8c0b82SPatrick Mooney {
6977c8c0b82SPatrick Mooney 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
6987c8c0b82SPatrick Mooney 	    ("getaddflags: invalid operand size %d", opsize));
6997c8c0b82SPatrick Mooney 
7007c8c0b82SPatrick Mooney 	if (opsize == 1)
7017c8c0b82SPatrick Mooney 		return (getaddflags8(x, y));
7027c8c0b82SPatrick Mooney 	else if (opsize == 2)
7037c8c0b82SPatrick Mooney 		return (getaddflags16(x, y));
7047c8c0b82SPatrick Mooney 	else if (opsize == 4)
7057c8c0b82SPatrick Mooney 		return (getaddflags32(x, y));
7067c8c0b82SPatrick Mooney 	else
7077c8c0b82SPatrick Mooney 		return (getaddflags64(x, y));
7087c8c0b82SPatrick Mooney }
7097c8c0b82SPatrick Mooney 
7107c8c0b82SPatrick Mooney /*
7111fde93bfSAndy Fiddaman  * Macro creation of functions getimulflags{16,32,64}
7121fde93bfSAndy Fiddaman  */
7131fde93bfSAndy Fiddaman /* BEGIN CSTYLED */
7141fde93bfSAndy Fiddaman #define	GETIMULFLAGS(sz)						\
7151fde93bfSAndy Fiddaman static ulong_t								\
7161fde93bfSAndy Fiddaman getimulflags##sz(uint##sz##_t x, uint##sz##_t y)			\
7171fde93bfSAndy Fiddaman {									\
7181fde93bfSAndy Fiddaman 	ulong_t rflags;							\
7191fde93bfSAndy Fiddaman 									\
7201fde93bfSAndy Fiddaman 	__asm __volatile("imul %2,%1; pushfq; popq %0" :		\
7211fde93bfSAndy Fiddaman 	    "=r" (rflags), "+r" (x) : "m" (y));				\
7221fde93bfSAndy Fiddaman 	return (rflags);						\
7231fde93bfSAndy Fiddaman } struct __hack
7241fde93bfSAndy Fiddaman /* END CSTYLED */
7251fde93bfSAndy Fiddaman 
7261fde93bfSAndy Fiddaman GETIMULFLAGS(16);
7271fde93bfSAndy Fiddaman GETIMULFLAGS(32);
7281fde93bfSAndy Fiddaman GETIMULFLAGS(64);
7291fde93bfSAndy Fiddaman 
7301fde93bfSAndy Fiddaman static ulong_t
getimulflags(int opsize,uint64_t x,uint64_t y)7311fde93bfSAndy Fiddaman getimulflags(int opsize, uint64_t x, uint64_t y)
7321fde93bfSAndy Fiddaman {
7331fde93bfSAndy Fiddaman 	KASSERT(opsize == 2 || opsize == 4 || opsize == 8,
7341fde93bfSAndy Fiddaman 	    ("getimulflags: invalid operand size %d", opsize));
7351fde93bfSAndy Fiddaman 
7361fde93bfSAndy Fiddaman 	if (opsize == 2)
7371fde93bfSAndy Fiddaman 		return (getimulflags16(x, y));
7381fde93bfSAndy Fiddaman 	else if (opsize == 4)
7391fde93bfSAndy Fiddaman 		return (getimulflags32(x, y));
7401fde93bfSAndy Fiddaman 	else
7411fde93bfSAndy Fiddaman 		return (getimulflags64(x, y));
7421fde93bfSAndy Fiddaman }
7431fde93bfSAndy Fiddaman 
7441fde93bfSAndy Fiddaman /*
7457c8c0b82SPatrick Mooney  * Return the status flags that would result from doing (x & y).
7467c8c0b82SPatrick Mooney  */
7477c8c0b82SPatrick Mooney /* BEGIN CSTYLED */
7487c8c0b82SPatrick Mooney #define	GETANDFLAGS(sz)							\
7497c8c0b82SPatrick Mooney static ulong_t								\
7507c8c0b82SPatrick Mooney getandflags##sz(uint##sz##_t x, uint##sz##_t y)				\
7517c8c0b82SPatrick Mooney {									\
7527c8c0b82SPatrick Mooney 	ulong_t rflags;							\
7537c8c0b82SPatrick Mooney 									\
7547c8c0b82SPatrick Mooney 	__asm __volatile("and %2,%1; pushfq; popq %0" :			\
7557c8c0b82SPatrick Mooney 	    "=r" (rflags), "+r" (x) : "m" (y));				\
7567c8c0b82SPatrick Mooney 	return (rflags);						\
7577c8c0b82SPatrick Mooney } struct __hack
7587c8c0b82SPatrick Mooney /* END CSTYLED */
7597c8c0b82SPatrick Mooney 
7607c8c0b82SPatrick Mooney GETANDFLAGS(8);
7617c8c0b82SPatrick Mooney GETANDFLAGS(16);
7627c8c0b82SPatrick Mooney GETANDFLAGS(32);
7637c8c0b82SPatrick Mooney GETANDFLAGS(64);
7647c8c0b82SPatrick Mooney 
7657c8c0b82SPatrick Mooney static ulong_t
getandflags(int opsize,uint64_t x,uint64_t y)7667c8c0b82SPatrick Mooney getandflags(int opsize, uint64_t x, uint64_t y)
7677c8c0b82SPatrick Mooney {
7687c8c0b82SPatrick Mooney 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
7697c8c0b82SPatrick Mooney 	    ("getandflags: invalid operand size %d", opsize));
7707c8c0b82SPatrick Mooney 
7717c8c0b82SPatrick Mooney 	if (opsize == 1)
7727c8c0b82SPatrick Mooney 		return (getandflags8(x, y));
7737c8c0b82SPatrick Mooney 	else if (opsize == 2)
7747c8c0b82SPatrick Mooney 		return (getandflags16(x, y));
7757c8c0b82SPatrick Mooney 	else if (opsize == 4)
7767c8c0b82SPatrick Mooney 		return (getandflags32(x, y));
7777c8c0b82SPatrick Mooney 	else
7787c8c0b82SPatrick Mooney 		return (getandflags64(x, y));
7797c8c0b82SPatrick Mooney }
7807c8c0b82SPatrick Mooney 
7817c8c0b82SPatrick Mooney static int
vie_emulate_mov_cr(struct vie * vie,struct vm * vm,int vcpuid)7827c8c0b82SPatrick Mooney vie_emulate_mov_cr(struct vie *vie, struct vm *vm, int vcpuid)
7837c8c0b82SPatrick Mooney {
7847c8c0b82SPatrick Mooney 	uint64_t val;
7857c8c0b82SPatrick Mooney 	int err;
7867c8c0b82SPatrick Mooney 	enum vm_reg_name gpr = gpr_map[vie->rm];
7877c8c0b82SPatrick Mooney 	enum vm_reg_name cr = cr_map[vie->reg];
7887c8c0b82SPatrick Mooney 
7897c8c0b82SPatrick Mooney 	uint_t size = 4;
7907c8c0b82SPatrick Mooney 	if (vie->paging.cpu_mode == CPU_MODE_64BIT) {
7917c8c0b82SPatrick Mooney 		size = 8;
7927c8c0b82SPatrick Mooney 	}
7937c8c0b82SPatrick Mooney 
7947c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
7957c8c0b82SPatrick Mooney 	case 0x20:
7967c8c0b82SPatrick Mooney 		/*
7977c8c0b82SPatrick Mooney 		 * MOV control register (ModRM:reg) to reg (ModRM:r/m)
7987c8c0b82SPatrick Mooney 		 * 20/r:	mov r32, CR0-CR7
7997c8c0b82SPatrick Mooney 		 * 20/r:	mov r64, CR0-CR7
8007c8c0b82SPatrick Mooney 		 * REX.R + 20/0:	mov r64, CR8
8017c8c0b82SPatrick Mooney 		 */
8027c8c0b82SPatrick Mooney 		if (vie->paging.cpl != 0) {
8037c8c0b82SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
8047c8c0b82SPatrick Mooney 			vie->num_processed = 0;
8057c8c0b82SPatrick Mooney 			return (0);
8067c8c0b82SPatrick Mooney 		}
8077c8c0b82SPatrick Mooney 		err = vm_get_register(vm, vcpuid, cr, &val);
8087c8c0b82SPatrick Mooney 		if (err != 0) {
8097c8c0b82SPatrick Mooney 			/* #UD for access to non-existent CRs */
8107c8c0b82SPatrick Mooney 			vm_inject_ud(vm, vcpuid);
8117c8c0b82SPatrick Mooney 			vie->num_processed = 0;
8127c8c0b82SPatrick Mooney 			return (0);
8137c8c0b82SPatrick Mooney 		}
8147c8c0b82SPatrick Mooney 		err = vie_update_register(vm, vcpuid, gpr, val, size);
8157c8c0b82SPatrick Mooney 		break;
8167c8c0b82SPatrick Mooney 	case 0x22: {
8177c8c0b82SPatrick Mooney 		/*
8187c8c0b82SPatrick Mooney 		 * MOV reg (ModRM:r/m) to control register (ModRM:reg)
8197c8c0b82SPatrick Mooney 		 * 22/r:	mov CR0-CR7, r32
8207c8c0b82SPatrick Mooney 		 * 22/r:	mov CR0-CR7, r64
8217c8c0b82SPatrick Mooney 		 * REX.R + 22/0:	mov CR8, r64
8227c8c0b82SPatrick Mooney 		 */
8237c8c0b82SPatrick Mooney 		uint64_t old, diff;
8247c8c0b82SPatrick Mooney 
8257c8c0b82SPatrick Mooney 		if (vie->paging.cpl != 0) {
8267c8c0b82SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
8277c8c0b82SPatrick Mooney 			vie->num_processed = 0;
8287c8c0b82SPatrick Mooney 			return (0);
8297c8c0b82SPatrick Mooney 		}
8307c8c0b82SPatrick Mooney 		err = vm_get_register(vm, vcpuid, cr, &old);
8317c8c0b82SPatrick Mooney 		if (err != 0) {
8327c8c0b82SPatrick Mooney 			/* #UD for access to non-existent CRs */
8337c8c0b82SPatrick Mooney 			vm_inject_ud(vm, vcpuid);
8347c8c0b82SPatrick Mooney 			vie->num_processed = 0;
8357c8c0b82SPatrick Mooney 			return (0);
8367c8c0b82SPatrick Mooney 		}
8377c8c0b82SPatrick Mooney 		err = vm_get_register(vm, vcpuid, gpr, &val);
8387c8c0b82SPatrick Mooney 		VERIFY0(err);
8397c8c0b82SPatrick Mooney 		val &= size2mask[size];
8407c8c0b82SPatrick Mooney 		diff = old ^ val;
8417c8c0b82SPatrick Mooney 
8427c8c0b82SPatrick Mooney 		switch (cr) {
8437c8c0b82SPatrick Mooney 		case VM_REG_GUEST_CR0:
8447c8c0b82SPatrick Mooney 			if ((diff & CR0_PG) != 0) {
8457c8c0b82SPatrick Mooney 				uint64_t efer;
8467c8c0b82SPatrick Mooney 
8477c8c0b82SPatrick Mooney 				err = vm_get_register(vm, vcpuid,
8487c8c0b82SPatrick Mooney 				    VM_REG_GUEST_EFER, &efer);
8497c8c0b82SPatrick Mooney 				VERIFY0(err);
8507c8c0b82SPatrick Mooney 
8517c8c0b82SPatrick Mooney 				/* Keep the long-mode state in EFER in sync */
8527c8c0b82SPatrick Mooney 				if ((val & CR0_PG) != 0 &&
8537c8c0b82SPatrick Mooney 				    (efer & EFER_LME) != 0) {
8547c8c0b82SPatrick Mooney 					efer |= EFER_LMA;
8557c8c0b82SPatrick Mooney 				}
8567c8c0b82SPatrick Mooney 				if ((val & CR0_PG) == 0 &&
8577c8c0b82SPatrick Mooney 				    (efer & EFER_LME) != 0) {
8587c8c0b82SPatrick Mooney 					efer &= ~EFER_LMA;
8597c8c0b82SPatrick Mooney 				}
8607c8c0b82SPatrick Mooney 
8617c8c0b82SPatrick Mooney 				err = vm_set_register(vm, vcpuid,
8627c8c0b82SPatrick Mooney 				    VM_REG_GUEST_EFER, efer);
8637c8c0b82SPatrick Mooney 				VERIFY0(err);
8647c8c0b82SPatrick Mooney 			}
8657c8c0b82SPatrick Mooney 			/* TODO: enforce more of the #GP checks */
8667c8c0b82SPatrick Mooney 			err = vm_set_register(vm, vcpuid, cr, val);
8677c8c0b82SPatrick Mooney 			VERIFY0(err);
8687c8c0b82SPatrick Mooney 			break;
8697c8c0b82SPatrick Mooney 		case VM_REG_GUEST_CR2:
8707c8c0b82SPatrick Mooney 		case VM_REG_GUEST_CR3:
8717c8c0b82SPatrick Mooney 		case VM_REG_GUEST_CR4:
8727c8c0b82SPatrick Mooney 			/* TODO: enforce more of the #GP checks */
8737c8c0b82SPatrick Mooney 			err = vm_set_register(vm, vcpuid, cr, val);
8747c8c0b82SPatrick Mooney 			break;
8757c8c0b82SPatrick Mooney 		default:
8767c8c0b82SPatrick Mooney 			/* The cr_map mapping should prevent this */
8777c8c0b82SPatrick Mooney 			panic("invalid cr %d", cr);
8787c8c0b82SPatrick Mooney 		}
8797c8c0b82SPatrick Mooney 		break;
8807c8c0b82SPatrick Mooney 	}
8817c8c0b82SPatrick Mooney 	default:
8827c8c0b82SPatrick Mooney 		return (EINVAL);
8837c8c0b82SPatrick Mooney 	}
8847c8c0b82SPatrick Mooney 	return (err);
8857c8c0b82SPatrick Mooney }
8867c8c0b82SPatrick Mooney 
8877c8c0b82SPatrick Mooney static int
vie_emulate_mov(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)8887c8c0b82SPatrick Mooney vie_emulate_mov(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
8897c8c0b82SPatrick Mooney {
8907c8c0b82SPatrick Mooney 	int error, size;
8917c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
8927c8c0b82SPatrick Mooney 	uint8_t byte;
8937c8c0b82SPatrick Mooney 	uint64_t val;
8947c8c0b82SPatrick Mooney 
8957c8c0b82SPatrick Mooney 	size = vie->opsize;
8967c8c0b82SPatrick Mooney 	error = EINVAL;
8977c8c0b82SPatrick Mooney 
8987c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
8997c8c0b82SPatrick Mooney 	case 0x88:
9007c8c0b82SPatrick Mooney 		/*
9017c8c0b82SPatrick Mooney 		 * MOV byte from reg (ModRM:reg) to mem (ModRM:r/m)
9027c8c0b82SPatrick Mooney 		 * 88/r:	mov r/m8, r8
9037c8c0b82SPatrick Mooney 		 * REX + 88/r:	mov r/m8, r8 (%ah, %ch, %dh, %bh not available)
9047c8c0b82SPatrick Mooney 		 */
9057c8c0b82SPatrick Mooney 		size = 1;	/* override for byte operation */
9067c8c0b82SPatrick Mooney 		error = vie_read_bytereg(vie, vm, vcpuid, &byte);
9077c8c0b82SPatrick Mooney 		if (error == 0) {
9087c8c0b82SPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, byte,
9097c8c0b82SPatrick Mooney 			    size);
9107c8c0b82SPatrick Mooney 		}
9117c8c0b82SPatrick Mooney 		break;
9127c8c0b82SPatrick Mooney 	case 0x89:
9137c8c0b82SPatrick Mooney 		/*
9147c8c0b82SPatrick Mooney 		 * MOV from reg (ModRM:reg) to mem (ModRM:r/m)
9157c8c0b82SPatrick Mooney 		 * 89/r:	mov r/m16, r16
9167c8c0b82SPatrick Mooney 		 * 89/r:	mov r/m32, r32
9177c8c0b82SPatrick Mooney 		 * REX.W + 89/r	mov r/m64, r64
9187c8c0b82SPatrick Mooney 		 */
9197c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
9207c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val);
9217c8c0b82SPatrick Mooney 		if (error == 0) {
9227c8c0b82SPatrick Mooney 			val &= size2mask[size];
9237c8c0b82SPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
9247c8c0b82SPatrick Mooney 		}
9257c8c0b82SPatrick Mooney 		break;
9267c8c0b82SPatrick Mooney 	case 0x8A:
9277c8c0b82SPatrick Mooney 		/*
9287c8c0b82SPatrick Mooney 		 * MOV byte from mem (ModRM:r/m) to reg (ModRM:reg)
9297c8c0b82SPatrick Mooney 		 * 8A/r:	mov r8, r/m8
9307c8c0b82SPatrick Mooney 		 * REX + 8A/r:	mov r8, r/m8
9317c8c0b82SPatrick Mooney 		 */
9327c8c0b82SPatrick Mooney 		size = 1;	/* override for byte operation */
9337c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
9347c8c0b82SPatrick Mooney 		if (error == 0)
9357c8c0b82SPatrick Mooney 			error = vie_write_bytereg(vie, vm, vcpuid, val);
9367c8c0b82SPatrick Mooney 		break;
9377c8c0b82SPatrick Mooney 	case 0x8B:
9387c8c0b82SPatrick Mooney 		/*
9397c8c0b82SPatrick Mooney 		 * MOV from mem (ModRM:r/m) to reg (ModRM:reg)
9407c8c0b82SPatrick Mooney 		 * 8B/r:	mov r16, r/m16
9417c8c0b82SPatrick Mooney 		 * 8B/r:	mov r32, r/m32
9427c8c0b82SPatrick Mooney 		 * REX.W 8B/r:	mov r64, r/m64
9437c8c0b82SPatrick Mooney 		 */
9447c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
9457c8c0b82SPatrick Mooney 		if (error == 0) {
9467c8c0b82SPatrick Mooney 			reg = gpr_map[vie->reg];
9477c8c0b82SPatrick Mooney 			error = vie_update_register(vm, vcpuid, reg, val, size);
9487c8c0b82SPatrick Mooney 		}
9497c8c0b82SPatrick Mooney 		break;
9507c8c0b82SPatrick Mooney 	case 0xA1:
9517c8c0b82SPatrick Mooney 		/*
9527c8c0b82SPatrick Mooney 		 * MOV from seg:moffset to AX/EAX/RAX
9537c8c0b82SPatrick Mooney 		 * A1:		mov AX, moffs16
9547c8c0b82SPatrick Mooney 		 * A1:		mov EAX, moffs32
9557c8c0b82SPatrick Mooney 		 * REX.W + A1:	mov RAX, moffs64
9567c8c0b82SPatrick Mooney 		 */
9577c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
9587c8c0b82SPatrick Mooney 		if (error == 0) {
9597c8c0b82SPatrick Mooney 			reg = VM_REG_GUEST_RAX;
9607c8c0b82SPatrick Mooney 			error = vie_update_register(vm, vcpuid, reg, val, size);
9617c8c0b82SPatrick Mooney 		}
9627c8c0b82SPatrick Mooney 		break;
9637c8c0b82SPatrick Mooney 	case 0xA3:
9647c8c0b82SPatrick Mooney 		/*
9657c8c0b82SPatrick Mooney 		 * MOV from AX/EAX/RAX to seg:moffset
9667c8c0b82SPatrick Mooney 		 * A3:		mov moffs16, AX
9677c8c0b82SPatrick Mooney 		 * A3:		mov moffs32, EAX
9687c8c0b82SPatrick Mooney 		 * REX.W + A3:	mov moffs64, RAX
9697c8c0b82SPatrick Mooney 		 */
9707c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RAX, &val);
9717c8c0b82SPatrick Mooney 		if (error == 0) {
9727c8c0b82SPatrick Mooney 			val &= size2mask[size];
9737c8c0b82SPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
9747c8c0b82SPatrick Mooney 		}
9757c8c0b82SPatrick Mooney 		break;
9767c8c0b82SPatrick Mooney 	case 0xC6:
9777c8c0b82SPatrick Mooney 		/*
9787c8c0b82SPatrick Mooney 		 * MOV from imm8 to mem (ModRM:r/m)
9797c8c0b82SPatrick Mooney 		 * C6/0		mov r/m8, imm8
9807c8c0b82SPatrick Mooney 		 * REX + C6/0	mov r/m8, imm8
9817c8c0b82SPatrick Mooney 		 */
9827c8c0b82SPatrick Mooney 		size = 1;	/* override for byte operation */
9837c8c0b82SPatrick Mooney 		val = vie->immediate;
9847c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
9857c8c0b82SPatrick Mooney 		break;
9867c8c0b82SPatrick Mooney 	case 0xC7:
9877c8c0b82SPatrick Mooney 		/*
9887c8c0b82SPatrick Mooney 		 * MOV from imm16/imm32 to mem (ModRM:r/m)
9897c8c0b82SPatrick Mooney 		 * C7/0		mov r/m16, imm16
9907c8c0b82SPatrick Mooney 		 * C7/0		mov r/m32, imm32
9917c8c0b82SPatrick Mooney 		 * REX.W + C7/0	mov r/m64, imm32 (sign-extended to 64-bits)
9927c8c0b82SPatrick Mooney 		 */
9937c8c0b82SPatrick Mooney 		val = vie->immediate & size2mask[size];
9947c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
9957c8c0b82SPatrick Mooney 		break;
9967c8c0b82SPatrick Mooney 	default:
9977c8c0b82SPatrick Mooney 		break;
9987c8c0b82SPatrick Mooney 	}
9997c8c0b82SPatrick Mooney 
10007c8c0b82SPatrick Mooney 	return (error);
10017c8c0b82SPatrick Mooney }
10027c8c0b82SPatrick Mooney 
10037c8c0b82SPatrick Mooney static int
vie_emulate_movx(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)10047c8c0b82SPatrick Mooney vie_emulate_movx(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
10057c8c0b82SPatrick Mooney {
10067c8c0b82SPatrick Mooney 	int error, size;
10077c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
10087c8c0b82SPatrick Mooney 	uint64_t val;
10097c8c0b82SPatrick Mooney 
10107c8c0b82SPatrick Mooney 	size = vie->opsize;
10117c8c0b82SPatrick Mooney 	error = EINVAL;
10127c8c0b82SPatrick Mooney 
10137c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
10147c8c0b82SPatrick Mooney 	case 0xB6:
10157c8c0b82SPatrick Mooney 		/*
10167c8c0b82SPatrick Mooney 		 * MOV and zero extend byte from mem (ModRM:r/m) to
10177c8c0b82SPatrick Mooney 		 * reg (ModRM:reg).
10187c8c0b82SPatrick Mooney 		 *
10197c8c0b82SPatrick Mooney 		 * 0F B6/r		movzx r16, r/m8
10207c8c0b82SPatrick Mooney 		 * 0F B6/r		movzx r32, r/m8
10217c8c0b82SPatrick Mooney 		 * REX.W + 0F B6/r	movzx r64, r/m8
10227c8c0b82SPatrick Mooney 		 */
10237c8c0b82SPatrick Mooney 
10247c8c0b82SPatrick Mooney 		/* get the first operand */
10257c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 1);
10267c8c0b82SPatrick Mooney 		if (error)
10277c8c0b82SPatrick Mooney 			break;
10287c8c0b82SPatrick Mooney 
10297c8c0b82SPatrick Mooney 		/* get the second operand */
10307c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
10317c8c0b82SPatrick Mooney 
10327c8c0b82SPatrick Mooney 		/* zero-extend byte */
10337c8c0b82SPatrick Mooney 		val = (uint8_t)val;
10347c8c0b82SPatrick Mooney 
10357c8c0b82SPatrick Mooney 		/* write the result */
10367c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, val, size);
10377c8c0b82SPatrick Mooney 		break;
10387c8c0b82SPatrick Mooney 	case 0xB7:
10397c8c0b82SPatrick Mooney 		/*
10407c8c0b82SPatrick Mooney 		 * MOV and zero extend word from mem (ModRM:r/m) to
10417c8c0b82SPatrick Mooney 		 * reg (ModRM:reg).
10427c8c0b82SPatrick Mooney 		 *
10437c8c0b82SPatrick Mooney 		 * 0F B7/r		movzx r32, r/m16
10447c8c0b82SPatrick Mooney 		 * REX.W + 0F B7/r	movzx r64, r/m16
10457c8c0b82SPatrick Mooney 		 */
10467c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 2);
10477c8c0b82SPatrick Mooney 		if (error)
10487c8c0b82SPatrick Mooney 			return (error);
10497c8c0b82SPatrick Mooney 
10507c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
10517c8c0b82SPatrick Mooney 
10527c8c0b82SPatrick Mooney 		/* zero-extend word */
10537c8c0b82SPatrick Mooney 		val = (uint16_t)val;
10547c8c0b82SPatrick Mooney 
10557c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, val, size);
10567c8c0b82SPatrick Mooney 		break;
10577c8c0b82SPatrick Mooney 	case 0xBE:
10587c8c0b82SPatrick Mooney 		/*
10597c8c0b82SPatrick Mooney 		 * MOV and sign extend byte from mem (ModRM:r/m) to
10607c8c0b82SPatrick Mooney 		 * reg (ModRM:reg).
10617c8c0b82SPatrick Mooney 		 *
10627c8c0b82SPatrick Mooney 		 * 0F BE/r		movsx r16, r/m8
10637c8c0b82SPatrick Mooney 		 * 0F BE/r		movsx r32, r/m8
10647c8c0b82SPatrick Mooney 		 * REX.W + 0F BE/r	movsx r64, r/m8
10657c8c0b82SPatrick Mooney 		 */
10667c8c0b82SPatrick Mooney 
10677c8c0b82SPatrick Mooney 		/* get the first operand */
10687c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 1);
10697c8c0b82SPatrick Mooney 		if (error)
10707c8c0b82SPatrick Mooney 			break;
10717c8c0b82SPatrick Mooney 
10727c8c0b82SPatrick Mooney 		/* get the second operand */
10737c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
10747c8c0b82SPatrick Mooney 
10757c8c0b82SPatrick Mooney 		/* sign extend byte */
10767c8c0b82SPatrick Mooney 		val = (int8_t)val;
10777c8c0b82SPatrick Mooney 
10787c8c0b82SPatrick Mooney 		/* write the result */
10797c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, val, size);
10807c8c0b82SPatrick Mooney 		break;
10817c8c0b82SPatrick Mooney 	default:
10827c8c0b82SPatrick Mooney 		break;
10837c8c0b82SPatrick Mooney 	}
10847c8c0b82SPatrick Mooney 	return (error);
10857c8c0b82SPatrick Mooney }
10867c8c0b82SPatrick Mooney 
10877c8c0b82SPatrick Mooney /*
10887c8c0b82SPatrick Mooney  * Helper function to calculate and validate a linear address.
10897c8c0b82SPatrick Mooney  */
10907c8c0b82SPatrick Mooney static int
vie_get_gla(struct vie * vie,struct vm * vm,int vcpuid,int opsize,int addrsize,int prot,enum vm_reg_name seg,enum vm_reg_name gpr,uint64_t * gla)10917c8c0b82SPatrick Mooney vie_get_gla(struct vie *vie, struct vm *vm, int vcpuid, int opsize,
10927c8c0b82SPatrick Mooney     int addrsize, int prot, enum vm_reg_name seg, enum vm_reg_name gpr,
10937c8c0b82SPatrick Mooney     uint64_t *gla)
10947c8c0b82SPatrick Mooney {
10957c8c0b82SPatrick Mooney 	struct seg_desc desc;
10967c8c0b82SPatrick Mooney 	uint64_t cr0, val, rflags;
10977c8c0b82SPatrick Mooney 	int error;
10987c8c0b82SPatrick Mooney 	struct vm_guest_paging *paging;
10997c8c0b82SPatrick Mooney 
11007c8c0b82SPatrick Mooney 	paging = &vie->paging;
11017c8c0b82SPatrick Mooney 
11027c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &cr0);
11037c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting cr0", __func__, error));
11047c8c0b82SPatrick Mooney 
11057c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
11067c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
11077c8c0b82SPatrick Mooney 
11087c8c0b82SPatrick Mooney 	error = vm_get_seg_desc(vm, vcpuid, seg, &desc);
11097c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting segment descriptor %d",
11107c8c0b82SPatrick Mooney 	    __func__, error, seg));
11117c8c0b82SPatrick Mooney 
11127c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, gpr, &val);
11137c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting register %d", __func__,
11147c8c0b82SPatrick Mooney 	    error, gpr));
11157c8c0b82SPatrick Mooney 
11167c8c0b82SPatrick Mooney 	if (vie_calculate_gla(paging->cpu_mode, seg, &desc, val, opsize,
11177c8c0b82SPatrick Mooney 	    addrsize, prot, gla)) {
11187c8c0b82SPatrick Mooney 		if (seg == VM_REG_GUEST_SS)
11197c8c0b82SPatrick Mooney 			vm_inject_ss(vm, vcpuid, 0);
11207c8c0b82SPatrick Mooney 		else
11217c8c0b82SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
11227c8c0b82SPatrick Mooney 		return (-1);
11237c8c0b82SPatrick Mooney 	}
11247c8c0b82SPatrick Mooney 
11257c8c0b82SPatrick Mooney 	if (vie_canonical_check(paging->cpu_mode, *gla)) {
11267c8c0b82SPatrick Mooney 		if (seg == VM_REG_GUEST_SS)
11277c8c0b82SPatrick Mooney 			vm_inject_ss(vm, vcpuid, 0);
11287c8c0b82SPatrick Mooney 		else
11297c8c0b82SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
11307c8c0b82SPatrick Mooney 		return (-1);
11317c8c0b82SPatrick Mooney 	}
11327c8c0b82SPatrick Mooney 
11337c8c0b82SPatrick Mooney 	if (vie_alignment_check(paging->cpl, opsize, cr0, rflags, *gla)) {
11347c8c0b82SPatrick Mooney 		vm_inject_ac(vm, vcpuid, 0);
11357c8c0b82SPatrick Mooney 		return (-1);
11367c8c0b82SPatrick Mooney 	}
11377c8c0b82SPatrick Mooney 
11387c8c0b82SPatrick Mooney 	return (0);
11397c8c0b82SPatrick Mooney }
11407c8c0b82SPatrick Mooney 
11417c8c0b82SPatrick Mooney static int
vie_emulate_movs(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)11427c8c0b82SPatrick Mooney vie_emulate_movs(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
11437c8c0b82SPatrick Mooney {
11447c8c0b82SPatrick Mooney 	struct vm_copyinfo copyinfo[2];
11457c8c0b82SPatrick Mooney 	uint64_t dstaddr, srcaddr, dstgpa, srcgpa, val;
11467c8c0b82SPatrick Mooney 	uint64_t rcx, rdi, rsi, rflags;
11477c8c0b82SPatrick Mooney 	int error, fault, opsize, seg, repeat;
11487c8c0b82SPatrick Mooney 	struct vm_guest_paging *paging;
11497c8c0b82SPatrick Mooney 
11507c8c0b82SPatrick Mooney 	opsize = (vie->op.op_byte == 0xA4) ? 1 : vie->opsize;
11517c8c0b82SPatrick Mooney 	val = 0;
11527c8c0b82SPatrick Mooney 	error = 0;
11537c8c0b82SPatrick Mooney 	paging = &vie->paging;
11547c8c0b82SPatrick Mooney 
11557c8c0b82SPatrick Mooney 	/*
11567c8c0b82SPatrick Mooney 	 * XXX although the MOVS instruction is only supposed to be used with
11577c8c0b82SPatrick Mooney 	 * the "rep" prefix some guests like FreeBSD will use "repnz" instead.
11587c8c0b82SPatrick Mooney 	 *
11597c8c0b82SPatrick Mooney 	 * Empirically the "repnz" prefix has identical behavior to "rep"
11607c8c0b82SPatrick Mooney 	 * and the zero flag does not make a difference.
11617c8c0b82SPatrick Mooney 	 */
11627c8c0b82SPatrick Mooney 	repeat = vie->repz_present | vie->repnz_present;
11637c8c0b82SPatrick Mooney 
11647c8c0b82SPatrick Mooney 	if (repeat) {
11657c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &rcx);
11667c8c0b82SPatrick Mooney 		KASSERT(!error, ("%s: error %d getting rcx", __func__, error));
11677c8c0b82SPatrick Mooney 
11687c8c0b82SPatrick Mooney 		/*
11697c8c0b82SPatrick Mooney 		 * The count register is %rcx, %ecx or %cx depending on the
11707c8c0b82SPatrick Mooney 		 * address size of the instruction.
11717c8c0b82SPatrick Mooney 		 */
11727c8c0b82SPatrick Mooney 		if ((rcx & vie_size2mask(vie->addrsize)) == 0) {
11737c8c0b82SPatrick Mooney 			error = 0;
11747c8c0b82SPatrick Mooney 			goto done;
11757c8c0b82SPatrick Mooney 		}
11767c8c0b82SPatrick Mooney 	}
11777c8c0b82SPatrick Mooney 
11787c8c0b82SPatrick Mooney 	/*
11797c8c0b82SPatrick Mooney 	 *	Source		Destination	Comments
11807c8c0b82SPatrick Mooney 	 *	--------------------------------------------
11817c8c0b82SPatrick Mooney 	 * (1)  memory		memory		n/a
11827c8c0b82SPatrick Mooney 	 * (2)  memory		mmio		emulated
11837c8c0b82SPatrick Mooney 	 * (3)  mmio		memory		emulated
11847c8c0b82SPatrick Mooney 	 * (4)  mmio		mmio		emulated
11857c8c0b82SPatrick Mooney 	 *
11867c8c0b82SPatrick Mooney 	 * At this point we don't have sufficient information to distinguish
11877c8c0b82SPatrick Mooney 	 * between (2), (3) and (4). We use 'vm_copy_setup()' to tease this
11887c8c0b82SPatrick Mooney 	 * out because it will succeed only when operating on regular memory.
11897c8c0b82SPatrick Mooney 	 *
11907c8c0b82SPatrick Mooney 	 * XXX the emulation doesn't properly handle the case where 'gpa'
11917c8c0b82SPatrick Mooney 	 * is straddling the boundary between the normal memory and MMIO.
11927c8c0b82SPatrick Mooney 	 */
11937c8c0b82SPatrick Mooney 
11947c8c0b82SPatrick Mooney 	seg = vie->segment_override ? vie->segment_register : VM_REG_GUEST_DS;
11957c8c0b82SPatrick Mooney 	if (vie_get_gla(vie, vm, vcpuid, opsize, vie->addrsize, PROT_READ, seg,
11967c8c0b82SPatrick Mooney 	    VM_REG_GUEST_RSI, &srcaddr) != 0) {
11977c8c0b82SPatrick Mooney 		goto done;
11987c8c0b82SPatrick Mooney 	}
11997c8c0b82SPatrick Mooney 
12007c8c0b82SPatrick Mooney 	error = vm_copy_setup(vm, vcpuid, paging, srcaddr, opsize, PROT_READ,
12017c8c0b82SPatrick Mooney 	    copyinfo, nitems(copyinfo), &fault);
12027c8c0b82SPatrick Mooney 	if (error == 0) {
12037c8c0b82SPatrick Mooney 		if (fault)
12047c8c0b82SPatrick Mooney 			goto done;	/* Resume guest to handle fault */
12057c8c0b82SPatrick Mooney 
12067c8c0b82SPatrick Mooney 		/*
12077c8c0b82SPatrick Mooney 		 * case (2): read from system memory and write to mmio.
12087c8c0b82SPatrick Mooney 		 */
12097c8c0b82SPatrick Mooney 		vm_copyin(vm, vcpuid, copyinfo, &val, opsize);
12107c8c0b82SPatrick Mooney 		vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
12117c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, opsize);
12127c8c0b82SPatrick Mooney 		if (error)
12137c8c0b82SPatrick Mooney 			goto done;
12147c8c0b82SPatrick Mooney 	} else {
12157c8c0b82SPatrick Mooney 		/*
12167c8c0b82SPatrick Mooney 		 * 'vm_copy_setup()' is expected to fail for cases (3) and (4)
12177c8c0b82SPatrick Mooney 		 * if 'srcaddr' is in the mmio space.
12187c8c0b82SPatrick Mooney 		 */
12197c8c0b82SPatrick Mooney 
12207c8c0b82SPatrick Mooney 		if (vie_get_gla(vie, vm, vcpuid, opsize, vie->addrsize,
12217c8c0b82SPatrick Mooney 		    PROT_WRITE, VM_REG_GUEST_ES, VM_REG_GUEST_RDI,
12227c8c0b82SPatrick Mooney 		    &dstaddr) != 0) {
12237c8c0b82SPatrick Mooney 			goto done;
12247c8c0b82SPatrick Mooney 		}
12257c8c0b82SPatrick Mooney 
12267c8c0b82SPatrick Mooney 		error = vm_copy_setup(vm, vcpuid, paging, dstaddr, opsize,
12277c8c0b82SPatrick Mooney 		    PROT_WRITE, copyinfo, nitems(copyinfo), &fault);
12287c8c0b82SPatrick Mooney 		if (error == 0) {
12297c8c0b82SPatrick Mooney 			if (fault)
12307c8c0b82SPatrick Mooney 				goto done;    /* Resume guest to handle fault */
12317c8c0b82SPatrick Mooney 
12327c8c0b82SPatrick Mooney 			/*
12337c8c0b82SPatrick Mooney 			 * case (3): read from MMIO and write to system memory.
12347c8c0b82SPatrick Mooney 			 *
12357c8c0b82SPatrick Mooney 			 * A MMIO read can have side-effects so we
12367c8c0b82SPatrick Mooney 			 * commit to it only after vm_copy_setup() is
12377c8c0b82SPatrick Mooney 			 * successful. If a page-fault needs to be
12387c8c0b82SPatrick Mooney 			 * injected into the guest then it will happen
12397c8c0b82SPatrick Mooney 			 * before the MMIO read is attempted.
12407c8c0b82SPatrick Mooney 			 */
12417c8c0b82SPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, gpa, &val,
12427c8c0b82SPatrick Mooney 			    opsize);
12437c8c0b82SPatrick Mooney 
12447c8c0b82SPatrick Mooney 			if (error == 0) {
12457c8c0b82SPatrick Mooney 				vm_copyout(vm, vcpuid, &val, copyinfo, opsize);
12467c8c0b82SPatrick Mooney 			}
12477c8c0b82SPatrick Mooney 			/*
12487c8c0b82SPatrick Mooney 			 * Regardless of whether the MMIO read was successful or
12497c8c0b82SPatrick Mooney 			 * not, the copy resources must be cleaned up.
12507c8c0b82SPatrick Mooney 			 */
12517c8c0b82SPatrick Mooney 			vm_copy_teardown(vm, vcpuid, copyinfo,
12527c8c0b82SPatrick Mooney 			    nitems(copyinfo));
12537c8c0b82SPatrick Mooney 			if (error != 0) {
12547c8c0b82SPatrick Mooney 				goto done;
12557c8c0b82SPatrick Mooney 			}
12567c8c0b82SPatrick Mooney 		} else {
12577c8c0b82SPatrick Mooney 			/*
12587c8c0b82SPatrick Mooney 			 * Case (4): read from and write to mmio.
12597c8c0b82SPatrick Mooney 			 *
12607c8c0b82SPatrick Mooney 			 * Commit to the MMIO read/write (with potential
12617c8c0b82SPatrick Mooney 			 * side-effects) only after we are sure that the
12627c8c0b82SPatrick Mooney 			 * instruction is not going to be restarted due
12637c8c0b82SPatrick Mooney 			 * to address translation faults.
12647c8c0b82SPatrick Mooney 			 */
12657c8c0b82SPatrick Mooney 			error = vm_gla2gpa(vm, vcpuid, paging, srcaddr,
12667c8c0b82SPatrick Mooney 			    PROT_READ, &srcgpa, &fault);
12677c8c0b82SPatrick Mooney 			if (error || fault)
12687c8c0b82SPatrick Mooney 				goto done;
12697c8c0b82SPatrick Mooney 
12707c8c0b82SPatrick Mooney 			error = vm_gla2gpa(vm, vcpuid, paging, dstaddr,
12717c8c0b82SPatrick Mooney 			    PROT_WRITE, &dstgpa, &fault);
12727c8c0b82SPatrick Mooney 			if (error || fault)
12737c8c0b82SPatrick Mooney 				goto done;
12747c8c0b82SPatrick Mooney 
12757c8c0b82SPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, srcgpa, &val,
12767c8c0b82SPatrick Mooney 			    opsize);
12777c8c0b82SPatrick Mooney 			if (error)
12787c8c0b82SPatrick Mooney 				goto done;
12797c8c0b82SPatrick Mooney 
12807c8c0b82SPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, dstgpa, val,
12817c8c0b82SPatrick Mooney 			    opsize);
12827c8c0b82SPatrick Mooney 			if (error)
12837c8c0b82SPatrick Mooney 				goto done;
12847c8c0b82SPatrick Mooney 		}
12857c8c0b82SPatrick Mooney 	}
12867c8c0b82SPatrick Mooney 
12877c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RSI, &rsi);
12887c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rsi", __func__, error));
12897c8c0b82SPatrick Mooney 
12907c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RDI, &rdi);
12917c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rdi", __func__, error));
12927c8c0b82SPatrick Mooney 
12937c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
12947c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
12957c8c0b82SPatrick Mooney 
12967c8c0b82SPatrick Mooney 	if (rflags & PSL_D) {
12977c8c0b82SPatrick Mooney 		rsi -= opsize;
12987c8c0b82SPatrick Mooney 		rdi -= opsize;
12997c8c0b82SPatrick Mooney 	} else {
13007c8c0b82SPatrick Mooney 		rsi += opsize;
13017c8c0b82SPatrick Mooney 		rdi += opsize;
13027c8c0b82SPatrick Mooney 	}
13037c8c0b82SPatrick Mooney 
13047c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RSI, rsi,
13057c8c0b82SPatrick Mooney 	    vie->addrsize);
13067c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d updating rsi", __func__, error));
13077c8c0b82SPatrick Mooney 
13087c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RDI, rdi,
13097c8c0b82SPatrick Mooney 	    vie->addrsize);
13107c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d updating rdi", __func__, error));
13117c8c0b82SPatrick Mooney 
13127c8c0b82SPatrick Mooney 	if (repeat) {
13137c8c0b82SPatrick Mooney 		rcx = rcx - 1;
13147c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
13157c8c0b82SPatrick Mooney 		    rcx, vie->addrsize);
13167c8c0b82SPatrick Mooney 		KASSERT(!error, ("%s: error %d updating rcx", __func__, error));
13177c8c0b82SPatrick Mooney 
13187c8c0b82SPatrick Mooney 		/*
13197c8c0b82SPatrick Mooney 		 * Repeat the instruction if the count register is not zero.
13207c8c0b82SPatrick Mooney 		 */
13217c8c0b82SPatrick Mooney 		if ((rcx & vie_size2mask(vie->addrsize)) != 0)
13227c8c0b82SPatrick Mooney 			return (vie_repeat(vie));
13237c8c0b82SPatrick Mooney 	}
13247c8c0b82SPatrick Mooney done:
13257c8c0b82SPatrick Mooney 	return (error);
13267c8c0b82SPatrick Mooney }
13277c8c0b82SPatrick Mooney 
13287c8c0b82SPatrick Mooney static int
vie_emulate_stos(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)13297c8c0b82SPatrick Mooney vie_emulate_stos(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
13307c8c0b82SPatrick Mooney {
13317c8c0b82SPatrick Mooney 	int error, opsize, repeat;
13327c8c0b82SPatrick Mooney 	uint64_t val;
13337c8c0b82SPatrick Mooney 	uint64_t rcx, rdi, rflags;
13347c8c0b82SPatrick Mooney 
13357c8c0b82SPatrick Mooney 	opsize = (vie->op.op_byte == 0xAA) ? 1 : vie->opsize;
13367c8c0b82SPatrick Mooney 	repeat = vie->repz_present | vie->repnz_present;
13377c8c0b82SPatrick Mooney 
13387c8c0b82SPatrick Mooney 	if (repeat) {
13397c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &rcx);
13407c8c0b82SPatrick Mooney 		KASSERT(!error, ("%s: error %d getting rcx", __func__, error));
13417c8c0b82SPatrick Mooney 
13427c8c0b82SPatrick Mooney 		/*
13437c8c0b82SPatrick Mooney 		 * The count register is %rcx, %ecx or %cx depending on the
13447c8c0b82SPatrick Mooney 		 * address size of the instruction.
13457c8c0b82SPatrick Mooney 		 */
13467c8c0b82SPatrick Mooney 		if ((rcx & vie_size2mask(vie->addrsize)) == 0)
13477c8c0b82SPatrick Mooney 			return (0);
13487c8c0b82SPatrick Mooney 	}
13497c8c0b82SPatrick Mooney 
13507c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RAX, &val);
13517c8c0b82SPatrick Mooney 	KASSERT(!error, ("%s: error %d getting rax", __func__, error));
13527c8c0b82SPatrick Mooney 
13537c8c0b82SPatrick Mooney 	error = vie_mmio_write(vie, vm, vcpuid, gpa, val, opsize);
13547c8c0b82SPatrick Mooney 	if (error)
13557c8c0b82SPatrick Mooney 		return (error);
13567c8c0b82SPatrick Mooney 
13577c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RDI, &rdi);
13587c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rdi", __func__, error));
13597c8c0b82SPatrick Mooney 
13607c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
13617c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
13627c8c0b82SPatrick Mooney 
13637c8c0b82SPatrick Mooney 	if (rflags & PSL_D)
13647c8c0b82SPatrick Mooney 		rdi -= opsize;
13657c8c0b82SPatrick Mooney 	else
13667c8c0b82SPatrick Mooney 		rdi += opsize;
13677c8c0b82SPatrick Mooney 
13687c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RDI, rdi,
13697c8c0b82SPatrick Mooney 	    vie->addrsize);
13707c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d updating rdi", __func__, error));
13717c8c0b82SPatrick Mooney 
13727c8c0b82SPatrick Mooney 	if (repeat) {
13737c8c0b82SPatrick Mooney 		rcx = rcx - 1;
13747c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
13757c8c0b82SPatrick Mooney 		    rcx, vie->addrsize);
13767c8c0b82SPatrick Mooney 		KASSERT(!error, ("%s: error %d updating rcx", __func__, error));
13777c8c0b82SPatrick Mooney 
13787c8c0b82SPatrick Mooney 		/*
13797c8c0b82SPatrick Mooney 		 * Repeat the instruction if the count register is not zero.
13807c8c0b82SPatrick Mooney 		 */
13817c8c0b82SPatrick Mooney 		if ((rcx & vie_size2mask(vie->addrsize)) != 0)
13827c8c0b82SPatrick Mooney 			return (vie_repeat(vie));
13837c8c0b82SPatrick Mooney 	}
13847c8c0b82SPatrick Mooney 
13857c8c0b82SPatrick Mooney 	return (0);
13867c8c0b82SPatrick Mooney }
13877c8c0b82SPatrick Mooney 
13887c8c0b82SPatrick Mooney static int
vie_emulate_and(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)13897c8c0b82SPatrick Mooney vie_emulate_and(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
13907c8c0b82SPatrick Mooney {
13917c8c0b82SPatrick Mooney 	int error, size;
13927c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
13937c8c0b82SPatrick Mooney 	uint64_t result, rflags, rflags2, val1, val2;
13947c8c0b82SPatrick Mooney 
13957c8c0b82SPatrick Mooney 	size = vie->opsize;
13967c8c0b82SPatrick Mooney 	error = EINVAL;
13977c8c0b82SPatrick Mooney 
13987c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
13997c8c0b82SPatrick Mooney 	case 0x23:
14007c8c0b82SPatrick Mooney 		/*
14017c8c0b82SPatrick Mooney 		 * AND reg (ModRM:reg) and mem (ModRM:r/m) and store the
14027c8c0b82SPatrick Mooney 		 * result in reg.
14037c8c0b82SPatrick Mooney 		 *
14047c8c0b82SPatrick Mooney 		 * 23/r		and r16, r/m16
14057c8c0b82SPatrick Mooney 		 * 23/r		and r32, r/m32
14067c8c0b82SPatrick Mooney 		 * REX.W + 23/r	and r64, r/m64
14077c8c0b82SPatrick Mooney 		 */
14087c8c0b82SPatrick Mooney 
14097c8c0b82SPatrick Mooney 		/* get the first operand */
14107c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
14117c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
14127c8c0b82SPatrick Mooney 		if (error)
14137c8c0b82SPatrick Mooney 			break;
14147c8c0b82SPatrick Mooney 
14157c8c0b82SPatrick Mooney 		/* get the second operand */
14167c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
14177c8c0b82SPatrick Mooney 		if (error)
14187c8c0b82SPatrick Mooney 			break;
14197c8c0b82SPatrick Mooney 
14207c8c0b82SPatrick Mooney 		/* perform the operation and write the result */
14217c8c0b82SPatrick Mooney 		result = val1 & val2;
14227c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, result, size);
14237c8c0b82SPatrick Mooney 		break;
14247c8c0b82SPatrick Mooney 	case 0x81:
14257c8c0b82SPatrick Mooney 	case 0x83:
14267c8c0b82SPatrick Mooney 		/*
14277c8c0b82SPatrick Mooney 		 * AND mem (ModRM:r/m) with immediate and store the
14287c8c0b82SPatrick Mooney 		 * result in mem.
14297c8c0b82SPatrick Mooney 		 *
14307c8c0b82SPatrick Mooney 		 * 81 /4		and r/m16, imm16
14317c8c0b82SPatrick Mooney 		 * 81 /4		and r/m32, imm32
14327c8c0b82SPatrick Mooney 		 * REX.W + 81 /4	and r/m64, imm32 sign-extended to 64
14337c8c0b82SPatrick Mooney 		 *
14347c8c0b82SPatrick Mooney 		 * 83 /4		and r/m16, imm8 sign-extended to 16
14357c8c0b82SPatrick Mooney 		 * 83 /4		and r/m32, imm8 sign-extended to 32
14367c8c0b82SPatrick Mooney 		 * REX.W + 83/4		and r/m64, imm8 sign-extended to 64
14377c8c0b82SPatrick Mooney 		 */
14387c8c0b82SPatrick Mooney 
14397c8c0b82SPatrick Mooney 		/* get the first operand */
14407c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val1, size);
14417c8c0b82SPatrick Mooney 		if (error)
14427c8c0b82SPatrick Mooney 			break;
14437c8c0b82SPatrick Mooney 
14447c8c0b82SPatrick Mooney 		/*
14457c8c0b82SPatrick Mooney 		 * perform the operation with the pre-fetched immediate
14467c8c0b82SPatrick Mooney 		 * operand and write the result
14477c8c0b82SPatrick Mooney 		 */
14487c8c0b82SPatrick Mooney 		result = val1 & vie->immediate;
14497c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, result, size);
14507c8c0b82SPatrick Mooney 		break;
14517c8c0b82SPatrick Mooney 	default:
14527c8c0b82SPatrick Mooney 		break;
14537c8c0b82SPatrick Mooney 	}
14547c8c0b82SPatrick Mooney 	if (error)
14557c8c0b82SPatrick Mooney 		return (error);
14567c8c0b82SPatrick Mooney 
14577c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
14587c8c0b82SPatrick Mooney 	if (error)
14597c8c0b82SPatrick Mooney 		return (error);
14607c8c0b82SPatrick Mooney 
14617c8c0b82SPatrick Mooney 	/*
14627c8c0b82SPatrick Mooney 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
14637c8c0b82SPatrick Mooney 	 * to the result; AF is undefined.
14647c8c0b82SPatrick Mooney 	 *
14657c8c0b82SPatrick Mooney 	 * The updated status flags are obtained by subtracting 0 from 'result'.
14667c8c0b82SPatrick Mooney 	 */
14677c8c0b82SPatrick Mooney 	rflags2 = getcc(size, result, 0);
14687c8c0b82SPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
14697c8c0b82SPatrick Mooney 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
14707c8c0b82SPatrick Mooney 
14717c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
14727c8c0b82SPatrick Mooney 	return (error);
14737c8c0b82SPatrick Mooney }
14747c8c0b82SPatrick Mooney 
14757c8c0b82SPatrick Mooney static int
vie_emulate_or(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)14767c8c0b82SPatrick Mooney vie_emulate_or(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
14777c8c0b82SPatrick Mooney {
14787c8c0b82SPatrick Mooney 	int error, size;
14797c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
14807c8c0b82SPatrick Mooney 	uint64_t result, rflags, rflags2, val1, val2;
14817c8c0b82SPatrick Mooney 
14827c8c0b82SPatrick Mooney 	size = vie->opsize;
14837c8c0b82SPatrick Mooney 	error = EINVAL;
14847c8c0b82SPatrick Mooney 
14857c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
14867c8c0b82SPatrick Mooney 	case 0x0B:
14877c8c0b82SPatrick Mooney 		/*
14887c8c0b82SPatrick Mooney 		 * OR reg (ModRM:reg) and mem (ModRM:r/m) and store the
14897c8c0b82SPatrick Mooney 		 * result in reg.
14907c8c0b82SPatrick Mooney 		 *
14917c8c0b82SPatrick Mooney 		 * 0b/r		or r16, r/m16
14927c8c0b82SPatrick Mooney 		 * 0b/r		or r32, r/m32
14937c8c0b82SPatrick Mooney 		 * REX.W + 0b/r	or r64, r/m64
14947c8c0b82SPatrick Mooney 		 */
14957c8c0b82SPatrick Mooney 
14967c8c0b82SPatrick Mooney 		/* get the first operand */
14977c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
14987c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
14997c8c0b82SPatrick Mooney 		if (error)
15007c8c0b82SPatrick Mooney 			break;
15017c8c0b82SPatrick Mooney 
15027c8c0b82SPatrick Mooney 		/* get the second operand */
15037c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
15047c8c0b82SPatrick Mooney 		if (error)
15057c8c0b82SPatrick Mooney 			break;
15067c8c0b82SPatrick Mooney 
15077c8c0b82SPatrick Mooney 		/* perform the operation and write the result */
15087c8c0b82SPatrick Mooney 		result = val1 | val2;
15097c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, result, size);
15107c8c0b82SPatrick Mooney 		break;
15117c8c0b82SPatrick Mooney 	case 0x81:
15127c8c0b82SPatrick Mooney 	case 0x83:
15137c8c0b82SPatrick Mooney 		/*
15147c8c0b82SPatrick Mooney 		 * OR mem (ModRM:r/m) with immediate and store the
15157c8c0b82SPatrick Mooney 		 * result in mem.
15167c8c0b82SPatrick Mooney 		 *
15177c8c0b82SPatrick Mooney 		 * 81 /1		or r/m16, imm16
15187c8c0b82SPatrick Mooney 		 * 81 /1		or r/m32, imm32
15197c8c0b82SPatrick Mooney 		 * REX.W + 81 /1	or r/m64, imm32 sign-extended to 64
15207c8c0b82SPatrick Mooney 		 *
15217c8c0b82SPatrick Mooney 		 * 83 /1		or r/m16, imm8 sign-extended to 16
15227c8c0b82SPatrick Mooney 		 * 83 /1		or r/m32, imm8 sign-extended to 32
15237c8c0b82SPatrick Mooney 		 * REX.W + 83/1		or r/m64, imm8 sign-extended to 64
15247c8c0b82SPatrick Mooney 		 */
15257c8c0b82SPatrick Mooney 
15267c8c0b82SPatrick Mooney 		/* get the first operand */
15277c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val1, size);
15287c8c0b82SPatrick Mooney 		if (error)
15297c8c0b82SPatrick Mooney 			break;
15307c8c0b82SPatrick Mooney 
15317c8c0b82SPatrick Mooney 		/*
15327c8c0b82SPatrick Mooney 		 * perform the operation with the pre-fetched immediate
15337c8c0b82SPatrick Mooney 		 * operand and write the result
15347c8c0b82SPatrick Mooney 		 */
15357c8c0b82SPatrick Mooney 		result = val1 | vie->immediate;
15367c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, result, size);
15377c8c0b82SPatrick Mooney 		break;
15387c8c0b82SPatrick Mooney 	default:
15397c8c0b82SPatrick Mooney 		break;
15407c8c0b82SPatrick Mooney 	}
15417c8c0b82SPatrick Mooney 	if (error)
15427c8c0b82SPatrick Mooney 		return (error);
15437c8c0b82SPatrick Mooney 
15447c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
15457c8c0b82SPatrick Mooney 	if (error)
15467c8c0b82SPatrick Mooney 		return (error);
15477c8c0b82SPatrick Mooney 
15487c8c0b82SPatrick Mooney 	/*
15497c8c0b82SPatrick Mooney 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
15507c8c0b82SPatrick Mooney 	 * to the result; AF is undefined.
15517c8c0b82SPatrick Mooney 	 *
15527c8c0b82SPatrick Mooney 	 * The updated status flags are obtained by subtracting 0 from 'result'.
15537c8c0b82SPatrick Mooney 	 */
15547c8c0b82SPatrick Mooney 	rflags2 = getcc(size, result, 0);
15557c8c0b82SPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
15567c8c0b82SPatrick Mooney 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
15577c8c0b82SPatrick Mooney 
15587c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
15597c8c0b82SPatrick Mooney 	return (error);
15607c8c0b82SPatrick Mooney }
15617c8c0b82SPatrick Mooney 
15627c8c0b82SPatrick Mooney static int
vie_emulate_cmp(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)15637c8c0b82SPatrick Mooney vie_emulate_cmp(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
15647c8c0b82SPatrick Mooney {
15657c8c0b82SPatrick Mooney 	int error, size;
15667c8c0b82SPatrick Mooney 	uint64_t regop, memop, op1, op2, rflags, rflags2;
15677c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
15687c8c0b82SPatrick Mooney 
15697c8c0b82SPatrick Mooney 	size = vie->opsize;
15707c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
15717c8c0b82SPatrick Mooney 	case 0x39:
15727c8c0b82SPatrick Mooney 	case 0x3B:
15737c8c0b82SPatrick Mooney 		/*
15747c8c0b82SPatrick Mooney 		 * 39/r		CMP r/m16, r16
15757c8c0b82SPatrick Mooney 		 * 39/r		CMP r/m32, r32
15767c8c0b82SPatrick Mooney 		 * REX.W 39/r	CMP r/m64, r64
15777c8c0b82SPatrick Mooney 		 *
15787c8c0b82SPatrick Mooney 		 * 3B/r		CMP r16, r/m16
15797c8c0b82SPatrick Mooney 		 * 3B/r		CMP r32, r/m32
15807c8c0b82SPatrick Mooney 		 * REX.W + 3B/r	CMP r64, r/m64
15817c8c0b82SPatrick Mooney 		 *
15827c8c0b82SPatrick Mooney 		 * Compare the first operand with the second operand and
15837c8c0b82SPatrick Mooney 		 * set status flags in EFLAGS register. The comparison is
15847c8c0b82SPatrick Mooney 		 * performed by subtracting the second operand from the first
15857c8c0b82SPatrick Mooney 		 * operand and then setting the status flags.
15867c8c0b82SPatrick Mooney 		 */
15877c8c0b82SPatrick Mooney 
15887c8c0b82SPatrick Mooney 		/* Get the register operand */
15897c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
15907c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &regop);
15917c8c0b82SPatrick Mooney 		if (error)
15927c8c0b82SPatrick Mooney 			return (error);
15937c8c0b82SPatrick Mooney 
15947c8c0b82SPatrick Mooney 		/* Get the memory operand */
15957c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &memop, size);
15967c8c0b82SPatrick Mooney 		if (error)
15977c8c0b82SPatrick Mooney 			return (error);
15987c8c0b82SPatrick Mooney 
15997c8c0b82SPatrick Mooney 		if (vie->op.op_byte == 0x3B) {
16007c8c0b82SPatrick Mooney 			op1 = regop;
16017c8c0b82SPatrick Mooney 			op2 = memop;
16027c8c0b82SPatrick Mooney 		} else {
16037c8c0b82SPatrick Mooney 			op1 = memop;
16047c8c0b82SPatrick Mooney 			op2 = regop;
16057c8c0b82SPatrick Mooney 		}
16067c8c0b82SPatrick Mooney 		rflags2 = getcc(size, op1, op2);
16077c8c0b82SPatrick Mooney 		break;
16087c8c0b82SPatrick Mooney 	case 0x80:
16097c8c0b82SPatrick Mooney 	case 0x81:
16107c8c0b82SPatrick Mooney 	case 0x83:
16117c8c0b82SPatrick Mooney 		/*
16127c8c0b82SPatrick Mooney 		 * 80 /7		cmp r/m8, imm8
16137c8c0b82SPatrick Mooney 		 * REX + 80 /7		cmp r/m8, imm8
16147c8c0b82SPatrick Mooney 		 *
16157c8c0b82SPatrick Mooney 		 * 81 /7		cmp r/m16, imm16
16167c8c0b82SPatrick Mooney 		 * 81 /7		cmp r/m32, imm32
16177c8c0b82SPatrick Mooney 		 * REX.W + 81 /7	cmp r/m64, imm32 sign-extended to 64
16187c8c0b82SPatrick Mooney 		 *
16197c8c0b82SPatrick Mooney 		 * 83 /7		cmp r/m16, imm8 sign-extended to 16
16207c8c0b82SPatrick Mooney 		 * 83 /7		cmp r/m32, imm8 sign-extended to 32
16217c8c0b82SPatrick Mooney 		 * REX.W + 83 /7	cmp r/m64, imm8 sign-extended to 64
16227c8c0b82SPatrick Mooney 		 *
16237c8c0b82SPatrick Mooney 		 * Compare mem (ModRM:r/m) with immediate and set
16247c8c0b82SPatrick Mooney 		 * status flags according to the results.  The
16257c8c0b82SPatrick Mooney 		 * comparison is performed by subtracting the
16267c8c0b82SPatrick Mooney 		 * immediate from the first operand and then setting
16277c8c0b82SPatrick Mooney 		 * the status flags.
16287c8c0b82SPatrick Mooney 		 *
16297c8c0b82SPatrick Mooney 		 */
16307c8c0b82SPatrick Mooney 		if (vie->op.op_byte == 0x80)
16317c8c0b82SPatrick Mooney 			size = 1;
16327c8c0b82SPatrick Mooney 
16337c8c0b82SPatrick Mooney 		/* get the first operand */
16347c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
16357c8c0b82SPatrick Mooney 		if (error)
16367c8c0b82SPatrick Mooney 			return (error);
16377c8c0b82SPatrick Mooney 
16387c8c0b82SPatrick Mooney 		rflags2 = getcc(size, op1, vie->immediate);
16397c8c0b82SPatrick Mooney 		break;
16407c8c0b82SPatrick Mooney 	default:
16417c8c0b82SPatrick Mooney 		return (EINVAL);
16427c8c0b82SPatrick Mooney 	}
16437c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
16447c8c0b82SPatrick Mooney 	if (error)
16457c8c0b82SPatrick Mooney 		return (error);
16467c8c0b82SPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
16477c8c0b82SPatrick Mooney 	rflags |= rflags2 & RFLAGS_STATUS_BITS;
16487c8c0b82SPatrick Mooney 
16497c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
16507c8c0b82SPatrick Mooney 	return (error);
16517c8c0b82SPatrick Mooney }
16527c8c0b82SPatrick Mooney 
16537c8c0b82SPatrick Mooney static int
vie_emulate_test(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)16547c8c0b82SPatrick Mooney vie_emulate_test(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
16557c8c0b82SPatrick Mooney {
16567c8c0b82SPatrick Mooney 	int error, size;
16577c8c0b82SPatrick Mooney 	uint64_t op1, rflags, rflags2;
16587c8c0b82SPatrick Mooney 
16597c8c0b82SPatrick Mooney 	size = vie->opsize;
16607c8c0b82SPatrick Mooney 	error = EINVAL;
16617c8c0b82SPatrick Mooney 
16627c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
16637c8c0b82SPatrick Mooney 	case 0xF6:
16647c8c0b82SPatrick Mooney 		/*
16657c8c0b82SPatrick Mooney 		 * F6 /0		test r/m8, imm8
16667c8c0b82SPatrick Mooney 		 *
16677c8c0b82SPatrick Mooney 		 * Test mem (ModRM:r/m) with immediate and set status
16687c8c0b82SPatrick Mooney 		 * flags according to the results.  The comparison is
16697c8c0b82SPatrick Mooney 		 * performed by anding the immediate from the first
16707c8c0b82SPatrick Mooney 		 * operand and then setting the status flags.
16717c8c0b82SPatrick Mooney 		 */
16727c8c0b82SPatrick Mooney 		if ((vie->reg & 7) != 0)
16737c8c0b82SPatrick Mooney 			return (EINVAL);
16747c8c0b82SPatrick Mooney 
16757c8c0b82SPatrick Mooney 		size = 1;	/* override for byte operation */
16767c8c0b82SPatrick Mooney 
16777c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
16787c8c0b82SPatrick Mooney 		if (error)
16797c8c0b82SPatrick Mooney 			return (error);
16807c8c0b82SPatrick Mooney 
16817c8c0b82SPatrick Mooney 		rflags2 = getandflags(size, op1, vie->immediate);
16827c8c0b82SPatrick Mooney 		break;
16837c8c0b82SPatrick Mooney 	case 0xF7:
16847c8c0b82SPatrick Mooney 		/*
16857c8c0b82SPatrick Mooney 		 * F7 /0		test r/m16, imm16
16867c8c0b82SPatrick Mooney 		 * F7 /0		test r/m32, imm32
16877c8c0b82SPatrick Mooney 		 * REX.W + F7 /0	test r/m64, imm32 sign-extended to 64
16887c8c0b82SPatrick Mooney 		 *
16897c8c0b82SPatrick Mooney 		 * Test mem (ModRM:r/m) with immediate and set status
16907c8c0b82SPatrick Mooney 		 * flags according to the results.  The comparison is
16917c8c0b82SPatrick Mooney 		 * performed by anding the immediate from the first
16927c8c0b82SPatrick Mooney 		 * operand and then setting the status flags.
16937c8c0b82SPatrick Mooney 		 */
16947c8c0b82SPatrick Mooney 		if ((vie->reg & 7) != 0)
16957c8c0b82SPatrick Mooney 			return (EINVAL);
16967c8c0b82SPatrick Mooney 
16977c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
16987c8c0b82SPatrick Mooney 		if (error)
16997c8c0b82SPatrick Mooney 			return (error);
17007c8c0b82SPatrick Mooney 
17017c8c0b82SPatrick Mooney 		rflags2 = getandflags(size, op1, vie->immediate);
17027c8c0b82SPatrick Mooney 		break;
17037c8c0b82SPatrick Mooney 	default:
17047c8c0b82SPatrick Mooney 		return (EINVAL);
17057c8c0b82SPatrick Mooney 	}
17067c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
17077c8c0b82SPatrick Mooney 	if (error)
17087c8c0b82SPatrick Mooney 		return (error);
17097c8c0b82SPatrick Mooney 
17107c8c0b82SPatrick Mooney 	/*
17117c8c0b82SPatrick Mooney 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
17127c8c0b82SPatrick Mooney 	 * to the result; AF is undefined.
17137c8c0b82SPatrick Mooney 	 */
17147c8c0b82SPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
17157c8c0b82SPatrick Mooney 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
17167c8c0b82SPatrick Mooney 
17177c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
17187c8c0b82SPatrick Mooney 	return (error);
17197c8c0b82SPatrick Mooney }
17207c8c0b82SPatrick Mooney 
17217c8c0b82SPatrick Mooney static int
vie_emulate_bextr(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)17227c8c0b82SPatrick Mooney vie_emulate_bextr(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
17237c8c0b82SPatrick Mooney {
17247c8c0b82SPatrick Mooney 	uint64_t src1, src2, dst, rflags;
172559d65d31SAndy Fiddaman 	unsigned start, len, size;
172659d65d31SAndy Fiddaman 	int error;
17277c8c0b82SPatrick Mooney 	struct vm_guest_paging *paging;
17287c8c0b82SPatrick Mooney 
17297c8c0b82SPatrick Mooney 	size = vie->opsize;
17307c8c0b82SPatrick Mooney 	error = EINVAL;
17317c8c0b82SPatrick Mooney 	paging = &vie->paging;
17327c8c0b82SPatrick Mooney 
17337c8c0b82SPatrick Mooney 	/*
17347c8c0b82SPatrick Mooney 	 * VEX.LZ.0F38.W0 F7 /r		BEXTR r32a, r/m32, r32b
17357c8c0b82SPatrick Mooney 	 * VEX.LZ.0F38.W1 F7 /r		BEXTR r64a, r/m64, r64b
17367c8c0b82SPatrick Mooney 	 *
17377c8c0b82SPatrick Mooney 	 * Destination operand is ModRM:reg.  Source operands are ModRM:r/m and
17387c8c0b82SPatrick Mooney 	 * Vex.vvvv.
17397c8c0b82SPatrick Mooney 	 *
17407c8c0b82SPatrick Mooney 	 * Operand size is always 32-bit if not in 64-bit mode (W1 is ignored).
17417c8c0b82SPatrick Mooney 	 */
17427c8c0b82SPatrick Mooney 	if (size != 4 && paging->cpu_mode != CPU_MODE_64BIT)
17437c8c0b82SPatrick Mooney 		size = 4;
17447c8c0b82SPatrick Mooney 
17457c8c0b82SPatrick Mooney 	/*
17467c8c0b82SPatrick Mooney 	 * Extracts contiguous bits from the first /source/ operand (second
17477c8c0b82SPatrick Mooney 	 * operand) using an index and length specified in the second /source/
17487c8c0b82SPatrick Mooney 	 * operand (third operand).
17497c8c0b82SPatrick Mooney 	 */
17507c8c0b82SPatrick Mooney 	error = vie_mmio_read(vie, vm, vcpuid, gpa, &src1, size);
17517c8c0b82SPatrick Mooney 	if (error)
17527c8c0b82SPatrick Mooney 		return (error);
17537c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, gpr_map[vie->vex_reg], &src2);
17547c8c0b82SPatrick Mooney 	if (error)
17557c8c0b82SPatrick Mooney 		return (error);
17567c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
17577c8c0b82SPatrick Mooney 	if (error)
17587c8c0b82SPatrick Mooney 		return (error);
17597c8c0b82SPatrick Mooney 
17607c8c0b82SPatrick Mooney 	start = (src2 & 0xff);
17617c8c0b82SPatrick Mooney 	len = (src2 & 0xff00) >> 8;
17627c8c0b82SPatrick Mooney 
17637c8c0b82SPatrick Mooney 	/* If no bits are extracted, the destination register is cleared. */
17647c8c0b82SPatrick Mooney 	dst = 0;
17657c8c0b82SPatrick Mooney 
17667c8c0b82SPatrick Mooney 	/* If START exceeds the operand size, no bits are extracted. */
17677c8c0b82SPatrick Mooney 	if (start > size * 8)
17687c8c0b82SPatrick Mooney 		goto done;
17697c8c0b82SPatrick Mooney 	/* Length is bounded by both the destination size and start offset. */
17707c8c0b82SPatrick Mooney 	if (start + len > size * 8)
17717c8c0b82SPatrick Mooney 		len = (size * 8) - start;
17727c8c0b82SPatrick Mooney 	if (len == 0)
17737c8c0b82SPatrick Mooney 		goto done;
17747c8c0b82SPatrick Mooney 
17757c8c0b82SPatrick Mooney 	if (start > 0)
17767c8c0b82SPatrick Mooney 		src1 = (src1 >> start);
17777c8c0b82SPatrick Mooney 	if (len < 64)
17787c8c0b82SPatrick Mooney 		src1 = src1 & ((1ull << len) - 1);
17797c8c0b82SPatrick Mooney 	dst = src1;
17807c8c0b82SPatrick Mooney 
17817c8c0b82SPatrick Mooney done:
17827c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, gpr_map[vie->reg], dst, size);
17837c8c0b82SPatrick Mooney 	if (error)
17847c8c0b82SPatrick Mooney 		return (error);
17857c8c0b82SPatrick Mooney 
17867c8c0b82SPatrick Mooney 	/*
17877c8c0b82SPatrick Mooney 	 * AMD: OF, CF cleared; SF/AF/PF undefined; ZF set by result.
17887c8c0b82SPatrick Mooney 	 * Intel: ZF is set by result; AF/SF/PF undefined; all others cleared.
17897c8c0b82SPatrick Mooney 	 */
17907c8c0b82SPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
17917c8c0b82SPatrick Mooney 	if (dst == 0)
17927c8c0b82SPatrick Mooney 		rflags |= PSL_Z;
17937c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags,
17947c8c0b82SPatrick Mooney 	    8);
17957c8c0b82SPatrick Mooney 	return (error);
17967c8c0b82SPatrick Mooney }
17977c8c0b82SPatrick Mooney 
17987c8c0b82SPatrick Mooney static int
vie_emulate_add(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)17997c8c0b82SPatrick Mooney vie_emulate_add(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
18007c8c0b82SPatrick Mooney {
18017c8c0b82SPatrick Mooney 	int error, size;
18027c8c0b82SPatrick Mooney 	uint64_t nval, rflags, rflags2, val1, val2;
18037c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
18047c8c0b82SPatrick Mooney 
18057c8c0b82SPatrick Mooney 	size = vie->opsize;
18067c8c0b82SPatrick Mooney 	error = EINVAL;
18077c8c0b82SPatrick Mooney 
18087c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
18097c8c0b82SPatrick Mooney 	case 0x03:
18107c8c0b82SPatrick Mooney 		/*
18117c8c0b82SPatrick Mooney 		 * ADD r/m to r and store the result in r
18127c8c0b82SPatrick Mooney 		 *
18137c8c0b82SPatrick Mooney 		 * 03/r			ADD r16, r/m16
18147c8c0b82SPatrick Mooney 		 * 03/r			ADD r32, r/m32
18157c8c0b82SPatrick Mooney 		 * REX.W + 03/r		ADD r64, r/m64
18167c8c0b82SPatrick Mooney 		 */
18177c8c0b82SPatrick Mooney 
18187c8c0b82SPatrick Mooney 		/* get the first operand */
18197c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
18207c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
18217c8c0b82SPatrick Mooney 		if (error)
18227c8c0b82SPatrick Mooney 			break;
18237c8c0b82SPatrick Mooney 
18247c8c0b82SPatrick Mooney 		/* get the second operand */
18257c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
18267c8c0b82SPatrick Mooney 		if (error)
18277c8c0b82SPatrick Mooney 			break;
18287c8c0b82SPatrick Mooney 
18297c8c0b82SPatrick Mooney 		/* perform the operation and write the result */
18307c8c0b82SPatrick Mooney 		nval = val1 + val2;
18317c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, nval, size);
18327c8c0b82SPatrick Mooney 		break;
18337c8c0b82SPatrick Mooney 	default:
18347c8c0b82SPatrick Mooney 		break;
18357c8c0b82SPatrick Mooney 	}
18367c8c0b82SPatrick Mooney 
18377c8c0b82SPatrick Mooney 	if (!error) {
18387c8c0b82SPatrick Mooney 		rflags2 = getaddflags(size, val1, val2);
18397c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
18407c8c0b82SPatrick Mooney 		    &rflags);
18417c8c0b82SPatrick Mooney 		if (error)
18427c8c0b82SPatrick Mooney 			return (error);
18437c8c0b82SPatrick Mooney 
18447c8c0b82SPatrick Mooney 		rflags &= ~RFLAGS_STATUS_BITS;
18457c8c0b82SPatrick Mooney 		rflags |= rflags2 & RFLAGS_STATUS_BITS;
18467c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
18477c8c0b82SPatrick Mooney 		    rflags, 8);
18487c8c0b82SPatrick Mooney 	}
18497c8c0b82SPatrick Mooney 
18507c8c0b82SPatrick Mooney 	return (error);
18517c8c0b82SPatrick Mooney }
18527c8c0b82SPatrick Mooney 
18537c8c0b82SPatrick Mooney static int
vie_emulate_sub(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)18547c8c0b82SPatrick Mooney vie_emulate_sub(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
18557c8c0b82SPatrick Mooney {
18567c8c0b82SPatrick Mooney 	int error, size;
18577c8c0b82SPatrick Mooney 	uint64_t nval, rflags, rflags2, val1, val2;
18587c8c0b82SPatrick Mooney 	enum vm_reg_name reg;
18597c8c0b82SPatrick Mooney 
18607c8c0b82SPatrick Mooney 	size = vie->opsize;
18617c8c0b82SPatrick Mooney 	error = EINVAL;
18627c8c0b82SPatrick Mooney 
18637c8c0b82SPatrick Mooney 	switch (vie->op.op_byte) {
18647c8c0b82SPatrick Mooney 	case 0x2B:
18657c8c0b82SPatrick Mooney 		/*
18667c8c0b82SPatrick Mooney 		 * SUB r/m from r and store the result in r
18677c8c0b82SPatrick Mooney 		 *
18687c8c0b82SPatrick Mooney 		 * 2B/r		SUB r16, r/m16
18697c8c0b82SPatrick Mooney 		 * 2B/r		SUB r32, r/m32
18707c8c0b82SPatrick Mooney 		 * REX.W + 2B/r	SUB r64, r/m64
18717c8c0b82SPatrick Mooney 		 */
18727c8c0b82SPatrick Mooney 
18737c8c0b82SPatrick Mooney 		/* get the first operand */
18747c8c0b82SPatrick Mooney 		reg = gpr_map[vie->reg];
18757c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
18767c8c0b82SPatrick Mooney 		if (error)
18777c8c0b82SPatrick Mooney 			break;
18787c8c0b82SPatrick Mooney 
18797c8c0b82SPatrick Mooney 		/* get the second operand */
18807c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
18817c8c0b82SPatrick Mooney 		if (error)
18827c8c0b82SPatrick Mooney 			break;
18837c8c0b82SPatrick Mooney 
18847c8c0b82SPatrick Mooney 		/* perform the operation and write the result */
18857c8c0b82SPatrick Mooney 		nval = val1 - val2;
18867c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, nval, size);
18877c8c0b82SPatrick Mooney 		break;
18887c8c0b82SPatrick Mooney 	default:
18897c8c0b82SPatrick Mooney 		break;
18907c8c0b82SPatrick Mooney 	}
18917c8c0b82SPatrick Mooney 
18927c8c0b82SPatrick Mooney 	if (!error) {
18937c8c0b82SPatrick Mooney 		rflags2 = getcc(size, val1, val2);
18947c8c0b82SPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
18957c8c0b82SPatrick Mooney 		    &rflags);
18967c8c0b82SPatrick Mooney 		if (error)
18977c8c0b82SPatrick Mooney 			return (error);
18987c8c0b82SPatrick Mooney 
18997c8c0b82SPatrick Mooney 		rflags &= ~RFLAGS_STATUS_BITS;
19007c8c0b82SPatrick Mooney 		rflags |= rflags2 & RFLAGS_STATUS_BITS;
19017c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
19027c8c0b82SPatrick Mooney 		    rflags, 8);
19037c8c0b82SPatrick Mooney 	}
19047c8c0b82SPatrick Mooney 
19057c8c0b82SPatrick Mooney 	return (error);
19067c8c0b82SPatrick Mooney }
19077c8c0b82SPatrick Mooney 
19087c8c0b82SPatrick Mooney static int
vie_emulate_mul(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)19091fde93bfSAndy Fiddaman vie_emulate_mul(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
19101fde93bfSAndy Fiddaman {
19111fde93bfSAndy Fiddaman 	int error, size;
19121fde93bfSAndy Fiddaman 	uint64_t rflags, rflags2, val1, val2;
19131fde93bfSAndy Fiddaman 	__int128_t nval;
19141fde93bfSAndy Fiddaman 	enum vm_reg_name reg;
19151fde93bfSAndy Fiddaman 	ulong_t (*getflags)(int, uint64_t, uint64_t) = NULL;
19161fde93bfSAndy Fiddaman 
19171fde93bfSAndy Fiddaman 	size = vie->opsize;
19181fde93bfSAndy Fiddaman 	error = EINVAL;
19191fde93bfSAndy Fiddaman 
19201fde93bfSAndy Fiddaman 	switch (vie->op.op_byte) {
19211fde93bfSAndy Fiddaman 	case 0xAF:
19221fde93bfSAndy Fiddaman 		/*
19231fde93bfSAndy Fiddaman 		 * Multiply the contents of a destination register by
19241fde93bfSAndy Fiddaman 		 * the contents of a register or memory operand and
19251fde93bfSAndy Fiddaman 		 * put the signed result in the destination register.
19261fde93bfSAndy Fiddaman 		 *
19271fde93bfSAndy Fiddaman 		 * AF/r		IMUL r16, r/m16
19281fde93bfSAndy Fiddaman 		 * AF/r		IMUL r32, r/m32
19291fde93bfSAndy Fiddaman 		 * REX.W + AF/r	IMUL r64, r/m64
19301fde93bfSAndy Fiddaman 		 */
19311fde93bfSAndy Fiddaman 
19321fde93bfSAndy Fiddaman 		getflags = getimulflags;
19331fde93bfSAndy Fiddaman 
19341fde93bfSAndy Fiddaman 		/* get the first operand */
19351fde93bfSAndy Fiddaman 		reg = gpr_map[vie->reg];
19361fde93bfSAndy Fiddaman 		error = vm_get_register(vm, vcpuid, reg, &val1);
19371fde93bfSAndy Fiddaman 		if (error != 0)
19381fde93bfSAndy Fiddaman 			break;
19391fde93bfSAndy Fiddaman 
19401fde93bfSAndy Fiddaman 		/* get the second operand */
19411fde93bfSAndy Fiddaman 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
19421fde93bfSAndy Fiddaman 		if (error != 0)
19431fde93bfSAndy Fiddaman 			break;
19441fde93bfSAndy Fiddaman 
19451fde93bfSAndy Fiddaman 		/* perform the operation and write the result */
19461fde93bfSAndy Fiddaman 		nval = (int64_t)val1 * (int64_t)val2;
19471fde93bfSAndy Fiddaman 
19481fde93bfSAndy Fiddaman 		error = vie_update_register(vm, vcpuid, reg, nval, size);
19491fde93bfSAndy Fiddaman 
19501fde93bfSAndy Fiddaman 		DTRACE_PROBE4(vie__imul,
19511fde93bfSAndy Fiddaman 		    const char *, vie_regnum_name(vie->reg, size),
19521fde93bfSAndy Fiddaman 		    uint64_t, val1, uint64_t, val2, __uint128_t, nval);
19531fde93bfSAndy Fiddaman 
19541fde93bfSAndy Fiddaman 		break;
19551fde93bfSAndy Fiddaman 	default:
19561fde93bfSAndy Fiddaman 		break;
19571fde93bfSAndy Fiddaman 	}
19581fde93bfSAndy Fiddaman 
19591fde93bfSAndy Fiddaman 	if (error == 0) {
19601fde93bfSAndy Fiddaman 		rflags2 = getflags(size, val1, val2);
19611fde93bfSAndy Fiddaman 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
19621fde93bfSAndy Fiddaman 		    &rflags);
19631fde93bfSAndy Fiddaman 		if (error)
19641fde93bfSAndy Fiddaman 			return (error);
19651fde93bfSAndy Fiddaman 
19661fde93bfSAndy Fiddaman 		rflags &= ~RFLAGS_STATUS_BITS;
19671fde93bfSAndy Fiddaman 		rflags |= rflags2 & RFLAGS_STATUS_BITS;
19681fde93bfSAndy Fiddaman 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
19691fde93bfSAndy Fiddaman 		    rflags, 8);
19701fde93bfSAndy Fiddaman 
19711fde93bfSAndy Fiddaman 		DTRACE_PROBE2(vie__imul__rflags,
19721fde93bfSAndy Fiddaman 		    uint64_t, rflags, uint64_t, rflags2);
19731fde93bfSAndy Fiddaman 	}
19741fde93bfSAndy Fiddaman 
19751fde93bfSAndy Fiddaman 	return (error);
19761fde93bfSAndy Fiddaman }
19771fde93bfSAndy Fiddaman 
19781fde93bfSAndy Fiddaman static int
vie_emulate_stack_op(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)19797c8c0b82SPatrick Mooney vie_emulate_stack_op(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
19807c8c0b82SPatrick Mooney {
19817c8c0b82SPatrick Mooney 	struct vm_copyinfo copyinfo[2];
19827c8c0b82SPatrick Mooney 	struct seg_desc ss_desc;
19837c8c0b82SPatrick Mooney 	uint64_t cr0, rflags, rsp, stack_gla, val;
19847c8c0b82SPatrick Mooney 	int error, fault, size, stackaddrsize, pushop;
19857c8c0b82SPatrick Mooney 	struct vm_guest_paging *paging;
19867c8c0b82SPatrick Mooney 
19877c8c0b82SPatrick Mooney 	val = 0;
19887c8c0b82SPatrick Mooney 	size = vie->opsize;
19897c8c0b82SPatrick Mooney 	pushop = (vie->op.op_type == VIE_OP_TYPE_PUSH) ? 1 : 0;
19907c8c0b82SPatrick Mooney 	paging = &vie->paging;
19917c8c0b82SPatrick Mooney 
19927c8c0b82SPatrick Mooney 	/*
19937c8c0b82SPatrick Mooney 	 * From "Address-Size Attributes for Stack Accesses", Intel SDL, Vol 1
19947c8c0b82SPatrick Mooney 	 */
19957c8c0b82SPatrick Mooney 	if (paging->cpu_mode == CPU_MODE_REAL) {
19967c8c0b82SPatrick Mooney 		stackaddrsize = 2;
19977c8c0b82SPatrick Mooney 	} else if (paging->cpu_mode == CPU_MODE_64BIT) {
19987c8c0b82SPatrick Mooney 		/*
19997c8c0b82SPatrick Mooney 		 * "Stack Manipulation Instructions in 64-bit Mode", SDM, Vol 3
20007c8c0b82SPatrick Mooney 		 * - Stack pointer size is always 64-bits.
20017c8c0b82SPatrick Mooney 		 * - PUSH/POP of 32-bit values is not possible in 64-bit mode.
20027c8c0b82SPatrick Mooney 		 * - 16-bit PUSH/POP is supported by using the operand size
20037c8c0b82SPatrick Mooney 		 *   override prefix (66H).
20047c8c0b82SPatrick Mooney 		 */
20057c8c0b82SPatrick Mooney 		stackaddrsize = 8;
20067c8c0b82SPatrick Mooney 		size = vie->opsize_override ? 2 : 8;
20077c8c0b82SPatrick Mooney 	} else {
20087c8c0b82SPatrick Mooney 		/*
20097c8c0b82SPatrick Mooney 		 * In protected or compatibility mode the 'B' flag in the
20107c8c0b82SPatrick Mooney 		 * stack-segment descriptor determines the size of the
20117c8c0b82SPatrick Mooney 		 * stack pointer.
20127c8c0b82SPatrick Mooney 		 */
20137c8c0b82SPatrick Mooney 		error = vm_get_seg_desc(vm, vcpuid, VM_REG_GUEST_SS, &ss_desc);
20147c8c0b82SPatrick Mooney 		KASSERT(error == 0, ("%s: error %d getting SS descriptor",
20157c8c0b82SPatrick Mooney 		    __func__, error));
20167c8c0b82SPatrick Mooney 		if (SEG_DESC_DEF32(ss_desc.access))
20177c8c0b82SPatrick Mooney 			stackaddrsize = 4;
20187c8c0b82SPatrick Mooney 		else
20197c8c0b82SPatrick Mooney 			stackaddrsize = 2;
20207c8c0b82SPatrick Mooney 	}
20217c8c0b82SPatrick Mooney 
20227c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &cr0);
20237c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting cr0", __func__, error));
20247c8c0b82SPatrick Mooney 
20257c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
20267c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
20277c8c0b82SPatrick Mooney 
20287c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RSP, &rsp);
20297c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rsp", __func__, error));
20307c8c0b82SPatrick Mooney 	if (pushop) {
20317c8c0b82SPatrick Mooney 		rsp -= size;
20327c8c0b82SPatrick Mooney 	}
20337c8c0b82SPatrick Mooney 
20347c8c0b82SPatrick Mooney 	if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS, &ss_desc,
20357c8c0b82SPatrick Mooney 	    rsp, size, stackaddrsize, pushop ? PROT_WRITE : PROT_READ,
20367c8c0b82SPatrick Mooney 	    &stack_gla)) {
20377c8c0b82SPatrick Mooney 		vm_inject_ss(vm, vcpuid, 0);
20387c8c0b82SPatrick Mooney 		return (0);
20397c8c0b82SPatrick Mooney 	}
20407c8c0b82SPatrick Mooney 
20417c8c0b82SPatrick Mooney 	if (vie_canonical_check(paging->cpu_mode, stack_gla)) {
20427c8c0b82SPatrick Mooney 		vm_inject_ss(vm, vcpuid, 0);
20437c8c0b82SPatrick Mooney 		return (0);
20447c8c0b82SPatrick Mooney 	}
20457c8c0b82SPatrick Mooney 
20467c8c0b82SPatrick Mooney 	if (vie_alignment_check(paging->cpl, size, cr0, rflags, stack_gla)) {
20477c8c0b82SPatrick Mooney 		vm_inject_ac(vm, vcpuid, 0);
20487c8c0b82SPatrick Mooney 		return (0);
20497c8c0b82SPatrick Mooney 	}
20507c8c0b82SPatrick Mooney 
20517c8c0b82SPatrick Mooney 	error = vm_copy_setup(vm, vcpuid, paging, stack_gla, size,
20527c8c0b82SPatrick Mooney 	    pushop ? PROT_WRITE : PROT_READ, copyinfo, nitems(copyinfo),
20537c8c0b82SPatrick Mooney 	    &fault);
20547c8c0b82SPatrick Mooney 	if (error || fault)
20557c8c0b82SPatrick Mooney 		return (error);
20567c8c0b82SPatrick Mooney 
20577c8c0b82SPatrick Mooney 	if (pushop) {
20587c8c0b82SPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
20597c8c0b82SPatrick Mooney 		if (error == 0)
20607c8c0b82SPatrick Mooney 			vm_copyout(vm, vcpuid, &val, copyinfo, size);
20617c8c0b82SPatrick Mooney 	} else {
20627c8c0b82SPatrick Mooney 		vm_copyin(vm, vcpuid, copyinfo, &val, size);
20637c8c0b82SPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
20647c8c0b82SPatrick Mooney 		rsp += size;
20657c8c0b82SPatrick Mooney 	}
20667c8c0b82SPatrick Mooney 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
20677c8c0b82SPatrick Mooney 
20687c8c0b82SPatrick Mooney 	if (error == 0) {
20697c8c0b82SPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RSP, rsp,
20707c8c0b82SPatrick Mooney 		    stackaddrsize);
20717c8c0b82SPatrick Mooney 		KASSERT(error == 0, ("error %d updating rsp", error));
20727c8c0b82SPatrick Mooney 	}
20737c8c0b82SPatrick Mooney 	return (error);
20747c8c0b82SPatrick Mooney }
20757c8c0b82SPatrick Mooney 
20767c8c0b82SPatrick Mooney static int
vie_emulate_push(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)20777c8c0b82SPatrick Mooney vie_emulate_push(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
20787c8c0b82SPatrick Mooney {
20797c8c0b82SPatrick Mooney 	int error;
20807c8c0b82SPatrick Mooney 
20817c8c0b82SPatrick Mooney 	/*
20827c8c0b82SPatrick Mooney 	 * Table A-6, "Opcode Extensions", Intel SDM, Vol 2.
20837c8c0b82SPatrick Mooney 	 *
20847c8c0b82SPatrick Mooney 	 * PUSH is part of the group 5 extended opcodes and is identified
20857c8c0b82SPatrick Mooney 	 * by ModRM:reg = b110.
20867c8c0b82SPatrick Mooney 	 */
20877c8c0b82SPatrick Mooney 	if ((vie->reg & 7) != 6)
20887c8c0b82SPatrick Mooney 		return (EINVAL);
20897c8c0b82SPatrick Mooney 
20907c8c0b82SPatrick Mooney 	error = vie_emulate_stack_op(vie, vm, vcpuid, gpa);
20917c8c0b82SPatrick Mooney 	return (error);
20927c8c0b82SPatrick Mooney }
20937c8c0b82SPatrick Mooney 
20947c8c0b82SPatrick Mooney static int
vie_emulate_pop(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)20957c8c0b82SPatrick Mooney vie_emulate_pop(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
20967c8c0b82SPatrick Mooney {
20977c8c0b82SPatrick Mooney 	int error;
20987c8c0b82SPatrick Mooney 
20997c8c0b82SPatrick Mooney 	/*
21007c8c0b82SPatrick Mooney 	 * Table A-6, "Opcode Extensions", Intel SDM, Vol 2.
21017c8c0b82SPatrick Mooney 	 *
21027c8c0b82SPatrick Mooney 	 * POP is part of the group 1A extended opcodes and is identified
21037c8c0b82SPatrick Mooney 	 * by ModRM:reg = b000.
21047c8c0b82SPatrick Mooney 	 */
21057c8c0b82SPatrick Mooney 	if ((vie->reg & 7) != 0)
21067c8c0b82SPatrick Mooney 		return (EINVAL);
21077c8c0b82SPatrick Mooney 
21087c8c0b82SPatrick Mooney 	error = vie_emulate_stack_op(vie, vm, vcpuid, gpa);
21097c8c0b82SPatrick Mooney 	return (error);
21107c8c0b82SPatrick Mooney }
21117c8c0b82SPatrick Mooney 
21127c8c0b82SPatrick Mooney static int
vie_emulate_group1(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21137c8c0b82SPatrick Mooney vie_emulate_group1(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
21147c8c0b82SPatrick Mooney {
21157c8c0b82SPatrick Mooney 	int error;
21167c8c0b82SPatrick Mooney 
21177c8c0b82SPatrick Mooney 	switch (vie->reg & 7) {
21187c8c0b82SPatrick Mooney 	case 0x1:	/* OR */
21197c8c0b82SPatrick Mooney 		error = vie_emulate_or(vie, vm, vcpuid, gpa);
21207c8c0b82SPatrick Mooney 		break;
21217c8c0b82SPatrick Mooney 	case 0x4:	/* AND */
21227c8c0b82SPatrick Mooney 		error = vie_emulate_and(vie, vm, vcpuid, gpa);
21237c8c0b82SPatrick Mooney 		break;
21247c8c0b82SPatrick Mooney 	case 0x7:	/* CMP */
21257c8c0b82SPatrick Mooney 		error = vie_emulate_cmp(vie, vm, vcpuid, gpa);
21267c8c0b82SPatrick Mooney 		break;
21277c8c0b82SPatrick Mooney 	default:
21287c8c0b82SPatrick Mooney 		error = EINVAL;
21297c8c0b82SPatrick Mooney 		break;
21307c8c0b82SPatrick Mooney 	}
21317c8c0b82SPatrick Mooney 
21327c8c0b82SPatrick Mooney 	return (error);
21337c8c0b82SPatrick Mooney }
21347c8c0b82SPatrick Mooney 
21357c8c0b82SPatrick Mooney static int
vie_emulate_bittest(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21367c8c0b82SPatrick Mooney vie_emulate_bittest(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
21377c8c0b82SPatrick Mooney {
21387c8c0b82SPatrick Mooney 	uint64_t val, rflags;
21397c8c0b82SPatrick Mooney 	int error, bitmask, bitoff;
21407c8c0b82SPatrick Mooney 
21417c8c0b82SPatrick Mooney 	/*
21427c8c0b82SPatrick Mooney 	 * 0F BA is a Group 8 extended opcode.
21437c8c0b82SPatrick Mooney 	 *
21447c8c0b82SPatrick Mooney 	 * Currently we only emulate the 'Bit Test' instruction which is
21457c8c0b82SPatrick Mooney 	 * identified by a ModR/M:reg encoding of 100b.
21467c8c0b82SPatrick Mooney 	 */
21477c8c0b82SPatrick Mooney 	if ((vie->reg & 7) != 4)
21487c8c0b82SPatrick Mooney 		return (EINVAL);
21497c8c0b82SPatrick Mooney 
21507c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
21517c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
21527c8c0b82SPatrick Mooney 
21537c8c0b82SPatrick Mooney 	error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, vie->opsize);
21547c8c0b82SPatrick Mooney 	if (error)
21557c8c0b82SPatrick Mooney 		return (error);
21567c8c0b82SPatrick Mooney 
21577c8c0b82SPatrick Mooney 	/*
21587c8c0b82SPatrick Mooney 	 * Intel SDM, Vol 2, Table 3-2:
21597c8c0b82SPatrick Mooney 	 * "Range of Bit Positions Specified by Bit Offset Operands"
21607c8c0b82SPatrick Mooney 	 */
21617c8c0b82SPatrick Mooney 	bitmask = vie->opsize * 8 - 1;
21627c8c0b82SPatrick Mooney 	bitoff = vie->immediate & bitmask;
21637c8c0b82SPatrick Mooney 
21647c8c0b82SPatrick Mooney 	/* Copy the bit into the Carry flag in %rflags */
21657c8c0b82SPatrick Mooney 	if (val & (1UL << bitoff))
21667c8c0b82SPatrick Mooney 		rflags |= PSL_C;
21677c8c0b82SPatrick Mooney 	else
21687c8c0b82SPatrick Mooney 		rflags &= ~PSL_C;
21697c8c0b82SPatrick Mooney 
21707c8c0b82SPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
21717c8c0b82SPatrick Mooney 	KASSERT(error == 0, ("%s: error %d updating rflags", __func__, error));
21727c8c0b82SPatrick Mooney 
21737c8c0b82SPatrick Mooney 	return (0);
21747c8c0b82SPatrick Mooney }
21757c8c0b82SPatrick Mooney 
21767c8c0b82SPatrick Mooney static int
vie_emulate_twob_group15(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21777c8c0b82SPatrick Mooney vie_emulate_twob_group15(struct vie *vie, struct vm *vm, int vcpuid,
21787c8c0b82SPatrick Mooney     uint64_t gpa)
21797c8c0b82SPatrick Mooney {
21807c8c0b82SPatrick Mooney 	int error;
21817c8c0b82SPatrick Mooney 	uint64_t buf;
21827c8c0b82SPatrick Mooney 
21837c8c0b82SPatrick Mooney 	switch (vie->reg & 7) {
21847c8c0b82SPatrick Mooney 	case 0x7:	/* CLFLUSH, CLFLUSHOPT, and SFENCE */
21857c8c0b82SPatrick Mooney 		if (vie->mod == 0x3) {
21867c8c0b82SPatrick Mooney 			/*
21877c8c0b82SPatrick Mooney 			 * SFENCE.  Ignore it, VM exit provides enough
21887c8c0b82SPatrick Mooney 			 * barriers on its own.
21897c8c0b82SPatrick Mooney 			 */
21907c8c0b82SPatrick Mooney 			error = 0;
21917c8c0b82SPatrick Mooney 		} else {
21927c8c0b82SPatrick Mooney 			/*
21937c8c0b82SPatrick Mooney 			 * CLFLUSH, CLFLUSHOPT.  Only check for access
21947c8c0b82SPatrick Mooney 			 * rights.
21957c8c0b82SPatrick Mooney 			 */
21967c8c0b82SPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, gpa, &buf, 1);
21977c8c0b82SPatrick Mooney 		}
21987c8c0b82SPatrick Mooney 		break;
21997c8c0b82SPatrick Mooney 	default:
22007c8c0b82SPatrick Mooney 		error = EINVAL;
22017c8c0b82SPatrick Mooney 		break;
22027c8c0b82SPatrick Mooney 	}
22037c8c0b82SPatrick Mooney 
22047c8c0b82SPatrick Mooney 	return (error);
22057c8c0b82SPatrick Mooney }
22067c8c0b82SPatrick Mooney 
22077c8c0b82SPatrick Mooney static int
vie_emulate_clts(struct vie * vie,struct vm * vm,int vcpuid)22087c8c0b82SPatrick Mooney vie_emulate_clts(struct vie *vie, struct vm *vm, int vcpuid)
22097c8c0b82SPatrick Mooney {
22107c8c0b82SPatrick Mooney 	uint64_t val;
2211374973c2SToomas Soome 	int error __maybe_unused;
22127c8c0b82SPatrick Mooney 
22137c8c0b82SPatrick Mooney 	if (vie->paging.cpl != 0) {
22147c8c0b82SPatrick Mooney 		vm_inject_gp(vm, vcpuid);
22157c8c0b82SPatrick Mooney 		vie->num_processed = 0;
22167c8c0b82SPatrick Mooney 		return (0);
22177c8c0b82SPatrick Mooney 	}
22187c8c0b82SPatrick Mooney 
22197c8c0b82SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &val);
22207c8c0b82SPatrick Mooney 	ASSERT(error == 0);
22217c8c0b82SPatrick Mooney 
22227c8c0b82SPatrick Mooney 	/* Clear %cr0.TS */
22237c8c0b82SPatrick Mooney 	val &= ~CR0_TS;
22247c8c0b82SPatrick Mooney 
22257c8c0b82SPatrick Mooney 	error = vm_set_register(vm, vcpuid, VM_REG_GUEST_CR0, val);
22267c8c0b82SPatrick Mooney 	ASSERT(error == 0);
22277c8c0b82SPatrick Mooney 
22287c8c0b82SPatrick Mooney 	return (0);
22297c8c0b82SPatrick Mooney }
22307c8c0b82SPatrick Mooney 
22317c8c0b82SPatrick Mooney static int
vie_mmio_read(struct vie * vie,struct vm * vm,int cpuid,uint64_t gpa,uint64_t * rval,int bytes)22327c8c0b82SPatrick Mooney vie_mmio_read(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa,
22337c8c0b82SPatrick Mooney     uint64_t *rval, int bytes)
22347c8c0b82SPatrick Mooney {
22357c8c0b82SPatrick Mooney 	int err;
22367c8c0b82SPatrick Mooney 
22377c8c0b82SPatrick Mooney 	if (vie->mmio_req_read.state == VR_DONE) {
22387c8c0b82SPatrick Mooney 		ASSERT(vie->mmio_req_read.bytes == bytes);
22397c8c0b82SPatrick Mooney 		ASSERT(vie->mmio_req_read.gpa == gpa);
22407c8c0b82SPatrick Mooney 
22417c8c0b82SPatrick Mooney 		*rval = vie->mmio_req_read.data;
22427c8c0b82SPatrick Mooney 		return (0);
22437c8c0b82SPatrick Mooney 	}
22447c8c0b82SPatrick Mooney 
22457c8c0b82SPatrick Mooney 	err = vm_service_mmio_read(vm, cpuid, gpa, rval, bytes);
22467c8c0b82SPatrick Mooney 	if (err == 0) {
22477c8c0b82SPatrick Mooney 		/*
22487c8c0b82SPatrick Mooney 		 * A successful read from an in-kernel-emulated device may come
22497c8c0b82SPatrick Mooney 		 * with side effects, so stash the result in case it's used for
22507c8c0b82SPatrick Mooney 		 * an instruction which subsequently needs to issue an MMIO
22517c8c0b82SPatrick Mooney 		 * write to userspace.
22527c8c0b82SPatrick Mooney 		 */
22537c8c0b82SPatrick Mooney 		ASSERT(vie->mmio_req_read.state == VR_NONE);
22547c8c0b82SPatrick Mooney 
22557c8c0b82SPatrick Mooney 		vie->mmio_req_read.bytes = bytes;
22567c8c0b82SPatrick Mooney 		vie->mmio_req_read.gpa = gpa;
22577c8c0b82SPatrick Mooney 		vie->mmio_req_read.data = *rval;
22587c8c0b82SPatrick Mooney 		vie->mmio_req_read.state = VR_DONE;
22597c8c0b82SPatrick Mooney 
22607c8c0b82SPatrick Mooney 	} else if (err == ESRCH) {
22617c8c0b82SPatrick Mooney 		/* Hope that userspace emulation can fulfill this read */
22627c8c0b82SPatrick Mooney 		vie->mmio_req_read.bytes = bytes;
22637c8c0b82SPatrick Mooney 		vie->mmio_req_read.gpa = gpa;
22647c8c0b82SPatrick Mooney 		vie->mmio_req_read.state = VR_PENDING;
22657c8c0b82SPatrick Mooney 		vie->status |= VIES_PENDING_MMIO;
22667c8c0b82SPatrick Mooney 	} else if (err < 0) {
22677c8c0b82SPatrick Mooney 		/*
22687c8c0b82SPatrick Mooney 		 * The MMIO read failed in such a way that fallback to handling
22697c8c0b82SPatrick Mooney 		 * in userspace is required.
22707c8c0b82SPatrick Mooney 		 */
22717c8c0b82SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
22727c8c0b82SPatrick Mooney 	}
22737c8c0b82SPatrick Mooney 	return (err);
22747c8c0b82SPatrick Mooney }
22757c8c0b82SPatrick Mooney 
22767c8c0b82SPatrick Mooney static int
vie_mmio_write(struct vie * vie,struct vm * vm,int cpuid,uint64_t gpa,uint64_t wval,int bytes)22777c8c0b82SPatrick Mooney vie_mmio_write(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa,
22787c8c0b82SPatrick Mooney     uint64_t wval, int bytes)
22797c8c0b82SPatrick Mooney {
22807c8c0b82SPatrick Mooney 	int err;
22817c8c0b82SPatrick Mooney 
22827c8c0b82SPatrick Mooney 	if (vie->mmio_req_write.state == VR_DONE) {
22837c8c0b82SPatrick Mooney 		ASSERT(vie->mmio_req_write.bytes == bytes);
22847c8c0b82SPatrick Mooney 		ASSERT(vie->mmio_req_write.gpa == gpa);
22857c8c0b82SPatrick Mooney 
22867c8c0b82SPatrick Mooney 		return (0);
22877c8c0b82SPatrick Mooney 	}
22887c8c0b82SPatrick Mooney 
22897c8c0b82SPatrick Mooney 	err = vm_service_mmio_write(vm, cpuid, gpa, wval, bytes);
22907c8c0b82SPatrick Mooney 	if (err == 0) {
22917c8c0b82SPatrick Mooney 		/*
22927c8c0b82SPatrick Mooney 		 * A successful write to an in-kernel-emulated device probably
22937c8c0b82SPatrick Mooney 		 * results in side effects, so stash the fact that such a write
22947c8c0b82SPatrick Mooney 		 * succeeded in case the operation requires other work.
22957c8c0b82SPatrick Mooney 		 */
22967c8c0b82SPatrick Mooney 		vie->mmio_req_write.bytes = bytes;
22977c8c0b82SPatrick Mooney 		vie->mmio_req_write.gpa = gpa;
22987c8c0b82SPatrick Mooney 		vie->mmio_req_write.data = wval;
22997c8c0b82SPatrick Mooney 		vie->mmio_req_write.state = VR_DONE;
23007c8c0b82SPatrick Mooney 	} else if (err == ESRCH) {
23017c8c0b82SPatrick Mooney 		/* Hope that userspace emulation can fulfill this write */
23027c8c0b82SPatrick Mooney 		vie->mmio_req_write.bytes = bytes;
23037c8c0b82SPatrick Mooney 		vie->mmio_req_write.gpa = gpa;
23047c8c0b82SPatrick Mooney 		vie->mmio_req_write.data = wval;
23057c8c0b82SPatrick Mooney 		vie->mmio_req_write.state = VR_PENDING;
23067c8c0b82SPatrick Mooney 		vie->status |= VIES_PENDING_MMIO;
23077c8c0b82SPatrick Mooney 	} else if (err < 0) {
23087c8c0b82SPatrick Mooney 		/*
23097c8c0b82SPatrick Mooney 		 * The MMIO write failed in such a way that fallback to handling
23107c8c0b82SPatrick Mooney 		 * in userspace is required.
23117c8c0b82SPatrick Mooney 		 */
23127c8c0b82SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
23137c8c0b82SPatrick Mooney 	}
23147c8c0b82SPatrick Mooney 	return (err);
23157c8c0b82SPatrick Mooney }
23167c8c0b82SPatrick Mooney 
23177c8c0b82SPatrick Mooney int
vie_emulate_mmio(struct vie * vie,struct vm * vm,int vcpuid)23187c8c0b82SPatrick Mooney vie_emulate_mmio(struct vie *vie, struct vm *vm, int vcpuid)
23197c8c0b82SPatrick Mooney {
23207c8c0b82SPatrick Mooney 	int error;
23217c8c0b82SPatrick Mooney 	uint64_t gpa;
23227c8c0b82SPatrick Mooney 
23237c8c0b82SPatrick Mooney 	if ((vie->status & (VIES_INST_DECODE | VIES_MMIO)) !=
23247c8c0b82SPatrick Mooney 	    (VIES_INST_DECODE | VIES_MMIO)) {
23257c8c0b82SPatrick Mooney 		return (EINVAL);
23267c8c0b82SPatrick Mooney 	}
23277c8c0b82SPatrick Mooney 
23287c8c0b82SPatrick Mooney 	gpa = vie->mmio_gpa;
23297c8c0b82SPatrick Mooney 
23307c8c0b82SPatrick Mooney 	switch (vie->op.op_type) {
23317c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_GROUP1:
23327c8c0b82SPatrick Mooney 		error = vie_emulate_group1(vie, vm, vcpuid, gpa);
23337c8c0b82SPatrick Mooney 		break;
23347c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_POP:
23357c8c0b82SPatrick Mooney 		error = vie_emulate_pop(vie, vm, vcpuid, gpa);
23367c8c0b82SPatrick Mooney 		break;
23377c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_PUSH:
23387c8c0b82SPatrick Mooney 		error = vie_emulate_push(vie, vm, vcpuid, gpa);
23397c8c0b82SPatrick Mooney 		break;
23407c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_CMP:
23417c8c0b82SPatrick Mooney 		error = vie_emulate_cmp(vie, vm, vcpuid, gpa);
23427c8c0b82SPatrick Mooney 		break;
23437c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_MOV:
23447c8c0b82SPatrick Mooney 		error = vie_emulate_mov(vie, vm, vcpuid, gpa);
23457c8c0b82SPatrick Mooney 		break;
23467c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_MOVSX:
23477c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_MOVZX:
23487c8c0b82SPatrick Mooney 		error = vie_emulate_movx(vie, vm, vcpuid, gpa);
23497c8c0b82SPatrick Mooney 		break;
23507c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_MOVS:
23517c8c0b82SPatrick Mooney 		error = vie_emulate_movs(vie, vm, vcpuid, gpa);
23527c8c0b82SPatrick Mooney 		break;
23537c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_STOS:
23547c8c0b82SPatrick Mooney 		error = vie_emulate_stos(vie, vm, vcpuid, gpa);
23557c8c0b82SPatrick Mooney 		break;
23567c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_AND:
23577c8c0b82SPatrick Mooney 		error = vie_emulate_and(vie, vm, vcpuid, gpa);
23587c8c0b82SPatrick Mooney 		break;
23597c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_OR:
23607c8c0b82SPatrick Mooney 		error = vie_emulate_or(vie, vm, vcpuid, gpa);
23617c8c0b82SPatrick Mooney 		break;
23627c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_SUB:
23637c8c0b82SPatrick Mooney 		error = vie_emulate_sub(vie, vm, vcpuid, gpa);
23647c8c0b82SPatrick Mooney 		break;
23657c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_BITTEST:
23667c8c0b82SPatrick Mooney 		error = vie_emulate_bittest(vie, vm, vcpuid, gpa);
23677c8c0b82SPatrick Mooney 		break;
23687c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_TWOB_GRP15:
23697c8c0b82SPatrick Mooney 		error = vie_emulate_twob_group15(vie, vm, vcpuid, gpa);
23707c8c0b82SPatrick Mooney 		break;
23717c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_ADD:
23727c8c0b82SPatrick Mooney 		error = vie_emulate_add(vie, vm, vcpuid, gpa);
23737c8c0b82SPatrick Mooney 		break;
23747c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_TEST:
23757c8c0b82SPatrick Mooney 		error = vie_emulate_test(vie, vm, vcpuid, gpa);
23767c8c0b82SPatrick Mooney 		break;
23777c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_BEXTR:
23787c8c0b82SPatrick Mooney 		error = vie_emulate_bextr(vie, vm, vcpuid, gpa);
23797c8c0b82SPatrick Mooney 		break;
23801fde93bfSAndy Fiddaman 	case VIE_OP_TYPE_MUL:
23811fde93bfSAndy Fiddaman 		error = vie_emulate_mul(vie, vm, vcpuid, gpa);
23821fde93bfSAndy Fiddaman 		break;
23837c8c0b82SPatrick Mooney 	default:
23847c8c0b82SPatrick Mooney 		error = EINVAL;
23857c8c0b82SPatrick Mooney 		break;
23867c8c0b82SPatrick Mooney 	}
23877c8c0b82SPatrick Mooney 
23887c8c0b82SPatrick Mooney 	if (error == ESRCH) {
23897c8c0b82SPatrick Mooney 		/* Return to userspace with the mmio request */
23907c8c0b82SPatrick Mooney 		return (-1);
23917c8c0b82SPatrick Mooney 	}
23927c8c0b82SPatrick Mooney 
23937c8c0b82SPatrick Mooney 	return (error);
23947c8c0b82SPatrick Mooney }
23957c8c0b82SPatrick Mooney 
23967c8c0b82SPatrick Mooney static int
vie_emulate_inout_port(struct vie * vie,struct vm * vm,int vcpuid,uint32_t * eax)23977c8c0b82SPatrick Mooney vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid,
23987c8c0b82SPatrick Mooney     uint32_t *eax)
23997c8c0b82SPatrick Mooney {
24007c8c0b82SPatrick Mooney 	uint32_t mask, val;
24017c8c0b82SPatrick Mooney 	bool in;
24027c8c0b82SPatrick Mooney 	int err;
24037c8c0b82SPatrick Mooney 
24047c8c0b82SPatrick Mooney 	mask = vie_size2mask(vie->inout.bytes);
24057c8c0b82SPatrick Mooney 	in = (vie->inout.flags & INOUT_IN) != 0;
24067c8c0b82SPatrick Mooney 
24077c8c0b82SPatrick Mooney 	if (!in) {
24087c8c0b82SPatrick Mooney 		val = *eax & mask;
24097c8c0b82SPatrick Mooney 	}
24107c8c0b82SPatrick Mooney 
24117c8c0b82SPatrick Mooney 	if (vie->inout_req_state != VR_DONE) {
24127c8c0b82SPatrick Mooney 		err = vm_ioport_access(vm, vcpuid, in, vie->inout.port,
24137c8c0b82SPatrick Mooney 		    vie->inout.bytes, &val);
24147c8c0b82SPatrick Mooney 		val &= mask;
24157c8c0b82SPatrick Mooney 	} else {
24167c8c0b82SPatrick Mooney 		/*
24177c8c0b82SPatrick Mooney 		 * This port access was handled in userspace and the result was
24187c8c0b82SPatrick Mooney 		 * injected in to be handled now.
24197c8c0b82SPatrick Mooney 		 */
24207c8c0b82SPatrick Mooney 		val = vie->inout_req_val & mask;
24217c8c0b82SPatrick Mooney 		vie->inout_req_state = VR_NONE;
24227c8c0b82SPatrick Mooney 		err = 0;
24237c8c0b82SPatrick Mooney 	}
24247c8c0b82SPatrick Mooney 
24257c8c0b82SPatrick Mooney 	if (err == ESRCH) {
24267c8c0b82SPatrick Mooney 		vie->status |= VIES_PENDING_INOUT;
24277c8c0b82SPatrick Mooney 		vie->inout_req_state = VR_PENDING;
24287c8c0b82SPatrick Mooney 		return (err);
24297c8c0b82SPatrick Mooney 	} else if (err != 0) {
24307c8c0b82SPatrick Mooney 		return (err);
24317c8c0b82SPatrick Mooney 	}
24327c8c0b82SPatrick Mooney 
24337c8c0b82SPatrick Mooney 	if (in) {
24347c8c0b82SPatrick Mooney 		*eax = (*eax & ~mask) | val;
24357c8c0b82SPatrick Mooney 	}
24367c8c0b82SPatrick Mooney 	return (0);
24377c8c0b82SPatrick Mooney }
24387c8c0b82SPatrick Mooney 
24397c8c0b82SPatrick Mooney static enum vm_reg_name
vie_inout_segname(const struct vie * vie)24407c8c0b82SPatrick Mooney vie_inout_segname(const struct vie *vie)
24417c8c0b82SPatrick Mooney {
24427c8c0b82SPatrick Mooney 	uint8_t segidx = vie->inout.segment;
24437c8c0b82SPatrick Mooney 	const enum vm_reg_name segmap[] = {
24447c8c0b82SPatrick Mooney 		VM_REG_GUEST_ES,
24457c8c0b82SPatrick Mooney 		VM_REG_GUEST_CS,
24467c8c0b82SPatrick Mooney 		VM_REG_GUEST_SS,
24477c8c0b82SPatrick Mooney 		VM_REG_GUEST_DS,
24487c8c0b82SPatrick Mooney 		VM_REG_GUEST_FS,
24497c8c0b82SPatrick Mooney 		VM_REG_GUEST_GS,
24507c8c0b82SPatrick Mooney 	};
24517c8c0b82SPatrick Mooney 	const uint8_t maxidx = (sizeof (segmap) / sizeof (segmap[0]));
24527c8c0b82SPatrick Mooney 
24537c8c0b82SPatrick Mooney 	if (segidx >= maxidx) {
24547c8c0b82SPatrick Mooney 		panic("unexpected segment index %u", segidx);
24557c8c0b82SPatrick Mooney 	}
24567c8c0b82SPatrick Mooney 	return (segmap[segidx]);
24577c8c0b82SPatrick Mooney }
24587c8c0b82SPatrick Mooney 
24597c8c0b82SPatrick Mooney static int
vie_emulate_inout_str(struct vie * vie,struct vm * vm,int vcpuid)24607c8c0b82SPatrick Mooney vie_emulate_inout_str(struct vie *vie, struct vm *vm, int vcpuid)
24617c8c0b82SPatrick Mooney {
24627c8c0b82SPatrick Mooney 	uint8_t bytes, addrsize;
24637c8c0b82SPatrick Mooney 	uint64_t index, count = 0, gla, rflags;
24647c8c0b82SPatrick Mooney 	int prot, err, fault;
24657c8c0b82SPatrick Mooney 	bool in, repeat;
24667c8c0b82SPatrick Mooney 	enum vm_reg_name seg_reg, idx_reg;
24677c8c0b82SPatrick Mooney 	struct vm_copyinfo copyinfo[2];
24687c8c0b82SPatrick Mooney 
24697c8c0b82SPatrick Mooney 	in = (vie->inout.flags & INOUT_IN) != 0;
24707c8c0b82SPatrick Mooney 	bytes = vie->inout.bytes;
24717c8c0b82SPatrick Mooney 	addrsize = vie->inout.addrsize;
24727c8c0b82SPatrick Mooney 	prot = in ? PROT_WRITE : PROT_READ;
24737c8c0b82SPatrick Mooney 
24747c8c0b82SPatrick Mooney 	ASSERT(bytes == 1 || bytes == 2 || bytes == 4);
24757c8c0b82SPatrick Mooney 	ASSERT(addrsize == 2 || addrsize == 4 || addrsize == 8);
24767c8c0b82SPatrick Mooney 
24777c8c0b82SPatrick Mooney 	idx_reg = (in) ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI;
24787c8c0b82SPatrick Mooney 	seg_reg = vie_inout_segname(vie);
24797c8c0b82SPatrick Mooney 	err = vm_get_register(vm, vcpuid, idx_reg, &index);
24807c8c0b82SPatrick Mooney 	ASSERT(err == 0);
24817c8c0b82SPatrick Mooney 	index = index & vie_size2mask(addrsize);
24827c8c0b82SPatrick Mooney 
24837c8c0b82SPatrick Mooney 	repeat = (vie->inout.flags & INOUT_REP) != 0;
24847c8c0b82SPatrick Mooney 
24857c8c0b82SPatrick Mooney 	/* Count register */
24867c8c0b82SPatrick Mooney 	if (repeat) {
24877c8c0b82SPatrick Mooney 		err = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &count);
24887c8c0b82SPatrick Mooney 		count &= vie_size2mask(addrsize);
24897c8c0b82SPatrick Mooney 
24907c8c0b82SPatrick Mooney 		if (count == 0) {
24917c8c0b82SPatrick Mooney 			/*
24927c8c0b82SPatrick Mooney 			 * If we were asked to emulate a REP INS/OUTS when the
24937c8c0b82SPatrick Mooney 			 * count register is zero, no further work is required.
24947c8c0b82SPatrick Mooney 			 */
24957c8c0b82SPatrick Mooney 			return (0);
24967c8c0b82SPatrick Mooney 		}
24977c8c0b82SPatrick Mooney 	} else {
24987c8c0b82SPatrick Mooney 		count = 1;
24997c8c0b82SPatrick Mooney 	}
25007c8c0b82SPatrick Mooney 
25017c8c0b82SPatrick Mooney 	gla = 0;
25027c8c0b82SPatrick Mooney 	if (vie_get_gla(vie, vm, vcpuid, bytes, addrsize, prot, seg_reg,
25037c8c0b82SPatrick Mooney 	    idx_reg, &gla) != 0) {
25047c8c0b82SPatrick Mooney 		/* vie_get_gla() already injected the appropriate fault */
25057c8c0b82SPatrick Mooney 		return (0);
25067c8c0b82SPatrick Mooney 	}
25077c8c0b82SPatrick Mooney 
25087c8c0b82SPatrick Mooney 	/*
25097c8c0b82SPatrick Mooney 	 * The INS/OUTS emulate currently assumes that the memory target resides
25107c8c0b82SPatrick Mooney 	 * within the guest system memory, rather than a device MMIO region.  If
25117c8c0b82SPatrick Mooney 	 * such a case becomes a necessity, that additional handling could be
25127c8c0b82SPatrick Mooney 	 * put in place.
25137c8c0b82SPatrick Mooney 	 */
25147c8c0b82SPatrick Mooney 	err = vm_copy_setup(vm, vcpuid, &vie->paging, gla, bytes, prot,
25157c8c0b82SPatrick Mooney 	    copyinfo, nitems(copyinfo), &fault);
25167c8c0b82SPatrick Mooney 
25177c8c0b82SPatrick Mooney 	if (err) {
25187c8c0b82SPatrick Mooney 		/* Unrecoverable error */
25197c8c0b82SPatrick Mooney 		return (err);
25207c8c0b82SPatrick Mooney 	} else if (fault) {
25217c8c0b82SPatrick Mooney 		/* Resume guest to handle fault */
25227c8c0b82SPatrick Mooney 		return (0);
25237c8c0b82SPatrick Mooney 	}
25247c8c0b82SPatrick Mooney 
25257c8c0b82SPatrick Mooney 	if (!in) {
25267c8c0b82SPatrick Mooney 		vm_copyin(vm, vcpuid, copyinfo, &vie->inout.eax, bytes);
25277c8c0b82SPatrick Mooney 	}
25287c8c0b82SPatrick Mooney 
25297c8c0b82SPatrick Mooney 	err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax);
25307c8c0b82SPatrick Mooney 
25317c8c0b82SPatrick Mooney 	if (err == 0 && in) {
25327c8c0b82SPatrick Mooney 		vm_copyout(vm, vcpuid, &vie->inout.eax, copyinfo, bytes);
25337c8c0b82SPatrick Mooney 	}
25347c8c0b82SPatrick Mooney 
25357c8c0b82SPatrick Mooney 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
25367c8c0b82SPatrick Mooney 
25377c8c0b82SPatrick Mooney 	if (err == 0) {
25387c8c0b82SPatrick Mooney 		err = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
25397c8c0b82SPatrick Mooney 		    &rflags);
25407c8c0b82SPatrick Mooney 		ASSERT(err == 0);
25417c8c0b82SPatrick Mooney 
25427c8c0b82SPatrick Mooney 		/* Update index */
25437c8c0b82SPatrick Mooney 		if (rflags & PSL_D) {
25447c8c0b82SPatrick Mooney 			index -= bytes;
25457c8c0b82SPatrick Mooney 		} else {
25467c8c0b82SPatrick Mooney 			index += bytes;
25477c8c0b82SPatrick Mooney 		}
25487c8c0b82SPatrick Mooney 
25497c8c0b82SPatrick Mooney 		/* Update index register */
25507c8c0b82SPatrick Mooney 		err = vie_update_register(vm, vcpuid, idx_reg, index, addrsize);
25517c8c0b82SPatrick Mooney 		ASSERT(err == 0);
25527c8c0b82SPatrick Mooney 
25537c8c0b82SPatrick Mooney 		/*
25547c8c0b82SPatrick Mooney 		 * Update count register only if the instruction had a repeat
25557c8c0b82SPatrick Mooney 		 * prefix.
25567c8c0b82SPatrick Mooney 		 */
25577c8c0b82SPatrick Mooney 		if ((vie->inout.flags & INOUT_REP) != 0) {
25587c8c0b82SPatrick Mooney 			count--;
25597c8c0b82SPatrick Mooney 			err = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
25607c8c0b82SPatrick Mooney 			    count, addrsize);
25617c8c0b82SPatrick Mooney 			ASSERT(err == 0);
25627c8c0b82SPatrick Mooney 
25637c8c0b82SPatrick Mooney 			if (count != 0) {
25647c8c0b82SPatrick Mooney 				return (vie_repeat(vie));
25657c8c0b82SPatrick Mooney 			}
25667c8c0b82SPatrick Mooney 		}
25677c8c0b82SPatrick Mooney 	}
25687c8c0b82SPatrick Mooney 
25697c8c0b82SPatrick Mooney 	return (err);
25707c8c0b82SPatrick Mooney }
25717c8c0b82SPatrick Mooney 
25727c8c0b82SPatrick Mooney int
vie_emulate_inout(struct vie * vie,struct vm * vm,int vcpuid)25737c8c0b82SPatrick Mooney vie_emulate_inout(struct vie *vie, struct vm *vm, int vcpuid)
25747c8c0b82SPatrick Mooney {
25757c8c0b82SPatrick Mooney 	int err = 0;
25767c8c0b82SPatrick Mooney 
25777c8c0b82SPatrick Mooney 	if ((vie->status & VIES_INOUT) == 0) {
25787c8c0b82SPatrick Mooney 		return (EINVAL);
25797c8c0b82SPatrick Mooney 	}
25807c8c0b82SPatrick Mooney 
25817c8c0b82SPatrick Mooney 	if ((vie->inout.flags & INOUT_STR) == 0) {
25827c8c0b82SPatrick Mooney 		/*
25837c8c0b82SPatrick Mooney 		 * For now, using the 'rep' prefixes with plain (non-string)
25847c8c0b82SPatrick Mooney 		 * in/out is not supported.
25857c8c0b82SPatrick Mooney 		 */
25867c8c0b82SPatrick Mooney 		if ((vie->inout.flags & INOUT_REP) != 0) {
25877c8c0b82SPatrick Mooney 			return (EINVAL);
25887c8c0b82SPatrick Mooney 		}
25897c8c0b82SPatrick Mooney 
25907c8c0b82SPatrick Mooney 		err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax);
25917c8c0b82SPatrick Mooney 		if (err == 0 && (vie->inout.flags & INOUT_IN) != 0) {
25927c8c0b82SPatrick Mooney 			/*
25937c8c0b82SPatrick Mooney 			 * With the inX access now a success, the result needs
25947c8c0b82SPatrick Mooney 			 * to be stored in the guest %rax.
25957c8c0b82SPatrick Mooney 			 */
25967c8c0b82SPatrick Mooney 			err = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX,
25977c8c0b82SPatrick Mooney 			    vie->inout.eax);
25987c8c0b82SPatrick Mooney 			VERIFY0(err);
25997c8c0b82SPatrick Mooney 		}
26007c8c0b82SPatrick Mooney 	} else {
26017c8c0b82SPatrick Mooney 		vie->status &= ~VIES_REPEAT;
26027c8c0b82SPatrick Mooney 		err = vie_emulate_inout_str(vie, vm, vcpuid);
26037c8c0b82SPatrick Mooney 
26047c8c0b82SPatrick Mooney 	}
26057c8c0b82SPatrick Mooney 	if (err < 0) {
26067c8c0b82SPatrick Mooney 		/*
26077c8c0b82SPatrick Mooney 		 * Access to an I/O port failed in such a way that fallback to
26087c8c0b82SPatrick Mooney 		 * handling in userspace is required.
26097c8c0b82SPatrick Mooney 		 */
26107c8c0b82SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
26117c8c0b82SPatrick Mooney 	} else if (err == ESRCH) {
26127c8c0b82SPatrick Mooney 		ASSERT(vie->status & VIES_PENDING_INOUT);
26137c8c0b82SPatrick Mooney 		/* Return to userspace with the in/out request */
26147c8c0b82SPatrick Mooney 		err = -1;
26157c8c0b82SPatrick Mooney 	}
26167c8c0b82SPatrick Mooney 
26177c8c0b82SPatrick Mooney 	return (err);
26187c8c0b82SPatrick Mooney }
26197c8c0b82SPatrick Mooney 
26207c8c0b82SPatrick Mooney int
vie_emulate_other(struct vie * vie,struct vm * vm,int vcpuid)26217c8c0b82SPatrick Mooney vie_emulate_other(struct vie *vie, struct vm *vm, int vcpuid)
26227c8c0b82SPatrick Mooney {
26237c8c0b82SPatrick Mooney 	int error;
26247c8c0b82SPatrick Mooney 
26257c8c0b82SPatrick Mooney 	if ((vie->status & (VIES_INST_DECODE | VIES_OTHER)) !=
26267c8c0b82SPatrick Mooney 	    (VIES_INST_DECODE | VIES_OTHER)) {
26277c8c0b82SPatrick Mooney 		return (EINVAL);
26287c8c0b82SPatrick Mooney 	}
26297c8c0b82SPatrick Mooney 
26307c8c0b82SPatrick Mooney 	switch (vie->op.op_type) {
26317c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_CLTS:
26327c8c0b82SPatrick Mooney 		error = vie_emulate_clts(vie, vm, vcpuid);
26337c8c0b82SPatrick Mooney 		break;
26347c8c0b82SPatrick Mooney 	case VIE_OP_TYPE_MOV_CR:
26357c8c0b82SPatrick Mooney 		error = vie_emulate_mov_cr(vie, vm, vcpuid);
26367c8c0b82SPatrick Mooney 		break;
26377c8c0b82SPatrick Mooney 	default:
26387c8c0b82SPatrick Mooney 		error = EINVAL;
26397c8c0b82SPatrick Mooney 		break;
26407c8c0b82SPatrick Mooney 	}
26417c8c0b82SPatrick Mooney 
26427c8c0b82SPatrick Mooney 	return (error);
26437c8c0b82SPatrick Mooney }
26447c8c0b82SPatrick Mooney 
26457c8c0b82SPatrick Mooney void
vie_reset(struct vie * vie)26467c8c0b82SPatrick Mooney vie_reset(struct vie *vie)
26477c8c0b82SPatrick Mooney {
26487c8c0b82SPatrick Mooney 	vie->status = 0;
26497c8c0b82SPatrick Mooney 	vie->num_processed = vie->num_valid = 0;
26507c8c0b82SPatrick Mooney }
26517c8c0b82SPatrick Mooney 
26527c8c0b82SPatrick Mooney void
vie_advance_pc(struct vie * vie,uint64_t * nextrip)26537c8c0b82SPatrick Mooney vie_advance_pc(struct vie *vie, uint64_t *nextrip)
26547c8c0b82SPatrick Mooney {
26557c8c0b82SPatrick Mooney 	VERIFY((vie->status & VIES_REPEAT) == 0);
26567c8c0b82SPatrick Mooney 
26577c8c0b82SPatrick Mooney 	*nextrip += vie->num_processed;
26587c8c0b82SPatrick Mooney 	vie_reset(vie);
26597c8c0b82SPatrick Mooney }
26607c8c0b82SPatrick Mooney 
26617c8c0b82SPatrick Mooney void
vie_exitinfo(const struct vie * vie,struct vm_exit * vme)26627c8c0b82SPatrick Mooney vie_exitinfo(const struct vie *vie, struct vm_exit *vme)
26637c8c0b82SPatrick Mooney {
26647c8c0b82SPatrick Mooney 	if (vie->status & VIES_USER_FALLBACK) {
26657c8c0b82SPatrick Mooney 		/*
26667c8c0b82SPatrick Mooney 		 * Despite the fact that the instruction was successfully
26677c8c0b82SPatrick Mooney 		 * decoded, some aspect of the emulation failed in such a way
26687c8c0b82SPatrick Mooney 		 * that it is left up to userspace to complete the operation.
26697c8c0b82SPatrick Mooney 		 */
26707c8c0b82SPatrick Mooney 		vie_fallback_exitinfo(vie, vme);
26717c8c0b82SPatrick Mooney 	} else if (vie->status & VIES_MMIO) {
26727c8c0b82SPatrick Mooney 		vme->exitcode = VM_EXITCODE_MMIO;
26737c8c0b82SPatrick Mooney 		if (vie->mmio_req_read.state == VR_PENDING) {
26747c8c0b82SPatrick Mooney 			vme->u.mmio.gpa = vie->mmio_req_read.gpa;
26757c8c0b82SPatrick Mooney 			vme->u.mmio.data = 0;
26767c8c0b82SPatrick Mooney 			vme->u.mmio.bytes = vie->mmio_req_read.bytes;
26777c8c0b82SPatrick Mooney 			vme->u.mmio.read = 1;
26787c8c0b82SPatrick Mooney 		} else if (vie->mmio_req_write.state == VR_PENDING) {
26797c8c0b82SPatrick Mooney 			vme->u.mmio.gpa = vie->mmio_req_write.gpa;
26807c8c0b82SPatrick Mooney 			vme->u.mmio.data = vie->mmio_req_write.data &
26817c8c0b82SPatrick Mooney 			    vie_size2mask(vie->mmio_req_write.bytes);
26827c8c0b82SPatrick Mooney 			vme->u.mmio.bytes = vie->mmio_req_write.bytes;
26837c8c0b82SPatrick Mooney 			vme->u.mmio.read = 0;
26847c8c0b82SPatrick Mooney 		} else {
26857c8c0b82SPatrick Mooney 			panic("bad pending MMIO state");
26867c8c0b82SPatrick Mooney 		}
26877c8c0b82SPatrick Mooney 	} else if (vie->status & VIES_INOUT) {
26887c8c0b82SPatrick Mooney 		vme->exitcode = VM_EXITCODE_INOUT;
26897c8c0b82SPatrick Mooney 		vme->u.inout.port = vie->inout.port;
26907c8c0b82SPatrick Mooney 		vme->u.inout.bytes = vie->inout.bytes;
26917c8c0b82SPatrick Mooney 		if ((vie->inout.flags & INOUT_IN) != 0) {
26927c8c0b82SPatrick Mooney 			vme->u.inout.flags = INOUT_IN;
26937c8c0b82SPatrick Mooney 			vme->u.inout.eax = 0;
26947c8c0b82SPatrick Mooney 		} else {
26957c8c0b82SPatrick Mooney 			vme->u.inout.flags = 0;
26967c8c0b82SPatrick Mooney 			vme->u.inout.eax = vie->inout.eax &
26977c8c0b82SPatrick Mooney 			    vie_size2mask(vie->inout.bytes);
26987c8c0b82SPatrick Mooney 		}
26997c8c0b82SPatrick Mooney 	} else {
27007c8c0b82SPatrick Mooney 		panic("no pending operation");
27017c8c0b82SPatrick Mooney 	}
27027c8c0b82SPatrick Mooney }
27037c8c0b82SPatrick Mooney 
27047c8c0b82SPatrick Mooney /*
27057c8c0b82SPatrick Mooney  * In the case of a decoding or verification failure, bailing out to userspace
27067c8c0b82SPatrick Mooney  * to do the instruction emulation is our only option for now.
27077c8c0b82SPatrick Mooney  */
27087c8c0b82SPatrick Mooney void
vie_fallback_exitinfo(const struct vie * vie,struct vm_exit * vme)27097c8c0b82SPatrick Mooney vie_fallback_exitinfo(const struct vie *vie, struct vm_exit *vme)
27107c8c0b82SPatrick Mooney {
27117c8c0b82SPatrick Mooney 	if ((vie->status & VIES_INST_FETCH) == 0) {
27127c8c0b82SPatrick Mooney 		bzero(&vme->u.inst_emul, sizeof (vme->u.inst_emul));
27137c8c0b82SPatrick Mooney 	} else {
27147c8c0b82SPatrick Mooney 		ASSERT(sizeof (vie->inst) == sizeof (vme->u.inst_emul.inst));
27157c8c0b82SPatrick Mooney 
27167c8c0b82SPatrick Mooney 		bcopy(vie->inst, vme->u.inst_emul.inst, sizeof (vie->inst));
27177c8c0b82SPatrick Mooney 		vme->u.inst_emul.num_valid = vie->num_valid;
27187c8c0b82SPatrick Mooney 	}
27197c8c0b82SPatrick Mooney 	vme->exitcode = VM_EXITCODE_INST_EMUL;
27207c8c0b82SPatrick Mooney }
27217c8c0b82SPatrick Mooney 
27227c8c0b82SPatrick Mooney void
vie_cs_info(const struct vie * vie,struct vm * vm,int vcpuid,uint64_t * cs_base,int * cs_d)27237c8c0b82SPatrick Mooney vie_cs_info(const struct vie *vie, struct vm *vm, int vcpuid, uint64_t *cs_base,
27247c8c0b82SPatrick Mooney     int *cs_d)
27257c8c0b82SPatrick Mooney {
27267c8c0b82SPatrick Mooney 	struct seg_desc cs_desc;
2727374973c2SToomas Soome 	int error __maybe_unused;
27287c8c0b82SPatrick Mooney 
27297c8c0b82SPatrick Mooney 	error = vm_get_seg_desc(vm, vcpuid, VM_REG_GUEST_CS, &cs_desc);
27307c8c0b82SPatrick Mooney 	ASSERT(error == 0);
27317c8c0b82SPatrick Mooney 
27327c8c0b82SPatrick Mooney 	/* Initialization required for the paging info to be populated */
27337c8c0b82SPatrick Mooney 	VERIFY(vie->status & VIES_INIT);
27347c8c0b82SPatrick Mooney 	switch (vie->paging.cpu_mode) {
27357c8c0b82SPatrick Mooney 	case CPU_MODE_REAL:
27367c8c0b82SPatrick Mooney 		*cs_base = cs_desc.base;
27377c8c0b82SPatrick Mooney 		*cs_d = 0;
27387c8c0b82SPatrick Mooney 		break;
27397c8c0b82SPatrick Mooney 	case CPU_MODE_PROTECTED:
27407c8c0b82SPatrick Mooney 	case CPU_MODE_COMPATIBILITY:
27417c8c0b82SPatrick Mooney 		*cs_base = cs_desc.base;
27427c8c0b82SPatrick Mooney 		*cs_d = SEG_DESC_DEF32(cs_desc.access) ? 1 : 0;
27437c8c0b82SPatrick Mooney 		break;
27447c8c0b82SPatrick Mooney 	default:
27457c8c0b82SPatrick Mooney 		*cs_base = 0;
27467c8c0b82SPatrick Mooney 		*cs_d = 0;
27477c8c0b82SPatrick Mooney 		break;
27487c8c0b82SPatrick Mooney 	}
27497c8c0b82SPatrick Mooney }
27507c8c0b82SPatrick Mooney 
27517c8c0b82SPatrick Mooney bool
vie_pending(const struct vie * vie)27527c8c0b82SPatrick Mooney vie_pending(const struct vie *vie)
27537c8c0b82SPatrick Mooney {
27547c8c0b82SPatrick Mooney 	/*
27557c8c0b82SPatrick Mooney 	 * These VIE status bits indicate conditions which must be addressed
27567c8c0b82SPatrick Mooney 	 * through either device IO fulfillment (with corresponding
27577c8c0b82SPatrick Mooney 	 * vie_fulfill_*()) or complete userspace emulation (followed by a
27587c8c0b82SPatrick Mooney 	 * vie_reset()).
27597c8c0b82SPatrick Mooney 	 */
27607c8c0b82SPatrick Mooney 	const enum vie_status of_interest =
27617c8c0b82SPatrick Mooney 	    VIES_PENDING_MMIO | VIES_PENDING_INOUT | VIES_USER_FALLBACK;
27627c8c0b82SPatrick Mooney 
27637c8c0b82SPatrick Mooney 	return ((vie->status & of_interest) != 0);
27647c8c0b82SPatrick Mooney }
27657c8c0b82SPatrick Mooney 
27667c8c0b82SPatrick Mooney bool
vie_needs_fetch(const struct vie * vie)27677c8c0b82SPatrick Mooney vie_needs_fetch(const struct vie *vie)
27687c8c0b82SPatrick Mooney {
27697c8c0b82SPatrick Mooney 	if (vie->status & VIES_INST_FETCH) {
27707c8c0b82SPatrick Mooney 		ASSERT(vie->num_valid != 0);
27717c8c0b82SPatrick Mooney 		return (false);
27727c8c0b82SPatrick Mooney 	}
27737c8c0b82SPatrick Mooney 	return (true);
27747c8c0b82SPatrick Mooney }
27757c8c0b82SPatrick Mooney 
27767c8c0b82SPatrick Mooney static int
vie_alignment_check(int cpl,int size,uint64_t cr0,uint64_t rf,uint64_t gla)27777c8c0b82SPatrick Mooney vie_alignment_check(int cpl, int size, uint64_t cr0, uint64_t rf, uint64_t gla)
27787c8c0b82SPatrick Mooney {
27797c8c0b82SPatrick Mooney 	KASSERT(size == 1 || size == 2 || size == 4 || size == 8,
27807c8c0b82SPatrick Mooney 	    ("%s: invalid size %d", __func__, size));
27817c8c0b82SPatrick Mooney 	KASSERT(cpl >= 0 && cpl <= 3, ("%s: invalid cpl %d", __func__, cpl));
27827c8c0b82SPatrick Mooney 
27837c8c0b82SPatrick Mooney 	if (cpl != 3 || (cr0 & CR0_AM) == 0 || (rf & PSL_AC) == 0)
27847c8c0b82SPatrick Mooney 		return (0);
27857c8c0b82SPatrick Mooney 
27867c8c0b82SPatrick Mooney 	return ((gla & (size - 1)) ? 1 : 0);
27877c8c0b82SPatrick Mooney }
27887c8c0b82SPatrick Mooney 
27897c8c0b82SPatrick Mooney static int
vie_canonical_check(enum vm_cpu_mode cpu_mode,uint64_t gla)27907c8c0b82SPatrick Mooney vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla)
27917c8c0b82SPatrick Mooney {
27927c8c0b82SPatrick Mooney 	uint64_t mask;
27937c8c0b82SPatrick Mooney 
27947c8c0b82SPatrick Mooney 	if (cpu_mode != CPU_MODE_64BIT)
27957c8c0b82SPatrick Mooney 		return (0);
27967c8c0b82SPatrick Mooney 
27977c8c0b82SPatrick Mooney 	/*
27987c8c0b82SPatrick Mooney 	 * The value of the bit 47 in the 'gla' should be replicated in the
27997c8c0b82SPatrick Mooney 	 * most significant 16 bits.
28007c8c0b82SPatrick Mooney 	 */
28017c8c0b82SPatrick Mooney 	mask = ~((1UL << 48) - 1);
28027c8c0b82SPatrick Mooney 	if (gla & (1UL << 47))
28037c8c0b82SPatrick Mooney 		return ((gla & mask) != mask);
28047c8c0b82SPatrick Mooney 	else
28057c8c0b82SPatrick Mooney 		return ((gla & mask) != 0);
28067c8c0b82SPatrick Mooney }
28077c8c0b82SPatrick Mooney 
28087c8c0b82SPatrick Mooney static uint64_t
vie_size2mask(int size)28097c8c0b82SPatrick Mooney vie_size2mask(int size)
28107c8c0b82SPatrick Mooney {
28117c8c0b82SPatrick Mooney 	KASSERT(size == 1 || size == 2 || size == 4 || size == 8,
28127c8c0b82SPatrick Mooney 	    ("vie_size2mask: invalid size %d", size));
28137c8c0b82SPatrick Mooney 	return (size2mask[size]);
28147c8c0b82SPatrick Mooney }
28157c8c0b82SPatrick Mooney 
28167c8c0b82SPatrick Mooney static int
vie_calculate_gla(enum vm_cpu_mode cpu_mode,enum vm_reg_name seg,struct seg_desc * desc,uint64_t offset,int length,int addrsize,int prot,uint64_t * gla)28177c8c0b82SPatrick Mooney vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg,
28187c8c0b82SPatrick Mooney     struct seg_desc *desc, uint64_t offset, int length, int addrsize,
28197c8c0b82SPatrick Mooney     int prot, uint64_t *gla)
28207c8c0b82SPatrick Mooney {
28217c8c0b82SPatrick Mooney 	uint64_t firstoff, low_limit, high_limit, segbase;
28227c8c0b82SPatrick Mooney 	int glasize, type;
28237c8c0b82SPatrick Mooney 
28247c8c0b82SPatrick Mooney 	KASSERT(seg >= VM_REG_GUEST_ES && seg <= VM_REG_GUEST_GS,
28257c8c0b82SPatrick Mooney 	    ("%s: invalid segment %d", __func__, seg));
28267c8c0b82SPatrick Mooney 	KASSERT(length == 1 || length == 2 || length == 4 || length == 8,
28277c8c0b82SPatrick Mooney 	    ("%s: invalid operand size %d", __func__, length));
28287c8c0b82SPatrick Mooney 	KASSERT((prot & ~(PROT_READ | PROT_WRITE)) == 0,
28297c8c0b82SPatrick Mooney 	    ("%s: invalid prot %x", __func__, prot));
28307c8c0b82SPatrick Mooney 
28317c8c0b82SPatrick Mooney 	firstoff = offset;
28327c8c0b82SPatrick Mooney 	if (cpu_mode == CPU_MODE_64BIT) {
28337c8c0b82SPatrick Mooney 		KASSERT(addrsize == 4 || addrsize == 8, ("%s: invalid address "
28347c8c0b82SPatrick Mooney 		    "size %d for cpu_mode %d", __func__, addrsize, cpu_mode));
28357c8c0b82SPatrick Mooney 		glasize = 8;
28367c8c0b82SPatrick Mooney 	} else {
28377c8c0b82SPatrick Mooney 		KASSERT(addrsize == 2 || addrsize == 4, ("%s: invalid address "
28387c8c0b82SPatrick Mooney 		    "size %d for cpu mode %d", __func__, addrsize, cpu_mode));
28397c8c0b82SPatrick Mooney 		glasize = 4;
28407c8c0b82SPatrick Mooney 		/*
28417c8c0b82SPatrick Mooney 		 * If the segment selector is loaded with a NULL selector
28427c8c0b82SPatrick Mooney 		 * then the descriptor is unusable and attempting to use
28437c8c0b82SPatrick Mooney 		 * it results in a #GP(0).
28447c8c0b82SPatrick Mooney 		 */
28457c8c0b82SPatrick Mooney 		if (SEG_DESC_UNUSABLE(desc->access))
28467c8c0b82SPatrick Mooney 			return (-1);
28477c8c0b82SPatrick Mooney 
28487c8c0b82SPatrick Mooney 		/*
28497c8c0b82SPatrick Mooney 		 * The processor generates a #NP exception when a segment
28507c8c0b82SPatrick Mooney 		 * register is loaded with a selector that points to a
28517c8c0b82SPatrick Mooney 		 * descriptor that is not present. If this was the case then
28527c8c0b82SPatrick Mooney 		 * it would have been checked before the VM-exit.
28537c8c0b82SPatrick Mooney 		 */
28547c8c0b82SPatrick Mooney 		KASSERT(SEG_DESC_PRESENT(desc->access),
28557c8c0b82SPatrick Mooney 		    ("segment %d not present: %x", seg, desc->access));
28567c8c0b82SPatrick Mooney 
28577c8c0b82SPatrick Mooney 		/*
28587c8c0b82SPatrick Mooney 		 * The descriptor type must indicate a code/data segment.
28597c8c0b82SPatrick Mooney 		 */
28607c8c0b82SPatrick Mooney 		type = SEG_DESC_TYPE(desc->access);
28617c8c0b82SPatrick Mooney 		KASSERT(type >= 16 && type <= 31, ("segment %d has invalid "
28627c8c0b82SPatrick Mooney 		    "descriptor type %x", seg, type));
28637c8c0b82SPatrick Mooney 
28647c8c0b82SPatrick Mooney 		if (prot & PROT_READ) {
28657c8c0b82SPatrick Mooney 			/* #GP on a read access to a exec-only code segment */
28667c8c0b82SPatrick Mooney 			if ((type & 0xA) == 0x8)
28677c8c0b82SPatrick Mooney 				return (-1);
28687c8c0b82SPatrick Mooney 		}
28697c8c0b82SPatrick Mooney 
28707c8c0b82SPatrick Mooney 		if (prot & PROT_WRITE) {
28717c8c0b82SPatrick Mooney 			/*
28727c8c0b82SPatrick Mooney 			 * #GP on a write access to a code segment or a
28737c8c0b82SPatrick Mooney 			 * read-only data segment.
28747c8c0b82SPatrick Mooney 			 */
28757c8c0b82SPatrick Mooney 			if (type & 0x8)			/* code segment */
28767c8c0b82SPatrick Mooney 				return (-1);
28777c8c0b82SPatrick Mooney 
28787c8c0b82SPatrick Mooney 			if ((type & 0xA) == 0)		/* read-only data seg */
28797c8c0b82SPatrick Mooney 				return (-1);
28807c8c0b82SPatrick Mooney 		}
28817c8c0b82SPatrick Mooney 
28827c8c0b82SPatrick Mooney 		/*
28837c8c0b82SPatrick Mooney 		 * 'desc->limit' is fully expanded taking granularity into
28847c8c0b82SPatrick Mooney 		 * account.
28857c8c0b82SPatrick Mooney 		 */
28867c8c0b82SPatrick Mooney 		if ((type & 0xC) == 0x4) {
28877c8c0b82SPatrick Mooney 			/* expand-down data segment */
28887c8c0b82SPatrick Mooney 			low_limit = desc->limit + 1;
28897c8c0b82SPatrick Mooney 			high_limit = SEG_DESC_DEF32(desc->access) ?
28907c8c0b82SPatrick Mooney 			    0xffffffff : 0xffff;
28917c8c0b82SPatrick Mooney 		} else {
28927c8c0b82SPatrick Mooney 			/* code segment or expand-up data segment */
28937c8c0b82SPatrick Mooney 			low_limit = 0;
28947c8c0b82SPatrick Mooney 			high_limit = desc->limit;
28957c8c0b82SPatrick Mooney 		}
28967c8c0b82SPatrick Mooney 
28977c8c0b82SPatrick Mooney 		while (length > 0) {
28987c8c0b82SPatrick Mooney 			offset &= vie_size2mask(addrsize);
28997c8c0b82SPatrick Mooney 			if (offset < low_limit || offset > high_limit)
29007c8c0b82SPatrick Mooney 				return (-1);
29017c8c0b82SPatrick Mooney 			offset++;
29027c8c0b82SPatrick Mooney 			length--;
29037c8c0b82SPatrick Mooney 		}
29047c8c0b82SPatrick Mooney 	}
29057c8c0b82SPatrick Mooney 
29067c8c0b82SPatrick Mooney 	/*
29077c8c0b82SPatrick Mooney 	 * In 64-bit mode all segments except %fs and %gs have a segment
29087c8c0b82SPatrick Mooney 	 * base address of 0.
29097c8c0b82SPatrick Mooney 	 */
29107c8c0b82SPatrick Mooney 	if (cpu_mode == CPU_MODE_64BIT && seg != VM_REG_GUEST_FS &&
29117c8c0b82SPatrick Mooney 	    seg != VM_REG_GUEST_GS) {
29127c8c0b82SPatrick Mooney 		segbase = 0;
29137c8c0b82SPatrick Mooney 	} else {
29147c8c0b82SPatrick Mooney 		segbase = desc->base;
29157c8c0b82SPatrick Mooney 	}
29167c8c0b82SPatrick Mooney 
29177c8c0b82SPatrick Mooney 	/*
29187c8c0b82SPatrick Mooney 	 * Truncate 'firstoff' to the effective address size before adding
29197c8c0b82SPatrick Mooney 	 * it to the segment base.
29207c8c0b82SPatrick Mooney 	 */
29217c8c0b82SPatrick Mooney 	firstoff &= vie_size2mask(addrsize);
29227c8c0b82SPatrick Mooney 	*gla = (segbase + firstoff) & vie_size2mask(glasize);
29237c8c0b82SPatrick Mooney 	return (0);
29247c8c0b82SPatrick Mooney }
29257c8c0b82SPatrick Mooney 
29267c8c0b82SPatrick Mooney void
vie_init_mmio(struct vie * vie,const char * inst_bytes,uint8_t inst_length,const struct vm_guest_paging * paging,uint64_t gpa)29277c8c0b82SPatrick Mooney vie_init_mmio(struct vie *vie, const char *inst_bytes, uint8_t inst_length,
29287c8c0b82SPatrick Mooney     const struct vm_guest_paging *paging, uint64_t gpa)
29297c8c0b82SPatrick Mooney {
29307c8c0b82SPatrick Mooney 	KASSERT(inst_length <= VIE_INST_SIZE,
29317c8c0b82SPatrick Mooney 	    ("%s: invalid instruction length (%d)", __func__, inst_length));
29327c8c0b82SPatrick Mooney 
29337c8c0b82SPatrick Mooney 	bzero(vie, sizeof (struct vie));
29347c8c0b82SPatrick Mooney 
29357c8c0b82SPatrick Mooney 	vie->base_register = VM_REG_LAST;
29367c8c0b82SPatrick Mooney 	vie->index_register = VM_REG_LAST;
29377c8c0b82SPatrick Mooney 	vie->segment_register = VM_REG_LAST;
29387c8c0b82SPatrick Mooney 	vie->status = VIES_INIT | VIES_MMIO;
29397c8c0b82SPatrick Mooney 
29407c8c0b82SPatrick Mooney 	if (inst_length != 0) {
29417c8c0b82SPatrick Mooney 		bcopy(inst_bytes, vie->inst, inst_length);
29427c8c0b82SPatrick Mooney 		vie->num_valid = inst_length;
29437c8c0b82SPatrick Mooney 		vie->status |= VIES_INST_FETCH;
29447c8c0b82SPatrick Mooney 	}
29457c8c0b82SPatrick Mooney 
29467c8c0b82SPatrick Mooney 	vie->paging = *paging;
29477c8c0b82SPatrick Mooney 	vie->mmio_gpa = gpa;
29487c8c0b82SPatrick Mooney }
29497c8c0b82SPatrick Mooney 
29507c8c0b82SPatrick Mooney void
vie_init_inout(struct vie * vie,const struct vm_inout * inout,uint8_t inst_len,const struct vm_guest_paging * paging)29517c8c0b82SPatrick Mooney vie_init_inout(struct vie *vie, const struct vm_inout *inout, uint8_t inst_len,
29527c8c0b82SPatrick Mooney     const struct vm_guest_paging *paging)
29537c8c0b82SPatrick Mooney {
29547c8c0b82SPatrick Mooney 	bzero(vie, sizeof (struct vie));
29557c8c0b82SPatrick Mooney 
29567c8c0b82SPatrick Mooney 	vie->status = VIES_INIT | VIES_INOUT;
29577c8c0b82SPatrick Mooney 
29587c8c0b82SPatrick Mooney 	vie->inout = *inout;
29597c8c0b82SPatrick Mooney 	vie->paging = *paging;
29607c8c0b82SPatrick Mooney 
29617c8c0b82SPatrick Mooney 	/*
29627c8c0b82SPatrick Mooney 	 * Since VMX/SVM assists already decoded the nature of the in/out
29637c8c0b82SPatrick Mooney 	 * instruction, let the status reflect that.
29647c8c0b82SPatrick Mooney 	 */
29657c8c0b82SPatrick Mooney 	vie->status |= VIES_INST_FETCH | VIES_INST_DECODE;
29667c8c0b82SPatrick Mooney 	vie->num_processed = inst_len;
29677c8c0b82SPatrick Mooney }
29687c8c0b82SPatrick Mooney 
29697c8c0b82SPatrick Mooney void
vie_init_other(struct vie * vie,const struct vm_guest_paging * paging)29707c8c0b82SPatrick Mooney vie_init_other(struct vie *vie, const struct vm_guest_paging *paging)
29717c8c0b82SPatrick Mooney {
29727c8c0b82SPatrick Mooney 	bzero(vie, sizeof (struct vie));
29737c8c0b82SPatrick Mooney 
29747c8c0b82SPatrick Mooney 	vie->base_register = VM_REG_LAST;
29757c8c0b82SPatrick Mooney 	vie->index_register = VM_REG_LAST;
29767c8c0b82SPatrick Mooney 	vie->segment_register = VM_REG_LAST;
29777c8c0b82SPatrick Mooney 	vie->status = VIES_INIT | VIES_OTHER;
29787c8c0b82SPatrick Mooney 
29797c8c0b82SPatrick Mooney 	vie->paging = *paging;
29807c8c0b82SPatrick Mooney }
29817c8c0b82SPatrick Mooney 
29827c8c0b82SPatrick Mooney int
vie_fulfill_mmio(struct vie * vie,const struct vm_mmio * result)29837c8c0b82SPatrick Mooney vie_fulfill_mmio(struct vie *vie, const struct vm_mmio *result)
29847c8c0b82SPatrick Mooney {
29857c8c0b82SPatrick Mooney 	struct vie_mmio *pending;
29867c8c0b82SPatrick Mooney 
29877c8c0b82SPatrick Mooney 	if ((vie->status & VIES_MMIO) == 0 ||
29887c8c0b82SPatrick Mooney 	    (vie->status & VIES_PENDING_MMIO) == 0) {
29897c8c0b82SPatrick Mooney 		return (EINVAL);
29907c8c0b82SPatrick Mooney 	}
29917c8c0b82SPatrick Mooney 
29927c8c0b82SPatrick Mooney 	if (result->read) {
29937c8c0b82SPatrick Mooney 		pending = &vie->mmio_req_read;
29947c8c0b82SPatrick Mooney 	} else {
29957c8c0b82SPatrick Mooney 		pending = &vie->mmio_req_write;
29967c8c0b82SPatrick Mooney 	}
29977c8c0b82SPatrick Mooney 
29987c8c0b82SPatrick Mooney 	if (pending->state != VR_PENDING ||
29997c8c0b82SPatrick Mooney 	    pending->bytes != result->bytes || pending->gpa != result->gpa) {
30007c8c0b82SPatrick Mooney 		return (EINVAL);
30017c8c0b82SPatrick Mooney 	}
30027c8c0b82SPatrick Mooney 
30037c8c0b82SPatrick Mooney 	if (result->read) {
30047c8c0b82SPatrick Mooney 		pending->data = result->data & vie_size2mask(pending->bytes);
30057c8c0b82SPatrick Mooney 	}
30067c8c0b82SPatrick Mooney 	pending->state = VR_DONE;
30077c8c0b82SPatrick Mooney 	vie->status &= ~VIES_PENDING_MMIO;
30087c8c0b82SPatrick Mooney 
30097c8c0b82SPatrick Mooney 	return (0);
30107c8c0b82SPatrick Mooney }
30117c8c0b82SPatrick Mooney 
30127c8c0b82SPatrick Mooney int
vie_fulfill_inout(struct vie * vie,const struct vm_inout * result)30137c8c0b82SPatrick Mooney vie_fulfill_inout(struct vie *vie, const struct vm_inout *result)
30147c8c0b82SPatrick Mooney {
30157c8c0b82SPatrick Mooney 	if ((vie->status & VIES_INOUT) == 0 ||
30167c8c0b82SPatrick Mooney 	    (vie->status & VIES_PENDING_INOUT) == 0) {
30177c8c0b82SPatrick Mooney 		return (EINVAL);
30187c8c0b82SPatrick Mooney 	}
30197c8c0b82SPatrick Mooney 	if ((vie->inout.flags & INOUT_IN) != (result->flags & INOUT_IN) ||
30207c8c0b82SPatrick Mooney 	    vie->inout.bytes != result->bytes ||
30217c8c0b82SPatrick Mooney 	    vie->inout.port != result->port) {
30227c8c0b82SPatrick Mooney 		return (EINVAL);
30237c8c0b82SPatrick Mooney 	}
30247c8c0b82SPatrick Mooney 
30257c8c0b82SPatrick Mooney 	if (result->flags & INOUT_IN) {
30267c8c0b82SPatrick Mooney 		vie->inout_req_val = result->eax &
30277c8c0b82SPatrick Mooney 		    vie_size2mask(vie->inout.bytes);
30287c8c0b82SPatrick Mooney 	}
30297c8c0b82SPatrick Mooney 	vie->inout_req_state = VR_DONE;
30307c8c0b82SPatrick Mooney 	vie->status &= ~(VIES_PENDING_INOUT);
30317c8c0b82SPatrick Mooney 
30327c8c0b82SPatrick Mooney 	return (0);
30337c8c0b82SPatrick Mooney }
30347c8c0b82SPatrick Mooney 
30357c8c0b82SPatrick Mooney uint64_t
vie_mmio_gpa(const struct vie * vie)30367c8c0b82SPatrick Mooney vie_mmio_gpa(const struct vie *vie)
30377c8c0b82SPatrick Mooney {
30387c8c0b82SPatrick Mooney 	return (vie->mmio_gpa);
30397c8c0b82SPatrick Mooney }
30407c8c0b82SPatrick Mooney 
30417c8c0b82SPatrick Mooney static int
pf_error_code(int usermode,int prot,int rsvd,uint64_t pte)30427c8c0b82SPatrick Mooney pf_error_code(int usermode, int prot, int rsvd, uint64_t pte)
30437c8c0b82SPatrick Mooney {
30447c8c0b82SPatrick Mooney 	int error_code = 0;
30457c8c0b82SPatrick Mooney 
30467c8c0b82SPatrick Mooney 	if (pte & PG_V)
30477c8c0b82SPatrick Mooney 		error_code |= PGEX_P;
30487c8c0b82SPatrick Mooney 	if (prot & PROT_WRITE)
30497c8c0b82SPatrick Mooney 		error_code |= PGEX_W;
30507c8c0b82SPatrick Mooney 	if (usermode)
30517c8c0b82SPatrick Mooney 		error_code |= PGEX_U;
30527c8c0b82SPatrick Mooney 	if (rsvd)
30537c8c0b82SPatrick Mooney 		error_code |= PGEX_RSV;
30547c8c0b82SPatrick Mooney 	if (prot & PROT_EXEC)
30557c8c0b82SPatrick Mooney 		error_code |= PGEX_I;
30567c8c0b82SPatrick Mooney 
30577c8c0b82SPatrick Mooney 	return (error_code);
30587c8c0b82SPatrick Mooney }
30597c8c0b82SPatrick Mooney 
30607c8c0b82SPatrick Mooney static void
ptp_release(vm_page_t ** vmp)30617c8c0b82SPatrick Mooney ptp_release(vm_page_t **vmp)
30627c8c0b82SPatrick Mooney {
30637c8c0b82SPatrick Mooney 	if (*vmp != NULL) {
3064e0994bd2SPatrick Mooney 		(void) vmp_release(*vmp);
30657c8c0b82SPatrick Mooney 		*vmp = NULL;
30667c8c0b82SPatrick Mooney 	}
30677c8c0b82SPatrick Mooney }
30687c8c0b82SPatrick Mooney 
30697c8c0b82SPatrick Mooney static void *
ptp_hold(struct vm * vm,int vcpu,uintptr_t gpa,size_t len,vm_page_t ** vmp)30707c8c0b82SPatrick Mooney ptp_hold(struct vm *vm, int vcpu, uintptr_t gpa, size_t len, vm_page_t **vmp)
30717c8c0b82SPatrick Mooney {
30727c8c0b82SPatrick Mooney 	vm_client_t *vmc = vm_get_vmclient(vm, vcpu);
30737c8c0b82SPatrick Mooney 	const uintptr_t hold_gpa = gpa & PAGEMASK;
30747c8c0b82SPatrick Mooney 
30757c8c0b82SPatrick Mooney 	/* Hold must not cross a page boundary */
30767c8c0b82SPatrick Mooney 	VERIFY3U(gpa + len, <=, hold_gpa + PAGESIZE);
30777c8c0b82SPatrick Mooney 
30787c8c0b82SPatrick Mooney 	if (*vmp != NULL) {
3079e0994bd2SPatrick Mooney 		(void) vmp_release(*vmp);
30807c8c0b82SPatrick Mooney 	}
30817c8c0b82SPatrick Mooney 
30827c8c0b82SPatrick Mooney 	*vmp = vmc_hold(vmc, hold_gpa, PROT_READ | PROT_WRITE);
30837c8c0b82SPatrick Mooney 	if (*vmp == NULL) {
30847c8c0b82SPatrick Mooney 		return (NULL);
30857c8c0b82SPatrick Mooney 	}
30867c8c0b82SPatrick Mooney 
30877c8c0b82SPatrick Mooney 	return ((caddr_t)vmp_get_writable(*vmp) + (gpa - hold_gpa));
30887c8c0b82SPatrick Mooney }
30897c8c0b82SPatrick Mooney 
30907c8c0b82SPatrick Mooney static int
_vm_gla2gpa(struct vm * vm,int vcpuid,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * guest_fault,bool check_only)30917c8c0b82SPatrick Mooney _vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
30927c8c0b82SPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault, bool check_only)
30937c8c0b82SPatrick Mooney {
30947c8c0b82SPatrick Mooney 	int nlevels, pfcode;
30957c8c0b82SPatrick Mooney 	int ptpshift = 0, ptpindex = 0;
30967c8c0b82SPatrick Mooney 	uint64_t ptpphys;
30977c8c0b82SPatrick Mooney 	uint64_t *ptpbase = NULL, pte = 0, pgsize = 0;
30987c8c0b82SPatrick Mooney 	vm_page_t *cookie = NULL;
30997c8c0b82SPatrick Mooney 	const bool usermode = paging->cpl == 3;
31007c8c0b82SPatrick Mooney 	const bool writable = (prot & PROT_WRITE) != 0;
31017c8c0b82SPatrick Mooney 
31027c8c0b82SPatrick Mooney 	*guest_fault = 0;
31037c8c0b82SPatrick Mooney restart:
31047c8c0b82SPatrick Mooney 	ptpphys = paging->cr3;		/* root of the page tables */
31057c8c0b82SPatrick Mooney 	ptp_release(&cookie);
31067c8c0b82SPatrick Mooney 
31077c8c0b82SPatrick Mooney 	if (vie_canonical_check(paging->cpu_mode, gla)) {
31087c8c0b82SPatrick Mooney 		/*
31097c8c0b82SPatrick Mooney 		 * XXX assuming a non-stack reference otherwise a stack fault
31107c8c0b82SPatrick Mooney 		 * should be generated.
31117c8c0b82SPatrick Mooney 		 */
31127c8c0b82SPatrick Mooney 		if (!check_only)
31137c8c0b82SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
31147c8c0b82SPatrick Mooney 		*guest_fault = 1;
31157c8c0b82SPatrick Mooney 		return (0);
31167c8c0b82SPatrick Mooney 	}
31177c8c0b82SPatrick Mooney 
31187c8c0b82SPatrick Mooney 	if (paging->paging_mode == PAGING_MODE_FLAT) {
31197c8c0b82SPatrick Mooney 		*gpa = gla;
31207c8c0b82SPatrick Mooney 		return (0);
31217c8c0b82SPatrick Mooney 	}
31227c8c0b82SPatrick Mooney 
31237c8c0b82SPatrick Mooney 	if (paging->paging_mode == PAGING_MODE_32) {
31247c8c0b82SPatrick Mooney 		uint32_t *ptpbase32, pte32;
31257c8c0b82SPatrick Mooney 
31267c8c0b82SPatrick Mooney 		nlevels = 2;
31277c8c0b82SPatrick Mooney 		while (--nlevels >= 0) {
31287c8c0b82SPatrick Mooney 			/* Zero out the lower 12 bits. */
31297c8c0b82SPatrick Mooney 			ptpphys &= ~0xfff;
31307c8c0b82SPatrick Mooney 
31317c8c0b82SPatrick Mooney 			ptpbase32 = ptp_hold(vm, vcpuid, ptpphys, PAGE_SIZE,
31327c8c0b82SPatrick Mooney 			    &cookie);
31337c8c0b82SPatrick Mooney 
31347c8c0b82SPatrick Mooney 			if (ptpbase32 == NULL) {
31357c8c0b82SPatrick Mooney 				return (EFAULT);
31367c8c0b82SPatrick Mooney 			}
31377c8c0b82SPatrick Mooney 
31387c8c0b82SPatrick Mooney 			ptpshift = PAGE_SHIFT + nlevels * 10;
31397c8c0b82SPatrick Mooney 			ptpindex = (gla >> ptpshift) & 0x3FF;
31407c8c0b82SPatrick Mooney 			pgsize = 1UL << ptpshift;
31417c8c0b82SPatrick Mooney 
31427c8c0b82SPatrick Mooney 			pte32 = ptpbase32[ptpindex];
31437c8c0b82SPatrick Mooney 
31447c8c0b82SPatrick Mooney 			if ((pte32 & PG_V) == 0 ||
31457c8c0b82SPatrick Mooney 			    (usermode && (pte32 & PG_U) == 0) ||
31467c8c0b82SPatrick Mooney 			    (writable && (pte32 & PG_RW) == 0)) {
31477c8c0b82SPatrick Mooney 				if (!check_only) {
31487c8c0b82SPatrick Mooney 					pfcode = pf_error_code(usermode, prot,
31497c8c0b82SPatrick Mooney 					    0, pte32);
31507c8c0b82SPatrick Mooney 					vm_inject_pf(vm, vcpuid, pfcode, gla);
31517c8c0b82SPatrick Mooney 				}
31527c8c0b82SPatrick Mooney 
31537c8c0b82SPatrick Mooney 				ptp_release(&cookie);
31547c8c0b82SPatrick Mooney 				*guest_fault = 1;
31557c8c0b82SPatrick Mooney 				return (0);
31567c8c0b82SPatrick Mooney 			}
31577c8c0b82SPatrick Mooney 
31587c8c0b82SPatrick Mooney 			/*
31597c8c0b82SPatrick Mooney 			 * Emulate the x86 MMU's management of the accessed
31607c8c0b82SPatrick Mooney 			 * and dirty flags. While the accessed flag is set
31617c8c0b82SPatrick Mooney 			 * at every level of the page table, the dirty flag
31627c8c0b82SPatrick Mooney 			 * is only set at the last level providing the guest
31637c8c0b82SPatrick Mooney 			 * physical address.
31647c8c0b82SPatrick Mooney 			 */
31657c8c0b82SPatrick Mooney 			if (!check_only && (pte32 & PG_A) == 0) {
31667c8c0b82SPatrick Mooney 				if (atomic_cmpset_32(&ptpbase32[ptpindex],
31677c8c0b82SPatrick Mooney 				    pte32, pte32 | PG_A) == 0) {
31687c8c0b82SPatrick Mooney 					goto restart;
31697c8c0b82SPatrick Mooney 				}
31707c8c0b82SPatrick Mooney 			}
31717c8c0b82SPatrick Mooney 
31727c8c0b82SPatrick Mooney 			/* XXX must be ignored if CR4.PSE=0 */
31737c8c0b82SPatrick Mooney 			if (nlevels > 0 && (pte32 & PG_PS) != 0)
31747c8c0b82SPatrick Mooney 				break;
31757c8c0b82SPatrick Mooney 
31767c8c0b82SPatrick Mooney 			ptpphys = pte32;
31777c8c0b82SPatrick Mooney 		}
31787c8c0b82SPatrick Mooney 
31797c8c0b82SPatrick Mooney 		/* Set the dirty bit in the page table entry if necessary */
31807c8c0b82SPatrick Mooney 		if (!check_only && writable && (pte32 & PG_M) == 0) {
31817c8c0b82SPatrick Mooney 			if (atomic_cmpset_32(&ptpbase32[ptpindex],
31827c8c0b82SPatrick Mooney 			    pte32, pte32 | PG_M) == 0) {
31837c8c0b82SPatrick Mooney 				goto restart;
31847c8c0b82SPatrick Mooney 			}
31857c8c0b82SPatrick Mooney 		}
31867c8c0b82SPatrick Mooney 
31877c8c0b82SPatrick Mooney 		/* Zero out the lower 'ptpshift' bits */
31887c8c0b82SPatrick Mooney 		pte32 >>= ptpshift; pte32 <<= ptpshift;
31897c8c0b82SPatrick Mooney 		*gpa = pte32 | (gla & (pgsize - 1));
31907c8c0b82SPatrick Mooney 		ptp_release(&cookie);
31917c8c0b82SPatrick Mooney 		return (0);
31927c8c0b82SPatrick Mooney 	}
31937c8c0b82SPatrick Mooney 
31947c8c0b82SPatrick Mooney 	if (paging->paging_mode == PAGING_MODE_PAE) {
31957c8c0b82SPatrick Mooney 		/* Zero out the lower 5 bits and the upper 32 bits */
31967c8c0b82SPatrick Mooney 		ptpphys &= 0xffffffe0UL;
31977c8c0b82SPatrick Mooney 
31987c8c0b82SPatrick Mooney 		ptpbase = ptp_hold(vm, vcpuid, ptpphys, sizeof (*ptpbase) * 4,
31997c8c0b82SPatrick Mooney 		    &cookie);
32007c8c0b82SPatrick Mooney 		if (ptpbase == NULL) {
32017c8c0b82SPatrick Mooney 			return (EFAULT);
32027c8c0b82SPatrick Mooney 		}
32037c8c0b82SPatrick Mooney 
32047c8c0b82SPatrick Mooney 		ptpindex = (gla >> 30) & 0x3;
32057c8c0b82SPatrick Mooney 
32067c8c0b82SPatrick Mooney 		pte = ptpbase[ptpindex];
32077c8c0b82SPatrick Mooney 
32087c8c0b82SPatrick Mooney 		if ((pte & PG_V) == 0) {
32097c8c0b82SPatrick Mooney 			if (!check_only) {
32107c8c0b82SPatrick Mooney 				pfcode = pf_error_code(usermode, prot, 0, pte);
32117c8c0b82SPatrick Mooney 				vm_inject_pf(vm, vcpuid, pfcode, gla);
32127c8c0b82SPatrick Mooney 			}
32137c8c0b82SPatrick Mooney 
32147c8c0b82SPatrick Mooney 			ptp_release(&cookie);
32157c8c0b82SPatrick Mooney 			*guest_fault = 1;
32167c8c0b82SPatrick Mooney 			return (0);
32177c8c0b82SPatrick Mooney 		}
32187c8c0b82SPatrick Mooney 
32197c8c0b82SPatrick Mooney 		ptpphys = pte;
32207c8c0b82SPatrick Mooney 
32217c8c0b82SPatrick Mooney 		nlevels = 2;
32227c8c0b82SPatrick Mooney 	} else {
32237c8c0b82SPatrick Mooney 		nlevels = 4;
32247c8c0b82SPatrick Mooney 	}
32257c8c0b82SPatrick Mooney 
32267c8c0b82SPatrick Mooney 	while (--nlevels >= 0) {
32277c8c0b82SPatrick Mooney 		/* Zero out the lower 12 bits and the upper 12 bits */
32287c8c0b82SPatrick Mooney 		ptpphys &= 0x000ffffffffff000UL;
32297c8c0b82SPatrick Mooney 
32307c8c0b82SPatrick Mooney 		ptpbase = ptp_hold(vm, vcpuid, ptpphys, PAGE_SIZE, &cookie);
32317c8c0b82SPatrick Mooney 		if (ptpbase == NULL) {
32327c8c0b82SPatrick Mooney 			return (EFAULT);
32337c8c0b82SPatrick Mooney 		}
32347c8c0b82SPatrick Mooney 
32357c8c0b82SPatrick Mooney 		ptpshift = PAGE_SHIFT + nlevels * 9;
32367c8c0b82SPatrick Mooney 		ptpindex = (gla >> ptpshift) & 0x1FF;
32377c8c0b82SPatrick Mooney 		pgsize = 1UL << ptpshift;
32387c8c0b82SPatrick Mooney 
32397c8c0b82SPatrick Mooney 		pte = ptpbase[ptpindex];
32407c8c0b82SPatrick Mooney 
32417c8c0b82SPatrick Mooney 		if ((pte & PG_V) == 0 ||
32427c8c0b82SPatrick Mooney 		    (usermode && (pte & PG_U) == 0) ||
32437c8c0b82SPatrick Mooney 		    (writable && (pte & PG_RW) == 0)) {
32447c8c0b82SPatrick Mooney 			if (!check_only) {
32457c8c0b82SPatrick Mooney 				pfcode = pf_error_code(usermode, prot, 0, pte);
32467c8c0b82SPatrick Mooney 				vm_inject_pf(vm, vcpuid, pfcode, gla);
32477c8c0b82SPatrick Mooney 			}
32487c8c0b82SPatrick Mooney 
32497c8c0b82SPatrick Mooney 			ptp_release(&cookie);
32507c8c0b82SPatrick Mooney 			*guest_fault = 1;
32517c8c0b82SPatrick Mooney 			return (0);
32527c8c0b82SPatrick Mooney 		}
32537c8c0b82SPatrick Mooney 
32547c8c0b82SPatrick Mooney 		/* Set the accessed bit in the page table entry */
32557c8c0b82SPatrick Mooney 		if (!check_only && (pte & PG_A) == 0) {
32567c8c0b82SPatrick Mooney 			if (atomic_cmpset_64(&ptpbase[ptpindex],
32577c8c0b82SPatrick Mooney 			    pte, pte | PG_A) == 0) {
32587c8c0b82SPatrick Mooney 				goto restart;
32597c8c0b82SPatrick Mooney 			}
32607c8c0b82SPatrick Mooney 		}
32617c8c0b82SPatrick Mooney 
32627c8c0b82SPatrick Mooney 		if (nlevels > 0 && (pte & PG_PS) != 0) {
32637c8c0b82SPatrick Mooney 			if (pgsize > 1 * GB) {
32647c8c0b82SPatrick Mooney 				if (!check_only) {
32657c8c0b82SPatrick Mooney 					pfcode = pf_error_code(usermode, prot,
32667c8c0b82SPatrick Mooney 					    1, pte);
32677c8c0b82SPatrick Mooney 					vm_inject_pf(vm, vcpuid, pfcode, gla);
32687c8c0b82SPatrick Mooney 				}
32697c8c0b82SPatrick Mooney 
32707c8c0b82SPatrick Mooney 				ptp_release(&cookie);
32717c8c0b82SPatrick Mooney 				*guest_fault = 1;
32727c8c0b82SPatrick Mooney 				return (0);
32737c8c0b82SPatrick Mooney 			}
32747c8c0b82SPatrick Mooney 			break;
32757c8c0b82SPatrick Mooney 		}
32767c8c0b82SPatrick Mooney 
32777c8c0b82SPatrick Mooney 		ptpphys = pte;
32787c8c0b82SPatrick Mooney 	}
32797c8c0b82SPatrick Mooney 
32807c8c0b82SPatrick Mooney 	/* Set the dirty bit in the page table entry if necessary */
32817c8c0b82SPatrick Mooney 	if (!check_only && writable && (pte & PG_M) == 0) {
32827c8c0b82SPatrick Mooney 		if (atomic_cmpset_64(&ptpbase[ptpindex], pte, pte | PG_M) == 0)
32837c8c0b82SPatrick Mooney 			goto restart;
32847c8c0b82SPatrick Mooney 	}
32857c8c0b82SPatrick Mooney 	ptp_release(&cookie);
32867c8c0b82SPatrick Mooney 
32877c8c0b82SPatrick Mooney 	/* Zero out the lower 'ptpshift' bits and the upper 12 bits */
32887c8c0b82SPatrick Mooney 	pte >>= ptpshift; pte <<= (ptpshift + 12); pte >>= 12;
32897c8c0b82SPatrick Mooney 	*gpa = pte | (gla & (pgsize - 1));
32907c8c0b82SPatrick Mooney 	return (0);
32917c8c0b82SPatrick Mooney }
32927c8c0b82SPatrick Mooney 
32937c8c0b82SPatrick Mooney int
vm_gla2gpa(struct vm * vm,int vcpuid,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * guest_fault)32947c8c0b82SPatrick Mooney vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
32957c8c0b82SPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault)
32967c8c0b82SPatrick Mooney {
32977c8c0b82SPatrick Mooney 
32987c8c0b82SPatrick Mooney 	return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault,
32997c8c0b82SPatrick Mooney 	    false));
33007c8c0b82SPatrick Mooney }
33017c8c0b82SPatrick Mooney 
33027c8c0b82SPatrick Mooney int
vm_gla2gpa_nofault(struct vm * vm,int vcpuid,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * guest_fault)33037c8c0b82SPatrick Mooney vm_gla2gpa_nofault(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
33047c8c0b82SPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault)
33057c8c0b82SPatrick Mooney {
33067c8c0b82SPatrick Mooney 
33077c8c0b82SPatrick Mooney 	return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault,
33087c8c0b82SPatrick Mooney 	    true));
33097c8c0b82SPatrick Mooney }
33107c8c0b82SPatrick Mooney 
33117c8c0b82SPatrick Mooney int
vie_fetch_instruction(struct vie * vie,struct vm * vm,int vcpuid,uint64_t rip,int * faultptr)33127c8c0b82SPatrick Mooney vie_fetch_instruction(struct vie *vie, struct vm *vm, int vcpuid, uint64_t rip,
33137c8c0b82SPatrick Mooney     int *faultptr)
33147c8c0b82SPatrick Mooney {
33157c8c0b82SPatrick Mooney 	struct vm_copyinfo copyinfo[2];
33167c8c0b82SPatrick Mooney 	int error, prot;
33177c8c0b82SPatrick Mooney 
33187c8c0b82SPatrick Mooney 	if ((vie->status & VIES_INIT) == 0) {
33197c8c0b82SPatrick Mooney 		return (EINVAL);
33207c8c0b82SPatrick Mooney 	}
33217c8c0b82SPatrick Mooney 
33227c8c0b82SPatrick Mooney 	prot = PROT_READ | PROT_EXEC;
33237c8c0b82SPatrick Mooney 	error = vm_copy_setup(vm, vcpuid, &vie->paging, rip, VIE_INST_SIZE,
33247c8c0b82SPatrick Mooney 	    prot, copyinfo, nitems(copyinfo), faultptr);
33257c8c0b82SPatrick Mooney 	if (error || *faultptr)
33267c8c0b82SPatrick Mooney 		return (error);
33277c8c0b82SPatrick Mooney 
33287c8c0b82SPatrick Mooney 	vm_copyin(vm, vcpuid, copyinfo, vie->inst, VIE_INST_SIZE);
33297c8c0b82SPatrick Mooney 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
33307c8c0b82SPatrick Mooney 	vie->num_valid = VIE_INST_SIZE;
33317c8c0b82SPatrick Mooney 	vie->status |= VIES_INST_FETCH;
33327c8c0b82SPatrick Mooney 	return (0);
33337c8c0b82SPatrick Mooney }
33347c8c0b82SPatrick Mooney 
33357c8c0b82SPatrick Mooney static int
vie_peek(struct vie * vie,uint8_t * x)33367c8c0b82SPatrick Mooney vie_peek(struct vie *vie, uint8_t *x)
33377c8c0b82SPatrick Mooney {
33387c8c0b82SPatrick Mooney 
33397c8c0b82SPatrick Mooney 	if (vie->num_processed < vie->num_valid) {
33407c8c0b82SPatrick Mooney 		*x = vie->inst[vie->num_processed];
33417c8c0b82SPatrick Mooney 		return (0);
33427c8c0b82SPatrick Mooney 	} else
33437c8c0b82SPatrick Mooney 		return (-1);
33447c8c0b82SPatrick Mooney }
33457c8c0b82SPatrick Mooney 
33467c8c0b82SPatrick Mooney static void
vie_advance(struct vie * vie)33477c8c0b82SPatrick Mooney vie_advance(struct vie *vie)
33487c8c0b82SPatrick Mooney {
33497c8c0b82SPatrick Mooney 
33507c8c0b82SPatrick Mooney 	vie->num_processed++;
33517c8c0b82SPatrick Mooney }
33527c8c0b82SPatrick Mooney 
33537c8c0b82SPatrick Mooney static bool
segment_override(uint8_t x,int * seg)33547c8c0b82SPatrick Mooney segment_override(uint8_t x, int *seg)
33557c8c0b82SPatrick Mooney {
33567c8c0b82SPatrick Mooney 
33577c8c0b82SPatrick Mooney 	switch (x) {
33587c8c0b82SPatrick Mooney 	case 0x2E:
33597c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_CS;
33607c8c0b82SPatrick Mooney 		break;
33617c8c0b82SPatrick Mooney 	case 0x36:
33627c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_SS;
33637c8c0b82SPatrick Mooney 		break;
33647c8c0b82SPatrick Mooney 	case 0x3E:
33657c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_DS;
33667c8c0b82SPatrick Mooney 		break;
33677c8c0b82SPatrick Mooney 	case 0x26:
33687c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_ES;
33697c8c0b82SPatrick Mooney 		break;
33707c8c0b82SPatrick Mooney 	case 0x64:
33717c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_FS;
33727c8c0b82SPatrick Mooney 		break;
33737c8c0b82SPatrick Mooney 	case 0x65:
33747c8c0b82SPatrick Mooney 		*seg = VM_REG_GUEST_GS;
33757c8c0b82SPatrick Mooney 		break;
33767c8c0b82SPatrick Mooney 	default:
33777c8c0b82SPatrick Mooney 		return (false);
33787c8c0b82SPatrick Mooney 	}
33797c8c0b82SPatrick Mooney 	return (true);
33807c8c0b82SPatrick Mooney }
33817c8c0b82SPatrick Mooney 
33827c8c0b82SPatrick Mooney static int
decode_prefixes(struct vie * vie,enum vm_cpu_mode cpu_mode,int cs_d)33837c8c0b82SPatrick Mooney decode_prefixes(struct vie *vie, enum vm_cpu_mode cpu_mode, int cs_d)
33847c8c0b82SPatrick Mooney {
33857c8c0b82SPatrick Mooney 	uint8_t x;
33867c8c0b82SPatrick Mooney 
33877c8c0b82SPatrick Mooney 	while (1) {
33887c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
33897c8c0b82SPatrick Mooney 			return (-1);
33907c8c0b82SPatrick Mooney 
33917c8c0b82SPatrick Mooney 		if (x == 0x66)
33927c8c0b82SPatrick Mooney 			vie->opsize_override = 1;
33937c8c0b82SPatrick Mooney 		else if (x == 0x67)
33947c8c0b82SPatrick Mooney 			vie->addrsize_override = 1;
33957c8c0b82SPatrick Mooney 		else if (x == 0xF3)
33967c8c0b82SPatrick Mooney 			vie->repz_present = 1;
33977c8c0b82SPatrick Mooney 		else if (x == 0xF2)
33987c8c0b82SPatrick Mooney 			vie->repnz_present = 1;
33997c8c0b82SPatrick Mooney 		else if (segment_override(x, &vie->segment_register))
34007c8c0b82SPatrick Mooney 			vie->segment_override = 1;
34017c8c0b82SPatrick Mooney 		else
34027c8c0b82SPatrick Mooney 			break;
34037c8c0b82SPatrick Mooney 
34047c8c0b82SPatrick Mooney 		vie_advance(vie);
34057c8c0b82SPatrick Mooney 	}
34067c8c0b82SPatrick Mooney 
34077c8c0b82SPatrick Mooney 	/*
34087c8c0b82SPatrick Mooney 	 * From section 2.2.1, "REX Prefixes", Intel SDM Vol 2:
34097c8c0b82SPatrick Mooney 	 * - Only one REX prefix is allowed per instruction.
34107c8c0b82SPatrick Mooney 	 * - The REX prefix must immediately precede the opcode byte or the
34117c8c0b82SPatrick Mooney 	 *   escape opcode byte.
34127c8c0b82SPatrick Mooney 	 * - If an instruction has a mandatory prefix (0x66, 0xF2 or 0xF3)
34137c8c0b82SPatrick Mooney 	 *   the mandatory prefix must come before the REX prefix.
34147c8c0b82SPatrick Mooney 	 */
34157c8c0b82SPatrick Mooney 	if (cpu_mode == CPU_MODE_64BIT && x >= 0x40 && x <= 0x4F) {
34167c8c0b82SPatrick Mooney 		vie->rex_present = 1;
34177c8c0b82SPatrick Mooney 		vie->rex_w = x & 0x8 ? 1 : 0;
34187c8c0b82SPatrick Mooney 		vie->rex_r = x & 0x4 ? 1 : 0;
34197c8c0b82SPatrick Mooney 		vie->rex_x = x & 0x2 ? 1 : 0;
34207c8c0b82SPatrick Mooney 		vie->rex_b = x & 0x1 ? 1 : 0;
34217c8c0b82SPatrick Mooney 		vie_advance(vie);
34227c8c0b82SPatrick Mooney 	}
34237c8c0b82SPatrick Mooney 
34247c8c0b82SPatrick Mooney 	/*
34257c8c0b82SPatrick Mooney 	 * § 2.3.5, "The VEX Prefix", SDM Vol 2.
34267c8c0b82SPatrick Mooney 	 */
34277c8c0b82SPatrick Mooney 	if ((cpu_mode == CPU_MODE_64BIT ||
34287c8c0b82SPatrick Mooney 	    cpu_mode == CPU_MODE_COMPATIBILITY) && x == 0xC4) {
34297c8c0b82SPatrick Mooney 		const struct vie_op *optab;
34307c8c0b82SPatrick Mooney 
34317c8c0b82SPatrick Mooney 		/* 3-byte VEX prefix. */
34327c8c0b82SPatrick Mooney 		vie->vex_present = 1;
34337c8c0b82SPatrick Mooney 
34347c8c0b82SPatrick Mooney 		vie_advance(vie);
34357c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
34367c8c0b82SPatrick Mooney 			return (-1);
34377c8c0b82SPatrick Mooney 
34387c8c0b82SPatrick Mooney 		/*
34397c8c0b82SPatrick Mooney 		 * 2nd byte: [R', X', B', mmmmm[4:0]].  Bits are inverted
34407c8c0b82SPatrick Mooney 		 * relative to REX encoding.
34417c8c0b82SPatrick Mooney 		 */
34427c8c0b82SPatrick Mooney 		vie->rex_r = x & 0x80 ? 0 : 1;
34437c8c0b82SPatrick Mooney 		vie->rex_x = x & 0x40 ? 0 : 1;
34447c8c0b82SPatrick Mooney 		vie->rex_b = x & 0x20 ? 0 : 1;
34457c8c0b82SPatrick Mooney 
34467c8c0b82SPatrick Mooney 		switch (x & 0x1F) {
34477c8c0b82SPatrick Mooney 		case 0x2:
34487c8c0b82SPatrick Mooney 			/* 0F 38. */
34497c8c0b82SPatrick Mooney 			optab = three_byte_opcodes_0f38;
34507c8c0b82SPatrick Mooney 			break;
34517c8c0b82SPatrick Mooney 		case 0x1:
34527c8c0b82SPatrick Mooney 			/* 0F class - nothing handled here yet. */
34537c8c0b82SPatrick Mooney 			/* FALLTHROUGH */
34547c8c0b82SPatrick Mooney 		case 0x3:
34557c8c0b82SPatrick Mooney 			/* 0F 3A class - nothing handled here yet. */
34567c8c0b82SPatrick Mooney 			/* FALLTHROUGH */
34577c8c0b82SPatrick Mooney 		default:
34587c8c0b82SPatrick Mooney 			/* Reserved (#UD). */
34597c8c0b82SPatrick Mooney 			return (-1);
34607c8c0b82SPatrick Mooney 		}
34617c8c0b82SPatrick Mooney 
34627c8c0b82SPatrick Mooney 		vie_advance(vie);
34637c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
34647c8c0b82SPatrick Mooney 			return (-1);
34657c8c0b82SPatrick Mooney 
34667c8c0b82SPatrick Mooney 		/* 3rd byte: [W, vvvv[6:3], L, pp[1:0]]. */
34677c8c0b82SPatrick Mooney 		vie->rex_w = x & 0x80 ? 1 : 0;
34687c8c0b82SPatrick Mooney 
34697c8c0b82SPatrick Mooney 		vie->vex_reg = ((~(unsigned)x & 0x78u) >> 3);
34707c8c0b82SPatrick Mooney 		vie->vex_l = !!(x & 0x4);
34717c8c0b82SPatrick Mooney 		vie->vex_pp = (x & 0x3);
34727c8c0b82SPatrick Mooney 
34737c8c0b82SPatrick Mooney 		/* PP: 1=66 2=F3 3=F2 prefixes. */
34747c8c0b82SPatrick Mooney 		switch (vie->vex_pp) {
34757c8c0b82SPatrick Mooney 		case 0x1:
34767c8c0b82SPatrick Mooney 			vie->opsize_override = 1;
34777c8c0b82SPatrick Mooney 			break;
34787c8c0b82SPatrick Mooney 		case 0x2:
34797c8c0b82SPatrick Mooney 			vie->repz_present = 1;
34807c8c0b82SPatrick Mooney 			break;
34817c8c0b82SPatrick Mooney 		case 0x3:
34827c8c0b82SPatrick Mooney 			vie->repnz_present = 1;
34837c8c0b82SPatrick Mooney 			break;
34847c8c0b82SPatrick Mooney 		}
34857c8c0b82SPatrick Mooney 
34867c8c0b82SPatrick Mooney 		vie_advance(vie);
34877c8c0b82SPatrick Mooney 
34887c8c0b82SPatrick Mooney 		/* Opcode, sans literal prefix prefix. */
34897c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
34907c8c0b82SPatrick Mooney 			return (-1);
34917c8c0b82SPatrick Mooney 
34927c8c0b82SPatrick Mooney 		vie->op = optab[x];
34937c8c0b82SPatrick Mooney 		if (vie->op.op_type == VIE_OP_TYPE_NONE)
34947c8c0b82SPatrick Mooney 			return (-1);
34957c8c0b82SPatrick Mooney 
34967c8c0b82SPatrick Mooney 		vie_advance(vie);
34977c8c0b82SPatrick Mooney 	}
34987c8c0b82SPatrick Mooney 
34997c8c0b82SPatrick Mooney 	/*
35007c8c0b82SPatrick Mooney 	 * Section "Operand-Size And Address-Size Attributes", Intel SDM, Vol 1
35017c8c0b82SPatrick Mooney 	 */
35027c8c0b82SPatrick Mooney 	if (cpu_mode == CPU_MODE_64BIT) {
35037c8c0b82SPatrick Mooney 		/*
35047c8c0b82SPatrick Mooney 		 * Default address size is 64-bits and default operand size
35057c8c0b82SPatrick Mooney 		 * is 32-bits.
35067c8c0b82SPatrick Mooney 		 */
35077c8c0b82SPatrick Mooney 		vie->addrsize = vie->addrsize_override ? 4 : 8;
35087c8c0b82SPatrick Mooney 		if (vie->rex_w)
35097c8c0b82SPatrick Mooney 			vie->opsize = 8;
35107c8c0b82SPatrick Mooney 		else if (vie->opsize_override)
35117c8c0b82SPatrick Mooney 			vie->opsize = 2;
35127c8c0b82SPatrick Mooney 		else
35137c8c0b82SPatrick Mooney 			vie->opsize = 4;
35147c8c0b82SPatrick Mooney 	} else if (cs_d) {
35157c8c0b82SPatrick Mooney 		/* Default address and operand sizes are 32-bits */
35167c8c0b82SPatrick Mooney 		vie->addrsize = vie->addrsize_override ? 2 : 4;
35177c8c0b82SPatrick Mooney 		vie->opsize = vie->opsize_override ? 2 : 4;
35187c8c0b82SPatrick Mooney 	} else {
35197c8c0b82SPatrick Mooney 		/* Default address and operand sizes are 16-bits */
35207c8c0b82SPatrick Mooney 		vie->addrsize = vie->addrsize_override ? 4 : 2;
35217c8c0b82SPatrick Mooney 		vie->opsize = vie->opsize_override ? 4 : 2;
35227c8c0b82SPatrick Mooney 	}
35237c8c0b82SPatrick Mooney 	return (0);
35247c8c0b82SPatrick Mooney }
35257c8c0b82SPatrick Mooney 
35267c8c0b82SPatrick Mooney static int
decode_two_byte_opcode(struct vie * vie)35277c8c0b82SPatrick Mooney decode_two_byte_opcode(struct vie *vie)
35287c8c0b82SPatrick Mooney {
35297c8c0b82SPatrick Mooney 	uint8_t x;
35307c8c0b82SPatrick Mooney 
35317c8c0b82SPatrick Mooney 	if (vie_peek(vie, &x))
35327c8c0b82SPatrick Mooney 		return (-1);
35337c8c0b82SPatrick Mooney 
35347c8c0b82SPatrick Mooney 	vie->op = two_byte_opcodes[x];
35357c8c0b82SPatrick Mooney 
35367c8c0b82SPatrick Mooney 	if (vie->op.op_type == VIE_OP_TYPE_NONE)
35377c8c0b82SPatrick Mooney 		return (-1);
35387c8c0b82SPatrick Mooney 
35397c8c0b82SPatrick Mooney 	vie_advance(vie);
35407c8c0b82SPatrick Mooney 	return (0);
35417c8c0b82SPatrick Mooney }
35427c8c0b82SPatrick Mooney 
35437c8c0b82SPatrick Mooney static int
decode_opcode(struct vie * vie)35447c8c0b82SPatrick Mooney decode_opcode(struct vie *vie)
35457c8c0b82SPatrick Mooney {
35467c8c0b82SPatrick Mooney 	uint8_t x;
35477c8c0b82SPatrick Mooney 
35487c8c0b82SPatrick Mooney 	if (vie_peek(vie, &x))
35497c8c0b82SPatrick Mooney 		return (-1);
35507c8c0b82SPatrick Mooney 
35517c8c0b82SPatrick Mooney 	/* Already did this via VEX prefix. */
35527c8c0b82SPatrick Mooney 	if (vie->op.op_type != VIE_OP_TYPE_NONE)
35537c8c0b82SPatrick Mooney 		return (0);
35547c8c0b82SPatrick Mooney 
35557c8c0b82SPatrick Mooney 	vie->op = one_byte_opcodes[x];
35567c8c0b82SPatrick Mooney 
35577c8c0b82SPatrick Mooney 	if (vie->op.op_type == VIE_OP_TYPE_NONE)
35587c8c0b82SPatrick Mooney 		return (-1);
35597c8c0b82SPatrick Mooney 
35607c8c0b82SPatrick Mooney 	vie_advance(vie);
35617c8c0b82SPatrick Mooney 
35627c8c0b82SPatrick Mooney 	if (vie->op.op_type == VIE_OP_TYPE_TWO_BYTE)
35637c8c0b82SPatrick Mooney 		return (decode_two_byte_opcode(vie));
35647c8c0b82SPatrick Mooney 
35657c8c0b82SPatrick Mooney 	return (0);
35667c8c0b82SPatrick Mooney }
35677c8c0b82SPatrick Mooney 
35687c8c0b82SPatrick Mooney static int
decode_modrm(struct vie * vie,enum vm_cpu_mode cpu_mode)35697c8c0b82SPatrick Mooney decode_modrm(struct vie *vie, enum vm_cpu_mode cpu_mode)
35707c8c0b82SPatrick Mooney {
35717c8c0b82SPatrick Mooney 	uint8_t x;
35727c8c0b82SPatrick Mooney 	/*
35737c8c0b82SPatrick Mooney 	 * Handling mov-to/from-cr is special since it is not issuing
35747c8c0b82SPatrick Mooney 	 * mmio/pio requests and can be done in real mode.  We must bypass some
35757c8c0b82SPatrick Mooney 	 * of the other existing decoding restrictions for it.
35767c8c0b82SPatrick Mooney 	 */
35777c8c0b82SPatrick Mooney 	const bool is_movcr = ((vie->op.op_flags & VIE_OP_F_REG_REG) != 0);
35787c8c0b82SPatrick Mooney 
35797c8c0b82SPatrick Mooney 	if (vie->op.op_flags & VIE_OP_F_NO_MODRM)
35807c8c0b82SPatrick Mooney 		return (0);
35817c8c0b82SPatrick Mooney 
35827c8c0b82SPatrick Mooney 	if (cpu_mode == CPU_MODE_REAL && !is_movcr)
35837c8c0b82SPatrick Mooney 		return (-1);
35847c8c0b82SPatrick Mooney 
35857c8c0b82SPatrick Mooney 	if (vie_peek(vie, &x))
35867c8c0b82SPatrick Mooney 		return (-1);
35877c8c0b82SPatrick Mooney 
35887c8c0b82SPatrick Mooney 	vie->mod = (x >> 6) & 0x3;
35897c8c0b82SPatrick Mooney 	vie->rm =  (x >> 0) & 0x7;
35907c8c0b82SPatrick Mooney 	vie->reg = (x >> 3) & 0x7;
35917c8c0b82SPatrick Mooney 
35927c8c0b82SPatrick Mooney 	/*
35937c8c0b82SPatrick Mooney 	 * A direct addressing mode makes no sense in the context of an EPT
35947c8c0b82SPatrick Mooney 	 * fault. There has to be a memory access involved to cause the
35957c8c0b82SPatrick Mooney 	 * EPT fault.
35967c8c0b82SPatrick Mooney 	 */
35977c8c0b82SPatrick Mooney 	if (vie->mod == VIE_MOD_DIRECT && !is_movcr)
35987c8c0b82SPatrick Mooney 		return (-1);
35997c8c0b82SPatrick Mooney 
36007c8c0b82SPatrick Mooney 	if ((vie->mod == VIE_MOD_INDIRECT && vie->rm == VIE_RM_DISP32) ||
36017c8c0b82SPatrick Mooney 	    (vie->mod != VIE_MOD_DIRECT && vie->rm == VIE_RM_SIB)) {
36027c8c0b82SPatrick Mooney 		/*
36037c8c0b82SPatrick Mooney 		 * Table 2-5: Special Cases of REX Encodings
36047c8c0b82SPatrick Mooney 		 *
36057c8c0b82SPatrick Mooney 		 * mod=0, r/m=5 is used in the compatibility mode to
36067c8c0b82SPatrick Mooney 		 * indicate a disp32 without a base register.
36077c8c0b82SPatrick Mooney 		 *
36087c8c0b82SPatrick Mooney 		 * mod!=3, r/m=4 is used in the compatibility mode to
36097c8c0b82SPatrick Mooney 		 * indicate that the SIB byte is present.
36107c8c0b82SPatrick Mooney 		 *
36117c8c0b82SPatrick Mooney 		 * The 'b' bit in the REX prefix is don't care in
36127c8c0b82SPatrick Mooney 		 * this case.
36137c8c0b82SPatrick Mooney 		 */
36147c8c0b82SPatrick Mooney 	} else {
36157c8c0b82SPatrick Mooney 		vie->rm |= (vie->rex_b << 3);
36167c8c0b82SPatrick Mooney 	}
36177c8c0b82SPatrick Mooney 
36187c8c0b82SPatrick Mooney 	vie->reg |= (vie->rex_r << 3);
36197c8c0b82SPatrick Mooney 
36207c8c0b82SPatrick Mooney 	/* SIB */
36217c8c0b82SPatrick Mooney 	if (vie->mod != VIE_MOD_DIRECT && vie->rm == VIE_RM_SIB)
36227c8c0b82SPatrick Mooney 		goto done;
36237c8c0b82SPatrick Mooney 
36247c8c0b82SPatrick Mooney 	vie->base_register = gpr_map[vie->rm];
36257c8c0b82SPatrick Mooney 
36267c8c0b82SPatrick Mooney 	switch (vie->mod) {
36277c8c0b82SPatrick Mooney 	case VIE_MOD_INDIRECT_DISP8:
36287c8c0b82SPatrick Mooney 		vie->disp_bytes = 1;
36297c8c0b82SPatrick Mooney 		break;
36307c8c0b82SPatrick Mooney 	case VIE_MOD_INDIRECT_DISP32:
36317c8c0b82SPatrick Mooney 		vie->disp_bytes = 4;
36327c8c0b82SPatrick Mooney 		break;
36337c8c0b82SPatrick Mooney 	case VIE_MOD_INDIRECT:
36347c8c0b82SPatrick Mooney 		if (vie->rm == VIE_RM_DISP32) {
36357c8c0b82SPatrick Mooney 			vie->disp_bytes = 4;
36367c8c0b82SPatrick Mooney 			/*
36377c8c0b82SPatrick Mooney 			 * Table 2-7. RIP-Relative Addressing
36387c8c0b82SPatrick Mooney 			 *
36397c8c0b82SPatrick Mooney 			 * In 64-bit mode mod=00 r/m=101 implies [rip] + disp32
36407c8c0b82SPatrick Mooney 			 * whereas in compatibility mode it just implies disp32.
36417c8c0b82SPatrick Mooney 			 */
36427c8c0b82SPatrick Mooney 
36437c8c0b82SPatrick Mooney 			if (cpu_mode == CPU_MODE_64BIT)
36447c8c0b82SPatrick Mooney 				vie->base_register = VM_REG_GUEST_RIP;
36457c8c0b82SPatrick Mooney 			else
36467c8c0b82SPatrick Mooney 				vie->base_register = VM_REG_LAST;
36477c8c0b82SPatrick Mooney 		}
36487c8c0b82SPatrick Mooney 		break;
36497c8c0b82SPatrick Mooney 	}
36507c8c0b82SPatrick Mooney 
36517c8c0b82SPatrick Mooney done:
36527c8c0b82SPatrick Mooney 	vie_advance(vie);
36537c8c0b82SPatrick Mooney 
36547c8c0b82SPatrick Mooney 	return (0);
36557c8c0b82SPatrick Mooney }
36567c8c0b82SPatrick Mooney 
36577c8c0b82SPatrick Mooney static int
decode_sib(struct vie * vie)36587c8c0b82SPatrick Mooney decode_sib(struct vie *vie)
36597c8c0b82SPatrick Mooney {
36607c8c0b82SPatrick Mooney 	uint8_t x;
36617c8c0b82SPatrick Mooney 
36627c8c0b82SPatrick Mooney 	/* Proceed only if SIB byte is present */
36637c8c0b82SPatrick Mooney 	if (vie->mod == VIE_MOD_DIRECT || vie->rm != VIE_RM_SIB)
36647c8c0b82SPatrick Mooney 		return (0);
36657c8c0b82SPatrick Mooney 
36667c8c0b82SPatrick Mooney 	if (vie_peek(vie, &x))
36677c8c0b82SPatrick Mooney 		return (-1);
36687c8c0b82SPatrick Mooney 
36697c8c0b82SPatrick Mooney 	/* De-construct the SIB byte */
36707c8c0b82SPatrick Mooney 	vie->ss = (x >> 6) & 0x3;
36717c8c0b82SPatrick Mooney 	vie->index = (x >> 3) & 0x7;
36727c8c0b82SPatrick Mooney 	vie->base = (x >> 0) & 0x7;
36737c8c0b82SPatrick Mooney 
36747c8c0b82SPatrick Mooney 	/* Apply the REX prefix modifiers */
36757c8c0b82SPatrick Mooney 	vie->index |= vie->rex_x << 3;
36767c8c0b82SPatrick Mooney 	vie->base |= vie->rex_b << 3;
36777c8c0b82SPatrick Mooney 
36787c8c0b82SPatrick Mooney 	switch (vie->mod) {
36797c8c0b82SPatrick Mooney 	case VIE_MOD_INDIRECT_DISP8:
36807c8c0b82SPatrick Mooney 		vie->disp_bytes = 1;
36817c8c0b82SPatrick Mooney 		break;
36827c8c0b82SPatrick Mooney 	case VIE_MOD_INDIRECT_DISP32:
36837c8c0b82SPatrick Mooney 		vie->disp_bytes = 4;
36847c8c0b82SPatrick Mooney 		break;
36857c8c0b82SPatrick Mooney 	}
36867c8c0b82SPatrick Mooney 
36877c8c0b82SPatrick Mooney 	if (vie->mod == VIE_MOD_INDIRECT &&
36887c8c0b82SPatrick Mooney 	    (vie->base == 5 || vie->base == 13)) {
36897c8c0b82SPatrick Mooney 		/*
36907c8c0b82SPatrick Mooney 		 * Special case when base register is unused if mod = 0
36917c8c0b82SPatrick Mooney 		 * and base = %rbp or %r13.
36927c8c0b82SPatrick Mooney 		 *
36937c8c0b82SPatrick Mooney 		 * Documented in:
36947c8c0b82SPatrick Mooney 		 * Table 2-3: 32-bit Addressing Forms with the SIB Byte
36957c8c0b82SPatrick Mooney 		 * Table 2-5: Special Cases of REX Encodings
36967c8c0b82SPatrick Mooney 		 */
36977c8c0b82SPatrick Mooney 		vie->disp_bytes = 4;
36987c8c0b82SPatrick Mooney 	} else {
36997c8c0b82SPatrick Mooney 		vie->base_register = gpr_map[vie->base];
37007c8c0b82SPatrick Mooney 	}
37017c8c0b82SPatrick Mooney 
37027c8c0b82SPatrick Mooney 	/*
37037c8c0b82SPatrick Mooney 	 * All encodings of 'index' are valid except for %rsp (4).
37047c8c0b82SPatrick Mooney 	 *
37057c8c0b82SPatrick Mooney 	 * Documented in:
37067c8c0b82SPatrick Mooney 	 * Table 2-3: 32-bit Addressing Forms with the SIB Byte
37077c8c0b82SPatrick Mooney 	 * Table 2-5: Special Cases of REX Encodings
37087c8c0b82SPatrick Mooney 	 */
37097c8c0b82SPatrick Mooney 	if (vie->index != 4)
37107c8c0b82SPatrick Mooney 		vie->index_register = gpr_map[vie->index];
37117c8c0b82SPatrick Mooney 
37127c8c0b82SPatrick Mooney 	/* 'scale' makes sense only in the context of an index register */
37137c8c0b82SPatrick Mooney 	if (vie->index_register < VM_REG_LAST)
37147c8c0b82SPatrick Mooney 		vie->scale = 1 << vie->ss;
37157c8c0b82SPatrick Mooney 
37167c8c0b82SPatrick Mooney 	vie_advance(vie);
37177c8c0b82SPatrick Mooney 
37187c8c0b82SPatrick Mooney 	return (0);
37197c8c0b82SPatrick Mooney }
37207c8c0b82SPatrick Mooney 
37217c8c0b82SPatrick Mooney static int
decode_displacement(struct vie * vie)37227c8c0b82SPatrick Mooney decode_displacement(struct vie *vie)
37237c8c0b82SPatrick Mooney {
37247c8c0b82SPatrick Mooney 	int n, i;
37257c8c0b82SPatrick Mooney 	uint8_t x;
37267c8c0b82SPatrick Mooney 
37277c8c0b82SPatrick Mooney 	union {
37287c8c0b82SPatrick Mooney 		char	buf[4];
37297c8c0b82SPatrick Mooney 		int8_t	signed8;
37307c8c0b82SPatrick Mooney 		int32_t	signed32;
37317c8c0b82SPatrick Mooney 	} u;
37327c8c0b82SPatrick Mooney 
37337c8c0b82SPatrick Mooney 	if ((n = vie->disp_bytes) == 0)
37347c8c0b82SPatrick Mooney 		return (0);
37357c8c0b82SPatrick Mooney 
37367c8c0b82SPatrick Mooney 	if (n != 1 && n != 4)
37377c8c0b82SPatrick Mooney 		panic("decode_displacement: invalid disp_bytes %d", n);
37387c8c0b82SPatrick Mooney 
37397c8c0b82SPatrick Mooney 	for (i = 0; i < n; i++) {
37407c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
37417c8c0b82SPatrick Mooney 			return (-1);
37427c8c0b82SPatrick Mooney 
37437c8c0b82SPatrick Mooney 		u.buf[i] = x;
37447c8c0b82SPatrick Mooney 		vie_advance(vie);
37457c8c0b82SPatrick Mooney 	}
37467c8c0b82SPatrick Mooney 
37477c8c0b82SPatrick Mooney 	if (n == 1)
37487c8c0b82SPatrick Mooney 		vie->displacement = u.signed8;		/* sign-extended */
37497c8c0b82SPatrick Mooney 	else
37507c8c0b82SPatrick Mooney 		vie->displacement = u.signed32;		/* sign-extended */
37517c8c0b82SPatrick Mooney 
37527c8c0b82SPatrick Mooney 	return (0);
37537c8c0b82SPatrick Mooney }
37547c8c0b82SPatrick Mooney 
37557c8c0b82SPatrick Mooney static int
decode_immediate(struct vie * vie)37567c8c0b82SPatrick Mooney decode_immediate(struct vie *vie)
37577c8c0b82SPatrick Mooney {
37587c8c0b82SPatrick Mooney 	int i, n;
37597c8c0b82SPatrick Mooney 	uint8_t x;
37607c8c0b82SPatrick Mooney 	union {
37617c8c0b82SPatrick Mooney 		char	buf[4];
37627c8c0b82SPatrick Mooney 		int8_t	signed8;
37637c8c0b82SPatrick Mooney 		int16_t	signed16;
37647c8c0b82SPatrick Mooney 		int32_t	signed32;
37657c8c0b82SPatrick Mooney 	} u;
37667c8c0b82SPatrick Mooney 
37677c8c0b82SPatrick Mooney 	/* Figure out immediate operand size (if any) */
37687c8c0b82SPatrick Mooney 	if (vie->op.op_flags & VIE_OP_F_IMM) {
37697c8c0b82SPatrick Mooney 		/*
37707c8c0b82SPatrick Mooney 		 * Section 2.2.1.5 "Immediates", Intel SDM:
37717c8c0b82SPatrick Mooney 		 * In 64-bit mode the typical size of immediate operands
37727c8c0b82SPatrick Mooney 		 * remains 32-bits. When the operand size if 64-bits, the
37737c8c0b82SPatrick Mooney 		 * processor sign-extends all immediates to 64-bits prior
37747c8c0b82SPatrick Mooney 		 * to their use.
37757c8c0b82SPatrick Mooney 		 */
37767c8c0b82SPatrick Mooney 		if (vie->opsize == 4 || vie->opsize == 8)
37777c8c0b82SPatrick Mooney 			vie->imm_bytes = 4;
37787c8c0b82SPatrick Mooney 		else
37797c8c0b82SPatrick Mooney 			vie->imm_bytes = 2;
37807c8c0b82SPatrick Mooney 	} else if (vie->op.op_flags & VIE_OP_F_IMM8) {
37817c8c0b82SPatrick Mooney 		vie->imm_bytes = 1;
37827c8c0b82SPatrick Mooney 	}
37837c8c0b82SPatrick Mooney 
37847c8c0b82SPatrick Mooney 	if ((n = vie->imm_bytes) == 0)
37857c8c0b82SPatrick Mooney 		return (0);
37867c8c0b82SPatrick Mooney 
37877c8c0b82SPatrick Mooney 	KASSERT(n == 1 || n == 2 || n == 4,
37887c8c0b82SPatrick Mooney 	    ("%s: invalid number of immediate bytes: %d", __func__, n));
37897c8c0b82SPatrick Mooney 
37907c8c0b82SPatrick Mooney 	for (i = 0; i < n; i++) {
37917c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
37927c8c0b82SPatrick Mooney 			return (-1);
37937c8c0b82SPatrick Mooney 
37947c8c0b82SPatrick Mooney 		u.buf[i] = x;
37957c8c0b82SPatrick Mooney 		vie_advance(vie);
37967c8c0b82SPatrick Mooney 	}
37977c8c0b82SPatrick Mooney 
37987c8c0b82SPatrick Mooney 	/* sign-extend the immediate value before use */
37997c8c0b82SPatrick Mooney 	if (n == 1)
38007c8c0b82SPatrick Mooney 		vie->immediate = u.signed8;
38017c8c0b82SPatrick Mooney 	else if (n == 2)
38027c8c0b82SPatrick Mooney 		vie->immediate = u.signed16;
38037c8c0b82SPatrick Mooney 	else
38047c8c0b82SPatrick Mooney 		vie->immediate = u.signed32;
38057c8c0b82SPatrick Mooney 
38067c8c0b82SPatrick Mooney 	return (0);
38077c8c0b82SPatrick Mooney }
38087c8c0b82SPatrick Mooney 
38097c8c0b82SPatrick Mooney static int
decode_moffset(struct vie * vie)38107c8c0b82SPatrick Mooney decode_moffset(struct vie *vie)
38117c8c0b82SPatrick Mooney {
38127c8c0b82SPatrick Mooney 	int i, n;
38137c8c0b82SPatrick Mooney 	uint8_t x;
38147c8c0b82SPatrick Mooney 	union {
38157c8c0b82SPatrick Mooney 		char	buf[8];
38167c8c0b82SPatrick Mooney 		uint64_t u64;
38177c8c0b82SPatrick Mooney 	} u;
38187c8c0b82SPatrick Mooney 
38197c8c0b82SPatrick Mooney 	if ((vie->op.op_flags & VIE_OP_F_MOFFSET) == 0)
38207c8c0b82SPatrick Mooney 		return (0);
38217c8c0b82SPatrick Mooney 
38227c8c0b82SPatrick Mooney 	/*
38237c8c0b82SPatrick Mooney 	 * Section 2.2.1.4, "Direct Memory-Offset MOVs", Intel SDM:
38247c8c0b82SPatrick Mooney 	 * The memory offset size follows the address-size of the instruction.
38257c8c0b82SPatrick Mooney 	 */
38267c8c0b82SPatrick Mooney 	n = vie->addrsize;
38277c8c0b82SPatrick Mooney 	KASSERT(n == 2 || n == 4 || n == 8, ("invalid moffset bytes: %d", n));
38287c8c0b82SPatrick Mooney 
38297c8c0b82SPatrick Mooney 	u.u64 = 0;
38307c8c0b82SPatrick Mooney 	for (i = 0; i < n; i++) {
38317c8c0b82SPatrick Mooney 		if (vie_peek(vie, &x))
38327c8c0b82SPatrick Mooney 			return (-1);
38337c8c0b82SPatrick Mooney 
38347c8c0b82SPatrick Mooney 		u.buf[i] = x;
38357c8c0b82SPatrick Mooney 		vie_advance(vie);
38367c8c0b82SPatrick Mooney 	}
38377c8c0b82SPatrick Mooney 	vie->displacement = u.u64;
38387c8c0b82SPatrick Mooney 	return (0);
38397c8c0b82SPatrick Mooney }
38407c8c0b82SPatrick Mooney 
38417c8c0b82SPatrick Mooney /*
38427c8c0b82SPatrick Mooney  * Verify that the 'guest linear address' provided as collateral of the nested
38437c8c0b82SPatrick Mooney  * page table fault matches with our instruction decoding.
38447c8c0b82SPatrick Mooney  */
38457c8c0b82SPatrick Mooney int
vie_verify_gla(struct vie * vie,struct vm * vm,int cpuid,uint64_t gla)38467c8c0b82SPatrick Mooney vie_verify_gla(struct vie *vie, struct vm *vm, int cpuid, uint64_t gla)
38477c8c0b82SPatrick Mooney {
38487c8c0b82SPatrick Mooney 	int error;
38497c8c0b82SPatrick Mooney 	uint64_t base, segbase, idx, gla2;
38507c8c0b82SPatrick Mooney 	enum vm_reg_name seg;
38517c8c0b82SPatrick Mooney 	struct seg_desc desc;
38527c8c0b82SPatrick Mooney 
38537c8c0b82SPatrick Mooney 	ASSERT((vie->status & VIES_INST_DECODE) != 0);
38547c8c0b82SPatrick Mooney 
38557c8c0b82SPatrick Mooney 	/*
38567c8c0b82SPatrick Mooney 	 * If there was no valid GLA context with the exit, or the decoded
38577c8c0b82SPatrick Mooney 	 * instruction acts on more than one address, verification is done.
38587c8c0b82SPatrick Mooney 	 */
38597c8c0b82SPatrick Mooney 	if (gla == VIE_INVALID_GLA ||
38607c8c0b82SPatrick Mooney 	    (vie->op.op_flags & VIE_OP_F_NO_GLA_VERIFICATION) != 0) {
38617c8c0b82SPatrick Mooney 		return (0);
38627c8c0b82SPatrick Mooney 	}
38637c8c0b82SPatrick Mooney 
38647c8c0b82SPatrick Mooney 	base = 0;
38657c8c0b82SPatrick Mooney 	if (vie->base_register != VM_REG_LAST) {
38667c8c0b82SPatrick Mooney 		error = vm_get_register(vm, cpuid, vie->base_register, &base);
38677c8c0b82SPatrick Mooney 		if (error) {
38687c8c0b82SPatrick Mooney 			printf("verify_gla: error %d getting base reg %d\n",
38697c8c0b82SPatrick Mooney 			    error, vie->base_register);
38707c8c0b82SPatrick Mooney 			return (-1);
38717c8c0b82SPatrick Mooney 		}
38727c8c0b82SPatrick Mooney 
38737c8c0b82SPatrick Mooney 		/*
38747c8c0b82SPatrick Mooney 		 * RIP-relative addressing starts from the following
38757c8c0b82SPatrick Mooney 		 * instruction
38767c8c0b82SPatrick Mooney 		 */
38777c8c0b82SPatrick Mooney 		if (vie->base_register == VM_REG_GUEST_RIP)
38787c8c0b82SPatrick Mooney 			base += vie->num_processed;
38797c8c0b82SPatrick Mooney 	}
38807c8c0b82SPatrick Mooney 
38817c8c0b82SPatrick Mooney 	idx = 0;
38827c8c0b82SPatrick Mooney 	if (vie->index_register != VM_REG_LAST) {
38837c8c0b82SPatrick Mooney 		error = vm_get_register(vm, cpuid, vie->index_register, &idx);
38847c8c0b82SPatrick Mooney 		if (error) {
38857c8c0b82SPatrick Mooney 			printf("verify_gla: error %d getting index reg %d\n",
38867c8c0b82SPatrick Mooney 			    error, vie->index_register);
38877c8c0b82SPatrick Mooney 			return (-1);
38887c8c0b82SPatrick Mooney 		}
38897c8c0b82SPatrick Mooney 	}
38907c8c0b82SPatrick Mooney 
38917c8c0b82SPatrick Mooney 	/*
38927c8c0b82SPatrick Mooney 	 * From "Specifying a Segment Selector", Intel SDM, Vol 1
38937c8c0b82SPatrick Mooney 	 *
38947c8c0b82SPatrick Mooney 	 * In 64-bit mode, segmentation is generally (but not
38957c8c0b82SPatrick Mooney 	 * completely) disabled.  The exceptions are the FS and GS
38967c8c0b82SPatrick Mooney 	 * segments.
38977c8c0b82SPatrick Mooney 	 *
38987c8c0b82SPatrick Mooney 	 * In legacy IA-32 mode, when the ESP or EBP register is used
38997c8c0b82SPatrick Mooney 	 * as the base, the SS segment is the default segment.  For
39007c8c0b82SPatrick Mooney 	 * other data references, except when relative to stack or
39017c8c0b82SPatrick Mooney 	 * string destination the DS segment is the default.  These
39027c8c0b82SPatrick Mooney 	 * can be overridden to allow other segments to be accessed.
39037c8c0b82SPatrick Mooney 	 */
39047c8c0b82SPatrick Mooney 	if (vie->segment_override) {
39057c8c0b82SPatrick Mooney 		seg = vie->segment_register;
39067c8c0b82SPatrick Mooney 	} else if (vie->base_register == VM_REG_GUEST_RSP ||
39077c8c0b82SPatrick Mooney 	    vie->base_register == VM_REG_GUEST_RBP) {
39087c8c0b82SPatrick Mooney 		seg = VM_REG_GUEST_SS;
39097c8c0b82SPatrick Mooney 	} else {
39107c8c0b82SPatrick Mooney 		seg = VM_REG_GUEST_DS;
39117c8c0b82SPatrick Mooney 	}
39127c8c0b82SPatrick Mooney 	if (vie->paging.cpu_mode == CPU_MODE_64BIT &&
39137c8c0b82SPatrick Mooney 	    seg != VM_REG_GUEST_FS && seg != VM_REG_GUEST_GS) {
39147c8c0b82SPatrick Mooney 		segbase = 0;
39157c8c0b82SPatrick Mooney 	} else {
39167c8c0b82SPatrick Mooney 		error = vm_get_seg_desc(vm, cpuid, seg, &desc);
39177c8c0b82SPatrick Mooney 		if (error) {
39187c8c0b82SPatrick Mooney 			printf("verify_gla: error %d getting segment"
39197c8c0b82SPatrick Mooney 			    " descriptor %d", error, vie->segment_register);
39207c8c0b82SPatrick Mooney 			return (-1);
39217c8c0b82SPatrick Mooney 		}
39227c8c0b82SPatrick Mooney 		segbase = desc.base;
39237c8c0b82SPatrick Mooney 	}
39247c8c0b82SPatrick Mooney 
39257c8c0b82SPatrick Mooney 	gla2 = segbase + base + vie->scale * idx + vie->displacement;
39267c8c0b82SPatrick Mooney 	gla2 &= size2mask[vie->addrsize];
39277c8c0b82SPatrick Mooney 	if (gla != gla2) {
39287c8c0b82SPatrick Mooney 		printf("verify_gla mismatch: segbase(0x%0lx)"
39297c8c0b82SPatrick Mooney 		    "base(0x%0lx), scale(%d), index(0x%0lx), "
39307c8c0b82SPatrick Mooney 		    "disp(0x%0lx), gla(0x%0lx), gla2(0x%0lx)\n",
39317c8c0b82SPatrick Mooney 		    segbase, base, vie->scale, idx, vie->displacement,
39327c8c0b82SPatrick Mooney 		    gla, gla2);
39337c8c0b82SPatrick Mooney 		return (-1);
39347c8c0b82SPatrick Mooney 	}
39357c8c0b82SPatrick Mooney 
39367c8c0b82SPatrick Mooney 	return (0);
39377c8c0b82SPatrick Mooney }
39387c8c0b82SPatrick Mooney 
39397c8c0b82SPatrick Mooney int
vie_decode_instruction(struct vie * vie,struct vm * vm,int cpuid,int cs_d)39407c8c0b82SPatrick Mooney vie_decode_instruction(struct vie *vie, struct vm *vm, int cpuid, int cs_d)
39417c8c0b82SPatrick Mooney {
39427c8c0b82SPatrick Mooney 	enum vm_cpu_mode cpu_mode;
39437c8c0b82SPatrick Mooney 
39447c8c0b82SPatrick Mooney 	if ((vie->status & VIES_INST_FETCH) == 0) {
39457c8c0b82SPatrick Mooney 		return (EINVAL);
39467c8c0b82SPatrick Mooney 	}
39477c8c0b82SPatrick Mooney 
39487c8c0b82SPatrick Mooney 	cpu_mode = vie->paging.cpu_mode;
39497c8c0b82SPatrick Mooney 
39507c8c0b82SPatrick Mooney 	if (decode_prefixes(vie, cpu_mode, cs_d))
39517c8c0b82SPatrick Mooney 		return (-1);
39527c8c0b82SPatrick Mooney 
39537c8c0b82SPatrick Mooney 	if (decode_opcode(vie))
39547c8c0b82SPatrick Mooney 		return (-1);
39557c8c0b82SPatrick Mooney 
39567c8c0b82SPatrick Mooney 	if (decode_modrm(vie, cpu_mode))
39577c8c0b82SPatrick Mooney 		return (-1);
39587c8c0b82SPatrick Mooney 
39597c8c0b82SPatrick Mooney 	if (decode_sib(vie))
39607c8c0b82SPatrick Mooney 		return (-1);
39617c8c0b82SPatrick Mooney 
39627c8c0b82SPatrick Mooney 	if (decode_displacement(vie))
39637c8c0b82SPatrick Mooney 		return (-1);
39647c8c0b82SPatrick Mooney 
39657c8c0b82SPatrick Mooney 	if (decode_immediate(vie))
39667c8c0b82SPatrick Mooney 		return (-1);
39677c8c0b82SPatrick Mooney 
39687c8c0b82SPatrick Mooney 	if (decode_moffset(vie))
39697c8c0b82SPatrick Mooney 		return (-1);
39707c8c0b82SPatrick Mooney 
39717c8c0b82SPatrick Mooney 	vie->status |= VIES_INST_DECODE;
39727c8c0b82SPatrick Mooney 
39737c8c0b82SPatrick Mooney 	return (0);
39747c8c0b82SPatrick Mooney }
3975