1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney  *
4bf21cd93STycho Nightingale  * Copyright (c) 2012 Sandvine, Inc.
5bf21cd93STycho Nightingale  * Copyright (c) 2012 NetApp, Inc.
6bf21cd93STycho Nightingale  * All rights reserved.
7bf21cd93STycho Nightingale  *
8bf21cd93STycho Nightingale  * Redistribution and use in source and binary forms, with or without
9bf21cd93STycho Nightingale  * modification, are permitted provided that the following conditions
10bf21cd93STycho Nightingale  * are met:
11bf21cd93STycho Nightingale  * 1. Redistributions of source code must retain the above copyright
12bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer.
13bf21cd93STycho Nightingale  * 2. Redistributions in binary form must reproduce the above copyright
14bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer in the
15bf21cd93STycho Nightingale  *    documentation and/or other materials provided with the distribution.
16bf21cd93STycho Nightingale  *
17bf21cd93STycho Nightingale  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18bf21cd93STycho Nightingale  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf21cd93STycho Nightingale  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bf21cd93STycho Nightingale  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21bf21cd93STycho Nightingale  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bf21cd93STycho Nightingale  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bf21cd93STycho Nightingale  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bf21cd93STycho Nightingale  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bf21cd93STycho Nightingale  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bf21cd93STycho Nightingale  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bf21cd93STycho Nightingale  * SUCH DAMAGE.
28bf21cd93STycho Nightingale  */
29bf21cd93STycho Nightingale /*
30bf21cd93STycho Nightingale  * This file and its contents are supplied under the terms of the
31bf21cd93STycho Nightingale  * Common Development and Distribution License ("CDDL"), version 1.0.
32bf21cd93STycho Nightingale  * You may only use this file in accordance with the terms of version
33bf21cd93STycho Nightingale  * 1.0 of the CDDL.
34bf21cd93STycho Nightingale  *
35bf21cd93STycho Nightingale  * A full copy of the text of the CDDL should have accompanied this
36bf21cd93STycho Nightingale  * source.  A copy of the CDDL is also available via the Internet at
37bf21cd93STycho Nightingale  * http://www.illumos.org/license/CDDL.
38bf21cd93STycho Nightingale  *
39bf21cd93STycho Nightingale  * Copyright 2015 Pluribus Networks Inc.
404c87aefeSPatrick Mooney  * Copyright 2018 Joyent, Inc.
41418ddc26SPatrick Mooney  * Copyright 2021 Oxide Computer Company
42e1ded6bdSAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
43bf21cd93STycho Nightingale  */
44bf21cd93STycho Nightingale 
45bf21cd93STycho Nightingale #include <sys/cdefs.h>
46bf21cd93STycho Nightingale 
47bf21cd93STycho Nightingale #include <sys/param.h>
48bf21cd93STycho Nightingale #include <sys/pcpu.h>
49bf21cd93STycho Nightingale #include <sys/systm.h>
504c87aefeSPatrick Mooney #include <sys/proc.h>
51bf21cd93STycho Nightingale 
52bf21cd93STycho Nightingale #include <machine/vmparam.h>
53bf21cd93STycho Nightingale #include <machine/vmm.h>
54e0c0d44eSPatrick Mooney #include <sys/vmm_kernel.h>
55cf409e3fSDan Cross #include <sys/vmm_vm.h>
56bf21cd93STycho Nightingale 
57e0c0d44eSPatrick Mooney #include <sys/vmm_instruction_emul.h>
58bf21cd93STycho Nightingale #include <x86/psl.h>
59bf21cd93STycho Nightingale #include <x86/specialreg.h>
60bf21cd93STycho Nightingale 
61e0c0d44eSPatrick Mooney #include "vmm_ioport.h"
62e0c0d44eSPatrick Mooney 
63e0c0d44eSPatrick Mooney enum vie_status {
64e0c0d44eSPatrick Mooney 	VIES_INIT		= (1U << 0),
65e0c0d44eSPatrick Mooney 	VIES_MMIO		= (1U << 1),
66e0c0d44eSPatrick Mooney 	VIES_INOUT		= (1U << 2),
677db0d193SPatrick Mooney 	VIES_OTHER		= (1U << 3),
687db0d193SPatrick Mooney 	VIES_INST_FETCH		= (1U << 4),
697db0d193SPatrick Mooney 	VIES_INST_DECODE	= (1U << 5),
707db0d193SPatrick Mooney 	VIES_PENDING_MMIO	= (1U << 6),
717db0d193SPatrick Mooney 	VIES_PENDING_INOUT	= (1U << 7),
727db0d193SPatrick Mooney 	VIES_REPEAT		= (1U << 8),
737db0d193SPatrick Mooney 	VIES_USER_FALLBACK	= (1U << 9),
747db0d193SPatrick Mooney 	VIES_COMPLETE		= (1U << 10),
75e0c0d44eSPatrick Mooney };
76e0c0d44eSPatrick Mooney 
77e0c0d44eSPatrick Mooney /* State of request to perform emulated access (inout or MMIO) */
78e0c0d44eSPatrick Mooney enum vie_req {
79e0c0d44eSPatrick Mooney 	VR_NONE,
80e0c0d44eSPatrick Mooney 	VR_PENDING,
81e0c0d44eSPatrick Mooney 	VR_DONE,
82e0c0d44eSPatrick Mooney };
83e0c0d44eSPatrick Mooney 
84e0c0d44eSPatrick Mooney struct vie_mmio {
85e0c0d44eSPatrick Mooney 	uint64_t		data;
86e0c0d44eSPatrick Mooney 	uint64_t		gpa;
87e0c0d44eSPatrick Mooney 	uint8_t			bytes;
88e0c0d44eSPatrick Mooney 	enum vie_req		state;
89e0c0d44eSPatrick Mooney };
90e0c0d44eSPatrick Mooney 
91e0c0d44eSPatrick Mooney struct vie_op {
92e0c0d44eSPatrick Mooney 	uint8_t		op_byte;	/* actual opcode byte */
93e0c0d44eSPatrick Mooney 	uint8_t		op_type;	/* type of operation (e.g. MOV) */
94e0c0d44eSPatrick Mooney 	uint16_t	op_flags;
95e0c0d44eSPatrick Mooney };
96e0c0d44eSPatrick Mooney 
97e0c0d44eSPatrick Mooney #define	VIE_INST_SIZE	15
98e0c0d44eSPatrick Mooney struct vie {
99e0c0d44eSPatrick Mooney 	uint8_t		inst[VIE_INST_SIZE];	/* instruction bytes */
100e0c0d44eSPatrick Mooney 	uint8_t		num_valid;		/* size of the instruction */
101e0c0d44eSPatrick Mooney 	uint8_t		num_processed;
102e0c0d44eSPatrick Mooney 
103e0c0d44eSPatrick Mooney 	uint8_t		addrsize:4, opsize:4;	/* address and operand sizes */
104e0c0d44eSPatrick Mooney 	uint8_t		rex_w:1,		/* REX prefix */
105e0c0d44eSPatrick Mooney 			rex_r:1,
106e0c0d44eSPatrick Mooney 			rex_x:1,
107e0c0d44eSPatrick Mooney 			rex_b:1,
108e0c0d44eSPatrick Mooney 			rex_present:1,
109e0c0d44eSPatrick Mooney 			repz_present:1,		/* REP/REPE/REPZ prefix */
110e0c0d44eSPatrick Mooney 			repnz_present:1,	/* REPNE/REPNZ prefix */
111e0c0d44eSPatrick Mooney 			opsize_override:1,	/* Operand size override */
112e0c0d44eSPatrick Mooney 			addrsize_override:1,	/* Address size override */
113e0c0d44eSPatrick Mooney 			segment_override:1;	/* Segment override */
114e0c0d44eSPatrick Mooney 
115e0c0d44eSPatrick Mooney 	uint8_t		mod:2,			/* ModRM byte */
116e0c0d44eSPatrick Mooney 			reg:4,
117e0c0d44eSPatrick Mooney 			rm:4;
118e0c0d44eSPatrick Mooney 
119e0c0d44eSPatrick Mooney 	uint8_t		ss:2,			/* SIB byte */
120e0c0d44eSPatrick Mooney 			vex_present:1,		/* VEX prefixed */
121e0c0d44eSPatrick Mooney 			vex_l:1,		/* L bit */
122e0c0d44eSPatrick Mooney 			index:4,		/* SIB byte */
123e0c0d44eSPatrick Mooney 			base:4;			/* SIB byte */
124e0c0d44eSPatrick Mooney 
125e0c0d44eSPatrick Mooney 	uint8_t		disp_bytes;
126e0c0d44eSPatrick Mooney 	uint8_t		imm_bytes;
127e0c0d44eSPatrick Mooney 
128e0c0d44eSPatrick Mooney 	uint8_t		scale;
129e0c0d44eSPatrick Mooney 
1302699b94cSPatrick Mooney 	uint8_t		vex_reg:4,	/* vvvv: first source reg specifier */
1312699b94cSPatrick Mooney 			vex_pp:2,	/* pp */
132e0c0d44eSPatrick Mooney 			_sparebits:2;
133e0c0d44eSPatrick Mooney 
134e0c0d44eSPatrick Mooney 	uint8_t		_sparebytes[2];
135e0c0d44eSPatrick Mooney 
136e0c0d44eSPatrick Mooney 	int		base_register;		/* VM_REG_GUEST_xyz */
137e0c0d44eSPatrick Mooney 	int		index_register;		/* VM_REG_GUEST_xyz */
138e0c0d44eSPatrick Mooney 	int		segment_register;	/* VM_REG_GUEST_xyz */
139e0c0d44eSPatrick Mooney 
140e0c0d44eSPatrick Mooney 	int64_t		displacement;		/* optional addr displacement */
141e0c0d44eSPatrick Mooney 	int64_t		immediate;		/* optional immediate operand */
142e0c0d44eSPatrick Mooney 
143e0c0d44eSPatrick Mooney 	struct vie_op	op;			/* opcode description */
144e0c0d44eSPatrick Mooney 
145e0c0d44eSPatrick Mooney 	enum vie_status	status;
146e0c0d44eSPatrick Mooney 
147e0c0d44eSPatrick Mooney 	struct vm_guest_paging paging;		/* guest paging state */
148e0c0d44eSPatrick Mooney 
149e0c0d44eSPatrick Mooney 	uint64_t	mmio_gpa;		/* faulting GPA */
150e0c0d44eSPatrick Mooney 	struct vie_mmio	mmio_req_read;
151e0c0d44eSPatrick Mooney 	struct vie_mmio	mmio_req_write;
152e0c0d44eSPatrick Mooney 
153e0c0d44eSPatrick Mooney 	struct vm_inout	inout;			/* active in/out op */
154e0c0d44eSPatrick Mooney 	enum vie_req	inout_req_state;
155e0c0d44eSPatrick Mooney 	uint32_t	inout_req_val;		/* value from userspace */
156e0c0d44eSPatrick Mooney };
157e0c0d44eSPatrick Mooney 
158e0c0d44eSPatrick Mooney 
159bf21cd93STycho Nightingale /* struct vie_op.op_type */
160bf21cd93STycho Nightingale enum {
161bf21cd93STycho Nightingale 	VIE_OP_TYPE_NONE = 0,
162bf21cd93STycho Nightingale 	VIE_OP_TYPE_MOV,
163bf21cd93STycho Nightingale 	VIE_OP_TYPE_MOVSX,
164bf21cd93STycho Nightingale 	VIE_OP_TYPE_MOVZX,
165418ddc26SPatrick Mooney 	VIE_OP_TYPE_MOV_CR,
166bf21cd93STycho Nightingale 	VIE_OP_TYPE_AND,
167bf21cd93STycho Nightingale 	VIE_OP_TYPE_OR,
168bf21cd93STycho Nightingale 	VIE_OP_TYPE_SUB,
169bf21cd93STycho Nightingale 	VIE_OP_TYPE_TWO_BYTE,
170bf21cd93STycho Nightingale 	VIE_OP_TYPE_PUSH,
171bf21cd93STycho Nightingale 	VIE_OP_TYPE_CMP,
172bf21cd93STycho Nightingale 	VIE_OP_TYPE_POP,
173bf21cd93STycho Nightingale 	VIE_OP_TYPE_MOVS,
174bf21cd93STycho Nightingale 	VIE_OP_TYPE_GROUP1,
175bf21cd93STycho Nightingale 	VIE_OP_TYPE_STOS,
1764c87aefeSPatrick Mooney 	VIE_OP_TYPE_BITTEST,
1774c87aefeSPatrick Mooney 	VIE_OP_TYPE_TWOB_GRP15,
1784c87aefeSPatrick Mooney 	VIE_OP_TYPE_ADD,
17984659b24SMichael Zeller 	VIE_OP_TYPE_TEST,
180154972afSPatrick Mooney 	VIE_OP_TYPE_BEXTR,
1817db0d193SPatrick Mooney 	VIE_OP_TYPE_CLTS,
1821fde93bfSAndy Fiddaman 	VIE_OP_TYPE_MUL,
183bf21cd93STycho Nightingale 	VIE_OP_TYPE_LAST
184bf21cd93STycho Nightingale };
185bf21cd93STycho Nightingale 
186bf21cd93STycho Nightingale /* struct vie_op.op_flags */
187bf21cd93STycho Nightingale #define	VIE_OP_F_IMM		(1 << 0)  /* 16/32-bit immediate operand */
188bf21cd93STycho Nightingale #define	VIE_OP_F_IMM8		(1 << 1)  /* 8-bit immediate operand */
189bf21cd93STycho Nightingale #define	VIE_OP_F_MOFFSET	(1 << 2)  /* 16/32/64-bit immediate moffset */
190bf21cd93STycho Nightingale #define	VIE_OP_F_NO_MODRM	(1 << 3)
191418ddc26SPatrick Mooney #define	VIE_OP_F_NO_GLA_VERIFICATION	(1 << 4)
192418ddc26SPatrick Mooney #define	VIE_OP_F_REG_REG	(1 << 5)  /* special-case for mov-cr */
193bf21cd93STycho Nightingale 
194154972afSPatrick Mooney static const struct vie_op three_byte_opcodes_0f38[256] = {
195154972afSPatrick Mooney 	[0xF7] = {
196154972afSPatrick Mooney 		.op_byte = 0xF7,
197154972afSPatrick Mooney 		.op_type = VIE_OP_TYPE_BEXTR,
198154972afSPatrick Mooney 	},
199154972afSPatrick Mooney };
200154972afSPatrick Mooney 
201bf21cd93STycho Nightingale static const struct vie_op two_byte_opcodes[256] = {
2027db0d193SPatrick Mooney 	[0x06] = {
2037db0d193SPatrick Mooney 		.op_byte = 0x06,
2047db0d193SPatrick Mooney 		.op_type = VIE_OP_TYPE_CLTS,
2057db0d193SPatrick Mooney 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
2067db0d193SPatrick Mooney 	},
207418ddc26SPatrick Mooney 	[0x20] = {
208418ddc26SPatrick Mooney 		.op_byte = 0x20,
209418ddc26SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV_CR,
210418ddc26SPatrick Mooney 		.op_flags = VIE_OP_F_REG_REG | VIE_OP_F_NO_GLA_VERIFICATION
211418ddc26SPatrick Mooney 	},
212418ddc26SPatrick Mooney 	[0x22] = {
213418ddc26SPatrick Mooney 		.op_byte = 0x22,
214418ddc26SPatrick Mooney 		.op_type = VIE_OP_TYPE_MOV_CR,
215418ddc26SPatrick Mooney 		.op_flags = VIE_OP_F_REG_REG | VIE_OP_F_NO_GLA_VERIFICATION
216418ddc26SPatrick Mooney 	},
2174c87aefeSPatrick Mooney 	[0xAE] = {
218154972afSPatrick Mooney 		.op_byte = 0xAE,
219154972afSPatrick Mooney 		.op_type = VIE_OP_TYPE_TWOB_GRP15,
2204c87aefeSPatrick Mooney 	},
2211fde93bfSAndy Fiddaman 	[0xAF] = {
2221fde93bfSAndy Fiddaman 		.op_byte = 0xAF,
2231fde93bfSAndy Fiddaman 		.op_type = VIE_OP_TYPE_MUL,
2241fde93bfSAndy Fiddaman 	},
225bf21cd93STycho Nightingale 	[0xB6] = {
226bf21cd93STycho Nightingale 		.op_byte = 0xB6,
227bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOVZX,
228bf21cd93STycho Nightingale 	},
229bf21cd93STycho Nightingale 	[0xB7] = {
230bf21cd93STycho Nightingale 		.op_byte = 0xB7,
231bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOVZX,
232bf21cd93STycho Nightingale 	},
2334c87aefeSPatrick Mooney 	[0xBA] = {
2344c87aefeSPatrick Mooney 		.op_byte = 0xBA,
2354c87aefeSPatrick Mooney 		.op_type = VIE_OP_TYPE_BITTEST,
2364c87aefeSPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
2374c87aefeSPatrick Mooney 	},
238bf21cd93STycho Nightingale 	[0xBE] = {
239bf21cd93STycho Nightingale 		.op_byte = 0xBE,
240bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOVSX,
241bf21cd93STycho Nightingale 	},
242bf21cd93STycho Nightingale };
243bf21cd93STycho Nightingale 
244bf21cd93STycho Nightingale static const struct vie_op one_byte_opcodes[256] = {
2454c87aefeSPatrick Mooney 	[0x03] = {
2464c87aefeSPatrick Mooney 		.op_byte = 0x03,
2474c87aefeSPatrick Mooney 		.op_type = VIE_OP_TYPE_ADD,
2484c87aefeSPatrick Mooney 	},
249bf21cd93STycho Nightingale 	[0x0F] = {
250bf21cd93STycho Nightingale 		.op_byte = 0x0F,
251bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_TWO_BYTE
252bf21cd93STycho Nightingale 	},
2534c87aefeSPatrick Mooney 	[0x0B] = {
2544c87aefeSPatrick Mooney 		.op_byte = 0x0B,
2554c87aefeSPatrick Mooney 		.op_type = VIE_OP_TYPE_OR,
2564c87aefeSPatrick Mooney 	},
257bf21cd93STycho Nightingale 	[0x2B] = {
258bf21cd93STycho Nightingale 		.op_byte = 0x2B,
259bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_SUB,
260bf21cd93STycho Nightingale 	},
2614c87aefeSPatrick Mooney 	[0x39] = {
2624c87aefeSPatrick Mooney 		.op_byte = 0x39,
2634c87aefeSPatrick Mooney 		.op_type = VIE_OP_TYPE_CMP,
2644c87aefeSPatrick Mooney 	},
265bf21cd93STycho Nightingale 	[0x3B] = {
266bf21cd93STycho Nightingale 		.op_byte = 0x3B,
267bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_CMP,
268bf21cd93STycho Nightingale 	},
269bf21cd93STycho Nightingale 	[0x88] = {
270bf21cd93STycho Nightingale 		.op_byte = 0x88,
271bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
272bf21cd93STycho Nightingale 	},
273bf21cd93STycho Nightingale 	[0x89] = {
274bf21cd93STycho Nightingale 		.op_byte = 0x89,
275bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
276bf21cd93STycho Nightingale 	},
277bf21cd93STycho Nightingale 	[0x8A] = {
278bf21cd93STycho Nightingale 		.op_byte = 0x8A,
279bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
280bf21cd93STycho Nightingale 	},
281bf21cd93STycho Nightingale 	[0x8B] = {
282bf21cd93STycho Nightingale 		.op_byte = 0x8B,
283bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
284bf21cd93STycho Nightingale 	},
285bf21cd93STycho Nightingale 	[0xA1] = {
286bf21cd93STycho Nightingale 		.op_byte = 0xA1,
287bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
288bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_MOFFSET | VIE_OP_F_NO_MODRM,
289bf21cd93STycho Nightingale 	},
290bf21cd93STycho Nightingale 	[0xA3] = {
291bf21cd93STycho Nightingale 		.op_byte = 0xA3,
292bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
293bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_MOFFSET | VIE_OP_F_NO_MODRM,
294bf21cd93STycho Nightingale 	},
295bf21cd93STycho Nightingale 	[0xA4] = {
296bf21cd93STycho Nightingale 		.op_byte = 0xA4,
297bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOVS,
298bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
299bf21cd93STycho Nightingale 	},
300bf21cd93STycho Nightingale 	[0xA5] = {
301bf21cd93STycho Nightingale 		.op_byte = 0xA5,
302bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOVS,
303bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
304bf21cd93STycho Nightingale 	},
305bf21cd93STycho Nightingale 	[0xAA] = {
306bf21cd93STycho Nightingale 		.op_byte = 0xAA,
307bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_STOS,
308bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
309bf21cd93STycho Nightingale 	},
310bf21cd93STycho Nightingale 	[0xAB] = {
311bf21cd93STycho Nightingale 		.op_byte = 0xAB,
312bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_STOS,
313bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_NO_MODRM | VIE_OP_F_NO_GLA_VERIFICATION
314bf21cd93STycho Nightingale 	},
315bf21cd93STycho Nightingale 	[0xC6] = {
316bf21cd93STycho Nightingale 		/* XXX Group 11 extended opcode - not just MOV */
317bf21cd93STycho Nightingale 		.op_byte = 0xC6,
318bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
319bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_IMM8,
320bf21cd93STycho Nightingale 	},
321bf21cd93STycho Nightingale 	[0xC7] = {
322bf21cd93STycho Nightingale 		.op_byte = 0xC7,
323bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_MOV,
324bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_IMM,
325bf21cd93STycho Nightingale 	},
326bf21cd93STycho Nightingale 	[0x23] = {
327bf21cd93STycho Nightingale 		.op_byte = 0x23,
328bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_AND,
329bf21cd93STycho Nightingale 	},
3304c87aefeSPatrick Mooney 	[0x80] = {
3314c87aefeSPatrick Mooney 		/* Group 1 extended opcode */
3324c87aefeSPatrick Mooney 		.op_byte = 0x80,
3334c87aefeSPatrick Mooney 		.op_type = VIE_OP_TYPE_GROUP1,
3344c87aefeSPatrick Mooney 		.op_flags = VIE_OP_F_IMM8,
3354c87aefeSPatrick Mooney 	},
336bf21cd93STycho Nightingale 	[0x81] = {
3374c87aefeSPatrick Mooney 		/* Group 1 extended opcode */
338bf21cd93STycho Nightingale 		.op_byte = 0x81,
339bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_GROUP1,
340bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_IMM,
341bf21cd93STycho Nightingale 	},
342bf21cd93STycho Nightingale 	[0x83] = {
3434c87aefeSPatrick Mooney 		/* Group 1 extended opcode */
344bf21cd93STycho Nightingale 		.op_byte = 0x83,
345bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_GROUP1,
346bf21cd93STycho Nightingale 		.op_flags = VIE_OP_F_IMM8,
347bf21cd93STycho Nightingale 	},
348bf21cd93STycho Nightingale 	[0x8F] = {
349bf21cd93STycho Nightingale 		/* XXX Group 1A extended opcode - not just POP */
350bf21cd93STycho Nightingale 		.op_byte = 0x8F,
351bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_POP,
352bf21cd93STycho Nightingale 	},
353e1ded6bdSAndy Fiddaman 	[0xF6] = {
354e1ded6bdSAndy Fiddaman 		/* XXX Group 3 extended opcode - not just TEST */
355e1ded6bdSAndy Fiddaman 		.op_byte = 0xF6,
356e1ded6bdSAndy Fiddaman 		.op_type = VIE_OP_TYPE_TEST,
357e1ded6bdSAndy Fiddaman 		.op_flags = VIE_OP_F_IMM8,
358e1ded6bdSAndy Fiddaman 	},
35984659b24SMichael Zeller 	[0xF7] = {
36084659b24SMichael Zeller 		/* XXX Group 3 extended opcode - not just TEST */
36184659b24SMichael Zeller 		.op_byte = 0xF7,
36284659b24SMichael Zeller 		.op_type = VIE_OP_TYPE_TEST,
36384659b24SMichael Zeller 		.op_flags = VIE_OP_F_IMM,
36484659b24SMichael Zeller 	},
365bf21cd93STycho Nightingale 	[0xFF] = {
366bf21cd93STycho Nightingale 		/* XXX Group 5 extended opcode - not just PUSH */
367bf21cd93STycho Nightingale 		.op_byte = 0xFF,
368bf21cd93STycho Nightingale 		.op_type = VIE_OP_TYPE_PUSH,
369bf21cd93STycho Nightingale 	}
370bf21cd93STycho Nightingale };
371bf21cd93STycho Nightingale 
372bf21cd93STycho Nightingale /* struct vie.mod */
373bf21cd93STycho Nightingale #define	VIE_MOD_INDIRECT		0
374bf21cd93STycho Nightingale #define	VIE_MOD_INDIRECT_DISP8		1
375bf21cd93STycho Nightingale #define	VIE_MOD_INDIRECT_DISP32		2
376bf21cd93STycho Nightingale #define	VIE_MOD_DIRECT			3
377bf21cd93STycho Nightingale 
378bf21cd93STycho Nightingale /* struct vie.rm */
379bf21cd93STycho Nightingale #define	VIE_RM_SIB			4
380bf21cd93STycho Nightingale #define	VIE_RM_DISP32			5
381bf21cd93STycho Nightingale 
382bf21cd93STycho Nightingale #define	GB				(1024 * 1024 * 1024)
383bf21cd93STycho Nightingale 
3840153d828SPatrick Mooney 
3850153d828SPatrick Mooney /*
3860153d828SPatrick Mooney  * Paging defines, previously pulled in from machine/pmap.h
3870153d828SPatrick Mooney  */
3880153d828SPatrick Mooney #define	PG_V	(1 << 0) /* Present */
3890153d828SPatrick Mooney #define	PG_RW	(1 << 1) /* Read/Write */
3900153d828SPatrick Mooney #define	PG_U	(1 << 2) /* User/Supervisor */
3910153d828SPatrick Mooney #define	PG_A	(1 << 5) /* Accessed */
3920153d828SPatrick Mooney #define	PG_M	(1 << 6) /* Dirty */
3930153d828SPatrick Mooney #define	PG_PS	(1 << 7) /* Largepage */
3940153d828SPatrick Mooney 
3950153d828SPatrick Mooney /*
3960153d828SPatrick Mooney  * Paging except defines, previously pulled in from machine/pmap.h
3970153d828SPatrick Mooney  */
3980153d828SPatrick Mooney #define	PGEX_P		(1 << 0) /* Non-present/Protection */
3990153d828SPatrick Mooney #define	PGEX_W		(1 << 1) /* Read/Write */
4000153d828SPatrick Mooney #define	PGEX_U		(1 << 2) /* User/Supervisor */
4010153d828SPatrick Mooney #define	PGEX_RSV	(1 << 3) /* (Non-)Reserved */
4020153d828SPatrick Mooney #define	PGEX_I		(1 << 4) /* Instruction */
4030153d828SPatrick Mooney 
4040153d828SPatrick Mooney 
405bf21cd93STycho Nightingale static enum vm_reg_name gpr_map[16] = {
406bf21cd93STycho Nightingale 	VM_REG_GUEST_RAX,
407bf21cd93STycho Nightingale 	VM_REG_GUEST_RCX,
408bf21cd93STycho Nightingale 	VM_REG_GUEST_RDX,
409bf21cd93STycho Nightingale 	VM_REG_GUEST_RBX,
410bf21cd93STycho Nightingale 	VM_REG_GUEST_RSP,
411bf21cd93STycho Nightingale 	VM_REG_GUEST_RBP,
412bf21cd93STycho Nightingale 	VM_REG_GUEST_RSI,
413bf21cd93STycho Nightingale 	VM_REG_GUEST_RDI,
414bf21cd93STycho Nightingale 	VM_REG_GUEST_R8,
415bf21cd93STycho Nightingale 	VM_REG_GUEST_R9,
416bf21cd93STycho Nightingale 	VM_REG_GUEST_R10,
417bf21cd93STycho Nightingale 	VM_REG_GUEST_R11,
418bf21cd93STycho Nightingale 	VM_REG_GUEST_R12,
419bf21cd93STycho Nightingale 	VM_REG_GUEST_R13,
420bf21cd93STycho Nightingale 	VM_REG_GUEST_R14,
421bf21cd93STycho Nightingale 	VM_REG_GUEST_R15
422bf21cd93STycho Nightingale };
423bf21cd93STycho Nightingale 
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 
443418ddc26SPatrick Mooney static enum vm_reg_name cr_map[16] = {
444418ddc26SPatrick Mooney 	VM_REG_GUEST_CR0,
445418ddc26SPatrick Mooney 	VM_REG_LAST,
446418ddc26SPatrick Mooney 	VM_REG_GUEST_CR2,
447418ddc26SPatrick Mooney 	VM_REG_GUEST_CR3,
448418ddc26SPatrick Mooney 	VM_REG_GUEST_CR4,
449418ddc26SPatrick Mooney 	VM_REG_LAST,
450418ddc26SPatrick Mooney 	VM_REG_LAST,
451418ddc26SPatrick Mooney 	VM_REG_LAST,
452418ddc26SPatrick Mooney 	VM_REG_LAST,
453418ddc26SPatrick Mooney 	VM_REG_LAST,
454418ddc26SPatrick Mooney 	VM_REG_LAST,
455418ddc26SPatrick Mooney 	VM_REG_LAST,
456418ddc26SPatrick Mooney 	VM_REG_LAST,
457418ddc26SPatrick Mooney 	VM_REG_LAST,
458418ddc26SPatrick Mooney 	VM_REG_LAST,
459418ddc26SPatrick Mooney 	VM_REG_LAST
460418ddc26SPatrick Mooney };
461418ddc26SPatrick Mooney 
462bf21cd93STycho Nightingale static uint64_t size2mask[] = {
463bf21cd93STycho Nightingale 	[1] = 0xff,
464bf21cd93STycho Nightingale 	[2] = 0xffff,
465bf21cd93STycho Nightingale 	[4] = 0xffffffff,
466bf21cd93STycho Nightingale 	[8] = 0xffffffffffffffff,
467bf21cd93STycho Nightingale };
468bf21cd93STycho Nightingale 
469bf21cd93STycho Nightingale 
470e0c0d44eSPatrick Mooney static int vie_mmio_read(struct vie *vie, struct vm *vm, int cpuid,
471e0c0d44eSPatrick Mooney     uint64_t gpa, uint64_t *rval, int bytes);
472e0c0d44eSPatrick Mooney static int vie_mmio_write(struct vie *vie, struct vm *vm, int cpuid,
473e0c0d44eSPatrick Mooney     uint64_t gpa, uint64_t wval, int bytes);
474e0c0d44eSPatrick Mooney static int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg,
475e0c0d44eSPatrick Mooney     struct seg_desc *desc, uint64_t offset, int length, int addrsize,
476e0c0d44eSPatrick Mooney     int prot, uint64_t *gla);
477e0c0d44eSPatrick Mooney static int vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla);
478e0c0d44eSPatrick Mooney static int vie_alignment_check(int cpl, int size, uint64_t cr0, uint64_t rf,
479e0c0d44eSPatrick Mooney     uint64_t gla);
480e0c0d44eSPatrick Mooney static uint64_t vie_size2mask(int size);
481e0c0d44eSPatrick Mooney 
482e0c0d44eSPatrick Mooney struct vie *
vie_alloc()483e0c0d44eSPatrick Mooney vie_alloc()
484e0c0d44eSPatrick Mooney {
485e0c0d44eSPatrick Mooney 	return (kmem_zalloc(sizeof (struct vie), KM_SLEEP));
486e0c0d44eSPatrick Mooney }
487bf21cd93STycho Nightingale 
488e0c0d44eSPatrick Mooney void
vie_free(struct vie * vie)489e0c0d44eSPatrick Mooney vie_free(struct vie *vie)
490e0c0d44eSPatrick Mooney {
491e0c0d44eSPatrick Mooney 	kmem_free(vie, sizeof (struct vie));
492bf21cd93STycho Nightingale }
493bf21cd93STycho Nightingale 
4947db0d193SPatrick Mooney enum vm_reg_name
vie_regnum_map(uint8_t regnum)4957db0d193SPatrick Mooney vie_regnum_map(uint8_t regnum)
4967db0d193SPatrick Mooney {
4977db0d193SPatrick Mooney 	VERIFY3U(regnum, <, 16);
4987db0d193SPatrick Mooney 	return (gpr_map[regnum]);
4997db0d193SPatrick Mooney }
5007db0d193SPatrick 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 
509bf21cd93STycho Nightingale static void
vie_calc_bytereg(struct vie * vie,enum vm_reg_name * reg,int * lhbr)510bf21cd93STycho Nightingale vie_calc_bytereg(struct vie *vie, enum vm_reg_name *reg, int *lhbr)
511bf21cd93STycho Nightingale {
512bf21cd93STycho Nightingale 	*lhbr = 0;
513bf21cd93STycho Nightingale 	*reg = gpr_map[vie->reg];
514bf21cd93STycho Nightingale 
515bf21cd93STycho Nightingale 	/*
516bf21cd93STycho Nightingale 	 * 64-bit mode imposes limitations on accessing legacy high byte
517bf21cd93STycho Nightingale 	 * registers (lhbr).
518bf21cd93STycho Nightingale 	 *
519bf21cd93STycho Nightingale 	 * The legacy high-byte registers cannot be addressed if the REX
520bf21cd93STycho Nightingale 	 * prefix is present. In this case the values 4, 5, 6 and 7 of the
521bf21cd93STycho Nightingale 	 * 'ModRM:reg' field address %spl, %bpl, %sil and %dil respectively.
522bf21cd93STycho Nightingale 	 *
523bf21cd93STycho Nightingale 	 * If the REX prefix is not present then the values 4, 5, 6 and 7
524bf21cd93STycho Nightingale 	 * of the 'ModRM:reg' field address the legacy high-byte registers,
525bf21cd93STycho Nightingale 	 * %ah, %ch, %dh and %bh respectively.
526bf21cd93STycho Nightingale 	 */
527bf21cd93STycho Nightingale 	if (!vie->rex_present) {
528bf21cd93STycho Nightingale 		if (vie->reg & 0x4) {
529bf21cd93STycho Nightingale 			*lhbr = 1;
530bf21cd93STycho Nightingale 			*reg = gpr_map[vie->reg & 0x3];
531bf21cd93STycho Nightingale 		}
532bf21cd93STycho Nightingale 	}
533bf21cd93STycho Nightingale }
534bf21cd93STycho Nightingale 
535bf21cd93STycho Nightingale static int
vie_read_bytereg(struct vie * vie,struct vm * vm,int vcpuid,uint8_t * rval)5363e1c5f3aSPatrick Mooney vie_read_bytereg(struct vie *vie, struct vm *vm, int vcpuid, uint8_t *rval)
537bf21cd93STycho Nightingale {
538bf21cd93STycho Nightingale 	uint64_t val;
539bf21cd93STycho Nightingale 	int error, lhbr;
540bf21cd93STycho Nightingale 	enum vm_reg_name reg;
541bf21cd93STycho Nightingale 
542bf21cd93STycho Nightingale 	vie_calc_bytereg(vie, &reg, &lhbr);
543bf21cd93STycho Nightingale 	error = vm_get_register(vm, vcpuid, reg, &val);
544bf21cd93STycho Nightingale 
545bf21cd93STycho Nightingale 	/*
546bf21cd93STycho Nightingale 	 * To obtain the value of a legacy high byte register shift the
547bf21cd93STycho Nightingale 	 * base register right by 8 bits (%ah = %rax >> 8).
548bf21cd93STycho Nightingale 	 */
549bf21cd93STycho Nightingale 	if (lhbr)
550bf21cd93STycho Nightingale 		*rval = val >> 8;
551bf21cd93STycho Nightingale 	else
552bf21cd93STycho Nightingale 		*rval = val;
553bf21cd93STycho Nightingale 	return (error);
554bf21cd93STycho Nightingale }
555bf21cd93STycho Nightingale 
556bf21cd93STycho Nightingale static int
vie_write_bytereg(struct vie * vie,struct vm * vm,int vcpuid,uint8_t byte)5573e1c5f3aSPatrick Mooney vie_write_bytereg(struct vie *vie, struct vm *vm, int vcpuid, uint8_t byte)
558bf21cd93STycho Nightingale {
559bf21cd93STycho Nightingale 	uint64_t origval, val, mask;
560bf21cd93STycho Nightingale 	int error, lhbr;
561bf21cd93STycho Nightingale 	enum vm_reg_name reg;
562bf21cd93STycho Nightingale 
563bf21cd93STycho Nightingale 	vie_calc_bytereg(vie, &reg, &lhbr);
564bf21cd93STycho Nightingale 	error = vm_get_register(vm, vcpuid, reg, &origval);
565bf21cd93STycho Nightingale 	if (error == 0) {
566bf21cd93STycho Nightingale 		val = byte;
567bf21cd93STycho Nightingale 		mask = 0xff;
568bf21cd93STycho Nightingale 		if (lhbr) {
569bf21cd93STycho Nightingale 			/*
570bf21cd93STycho Nightingale 			 * Shift left by 8 to store 'byte' in a legacy high
571bf21cd93STycho Nightingale 			 * byte register.
572bf21cd93STycho Nightingale 			 */
573bf21cd93STycho Nightingale 			val <<= 8;
574bf21cd93STycho Nightingale 			mask <<= 8;
575bf21cd93STycho Nightingale 		}
576bf21cd93STycho Nightingale 		val |= origval & ~mask;
577bf21cd93STycho Nightingale 		error = vm_set_register(vm, vcpuid, reg, val);
578bf21cd93STycho Nightingale 	}
579bf21cd93STycho Nightingale 	return (error);
580bf21cd93STycho Nightingale }
581bf21cd93STycho Nightingale 
582e0c0d44eSPatrick Mooney static int
vie_update_register(struct vm * vm,int vcpuid,enum vm_reg_name reg,uint64_t val,int size)5833e1c5f3aSPatrick Mooney vie_update_register(struct vm *vm, int vcpuid, enum vm_reg_name reg,
5843e1c5f3aSPatrick Mooney     uint64_t val, int size)
585bf21cd93STycho Nightingale {
586bf21cd93STycho Nightingale 	int error;
587bf21cd93STycho Nightingale 	uint64_t origval;
588bf21cd93STycho Nightingale 
589bf21cd93STycho Nightingale 	switch (size) {
590bf21cd93STycho Nightingale 	case 1:
591bf21cd93STycho Nightingale 	case 2:
592e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &origval);
593bf21cd93STycho Nightingale 		if (error)
594bf21cd93STycho Nightingale 			return (error);
595bf21cd93STycho Nightingale 		val &= size2mask[size];
596bf21cd93STycho Nightingale 		val |= origval & ~size2mask[size];
597bf21cd93STycho Nightingale 		break;
598bf21cd93STycho Nightingale 	case 4:
599bf21cd93STycho Nightingale 		val &= 0xffffffffUL;
600bf21cd93STycho Nightingale 		break;
601bf21cd93STycho Nightingale 	case 8:
602bf21cd93STycho Nightingale 		break;
603bf21cd93STycho Nightingale 	default:
604bf21cd93STycho Nightingale 		return (EINVAL);
605bf21cd93STycho Nightingale 	}
606bf21cd93STycho Nightingale 
607bf21cd93STycho Nightingale 	error = vm_set_register(vm, vcpuid, reg, val);
608bf21cd93STycho Nightingale 	return (error);
609bf21cd93STycho Nightingale }
610bf21cd93STycho Nightingale 
611e0c0d44eSPatrick Mooney static int
vie_repeat(struct vie * vie)612e0c0d44eSPatrick Mooney vie_repeat(struct vie *vie)
613e0c0d44eSPatrick Mooney {
614e0c0d44eSPatrick Mooney 	vie->status |= VIES_REPEAT;
615e0c0d44eSPatrick Mooney 
616e0c0d44eSPatrick Mooney 	/*
617e0c0d44eSPatrick Mooney 	 * Clear out any cached operation values so the repeated instruction can
618e0c0d44eSPatrick Mooney 	 * begin without using that stale state.  Other state, such as the
619e0c0d44eSPatrick Mooney 	 * decoding results, are kept around as it will not vary between
620e0c0d44eSPatrick Mooney 	 * iterations of a rep-prefixed instruction.
621e0c0d44eSPatrick Mooney 	 */
622e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_MMIO) != 0) {
623e0c0d44eSPatrick Mooney 		vie->mmio_req_read.state = VR_NONE;
624e0c0d44eSPatrick Mooney 		vie->mmio_req_write.state = VR_NONE;
625e0c0d44eSPatrick Mooney 	} else if ((vie->status & VIES_INOUT) != 0) {
626e0c0d44eSPatrick Mooney 		vie->inout_req_state = VR_NONE;
627e0c0d44eSPatrick Mooney 	} else {
628e0c0d44eSPatrick Mooney 		panic("unexpected emulation state");
629e0c0d44eSPatrick Mooney 	}
630e0c0d44eSPatrick Mooney 
631e0c0d44eSPatrick Mooney 	return (EAGAIN);
632e0c0d44eSPatrick Mooney }
633e0c0d44eSPatrick Mooney 
634bf21cd93STycho Nightingale #define	RFLAGS_STATUS_BITS    (PSL_C | PSL_PF | PSL_AF | PSL_Z | PSL_N | PSL_V)
635bf21cd93STycho Nightingale 
636bf21cd93STycho Nightingale /*
637bf21cd93STycho Nightingale  * Return the status flags that would result from doing (x - y).
638bf21cd93STycho Nightingale  */
6392699b94cSPatrick Mooney /* BEGIN CSTYLED */
640bf21cd93STycho Nightingale #define	GETCC(sz)							\
6412699b94cSPatrick Mooney static ulong_t								\
642bf21cd93STycho Nightingale getcc##sz(uint##sz##_t x, uint##sz##_t y)				\
643bf21cd93STycho Nightingale {									\
6442699b94cSPatrick Mooney 	ulong_t rflags;							\
645bf21cd93STycho Nightingale 									\
646bf21cd93STycho Nightingale 	__asm __volatile("sub %2,%1; pushfq; popq %0" :			\
647bf21cd93STycho Nightingale 	    "=r" (rflags), "+r" (x) : "m" (y));				\
648bf21cd93STycho Nightingale 	return (rflags);						\
649bf21cd93STycho Nightingale } struct __hack
6502699b94cSPatrick Mooney /* END CSTYLED */
651bf21cd93STycho Nightingale 
652bf21cd93STycho Nightingale GETCC(8);
653bf21cd93STycho Nightingale GETCC(16);
654bf21cd93STycho Nightingale GETCC(32);
655bf21cd93STycho Nightingale GETCC(64);
656bf21cd93STycho Nightingale 
6572699b94cSPatrick Mooney static ulong_t
getcc(int opsize,uint64_t x,uint64_t y)658bf21cd93STycho Nightingale getcc(int opsize, uint64_t x, uint64_t y)
659bf21cd93STycho Nightingale {
660bf21cd93STycho Nightingale 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
661bf21cd93STycho Nightingale 	    ("getcc: invalid operand size %d", opsize));
662bf21cd93STycho Nightingale 
663bf21cd93STycho Nightingale 	if (opsize == 1)
664bf21cd93STycho Nightingale 		return (getcc8(x, y));
665bf21cd93STycho Nightingale 	else if (opsize == 2)
666bf21cd93STycho Nightingale 		return (getcc16(x, y));
667bf21cd93STycho Nightingale 	else if (opsize == 4)
668bf21cd93STycho Nightingale 		return (getcc32(x, y));
669bf21cd93STycho Nightingale 	else
670bf21cd93STycho Nightingale 		return (getcc64(x, y));
671bf21cd93STycho Nightingale }
672bf21cd93STycho Nightingale 
6734c87aefeSPatrick Mooney /*
6744c87aefeSPatrick Mooney  * Macro creation of functions getaddflags{8,16,32,64}
6754c87aefeSPatrick Mooney  */
6762699b94cSPatrick Mooney /* BEGIN CSTYLED */
6774c87aefeSPatrick Mooney #define	GETADDFLAGS(sz)							\
6782699b94cSPatrick Mooney static ulong_t								\
6794c87aefeSPatrick Mooney getaddflags##sz(uint##sz##_t x, uint##sz##_t y)				\
6804c87aefeSPatrick Mooney {									\
6812699b94cSPatrick Mooney 	ulong_t rflags;							\
6824c87aefeSPatrick Mooney 									\
6834c87aefeSPatrick Mooney 	__asm __volatile("add %2,%1; pushfq; popq %0" :			\
6844c87aefeSPatrick Mooney 	    "=r" (rflags), "+r" (x) : "m" (y));				\
6854c87aefeSPatrick Mooney 	return (rflags);						\
6864c87aefeSPatrick Mooney } struct __hack
6872699b94cSPatrick Mooney /* END CSTYLED */
6884c87aefeSPatrick Mooney 
6894c87aefeSPatrick Mooney GETADDFLAGS(8);
6904c87aefeSPatrick Mooney GETADDFLAGS(16);
6914c87aefeSPatrick Mooney GETADDFLAGS(32);
6924c87aefeSPatrick Mooney GETADDFLAGS(64);
6934c87aefeSPatrick Mooney 
6942699b94cSPatrick Mooney static ulong_t
getaddflags(int opsize,uint64_t x,uint64_t y)6954c87aefeSPatrick Mooney getaddflags(int opsize, uint64_t x, uint64_t y)
6964c87aefeSPatrick Mooney {
6974c87aefeSPatrick Mooney 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
6984c87aefeSPatrick Mooney 	    ("getaddflags: invalid operand size %d", opsize));
6994c87aefeSPatrick Mooney 
7004c87aefeSPatrick Mooney 	if (opsize == 1)
7014c87aefeSPatrick Mooney 		return (getaddflags8(x, y));
7024c87aefeSPatrick Mooney 	else if (opsize == 2)
7034c87aefeSPatrick Mooney 		return (getaddflags16(x, y));
7044c87aefeSPatrick Mooney 	else if (opsize == 4)
7054c87aefeSPatrick Mooney 		return (getaddflags32(x, y));
7064c87aefeSPatrick Mooney 	else
7074c87aefeSPatrick Mooney 		return (getaddflags64(x, y));
7084c87aefeSPatrick Mooney }
7094c87aefeSPatrick Mooney 
7101fde93bfSAndy Fiddaman /*
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 
74484659b24SMichael Zeller /*
74584659b24SMichael Zeller  * Return the status flags that would result from doing (x & y).
74684659b24SMichael Zeller  */
7472699b94cSPatrick Mooney /* BEGIN CSTYLED */
74884659b24SMichael Zeller #define	GETANDFLAGS(sz)							\
7492699b94cSPatrick Mooney static ulong_t								\
75084659b24SMichael Zeller getandflags##sz(uint##sz##_t x, uint##sz##_t y)				\
75184659b24SMichael Zeller {									\
7522699b94cSPatrick Mooney 	ulong_t rflags;							\
75384659b24SMichael Zeller 									\
75484659b24SMichael Zeller 	__asm __volatile("and %2,%1; pushfq; popq %0" :			\
75584659b24SMichael Zeller 	    "=r" (rflags), "+r" (x) : "m" (y));				\
75684659b24SMichael Zeller 	return (rflags);						\
75784659b24SMichael Zeller } struct __hack
7582699b94cSPatrick Mooney /* END CSTYLED */
75984659b24SMichael Zeller 
76084659b24SMichael Zeller GETANDFLAGS(8);
76184659b24SMichael Zeller GETANDFLAGS(16);
76284659b24SMichael Zeller GETANDFLAGS(32);
76384659b24SMichael Zeller GETANDFLAGS(64);
76484659b24SMichael Zeller 
7652699b94cSPatrick Mooney static ulong_t
getandflags(int opsize,uint64_t x,uint64_t y)76684659b24SMichael Zeller getandflags(int opsize, uint64_t x, uint64_t y)
76784659b24SMichael Zeller {
76884659b24SMichael Zeller 	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
76984659b24SMichael Zeller 	    ("getandflags: invalid operand size %d", opsize));
77084659b24SMichael Zeller 
77184659b24SMichael Zeller 	if (opsize == 1)
77284659b24SMichael Zeller 		return (getandflags8(x, y));
77384659b24SMichael Zeller 	else if (opsize == 2)
77484659b24SMichael Zeller 		return (getandflags16(x, y));
77584659b24SMichael Zeller 	else if (opsize == 4)
77684659b24SMichael Zeller 		return (getandflags32(x, y));
77784659b24SMichael Zeller 	else
77884659b24SMichael Zeller 		return (getandflags64(x, y));
77984659b24SMichael Zeller }
78084659b24SMichael Zeller 
781418ddc26SPatrick Mooney static int
vie_emulate_mov_cr(struct vie * vie,struct vm * vm,int vcpuid)782418ddc26SPatrick Mooney vie_emulate_mov_cr(struct vie *vie, struct vm *vm, int vcpuid)
783418ddc26SPatrick Mooney {
784418ddc26SPatrick Mooney 	uint64_t val;
785418ddc26SPatrick Mooney 	int err;
786418ddc26SPatrick Mooney 	enum vm_reg_name gpr = gpr_map[vie->rm];
787418ddc26SPatrick Mooney 	enum vm_reg_name cr = cr_map[vie->reg];
788418ddc26SPatrick Mooney 
789418ddc26SPatrick Mooney 	uint_t size = 4;
790418ddc26SPatrick Mooney 	if (vie->paging.cpu_mode == CPU_MODE_64BIT) {
791418ddc26SPatrick Mooney 		size = 8;
792418ddc26SPatrick Mooney 	}
793418ddc26SPatrick Mooney 
794418ddc26SPatrick Mooney 	switch (vie->op.op_byte) {
795418ddc26SPatrick Mooney 	case 0x20:
796418ddc26SPatrick Mooney 		/*
797418ddc26SPatrick Mooney 		 * MOV control register (ModRM:reg) to reg (ModRM:r/m)
798418ddc26SPatrick Mooney 		 * 20/r:	mov r32, CR0-CR7
799418ddc26SPatrick Mooney 		 * 20/r:	mov r64, CR0-CR7
800418ddc26SPatrick Mooney 		 * REX.R + 20/0:	mov r64, CR8
801418ddc26SPatrick Mooney 		 */
802418ddc26SPatrick Mooney 		if (vie->paging.cpl != 0) {
803418ddc26SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
804418ddc26SPatrick Mooney 			vie->num_processed = 0;
805418ddc26SPatrick Mooney 			return (0);
806418ddc26SPatrick Mooney 		}
807418ddc26SPatrick Mooney 		err = vm_get_register(vm, vcpuid, cr, &val);
808418ddc26SPatrick Mooney 		if (err != 0) {
809418ddc26SPatrick Mooney 			/* #UD for access to non-existent CRs */
810418ddc26SPatrick Mooney 			vm_inject_ud(vm, vcpuid);
811418ddc26SPatrick Mooney 			vie->num_processed = 0;
812418ddc26SPatrick Mooney 			return (0);
813418ddc26SPatrick Mooney 		}
814418ddc26SPatrick Mooney 		err = vie_update_register(vm, vcpuid, gpr, val, size);
815418ddc26SPatrick Mooney 		break;
816418ddc26SPatrick Mooney 	case 0x22: {
817418ddc26SPatrick Mooney 		/*
818418ddc26SPatrick Mooney 		 * MOV reg (ModRM:r/m) to control register (ModRM:reg)
819418ddc26SPatrick Mooney 		 * 22/r:	mov CR0-CR7, r32
820418ddc26SPatrick Mooney 		 * 22/r:	mov CR0-CR7, r64
821418ddc26SPatrick Mooney 		 * REX.R + 22/0:	mov CR8, r64
822418ddc26SPatrick Mooney 		 */
823418ddc26SPatrick Mooney 		uint64_t old, diff;
824418ddc26SPatrick Mooney 
825418ddc26SPatrick Mooney 		if (vie->paging.cpl != 0) {
826418ddc26SPatrick Mooney 			vm_inject_gp(vm, vcpuid);
827418ddc26SPatrick Mooney 			vie->num_processed = 0;
828418ddc26SPatrick Mooney 			return (0);
829418ddc26SPatrick Mooney 		}
830418ddc26SPatrick Mooney 		err = vm_get_register(vm, vcpuid, cr, &old);
831418ddc26SPatrick Mooney 		if (err != 0) {
832418ddc26SPatrick Mooney 			/* #UD for access to non-existent CRs */
833418ddc26SPatrick Mooney 			vm_inject_ud(vm, vcpuid);
834418ddc26SPatrick Mooney 			vie->num_processed = 0;
835418ddc26SPatrick Mooney 			return (0);
836418ddc26SPatrick Mooney 		}
837418ddc26SPatrick Mooney 		err = vm_get_register(vm, vcpuid, gpr, &val);
838418ddc26SPatrick Mooney 		VERIFY0(err);
839418ddc26SPatrick Mooney 		val &= size2mask[size];
840418ddc26SPatrick Mooney 		diff = old ^ val;
841418ddc26SPatrick Mooney 
842418ddc26SPatrick Mooney 		switch (cr) {
843418ddc26SPatrick Mooney 		case VM_REG_GUEST_CR0:
844418ddc26SPatrick Mooney 			if ((diff & CR0_PG) != 0) {
845418ddc26SPatrick Mooney 				uint64_t efer;
846418ddc26SPatrick Mooney 
847418ddc26SPatrick Mooney 				err = vm_get_register(vm, vcpuid,
848418ddc26SPatrick Mooney 				    VM_REG_GUEST_EFER, &efer);
849418ddc26SPatrick Mooney 				VERIFY0(err);
850418ddc26SPatrick Mooney 
851418ddc26SPatrick Mooney 				/* Keep the long-mode state in EFER in sync */
852418ddc26SPatrick Mooney 				if ((val & CR0_PG) != 0 &&
853418ddc26SPatrick Mooney 				    (efer & EFER_LME) != 0) {
854418ddc26SPatrick Mooney 					efer |= EFER_LMA;
855418ddc26SPatrick Mooney 				}
856418ddc26SPatrick Mooney 				if ((val & CR0_PG) == 0 &&
857418ddc26SPatrick Mooney 				    (efer & EFER_LME) != 0) {
858418ddc26SPatrick Mooney 					efer &= ~EFER_LMA;
859418ddc26SPatrick Mooney 				}
860418ddc26SPatrick Mooney 
861418ddc26SPatrick Mooney 				err = vm_set_register(vm, vcpuid,
862418ddc26SPatrick Mooney 				    VM_REG_GUEST_EFER, efer);
863418ddc26SPatrick Mooney 				VERIFY0(err);
864418ddc26SPatrick Mooney 			}
865418ddc26SPatrick Mooney 			/* TODO: enforce more of the #GP checks */
866418ddc26SPatrick Mooney 			err = vm_set_register(vm, vcpuid, cr, val);
867418ddc26SPatrick Mooney 			VERIFY0(err);
868418ddc26SPatrick Mooney 			break;
869418ddc26SPatrick Mooney 		case VM_REG_GUEST_CR2:
870418ddc26SPatrick Mooney 		case VM_REG_GUEST_CR3:
871418ddc26SPatrick Mooney 		case VM_REG_GUEST_CR4:
872418ddc26SPatrick Mooney 			/* TODO: enforce more of the #GP checks */
873418ddc26SPatrick Mooney 			err = vm_set_register(vm, vcpuid, cr, val);
874418ddc26SPatrick Mooney 			break;
875418ddc26SPatrick Mooney 		default:
876418ddc26SPatrick Mooney 			/* The cr_map mapping should prevent this */
877418ddc26SPatrick Mooney 			panic("invalid cr %d", cr);
878418ddc26SPatrick Mooney 		}
879418ddc26SPatrick Mooney 		break;
880418ddc26SPatrick Mooney 	}
881418ddc26SPatrick Mooney 	default:
882418ddc26SPatrick Mooney 		return (EINVAL);
883418ddc26SPatrick Mooney 	}
884418ddc26SPatrick Mooney 	return (err);
885418ddc26SPatrick Mooney }
886418ddc26SPatrick Mooney 
887bf21cd93STycho Nightingale static int
vie_emulate_mov(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)8883e1c5f3aSPatrick Mooney vie_emulate_mov(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
889bf21cd93STycho Nightingale {
890bf21cd93STycho Nightingale 	int error, size;
891bf21cd93STycho Nightingale 	enum vm_reg_name reg;
892bf21cd93STycho Nightingale 	uint8_t byte;
893bf21cd93STycho Nightingale 	uint64_t val;
894bf21cd93STycho Nightingale 
895bf21cd93STycho Nightingale 	size = vie->opsize;
896bf21cd93STycho Nightingale 	error = EINVAL;
897bf21cd93STycho Nightingale 
898bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
899bf21cd93STycho Nightingale 	case 0x88:
900bf21cd93STycho Nightingale 		/*
901bf21cd93STycho Nightingale 		 * MOV byte from reg (ModRM:reg) to mem (ModRM:r/m)
902bf21cd93STycho Nightingale 		 * 88/r:	mov r/m8, r8
903bf21cd93STycho Nightingale 		 * REX + 88/r:	mov r/m8, r8 (%ah, %ch, %dh, %bh not available)
904bf21cd93STycho Nightingale 		 */
905bf21cd93STycho Nightingale 		size = 1;	/* override for byte operation */
906e0c0d44eSPatrick Mooney 		error = vie_read_bytereg(vie, vm, vcpuid, &byte);
9072699b94cSPatrick Mooney 		if (error == 0) {
9082699b94cSPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, byte,
9092699b94cSPatrick Mooney 			    size);
9102699b94cSPatrick Mooney 		}
911bf21cd93STycho Nightingale 		break;
912bf21cd93STycho Nightingale 	case 0x89:
913bf21cd93STycho Nightingale 		/*
914bf21cd93STycho Nightingale 		 * MOV from reg (ModRM:reg) to mem (ModRM:r/m)
915bf21cd93STycho Nightingale 		 * 89/r:	mov r/m16, r16
916bf21cd93STycho Nightingale 		 * 89/r:	mov r/m32, r32
917bf21cd93STycho Nightingale 		 * REX.W + 89/r	mov r/m64, r64
918bf21cd93STycho Nightingale 		 */
919bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
920e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val);
921bf21cd93STycho Nightingale 		if (error == 0) {
922bf21cd93STycho Nightingale 			val &= size2mask[size];
923e0c0d44eSPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
924bf21cd93STycho Nightingale 		}
925bf21cd93STycho Nightingale 		break;
926bf21cd93STycho Nightingale 	case 0x8A:
927bf21cd93STycho Nightingale 		/*
928bf21cd93STycho Nightingale 		 * MOV byte from mem (ModRM:r/m) to reg (ModRM:reg)
929bf21cd93STycho Nightingale 		 * 8A/r:	mov r8, r/m8
930bf21cd93STycho Nightingale 		 * REX + 8A/r:	mov r8, r/m8
931bf21cd93STycho Nightingale 		 */
932bf21cd93STycho Nightingale 		size = 1;	/* override for byte operation */
933e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
934bf21cd93STycho Nightingale 		if (error == 0)
935e0c0d44eSPatrick Mooney 			error = vie_write_bytereg(vie, vm, vcpuid, val);
936bf21cd93STycho Nightingale 		break;
937bf21cd93STycho Nightingale 	case 0x8B:
938bf21cd93STycho Nightingale 		/*
939bf21cd93STycho Nightingale 		 * MOV from mem (ModRM:r/m) to reg (ModRM:reg)
940bf21cd93STycho Nightingale 		 * 8B/r:	mov r16, r/m16
941bf21cd93STycho Nightingale 		 * 8B/r:	mov r32, r/m32
942bf21cd93STycho Nightingale 		 * REX.W 8B/r:	mov r64, r/m64
943bf21cd93STycho Nightingale 		 */
944e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
945bf21cd93STycho Nightingale 		if (error == 0) {
946bf21cd93STycho Nightingale 			reg = gpr_map[vie->reg];
947bf21cd93STycho Nightingale 			error = vie_update_register(vm, vcpuid, reg, val, size);
948bf21cd93STycho Nightingale 		}
949bf21cd93STycho Nightingale 		break;
950bf21cd93STycho Nightingale 	case 0xA1:
951bf21cd93STycho Nightingale 		/*
952bf21cd93STycho Nightingale 		 * MOV from seg:moffset to AX/EAX/RAX
953bf21cd93STycho Nightingale 		 * A1:		mov AX, moffs16
954bf21cd93STycho Nightingale 		 * A1:		mov EAX, moffs32
955bf21cd93STycho Nightingale 		 * REX.W + A1:	mov RAX, moffs64
956bf21cd93STycho Nightingale 		 */
957e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
958bf21cd93STycho Nightingale 		if (error == 0) {
959bf21cd93STycho Nightingale 			reg = VM_REG_GUEST_RAX;
960bf21cd93STycho Nightingale 			error = vie_update_register(vm, vcpuid, reg, val, size);
961bf21cd93STycho Nightingale 		}
962bf21cd93STycho Nightingale 		break;
963bf21cd93STycho Nightingale 	case 0xA3:
964bf21cd93STycho Nightingale 		/*
965bf21cd93STycho Nightingale 		 * MOV from AX/EAX/RAX to seg:moffset
966bf21cd93STycho Nightingale 		 * A3:		mov moffs16, AX
967e0c0d44eSPatrick Mooney 		 * A3:		mov moffs32, EAX
968bf21cd93STycho Nightingale 		 * REX.W + A3:	mov moffs64, RAX
969bf21cd93STycho Nightingale 		 */
970e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RAX, &val);
971bf21cd93STycho Nightingale 		if (error == 0) {
972bf21cd93STycho Nightingale 			val &= size2mask[size];
973e0c0d44eSPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
974bf21cd93STycho Nightingale 		}
975bf21cd93STycho Nightingale 		break;
976bf21cd93STycho Nightingale 	case 0xC6:
977bf21cd93STycho Nightingale 		/*
978bf21cd93STycho Nightingale 		 * MOV from imm8 to mem (ModRM:r/m)
979bf21cd93STycho Nightingale 		 * C6/0		mov r/m8, imm8
980bf21cd93STycho Nightingale 		 * REX + C6/0	mov r/m8, imm8
981bf21cd93STycho Nightingale 		 */
982bf21cd93STycho Nightingale 		size = 1;	/* override for byte operation */
983e0c0d44eSPatrick Mooney 		val = vie->immediate;
984e0c0d44eSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
985bf21cd93STycho Nightingale 		break;
986bf21cd93STycho Nightingale 	case 0xC7:
987bf21cd93STycho Nightingale 		/*
988bf21cd93STycho Nightingale 		 * MOV from imm16/imm32 to mem (ModRM:r/m)
989bf21cd93STycho Nightingale 		 * C7/0		mov r/m16, imm16
990bf21cd93STycho Nightingale 		 * C7/0		mov r/m32, imm32
991bf21cd93STycho Nightingale 		 * REX.W + C7/0	mov r/m64, imm32 (sign-extended to 64-bits)
992bf21cd93STycho Nightingale 		 */
993bf21cd93STycho Nightingale 		val = vie->immediate & size2mask[size];
994e0c0d44eSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
995bf21cd93STycho Nightingale 		break;
996bf21cd93STycho Nightingale 	default:
997bf21cd93STycho Nightingale 		break;
998bf21cd93STycho Nightingale 	}
999bf21cd93STycho Nightingale 
1000bf21cd93STycho Nightingale 	return (error);
1001bf21cd93STycho Nightingale }
1002bf21cd93STycho Nightingale 
1003bf21cd93STycho Nightingale static int
vie_emulate_movx(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)10043e1c5f3aSPatrick Mooney vie_emulate_movx(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1005bf21cd93STycho Nightingale {
1006bf21cd93STycho Nightingale 	int error, size;
1007bf21cd93STycho Nightingale 	enum vm_reg_name reg;
1008bf21cd93STycho Nightingale 	uint64_t val;
1009bf21cd93STycho Nightingale 
1010bf21cd93STycho Nightingale 	size = vie->opsize;
1011bf21cd93STycho Nightingale 	error = EINVAL;
1012bf21cd93STycho Nightingale 
1013bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
1014bf21cd93STycho Nightingale 	case 0xB6:
1015bf21cd93STycho Nightingale 		/*
1016bf21cd93STycho Nightingale 		 * MOV and zero extend byte from mem (ModRM:r/m) to
1017bf21cd93STycho Nightingale 		 * reg (ModRM:reg).
1018bf21cd93STycho Nightingale 		 *
1019bf21cd93STycho Nightingale 		 * 0F B6/r		movzx r16, r/m8
1020bf21cd93STycho Nightingale 		 * 0F B6/r		movzx r32, r/m8
1021bf21cd93STycho Nightingale 		 * REX.W + 0F B6/r	movzx r64, r/m8
1022bf21cd93STycho Nightingale 		 */
1023bf21cd93STycho Nightingale 
1024bf21cd93STycho Nightingale 		/* get the first operand */
1025e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 1);
1026bf21cd93STycho Nightingale 		if (error)
1027bf21cd93STycho Nightingale 			break;
1028bf21cd93STycho Nightingale 
1029bf21cd93STycho Nightingale 		/* get the second operand */
1030bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1031bf21cd93STycho Nightingale 
1032bf21cd93STycho Nightingale 		/* zero-extend byte */
1033bf21cd93STycho Nightingale 		val = (uint8_t)val;
1034bf21cd93STycho Nightingale 
1035bf21cd93STycho Nightingale 		/* write the result */
1036bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, reg, val, size);
1037bf21cd93STycho Nightingale 		break;
1038bf21cd93STycho Nightingale 	case 0xB7:
1039bf21cd93STycho Nightingale 		/*
1040bf21cd93STycho Nightingale 		 * MOV and zero extend word from mem (ModRM:r/m) to
1041bf21cd93STycho Nightingale 		 * reg (ModRM:reg).
1042bf21cd93STycho Nightingale 		 *
1043bf21cd93STycho Nightingale 		 * 0F B7/r		movzx r32, r/m16
1044bf21cd93STycho Nightingale 		 * REX.W + 0F B7/r	movzx r64, r/m16
1045bf21cd93STycho Nightingale 		 */
1046e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 2);
1047bf21cd93STycho Nightingale 		if (error)
1048bf21cd93STycho Nightingale 			return (error);
1049bf21cd93STycho Nightingale 
1050bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1051bf21cd93STycho Nightingale 
1052bf21cd93STycho Nightingale 		/* zero-extend word */
1053bf21cd93STycho Nightingale 		val = (uint16_t)val;
1054bf21cd93STycho Nightingale 
1055bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, reg, val, size);
1056bf21cd93STycho Nightingale 		break;
1057bf21cd93STycho Nightingale 	case 0xBE:
1058bf21cd93STycho Nightingale 		/*
1059bf21cd93STycho Nightingale 		 * MOV and sign extend byte from mem (ModRM:r/m) to
1060bf21cd93STycho Nightingale 		 * reg (ModRM:reg).
1061bf21cd93STycho Nightingale 		 *
1062bf21cd93STycho Nightingale 		 * 0F BE/r		movsx r16, r/m8
1063bf21cd93STycho Nightingale 		 * 0F BE/r		movsx r32, r/m8
1064bf21cd93STycho Nightingale 		 * REX.W + 0F BE/r	movsx r64, r/m8
1065bf21cd93STycho Nightingale 		 */
1066bf21cd93STycho Nightingale 
1067bf21cd93STycho Nightingale 		/* get the first operand */
1068e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, 1);
1069bf21cd93STycho Nightingale 		if (error)
1070bf21cd93STycho Nightingale 			break;
1071bf21cd93STycho Nightingale 
1072bf21cd93STycho Nightingale 		/* get the second operand */
1073bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1074bf21cd93STycho Nightingale 
1075bf21cd93STycho Nightingale 		/* sign extend byte */
1076bf21cd93STycho Nightingale 		val = (int8_t)val;
1077bf21cd93STycho Nightingale 
1078bf21cd93STycho Nightingale 		/* write the result */
1079bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, reg, val, size);
1080bf21cd93STycho Nightingale 		break;
1081bf21cd93STycho Nightingale 	default:
1082bf21cd93STycho Nightingale 		break;
1083bf21cd93STycho Nightingale 	}
1084bf21cd93STycho Nightingale 	return (error);
1085bf21cd93STycho Nightingale }
1086bf21cd93STycho Nightingale 
1087bf21cd93STycho Nightingale /*
1088bf21cd93STycho Nightingale  * Helper function to calculate and validate a linear address.
1089bf21cd93STycho Nightingale  */
1090bf21cd93STycho Nightingale 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)10913e1c5f3aSPatrick Mooney vie_get_gla(struct vie *vie, struct vm *vm, int vcpuid, int opsize,
10923e1c5f3aSPatrick Mooney     int addrsize, int prot, enum vm_reg_name seg, enum vm_reg_name gpr,
10933e1c5f3aSPatrick Mooney     uint64_t *gla)
1094bf21cd93STycho Nightingale {
1095bf21cd93STycho Nightingale 	struct seg_desc desc;
1096bf21cd93STycho Nightingale 	uint64_t cr0, val, rflags;
1097bf21cd93STycho Nightingale 	int error;
1098e0c0d44eSPatrick Mooney 	struct vm_guest_paging *paging;
1099e0c0d44eSPatrick Mooney 
1100e0c0d44eSPatrick Mooney 	paging = &vie->paging;
1101bf21cd93STycho Nightingale 
1102e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &cr0);
1103bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting cr0", __func__, error));
1104bf21cd93STycho Nightingale 
1105e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1106bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
1107bf21cd93STycho Nightingale 
1108bf21cd93STycho Nightingale 	error = vm_get_seg_desc(vm, vcpuid, seg, &desc);
1109bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting segment descriptor %d",
1110bf21cd93STycho Nightingale 	    __func__, error, seg));
1111bf21cd93STycho Nightingale 
1112e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, gpr, &val);
1113bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting register %d", __func__,
1114bf21cd93STycho Nightingale 	    error, gpr));
1115bf21cd93STycho Nightingale 
1116bf21cd93STycho Nightingale 	if (vie_calculate_gla(paging->cpu_mode, seg, &desc, val, opsize,
1117bf21cd93STycho Nightingale 	    addrsize, prot, gla)) {
1118bf21cd93STycho Nightingale 		if (seg == VM_REG_GUEST_SS)
1119bf21cd93STycho Nightingale 			vm_inject_ss(vm, vcpuid, 0);
1120bf21cd93STycho Nightingale 		else
1121bf21cd93STycho Nightingale 			vm_inject_gp(vm, vcpuid);
1122e0c0d44eSPatrick Mooney 		return (-1);
1123bf21cd93STycho Nightingale 	}
1124bf21cd93STycho Nightingale 
1125bf21cd93STycho Nightingale 	if (vie_canonical_check(paging->cpu_mode, *gla)) {
1126bf21cd93STycho Nightingale 		if (seg == VM_REG_GUEST_SS)
1127bf21cd93STycho Nightingale 			vm_inject_ss(vm, vcpuid, 0);
1128bf21cd93STycho Nightingale 		else
1129bf21cd93STycho Nightingale 			vm_inject_gp(vm, vcpuid);
1130e0c0d44eSPatrick Mooney 		return (-1);
1131bf21cd93STycho Nightingale 	}
1132bf21cd93STycho Nightingale 
1133bf21cd93STycho Nightingale 	if (vie_alignment_check(paging->cpl, opsize, cr0, rflags, *gla)) {
1134bf21cd93STycho Nightingale 		vm_inject_ac(vm, vcpuid, 0);
1135e0c0d44eSPatrick Mooney 		return (-1);
1136bf21cd93STycho Nightingale 	}
1137bf21cd93STycho Nightingale 
1138bf21cd93STycho Nightingale 	return (0);
1139bf21cd93STycho Nightingale }
1140bf21cd93STycho Nightingale 
1141bf21cd93STycho Nightingale static int
vie_emulate_movs(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)11423e1c5f3aSPatrick Mooney vie_emulate_movs(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1143bf21cd93STycho Nightingale {
1144bf21cd93STycho Nightingale 	struct vm_copyinfo copyinfo[2];
1145bf21cd93STycho Nightingale 	uint64_t dstaddr, srcaddr, dstgpa, srcgpa, val;
1146bf21cd93STycho Nightingale 	uint64_t rcx, rdi, rsi, rflags;
11474c87aefeSPatrick Mooney 	int error, fault, opsize, seg, repeat;
1148e0c0d44eSPatrick Mooney 	struct vm_guest_paging *paging;
1149bf21cd93STycho Nightingale 
1150bf21cd93STycho Nightingale 	opsize = (vie->op.op_byte == 0xA4) ? 1 : vie->opsize;
1151bf21cd93STycho Nightingale 	val = 0;
1152bf21cd93STycho Nightingale 	error = 0;
1153e0c0d44eSPatrick Mooney 	paging = &vie->paging;
1154bf21cd93STycho Nightingale 
1155bf21cd93STycho Nightingale 	/*
1156bf21cd93STycho Nightingale 	 * XXX although the MOVS instruction is only supposed to be used with
1157bf21cd93STycho Nightingale 	 * the "rep" prefix some guests like FreeBSD will use "repnz" instead.
1158bf21cd93STycho Nightingale 	 *
1159bf21cd93STycho Nightingale 	 * Empirically the "repnz" prefix has identical behavior to "rep"
1160bf21cd93STycho Nightingale 	 * and the zero flag does not make a difference.
1161bf21cd93STycho Nightingale 	 */
1162bf21cd93STycho Nightingale 	repeat = vie->repz_present | vie->repnz_present;
1163bf21cd93STycho Nightingale 
1164bf21cd93STycho Nightingale 	if (repeat) {
1165e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &rcx);
1166bf21cd93STycho Nightingale 		KASSERT(!error, ("%s: error %d getting rcx", __func__, error));
1167bf21cd93STycho Nightingale 
1168bf21cd93STycho Nightingale 		/*
1169bf21cd93STycho Nightingale 		 * The count register is %rcx, %ecx or %cx depending on the
1170bf21cd93STycho Nightingale 		 * address size of the instruction.
1171bf21cd93STycho Nightingale 		 */
11724c87aefeSPatrick Mooney 		if ((rcx & vie_size2mask(vie->addrsize)) == 0) {
11734c87aefeSPatrick Mooney 			error = 0;
11744c87aefeSPatrick Mooney 			goto done;
11754c87aefeSPatrick Mooney 		}
1176bf21cd93STycho Nightingale 	}
1177bf21cd93STycho Nightingale 
1178bf21cd93STycho Nightingale 	/*
1179bf21cd93STycho Nightingale 	 *	Source		Destination	Comments
1180bf21cd93STycho Nightingale 	 *	--------------------------------------------
1181bf21cd93STycho Nightingale 	 * (1)  memory		memory		n/a
1182bf21cd93STycho Nightingale 	 * (2)  memory		mmio		emulated
1183bf21cd93STycho Nightingale 	 * (3)  mmio		memory		emulated
1184bf21cd93STycho Nightingale 	 * (4)  mmio		mmio		emulated
1185bf21cd93STycho Nightingale 	 *
1186bf21cd93STycho Nightingale 	 * At this point we don't have sufficient information to distinguish
1187bf21cd93STycho Nightingale 	 * between (2), (3) and (4). We use 'vm_copy_setup()' to tease this
1188bf21cd93STycho Nightingale 	 * out because it will succeed only when operating on regular memory.
1189bf21cd93STycho Nightingale 	 *
1190bf21cd93STycho Nightingale 	 * XXX the emulation doesn't properly handle the case where 'gpa'
1191bf21cd93STycho Nightingale 	 * is straddling the boundary between the normal memory and MMIO.
1192bf21cd93STycho Nightingale 	 */
1193bf21cd93STycho Nightingale 
1194bf21cd93STycho Nightingale 	seg = vie->segment_override ? vie->segment_register : VM_REG_GUEST_DS;
1195e0c0d44eSPatrick Mooney 	if (vie_get_gla(vie, vm, vcpuid, opsize, vie->addrsize, PROT_READ, seg,
1196e0c0d44eSPatrick Mooney 	    VM_REG_GUEST_RSI, &srcaddr) != 0) {
1197bf21cd93STycho Nightingale 		goto done;
1198e0c0d44eSPatrick Mooney 	}
1199bf21cd93STycho Nightingale 
1200bf21cd93STycho Nightingale 	error = vm_copy_setup(vm, vcpuid, paging, srcaddr, opsize, PROT_READ,
12014c87aefeSPatrick Mooney 	    copyinfo, nitems(copyinfo), &fault);
1202bf21cd93STycho Nightingale 	if (error == 0) {
12034c87aefeSPatrick Mooney 		if (fault)
12044c87aefeSPatrick Mooney 			goto done;	/* Resume guest to handle fault */
12054c87aefeSPatrick Mooney 
1206bf21cd93STycho Nightingale 		/*
1207bf21cd93STycho Nightingale 		 * case (2): read from system memory and write to mmio.
1208bf21cd93STycho Nightingale 		 */
1209bf21cd93STycho Nightingale 		vm_copyin(vm, vcpuid, copyinfo, &val, opsize);
1210bf21cd93STycho Nightingale 		vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
1211e0c0d44eSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, opsize);
1212bf21cd93STycho Nightingale 		if (error)
1213bf21cd93STycho Nightingale 			goto done;
1214bf21cd93STycho Nightingale 	} else {
1215bf21cd93STycho Nightingale 		/*
1216bf21cd93STycho Nightingale 		 * 'vm_copy_setup()' is expected to fail for cases (3) and (4)
1217bf21cd93STycho Nightingale 		 * if 'srcaddr' is in the mmio space.
1218bf21cd93STycho Nightingale 		 */
1219bf21cd93STycho Nightingale 
1220e0c0d44eSPatrick Mooney 		if (vie_get_gla(vie, vm, vcpuid, opsize, vie->addrsize,
1221e0c0d44eSPatrick Mooney 		    PROT_WRITE, VM_REG_GUEST_ES, VM_REG_GUEST_RDI,
1222e0c0d44eSPatrick Mooney 		    &dstaddr) != 0) {
1223bf21cd93STycho Nightingale 			goto done;
1224e0c0d44eSPatrick Mooney 		}
1225bf21cd93STycho Nightingale 
1226bf21cd93STycho Nightingale 		error = vm_copy_setup(vm, vcpuid, paging, dstaddr, opsize,
12274c87aefeSPatrick Mooney 		    PROT_WRITE, copyinfo, nitems(copyinfo), &fault);
1228bf21cd93STycho Nightingale 		if (error == 0) {
12294c87aefeSPatrick Mooney 			if (fault)
12304c87aefeSPatrick Mooney 				goto done;    /* Resume guest to handle fault */
12314c87aefeSPatrick Mooney 
1232bf21cd93STycho Nightingale 			/*
1233bf21cd93STycho Nightingale 			 * case (3): read from MMIO and write to system memory.
1234bf21cd93STycho Nightingale 			 *
1235bf21cd93STycho Nightingale 			 * A MMIO read can have side-effects so we
1236bf21cd93STycho Nightingale 			 * commit to it only after vm_copy_setup() is
1237bf21cd93STycho Nightingale 			 * successful. If a page-fault needs to be
1238bf21cd93STycho Nightingale 			 * injected into the guest then it will happen
1239bf21cd93STycho Nightingale 			 * before the MMIO read is attempted.
1240bf21cd93STycho Nightingale 			 */
1241e0c0d44eSPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, gpa, &val,
1242e0c0d44eSPatrick Mooney 			    opsize);
1243bf21cd93STycho Nightingale 
124483cd75bbSPatrick Mooney 			if (error == 0) {
124583cd75bbSPatrick Mooney 				vm_copyout(vm, vcpuid, &val, copyinfo, opsize);
124683cd75bbSPatrick Mooney 			}
124783cd75bbSPatrick Mooney 			/*
124883cd75bbSPatrick Mooney 			 * Regardless of whether the MMIO read was successful or
124983cd75bbSPatrick Mooney 			 * not, the copy resources must be cleaned up.
125083cd75bbSPatrick Mooney 			 */
125183cd75bbSPatrick Mooney 			vm_copy_teardown(vm, vcpuid, copyinfo,
125283cd75bbSPatrick Mooney 			    nitems(copyinfo));
125383cd75bbSPatrick Mooney 			if (error != 0) {
125483cd75bbSPatrick Mooney 				goto done;
125583cd75bbSPatrick Mooney 			}
1256bf21cd93STycho Nightingale 		} else {
1257bf21cd93STycho Nightingale 			/*
1258bf21cd93STycho Nightingale 			 * Case (4): read from and write to mmio.
12594c87aefeSPatrick Mooney 			 *
12604c87aefeSPatrick Mooney 			 * Commit to the MMIO read/write (with potential
12614c87aefeSPatrick Mooney 			 * side-effects) only after we are sure that the
12624c87aefeSPatrick Mooney 			 * instruction is not going to be restarted due
12634c87aefeSPatrick Mooney 			 * to address translation faults.
1264bf21cd93STycho Nightingale 			 */
1265bf21cd93STycho Nightingale 			error = vm_gla2gpa(vm, vcpuid, paging, srcaddr,
12664c87aefeSPatrick Mooney 			    PROT_READ, &srcgpa, &fault);
12674c87aefeSPatrick Mooney 			if (error || fault)
1268bf21cd93STycho Nightingale 				goto done;
1269bf21cd93STycho Nightingale 
1270bf21cd93STycho Nightingale 			error = vm_gla2gpa(vm, vcpuid, paging, dstaddr,
12712699b94cSPatrick Mooney 			    PROT_WRITE, &dstgpa, &fault);
12724c87aefeSPatrick Mooney 			if (error || fault)
12734c87aefeSPatrick Mooney 				goto done;
12744c87aefeSPatrick Mooney 
1275e0c0d44eSPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, srcgpa, &val,
1276e0c0d44eSPatrick Mooney 			    opsize);
1277bf21cd93STycho Nightingale 			if (error)
1278bf21cd93STycho Nightingale 				goto done;
12794c87aefeSPatrick Mooney 
1280e0c0d44eSPatrick Mooney 			error = vie_mmio_write(vie, vm, vcpuid, dstgpa, val,
1281e0c0d44eSPatrick Mooney 			    opsize);
1282bf21cd93STycho Nightingale 			if (error)
1283bf21cd93STycho Nightingale 				goto done;
1284bf21cd93STycho Nightingale 		}
1285bf21cd93STycho Nightingale 	}
1286bf21cd93STycho Nightingale 
1287e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RSI, &rsi);
1288bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rsi", __func__, error));
1289bf21cd93STycho Nightingale 
1290e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RDI, &rdi);
1291bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rdi", __func__, error));
1292bf21cd93STycho Nightingale 
1293e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1294bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
1295bf21cd93STycho Nightingale 
1296bf21cd93STycho Nightingale 	if (rflags & PSL_D) {
1297bf21cd93STycho Nightingale 		rsi -= opsize;
1298bf21cd93STycho Nightingale 		rdi -= opsize;
1299bf21cd93STycho Nightingale 	} else {
1300bf21cd93STycho Nightingale 		rsi += opsize;
1301bf21cd93STycho Nightingale 		rdi += opsize;
1302bf21cd93STycho Nightingale 	}
1303bf21cd93STycho Nightingale 
1304bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RSI, rsi,
1305bf21cd93STycho Nightingale 	    vie->addrsize);
1306bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d updating rsi", __func__, error));
1307bf21cd93STycho Nightingale 
1308bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RDI, rdi,
1309bf21cd93STycho Nightingale 	    vie->addrsize);
1310bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d updating rdi", __func__, error));
1311bf21cd93STycho Nightingale 
1312bf21cd93STycho Nightingale 	if (repeat) {
1313bf21cd93STycho Nightingale 		rcx = rcx - 1;
1314bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
1315bf21cd93STycho Nightingale 		    rcx, vie->addrsize);
1316bf21cd93STycho Nightingale 		KASSERT(!error, ("%s: error %d updating rcx", __func__, error));
1317bf21cd93STycho Nightingale 
1318bf21cd93STycho Nightingale 		/*
1319bf21cd93STycho Nightingale 		 * Repeat the instruction if the count register is not zero.
1320bf21cd93STycho Nightingale 		 */
1321bf21cd93STycho Nightingale 		if ((rcx & vie_size2mask(vie->addrsize)) != 0)
1322e0c0d44eSPatrick Mooney 			return (vie_repeat(vie));
1323bf21cd93STycho Nightingale 	}
1324bf21cd93STycho Nightingale done:
13254c87aefeSPatrick Mooney 	return (error);
1326bf21cd93STycho Nightingale }
1327bf21cd93STycho Nightingale 
1328bf21cd93STycho Nightingale static int
vie_emulate_stos(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)13293e1c5f3aSPatrick Mooney vie_emulate_stos(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1330bf21cd93STycho Nightingale {
1331bf21cd93STycho Nightingale 	int error, opsize, repeat;
1332bf21cd93STycho Nightingale 	uint64_t val;
1333bf21cd93STycho Nightingale 	uint64_t rcx, rdi, rflags;
1334bf21cd93STycho Nightingale 
1335bf21cd93STycho Nightingale 	opsize = (vie->op.op_byte == 0xAA) ? 1 : vie->opsize;
1336bf21cd93STycho Nightingale 	repeat = vie->repz_present | vie->repnz_present;
1337bf21cd93STycho Nightingale 
1338bf21cd93STycho Nightingale 	if (repeat) {
1339e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &rcx);
1340bf21cd93STycho Nightingale 		KASSERT(!error, ("%s: error %d getting rcx", __func__, error));
1341bf21cd93STycho Nightingale 
1342bf21cd93STycho Nightingale 		/*
1343bf21cd93STycho Nightingale 		 * The count register is %rcx, %ecx or %cx depending on the
1344bf21cd93STycho Nightingale 		 * address size of the instruction.
1345bf21cd93STycho Nightingale 		 */
1346bf21cd93STycho Nightingale 		if ((rcx & vie_size2mask(vie->addrsize)) == 0)
1347bf21cd93STycho Nightingale 			return (0);
1348bf21cd93STycho Nightingale 	}
1349bf21cd93STycho Nightingale 
1350e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RAX, &val);
1351bf21cd93STycho Nightingale 	KASSERT(!error, ("%s: error %d getting rax", __func__, error));
1352bf21cd93STycho Nightingale 
1353e0c0d44eSPatrick Mooney 	error = vie_mmio_write(vie, vm, vcpuid, gpa, val, opsize);
1354bf21cd93STycho Nightingale 	if (error)
1355bf21cd93STycho Nightingale 		return (error);
1356bf21cd93STycho Nightingale 
1357e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RDI, &rdi);
1358bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rdi", __func__, error));
1359bf21cd93STycho Nightingale 
1360e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1361bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
1362bf21cd93STycho Nightingale 
1363bf21cd93STycho Nightingale 	if (rflags & PSL_D)
1364bf21cd93STycho Nightingale 		rdi -= opsize;
1365bf21cd93STycho Nightingale 	else
1366bf21cd93STycho Nightingale 		rdi += opsize;
1367bf21cd93STycho Nightingale 
1368bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RDI, rdi,
1369bf21cd93STycho Nightingale 	    vie->addrsize);
1370bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d updating rdi", __func__, error));
1371bf21cd93STycho Nightingale 
1372bf21cd93STycho Nightingale 	if (repeat) {
1373bf21cd93STycho Nightingale 		rcx = rcx - 1;
1374bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
1375bf21cd93STycho Nightingale 		    rcx, vie->addrsize);
1376bf21cd93STycho Nightingale 		KASSERT(!error, ("%s: error %d updating rcx", __func__, error));
1377bf21cd93STycho Nightingale 
1378bf21cd93STycho Nightingale 		/*
1379bf21cd93STycho Nightingale 		 * Repeat the instruction if the count register is not zero.
1380bf21cd93STycho Nightingale 		 */
1381bf21cd93STycho Nightingale 		if ((rcx & vie_size2mask(vie->addrsize)) != 0)
1382e0c0d44eSPatrick Mooney 			return (vie_repeat(vie));
1383bf21cd93STycho Nightingale 	}
1384bf21cd93STycho Nightingale 
1385bf21cd93STycho Nightingale 	return (0);
1386bf21cd93STycho Nightingale }
1387bf21cd93STycho Nightingale 
1388bf21cd93STycho Nightingale static int
vie_emulate_and(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)13893e1c5f3aSPatrick Mooney vie_emulate_and(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1390bf21cd93STycho Nightingale {
1391bf21cd93STycho Nightingale 	int error, size;
1392bf21cd93STycho Nightingale 	enum vm_reg_name reg;
1393bf21cd93STycho Nightingale 	uint64_t result, rflags, rflags2, val1, val2;
1394bf21cd93STycho Nightingale 
1395bf21cd93STycho Nightingale 	size = vie->opsize;
1396bf21cd93STycho Nightingale 	error = EINVAL;
1397bf21cd93STycho Nightingale 
1398bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
1399bf21cd93STycho Nightingale 	case 0x23:
1400bf21cd93STycho Nightingale 		/*
1401bf21cd93STycho Nightingale 		 * AND reg (ModRM:reg) and mem (ModRM:r/m) and store the
1402bf21cd93STycho Nightingale 		 * result in reg.
1403bf21cd93STycho Nightingale 		 *
1404bf21cd93STycho Nightingale 		 * 23/r		and r16, r/m16
1405bf21cd93STycho Nightingale 		 * 23/r		and r32, r/m32
1406bf21cd93STycho Nightingale 		 * REX.W + 23/r	and r64, r/m64
1407bf21cd93STycho Nightingale 		 */
1408bf21cd93STycho Nightingale 
1409bf21cd93STycho Nightingale 		/* get the first operand */
1410bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1411e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
1412bf21cd93STycho Nightingale 		if (error)
1413bf21cd93STycho Nightingale 			break;
1414bf21cd93STycho Nightingale 
1415bf21cd93STycho Nightingale 		/* get the second operand */
1416e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
1417bf21cd93STycho Nightingale 		if (error)
1418bf21cd93STycho Nightingale 			break;
1419bf21cd93STycho Nightingale 
1420bf21cd93STycho Nightingale 		/* perform the operation and write the result */
1421bf21cd93STycho Nightingale 		result = val1 & val2;
1422bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, reg, result, size);
1423bf21cd93STycho Nightingale 		break;
1424bf21cd93STycho Nightingale 	case 0x81:
1425bf21cd93STycho Nightingale 	case 0x83:
1426bf21cd93STycho Nightingale 		/*
1427bf21cd93STycho Nightingale 		 * AND mem (ModRM:r/m) with immediate and store the
1428bf21cd93STycho Nightingale 		 * result in mem.
1429bf21cd93STycho Nightingale 		 *
1430bf21cd93STycho Nightingale 		 * 81 /4		and r/m16, imm16
1431bf21cd93STycho Nightingale 		 * 81 /4		and r/m32, imm32
1432bf21cd93STycho Nightingale 		 * REX.W + 81 /4	and r/m64, imm32 sign-extended to 64
1433bf21cd93STycho Nightingale 		 *
1434bf21cd93STycho Nightingale 		 * 83 /4		and r/m16, imm8 sign-extended to 16
1435bf21cd93STycho Nightingale 		 * 83 /4		and r/m32, imm8 sign-extended to 32
1436bf21cd93STycho Nightingale 		 * REX.W + 83/4		and r/m64, imm8 sign-extended to 64
1437bf21cd93STycho Nightingale 		 */
1438bf21cd93STycho Nightingale 
1439bf21cd93STycho Nightingale 		/* get the first operand */
14402699b94cSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val1, size);
14412699b94cSPatrick Mooney 		if (error)
1442bf21cd93STycho Nightingale 			break;
1443bf21cd93STycho Nightingale 
14442699b94cSPatrick Mooney 		/*
1445bf21cd93STycho Nightingale 		 * perform the operation with the pre-fetched immediate
1446bf21cd93STycho Nightingale 		 * operand and write the result
1447bf21cd93STycho Nightingale 		 */
14482699b94cSPatrick Mooney 		result = val1 & vie->immediate;
14492699b94cSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, result, size);
1450bf21cd93STycho Nightingale 		break;
1451bf21cd93STycho Nightingale 	default:
1452bf21cd93STycho Nightingale 		break;
1453bf21cd93STycho Nightingale 	}
1454bf21cd93STycho Nightingale 	if (error)
1455bf21cd93STycho Nightingale 		return (error);
1456bf21cd93STycho Nightingale 
1457e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1458bf21cd93STycho Nightingale 	if (error)
1459bf21cd93STycho Nightingale 		return (error);
1460bf21cd93STycho Nightingale 
1461bf21cd93STycho Nightingale 	/*
1462bf21cd93STycho Nightingale 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
1463bf21cd93STycho Nightingale 	 * to the result; AF is undefined.
1464bf21cd93STycho Nightingale 	 *
1465bf21cd93STycho Nightingale 	 * The updated status flags are obtained by subtracting 0 from 'result'.
1466bf21cd93STycho Nightingale 	 */
1467bf21cd93STycho Nightingale 	rflags2 = getcc(size, result, 0);
1468bf21cd93STycho Nightingale 	rflags &= ~RFLAGS_STATUS_BITS;
1469bf21cd93STycho Nightingale 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
1470bf21cd93STycho Nightingale 
1471bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
1472bf21cd93STycho Nightingale 	return (error);
1473bf21cd93STycho Nightingale }
1474bf21cd93STycho Nightingale 
1475bf21cd93STycho Nightingale static int
vie_emulate_or(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)14763e1c5f3aSPatrick Mooney vie_emulate_or(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1477bf21cd93STycho Nightingale {
1478bf21cd93STycho Nightingale 	int error, size;
14794c87aefeSPatrick Mooney 	enum vm_reg_name reg;
14804c87aefeSPatrick Mooney 	uint64_t result, rflags, rflags2, val1, val2;
1481bf21cd93STycho Nightingale 
1482bf21cd93STycho Nightingale 	size = vie->opsize;
1483bf21cd93STycho Nightingale 	error = EINVAL;
1484bf21cd93STycho Nightingale 
1485bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
14864c87aefeSPatrick Mooney 	case 0x0B:
14874c87aefeSPatrick Mooney 		/*
14884c87aefeSPatrick Mooney 		 * OR reg (ModRM:reg) and mem (ModRM:r/m) and store the
14894c87aefeSPatrick Mooney 		 * result in reg.
14904c87aefeSPatrick Mooney 		 *
14912699b94cSPatrick Mooney 		 * 0b/r		or r16, r/m16
14922699b94cSPatrick Mooney 		 * 0b/r		or r32, r/m32
14932699b94cSPatrick Mooney 		 * REX.W + 0b/r	or r64, r/m64
14944c87aefeSPatrick Mooney 		 */
14954c87aefeSPatrick Mooney 
14964c87aefeSPatrick Mooney 		/* get the first operand */
14974c87aefeSPatrick Mooney 		reg = gpr_map[vie->reg];
1498e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
14994c87aefeSPatrick Mooney 		if (error)
15004c87aefeSPatrick Mooney 			break;
1501e0c0d44eSPatrick Mooney 
15024c87aefeSPatrick Mooney 		/* get the second operand */
1503e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
15044c87aefeSPatrick Mooney 		if (error)
15054c87aefeSPatrick Mooney 			break;
15064c87aefeSPatrick Mooney 
15074c87aefeSPatrick Mooney 		/* perform the operation and write the result */
15084c87aefeSPatrick Mooney 		result = val1 | val2;
15094c87aefeSPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, result, size);
15104c87aefeSPatrick Mooney 		break;
1511bf21cd93STycho Nightingale 	case 0x81:
1512bf21cd93STycho Nightingale 	case 0x83:
1513bf21cd93STycho Nightingale 		/*
1514bf21cd93STycho Nightingale 		 * OR mem (ModRM:r/m) with immediate and store the
1515bf21cd93STycho Nightingale 		 * result in mem.
1516bf21cd93STycho Nightingale 		 *
1517bf21cd93STycho Nightingale 		 * 81 /1		or r/m16, imm16
1518bf21cd93STycho Nightingale 		 * 81 /1		or r/m32, imm32
1519bf21cd93STycho Nightingale 		 * REX.W + 81 /1	or r/m64, imm32 sign-extended to 64
1520bf21cd93STycho Nightingale 		 *
1521bf21cd93STycho Nightingale 		 * 83 /1		or r/m16, imm8 sign-extended to 16
1522bf21cd93STycho Nightingale 		 * 83 /1		or r/m32, imm8 sign-extended to 32
1523bf21cd93STycho Nightingale 		 * REX.W + 83/1		or r/m64, imm8 sign-extended to 64
1524bf21cd93STycho Nightingale 		 */
1525bf21cd93STycho Nightingale 
1526bf21cd93STycho Nightingale 		/* get the first operand */
15272699b94cSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val1, size);
15282699b94cSPatrick Mooney 		if (error)
1529bf21cd93STycho Nightingale 			break;
1530bf21cd93STycho Nightingale 
15312699b94cSPatrick Mooney 		/*
1532bf21cd93STycho Nightingale 		 * perform the operation with the pre-fetched immediate
1533bf21cd93STycho Nightingale 		 * operand and write the result
1534bf21cd93STycho Nightingale 		 */
15352699b94cSPatrick Mooney 		result = val1 | vie->immediate;
15362699b94cSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, result, size);
1537bf21cd93STycho Nightingale 		break;
1538bf21cd93STycho Nightingale 	default:
1539bf21cd93STycho Nightingale 		break;
1540bf21cd93STycho Nightingale 	}
1541bf21cd93STycho Nightingale 	if (error)
1542bf21cd93STycho Nightingale 		return (error);
1543bf21cd93STycho Nightingale 
1544e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1545bf21cd93STycho Nightingale 	if (error)
1546bf21cd93STycho Nightingale 		return (error);
1547bf21cd93STycho Nightingale 
1548bf21cd93STycho Nightingale 	/*
1549bf21cd93STycho Nightingale 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
1550bf21cd93STycho Nightingale 	 * to the result; AF is undefined.
1551bf21cd93STycho Nightingale 	 *
1552bf21cd93STycho Nightingale 	 * The updated status flags are obtained by subtracting 0 from 'result'.
1553bf21cd93STycho Nightingale 	 */
1554bf21cd93STycho Nightingale 	rflags2 = getcc(size, result, 0);
1555bf21cd93STycho Nightingale 	rflags &= ~RFLAGS_STATUS_BITS;
1556bf21cd93STycho Nightingale 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
1557bf21cd93STycho Nightingale 
1558bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
1559bf21cd93STycho Nightingale 	return (error);
1560bf21cd93STycho Nightingale }
1561bf21cd93STycho Nightingale 
1562bf21cd93STycho Nightingale static int
vie_emulate_cmp(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)15633e1c5f3aSPatrick Mooney vie_emulate_cmp(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1564bf21cd93STycho Nightingale {
1565bf21cd93STycho Nightingale 	int error, size;
15664c87aefeSPatrick Mooney 	uint64_t regop, memop, op1, op2, rflags, rflags2;
1567bf21cd93STycho Nightingale 	enum vm_reg_name reg;
1568bf21cd93STycho Nightingale 
1569bf21cd93STycho Nightingale 	size = vie->opsize;
1570bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
15714c87aefeSPatrick Mooney 	case 0x39:
1572bf21cd93STycho Nightingale 	case 0x3B:
1573bf21cd93STycho Nightingale 		/*
15744c87aefeSPatrick Mooney 		 * 39/r		CMP r/m16, r16
15754c87aefeSPatrick Mooney 		 * 39/r		CMP r/m32, r32
15764c87aefeSPatrick Mooney 		 * REX.W 39/r	CMP r/m64, r64
15774c87aefeSPatrick Mooney 		 *
1578bf21cd93STycho Nightingale 		 * 3B/r		CMP r16, r/m16
1579bf21cd93STycho Nightingale 		 * 3B/r		CMP r32, r/m32
1580bf21cd93STycho Nightingale 		 * REX.W + 3B/r	CMP r64, r/m64
1581bf21cd93STycho Nightingale 		 *
15824c87aefeSPatrick Mooney 		 * Compare the first operand with the second operand and
1583bf21cd93STycho Nightingale 		 * set status flags in EFLAGS register. The comparison is
1584bf21cd93STycho Nightingale 		 * performed by subtracting the second operand from the first
1585bf21cd93STycho Nightingale 		 * operand and then setting the status flags.
1586bf21cd93STycho Nightingale 		 */
1587bf21cd93STycho Nightingale 
15884c87aefeSPatrick Mooney 		/* Get the register operand */
1589bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1590e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &regop);
1591bf21cd93STycho Nightingale 		if (error)
1592bf21cd93STycho Nightingale 			return (error);
1593bf21cd93STycho Nightingale 
15944c87aefeSPatrick Mooney 		/* Get the memory operand */
1595e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &memop, size);
1596bf21cd93STycho Nightingale 		if (error)
1597bf21cd93STycho Nightingale 			return (error);
1598bf21cd93STycho Nightingale 
15994c87aefeSPatrick Mooney 		if (vie->op.op_byte == 0x3B) {
16004c87aefeSPatrick Mooney 			op1 = regop;
16014c87aefeSPatrick Mooney 			op2 = memop;
16024c87aefeSPatrick Mooney 		} else {
16034c87aefeSPatrick Mooney 			op1 = memop;
16044c87aefeSPatrick Mooney 			op2 = regop;
16054c87aefeSPatrick Mooney 		}
1606bf21cd93STycho Nightingale 		rflags2 = getcc(size, op1, op2);
1607bf21cd93STycho Nightingale 		break;
16084c87aefeSPatrick Mooney 	case 0x80:
1609bf21cd93STycho Nightingale 	case 0x81:
1610bf21cd93STycho Nightingale 	case 0x83:
1611bf21cd93STycho Nightingale 		/*
16124c87aefeSPatrick Mooney 		 * 80 /7		cmp r/m8, imm8
16134c87aefeSPatrick Mooney 		 * REX + 80 /7		cmp r/m8, imm8
16144c87aefeSPatrick Mooney 		 *
1615bf21cd93STycho Nightingale 		 * 81 /7		cmp r/m16, imm16
1616bf21cd93STycho Nightingale 		 * 81 /7		cmp r/m32, imm32
1617bf21cd93STycho Nightingale 		 * REX.W + 81 /7	cmp r/m64, imm32 sign-extended to 64
1618bf21cd93STycho Nightingale 		 *
1619bf21cd93STycho Nightingale 		 * 83 /7		cmp r/m16, imm8 sign-extended to 16
1620bf21cd93STycho Nightingale 		 * 83 /7		cmp r/m32, imm8 sign-extended to 32
1621bf21cd93STycho Nightingale 		 * REX.W + 83 /7	cmp r/m64, imm8 sign-extended to 64
1622bf21cd93STycho Nightingale 		 *
1623bf21cd93STycho Nightingale 		 * Compare mem (ModRM:r/m) with immediate and set
1624bf21cd93STycho Nightingale 		 * status flags according to the results.  The
1625bf21cd93STycho Nightingale 		 * comparison is performed by subtracting the
1626bf21cd93STycho Nightingale 		 * immediate from the first operand and then setting
1627bf21cd93STycho Nightingale 		 * the status flags.
1628bf21cd93STycho Nightingale 		 *
1629bf21cd93STycho Nightingale 		 */
16304c87aefeSPatrick Mooney 		if (vie->op.op_byte == 0x80)
16314c87aefeSPatrick Mooney 			size = 1;
1632bf21cd93STycho Nightingale 
1633bf21cd93STycho Nightingale 		/* get the first operand */
16342699b94cSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
1635bf21cd93STycho Nightingale 		if (error)
1636bf21cd93STycho Nightingale 			return (error);
1637bf21cd93STycho Nightingale 
1638bf21cd93STycho Nightingale 		rflags2 = getcc(size, op1, vie->immediate);
1639bf21cd93STycho Nightingale 		break;
1640bf21cd93STycho Nightingale 	default:
1641bf21cd93STycho Nightingale 		return (EINVAL);
1642bf21cd93STycho Nightingale 	}
1643e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1644bf21cd93STycho Nightingale 	if (error)
1645bf21cd93STycho Nightingale 		return (error);
1646bf21cd93STycho Nightingale 	rflags &= ~RFLAGS_STATUS_BITS;
1647bf21cd93STycho Nightingale 	rflags |= rflags2 & RFLAGS_STATUS_BITS;
1648bf21cd93STycho Nightingale 
1649bf21cd93STycho Nightingale 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
1650bf21cd93STycho Nightingale 	return (error);
1651bf21cd93STycho Nightingale }
1652bf21cd93STycho Nightingale 
165384659b24SMichael Zeller static int
vie_emulate_test(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)16543e1c5f3aSPatrick Mooney vie_emulate_test(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
165584659b24SMichael Zeller {
165684659b24SMichael Zeller 	int error, size;
165784659b24SMichael Zeller 	uint64_t op1, rflags, rflags2;
165884659b24SMichael Zeller 
165984659b24SMichael Zeller 	size = vie->opsize;
166084659b24SMichael Zeller 	error = EINVAL;
166184659b24SMichael Zeller 
166284659b24SMichael Zeller 	switch (vie->op.op_byte) {
1663e1ded6bdSAndy Fiddaman 	case 0xF6:
1664e1ded6bdSAndy Fiddaman 		/*
1665e1ded6bdSAndy Fiddaman 		 * F6 /0		test r/m8, imm8
1666e1ded6bdSAndy Fiddaman 		 *
1667e1ded6bdSAndy Fiddaman 		 * Test mem (ModRM:r/m) with immediate and set status
1668e1ded6bdSAndy Fiddaman 		 * flags according to the results.  The comparison is
1669e1ded6bdSAndy Fiddaman 		 * performed by anding the immediate from the first
1670e1ded6bdSAndy Fiddaman 		 * operand and then setting the status flags.
1671e1ded6bdSAndy Fiddaman 		 */
1672e1ded6bdSAndy Fiddaman 		if ((vie->reg & 7) != 0)
1673e1ded6bdSAndy Fiddaman 			return (EINVAL);
1674e1ded6bdSAndy Fiddaman 
1675e1ded6bdSAndy Fiddaman 		size = 1;	/* override for byte operation */
1676e1ded6bdSAndy Fiddaman 
1677e1ded6bdSAndy Fiddaman 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
1678e1ded6bdSAndy Fiddaman 		if (error)
1679e1ded6bdSAndy Fiddaman 			return (error);
1680e1ded6bdSAndy Fiddaman 
1681e1ded6bdSAndy Fiddaman 		rflags2 = getandflags(size, op1, vie->immediate);
1682e1ded6bdSAndy Fiddaman 		break;
168384659b24SMichael Zeller 	case 0xF7:
168484659b24SMichael Zeller 		/*
168584659b24SMichael Zeller 		 * F7 /0		test r/m16, imm16
168684659b24SMichael Zeller 		 * F7 /0		test r/m32, imm32
168784659b24SMichael Zeller 		 * REX.W + F7 /0	test r/m64, imm32 sign-extended to 64
168884659b24SMichael Zeller 		 *
168984659b24SMichael Zeller 		 * Test mem (ModRM:r/m) with immediate and set status
169084659b24SMichael Zeller 		 * flags according to the results.  The comparison is
169184659b24SMichael Zeller 		 * performed by anding the immediate from the first
169284659b24SMichael Zeller 		 * operand and then setting the status flags.
169384659b24SMichael Zeller 		 */
169484659b24SMichael Zeller 		if ((vie->reg & 7) != 0)
169584659b24SMichael Zeller 			return (EINVAL);
169684659b24SMichael Zeller 
1697e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &op1, size);
169884659b24SMichael Zeller 		if (error)
169984659b24SMichael Zeller 			return (error);
170084659b24SMichael Zeller 
170184659b24SMichael Zeller 		rflags2 = getandflags(size, op1, vie->immediate);
170284659b24SMichael Zeller 		break;
170384659b24SMichael Zeller 	default:
170484659b24SMichael Zeller 		return (EINVAL);
170584659b24SMichael Zeller 	}
1706e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
170784659b24SMichael Zeller 	if (error)
170884659b24SMichael Zeller 		return (error);
170984659b24SMichael Zeller 
171084659b24SMichael Zeller 	/*
171184659b24SMichael Zeller 	 * OF and CF are cleared; the SF, ZF and PF flags are set according
171284659b24SMichael Zeller 	 * to the result; AF is undefined.
171384659b24SMichael Zeller 	 */
171484659b24SMichael Zeller 	rflags &= ~RFLAGS_STATUS_BITS;
171584659b24SMichael Zeller 	rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
171684659b24SMichael Zeller 
171784659b24SMichael Zeller 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
171884659b24SMichael Zeller 	return (error);
171984659b24SMichael Zeller }
172084659b24SMichael Zeller 
1721154972afSPatrick Mooney static int
vie_emulate_bextr(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)17223e1c5f3aSPatrick Mooney vie_emulate_bextr(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1723154972afSPatrick Mooney {
1724154972afSPatrick Mooney 	uint64_t src1, src2, dst, rflags;
172559d65d31SAndy Fiddaman 	unsigned start, len, size;
172659d65d31SAndy Fiddaman 	int error;
1727e0c0d44eSPatrick Mooney 	struct vm_guest_paging *paging;
1728154972afSPatrick Mooney 
1729154972afSPatrick Mooney 	size = vie->opsize;
1730154972afSPatrick Mooney 	error = EINVAL;
1731e0c0d44eSPatrick Mooney 	paging = &vie->paging;
1732154972afSPatrick Mooney 
1733154972afSPatrick Mooney 	/*
1734154972afSPatrick Mooney 	 * VEX.LZ.0F38.W0 F7 /r		BEXTR r32a, r/m32, r32b
1735154972afSPatrick Mooney 	 * VEX.LZ.0F38.W1 F7 /r		BEXTR r64a, r/m64, r64b
1736154972afSPatrick Mooney 	 *
1737154972afSPatrick Mooney 	 * Destination operand is ModRM:reg.  Source operands are ModRM:r/m and
1738154972afSPatrick Mooney 	 * Vex.vvvv.
1739154972afSPatrick Mooney 	 *
1740154972afSPatrick Mooney 	 * Operand size is always 32-bit if not in 64-bit mode (W1 is ignored).
1741154972afSPatrick Mooney 	 */
1742154972afSPatrick Mooney 	if (size != 4 && paging->cpu_mode != CPU_MODE_64BIT)
1743154972afSPatrick Mooney 		size = 4;
1744154972afSPatrick Mooney 
1745154972afSPatrick Mooney 	/*
1746154972afSPatrick Mooney 	 * Extracts contiguous bits from the first /source/ operand (second
1747154972afSPatrick Mooney 	 * operand) using an index and length specified in the second /source/
1748154972afSPatrick Mooney 	 * operand (third operand).
1749154972afSPatrick Mooney 	 */
1750e0c0d44eSPatrick Mooney 	error = vie_mmio_read(vie, vm, vcpuid, gpa, &src1, size);
1751154972afSPatrick Mooney 	if (error)
1752154972afSPatrick Mooney 		return (error);
1753e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, gpr_map[vie->vex_reg], &src2);
1754154972afSPatrick Mooney 	if (error)
1755154972afSPatrick Mooney 		return (error);
1756e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
1757154972afSPatrick Mooney 	if (error)
1758154972afSPatrick Mooney 		return (error);
1759154972afSPatrick Mooney 
1760154972afSPatrick Mooney 	start = (src2 & 0xff);
1761154972afSPatrick Mooney 	len = (src2 & 0xff00) >> 8;
1762154972afSPatrick Mooney 
1763154972afSPatrick Mooney 	/* If no bits are extracted, the destination register is cleared. */
1764154972afSPatrick Mooney 	dst = 0;
1765154972afSPatrick Mooney 
1766154972afSPatrick Mooney 	/* If START exceeds the operand size, no bits are extracted. */
1767154972afSPatrick Mooney 	if (start > size * 8)
1768154972afSPatrick Mooney 		goto done;
1769154972afSPatrick Mooney 	/* Length is bounded by both the destination size and start offset. */
1770154972afSPatrick Mooney 	if (start + len > size * 8)
1771154972afSPatrick Mooney 		len = (size * 8) - start;
1772154972afSPatrick Mooney 	if (len == 0)
1773154972afSPatrick Mooney 		goto done;
1774154972afSPatrick Mooney 
1775154972afSPatrick Mooney 	if (start > 0)
1776154972afSPatrick Mooney 		src1 = (src1 >> start);
1777154972afSPatrick Mooney 	if (len < 64)
1778154972afSPatrick Mooney 		src1 = src1 & ((1ull << len) - 1);
1779154972afSPatrick Mooney 	dst = src1;
1780154972afSPatrick Mooney 
1781154972afSPatrick Mooney done:
1782154972afSPatrick Mooney 	error = vie_update_register(vm, vcpuid, gpr_map[vie->reg], dst, size);
1783154972afSPatrick Mooney 	if (error)
1784154972afSPatrick Mooney 		return (error);
1785154972afSPatrick Mooney 
1786154972afSPatrick Mooney 	/*
1787154972afSPatrick Mooney 	 * AMD: OF, CF cleared; SF/AF/PF undefined; ZF set by result.
1788154972afSPatrick Mooney 	 * Intel: ZF is set by result; AF/SF/PF undefined; all others cleared.
1789154972afSPatrick Mooney 	 */
1790154972afSPatrick Mooney 	rflags &= ~RFLAGS_STATUS_BITS;
1791154972afSPatrick Mooney 	if (dst == 0)
1792154972afSPatrick Mooney 		rflags |= PSL_Z;
1793154972afSPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags,
1794154972afSPatrick Mooney 	    8);
1795154972afSPatrick Mooney 	return (error);
1796154972afSPatrick Mooney }
1797154972afSPatrick Mooney 
17984c87aefeSPatrick Mooney static int
vie_emulate_add(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)17993e1c5f3aSPatrick Mooney vie_emulate_add(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
18004c87aefeSPatrick Mooney {
18014c87aefeSPatrick Mooney 	int error, size;
18024c87aefeSPatrick Mooney 	uint64_t nval, rflags, rflags2, val1, val2;
18034c87aefeSPatrick Mooney 	enum vm_reg_name reg;
18044c87aefeSPatrick Mooney 
18054c87aefeSPatrick Mooney 	size = vie->opsize;
18064c87aefeSPatrick Mooney 	error = EINVAL;
18074c87aefeSPatrick Mooney 
18084c87aefeSPatrick Mooney 	switch (vie->op.op_byte) {
18094c87aefeSPatrick Mooney 	case 0x03:
18104c87aefeSPatrick Mooney 		/*
18114c87aefeSPatrick Mooney 		 * ADD r/m to r and store the result in r
18124c87aefeSPatrick Mooney 		 *
18132699b94cSPatrick Mooney 		 * 03/r			ADD r16, r/m16
18142699b94cSPatrick Mooney 		 * 03/r			ADD r32, r/m32
18152699b94cSPatrick Mooney 		 * REX.W + 03/r		ADD r64, r/m64
18164c87aefeSPatrick Mooney 		 */
18174c87aefeSPatrick Mooney 
18184c87aefeSPatrick Mooney 		/* get the first operand */
18194c87aefeSPatrick Mooney 		reg = gpr_map[vie->reg];
1820e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
18214c87aefeSPatrick Mooney 		if (error)
18224c87aefeSPatrick Mooney 			break;
18234c87aefeSPatrick Mooney 
18244c87aefeSPatrick Mooney 		/* get the second operand */
1825e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
18264c87aefeSPatrick Mooney 		if (error)
18274c87aefeSPatrick Mooney 			break;
18284c87aefeSPatrick Mooney 
18294c87aefeSPatrick Mooney 		/* perform the operation and write the result */
18304c87aefeSPatrick Mooney 		nval = val1 + val2;
18314c87aefeSPatrick Mooney 		error = vie_update_register(vm, vcpuid, reg, nval, size);
18324c87aefeSPatrick Mooney 		break;
18334c87aefeSPatrick Mooney 	default:
18344c87aefeSPatrick Mooney 		break;
18354c87aefeSPatrick Mooney 	}
18364c87aefeSPatrick Mooney 
18374c87aefeSPatrick Mooney 	if (!error) {
18384c87aefeSPatrick Mooney 		rflags2 = getaddflags(size, val1, val2);
1839e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
18404c87aefeSPatrick Mooney 		    &rflags);
18414c87aefeSPatrick Mooney 		if (error)
18424c87aefeSPatrick Mooney 			return (error);
18434c87aefeSPatrick Mooney 
18444c87aefeSPatrick Mooney 		rflags &= ~RFLAGS_STATUS_BITS;
18454c87aefeSPatrick Mooney 		rflags |= rflags2 & RFLAGS_STATUS_BITS;
18464c87aefeSPatrick Mooney 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
18474c87aefeSPatrick Mooney 		    rflags, 8);
18484c87aefeSPatrick Mooney 	}
18494c87aefeSPatrick Mooney 
18504c87aefeSPatrick Mooney 	return (error);
18514c87aefeSPatrick Mooney }
18524c87aefeSPatrick Mooney 
1853bf21cd93STycho Nightingale static int
vie_emulate_sub(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)18543e1c5f3aSPatrick Mooney vie_emulate_sub(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1855bf21cd93STycho Nightingale {
1856bf21cd93STycho Nightingale 	int error, size;
1857bf21cd93STycho Nightingale 	uint64_t nval, rflags, rflags2, val1, val2;
1858bf21cd93STycho Nightingale 	enum vm_reg_name reg;
1859bf21cd93STycho Nightingale 
1860bf21cd93STycho Nightingale 	size = vie->opsize;
1861bf21cd93STycho Nightingale 	error = EINVAL;
1862bf21cd93STycho Nightingale 
1863bf21cd93STycho Nightingale 	switch (vie->op.op_byte) {
1864bf21cd93STycho Nightingale 	case 0x2B:
1865bf21cd93STycho Nightingale 		/*
1866bf21cd93STycho Nightingale 		 * SUB r/m from r and store the result in r
1867e0c0d44eSPatrick Mooney 		 *
18682699b94cSPatrick Mooney 		 * 2B/r		SUB r16, r/m16
18692699b94cSPatrick Mooney 		 * 2B/r		SUB r32, r/m32
18702699b94cSPatrick Mooney 		 * REX.W + 2B/r	SUB r64, r/m64
1871bf21cd93STycho Nightingale 		 */
1872bf21cd93STycho Nightingale 
1873bf21cd93STycho Nightingale 		/* get the first operand */
1874bf21cd93STycho Nightingale 		reg = gpr_map[vie->reg];
1875e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, reg, &val1);
1876bf21cd93STycho Nightingale 		if (error)
1877bf21cd93STycho Nightingale 			break;
1878bf21cd93STycho Nightingale 
1879bf21cd93STycho Nightingale 		/* get the second operand */
1880e0c0d44eSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size);
1881bf21cd93STycho Nightingale 		if (error)
1882bf21cd93STycho Nightingale 			break;
1883bf21cd93STycho Nightingale 
1884bf21cd93STycho Nightingale 		/* perform the operation and write the result */
1885bf21cd93STycho Nightingale 		nval = val1 - val2;
1886bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, reg, nval, size);
1887bf21cd93STycho Nightingale 		break;
1888bf21cd93STycho Nightingale 	default:
1889bf21cd93STycho Nightingale 		break;
1890bf21cd93STycho Nightingale 	}
1891bf21cd93STycho Nightingale 
1892bf21cd93STycho Nightingale 	if (!error) {
1893bf21cd93STycho Nightingale 		rflags2 = getcc(size, val1, val2);
1894e0c0d44eSPatrick Mooney 		error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
1895bf21cd93STycho Nightingale 		    &rflags);
1896bf21cd93STycho Nightingale 		if (error)
1897bf21cd93STycho Nightingale 			return (error);
1898bf21cd93STycho Nightingale 
1899bf21cd93STycho Nightingale 		rflags &= ~RFLAGS_STATUS_BITS;
1900bf21cd93STycho Nightingale 		rflags |= rflags2 & RFLAGS_STATUS_BITS;
1901bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
1902bf21cd93STycho Nightingale 		    rflags, 8);
1903bf21cd93STycho Nightingale 	}
1904bf21cd93STycho Nightingale 
1905bf21cd93STycho Nightingale 	return (error);
1906bf21cd93STycho Nightingale }
1907bf21cd93STycho Nightingale 
19081fde93bfSAndy Fiddaman 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 
1978bf21cd93STycho Nightingale static int
vie_emulate_stack_op(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)19793e1c5f3aSPatrick Mooney vie_emulate_stack_op(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
1980bf21cd93STycho Nightingale {
1981bf21cd93STycho Nightingale 	struct vm_copyinfo copyinfo[2];
1982bf21cd93STycho Nightingale 	struct seg_desc ss_desc;
1983bf21cd93STycho Nightingale 	uint64_t cr0, rflags, rsp, stack_gla, val;
19844c87aefeSPatrick Mooney 	int error, fault, size, stackaddrsize, pushop;
1985e0c0d44eSPatrick Mooney 	struct vm_guest_paging *paging;
1986bf21cd93STycho Nightingale 
1987bf21cd93STycho Nightingale 	val = 0;
1988bf21cd93STycho Nightingale 	size = vie->opsize;
1989bf21cd93STycho Nightingale 	pushop = (vie->op.op_type == VIE_OP_TYPE_PUSH) ? 1 : 0;
1990e0c0d44eSPatrick Mooney 	paging = &vie->paging;
1991bf21cd93STycho Nightingale 
1992bf21cd93STycho Nightingale 	/*
1993bf21cd93STycho Nightingale 	 * From "Address-Size Attributes for Stack Accesses", Intel SDL, Vol 1
1994bf21cd93STycho Nightingale 	 */
1995bf21cd93STycho Nightingale 	if (paging->cpu_mode == CPU_MODE_REAL) {
1996bf21cd93STycho Nightingale 		stackaddrsize = 2;
1997bf21cd93STycho Nightingale 	} else if (paging->cpu_mode == CPU_MODE_64BIT) {
1998bf21cd93STycho Nightingale 		/*
1999bf21cd93STycho Nightingale 		 * "Stack Manipulation Instructions in 64-bit Mode", SDM, Vol 3
2000bf21cd93STycho Nightingale 		 * - Stack pointer size is always 64-bits.
2001bf21cd93STycho Nightingale 		 * - PUSH/POP of 32-bit values is not possible in 64-bit mode.
2002bf21cd93STycho Nightingale 		 * - 16-bit PUSH/POP is supported by using the operand size
2003bf21cd93STycho Nightingale 		 *   override prefix (66H).
2004bf21cd93STycho Nightingale 		 */
2005bf21cd93STycho Nightingale 		stackaddrsize = 8;
2006bf21cd93STycho Nightingale 		size = vie->opsize_override ? 2 : 8;
2007bf21cd93STycho Nightingale 	} else {
2008bf21cd93STycho Nightingale 		/*
20094c87aefeSPatrick Mooney 		 * In protected or compatibility mode the 'B' flag in the
2010bf21cd93STycho Nightingale 		 * stack-segment descriptor determines the size of the
2011bf21cd93STycho Nightingale 		 * stack pointer.
2012bf21cd93STycho Nightingale 		 */
2013bf21cd93STycho Nightingale 		error = vm_get_seg_desc(vm, vcpuid, VM_REG_GUEST_SS, &ss_desc);
2014bf21cd93STycho Nightingale 		KASSERT(error == 0, ("%s: error %d getting SS descriptor",
2015bf21cd93STycho Nightingale 		    __func__, error));
2016bf21cd93STycho Nightingale 		if (SEG_DESC_DEF32(ss_desc.access))
2017bf21cd93STycho Nightingale 			stackaddrsize = 4;
2018bf21cd93STycho Nightingale 		else
2019bf21cd93STycho Nightingale 			stackaddrsize = 2;
2020bf21cd93STycho Nightingale 	}
2021bf21cd93STycho Nightingale 
2022e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &cr0);
2023bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting cr0", __func__, error));
2024bf21cd93STycho Nightingale 
2025e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
2026bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
2027bf21cd93STycho Nightingale 
2028e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RSP, &rsp);
2029bf21cd93STycho Nightingale 	KASSERT(error == 0, ("%s: error %d getting rsp", __func__, error));
2030bf21cd93STycho Nightingale 	if (pushop) {
2031bf21cd93STycho Nightingale 		rsp -= size;
2032bf21cd93STycho Nightingale 	}
2033bf21cd93STycho Nightingale 
2034bf21cd93STycho Nightingale 	if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS, &ss_desc,
2035bf21cd93STycho Nightingale 	    rsp, size, stackaddrsize, pushop ? PROT_WRITE : PROT_READ,
2036bf21cd93STycho Nightingale 	    &stack_gla)) {
2037bf21cd93STycho Nightingale 		vm_inject_ss(vm, vcpuid, 0);
2038bf21cd93STycho Nightingale 		return (0);
2039bf21cd93STycho Nightingale 	}
2040bf21cd93STycho Nightingale 
2041bf21cd93STycho Nightingale 	if (vie_canonical_check(paging->cpu_mode, stack_gla)) {
2042bf21cd93STycho Nightingale 		vm_inject_ss(vm, vcpuid, 0);
2043bf21cd93STycho Nightingale 		return (0);
2044bf21cd93STycho Nightingale 	}
2045bf21cd93STycho Nightingale 
2046bf21cd93STycho Nightingale 	if (vie_alignment_check(paging->cpl, size, cr0, rflags, stack_gla)) {
2047bf21cd93STycho Nightingale 		vm_inject_ac(vm, vcpuid, 0);
2048bf21cd93STycho Nightingale 		return (0);
2049bf21cd93STycho Nightingale 	}
2050bf21cd93STycho Nightingale 
2051bf21cd93STycho Nightingale 	error = vm_copy_setup(vm, vcpuid, paging, stack_gla, size,
20524c87aefeSPatrick Mooney 	    pushop ? PROT_WRITE : PROT_READ, copyinfo, nitems(copyinfo),
20534c87aefeSPatrick Mooney 	    &fault);
20544c87aefeSPatrick Mooney 	if (error || fault)
20554c87aefeSPatrick Mooney 		return (error);
2056bf21cd93STycho Nightingale 
2057bf21cd93STycho Nightingale 	if (pushop) {
20583e1c5f3aSPatrick Mooney 		error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, size);
2059bf21cd93STycho Nightingale 		if (error == 0)
2060bf21cd93STycho Nightingale 			vm_copyout(vm, vcpuid, &val, copyinfo, size);
2061bf21cd93STycho Nightingale 	} else {
2062bf21cd93STycho Nightingale 		vm_copyin(vm, vcpuid, copyinfo, &val, size);
20633e1c5f3aSPatrick Mooney 		error = vie_mmio_write(vie, vm, vcpuid, gpa, val, size);
2064bf21cd93STycho Nightingale 		rsp += size;
2065bf21cd93STycho Nightingale 	}
2066bf21cd93STycho Nightingale 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
2067bf21cd93STycho Nightingale 
2068bf21cd93STycho Nightingale 	if (error == 0) {
2069bf21cd93STycho Nightingale 		error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RSP, rsp,
2070bf21cd93STycho Nightingale 		    stackaddrsize);
2071bf21cd93STycho Nightingale 		KASSERT(error == 0, ("error %d updating rsp", error));
2072bf21cd93STycho Nightingale 	}
2073bf21cd93STycho Nightingale 	return (error);
2074bf21cd93STycho Nightingale }
2075bf21cd93STycho Nightingale 
2076bf21cd93STycho Nightingale static int
vie_emulate_push(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)20773e1c5f3aSPatrick Mooney vie_emulate_push(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
2078bf21cd93STycho Nightingale {
2079bf21cd93STycho Nightingale 	int error;
2080bf21cd93STycho Nightingale 
2081bf21cd93STycho Nightingale 	/*
2082bf21cd93STycho Nightingale 	 * Table A-6, "Opcode Extensions", Intel SDM, Vol 2.
2083bf21cd93STycho Nightingale 	 *
2084bf21cd93STycho Nightingale 	 * PUSH is part of the group 5 extended opcodes and is identified
2085bf21cd93STycho Nightingale 	 * by ModRM:reg = b110.
2086bf21cd93STycho Nightingale 	 */
2087bf21cd93STycho Nightingale 	if ((vie->reg & 7) != 6)
2088bf21cd93STycho Nightingale 		return (EINVAL);
2089bf21cd93STycho Nightingale 
20903e1c5f3aSPatrick Mooney 	error = vie_emulate_stack_op(vie, vm, vcpuid, gpa);
2091bf21cd93STycho Nightingale 	return (error);
2092bf21cd93STycho Nightingale }
2093bf21cd93STycho Nightingale 
2094bf21cd93STycho Nightingale static int
vie_emulate_pop(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)20953e1c5f3aSPatrick Mooney vie_emulate_pop(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
2096bf21cd93STycho Nightingale {
2097bf21cd93STycho Nightingale 	int error;
2098bf21cd93STycho Nightingale 
2099bf21cd93STycho Nightingale 	/*
2100bf21cd93STycho Nightingale 	 * Table A-6, "Opcode Extensions", Intel SDM, Vol 2.
2101bf21cd93STycho Nightingale 	 *
2102bf21cd93STycho Nightingale 	 * POP is part of the group 1A extended opcodes and is identified
2103bf21cd93STycho Nightingale 	 * by ModRM:reg = b000.
2104bf21cd93STycho Nightingale 	 */
2105bf21cd93STycho Nightingale 	if ((vie->reg & 7) != 0)
2106bf21cd93STycho Nightingale 		return (EINVAL);
2107bf21cd93STycho Nightingale 
21083e1c5f3aSPatrick Mooney 	error = vie_emulate_stack_op(vie, vm, vcpuid, gpa);
2109bf21cd93STycho Nightingale 	return (error);
2110bf21cd93STycho Nightingale }
2111bf21cd93STycho Nightingale 
2112bf21cd93STycho Nightingale static int
vie_emulate_group1(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21133e1c5f3aSPatrick Mooney vie_emulate_group1(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
2114bf21cd93STycho Nightingale {
2115bf21cd93STycho Nightingale 	int error;
2116bf21cd93STycho Nightingale 
2117bf21cd93STycho Nightingale 	switch (vie->reg & 7) {
2118bf21cd93STycho Nightingale 	case 0x1:	/* OR */
21193e1c5f3aSPatrick Mooney 		error = vie_emulate_or(vie, vm, vcpuid, gpa);
2120bf21cd93STycho Nightingale 		break;
2121bf21cd93STycho Nightingale 	case 0x4:	/* AND */
21223e1c5f3aSPatrick Mooney 		error = vie_emulate_and(vie, vm, vcpuid, gpa);
2123bf21cd93STycho Nightingale 		break;
2124bf21cd93STycho Nightingale 	case 0x7:	/* CMP */
21253e1c5f3aSPatrick Mooney 		error = vie_emulate_cmp(vie, vm, vcpuid, gpa);
2126bf21cd93STycho Nightingale 		break;
2127bf21cd93STycho Nightingale 	default:
2128bf21cd93STycho Nightingale 		error = EINVAL;
2129bf21cd93STycho Nightingale 		break;
2130bf21cd93STycho Nightingale 	}
2131bf21cd93STycho Nightingale 
2132bf21cd93STycho Nightingale 	return (error);
2133bf21cd93STycho Nightingale }
2134bf21cd93STycho Nightingale 
21354c87aefeSPatrick Mooney static int
vie_emulate_bittest(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21363e1c5f3aSPatrick Mooney vie_emulate_bittest(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa)
21374c87aefeSPatrick Mooney {
21384c87aefeSPatrick Mooney 	uint64_t val, rflags;
21394c87aefeSPatrick Mooney 	int error, bitmask, bitoff;
21404c87aefeSPatrick Mooney 
21414c87aefeSPatrick Mooney 	/*
21424c87aefeSPatrick Mooney 	 * 0F BA is a Group 8 extended opcode.
21434c87aefeSPatrick Mooney 	 *
21444c87aefeSPatrick Mooney 	 * Currently we only emulate the 'Bit Test' instruction which is
21454c87aefeSPatrick Mooney 	 * identified by a ModR/M:reg encoding of 100b.
21464c87aefeSPatrick Mooney 	 */
21474c87aefeSPatrick Mooney 	if ((vie->reg & 7) != 4)
21484c87aefeSPatrick Mooney 		return (EINVAL);
21494c87aefeSPatrick Mooney 
2150e0c0d44eSPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
21514c87aefeSPatrick Mooney 	KASSERT(error == 0, ("%s: error %d getting rflags", __func__, error));
21524c87aefeSPatrick Mooney 
2153e0c0d44eSPatrick Mooney 	error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, vie->opsize);
21544c87aefeSPatrick Mooney 	if (error)
21554c87aefeSPatrick Mooney 		return (error);
21564c87aefeSPatrick Mooney 
21574c87aefeSPatrick Mooney 	/*
21584c87aefeSPatrick Mooney 	 * Intel SDM, Vol 2, Table 3-2:
21594c87aefeSPatrick Mooney 	 * "Range of Bit Positions Specified by Bit Offset Operands"
21604c87aefeSPatrick Mooney 	 */
21614c87aefeSPatrick Mooney 	bitmask = vie->opsize * 8 - 1;
21624c87aefeSPatrick Mooney 	bitoff = vie->immediate & bitmask;
21634c87aefeSPatrick Mooney 
21644c87aefeSPatrick Mooney 	/* Copy the bit into the Carry flag in %rflags */
21654c87aefeSPatrick Mooney 	if (val & (1UL << bitoff))
21664c87aefeSPatrick Mooney 		rflags |= PSL_C;
21674c87aefeSPatrick Mooney 	else
21684c87aefeSPatrick Mooney 		rflags &= ~PSL_C;
21694c87aefeSPatrick Mooney 
21704c87aefeSPatrick Mooney 	error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
21714c87aefeSPatrick Mooney 	KASSERT(error == 0, ("%s: error %d updating rflags", __func__, error));
21724c87aefeSPatrick Mooney 
21734c87aefeSPatrick Mooney 	return (0);
21744c87aefeSPatrick Mooney }
21754c87aefeSPatrick Mooney 
21764c87aefeSPatrick Mooney static int
vie_emulate_twob_group15(struct vie * vie,struct vm * vm,int vcpuid,uint64_t gpa)21773e1c5f3aSPatrick Mooney vie_emulate_twob_group15(struct vie *vie, struct vm *vm, int vcpuid,
21783e1c5f3aSPatrick Mooney     uint64_t gpa)
21794c87aefeSPatrick Mooney {
21804c87aefeSPatrick Mooney 	int error;
21814c87aefeSPatrick Mooney 	uint64_t buf;
21824c87aefeSPatrick Mooney 
21834c87aefeSPatrick Mooney 	switch (vie->reg & 7) {
21844c87aefeSPatrick Mooney 	case 0x7:	/* CLFLUSH, CLFLUSHOPT, and SFENCE */
21854c87aefeSPatrick Mooney 		if (vie->mod == 0x3) {
21864c87aefeSPatrick Mooney 			/*
21874c87aefeSPatrick Mooney 			 * SFENCE.  Ignore it, VM exit provides enough
21884c87aefeSPatrick Mooney 			 * barriers on its own.
21894c87aefeSPatrick Mooney 			 */
21904c87aefeSPatrick Mooney 			error = 0;
21914c87aefeSPatrick Mooney 		} else {
21924c87aefeSPatrick Mooney 			/*
21934c87aefeSPatrick Mooney 			 * CLFLUSH, CLFLUSHOPT.  Only check for access
21944c87aefeSPatrick Mooney 			 * rights.
21954c87aefeSPatrick Mooney 			 */
2196e0c0d44eSPatrick Mooney 			error = vie_mmio_read(vie, vm, vcpuid, gpa, &buf, 1);
21974c87aefeSPatrick Mooney 		}
21984c87aefeSPatrick Mooney 		break;
21994c87aefeSPatrick Mooney 	default:
22004c87aefeSPatrick Mooney 		error = EINVAL;
22014c87aefeSPatrick Mooney 		break;
22024c87aefeSPatrick Mooney 	}
22034c87aefeSPatrick Mooney 
22044c87aefeSPatrick Mooney 	return (error);
22054c87aefeSPatrick Mooney }
22064c87aefeSPatrick Mooney 
22077db0d193SPatrick Mooney static int
vie_emulate_clts(struct vie * vie,struct vm * vm,int vcpuid)22087db0d193SPatrick Mooney vie_emulate_clts(struct vie *vie, struct vm *vm, int vcpuid)
22097db0d193SPatrick Mooney {
22107db0d193SPatrick Mooney 	uint64_t val;
2211374973c2SToomas Soome 	int error __maybe_unused;
22127db0d193SPatrick Mooney 
22137db0d193SPatrick Mooney 	if (vie->paging.cpl != 0) {
22147db0d193SPatrick Mooney 		vm_inject_gp(vm, vcpuid);
22157db0d193SPatrick Mooney 		vie->num_processed = 0;
22167db0d193SPatrick Mooney 		return (0);
22177db0d193SPatrick Mooney 	}
22187db0d193SPatrick Mooney 
22197db0d193SPatrick Mooney 	error = vm_get_register(vm, vcpuid, VM_REG_GUEST_CR0, &val);
22207db0d193SPatrick Mooney 	ASSERT(error == 0);
22217db0d193SPatrick Mooney 
22227db0d193SPatrick Mooney 	/* Clear %cr0.TS */
22237db0d193SPatrick Mooney 	val &= ~CR0_TS;
22247db0d193SPatrick Mooney 
22257db0d193SPatrick Mooney 	error = vm_set_register(vm, vcpuid, VM_REG_GUEST_CR0, val);
22267db0d193SPatrick Mooney 	ASSERT(error == 0);
22277db0d193SPatrick Mooney 
22287db0d193SPatrick Mooney 	return (0);
22297db0d193SPatrick Mooney }
22307db0d193SPatrick Mooney 
2231e0c0d44eSPatrick Mooney static int
vie_mmio_read(struct vie * vie,struct vm * vm,int cpuid,uint64_t gpa,uint64_t * rval,int bytes)2232e0c0d44eSPatrick Mooney vie_mmio_read(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa,
2233e0c0d44eSPatrick Mooney     uint64_t *rval, int bytes)
2234e0c0d44eSPatrick Mooney {
2235e0c0d44eSPatrick Mooney 	int err;
2236e0c0d44eSPatrick Mooney 
2237e0c0d44eSPatrick Mooney 	if (vie->mmio_req_read.state == VR_DONE) {
2238e0c0d44eSPatrick Mooney 		ASSERT(vie->mmio_req_read.bytes == bytes);
2239e0c0d44eSPatrick Mooney 		ASSERT(vie->mmio_req_read.gpa == gpa);
2240e0c0d44eSPatrick Mooney 
2241e0c0d44eSPatrick Mooney 		*rval = vie->mmio_req_read.data;
2242e0c0d44eSPatrick Mooney 		return (0);
2243e0c0d44eSPatrick Mooney 	}
2244e0c0d44eSPatrick Mooney 
2245e0c0d44eSPatrick Mooney 	err = vm_service_mmio_read(vm, cpuid, gpa, rval, bytes);
2246e0c0d44eSPatrick Mooney 	if (err == 0) {
2247e0c0d44eSPatrick Mooney 		/*
2248e0c0d44eSPatrick Mooney 		 * A successful read from an in-kernel-emulated device may come
2249e0c0d44eSPatrick Mooney 		 * with side effects, so stash the result in case it's used for
2250e0c0d44eSPatrick Mooney 		 * an instruction which subsequently needs to issue an MMIO
2251e0c0d44eSPatrick Mooney 		 * write to userspace.
2252e0c0d44eSPatrick Mooney 		 */
2253e0c0d44eSPatrick Mooney 		ASSERT(vie->mmio_req_read.state == VR_NONE);
2254e0c0d44eSPatrick Mooney 
2255e0c0d44eSPatrick Mooney 		vie->mmio_req_read.bytes = bytes;
2256e0c0d44eSPatrick Mooney 		vie->mmio_req_read.gpa = gpa;
2257e0c0d44eSPatrick Mooney 		vie->mmio_req_read.data = *rval;
2258e0c0d44eSPatrick Mooney 		vie->mmio_req_read.state = VR_DONE;
2259e0c0d44eSPatrick Mooney 
2260e0c0d44eSPatrick Mooney 	} else if (err == ESRCH) {
2261e0c0d44eSPatrick Mooney 		/* Hope that userspace emulation can fulfill this read */
2262e0c0d44eSPatrick Mooney 		vie->mmio_req_read.bytes = bytes;
2263e0c0d44eSPatrick Mooney 		vie->mmio_req_read.gpa = gpa;
2264e0c0d44eSPatrick Mooney 		vie->mmio_req_read.state = VR_PENDING;
2265e0c0d44eSPatrick Mooney 		vie->status |= VIES_PENDING_MMIO;
226606524cf4SPatrick Mooney 	} else if (err < 0) {
226706524cf4SPatrick Mooney 		/*
226806524cf4SPatrick Mooney 		 * The MMIO read failed in such a way that fallback to handling
226906524cf4SPatrick Mooney 		 * in userspace is required.
227006524cf4SPatrick Mooney 		 */
227106524cf4SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
2272e0c0d44eSPatrick Mooney 	}
2273e0c0d44eSPatrick Mooney 	return (err);
2274e0c0d44eSPatrick Mooney }
2275e0c0d44eSPatrick Mooney 
2276e0c0d44eSPatrick Mooney static int
vie_mmio_write(struct vie * vie,struct vm * vm,int cpuid,uint64_t gpa,uint64_t wval,int bytes)2277e0c0d44eSPatrick Mooney vie_mmio_write(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa,
2278e0c0d44eSPatrick Mooney     uint64_t wval, int bytes)
2279e0c0d44eSPatrick Mooney {
2280e0c0d44eSPatrick Mooney 	int err;
2281e0c0d44eSPatrick Mooney 
2282e0c0d44eSPatrick Mooney 	if (vie->mmio_req_write.state == VR_DONE) {
2283e0c0d44eSPatrick Mooney 		ASSERT(vie->mmio_req_write.bytes == bytes);
2284e0c0d44eSPatrick Mooney 		ASSERT(vie->mmio_req_write.gpa == gpa);
2285e0c0d44eSPatrick Mooney 
2286e0c0d44eSPatrick Mooney 		return (0);
2287e0c0d44eSPatrick Mooney 	}
2288e0c0d44eSPatrick Mooney 
2289e0c0d44eSPatrick Mooney 	err = vm_service_mmio_write(vm, cpuid, gpa, wval, bytes);
2290e0c0d44eSPatrick Mooney 	if (err == 0) {
2291e0c0d44eSPatrick Mooney 		/*
2292e0c0d44eSPatrick Mooney 		 * A successful write to an in-kernel-emulated device probably
2293e0c0d44eSPatrick Mooney 		 * results in side effects, so stash the fact that such a write
2294e0c0d44eSPatrick Mooney 		 * succeeded in case the operation requires other work.
2295e0c0d44eSPatrick Mooney 		 */
2296e0c0d44eSPatrick Mooney 		vie->mmio_req_write.bytes = bytes;
2297e0c0d44eSPatrick Mooney 		vie->mmio_req_write.gpa = gpa;
2298e0c0d44eSPatrick Mooney 		vie->mmio_req_write.data = wval;
2299e0c0d44eSPatrick Mooney 		vie->mmio_req_write.state = VR_DONE;
2300e0c0d44eSPatrick Mooney 	} else if (err == ESRCH) {
2301e0c0d44eSPatrick Mooney 		/* Hope that userspace emulation can fulfill this write */
2302e0c0d44eSPatrick Mooney 		vie->mmio_req_write.bytes = bytes;
2303e0c0d44eSPatrick Mooney 		vie->mmio_req_write.gpa = gpa;
2304e0c0d44eSPatrick Mooney 		vie->mmio_req_write.data = wval;
2305e0c0d44eSPatrick Mooney 		vie->mmio_req_write.state = VR_PENDING;
2306e0c0d44eSPatrick Mooney 		vie->status |= VIES_PENDING_MMIO;
230706524cf4SPatrick Mooney 	} else if (err < 0) {
230806524cf4SPatrick Mooney 		/*
230906524cf4SPatrick Mooney 		 * The MMIO write failed in such a way that fallback to handling
231006524cf4SPatrick Mooney 		 * in userspace is required.
231106524cf4SPatrick Mooney 		 */
231206524cf4SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
2313e0c0d44eSPatrick Mooney 	}
2314e0c0d44eSPatrick Mooney 	return (err);
2315e0c0d44eSPatrick Mooney }
2316e0c0d44eSPatrick Mooney 
2317bf21cd93STycho Nightingale int
vie_emulate_mmio(struct vie * vie,struct vm * vm,int vcpuid)23183e1c5f3aSPatrick Mooney vie_emulate_mmio(struct vie *vie, struct vm *vm, int vcpuid)
2319bf21cd93STycho Nightingale {
2320bf21cd93STycho Nightingale 	int error;
2321e0c0d44eSPatrick Mooney 	uint64_t gpa;
2322bf21cd93STycho Nightingale 
2323e0c0d44eSPatrick Mooney 	if ((vie->status & (VIES_INST_DECODE | VIES_MMIO)) !=
2324e0c0d44eSPatrick Mooney 	    (VIES_INST_DECODE | VIES_MMIO)) {
2325bf21cd93STycho Nightingale 		return (EINVAL);
2326e0c0d44eSPatrick Mooney 	}
2327e0c0d44eSPatrick Mooney 
2328e0c0d44eSPatrick Mooney 	gpa = vie->mmio_gpa;
2329bf21cd93STycho Nightingale 
2330bf21cd93STycho Nightingale 	switch (vie->op.op_type) {
2331bf21cd93STycho Nightingale 	case VIE_OP_TYPE_GROUP1:
23323e1c5f3aSPatrick Mooney 		error = vie_emulate_group1(vie, vm, vcpuid, gpa);
2333bf21cd93STycho Nightingale 		break;
2334bf21cd93STycho Nightingale 	case VIE_OP_TYPE_POP:
23353e1c5f3aSPatrick Mooney 		error = vie_emulate_pop(vie, vm, vcpuid, gpa);
2336bf21cd93STycho Nightingale 		break;
2337bf21cd93STycho Nightingale 	case VIE_OP_TYPE_PUSH:
23383e1c5f3aSPatrick Mooney 		error = vie_emulate_push(vie, vm, vcpuid, gpa);
2339bf21cd93STycho Nightingale 		break;
2340bf21cd93STycho Nightingale 	case VIE_OP_TYPE_CMP:
23413e1c5f3aSPatrick Mooney 		error = vie_emulate_cmp(vie, vm, vcpuid, gpa);
2342bf21cd93STycho Nightingale 		break;
2343bf21cd93STycho Nightingale 	case VIE_OP_TYPE_MOV:
23443e1c5f3aSPatrick Mooney 		error = vie_emulate_mov(vie, vm, vcpuid, gpa);
2345bf21cd93STycho Nightingale 		break;
2346bf21cd93STycho Nightingale 	case VIE_OP_TYPE_MOVSX:
2347bf21cd93STycho Nightingale 	case VIE_OP_TYPE_MOVZX:
23483e1c5f3aSPatrick Mooney 		error = vie_emulate_movx(vie, vm, vcpuid, gpa);
2349bf21cd93STycho Nightingale 		break;
2350bf21cd93STycho Nightingale 	case VIE_OP_TYPE_MOVS:
23513e1c5f3aSPatrick Mooney 		error = vie_emulate_movs(vie, vm, vcpuid, gpa);
2352bf21cd93STycho Nightingale 		break;
2353bf21cd93STycho Nightingale 	case VIE_OP_TYPE_STOS:
23543e1c5f3aSPatrick Mooney 		error = vie_emulate_stos(vie, vm, vcpuid, gpa);
2355bf21cd93STycho Nightingale 		break;
2356bf21cd93STycho Nightingale 	case VIE_OP_TYPE_AND:
23573e1c5f3aSPatrick Mooney 		error = vie_emulate_and(vie, vm, vcpuid, gpa);
2358bf21cd93STycho Nightingale 		break;
2359bf21cd93STycho Nightingale 	case VIE_OP_TYPE_OR:
23603e1c5f3aSPatrick Mooney 		error = vie_emulate_or(vie, vm, vcpuid, gpa);
2361bf21cd93STycho Nightingale 		break;
2362bf21cd93STycho Nightingale 	case VIE_OP_TYPE_SUB:
23633e1c5f3aSPatrick Mooney 		error = vie_emulate_sub(vie, vm, vcpuid, gpa);
2364bf21cd93STycho Nightingale 		break;
23654c87aefeSPatrick Mooney 	case VIE_OP_TYPE_BITTEST:
23663e1c5f3aSPatrick Mooney 		error = vie_emulate_bittest(vie, vm, vcpuid, gpa);
23674c87aefeSPatrick Mooney 		break;
23684c87aefeSPatrick Mooney 	case VIE_OP_TYPE_TWOB_GRP15:
23693e1c5f3aSPatrick Mooney 		error = vie_emulate_twob_group15(vie, vm, vcpuid, gpa);
23704c87aefeSPatrick Mooney 		break;
23714c87aefeSPatrick Mooney 	case VIE_OP_TYPE_ADD:
23723e1c5f3aSPatrick Mooney 		error = vie_emulate_add(vie, vm, vcpuid, gpa);
23734c87aefeSPatrick Mooney 		break;
237484659b24SMichael Zeller 	case VIE_OP_TYPE_TEST:
23753e1c5f3aSPatrick Mooney 		error = vie_emulate_test(vie, vm, vcpuid, gpa);
237684659b24SMichael Zeller 		break;
2377154972afSPatrick Mooney 	case VIE_OP_TYPE_BEXTR:
23783e1c5f3aSPatrick Mooney 		error = vie_emulate_bextr(vie, vm, vcpuid, gpa);
2379154972afSPatrick Mooney 		break;
23801fde93bfSAndy Fiddaman 	case VIE_OP_TYPE_MUL:
23811fde93bfSAndy Fiddaman 		error = vie_emulate_mul(vie, vm, vcpuid, gpa);
23821fde93bfSAndy Fiddaman 		break;
2383bf21cd93STycho Nightingale 	default:
2384bf21cd93STycho Nightingale 		error = EINVAL;
2385bf21cd93STycho Nightingale 		break;
2386bf21cd93STycho Nightingale 	}
2387bf21cd93STycho Nightingale 
2388e0c0d44eSPatrick Mooney 	if (error == ESRCH) {
2389e0c0d44eSPatrick Mooney 		/* Return to userspace with the mmio request */
2390e0c0d44eSPatrick Mooney 		return (-1);
2391e0c0d44eSPatrick Mooney 	}
2392e0c0d44eSPatrick Mooney 
2393bf21cd93STycho Nightingale 	return (error);
2394bf21cd93STycho Nightingale }
2395bf21cd93STycho Nightingale 
2396e0c0d44eSPatrick Mooney static int
vie_emulate_inout_port(struct vie * vie,struct vm * vm,int vcpuid,uint32_t * eax)239704573c73SPatrick Mooney vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid,
239804573c73SPatrick Mooney     uint32_t *eax)
2399e0c0d44eSPatrick Mooney {
2400e0c0d44eSPatrick Mooney 	uint32_t mask, val;
2401e0c0d44eSPatrick Mooney 	bool in;
2402e0c0d44eSPatrick Mooney 	int err;
2403e0c0d44eSPatrick Mooney 
2404e0c0d44eSPatrick Mooney 	mask = vie_size2mask(vie->inout.bytes);
2405e0c0d44eSPatrick Mooney 	in = (vie->inout.flags & INOUT_IN) != 0;
2406e0c0d44eSPatrick Mooney 
2407e0c0d44eSPatrick Mooney 	if (!in) {
240804573c73SPatrick Mooney 		val = *eax & mask;
2409e0c0d44eSPatrick Mooney 	}
2410e0c0d44eSPatrick Mooney 
2411e0c0d44eSPatrick Mooney 	if (vie->inout_req_state != VR_DONE) {
24120e1453c3SPatrick Mooney 		err = vm_ioport_access(vm, vcpuid, in, vie->inout.port,
2413e0c0d44eSPatrick Mooney 		    vie->inout.bytes, &val);
241404573c73SPatrick Mooney 		val &= mask;
2415e0c0d44eSPatrick Mooney 	} else {
2416e0c0d44eSPatrick Mooney 		/*
2417e0c0d44eSPatrick Mooney 		 * This port access was handled in userspace and the result was
2418e0c0d44eSPatrick Mooney 		 * injected in to be handled now.
2419e0c0d44eSPatrick Mooney 		 */
242004573c73SPatrick Mooney 		val = vie->inout_req_val & mask;
2421e0c0d44eSPatrick Mooney 		vie->inout_req_state = VR_NONE;
2422e0c0d44eSPatrick Mooney 		err = 0;
2423e0c0d44eSPatrick Mooney 	}
2424e0c0d44eSPatrick Mooney 
2425e0c0d44eSPatrick Mooney 	if (err == ESRCH) {
2426e0c0d44eSPatrick Mooney 		vie->status |= VIES_PENDING_INOUT;
2427e0c0d44eSPatrick Mooney 		vie->inout_req_state = VR_PENDING;
2428e0c0d44eSPatrick Mooney 		return (err);
2429e0c0d44eSPatrick Mooney 	} else if (err != 0) {
2430e0c0d44eSPatrick Mooney 		return (err);
2431e0c0d44eSPatrick Mooney 	}
2432e0c0d44eSPatrick Mooney 
2433e0c0d44eSPatrick Mooney 	if (in) {
243404573c73SPatrick Mooney 		*eax = (*eax & ~mask) | val;
2435e0c0d44eSPatrick Mooney 	}
2436e0c0d44eSPatrick Mooney 	return (0);
2437e0c0d44eSPatrick Mooney }
2438e0c0d44eSPatrick Mooney 
2439e0c0d44eSPatrick Mooney static enum vm_reg_name
vie_inout_segname(const struct vie * vie)2440e0c0d44eSPatrick Mooney vie_inout_segname(const struct vie *vie)
2441e0c0d44eSPatrick Mooney {
2442e0c0d44eSPatrick Mooney 	uint8_t segidx = vie->inout.segment;
2443e0c0d44eSPatrick Mooney 	const enum vm_reg_name segmap[] = {
2444e0c0d44eSPatrick Mooney 		VM_REG_GUEST_ES,
2445e0c0d44eSPatrick Mooney 		VM_REG_GUEST_CS,
2446e0c0d44eSPatrick Mooney 		VM_REG_GUEST_SS,
2447e0c0d44eSPatrick Mooney 		VM_REG_GUEST_DS,
2448e0c0d44eSPatrick Mooney 		VM_REG_GUEST_FS,
2449e0c0d44eSPatrick Mooney 		VM_REG_GUEST_GS,
2450e0c0d44eSPatrick Mooney 	};
2451e0c0d44eSPatrick Mooney 	const uint8_t maxidx = (sizeof (segmap) / sizeof (segmap[0]));
2452e0c0d44eSPatrick Mooney 
2453e0c0d44eSPatrick Mooney 	if (segidx >= maxidx) {
2454e0c0d44eSPatrick Mooney 		panic("unexpected segment index %u", segidx);
2455e0c0d44eSPatrick Mooney 	}
2456e0c0d44eSPatrick Mooney 	return (segmap[segidx]);
2457e0c0d44eSPatrick Mooney }
2458e0c0d44eSPatrick Mooney 
2459e0c0d44eSPatrick Mooney static int
vie_emulate_inout_str(struct vie * vie,struct vm * vm,int vcpuid)2460e0c0d44eSPatrick Mooney vie_emulate_inout_str(struct vie *vie, struct vm *vm, int vcpuid)
2461e0c0d44eSPatrick Mooney {
2462e0c0d44eSPatrick Mooney 	uint8_t bytes, addrsize;
2463e0c0d44eSPatrick Mooney 	uint64_t index, count = 0, gla, rflags;
2464e0c0d44eSPatrick Mooney 	int prot, err, fault;
2465e0c0d44eSPatrick Mooney 	bool in, repeat;
2466e0c0d44eSPatrick Mooney 	enum vm_reg_name seg_reg, idx_reg;
2467e0c0d44eSPatrick Mooney 	struct vm_copyinfo copyinfo[2];
2468e0c0d44eSPatrick Mooney 
2469e0c0d44eSPatrick Mooney 	in = (vie->inout.flags & INOUT_IN) != 0;
2470e0c0d44eSPatrick Mooney 	bytes = vie->inout.bytes;
2471e0c0d44eSPatrick Mooney 	addrsize = vie->inout.addrsize;
2472e0c0d44eSPatrick Mooney 	prot = in ? PROT_WRITE : PROT_READ;
2473e0c0d44eSPatrick Mooney 
2474e0c0d44eSPatrick Mooney 	ASSERT(bytes == 1 || bytes == 2 || bytes == 4);
2475e0c0d44eSPatrick Mooney 	ASSERT(addrsize == 2 || addrsize == 4 || addrsize == 8);
2476e0c0d44eSPatrick Mooney 
2477e0c0d44eSPatrick Mooney 	idx_reg = (in) ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI;
2478e0c0d44eSPatrick Mooney 	seg_reg = vie_inout_segname(vie);
2479e0c0d44eSPatrick Mooney 	err = vm_get_register(vm, vcpuid, idx_reg, &index);
2480e0c0d44eSPatrick Mooney 	ASSERT(err == 0);
2481e0c0d44eSPatrick Mooney 	index = index & vie_size2mask(addrsize);
2482e0c0d44eSPatrick Mooney 
2483e0c0d44eSPatrick Mooney 	repeat = (vie->inout.flags & INOUT_REP) != 0;
2484e0c0d44eSPatrick Mooney 
2485e0c0d44eSPatrick Mooney 	/* Count register */
2486e0c0d44eSPatrick Mooney 	if (repeat) {
2487e0c0d44eSPatrick Mooney 		err = vm_get_register(vm, vcpuid, VM_REG_GUEST_RCX, &count);
2488e0c0d44eSPatrick Mooney 		count &= vie_size2mask(addrsize);
2489e0c0d44eSPatrick Mooney 
2490e0c0d44eSPatrick Mooney 		if (count == 0) {
2491e0c0d44eSPatrick Mooney 			/*
2492e0c0d44eSPatrick Mooney 			 * If we were asked to emulate a REP INS/OUTS when the
2493e0c0d44eSPatrick Mooney 			 * count register is zero, no further work is required.
2494e0c0d44eSPatrick Mooney 			 */
2495e0c0d44eSPatrick Mooney 			return (0);
2496e0c0d44eSPatrick Mooney 		}
2497e0c0d44eSPatrick Mooney 	} else {
2498e0c0d44eSPatrick Mooney 		count = 1;
2499e0c0d44eSPatrick Mooney 	}
2500e0c0d44eSPatrick Mooney 
2501e0c0d44eSPatrick Mooney 	gla = 0;
2502e0c0d44eSPatrick Mooney 	if (vie_get_gla(vie, vm, vcpuid, bytes, addrsize, prot, seg_reg,
2503e0c0d44eSPatrick Mooney 	    idx_reg, &gla) != 0) {
2504e0c0d44eSPatrick Mooney 		/* vie_get_gla() already injected the appropriate fault */
2505e0c0d44eSPatrick Mooney 		return (0);
2506e0c0d44eSPatrick Mooney 	}
2507e0c0d44eSPatrick Mooney 
2508e0c0d44eSPatrick Mooney 	/*
2509e0c0d44eSPatrick Mooney 	 * The INS/OUTS emulate currently assumes that the memory target resides
2510e0c0d44eSPatrick Mooney 	 * within the guest system memory, rather than a device MMIO region.  If
2511e0c0d44eSPatrick Mooney 	 * such a case becomes a necessity, that additional handling could be
2512e0c0d44eSPatrick Mooney 	 * put in place.
2513e0c0d44eSPatrick Mooney 	 */
2514e0c0d44eSPatrick Mooney 	err = vm_copy_setup(vm, vcpuid, &vie->paging, gla, bytes, prot,
2515e0c0d44eSPatrick Mooney 	    copyinfo, nitems(copyinfo), &fault);
2516e0c0d44eSPatrick Mooney 
2517e0c0d44eSPatrick Mooney 	if (err) {
2518e0c0d44eSPatrick Mooney 		/* Unrecoverable error */
2519e0c0d44eSPatrick Mooney 		return (err);
2520e0c0d44eSPatrick Mooney 	} else if (fault) {
2521e0c0d44eSPatrick Mooney 		/* Resume guest to handle fault */
2522e0c0d44eSPatrick Mooney 		return (0);
2523e0c0d44eSPatrick Mooney 	}
2524e0c0d44eSPatrick Mooney 
2525e0c0d44eSPatrick Mooney 	if (!in) {
2526e0c0d44eSPatrick Mooney 		vm_copyin(vm, vcpuid, copyinfo, &vie->inout.eax, bytes);
2527e0c0d44eSPatrick Mooney 	}
2528e0c0d44eSPatrick Mooney 
252904573c73SPatrick Mooney 	err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax);
2530e0c0d44eSPatrick Mooney 
2531e0c0d44eSPatrick Mooney 	if (err == 0 && in) {
2532e0c0d44eSPatrick Mooney 		vm_copyout(vm, vcpuid, &vie->inout.eax, copyinfo, bytes);
2533e0c0d44eSPatrick Mooney 	}
2534e0c0d44eSPatrick Mooney 
2535e0c0d44eSPatrick Mooney 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
2536e0c0d44eSPatrick Mooney 
2537e0c0d44eSPatrick Mooney 	if (err == 0) {
2538e0c0d44eSPatrick Mooney 		err = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
2539e0c0d44eSPatrick Mooney 		    &rflags);
2540e0c0d44eSPatrick Mooney 		ASSERT(err == 0);
2541e0c0d44eSPatrick Mooney 
2542e0c0d44eSPatrick Mooney 		/* Update index */
2543e0c0d44eSPatrick Mooney 		if (rflags & PSL_D) {
2544e0c0d44eSPatrick Mooney 			index -= bytes;
2545e0c0d44eSPatrick Mooney 		} else {
2546e0c0d44eSPatrick Mooney 			index += bytes;
2547e0c0d44eSPatrick Mooney 		}
2548e0c0d44eSPatrick Mooney 
2549e0c0d44eSPatrick Mooney 		/* Update index register */
2550e0c0d44eSPatrick Mooney 		err = vie_update_register(vm, vcpuid, idx_reg, index, addrsize);
2551e0c0d44eSPatrick Mooney 		ASSERT(err == 0);
2552e0c0d44eSPatrick Mooney 
2553e0c0d44eSPatrick Mooney 		/*
2554e0c0d44eSPatrick Mooney 		 * Update count register only if the instruction had a repeat
2555e0c0d44eSPatrick Mooney 		 * prefix.
2556e0c0d44eSPatrick Mooney 		 */
2557e0c0d44eSPatrick Mooney 		if ((vie->inout.flags & INOUT_REP) != 0) {
2558e0c0d44eSPatrick Mooney 			count--;
2559e0c0d44eSPatrick Mooney 			err = vie_update_register(vm, vcpuid, VM_REG_GUEST_RCX,
2560e0c0d44eSPatrick Mooney 			    count, addrsize);
2561e0c0d44eSPatrick Mooney 			ASSERT(err == 0);
2562e0c0d44eSPatrick Mooney 
2563e0c0d44eSPatrick Mooney 			if (count != 0) {
2564e0c0d44eSPatrick Mooney 				return (vie_repeat(vie));
2565e0c0d44eSPatrick Mooney 			}
2566e0c0d44eSPatrick Mooney 		}
2567e0c0d44eSPatrick Mooney 	}
2568e0c0d44eSPatrick Mooney 
2569e0c0d44eSPatrick Mooney 	return (err);
2570e0c0d44eSPatrick Mooney }
2571e0c0d44eSPatrick Mooney 
2572bf21cd93STycho Nightingale int
vie_emulate_inout(struct vie * vie,struct vm * vm,int vcpuid)2573e0c0d44eSPatrick Mooney vie_emulate_inout(struct vie *vie, struct vm *vm, int vcpuid)
2574e0c0d44eSPatrick Mooney {
2575e0c0d44eSPatrick Mooney 	int err = 0;
2576e0c0d44eSPatrick Mooney 
2577e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_INOUT) == 0) {
2578e0c0d44eSPatrick Mooney 		return (EINVAL);
2579e0c0d44eSPatrick Mooney 	}
2580e0c0d44eSPatrick Mooney 
2581e0c0d44eSPatrick Mooney 	if ((vie->inout.flags & INOUT_STR) == 0) {
2582e0c0d44eSPatrick Mooney 		/*
2583e0c0d44eSPatrick Mooney 		 * For now, using the 'rep' prefixes with plain (non-string)
2584e0c0d44eSPatrick Mooney 		 * in/out is not supported.
2585e0c0d44eSPatrick Mooney 		 */
2586e0c0d44eSPatrick Mooney 		if ((vie->inout.flags & INOUT_REP) != 0) {
2587e0c0d44eSPatrick Mooney 			return (EINVAL);
2588e0c0d44eSPatrick Mooney 		}
2589e0c0d44eSPatrick Mooney 
259004573c73SPatrick Mooney 		err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax);
259104573c73SPatrick Mooney 		if (err == 0 && (vie->inout.flags & INOUT_IN) != 0) {
259204573c73SPatrick Mooney 			/*
259304573c73SPatrick Mooney 			 * With the inX access now a success, the result needs
259404573c73SPatrick Mooney 			 * to be stored in the guest %rax.
259504573c73SPatrick Mooney 			 */
259604573c73SPatrick Mooney 			err = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX,
259704573c73SPatrick Mooney 			    vie->inout.eax);
259804573c73SPatrick Mooney 			VERIFY0(err);
259904573c73SPatrick Mooney 		}
2600e0c0d44eSPatrick Mooney 	} else {
2601e0c0d44eSPatrick Mooney 		vie->status &= ~VIES_REPEAT;
2602e0c0d44eSPatrick Mooney 		err = vie_emulate_inout_str(vie, vm, vcpuid);
2603e0c0d44eSPatrick Mooney 
260406524cf4SPatrick Mooney 	}
260506524cf4SPatrick Mooney 	if (err < 0) {
260606524cf4SPatrick Mooney 		/*
260706524cf4SPatrick Mooney 		 * Access to an I/O port failed in such a way that fallback to
260806524cf4SPatrick Mooney 		 * handling in userspace is required.
260906524cf4SPatrick Mooney 		 */
261006524cf4SPatrick Mooney 		vie->status |= VIES_USER_FALLBACK;
261106524cf4SPatrick Mooney 	} else if (err == ESRCH) {
261206524cf4SPatrick Mooney 		ASSERT(vie->status & VIES_PENDING_INOUT);
261306524cf4SPatrick Mooney 		/* Return to userspace with the in/out request */
261406524cf4SPatrick Mooney 		err = -1;
2615e0c0d44eSPatrick Mooney 	}
2616e0c0d44eSPatrick Mooney 
2617e0c0d44eSPatrick Mooney 	return (err);
2618e0c0d44eSPatrick Mooney }
2619e0c0d44eSPatrick Mooney 
26207db0d193SPatrick Mooney int
vie_emulate_other(struct vie * vie,struct vm * vm,int vcpuid)26217db0d193SPatrick Mooney vie_emulate_other(struct vie *vie, struct vm *vm, int vcpuid)
26227db0d193SPatrick Mooney {
26237db0d193SPatrick Mooney 	int error;
26247db0d193SPatrick Mooney 
26257db0d193SPatrick Mooney 	if ((vie->status & (VIES_INST_DECODE | VIES_OTHER)) !=
26267db0d193SPatrick Mooney 	    (VIES_INST_DECODE | VIES_OTHER)) {
26277db0d193SPatrick Mooney 		return (EINVAL);
26287db0d193SPatrick Mooney 	}
26297db0d193SPatrick Mooney 
26307db0d193SPatrick Mooney 	switch (vie->op.op_type) {
26317db0d193SPatrick Mooney 	case VIE_OP_TYPE_CLTS:
26327db0d193SPatrick Mooney 		error = vie_emulate_clts(vie, vm, vcpuid);
26337db0d193SPatrick Mooney 		break;
2634418ddc26SPatrick Mooney 	case VIE_OP_TYPE_MOV_CR:
2635418ddc26SPatrick Mooney 		error = vie_emulate_mov_cr(vie, vm, vcpuid);
2636418ddc26SPatrick Mooney 		break;
26377db0d193SPatrick Mooney 	default:
26387db0d193SPatrick Mooney 		error = EINVAL;
26397db0d193SPatrick Mooney 		break;
26407db0d193SPatrick Mooney 	}
26417db0d193SPatrick Mooney 
26427db0d193SPatrick Mooney 	return (error);
26437db0d193SPatrick Mooney }
26447db0d193SPatrick Mooney 
2645e0c0d44eSPatrick Mooney void
vie_reset(struct vie * vie)2646e0c0d44eSPatrick Mooney vie_reset(struct vie *vie)
2647e0c0d44eSPatrick Mooney {
2648e0c0d44eSPatrick Mooney 	vie->status = 0;
2649e0c0d44eSPatrick Mooney 	vie->num_processed = vie->num_valid = 0;
2650e0c0d44eSPatrick Mooney }
2651e0c0d44eSPatrick Mooney 
2652e0c0d44eSPatrick Mooney void
vie_advance_pc(struct vie * vie,uint64_t * nextrip)2653e0c0d44eSPatrick Mooney vie_advance_pc(struct vie *vie, uint64_t *nextrip)
2654e0c0d44eSPatrick Mooney {
2655e0c0d44eSPatrick Mooney 	VERIFY((vie->status & VIES_REPEAT) == 0);
2656e0c0d44eSPatrick Mooney 
2657e0c0d44eSPatrick Mooney 	*nextrip += vie->num_processed;
2658e0c0d44eSPatrick Mooney 	vie_reset(vie);
2659e0c0d44eSPatrick Mooney }
2660e0c0d44eSPatrick Mooney 
2661e0c0d44eSPatrick Mooney void
vie_exitinfo(const struct vie * vie,struct vm_exit * vme)2662e0c0d44eSPatrick Mooney vie_exitinfo(const struct vie *vie, struct vm_exit *vme)
2663e0c0d44eSPatrick Mooney {
266406524cf4SPatrick Mooney 	if (vie->status & VIES_USER_FALLBACK) {
266506524cf4SPatrick Mooney 		/*
266606524cf4SPatrick Mooney 		 * Despite the fact that the instruction was successfully
266706524cf4SPatrick Mooney 		 * decoded, some aspect of the emulation failed in such a way
266806524cf4SPatrick Mooney 		 * that it is left up to userspace to complete the operation.
266906524cf4SPatrick Mooney 		 */
267006524cf4SPatrick Mooney 		vie_fallback_exitinfo(vie, vme);
267106524cf4SPatrick Mooney 	} else if (vie->status & VIES_MMIO) {
2672e0c0d44eSPatrick Mooney 		vme->exitcode = VM_EXITCODE_MMIO;
2673e0c0d44eSPatrick Mooney 		if (vie->mmio_req_read.state == VR_PENDING) {
2674e0c0d44eSPatrick Mooney 			vme->u.mmio.gpa = vie->mmio_req_read.gpa;
2675e0c0d44eSPatrick Mooney 			vme->u.mmio.data = 0;
2676e0c0d44eSPatrick Mooney 			vme->u.mmio.bytes = vie->mmio_req_read.bytes;
2677e0c0d44eSPatrick Mooney 			vme->u.mmio.read = 1;
2678e0c0d44eSPatrick Mooney 		} else if (vie->mmio_req_write.state == VR_PENDING) {
2679e0c0d44eSPatrick Mooney 			vme->u.mmio.gpa = vie->mmio_req_write.gpa;
2680e0c0d44eSPatrick Mooney 			vme->u.mmio.data = vie->mmio_req_write.data &
2681e0c0d44eSPatrick Mooney 			    vie_size2mask(vie->mmio_req_write.bytes);
2682e0c0d44eSPatrick Mooney 			vme->u.mmio.bytes = vie->mmio_req_write.bytes;
2683e0c0d44eSPatrick Mooney 			vme->u.mmio.read = 0;
2684e0c0d44eSPatrick Mooney 		} else {
2685e0c0d44eSPatrick Mooney 			panic("bad pending MMIO state");
2686e0c0d44eSPatrick Mooney 		}
2687e0c0d44eSPatrick Mooney 	} else if (vie->status & VIES_INOUT) {
2688e0c0d44eSPatrick Mooney 		vme->exitcode = VM_EXITCODE_INOUT;
2689e0c0d44eSPatrick Mooney 		vme->u.inout.port = vie->inout.port;
2690e0c0d44eSPatrick Mooney 		vme->u.inout.bytes = vie->inout.bytes;
2691e0c0d44eSPatrick Mooney 		if ((vie->inout.flags & INOUT_IN) != 0) {
2692e0c0d44eSPatrick Mooney 			vme->u.inout.flags = INOUT_IN;
2693e0c0d44eSPatrick Mooney 			vme->u.inout.eax = 0;
2694e0c0d44eSPatrick Mooney 		} else {
2695e0c0d44eSPatrick Mooney 			vme->u.inout.flags = 0;
2696e0c0d44eSPatrick Mooney 			vme->u.inout.eax = vie->inout.eax &
2697e0c0d44eSPatrick Mooney 			    vie_size2mask(vie->inout.bytes);
2698e0c0d44eSPatrick Mooney 		}
2699e0c0d44eSPatrick Mooney 	} else {
2700e0c0d44eSPatrick Mooney 		panic("no pending operation");
2701e0c0d44eSPatrick Mooney 	}
2702e0c0d44eSPatrick Mooney }
2703e0c0d44eSPatrick Mooney 
2704e0c0d44eSPatrick Mooney /*
2705e0c0d44eSPatrick Mooney  * In the case of a decoding or verification failure, bailing out to userspace
2706e0c0d44eSPatrick Mooney  * to do the instruction emulation is our only option for now.
2707e0c0d44eSPatrick Mooney  */
2708e0c0d44eSPatrick Mooney void
vie_fallback_exitinfo(const struct vie * vie,struct vm_exit * vme)2709e0c0d44eSPatrick Mooney vie_fallback_exitinfo(const struct vie *vie, struct vm_exit *vme)
2710e0c0d44eSPatrick Mooney {
2711e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_INST_FETCH) == 0) {
2712e0c0d44eSPatrick Mooney 		bzero(&vme->u.inst_emul, sizeof (vme->u.inst_emul));
2713e0c0d44eSPatrick Mooney 	} else {
2714e0c0d44eSPatrick Mooney 		ASSERT(sizeof (vie->inst) == sizeof (vme->u.inst_emul.inst));
2715e0c0d44eSPatrick Mooney 
2716e0c0d44eSPatrick Mooney 		bcopy(vie->inst, vme->u.inst_emul.inst, sizeof (vie->inst));
2717e0c0d44eSPatrick Mooney 		vme->u.inst_emul.num_valid = vie->num_valid;
2718e0c0d44eSPatrick Mooney 	}
2719e0c0d44eSPatrick Mooney 	vme->exitcode = VM_EXITCODE_INST_EMUL;
2720e0c0d44eSPatrick Mooney }
2721e0c0d44eSPatrick Mooney 
27227db0d193SPatrick Mooney void
vie_cs_info(const struct vie * vie,struct vm * vm,int vcpuid,uint64_t * cs_base,int * cs_d)27237db0d193SPatrick Mooney vie_cs_info(const struct vie *vie, struct vm *vm, int vcpuid, uint64_t *cs_base,
27247db0d193SPatrick Mooney     int *cs_d)
27257db0d193SPatrick Mooney {
27267db0d193SPatrick Mooney 	struct seg_desc cs_desc;
2727374973c2SToomas Soome 	int error __maybe_unused;
27287db0d193SPatrick Mooney 
27297db0d193SPatrick Mooney 	error = vm_get_seg_desc(vm, vcpuid, VM_REG_GUEST_CS, &cs_desc);
27307db0d193SPatrick Mooney 	ASSERT(error == 0);
27317db0d193SPatrick Mooney 
27327db0d193SPatrick Mooney 	/* Initialization required for the paging info to be populated */
27337db0d193SPatrick Mooney 	VERIFY(vie->status & VIES_INIT);
27347db0d193SPatrick Mooney 	switch (vie->paging.cpu_mode) {
27357db0d193SPatrick Mooney 	case CPU_MODE_REAL:
27367db0d193SPatrick Mooney 		*cs_base = cs_desc.base;
27377db0d193SPatrick Mooney 		*cs_d = 0;
27387db0d193SPatrick Mooney 		break;
27397db0d193SPatrick Mooney 	case CPU_MODE_PROTECTED:
27407db0d193SPatrick Mooney 	case CPU_MODE_COMPATIBILITY:
27417db0d193SPatrick Mooney 		*cs_base = cs_desc.base;
27427db0d193SPatrick Mooney 		*cs_d = SEG_DESC_DEF32(cs_desc.access) ? 1 : 0;
27437db0d193SPatrick Mooney 		break;
27447db0d193SPatrick Mooney 	default:
27457db0d193SPatrick Mooney 		*cs_base = 0;
27467db0d193SPatrick Mooney 		*cs_d = 0;
27477db0d193SPatrick Mooney 		break;
27487db0d193SPatrick Mooney 	}
27497db0d193SPatrick Mooney }
27507db0d193SPatrick Mooney 
2751e0c0d44eSPatrick Mooney bool
vie_pending(const struct vie * vie)2752e0c0d44eSPatrick Mooney vie_pending(const struct vie *vie)
2753e0c0d44eSPatrick Mooney {
275406524cf4SPatrick Mooney 	/*
275506524cf4SPatrick Mooney 	 * These VIE status bits indicate conditions which must be addressed
275606524cf4SPatrick Mooney 	 * through either device IO fulfillment (with corresponding
275706524cf4SPatrick Mooney 	 * vie_fulfill_*()) or complete userspace emulation (followed by a
275806524cf4SPatrick Mooney 	 * vie_reset()).
275906524cf4SPatrick Mooney 	 */
276006524cf4SPatrick Mooney 	const enum vie_status of_interest =
276106524cf4SPatrick Mooney 	    VIES_PENDING_MMIO | VIES_PENDING_INOUT | VIES_USER_FALLBACK;
276206524cf4SPatrick Mooney 
276306524cf4SPatrick Mooney 	return ((vie->status & of_interest) != 0);
2764e0c0d44eSPatrick Mooney }
2765e0c0d44eSPatrick Mooney 
2766e0c0d44eSPatrick Mooney bool
vie_needs_fetch(const struct vie * vie)2767e0c0d44eSPatrick Mooney vie_needs_fetch(const struct vie *vie)
2768e0c0d44eSPatrick Mooney {
2769e0c0d44eSPatrick Mooney 	if (vie->status & VIES_INST_FETCH) {
2770e0c0d44eSPatrick Mooney 		ASSERT(vie->num_valid != 0);
2771e0c0d44eSPatrick Mooney 		return (false);
2772e0c0d44eSPatrick Mooney 	}
2773e0c0d44eSPatrick Mooney 	return (true);
2774e0c0d44eSPatrick Mooney }
2775e0c0d44eSPatrick Mooney 
2776e0c0d44eSPatrick Mooney static int
vie_alignment_check(int cpl,int size,uint64_t cr0,uint64_t rf,uint64_t gla)2777bf21cd93STycho Nightingale vie_alignment_check(int cpl, int size, uint64_t cr0, uint64_t rf, uint64_t gla)
2778bf21cd93STycho Nightingale {
2779bf21cd93STycho Nightingale 	KASSERT(size == 1 || size == 2 || size == 4 || size == 8,
2780bf21cd93STycho Nightingale 	    ("%s: invalid size %d", __func__, size));
2781bf21cd93STycho Nightingale 	KASSERT(cpl >= 0 && cpl <= 3, ("%s: invalid cpl %d", __func__, cpl));
2782bf21cd93STycho Nightingale 
2783bf21cd93STycho Nightingale 	if (cpl != 3 || (cr0 & CR0_AM) == 0 || (rf & PSL_AC) == 0)
2784bf21cd93STycho Nightingale 		return (0);
2785bf21cd93STycho Nightingale 
2786bf21cd93STycho Nightingale 	return ((gla & (size - 1)) ? 1 : 0);
2787bf21cd93STycho Nightingale }
2788bf21cd93STycho Nightingale 
2789e0c0d44eSPatrick Mooney static int
vie_canonical_check(enum vm_cpu_mode cpu_mode,uint64_t gla)2790bf21cd93STycho Nightingale vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla)
2791bf21cd93STycho Nightingale {
2792bf21cd93STycho Nightingale 	uint64_t mask;
2793bf21cd93STycho Nightingale 
2794bf21cd93STycho Nightingale 	if (cpu_mode != CPU_MODE_64BIT)
2795bf21cd93STycho Nightingale 		return (0);
2796bf21cd93STycho Nightingale 
2797bf21cd93STycho Nightingale 	/*
2798bf21cd93STycho Nightingale 	 * The value of the bit 47 in the 'gla' should be replicated in the
2799bf21cd93STycho Nightingale 	 * most significant 16 bits.
2800bf21cd93STycho Nightingale 	 */
2801bf21cd93STycho Nightingale 	mask = ~((1UL << 48) - 1);
2802bf21cd93STycho Nightingale 	if (gla & (1UL << 47))
2803bf21cd93STycho Nightingale 		return ((gla & mask) != mask);
2804bf21cd93STycho Nightingale 	else
2805bf21cd93STycho Nightingale 		return ((gla & mask) != 0);
2806bf21cd93STycho Nightingale }
2807bf21cd93STycho Nightingale 
2808e0c0d44eSPatrick Mooney static uint64_t
vie_size2mask(int size)2809bf21cd93STycho Nightingale vie_size2mask(int size)
2810bf21cd93STycho Nightingale {
2811bf21cd93STycho Nightingale 	KASSERT(size == 1 || size == 2 || size == 4 || size == 8,
2812bf21cd93STycho Nightingale 	    ("vie_size2mask: invalid size %d", size));
2813bf21cd93STycho Nightingale 	return (size2mask[size]);
2814bf21cd93STycho Nightingale }
2815bf21cd93STycho Nightingale 
2816e0c0d44eSPatrick 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)2817bf21cd93STycho Nightingale vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg,
2818bf21cd93STycho Nightingale     struct seg_desc *desc, uint64_t offset, int length, int addrsize,
2819bf21cd93STycho Nightingale     int prot, uint64_t *gla)
2820bf21cd93STycho Nightingale {
2821bf21cd93STycho Nightingale 	uint64_t firstoff, low_limit, high_limit, segbase;
2822bf21cd93STycho Nightingale 	int glasize, type;
2823bf21cd93STycho Nightingale 
2824bf21cd93STycho Nightingale 	KASSERT(seg >= VM_REG_GUEST_ES && seg <= VM_REG_GUEST_GS,
2825bf21cd93STycho Nightingale 	    ("%s: invalid segment %d", __func__, seg));
2826bf21cd93STycho Nightingale 	KASSERT(length == 1 || length == 2 || length == 4 || length == 8,
2827bf21cd93STycho Nightingale 	    ("%s: invalid operand size %d", __func__, length));
2828bf21cd93STycho Nightingale 	KASSERT((prot & ~(PROT_READ | PROT_WRITE)) == 0,
2829bf21cd93STycho Nightingale 	    ("%s: invalid prot %x", __func__, prot));
2830bf21cd93STycho Nightingale 
2831bf21cd93STycho Nightingale 	firstoff = offset;
2832bf21cd93STycho Nightingale 	if (cpu_mode == CPU_MODE_64BIT) {
2833bf21cd93STycho Nightingale 		KASSERT(addrsize == 4 || addrsize == 8, ("%s: invalid address "
2834bf21cd93STycho Nightingale 		    "size %d for cpu_mode %d", __func__, addrsize, cpu_mode));
2835bf21cd93STycho Nightingale 		glasize = 8;
2836bf21cd93STycho Nightingale 	} else {
2837bf21cd93STycho Nightingale 		KASSERT(addrsize == 2 || addrsize == 4, ("%s: invalid address "
2838bf21cd93STycho Nightingale 		    "size %d for cpu mode %d", __func__, addrsize, cpu_mode));
2839bf21cd93STycho Nightingale 		glasize = 4;
2840bf21cd93STycho Nightingale 		/*
2841bf21cd93STycho Nightingale 		 * If the segment selector is loaded with a NULL selector
2842bf21cd93STycho Nightingale 		 * then the descriptor is unusable and attempting to use
2843bf21cd93STycho Nightingale 		 * it results in a #GP(0).
2844bf21cd93STycho Nightingale 		 */
2845bf21cd93STycho Nightingale 		if (SEG_DESC_UNUSABLE(desc->access))
2846bf21cd93STycho Nightingale 			return (-1);
2847bf21cd93STycho Nightingale 
2848e0c0d44eSPatrick Mooney 		/*
2849bf21cd93STycho Nightingale 		 * The processor generates a #NP exception when a segment
2850bf21cd93STycho Nightingale 		 * register is loaded with a selector that points to a
2851bf21cd93STycho Nightingale 		 * descriptor that is not present. If this was the case then
2852bf21cd93STycho Nightingale 		 * it would have been checked before the VM-exit.
2853bf21cd93STycho Nightingale 		 */
2854bf21cd93STycho Nightingale 		KASSERT(SEG_DESC_PRESENT(desc->access),
2855bf21cd93STycho Nightingale 		    ("segment %d not present: %x", seg, desc->access));
2856bf21cd93STycho Nightingale 
2857bf21cd93STycho Nightingale 		/*
2858bf21cd93STycho Nightingale 		 * The descriptor type must indicate a code/data segment.
2859bf21cd93STycho Nightingale 		 */
2860bf21cd93STycho Nightingale 		type = SEG_DESC_TYPE(desc->access);
2861bf21cd93STycho Nightingale 		KASSERT(type >= 16 && type <= 31, ("segment %d has invalid "
2862bf21cd93STycho Nightingale 		    "descriptor type %x", seg, type));
2863bf21cd93STycho Nightingale 
2864bf21cd93STycho Nightingale 		if (prot & PROT_READ) {
2865bf21cd93STycho Nightingale 			/* #GP on a read access to a exec-only code segment */
2866bf21cd93STycho Nightingale 			if ((type & 0xA) == 0x8)
2867bf21cd93STycho Nightingale 				return (-1);
2868bf21cd93STycho Nightingale 		}
2869bf21cd93STycho Nightingale 
2870bf21cd93STycho Nightingale 		if (prot & PROT_WRITE) {
2871bf21cd93STycho Nightingale 			/*
2872bf21cd93STycho Nightingale 			 * #GP on a write access to a code segment or a
2873bf21cd93STycho Nightingale 			 * read-only data segment.
2874bf21cd93STycho Nightingale 			 */
2875bf21cd93STycho Nightingale 			if (type & 0x8)			/* code segment */
2876bf21cd93STycho Nightingale 				return (-1);
2877bf21cd93STycho Nightingale 
2878bf21cd93STycho Nightingale 			if ((type & 0xA) == 0)		/* read-only data seg */
2879bf21cd93STycho Nightingale 				return (-1);
2880bf21cd93STycho Nightingale 		}
2881bf21cd93STycho Nightingale 
2882bf21cd93STycho Nightingale 		/*
2883bf21cd93STycho Nightingale 		 * 'desc->limit' is fully expanded taking granularity into
2884bf21cd93STycho Nightingale 		 * account.
2885bf21cd93STycho Nightingale 		 */
2886bf21cd93STycho Nightingale 		if ((type & 0xC) == 0x4) {
2887bf21cd93STycho Nightingale 			/* expand-down data segment */
2888bf21cd93STycho Nightingale 			low_limit = desc->limit + 1;
2889bf21cd93STycho Nightingale 			high_limit = SEG_DESC_DEF32(desc->access) ?
2890bf21cd93STycho Nightingale 			    0xffffffff : 0xffff;
2891bf21cd93STycho Nightingale 		} else {
2892bf21cd93STycho Nightingale 			/* code segment or expand-up data segment */
2893bf21cd93STycho Nightingale 			low_limit = 0;
2894bf21cd93STycho Nightingale 			high_limit = desc->limit;
2895bf21cd93STycho Nightingale 		}
2896bf21cd93STycho Nightingale 
2897bf21cd93STycho Nightingale 		while (length > 0) {
2898bf21cd93STycho Nightingale 			offset &= vie_size2mask(addrsize);
2899bf21cd93STycho Nightingale 			if (offset < low_limit || offset > high_limit)
2900bf21cd93STycho Nightingale 				return (-1);
2901bf21cd93STycho Nightingale 			offset++;
2902bf21cd93STycho Nightingale 			length--;
2903bf21cd93STycho Nightingale 		}
2904bf21cd93STycho Nightingale 	}
2905bf21cd93STycho Nightingale 
2906bf21cd93STycho Nightingale 	/*
2907bf21cd93STycho Nightingale 	 * In 64-bit mode all segments except %fs and %gs have a segment
2908bf21cd93STycho Nightingale 	 * base address of 0.
2909bf21cd93STycho Nightingale 	 */
2910bf21cd93STycho Nightingale 	if (cpu_mode == CPU_MODE_64BIT && seg != VM_REG_GUEST_FS &&
2911bf21cd93STycho Nightingale 	    seg != VM_REG_GUEST_GS) {
2912bf21cd93STycho Nightingale 		segbase = 0;
2913bf21cd93STycho Nightingale 	} else {
2914bf21cd93STycho Nightingale 		segbase = desc->base;
2915bf21cd93STycho Nightingale 	}
2916bf21cd93STycho Nightingale 
2917bf21cd93STycho Nightingale 	/*
2918bf21cd93STycho Nightingale 	 * Truncate 'firstoff' to the effective address size before adding
2919bf21cd93STycho Nightingale 	 * it to the segment base.
2920bf21cd93STycho Nightingale 	 */
2921bf21cd93STycho Nightingale 	firstoff &= vie_size2mask(addrsize);
2922bf21cd93STycho Nightingale 	*gla = (segbase + firstoff) & vie_size2mask(glasize);
2923bf21cd93STycho Nightingale 	return (0);
2924bf21cd93STycho Nightingale }
2925bf21cd93STycho Nightingale 
2926bf21cd93STycho Nightingale void
vie_init_mmio(struct vie * vie,const char * inst_bytes,uint8_t inst_length,const struct vm_guest_paging * paging,uint64_t gpa)2927e0c0d44eSPatrick Mooney vie_init_mmio(struct vie *vie, const char *inst_bytes, uint8_t inst_length,
2928e0c0d44eSPatrick Mooney     const struct vm_guest_paging *paging, uint64_t gpa)
2929bf21cd93STycho Nightingale {
2930e0c0d44eSPatrick Mooney 	KASSERT(inst_length <= VIE_INST_SIZE,
2931bf21cd93STycho Nightingale 	    ("%s: invalid instruction length (%d)", __func__, inst_length));
2932bf21cd93STycho Nightingale 
2933e0c0d44eSPatrick Mooney 	bzero(vie, sizeof (struct vie));
2934bf21cd93STycho Nightingale 
2935bf21cd93STycho Nightingale 	vie->base_register = VM_REG_LAST;
2936bf21cd93STycho Nightingale 	vie->index_register = VM_REG_LAST;
2937bf21cd93STycho Nightingale 	vie->segment_register = VM_REG_LAST;
2938e0c0d44eSPatrick Mooney 	vie->status = VIES_INIT | VIES_MMIO;
2939bf21cd93STycho Nightingale 
2940e0c0d44eSPatrick Mooney 	if (inst_length != 0) {
2941bf21cd93STycho Nightingale 		bcopy(inst_bytes, vie->inst, inst_length);
2942bf21cd93STycho Nightingale 		vie->num_valid = inst_length;
2943e0c0d44eSPatrick Mooney 		vie->status |= VIES_INST_FETCH;
2944e0c0d44eSPatrick Mooney 	}
2945e0c0d44eSPatrick Mooney 
2946e0c0d44eSPatrick Mooney 	vie->paging = *paging;
2947e0c0d44eSPatrick Mooney 	vie->mmio_gpa = gpa;
2948e0c0d44eSPatrick Mooney }
2949e0c0d44eSPatrick Mooney 
2950e0c0d44eSPatrick Mooney void
vie_init_inout(struct vie * vie,const struct vm_inout * inout,uint8_t inst_len,const struct vm_guest_paging * paging)2951e0c0d44eSPatrick Mooney vie_init_inout(struct vie *vie, const struct vm_inout *inout, uint8_t inst_len,
2952e0c0d44eSPatrick Mooney     const struct vm_guest_paging *paging)
2953e0c0d44eSPatrick Mooney {
2954e0c0d44eSPatrick Mooney 	bzero(vie, sizeof (struct vie));
2955e0c0d44eSPatrick Mooney 
2956e0c0d44eSPatrick Mooney 	vie->status = VIES_INIT | VIES_INOUT;
2957e0c0d44eSPatrick Mooney 
2958e0c0d44eSPatrick Mooney 	vie->inout = *inout;
2959e0c0d44eSPatrick Mooney 	vie->paging = *paging;
2960e0c0d44eSPatrick Mooney 
2961e0c0d44eSPatrick Mooney 	/*
2962e0c0d44eSPatrick Mooney 	 * Since VMX/SVM assists already decoded the nature of the in/out
2963e0c0d44eSPatrick Mooney 	 * instruction, let the status reflect that.
2964e0c0d44eSPatrick Mooney 	 */
2965e0c0d44eSPatrick Mooney 	vie->status |= VIES_INST_FETCH | VIES_INST_DECODE;
2966e0c0d44eSPatrick Mooney 	vie->num_processed = inst_len;
2967e0c0d44eSPatrick Mooney }
2968e0c0d44eSPatrick Mooney 
29697db0d193SPatrick Mooney void
vie_init_other(struct vie * vie,const struct vm_guest_paging * paging)29707db0d193SPatrick Mooney vie_init_other(struct vie *vie, const struct vm_guest_paging *paging)
29717db0d193SPatrick Mooney {
29727db0d193SPatrick Mooney 	bzero(vie, sizeof (struct vie));
29737db0d193SPatrick Mooney 
29747db0d193SPatrick Mooney 	vie->base_register = VM_REG_LAST;
29757db0d193SPatrick Mooney 	vie->index_register = VM_REG_LAST;
29767db0d193SPatrick Mooney 	vie->segment_register = VM_REG_LAST;
29777db0d193SPatrick Mooney 	vie->status = VIES_INIT | VIES_OTHER;
29787db0d193SPatrick Mooney 
29797db0d193SPatrick Mooney 	vie->paging = *paging;
29807db0d193SPatrick Mooney }
29817db0d193SPatrick Mooney 
2982e0c0d44eSPatrick Mooney int
vie_fulfill_mmio(struct vie * vie,const struct vm_mmio * result)2983e0c0d44eSPatrick Mooney vie_fulfill_mmio(struct vie *vie, const struct vm_mmio *result)
2984e0c0d44eSPatrick Mooney {
2985e0c0d44eSPatrick Mooney 	struct vie_mmio *pending;
2986e0c0d44eSPatrick Mooney 
2987e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_MMIO) == 0 ||
2988e0c0d44eSPatrick Mooney 	    (vie->status & VIES_PENDING_MMIO) == 0) {
2989e0c0d44eSPatrick Mooney 		return (EINVAL);
2990e0c0d44eSPatrick Mooney 	}
2991e0c0d44eSPatrick Mooney 
2992e0c0d44eSPatrick Mooney 	if (result->read) {
2993e0c0d44eSPatrick Mooney 		pending = &vie->mmio_req_read;
2994e0c0d44eSPatrick Mooney 	} else {
2995e0c0d44eSPatrick Mooney 		pending = &vie->mmio_req_write;
2996e0c0d44eSPatrick Mooney 	}
2997e0c0d44eSPatrick Mooney 
2998e0c0d44eSPatrick Mooney 	if (pending->state != VR_PENDING ||
2999e0c0d44eSPatrick Mooney 	    pending->bytes != result->bytes || pending->gpa != result->gpa) {
3000e0c0d44eSPatrick Mooney 		return (EINVAL);
3001e0c0d44eSPatrick Mooney 	}
3002e0c0d44eSPatrick Mooney 
3003e0c0d44eSPatrick Mooney 	if (result->read) {
3004e0c0d44eSPatrick Mooney 		pending->data = result->data & vie_size2mask(pending->bytes);
3005e0c0d44eSPatrick Mooney 	}
3006e0c0d44eSPatrick Mooney 	pending->state = VR_DONE;
3007e0c0d44eSPatrick Mooney 	vie->status &= ~VIES_PENDING_MMIO;
3008e0c0d44eSPatrick Mooney 
3009e0c0d44eSPatrick Mooney 	return (0);
3010e0c0d44eSPatrick Mooney }
3011e0c0d44eSPatrick Mooney 
3012e0c0d44eSPatrick Mooney int
vie_fulfill_inout(struct vie * vie,const struct vm_inout * result)3013e0c0d44eSPatrick Mooney vie_fulfill_inout(struct vie *vie, const struct vm_inout *result)
3014e0c0d44eSPatrick Mooney {
3015e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_INOUT) == 0 ||
3016e0c0d44eSPatrick Mooney 	    (vie->status & VIES_PENDING_INOUT) == 0) {
3017e0c0d44eSPatrick Mooney 		return (EINVAL);
3018bf21cd93STycho Nightingale 	}
3019e0c0d44eSPatrick Mooney 	if ((vie->inout.flags & INOUT_IN) != (result->flags & INOUT_IN) ||
3020e0c0d44eSPatrick Mooney 	    vie->inout.bytes != result->bytes ||
3021e0c0d44eSPatrick Mooney 	    vie->inout.port != result->port) {
3022e0c0d44eSPatrick Mooney 		return (EINVAL);
3023e0c0d44eSPatrick Mooney 	}
3024e0c0d44eSPatrick Mooney 
3025e0c0d44eSPatrick Mooney 	if (result->flags & INOUT_IN) {
3026e0c0d44eSPatrick Mooney 		vie->inout_req_val = result->eax &
3027e0c0d44eSPatrick Mooney 		    vie_size2mask(vie->inout.bytes);
3028e0c0d44eSPatrick Mooney 	}
3029e0c0d44eSPatrick Mooney 	vie->inout_req_state = VR_DONE;
3030e0c0d44eSPatrick Mooney 	vie->status &= ~(VIES_PENDING_INOUT);
3031e0c0d44eSPatrick Mooney 
3032e0c0d44eSPatrick Mooney 	return (0);
3033e0c0d44eSPatrick Mooney }
3034e0c0d44eSPatrick Mooney 
3035e0c0d44eSPatrick Mooney uint64_t
vie_mmio_gpa(const struct vie * vie)3036e0c0d44eSPatrick Mooney vie_mmio_gpa(const struct vie *vie)
3037e0c0d44eSPatrick Mooney {
3038e0c0d44eSPatrick Mooney 	return (vie->mmio_gpa);
3039bf21cd93STycho Nightingale }
3040bf21cd93STycho Nightingale 
3041bf21cd93STycho Nightingale static int
pf_error_code(int usermode,int prot,int rsvd,uint64_t pte)3042bf21cd93STycho Nightingale pf_error_code(int usermode, int prot, int rsvd, uint64_t pte)
3043bf21cd93STycho Nightingale {
3044bf21cd93STycho Nightingale 	int error_code = 0;
3045bf21cd93STycho Nightingale 
3046bf21cd93STycho Nightingale 	if (pte & PG_V)
3047bf21cd93STycho Nightingale 		error_code |= PGEX_P;
3048cf409e3fSDan Cross 	if (prot & PROT_WRITE)
3049bf21cd93STycho Nightingale 		error_code |= PGEX_W;
3050bf21cd93STycho Nightingale 	if (usermode)
3051bf21cd93STycho Nightingale 		error_code |= PGEX_U;
3052bf21cd93STycho Nightingale 	if (rsvd)
3053bf21cd93STycho Nightingale 		error_code |= PGEX_RSV;
3054cf409e3fSDan Cross 	if (prot & PROT_EXEC)
3055bf21cd93STycho Nightingale 		error_code |= PGEX_I;
3056bf21cd93STycho Nightingale 
3057bf21cd93STycho Nightingale 	return (error_code);
3058bf21cd93STycho Nightingale }
3059bf21cd93STycho Nightingale 
3060bf21cd93STycho Nightingale static void
ptp_release(vm_page_t ** vmp)30610153d828SPatrick Mooney ptp_release(vm_page_t **vmp)
3062bf21cd93STycho Nightingale {
30630153d828SPatrick Mooney 	if (*vmp != NULL) {
3064e0994bd2SPatrick Mooney 		(void) vmp_release(*vmp);
30650153d828SPatrick Mooney 		*vmp = NULL;
3066bf21cd93STycho Nightingale 	}
3067bf21cd93STycho Nightingale }
3068bf21cd93STycho Nightingale 
3069bf21cd93STycho Nightingale static void *
ptp_hold(struct vm * vm,int vcpu,uintptr_t gpa,size_t len,vm_page_t ** vmp)30700153d828SPatrick Mooney ptp_hold(struct vm *vm, int vcpu, uintptr_t gpa, size_t len, vm_page_t **vmp)
3071bf21cd93STycho Nightingale {
30720153d828SPatrick Mooney 	vm_client_t *vmc = vm_get_vmclient(vm, vcpu);
30730153d828SPatrick Mooney 	const uintptr_t hold_gpa = gpa & PAGEMASK;
30740153d828SPatrick Mooney 
30750153d828SPatrick Mooney 	/* Hold must not cross a page boundary */
30760153d828SPatrick Mooney 	VERIFY3U(gpa + len, <=, hold_gpa + PAGESIZE);
3077bf21cd93STycho Nightingale 
30780153d828SPatrick Mooney 	if (*vmp != NULL) {
3079e0994bd2SPatrick Mooney 		(void) vmp_release(*vmp);
30800153d828SPatrick Mooney 	}
30810153d828SPatrick Mooney 
30820153d828SPatrick Mooney 	*vmp = vmc_hold(vmc, hold_gpa, PROT_READ | PROT_WRITE);
30830153d828SPatrick Mooney 	if (*vmp == NULL) {
30840153d828SPatrick Mooney 		return (NULL);
30850153d828SPatrick Mooney 	}
3086cf409e3fSDan Cross 
30870153d828SPatrick Mooney 	return ((caddr_t)vmp_get_writable(*vmp) + (gpa - hold_gpa));
3088bf21cd93STycho Nightingale }
3089bf21cd93STycho Nightingale 
30904c87aefeSPatrick 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)30914c87aefeSPatrick Mooney _vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
30924c87aefeSPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault, bool check_only)
3093bf21cd93STycho Nightingale {
30940153d828SPatrick Mooney 	int nlevels, pfcode;
30954c87aefeSPatrick Mooney 	int ptpshift = 0, ptpindex = 0;
30964c87aefeSPatrick Mooney 	uint64_t ptpphys;
30974c87aefeSPatrick Mooney 	uint64_t *ptpbase = NULL, pte = 0, pgsize = 0;
30980153d828SPatrick Mooney 	vm_page_t *cookie = NULL;
30990153d828SPatrick Mooney 	const bool usermode = paging->cpl == 3;
31000153d828SPatrick Mooney 	const bool writable = (prot & PROT_WRITE) != 0;
3101bf21cd93STycho Nightingale 
31024c87aefeSPatrick Mooney 	*guest_fault = 0;
3103bf21cd93STycho Nightingale restart:
3104bf21cd93STycho Nightingale 	ptpphys = paging->cr3;		/* root of the page tables */
3105bf21cd93STycho Nightingale 	ptp_release(&cookie);
3106bf21cd93STycho Nightingale 
3107bf21cd93STycho Nightingale 	if (vie_canonical_check(paging->cpu_mode, gla)) {
3108bf21cd93STycho Nightingale 		/*
3109bf21cd93STycho Nightingale 		 * XXX assuming a non-stack reference otherwise a stack fault
3110bf21cd93STycho Nightingale 		 * should be generated.
3111bf21cd93STycho Nightingale 		 */
31124c87aefeSPatrick Mooney 		if (!check_only)
31134c87aefeSPatrick Mooney 			vm_inject_gp(vm, vcpuid);
31140153d828SPatrick Mooney 		*guest_fault = 1;
31150153d828SPatrick Mooney 		return (0);
3116bf21cd93STycho Nightingale 	}
3117bf21cd93STycho Nightingale 
3118bf21cd93STycho Nightingale 	if (paging->paging_mode == PAGING_MODE_FLAT) {
3119bf21cd93STycho Nightingale 		*gpa = gla;
31200153d828SPatrick Mooney 		return (0);
3121bf21cd93STycho Nightingale 	}
3122bf21cd93STycho Nightingale 
3123bf21cd93STycho Nightingale 	if (paging->paging_mode == PAGING_MODE_32) {
31240153d828SPatrick Mooney 		uint32_t *ptpbase32, pte32;
31250153d828SPatrick Mooney 
3126bf21cd93STycho Nightingale 		nlevels = 2;
3127bf21cd93STycho Nightingale 		while (--nlevels >= 0) {
3128bf21cd93STycho Nightingale 			/* Zero out the lower 12 bits. */
3129bf21cd93STycho Nightingale 			ptpphys &= ~0xfff;
3130bf21cd93STycho Nightingale 
31314c87aefeSPatrick Mooney 			ptpbase32 = ptp_hold(vm, vcpuid, ptpphys, PAGE_SIZE,
31324c87aefeSPatrick Mooney 			    &cookie);
3133bf21cd93STycho Nightingale 
31340153d828SPatrick Mooney 			if (ptpbase32 == NULL) {
31350153d828SPatrick Mooney 				return (EFAULT);
31360153d828SPatrick Mooney 			}
3137bf21cd93STycho Nightingale 
3138bf21cd93STycho Nightingale 			ptpshift = PAGE_SHIFT + nlevels * 10;
3139bf21cd93STycho Nightingale 			ptpindex = (gla >> ptpshift) & 0x3FF;
3140bf21cd93STycho Nightingale 			pgsize = 1UL << ptpshift;
3141bf21cd93STycho Nightingale 
3142bf21cd93STycho Nightingale 			pte32 = ptpbase32[ptpindex];
3143bf21cd93STycho Nightingale 
3144bf21cd93STycho Nightingale 			if ((pte32 & PG_V) == 0 ||
3145bf21cd93STycho Nightingale 			    (usermode && (pte32 & PG_U) == 0) ||
3146bf21cd93STycho Nightingale 			    (writable && (pte32 & PG_RW) == 0)) {
31474c87aefeSPatrick Mooney 				if (!check_only) {
31482699b94cSPatrick Mooney 					pfcode = pf_error_code(usermode, prot,
31492699b94cSPatrick Mooney 					    0, pte32);
31504c87aefeSPatrick Mooney 					vm_inject_pf(vm, vcpuid, pfcode, gla);
31514c87aefeSPatrick Mooney 				}
31520153d828SPatrick Mooney 
31530153d828SPatrick Mooney 				ptp_release(&cookie);
31540153d828SPatrick Mooney 				*guest_fault = 1;
31550153d828SPatrick Mooney 				return (0);
3156bf21cd93STycho Nightingale 			}
3157bf21cd93STycho Nightingale 
3158bf21cd93STycho Nightingale 			/*
3159bf21cd93STycho Nightingale 			 * Emulate the x86 MMU's management of the accessed
3160bf21cd93STycho Nightingale 			 * and dirty flags. While the accessed flag is set
3161bf21cd93STycho Nightingale 			 * at every level of the page table, the dirty flag
3162bf21cd93STycho Nightingale 			 * is only set at the last level providing the guest
3163bf21cd93STycho Nightingale 			 * physical address.
3164bf21cd93STycho Nightingale 			 */
31654c87aefeSPatrick Mooney 			if (!check_only && (pte32 & PG_A) == 0) {
3166bf21cd93STycho Nightingale 				if (atomic_cmpset_32(&ptpbase32[ptpindex],
3167bf21cd93STycho Nightingale 				    pte32, pte32 | PG_A) == 0) {
3168bf21cd93STycho Nightingale 					goto restart;
3169bf21cd93STycho Nightingale 				}
3170bf21cd93STycho Nightingale 			}
3171bf21cd93STycho Nightingale 
3172bf21cd93STycho Nightingale 			/* XXX must be ignored if CR4.PSE=0 */
3173bf21cd93STycho Nightingale 			if (nlevels > 0 && (pte32 & PG_PS) != 0)
3174bf21cd93STycho Nightingale 				break;
3175bf21cd93STycho Nightingale 
3176bf21cd93STycho Nightingale 			ptpphys = pte32;
3177bf21cd93STycho Nightingale 		}
3178bf21cd93STycho Nightingale 
3179bf21cd93STycho Nightingale 		/* Set the dirty bit in the page table entry if necessary */
31804c87aefeSPatrick Mooney 		if (!check_only && writable && (pte32 & PG_M) == 0) {
3181bf21cd93STycho Nightingale 			if (atomic_cmpset_32(&ptpbase32[ptpindex],
3182bf21cd93STycho Nightingale 			    pte32, pte32 | PG_M) == 0) {
3183bf21cd93STycho Nightingale 				goto restart;
3184bf21cd93STycho Nightingale 			}
3185bf21cd93STycho Nightingale 		}
3186bf21cd93STycho Nightingale 
3187bf21cd93STycho Nightingale 		/* Zero out the lower 'ptpshift' bits */
3188bf21cd93STycho Nightingale 		pte32 >>= ptpshift; pte32 <<= ptpshift;
3189bf21cd93STycho Nightingale 		*gpa = pte32 | (gla & (pgsize - 1));
31900153d828SPatrick Mooney 		ptp_release(&cookie);
31910153d828SPatrick Mooney 		return (0);
3192bf21cd93STycho Nightingale 	}
3193bf21cd93STycho Nightingale 
3194bf21cd93STycho Nightingale 	if (paging->paging_mode == PAGING_MODE_PAE) {
3195bf21cd93STycho Nightingale 		/* Zero out the lower 5 bits and the upper 32 bits */
3196bf21cd93STycho Nightingale 		ptpphys &= 0xffffffe0UL;
3197bf21cd93STycho Nightingale 
31982699b94cSPatrick Mooney 		ptpbase = ptp_hold(vm, vcpuid, ptpphys, sizeof (*ptpbase) * 4,
31994c87aefeSPatrick Mooney 		    &cookie);
32000153d828SPatrick Mooney 		if (ptpbase == NULL) {
32010153d828SPatrick Mooney 			return (EFAULT);
32020153d828SPatrick Mooney 		}
3203bf21cd93STycho Nightingale 
3204bf21cd93STycho Nightingale 		ptpindex = (gla >> 30) & 0x3;
3205bf21cd93STycho Nightingale 
3206bf21cd93STycho Nightingale 		pte = ptpbase[ptpindex];
3207bf21cd93STycho Nightingale 
3208bf21cd93STycho Nightingale 		if ((pte & PG_V) == 0) {
32094c87aefeSPatrick Mooney 			if (!check_only) {
32104c87aefeSPatrick Mooney 				pfcode = pf_error_code(usermode, prot, 0, pte);
32114c87aefeSPatrick Mooney 				vm_inject_pf(vm, vcpuid, pfcode, gla);
32124c87aefeSPatrick Mooney 			}
32130153d828SPatrick Mooney 
32140153d828SPatrick Mooney 			ptp_release(&cookie);
32150153d828SPatrick Mooney 			*guest_fault = 1;
32160153d828SPatrick Mooney 			return (0);
3217bf21cd93STycho Nightingale 		}
3218bf21cd93STycho Nightingale 
3219bf21cd93STycho Nightingale 		ptpphys = pte;
3220bf21cd93STycho Nightingale 
3221bf21cd93STycho Nightingale 		nlevels = 2;
32220153d828SPatrick Mooney 	} else {
3223bf21cd93STycho Nightingale 		nlevels = 4;
32240153d828SPatrick Mooney 	}
32250153d828SPatrick Mooney 
3226bf21cd93STycho Nightingale 	while (--nlevels >= 0) {
3227bf21cd93STycho Nightingale 		/* Zero out the lower 12 bits and the upper 12 bits */
32280153d828SPatrick Mooney 		ptpphys &= 0x000ffffffffff000UL;
3229bf21cd93STycho Nightingale 
32304c87aefeSPatrick Mooney 		ptpbase = ptp_hold(vm, vcpuid, ptpphys, PAGE_SIZE, &cookie);
32310153d828SPatrick Mooney 		if (ptpbase == NULL) {
32320153d828SPatrick Mooney 			return (EFAULT);
32330153d828SPatrick Mooney 		}
3234bf21cd93STycho Nightingale 
3235bf21cd93STycho Nightingale 		ptpshift = PAGE_SHIFT + nlevels * 9;
3236bf21cd93STycho Nightingale 		ptpindex = (gla >> ptpshift) & 0x1FF;
3237bf21cd93STycho Nightingale 		pgsize = 1UL << ptpshift;
3238bf21cd93STycho Nightingale 
3239bf21cd93STycho Nightingale 		pte = ptpbase[ptpindex];
3240bf21cd93STycho Nightingale 
3241bf21cd93STycho Nightingale 		if ((pte & PG_V) == 0 ||
3242bf21cd93STycho Nightingale 		    (usermode && (pte & PG_U) == 0) ||
3243bf21cd93STycho Nightingale 		    (writable && (pte & PG_RW) == 0)) {
32444c87aefeSPatrick Mooney 			if (!check_only) {
32454c87aefeSPatrick Mooney 				pfcode = pf_error_code(usermode, prot, 0, pte);
32464c87aefeSPatrick Mooney 				vm_inject_pf(vm, vcpuid, pfcode, gla);
32474c87aefeSPatrick Mooney 			}
32480153d828SPatrick Mooney 
32490153d828SPatrick Mooney 			ptp_release(&cookie);
32500153d828SPatrick Mooney 			*guest_fault = 1;
32510153d828SPatrick Mooney 			return (0);
3252bf21cd93STycho Nightingale 		}
3253bf21cd93STycho Nightingale 
3254bf21cd93STycho Nightingale 		/* Set the accessed bit in the page table entry */
32554c87aefeSPatrick Mooney 		if (!check_only && (pte & PG_A) == 0) {
3256bf21cd93STycho Nightingale 			if (atomic_cmpset_64(&ptpbase[ptpindex],
3257bf21cd93STycho Nightingale 			    pte, pte | PG_A) == 0) {
3258bf21cd93STycho Nightingale 				goto restart;
3259bf21cd93STycho Nightingale 			}
3260bf21cd93STycho Nightingale 		}
3261bf21cd93STycho Nightingale 
3262bf21cd93STycho Nightingale 		if (nlevels > 0 && (pte & PG_PS) != 0) {
3263bf21cd93STycho Nightingale 			if (pgsize > 1 * GB) {
32644c87aefeSPatrick Mooney 				if (!check_only) {
32652699b94cSPatrick Mooney 					pfcode = pf_error_code(usermode, prot,
32662699b94cSPatrick Mooney 					    1, pte);
32674c87aefeSPatrick Mooney 					vm_inject_pf(vm, vcpuid, pfcode, gla);
32684c87aefeSPatrick Mooney 				}
32690153d828SPatrick Mooney 
32700153d828SPatrick Mooney 				ptp_release(&cookie);
32710153d828SPatrick Mooney 				*guest_fault = 1;
32720153d828SPatrick Mooney 				return (0);
3273bf21cd93STycho Nightingale 			}
3274bf21cd93STycho Nightingale 			break;
3275bf21cd93STycho Nightingale 		}
3276bf21cd93STycho Nightingale 
3277bf21cd93STycho Nightingale 		ptpphys = pte;
3278bf21cd93STycho Nightingale 	}
3279bf21cd93STycho Nightingale 
3280bf21cd93STycho Nightingale 	/* Set the dirty bit in the page table entry if necessary */
32814c87aefeSPatrick Mooney 	if (!check_only && writable && (pte & PG_M) == 0) {
3282bf21cd93STycho Nightingale 		if (atomic_cmpset_64(&ptpbase[ptpindex], pte, pte | PG_M) == 0)
3283bf21cd93STycho Nightingale 			goto restart;
3284bf21cd93STycho Nightingale 	}
32850153d828SPatrick Mooney 	ptp_release(&cookie);
3286bf21cd93STycho Nightingale 
3287bf21cd93STycho Nightingale 	/* Zero out the lower 'ptpshift' bits and the upper 12 bits */
3288bf21cd93STycho Nightingale 	pte >>= ptpshift; pte <<= (ptpshift + 12); pte >>= 12;
3289bf21cd93STycho Nightingale 	*gpa = pte | (gla & (pgsize - 1));
32900153d828SPatrick Mooney 	return (0);
3291bf21cd93STycho Nightingale }
3292bf21cd93STycho Nightingale 
32934c87aefeSPatrick 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)32944c87aefeSPatrick Mooney vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
32954c87aefeSPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault)
32964c87aefeSPatrick Mooney {
32974c87aefeSPatrick Mooney 
32984c87aefeSPatrick Mooney 	return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault,
32994c87aefeSPatrick Mooney 	    false));
33004c87aefeSPatrick Mooney }
33014c87aefeSPatrick Mooney 
33024c87aefeSPatrick 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)33034c87aefeSPatrick Mooney vm_gla2gpa_nofault(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
33044c87aefeSPatrick Mooney     uint64_t gla, int prot, uint64_t *gpa, int *guest_fault)
33054c87aefeSPatrick Mooney {
33064c87aefeSPatrick Mooney 
33074c87aefeSPatrick Mooney 	return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault,
33084c87aefeSPatrick Mooney 	    true));
33094c87aefeSPatrick Mooney }
33104c87aefeSPatrick Mooney 
3311bf21cd93STycho Nightingale int
vie_fetch_instruction(struct vie * vie,struct vm * vm,int vcpuid,uint64_t rip,int * faultptr)3312e0c0d44eSPatrick Mooney vie_fetch_instruction(struct vie *vie, struct vm *vm, int vcpuid, uint64_t rip,
3313e0c0d44eSPatrick Mooney     int *faultptr)
3314bf21cd93STycho Nightingale {
3315bf21cd93STycho Nightingale 	struct vm_copyinfo copyinfo[2];
3316bf21cd93STycho Nightingale 	int error, prot;
3317bf21cd93STycho Nightingale 
33187db0d193SPatrick Mooney 	if ((vie->status & VIES_INIT) == 0) {
3319e0c0d44eSPatrick Mooney 		return (EINVAL);
3320e0c0d44eSPatrick Mooney 	}
3321bf21cd93STycho Nightingale 
3322bf21cd93STycho Nightingale 	prot = PROT_READ | PROT_EXEC;
3323e0c0d44eSPatrick Mooney 	error = vm_copy_setup(vm, vcpuid, &vie->paging, rip, VIE_INST_SIZE,
3324e0c0d44eSPatrick Mooney 	    prot, copyinfo, nitems(copyinfo), faultptr);
33254c87aefeSPatrick Mooney 	if (error || *faultptr)
33264c87aefeSPatrick Mooney 		return (error);
33274c87aefeSPatrick Mooney 
3328e0c0d44eSPatrick Mooney 	vm_copyin(vm, vcpuid, copyinfo, vie->inst, VIE_INST_SIZE);
33294c87aefeSPatrick Mooney 	vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
3330e0c0d44eSPatrick Mooney 	vie->num_valid = VIE_INST_SIZE;
3331e0c0d44eSPatrick Mooney 	vie->status |= VIES_INST_FETCH;
33324c87aefeSPatrick Mooney 	return (0);
3333bf21cd93STycho Nightingale }
3334bf21cd93STycho Nightingale 
3335bf21cd93STycho Nightingale static int
vie_peek(struct vie * vie,uint8_t * x)3336bf21cd93STycho Nightingale vie_peek(struct vie *vie, uint8_t *x)
3337bf21cd93STycho Nightingale {
3338bf21cd93STycho Nightingale 
3339bf21cd93STycho Nightingale 	if (vie->num_processed < vie->num_valid) {
3340bf21cd93STycho Nightingale 		*x = vie->inst[vie->num_processed];
3341bf21cd93STycho Nightingale 		return (0);
3342bf21cd93STycho Nightingale 	} else
3343bf21cd93STycho Nightingale 		return (-1);
3344bf21cd93STycho Nightingale }
3345bf21cd93STycho Nightingale 
3346bf21cd93STycho Nightingale static void
vie_advance(struct vie * vie)3347bf21cd93STycho Nightingale vie_advance(struct vie *vie)
3348bf21cd93STycho Nightingale {
3349bf21cd93STycho Nightingale 
3350bf21cd93STycho Nightingale 	vie->num_processed++;
3351bf21cd93STycho Nightingale }
3352bf21cd93STycho Nightingale 
3353bf21cd93STycho Nightingale static bool
segment_override(uint8_t x,int * seg)3354bf21cd93STycho Nightingale segment_override(uint8_t x, int *seg)
3355bf21cd93STycho Nightingale {
3356bf21cd93STycho Nightingale 
3357bf21cd93STycho Nightingale 	switch (x) {
3358bf21cd93STycho Nightingale 	case 0x2E:
3359bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_CS;
3360bf21cd93STycho Nightingale 		break;
3361bf21cd93STycho Nightingale 	case 0x36:
3362bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_SS;
3363bf21cd93STycho Nightingale 		break;
3364bf21cd93STycho Nightingale 	case 0x3E:
3365bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_DS;
3366bf21cd93STycho Nightingale 		break;
3367bf21cd93STycho Nightingale 	case 0x26:
3368bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_ES;
3369bf21cd93STycho Nightingale 		break;
3370bf21cd93STycho Nightingale 	case 0x64:
3371bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_FS;
3372bf21cd93STycho Nightingale 		break;
3373bf21cd93STycho Nightingale 	case 0x65:
3374bf21cd93STycho Nightingale 		*seg = VM_REG_GUEST_GS;
3375bf21cd93STycho Nightingale 		break;
3376bf21cd93STycho Nightingale 	default:
3377bf21cd93STycho Nightingale 		return (false);
3378bf21cd93STycho Nightingale 	}
3379bf21cd93STycho Nightingale 	return (true);
3380bf21cd93STycho Nightingale }
3381bf21cd93STycho Nightingale 
3382bf21cd93STycho Nightingale static int
decode_prefixes(struct vie * vie,enum vm_cpu_mode cpu_mode,int cs_d)3383bf21cd93STycho Nightingale decode_prefixes(struct vie *vie, enum vm_cpu_mode cpu_mode, int cs_d)
3384bf21cd93STycho Nightingale {
3385bf21cd93STycho Nightingale 	uint8_t x;
3386bf21cd93STycho Nightingale 
3387bf21cd93STycho Nightingale 	while (1) {
3388bf21cd93STycho Nightingale 		if (vie_peek(vie, &x))
3389bf21cd93STycho Nightingale 			return (-1);
3390bf21cd93STycho Nightingale 
3391bf21cd93STycho Nightingale 		if (x == 0x66)
3392bf21cd93STycho Nightingale 			vie->opsize_override = 1;
3393bf21cd93STycho Nightingale 		else if (x == 0x67)
3394bf21cd93STycho Nightingale 			vie->addrsize_override = 1;
3395bf21cd93STycho Nightingale 		else if (x == 0xF3)
3396bf21cd93STycho Nightingale 			vie->repz_present = 1;
3397bf21cd93STycho Nightingale 		else if (x == 0xF2)
3398bf21cd93STycho Nightingale 			vie->repnz_present = 1;
3399bf21cd93STycho Nightingale 		else if (segment_override(x, &vie->segment_register))
3400bf21cd93STycho Nightingale 			vie->segment_override = 1;
3401bf21cd93STycho Nightingale 		else
3402bf21cd93STycho Nightingale 			break;
3403bf21cd93STycho Nightingale 
3404bf21cd93STycho Nightingale 		vie_advance(vie);
3405bf21cd93STycho Nightingale 	}
3406bf21cd93STycho Nightingale 
3407bf21cd93STycho Nightingale 	/*
3408bf21cd93STycho Nightingale 	 * From section 2.2.1, "REX Prefixes", Intel SDM Vol 2:
3409bf21cd93STycho Nightingale 	 * - Only one REX prefix is allowed per instruction.
3410bf21cd93STycho Nightingale 	 * - The REX prefix must immediately precede the opcode byte or the
3411bf21cd93STycho Nightingale 	 *   escape opcode byte.
3412bf21cd93STycho Nightingale 	 * - If an instruction has a mandatory prefix (0x66, 0xF2 or 0xF3)
3413bf21cd93STycho Nightingale 	 *   the mandatory prefix must come before the REX prefix.
3414bf21cd93STycho Nightingale 	 */
3415bf21cd93STycho Nightingale 	if (cpu_mode == CPU_MODE_64BIT && x >= 0x40 && x <= 0x4F) {
3416bf21cd93STycho Nightingale 		vie->rex_present = 1;
3417bf21cd93STycho Nightingale 		vie->rex_w = x & 0x8 ? 1 : 0;
3418bf21cd93STycho Nightingale 		vie->rex_r = x & 0x4 ? 1 : 0;
3419bf21cd93STycho Nightingale 		vie->rex_x = x & 0x2 ? 1 : 0;
3420bf21cd93STycho Nightingale 		vie->rex_b = x & 0x1 ? 1 : 0;
3421bf21cd93STycho Nightingale 		vie_advance(vie);
3422bf21cd93STycho Nightingale 	}
3423bf21cd93STycho Nightingale 
3424154972afSPatrick Mooney 	/*
3425154972afSPatrick Mooney 	 * § 2.3.5, "The VEX Prefix", SDM Vol 2.
3426154972afSPatrick Mooney 	 */
34272699b94cSPatrick Mooney 	if ((cpu_mode == CPU_MODE_64BIT ||
34282699b94cSPatrick Mooney 	    cpu_mode == CPU_MODE_COMPATIBILITY) && x == 0xC4) {
3429154972afSPatrick Mooney 		const struct vie_op *optab;
3430154972afSPatrick Mooney 
3431154972afSPatrick Mooney 		/* 3-byte VEX prefix. */
3432154972afSPatrick Mooney 		vie->vex_present = 1;
3433154972afSPatrick Mooney 
3434154972afSPatrick Mooney 		vie_advance(vie);
3435154972afSPatrick Mooney 		if (vie_peek(vie, &x))
3436154972afSPatrick Mooney 			return (-1);
3437154972afSPatrick Mooney 
3438154972afSPatrick Mooney 		/*
3439154972afSPatrick Mooney 		 * 2nd byte: [R', X', B', mmmmm[4:0]].  Bits are inverted
3440154972afSPatrick Mooney 		 * relative to REX encoding.
3441154972afSPatrick Mooney 		 */
3442154972afSPatrick Mooney 		vie->rex_r = x & 0x80 ? 0 : 1;
3443154972afSPatrick Mooney 		vie->rex_x = x & 0x40 ? 0 : 1;
3444154972afSPatrick Mooney 		vie->rex_b = x & 0x20 ? 0 : 1;
3445154972afSPatrick Mooney 
3446154972afSPatrick Mooney 		switch (x & 0x1F) {
3447154972afSPatrick Mooney 		case 0x2:
3448154972afSPatrick Mooney 			/* 0F 38. */
3449154972afSPatrick Mooney 			optab = three_byte_opcodes_0f38;
3450154972afSPatrick Mooney 			break;
3451154972afSPatrick Mooney 		case 0x1:
3452154972afSPatrick Mooney 			/* 0F class - nothing handled here yet. */
3453154972afSPatrick Mooney 			/* FALLTHROUGH */
3454154972afSPatrick Mooney 		case 0x3:
3455154972afSPatrick Mooney 			/* 0F 3A class - nothing handled here yet. */
3456154972afSPatrick Mooney 			/* FALLTHROUGH */
3457154972afSPatrick Mooney 		default:
3458154972afSPatrick Mooney 			/* Reserved (#UD). */
3459154972afSPatrick Mooney 			return (-1);
3460154972afSPatrick Mooney 		}
3461154972afSPatrick Mooney 
3462154972afSPatrick Mooney 		vie_advance(vie);
3463154972afSPatrick Mooney 		if (vie_peek(vie, &x))
3464154972afSPatrick Mooney 			return (-1);
3465154972afSPatrick Mooney 
3466154972afSPatrick Mooney 		/* 3rd byte: [W, vvvv[6:3], L, pp[1:0]]. */
3467154972afSPatrick Mooney 		vie->rex_w = x & 0x80 ? 1 : 0;
3468154972afSPatrick Mooney 
3469154972afSPatrick Mooney 		vie->vex_reg = ((~(unsigned)x & 0x78u) >> 3);
3470154972afSPatrick Mooney 		vie->vex_l = !!(x & 0x4);
3471154972afSPatrick Mooney 		vie->vex_pp = (x & 0x3);
3472154972afSPatrick Mooney 
3473154972afSPatrick Mooney 		/* PP: 1=66 2=F3 3=F2 prefixes. */
3474154972afSPatrick Mooney 		switch (vie->vex_pp) {
3475154972afSPatrick Mooney 		case 0x1:
3476154972afSPatrick Mooney 			vie->opsize_override = 1;
3477154972afSPatrick Mooney 			break;
3478154972afSPatrick Mooney 		case 0x2:
3479154972afSPatrick Mooney 			vie->repz_present = 1;
3480154972afSPatrick Mooney 			break;
3481154972afSPatrick Mooney 		case 0x3:
3482154972afSPatrick Mooney 			vie->repnz_present = 1;
3483154972afSPatrick Mooney 			break;
3484154972afSPatrick Mooney 		}
3485154972afSPatrick Mooney 
3486154972afSPatrick Mooney 		vie_advance(vie);
3487154972afSPatrick Mooney 
3488154972afSPatrick Mooney 		/* Opcode, sans literal prefix prefix. */
3489154972afSPatrick Mooney 		if (vie_peek(vie, &x))
3490154972afSPatrick Mooney 			return (-1);
3491154972afSPatrick Mooney 
3492154972afSPatrick Mooney 		vie->op = optab[x];
3493154972afSPatrick Mooney 		if (vie->op.op_type == VIE_OP_TYPE_NONE)
3494154972afSPatrick Mooney 			return (-1);
3495154972afSPatrick Mooney 
3496154972afSPatrick Mooney 		vie_advance(vie);
3497154972afSPatrick Mooney 	}
3498154972afSPatrick Mooney 
3499bf21cd93STycho Nightingale 	/*
3500bf21cd93STycho Nightingale 	 * Section "Operand-Size And Address-Size Attributes", Intel SDM, Vol 1
3501bf21cd93STycho Nightingale 	 */
3502bf21cd93STycho Nightingale 	if (cpu_mode == CPU_MODE_64BIT) {
3503bf21cd93STycho Nightingale 		/*
3504bf21cd93STycho Nightingale 		 * Default address size is 64-bits and default operand size
3505bf21cd93STycho Nightingale 		 * is 32-bits.
3506bf21cd93STycho Nightingale 		 */
3507bf21cd93STycho Nightingale 		vie->addrsize = vie->addrsize_override ? 4 : 8;
3508bf21cd93STycho Nightingale 		if (vie->rex_w)
3509bf21cd93STycho Nightingale 			vie->opsize = 8;
3510bf21cd93STycho Nightingale 		else if (vie->opsize_override)
3511bf21cd93STycho Nightingale 			vie->opsize = 2;
3512bf21cd93STycho Nightingale 		else
3513bf21cd93STycho Nightingale 			vie->opsize = 4;
3514bf21cd93STycho Nightingale 	} else if (cs_d) {
3515bf21cd93STycho Nightingale 		/* Default address and operand sizes are 32-bits */
3516bf21cd93STycho Nightingale 		vie->addrsize = vie->addrsize_override ? 2 : 4;
3517bf21cd93STycho Nightingale 		vie->opsize = vie->opsize_override ? 2 : 4;
3518bf21cd93STycho Nightingale 	} else {
3519bf21cd93STycho Nightingale 		/* Default address and operand sizes are 16-bits */
3520bf21cd93STycho Nightingale 		vie->addrsize = vie->addrsize_override ? 4 : 2;
3521bf21cd93STycho Nightingale 		vie->opsize = vie->opsize_override ? 4 : 2;
3522bf21cd93STycho Nightingale 	}
3523bf21cd93STycho Nightingale 	return (0);
3524bf21cd93STycho Nightingale }
3525bf21cd93STycho Nightingale 
3526bf21cd93STycho Nightingale static int
decode_two_byte_opcode(struct vie * vie)3527bf21cd93STycho Nightingale decode_two_byte_opcode(struct vie *vie)
3528bf21cd93STycho Nightingale {
3529bf21cd93STycho Nightingale 	uint8_t x;
3530bf21cd93STycho Nightingale 
3531bf21cd93STycho Nightingale 	if (vie_peek(vie, &x))
3532bf21cd93STycho Nightingale 		return (-1);
3533bf21cd93STycho Nightingale 
3534bf21cd93STycho Nightingale 	vie->op = two_byte_opcodes[x];
3535bf21cd93STycho Nightingale 
3536bf21cd93STycho Nightingale 	if (vie->op.op_type == VIE_OP_TYPE_NONE)
3537bf21cd93STycho Nightingale 		return (-1);
3538bf21cd93STycho Nightingale 
3539bf21cd93STycho Nightingale 	vie_advance(vie);
3540bf21cd93STycho Nightingale 	return (0);
3541bf21cd93STycho Nightingale }
3542bf21cd93STycho Nightingale 
3543bf21cd93STycho Nightingale static int
decode_opcode(struct vie * vie)3544bf21cd93STycho Nightingale decode_opcode(struct vie *vie)
3545bf21cd93STycho Nightingale {
3546bf21cd93STycho Nightingale 	uint8_t x;
3547bf21cd93STycho Nightingale 
3548bf21cd93STycho Nightingale 	if (vie_peek(vie, &x))
3549bf21cd93STycho Nightingale 		return (-1);
3550bf21cd93STycho Nightingale 
3551154972afSPatrick Mooney 	/* Already did this via VEX prefix. */
3552154972afSPatrick Mooney 	if (vie->op.op_type != VIE_OP_TYPE_NONE)
3553154972afSPatrick Mooney 		return (0);
3554154972afSPatrick Mooney 
3555bf21cd93STycho Nightingale 	vie->op = one_byte_opcodes[x];
3556bf21cd93STycho Nightingale 
3557bf21cd93STycho Nightingale 	if (vie->op.op_type == VIE_OP_TYPE_NONE)
3558bf21cd93STycho Nightingale 		return (-1);
3559bf21cd93STycho Nightingale 
3560bf21cd93STycho Nightingale 	vie_advance(vie);
3561bf21cd93STycho Nightingale 
3562bf21cd93STycho Nightingale 	if (vie->op.op_type == VIE_OP_TYPE_TWO_BYTE)
3563bf21cd93STycho Nightingale 		return (decode_two_byte_opcode(vie));
3564bf21cd93STycho Nightingale 
3565bf21cd93STycho Nightingale 	return (0);
3566bf21cd93STycho Nightingale }
3567bf21cd93STycho Nightingale 
3568bf21cd93STycho Nightingale static int
decode_modrm(struct vie * vie,enum vm_cpu_mode cpu_mode)3569bf21cd93STycho Nightingale decode_modrm(struct vie *vie, enum vm_cpu_mode cpu_mode)
3570bf21cd93STycho Nightingale {
3571bf21cd93STycho Nightingale 	uint8_t x;
3572418ddc26SPatrick Mooney 	/*
3573418ddc26SPatrick Mooney 	 * Handling mov-to/from-cr is special since it is not issuing
3574418ddc26SPatrick Mooney 	 * mmio/pio requests and can be done in real mode.  We must bypass some
3575418ddc26SPatrick Mooney 	 * of the other existing decoding restrictions for it.
3576418ddc26SPatrick Mooney 	 */
3577418ddc26SPatrick Mooney 	const bool is_movcr = ((vie->op.op_flags & VIE_OP_F_REG_REG) != 0);
3578bf21cd93STycho Nightingale 
3579bf21cd93STycho Nightingale 	if (vie->op.op_flags & VIE_OP_F_NO_MODRM)
3580bf21cd93STycho Nightingale 		return (0);
3581bf21cd93STycho Nightingale 
3582418ddc26SPatrick Mooney 	if (cpu_mode == CPU_MODE_REAL && !is_movcr)
3583bf21cd93STycho Nightingale 		return (-1);
3584bf21cd93STycho Nightingale 
3585bf21cd93STycho Nightingale 	if (vie_peek(vie, &x))
3586bf21cd93STycho Nightingale 		return (-1);
3587bf21cd93STycho Nightingale 
3588bf21cd93STycho Nightingale 	vie->mod = (x >> 6) & 0x3;
3589bf21cd93STycho Nightingale 	vie->rm =  (x >> 0) & 0x7;
3590bf21cd93STycho Nightingale 	vie->reg = (x >> 3) & 0x7;
3591bf21cd93STycho Nightingale 
3592bf21cd93STycho Nightingale 	/*
3593bf21cd93STycho Nightingale 	 * A direct addressing mode makes no sense in the context of an EPT
3594bf21cd93STycho Nightingale 	 * fault. There has to be a memory access involved to cause the
3595bf21cd93STycho Nightingale 	 * EPT fault.
3596bf21cd93STycho Nightingale 	 */
3597418ddc26SPatrick Mooney 	if (vie->mod == VIE_MOD_DIRECT && !is_movcr)
3598bf21cd93STycho Nightingale 		return (-1);
3599bf21cd93STycho Nightingale 
3600bf21cd93STycho Nightingale 	if ((vie->mod == VIE_MOD_INDIRECT && vie->rm == VIE_RM_DISP32) ||
3601bf21cd93STycho Nightingale 	    (vie->mod != VIE_MOD_DIRECT && vie->rm == VIE_RM_SIB)) {
3602bf21cd93STycho Nightingale 		/*
3603bf21cd93STycho Nightingale 		 * Table 2-5: Special Cases of REX Encodings
3604bf21cd93STycho Nightingale 		 *
3605bf21cd93STycho Nightingale 		 * mod=0, r/m=5 is used in the compatibility mode to
3606bf21cd93STycho Nightingale 		 * indicate a disp32 without a base register.
3607bf21cd93STycho Nightingale 		 *
3608bf21cd93STycho Nightingale 		 * mod!=3, r/m=4 is used in the compatibility mode to
3609bf21cd93STycho Nightingale 		 * indicate that the SIB byte is present.
3610bf21cd93STycho Nightingale 		 *
3611bf21cd93STycho Nightingale 		 * The 'b' bit in the REX prefix is don't care in
3612bf21cd93STycho Nightingale 		 * this case.
3613bf21cd93STycho Nightingale 		 */
3614bf21cd93STycho Nightingale 	} else {
3615bf21cd93STycho Nightingale 		vie->rm |= (vie->rex_b << 3);
3616bf21cd93STycho Nightingale 	}
3617bf21cd93STycho Nightingale 
3618bf21cd93STycho Nightingale 	vie->reg |= (vie->rex_r << 3);
3619bf21cd93STycho Nightingale 
3620bf21cd93STycho Nightingale 	/* SIB */
3621bf21cd93STycho Nightingale 	if (vie->mod != VIE_MOD_DIRECT && vie->rm == VIE_RM_SIB)
3622bf21cd93STycho Nightingale 		goto done;
3623bf21cd93STycho Nightingale 
3624bf21cd93STycho Nightingale 	vie->base_register = gpr_map[vie->rm];
3625bf21cd93STycho Nightingale 
3626bf21cd93STycho Nightingale 	switch (vie->mod) {
3627bf21cd93STycho Nightingale 	case VIE_MOD_INDIRECT_DISP8:
3628bf21cd93STycho Nightingale 		vie->disp_bytes = 1;
3629bf21cd93STycho Nightingale 		break;
3630bf21cd93STycho Nightingale 	case VIE_MOD_INDIRECT_DISP32:
3631bf21cd93STycho Nightingale 		vie->disp_bytes = 4;
3632bf21cd93STycho Nightingale 		break;
3633bf21cd93STycho Nightingale 	case VIE_MOD_INDIRECT:
3634bf21cd93STycho Nightingale 		if (vie->rm == VIE_RM_DISP32) {
3635bf21cd93STycho Nightingale 			vie->disp_bytes = 4;
3636bf21cd93STycho Nightingale 			/*
3637bf21cd93STycho Nightingale 			 * Table 2-7. RIP-Relative Addressing
3638bf21cd93STycho Nightingale 			 *
3639bf21cd93STycho Nightingale 			 * In 64-bit mode mod=00 r/m=101 implies [rip] + disp32
3640bf21cd93STycho Nightingale 			 * whereas in compatibility mode it just implies disp32.
3641bf21cd93STycho Nightingale 			 */
3642bf21cd93STycho Nightingale 
3643bf21cd93STycho Nightingale 			if (cpu_mode == CPU_MODE_64BIT)
3644bf21cd93STycho Nightingale 				vie->base_register = VM_REG_GUEST_RIP;
3645bf21cd93STycho Nightingale 			else
3646bf21cd93STycho Nightingale 				vie->base_register = VM_REG_LAST;
3647bf21cd93STycho Nightingale 		}
3648bf21cd93STycho Nightingale 		break;
3649bf21cd93STycho Nightingale 	}
3650bf21cd93STycho Nightingale 
3651bf21cd93STycho Nightingale done:
3652bf21cd93STycho Nightingale 	vie_advance(vie);
3653bf21cd93STycho Nightingale 
3654bf21cd93STycho Nightingale 	return (0);
3655bf21cd93STycho Nightingale }
3656bf21cd93STycho Nightingale 
3657bf21cd93STycho Nightingale static int
decode_sib(struct vie * vie)3658bf21cd93STycho Nightingale decode_sib(struct vie *vie)
3659bf21cd93STycho Nightingale {
3660bf21cd93STycho Nightingale 	uint8_t x;
3661bf21cd93STycho Nightingale 
3662bf21cd93STycho Nightingale 	/* Proceed only if SIB byte is present */
3663bf21cd93STycho Nightingale 	if (vie->mod == VIE_MOD_DIRECT || vie->rm != VIE_RM_SIB)
3664bf21cd93STycho Nightingale 		return (0);
3665bf21cd93STycho Nightingale 
3666bf21cd93STycho Nightingale 	if (vie_peek(vie, &x))
3667bf21cd93STycho Nightingale 		return (-1);
3668bf21cd93STycho Nightingale 
3669bf21cd93STycho Nightingale 	/* De-construct the SIB byte */
3670bf21cd93STycho Nightingale 	vie->ss = (x >> 6) & 0x3;
3671bf21cd93STycho Nightingale 	vie->index = (x >> 3) & 0x7;
3672bf21cd93STycho Nightingale 	vie->base = (x >> 0) & 0x7;
3673bf21cd93STycho Nightingale 
3674bf21cd93STycho Nightingale 	/* Apply the REX prefix modifiers */
3675bf21cd93STycho Nightingale 	vie->index |= vie->rex_x << 3;
3676bf21cd93STycho Nightingale 	vie->base |= vie->rex_b << 3;
3677bf21cd93STycho Nightingale 
3678bf21cd93STycho Nightingale 	switch (vie->mod) {
3679bf21cd93STycho Nightingale 	case VIE_MOD_INDIRECT_DISP8:
3680bf21cd93STycho Nightingale 		vie->disp_bytes = 1;
3681bf21cd93STycho Nightingale 		break;
3682bf21cd93STycho Nightingale 	case VIE_MOD_INDIRECT_DISP32:
3683bf21cd93STycho Nightingale 		vie->disp_bytes = 4;
3684bf21cd93STycho Nightingale 		break;
3685bf21cd93STycho Nightingale 	}
3686bf21cd93STycho Nightingale 
3687bf21cd93STycho Nightingale 	if (vie->mod == VIE_MOD_INDIRECT &&
3688bf21cd93STycho Nightingale 	    (vie->base == 5 || vie->base == 13)) {
3689bf21cd93STycho Nightingale 		/*
3690bf21cd93STycho Nightingale 		 * Special case when base register is unused if mod = 0
3691bf21cd93STycho Nightingale 		 * and base = %rbp or %r13.
3692bf21cd93STycho Nightingale 		 *
3693bf21cd93STycho Nightingale 		 * Documented in:
3694bf21cd93STycho Nightingale 		 * Table 2-3: 32-bit Addressing Forms with the SIB Byte
3695bf21cd93STycho Nightingale 		 * Table 2-5: Special Cases of REX Encodings
3696bf21cd93STycho Nightingale 		 */
3697bf21cd93STycho Nightingale 		vie->disp_bytes = 4;
3698bf21cd93STycho Nightingale 	} else {
3699bf21cd93STycho Nightingale 		vie->base_register = gpr_map[vie->base];
3700bf21cd93STycho Nightingale 	}
3701bf21cd93STycho Nightingale 
3702bf21cd93STycho Nightingale 	/*
3703bf21cd93STycho Nightingale 	 * All encodings of 'index' are valid except for %rsp (4).
3704bf21cd93STycho Nightingale 	 *
3705bf21cd93STycho Nightingale 	 * Documented in:
3706bf21cd93STycho Nightingale 	 * Table 2-3: 32-bit Addressing Forms with the SIB Byte
3707bf21cd93STycho Nightingale 	 * Table 2-5: Special Cases of REX Encodings
3708bf21cd93STycho Nightingale 	 */
3709bf21cd93STycho Nightingale 	if (vie->index != 4)
3710bf21cd93STycho Nightingale 		vie->index_register = gpr_map[vie->index];
3711bf21cd93STycho Nightingale 
3712bf21cd93STycho Nightingale 	/* 'scale' makes sense only in the context of an index register */
3713bf21cd93STycho Nightingale 	if (vie->index_register < VM_REG_LAST)
3714bf21cd93STycho Nightingale 		vie->scale = 1 << vie->ss;
3715bf21cd93STycho Nightingale 
3716bf21cd93STycho Nightingale 	vie_advance(vie);
3717bf21cd93STycho Nightingale 
3718bf21cd93STycho Nightingale 	return (0);
3719bf21cd93STycho Nightingale }
3720bf21cd93STycho Nightingale 
3721bf21cd93STycho Nightingale static int
decode_displacement(struct vie * vie)3722bf21cd93STycho Nightingale decode_displacement(struct vie *vie)
3723bf21cd93STycho Nightingale {
3724bf21cd93STycho Nightingale 	int n, i;
3725bf21cd93STycho Nightingale 	uint8_t x;
3726bf21cd93STycho Nightingale 
3727bf21cd93STycho Nightingale 	union {
3728bf21cd93STycho Nightingale 		char	buf[4];
3729bf21cd93STycho Nightingale 		int8_t	signed8;
3730bf21cd93STycho Nightingale 		int32_t	signed32;
3731bf21cd93STycho Nightingale 	} u;
3732bf21cd93STycho Nightingale 
3733bf21cd93STycho Nightingale 	if ((n = vie->disp_bytes) == 0)
3734bf21cd93STycho Nightingale 		return (0);
3735bf21cd93STycho Nightingale 
3736bf21cd93STycho Nightingale 	if (n != 1 && n != 4)
3737bf21cd93STycho Nightingale 		panic("decode_displacement: invalid disp_bytes %d", n);
3738bf21cd93STycho Nightingale 
3739bf21cd93STycho Nightingale 	for (i = 0; i < n; i++) {
3740bf21cd93STycho Nightingale 		if (vie_peek(vie, &x))
3741bf21cd93STycho Nightingale 			return (-1);
3742bf21cd93STycho Nightingale 
3743bf21cd93STycho Nightingale 		u.buf[i] = x;
3744bf21cd93STycho Nightingale 		vie_advance(vie);
3745bf21cd93STycho Nightingale 	}
3746bf21cd93STycho Nightingale 
3747bf21cd93STycho Nightingale 	if (n == 1)
3748bf21cd93STycho Nightingale 		vie->displacement = u.signed8;		/* sign-extended */
3749bf21cd93STycho Nightingale 	else
3750bf21cd93STycho Nightingale 		vie->displacement = u.signed32;		/* sign-extended */
3751bf21cd93STycho Nightingale 
3752bf21cd93STycho Nightingale 	return (0);
3753bf21cd93STycho Nightingale }
3754bf21cd93STycho Nightingale 
3755bf21cd93STycho Nightingale static int
decode_immediate(struct vie * vie)3756bf21cd93STycho Nightingale decode_immediate(struct vie *vie)
3757bf21cd93STycho Nightingale {
3758bf21cd93STycho Nightingale 	int i, n;
3759bf21cd93STycho Nightingale 	uint8_t x;
3760bf21cd93STycho Nightingale 	union {
3761bf21cd93STycho Nightingale 		char	buf[4];
3762bf21cd93STycho Nightingale 		int8_t	signed8;
3763bf21cd93STycho Nightingale 		int16_t	signed16;
3764bf21cd93STycho Nightingale 		int32_t	signed32;
3765bf21cd93STycho Nightingale 	} u;
3766bf21cd93STycho Nightingale 
3767bf21cd93STycho Nightingale 	/* Figure out immediate operand size (if any) */
3768bf21cd93STycho Nightingale 	if (vie->op.op_flags & VIE_OP_F_IMM) {
3769bf21cd93STycho Nightingale 		/*
3770bf21cd93STycho Nightingale 		 * Section 2.2.1.5 "Immediates", Intel SDM:
3771bf21cd93STycho Nightingale 		 * In 64-bit mode the typical size of immediate operands
3772bf21cd93STycho Nightingale 		 * remains 32-bits. When the operand size if 64-bits, the
3773bf21cd93STycho Nightingale 		 * processor sign-extends all immediates to 64-bits prior
3774bf21cd93STycho Nightingale 		 * to their use.
3775bf21cd93STycho Nightingale 		 */
3776bf21cd93STycho Nightingale 		if (vie->opsize == 4 || vie->opsize == 8)
3777bf21cd93STycho Nightingale 			vie->imm_bytes = 4;
3778bf21cd93STycho Nightingale 		else
3779bf21cd93STycho Nightingale 			vie->imm_bytes = 2;
3780bf21cd93STycho Nightingale 	} else if (vie->op.op_flags & VIE_OP_F_IMM8) {
3781bf21cd93STycho Nightingale 		vie->imm_bytes = 1;
3782bf21cd93STycho Nightingale 	}
3783bf21cd93STycho Nightingale 
3784bf21cd93STycho Nightingale 	if ((n = vie->imm_bytes) == 0)
3785bf21cd93STycho Nightingale 		return (0);
3786bf21cd93STycho Nightingale 
3787bf21cd93STycho Nightingale 	KASSERT(n == 1 || n == 2 || n == 4,
3788bf21cd93STycho Nightingale 	    ("%s: invalid number of immediate bytes: %d", __func__, n));
3789bf21cd93STycho Nightingale 
3790bf21cd93STycho Nightingale 	for (i = 0; i < n; i++) {
3791bf21cd93STycho Nightingale 		if (vie_peek(vie, &x))
3792bf21cd93STycho Nightingale 			return (-1);
3793bf21cd93STycho Nightingale 
3794bf21cd93STycho Nightingale 		u.buf[i] = x;
3795bf21cd93STycho Nightingale 		vie_advance(vie);
3796bf21cd93STycho Nightingale 	}
3797bf21cd93STycho Nightingale 
3798bf21cd93STycho Nightingale 	/* sign-extend the immediate value before use */
3799bf21cd93STycho Nightingale 	if (n == 1)
3800bf21cd93STycho Nightingale 		vie->immediate = u.signed8;
3801bf21cd93STycho Nightingale 	else if (n == 2)
3802bf21cd93STycho Nightingale 		vie->immediate = u.signed16;
3803bf21cd93STycho Nightingale 	else
3804bf21cd93STycho Nightingale 		vie->immediate = u.signed32;
3805bf21cd93STycho Nightingale 
3806bf21cd93STycho Nightingale 	return (0);
3807bf21cd93STycho Nightingale }
3808bf21cd93STycho Nightingale 
3809bf21cd93STycho Nightingale static int
decode_moffset(struct vie * vie)3810bf21cd93STycho Nightingale decode_moffset(struct vie *vie)
3811bf21cd93STycho Nightingale {
3812bf21cd93STycho Nightingale 	int i, n;
3813bf21cd93STycho Nightingale 	uint8_t x;
3814bf21cd93STycho Nightingale 	union {
3815bf21cd93STycho Nightingale 		char	buf[8];
3816bf21cd93STycho Nightingale 		uint64_t u64;
3817bf21cd93STycho Nightingale 	} u;
3818bf21cd93STycho Nightingale 
3819bf21cd93STycho Nightingale 	if ((vie->op.op_flags & VIE_OP_F_MOFFSET) == 0)
3820bf21cd93STycho Nightingale 		return (0);
3821bf21cd93STycho Nightingale 
3822bf21cd93STycho Nightingale 	/*
3823bf21cd93STycho Nightingale 	 * Section 2.2.1.4, "Direct Memory-Offset MOVs", Intel SDM:
3824bf21cd93STycho Nightingale 	 * The memory offset size follows the address-size of the instruction.
3825bf21cd93STycho Nightingale 	 */
3826bf21cd93STycho Nightingale 	n = vie->addrsize;
3827bf21cd93STycho Nightingale 	KASSERT(n == 2 || n == 4 || n == 8, ("invalid moffset bytes: %d", n));
3828bf21cd93STycho Nightingale 
3829bf21cd93STycho Nightingale 	u.u64 = 0;
3830bf21cd93STycho Nightingale 	for (i = 0; i < n; i++) {
3831bf21cd93STycho Nightingale 		if (vie_peek(vie, &x))
3832bf21cd93STycho Nightingale 			return (-1);
3833bf21cd93STycho Nightingale 
3834bf21cd93STycho Nightingale 		u.buf[i] = x;
3835bf21cd93STycho Nightingale 		vie_advance(vie);
3836bf21cd93STycho Nightingale 	}
3837bf21cd93STycho Nightingale 	vie->displacement = u.u64;
3838bf21cd93STycho Nightingale 	return (0);
3839bf21cd93STycho Nightingale }
3840bf21cd93STycho Nightingale 
3841bf21cd93STycho Nightingale /*
3842bf21cd93STycho Nightingale  * Verify that the 'guest linear address' provided as collateral of the nested
3843bf21cd93STycho Nightingale  * page table fault matches with our instruction decoding.
3844bf21cd93STycho Nightingale  */
3845e0c0d44eSPatrick Mooney int
vie_verify_gla(struct vie * vie,struct vm * vm,int cpuid,uint64_t gla)3846e0c0d44eSPatrick Mooney vie_verify_gla(struct vie *vie, struct vm *vm, int cpuid, uint64_t gla)
3847bf21cd93STycho Nightingale {
3848bf21cd93STycho Nightingale 	int error;
38494c87aefeSPatrick Mooney 	uint64_t base, segbase, idx, gla2;
38504c87aefeSPatrick Mooney 	enum vm_reg_name seg;
38514c87aefeSPatrick Mooney 	struct seg_desc desc;
3852bf21cd93STycho Nightingale 
3853e0c0d44eSPatrick Mooney 	ASSERT((vie->status & VIES_INST_DECODE) != 0);
3854e0c0d44eSPatrick Mooney 
3855e0c0d44eSPatrick Mooney 	/*
3856e0c0d44eSPatrick Mooney 	 * If there was no valid GLA context with the exit, or the decoded
3857e0c0d44eSPatrick Mooney 	 * instruction acts on more than one address, verification is done.
3858e0c0d44eSPatrick Mooney 	 */
3859e0c0d44eSPatrick Mooney 	if (gla == VIE_INVALID_GLA ||
3860e0c0d44eSPatrick Mooney 	    (vie->op.op_flags & VIE_OP_F_NO_GLA_VERIFICATION) != 0) {
3861bf21cd93STycho Nightingale 		return (0);
3862e0c0d44eSPatrick Mooney 	}
3863bf21cd93STycho Nightingale 
3864bf21cd93STycho Nightingale 	base = 0;
3865bf21cd93STycho Nightingale 	if (vie->base_register != VM_REG_LAST) {
3866bf21cd93STycho Nightingale 		error = vm_get_register(vm, cpuid, vie->base_register, &base);
3867bf21cd93STycho Nightingale 		if (error) {
3868bf21cd93STycho Nightingale 			printf("verify_gla: error %d getting base reg %d\n",
38692699b94cSPatrick Mooney 			    error, vie->base_register);
3870bf21cd93STycho Nightingale 			return (-1);
3871bf21cd93STycho Nightingale 		}
3872bf21cd93STycho Nightingale 
3873bf21cd93STycho Nightingale 		/*
3874bf21cd93STycho Nightingale 		 * RIP-relative addressing starts from the following
3875bf21cd93STycho Nightingale 		 * instruction
3876bf21cd93STycho Nightingale 		 */
3877bf21cd93STycho Nightingale 		if (vie->base_register == VM_REG_GUEST_RIP)
38784c87aefeSPatrick Mooney 			base += vie->num_processed;
3879bf21cd93STycho Nightingale 	}
3880bf21cd93STycho Nightingale 
3881bf21cd93STycho Nightingale 	idx = 0;
3882bf21cd93STycho Nightingale 	if (vie->index_register != VM_REG_LAST) {
3883bf21cd93STycho Nightingale 		error = vm_get_register(vm, cpuid, vie->index_register, &idx);
3884bf21cd93STycho Nightingale 		if (error) {
3885bf21cd93STycho Nightingale 			printf("verify_gla: error %d getting index reg %d\n",
38862699b94cSPatrick Mooney 			    error, vie->index_register);
3887bf21cd93STycho Nightingale 			return (-1);
3888bf21cd93STycho Nightingale 		}
3889bf21cd93STycho Nightingale 	}
3890bf21cd93STycho Nightingale 
38914c87aefeSPatrick Mooney 	/*
38924c87aefeSPatrick Mooney 	 * From "Specifying a Segment Selector", Intel SDM, Vol 1
38934c87aefeSPatrick Mooney 	 *
38944c87aefeSPatrick Mooney 	 * In 64-bit mode, segmentation is generally (but not
38954c87aefeSPatrick Mooney 	 * completely) disabled.  The exceptions are the FS and GS
38964c87aefeSPatrick Mooney 	 * segments.
38974c87aefeSPatrick Mooney 	 *
38984c87aefeSPatrick Mooney 	 * In legacy IA-32 mode, when the ESP or EBP register is used
38994c87aefeSPatrick Mooney 	 * as the base, the SS segment is the default segment.  For
39004c87aefeSPatrick Mooney 	 * other data references, except when relative to stack or
39014c87aefeSPatrick Mooney 	 * string destination the DS segment is the default.  These
39024c87aefeSPatrick Mooney 	 * can be overridden to allow other segments to be accessed.
39034c87aefeSPatrick Mooney 	 */
3904e0c0d44eSPatrick Mooney 	if (vie->segment_override) {
39054c87aefeSPatrick Mooney 		seg = vie->segment_register;
3906e0c0d44eSPatrick Mooney 	} else if (vie->base_register == VM_REG_GUEST_RSP ||
3907e0c0d44eSPatrick Mooney 	    vie->base_register == VM_REG_GUEST_RBP) {
39084c87aefeSPatrick Mooney 		seg = VM_REG_GUEST_SS;
3909e0c0d44eSPatrick Mooney 	} else {
39104c87aefeSPatrick Mooney 		seg = VM_REG_GUEST_DS;
3911e0c0d44eSPatrick Mooney 	}
3912e0c0d44eSPatrick Mooney 	if (vie->paging.cpu_mode == CPU_MODE_64BIT &&
3913e0c0d44eSPatrick Mooney 	    seg != VM_REG_GUEST_FS && seg != VM_REG_GUEST_GS) {
39144c87aefeSPatrick Mooney 		segbase = 0;
39154c87aefeSPatrick Mooney 	} else {
39164c87aefeSPatrick Mooney 		error = vm_get_seg_desc(vm, cpuid, seg, &desc);
39174c87aefeSPatrick Mooney 		if (error) {
39184c87aefeSPatrick Mooney 			printf("verify_gla: error %d getting segment"
39192699b94cSPatrick Mooney 			    " descriptor %d", error, vie->segment_register);
39204c87aefeSPatrick Mooney 			return (-1);
39214c87aefeSPatrick Mooney 		}
39224c87aefeSPatrick Mooney 		segbase = desc.base;
39234c87aefeSPatrick Mooney 	}
39244c87aefeSPatrick Mooney 
39254c87aefeSPatrick Mooney 	gla2 = segbase + base + vie->scale * idx + vie->displacement;
3926bf21cd93STycho Nightingale 	gla2 &= size2mask[vie->addrsize];
3927bf21cd93STycho Nightingale 	if (gla != gla2) {
39284c87aefeSPatrick Mooney 		printf("verify_gla mismatch: segbase(0x%0lx)"
39292699b94cSPatrick Mooney 		    "base(0x%0lx), scale(%d), index(0x%0lx), "
39302699b94cSPatrick Mooney 		    "disp(0x%0lx), gla(0x%0lx), gla2(0x%0lx)\n",
39312699b94cSPatrick Mooney 		    segbase, base, vie->scale, idx, vie->displacement,
39322699b94cSPatrick Mooney 		    gla, gla2);
3933bf21cd93STycho Nightingale 		return (-1);
3934bf21cd93STycho Nightingale 	}
3935bf21cd93STycho Nightingale 
3936bf21cd93STycho Nightingale 	return (0);
3937bf21cd93STycho Nightingale }
3938bf21cd93STycho Nightingale 
3939bf21cd93STycho Nightingale int
vie_decode_instruction(struct vie * vie,struct vm * vm,int cpuid,int cs_d)3940e0c0d44eSPatrick Mooney vie_decode_instruction(struct vie *vie, struct vm *vm, int cpuid, int cs_d)
3941bf21cd93STycho Nightingale {
3942e0c0d44eSPatrick Mooney 	enum vm_cpu_mode cpu_mode;
3943e0c0d44eSPatrick Mooney 
3944e0c0d44eSPatrick Mooney 	if ((vie->status & VIES_INST_FETCH) == 0) {
3945e0c0d44eSPatrick Mooney 		return (EINVAL);
3946e0c0d44eSPatrick Mooney 	}
3947e0c0d44eSPatrick Mooney 
3948e0c0d44eSPatrick Mooney 	cpu_mode = vie->paging.cpu_mode;
3949bf21cd93STycho Nightingale 
3950bf21cd93STycho Nightingale 	if (decode_prefixes(vie, cpu_mode, cs_d))
3951bf21cd93STycho Nightingale 		return (-1);
3952bf21cd93STycho Nightingale 
3953bf21cd93STycho Nightingale 	if (decode_opcode(vie))
3954bf21cd93STycho Nightingale 		return (-1);
3955bf21cd93STycho Nightingale 
3956bf21cd93STycho Nightingale 	if (decode_modrm(vie, cpu_mode))
3957bf21cd93STycho Nightingale 		return (-1);
3958bf21cd93STycho Nightingale 
3959bf21cd93STycho Nightingale 	if (decode_sib(vie))
3960bf21cd93STycho Nightingale 		return (-1);
3961bf21cd93STycho Nightingale 
3962bf21cd93STycho Nightingale 	if (decode_displacement(vie))
3963bf21cd93STycho Nightingale 		return (-1);
3964bf21cd93STycho Nightingale 
3965bf21cd93STycho Nightingale 	if (decode_immediate(vie))
3966bf21cd93STycho Nightingale 		return (-1);
3967bf21cd93STycho Nightingale 
3968bf21cd93STycho Nightingale 	if (decode_moffset(vie))
3969bf21cd93STycho Nightingale 		return (-1);
3970bf21cd93STycho Nightingale 
3971e0c0d44eSPatrick Mooney 	vie->status |= VIES_INST_DECODE;
3972bf21cd93STycho Nightingale 
3973bf21cd93STycho Nightingale 	return (0);
3974bf21cd93STycho Nightingale }
3975