xref: /openbmc/linux/tools/arch/x86/lib/insn.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1d046b725SJosh Poimboeuf // SPDX-License-Identifier: GPL-2.0-or-later
2d046b725SJosh Poimboeuf /*
3d046b725SJosh Poimboeuf  * x86 instruction analysis
4d046b725SJosh Poimboeuf  *
5d046b725SJosh Poimboeuf  * Copyright (C) IBM Corporation, 2002, 2004, 2009
6d046b725SJosh Poimboeuf  */
7d046b725SJosh Poimboeuf 
81d509f2aSMartin Schwidefsky #include <linux/kernel.h>
9d046b725SJosh Poimboeuf #ifdef __KERNEL__
10d046b725SJosh Poimboeuf #include <linux/string.h>
11d046b725SJosh Poimboeuf #else
12d046b725SJosh Poimboeuf #include <string.h>
13d046b725SJosh Poimboeuf #endif
140705ef64SBorislav Petkov #include "../include/asm/inat.h" /* __ignore_sync_check__ */
150705ef64SBorislav Petkov #include "../include/asm/insn.h" /* __ignore_sync_check__ */
16*f96b4675SBorislav Petkov #include "../include/asm-generic/unaligned.h" /* __ignore_sync_check__ */
17d046b725SJosh Poimboeuf 
1893281c4aSBorislav Petkov #include <linux/errno.h>
1993281c4aSBorislav Petkov #include <linux/kconfig.h>
2093281c4aSBorislav Petkov 
210705ef64SBorislav Petkov #include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */
224d65adfcSMasami Hiramatsu 
231d509f2aSMartin Schwidefsky #define leXX_to_cpu(t, r)						\
241d509f2aSMartin Schwidefsky ({									\
251d509f2aSMartin Schwidefsky 	__typeof__(t) v;						\
261d509f2aSMartin Schwidefsky 	switch (sizeof(t)) {						\
271d509f2aSMartin Schwidefsky 	case 4: v = le32_to_cpu(r); break;				\
281d509f2aSMartin Schwidefsky 	case 2: v = le16_to_cpu(r); break;				\
291d509f2aSMartin Schwidefsky 	case 1:	v = r; break;						\
301d509f2aSMartin Schwidefsky 	default:							\
311d509f2aSMartin Schwidefsky 		BUILD_BUG(); break;					\
321d509f2aSMartin Schwidefsky 	}								\
331d509f2aSMartin Schwidefsky 	v;								\
341d509f2aSMartin Schwidefsky })
351d509f2aSMartin Schwidefsky 
36d046b725SJosh Poimboeuf /* Verify next sizeof(t) bytes can be on the same instruction */
37d046b725SJosh Poimboeuf #define validate_next(t, insn, n)	\
38d046b725SJosh Poimboeuf 	((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr)
39d046b725SJosh Poimboeuf 
40d046b725SJosh Poimboeuf #define __get_next(t, insn)	\
41*f96b4675SBorislav Petkov 	({ t r = get_unaligned((t *)(insn)->next_byte); (insn)->next_byte += sizeof(t); leXX_to_cpu(t, r); })
42d046b725SJosh Poimboeuf 
43d046b725SJosh Poimboeuf #define __peek_nbyte_next(t, insn, n)	\
44*f96b4675SBorislav Petkov 	({ t r = get_unaligned((t *)(insn)->next_byte + n); leXX_to_cpu(t, r); })
45d046b725SJosh Poimboeuf 
46d046b725SJosh Poimboeuf #define get_next(t, insn)	\
47d046b725SJosh Poimboeuf 	({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); })
48d046b725SJosh Poimboeuf 
49d046b725SJosh Poimboeuf #define peek_nbyte_next(t, insn, n)	\
50d046b725SJosh Poimboeuf 	({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); })
51d046b725SJosh Poimboeuf 
52d046b725SJosh Poimboeuf #define peek_next(t, insn)	peek_nbyte_next(t, insn, 0)
53d046b725SJosh Poimboeuf 
54d046b725SJosh Poimboeuf /**
55d046b725SJosh Poimboeuf  * insn_init() - initialize struct insn
56d046b725SJosh Poimboeuf  * @insn:	&struct insn to be initialized
57d046b725SJosh Poimboeuf  * @kaddr:	address (in kernel memory) of instruction (or copy thereof)
58508ef286SBorislav Petkov  * @buf_len:	length of the insn buffer at @kaddr
59d046b725SJosh Poimboeuf  * @x86_64:	!0 for 64-bit kernel or 64-bit app
60d046b725SJosh Poimboeuf  */
insn_init(struct insn * insn,const void * kaddr,int buf_len,int x86_64)61d046b725SJosh Poimboeuf void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64)
62d046b725SJosh Poimboeuf {
63d046b725SJosh Poimboeuf 	/*
64d046b725SJosh Poimboeuf 	 * Instructions longer than MAX_INSN_SIZE (15 bytes) are invalid
65d046b725SJosh Poimboeuf 	 * even if the input buffer is long enough to hold them.
66d046b725SJosh Poimboeuf 	 */
67d046b725SJosh Poimboeuf 	if (buf_len > MAX_INSN_SIZE)
68d046b725SJosh Poimboeuf 		buf_len = MAX_INSN_SIZE;
69d046b725SJosh Poimboeuf 
70d046b725SJosh Poimboeuf 	memset(insn, 0, sizeof(*insn));
71d046b725SJosh Poimboeuf 	insn->kaddr = kaddr;
72d046b725SJosh Poimboeuf 	insn->end_kaddr = kaddr + buf_len;
73d046b725SJosh Poimboeuf 	insn->next_byte = kaddr;
74d046b725SJosh Poimboeuf 	insn->x86_64 = x86_64 ? 1 : 0;
75d046b725SJosh Poimboeuf 	insn->opnd_bytes = 4;
76d046b725SJosh Poimboeuf 	if (x86_64)
77d046b725SJosh Poimboeuf 		insn->addr_bytes = 8;
78d046b725SJosh Poimboeuf 	else
79d046b725SJosh Poimboeuf 		insn->addr_bytes = 4;
80d046b725SJosh Poimboeuf }
81d046b725SJosh Poimboeuf 
824d65adfcSMasami Hiramatsu static const insn_byte_t xen_prefix[] = { __XEN_EMULATE_PREFIX };
834d65adfcSMasami Hiramatsu static const insn_byte_t kvm_prefix[] = { __KVM_EMULATE_PREFIX };
844d65adfcSMasami Hiramatsu 
__insn_get_emulate_prefix(struct insn * insn,const insn_byte_t * prefix,size_t len)854d65adfcSMasami Hiramatsu static int __insn_get_emulate_prefix(struct insn *insn,
864d65adfcSMasami Hiramatsu 				     const insn_byte_t *prefix, size_t len)
874d65adfcSMasami Hiramatsu {
884d65adfcSMasami Hiramatsu 	size_t i;
894d65adfcSMasami Hiramatsu 
904d65adfcSMasami Hiramatsu 	for (i = 0; i < len; i++) {
914d65adfcSMasami Hiramatsu 		if (peek_nbyte_next(insn_byte_t, insn, i) != prefix[i])
924d65adfcSMasami Hiramatsu 			goto err_out;
934d65adfcSMasami Hiramatsu 	}
944d65adfcSMasami Hiramatsu 
954d65adfcSMasami Hiramatsu 	insn->emulate_prefix_size = len;
964d65adfcSMasami Hiramatsu 	insn->next_byte += len;
974d65adfcSMasami Hiramatsu 
984d65adfcSMasami Hiramatsu 	return 1;
994d65adfcSMasami Hiramatsu 
1004d65adfcSMasami Hiramatsu err_out:
1014d65adfcSMasami Hiramatsu 	return 0;
1024d65adfcSMasami Hiramatsu }
1034d65adfcSMasami Hiramatsu 
insn_get_emulate_prefix(struct insn * insn)1044d65adfcSMasami Hiramatsu static void insn_get_emulate_prefix(struct insn *insn)
1054d65adfcSMasami Hiramatsu {
1064d65adfcSMasami Hiramatsu 	if (__insn_get_emulate_prefix(insn, xen_prefix, sizeof(xen_prefix)))
1074d65adfcSMasami Hiramatsu 		return;
1084d65adfcSMasami Hiramatsu 
1094d65adfcSMasami Hiramatsu 	__insn_get_emulate_prefix(insn, kvm_prefix, sizeof(kvm_prefix));
1104d65adfcSMasami Hiramatsu }
1114d65adfcSMasami Hiramatsu 
112d046b725SJosh Poimboeuf /**
113d046b725SJosh Poimboeuf  * insn_get_prefixes - scan x86 instruction prefix bytes
114d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
115d046b725SJosh Poimboeuf  *
116d046b725SJosh Poimboeuf  * Populates the @insn->prefixes bitmap, and updates @insn->next_byte
117d046b725SJosh Poimboeuf  * to point to the (first) opcode.  No effect if @insn->prefixes.got
118d046b725SJosh Poimboeuf  * is already set.
11993281c4aSBorislav Petkov  *
12093281c4aSBorislav Petkov  * * Returns:
12193281c4aSBorislav Petkov  * 0:  on success
12293281c4aSBorislav Petkov  * < 0: on error
123d046b725SJosh Poimboeuf  */
insn_get_prefixes(struct insn * insn)12493281c4aSBorislav Petkov int insn_get_prefixes(struct insn *insn)
125d046b725SJosh Poimboeuf {
126d046b725SJosh Poimboeuf 	struct insn_field *prefixes = &insn->prefixes;
127d046b725SJosh Poimboeuf 	insn_attr_t attr;
128d046b725SJosh Poimboeuf 	insn_byte_t b, lb;
129d046b725SJosh Poimboeuf 	int i, nb;
130d046b725SJosh Poimboeuf 
131d046b725SJosh Poimboeuf 	if (prefixes->got)
13293281c4aSBorislav Petkov 		return 0;
133d046b725SJosh Poimboeuf 
1344d65adfcSMasami Hiramatsu 	insn_get_emulate_prefix(insn);
1354d65adfcSMasami Hiramatsu 
136d046b725SJosh Poimboeuf 	nb = 0;
137d046b725SJosh Poimboeuf 	lb = 0;
138d046b725SJosh Poimboeuf 	b = peek_next(insn_byte_t, insn);
139d046b725SJosh Poimboeuf 	attr = inat_get_opcode_attribute(b);
140d046b725SJosh Poimboeuf 	while (inat_is_legacy_prefix(attr)) {
141d046b725SJosh Poimboeuf 		/* Skip if same prefix */
142d046b725SJosh Poimboeuf 		for (i = 0; i < nb; i++)
143d046b725SJosh Poimboeuf 			if (prefixes->bytes[i] == b)
144d046b725SJosh Poimboeuf 				goto found;
145d046b725SJosh Poimboeuf 		if (nb == 4)
146d046b725SJosh Poimboeuf 			/* Invalid instruction */
147d046b725SJosh Poimboeuf 			break;
148d046b725SJosh Poimboeuf 		prefixes->bytes[nb++] = b;
149d046b725SJosh Poimboeuf 		if (inat_is_address_size_prefix(attr)) {
150d046b725SJosh Poimboeuf 			/* address size switches 2/4 or 4/8 */
151d046b725SJosh Poimboeuf 			if (insn->x86_64)
152d046b725SJosh Poimboeuf 				insn->addr_bytes ^= 12;
153d046b725SJosh Poimboeuf 			else
154d046b725SJosh Poimboeuf 				insn->addr_bytes ^= 6;
155d046b725SJosh Poimboeuf 		} else if (inat_is_operand_size_prefix(attr)) {
156d046b725SJosh Poimboeuf 			/* oprand size switches 2/4 */
157d046b725SJosh Poimboeuf 			insn->opnd_bytes ^= 6;
158d046b725SJosh Poimboeuf 		}
159d046b725SJosh Poimboeuf found:
160d046b725SJosh Poimboeuf 		prefixes->nbytes++;
161d046b725SJosh Poimboeuf 		insn->next_byte++;
162d046b725SJosh Poimboeuf 		lb = b;
163d046b725SJosh Poimboeuf 		b = peek_next(insn_byte_t, insn);
164d046b725SJosh Poimboeuf 		attr = inat_get_opcode_attribute(b);
165d046b725SJosh Poimboeuf 	}
166d046b725SJosh Poimboeuf 	/* Set the last prefix */
167d046b725SJosh Poimboeuf 	if (lb && lb != insn->prefixes.bytes[3]) {
168d046b725SJosh Poimboeuf 		if (unlikely(insn->prefixes.bytes[3])) {
169d046b725SJosh Poimboeuf 			/* Swap the last prefix */
170d046b725SJosh Poimboeuf 			b = insn->prefixes.bytes[3];
171d046b725SJosh Poimboeuf 			for (i = 0; i < nb; i++)
172d046b725SJosh Poimboeuf 				if (prefixes->bytes[i] == lb)
1735ed934e5SVasily Gorbik 					insn_set_byte(prefixes, i, b);
174d046b725SJosh Poimboeuf 		}
1755ed934e5SVasily Gorbik 		insn_set_byte(&insn->prefixes, 3, lb);
176d046b725SJosh Poimboeuf 	}
177d046b725SJosh Poimboeuf 
178d046b725SJosh Poimboeuf 	/* Decode REX prefix */
179d046b725SJosh Poimboeuf 	if (insn->x86_64) {
180d046b725SJosh Poimboeuf 		b = peek_next(insn_byte_t, insn);
181d046b725SJosh Poimboeuf 		attr = inat_get_opcode_attribute(b);
182d046b725SJosh Poimboeuf 		if (inat_is_rex_prefix(attr)) {
1831d509f2aSMartin Schwidefsky 			insn_field_set(&insn->rex_prefix, b, 1);
184d046b725SJosh Poimboeuf 			insn->next_byte++;
185d046b725SJosh Poimboeuf 			if (X86_REX_W(b))
186d046b725SJosh Poimboeuf 				/* REX.W overrides opnd_size */
187d046b725SJosh Poimboeuf 				insn->opnd_bytes = 8;
188d046b725SJosh Poimboeuf 		}
189d046b725SJosh Poimboeuf 	}
190d046b725SJosh Poimboeuf 	insn->rex_prefix.got = 1;
191d046b725SJosh Poimboeuf 
192d046b725SJosh Poimboeuf 	/* Decode VEX prefix */
193d046b725SJosh Poimboeuf 	b = peek_next(insn_byte_t, insn);
194d046b725SJosh Poimboeuf 	attr = inat_get_opcode_attribute(b);
195d046b725SJosh Poimboeuf 	if (inat_is_vex_prefix(attr)) {
196d046b725SJosh Poimboeuf 		insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1);
197d046b725SJosh Poimboeuf 		if (!insn->x86_64) {
198d046b725SJosh Poimboeuf 			/*
199d046b725SJosh Poimboeuf 			 * In 32-bits mode, if the [7:6] bits (mod bits of
200d046b725SJosh Poimboeuf 			 * ModRM) on the second byte are not 11b, it is
201d046b725SJosh Poimboeuf 			 * LDS or LES or BOUND.
202d046b725SJosh Poimboeuf 			 */
203d046b725SJosh Poimboeuf 			if (X86_MODRM_MOD(b2) != 3)
204d046b725SJosh Poimboeuf 				goto vex_end;
205d046b725SJosh Poimboeuf 		}
2065ed934e5SVasily Gorbik 		insn_set_byte(&insn->vex_prefix, 0, b);
2075ed934e5SVasily Gorbik 		insn_set_byte(&insn->vex_prefix, 1, b2);
208d046b725SJosh Poimboeuf 		if (inat_is_evex_prefix(attr)) {
209d046b725SJosh Poimboeuf 			b2 = peek_nbyte_next(insn_byte_t, insn, 2);
2105ed934e5SVasily Gorbik 			insn_set_byte(&insn->vex_prefix, 2, b2);
211d046b725SJosh Poimboeuf 			b2 = peek_nbyte_next(insn_byte_t, insn, 3);
2125ed934e5SVasily Gorbik 			insn_set_byte(&insn->vex_prefix, 3, b2);
213d046b725SJosh Poimboeuf 			insn->vex_prefix.nbytes = 4;
214d046b725SJosh Poimboeuf 			insn->next_byte += 4;
215d046b725SJosh Poimboeuf 			if (insn->x86_64 && X86_VEX_W(b2))
216d046b725SJosh Poimboeuf 				/* VEX.W overrides opnd_size */
217d046b725SJosh Poimboeuf 				insn->opnd_bytes = 8;
218d046b725SJosh Poimboeuf 		} else if (inat_is_vex3_prefix(attr)) {
219d046b725SJosh Poimboeuf 			b2 = peek_nbyte_next(insn_byte_t, insn, 2);
2205ed934e5SVasily Gorbik 			insn_set_byte(&insn->vex_prefix, 2, b2);
221d046b725SJosh Poimboeuf 			insn->vex_prefix.nbytes = 3;
222d046b725SJosh Poimboeuf 			insn->next_byte += 3;
223d046b725SJosh Poimboeuf 			if (insn->x86_64 && X86_VEX_W(b2))
224d046b725SJosh Poimboeuf 				/* VEX.W overrides opnd_size */
225d046b725SJosh Poimboeuf 				insn->opnd_bytes = 8;
226d046b725SJosh Poimboeuf 		} else {
227d046b725SJosh Poimboeuf 			/*
228d046b725SJosh Poimboeuf 			 * For VEX2, fake VEX3-like byte#2.
229d046b725SJosh Poimboeuf 			 * Makes it easier to decode vex.W, vex.vvvv,
230d046b725SJosh Poimboeuf 			 * vex.L and vex.pp. Masking with 0x7f sets vex.W == 0.
231d046b725SJosh Poimboeuf 			 */
2325ed934e5SVasily Gorbik 			insn_set_byte(&insn->vex_prefix, 2, b2 & 0x7f);
233d046b725SJosh Poimboeuf 			insn->vex_prefix.nbytes = 2;
234d046b725SJosh Poimboeuf 			insn->next_byte += 2;
235d046b725SJosh Poimboeuf 		}
236d046b725SJosh Poimboeuf 	}
237d046b725SJosh Poimboeuf vex_end:
238d046b725SJosh Poimboeuf 	insn->vex_prefix.got = 1;
239d046b725SJosh Poimboeuf 
240d046b725SJosh Poimboeuf 	prefixes->got = 1;
241d046b725SJosh Poimboeuf 
24293281c4aSBorislav Petkov 	return 0;
24393281c4aSBorislav Petkov 
244d046b725SJosh Poimboeuf err_out:
24593281c4aSBorislav Petkov 	return -ENODATA;
246d046b725SJosh Poimboeuf }
247d046b725SJosh Poimboeuf 
248d046b725SJosh Poimboeuf /**
249d046b725SJosh Poimboeuf  * insn_get_opcode - collect opcode(s)
250d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
251d046b725SJosh Poimboeuf  *
252d046b725SJosh Poimboeuf  * Populates @insn->opcode, updates @insn->next_byte to point past the
253d046b725SJosh Poimboeuf  * opcode byte(s), and set @insn->attr (except for groups).
254d046b725SJosh Poimboeuf  * If necessary, first collects any preceding (prefix) bytes.
255d046b725SJosh Poimboeuf  * Sets @insn->opcode.value = opcode1.  No effect if @insn->opcode.got
256d046b725SJosh Poimboeuf  * is already 1.
25793281c4aSBorislav Petkov  *
25893281c4aSBorislav Petkov  * Returns:
25993281c4aSBorislav Petkov  * 0:  on success
26093281c4aSBorislav Petkov  * < 0: on error
261d046b725SJosh Poimboeuf  */
insn_get_opcode(struct insn * insn)26293281c4aSBorislav Petkov int insn_get_opcode(struct insn *insn)
263d046b725SJosh Poimboeuf {
264d046b725SJosh Poimboeuf 	struct insn_field *opcode = &insn->opcode;
26593281c4aSBorislav Petkov 	int pfx_id, ret;
266d046b725SJosh Poimboeuf 	insn_byte_t op;
26793281c4aSBorislav Petkov 
268d046b725SJosh Poimboeuf 	if (opcode->got)
26993281c4aSBorislav Petkov 		return 0;
27093281c4aSBorislav Petkov 
27193281c4aSBorislav Petkov 	if (!insn->prefixes.got) {
27293281c4aSBorislav Petkov 		ret = insn_get_prefixes(insn);
27393281c4aSBorislav Petkov 		if (ret)
27493281c4aSBorislav Petkov 			return ret;
27593281c4aSBorislav Petkov 	}
276d046b725SJosh Poimboeuf 
277d046b725SJosh Poimboeuf 	/* Get first opcode */
278d046b725SJosh Poimboeuf 	op = get_next(insn_byte_t, insn);
2795ed934e5SVasily Gorbik 	insn_set_byte(opcode, 0, op);
280d046b725SJosh Poimboeuf 	opcode->nbytes = 1;
281d046b725SJosh Poimboeuf 
282d046b725SJosh Poimboeuf 	/* Check if there is VEX prefix or not */
283d046b725SJosh Poimboeuf 	if (insn_is_avx(insn)) {
284d046b725SJosh Poimboeuf 		insn_byte_t m, p;
285d046b725SJosh Poimboeuf 		m = insn_vex_m_bits(insn);
286d046b725SJosh Poimboeuf 		p = insn_vex_p_bits(insn);
287d046b725SJosh Poimboeuf 		insn->attr = inat_get_avx_attribute(op, m, p);
288d046b725SJosh Poimboeuf 		if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
289d046b725SJosh Poimboeuf 		    (!inat_accept_vex(insn->attr) &&
29093281c4aSBorislav Petkov 		     !inat_is_group(insn->attr))) {
29193281c4aSBorislav Petkov 			/* This instruction is bad */
29293281c4aSBorislav Petkov 			insn->attr = 0;
29393281c4aSBorislav Petkov 			return -EINVAL;
29493281c4aSBorislav Petkov 		}
29593281c4aSBorislav Petkov 		/* VEX has only 1 byte for opcode */
29693281c4aSBorislav Petkov 		goto end;
297d046b725SJosh Poimboeuf 	}
298d046b725SJosh Poimboeuf 
299d046b725SJosh Poimboeuf 	insn->attr = inat_get_opcode_attribute(op);
300d046b725SJosh Poimboeuf 	while (inat_is_escape(insn->attr)) {
301d046b725SJosh Poimboeuf 		/* Get escaped opcode */
302d046b725SJosh Poimboeuf 		op = get_next(insn_byte_t, insn);
303d046b725SJosh Poimboeuf 		opcode->bytes[opcode->nbytes++] = op;
304d046b725SJosh Poimboeuf 		pfx_id = insn_last_prefix_id(insn);
305d046b725SJosh Poimboeuf 		insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr);
306d046b725SJosh Poimboeuf 	}
30793281c4aSBorislav Petkov 
30893281c4aSBorislav Petkov 	if (inat_must_vex(insn->attr)) {
30993281c4aSBorislav Petkov 		/* This instruction is bad */
31093281c4aSBorislav Petkov 		insn->attr = 0;
31193281c4aSBorislav Petkov 		return -EINVAL;
31293281c4aSBorislav Petkov 	}
313d046b725SJosh Poimboeuf end:
314d046b725SJosh Poimboeuf 	opcode->got = 1;
31593281c4aSBorislav Petkov 	return 0;
316d046b725SJosh Poimboeuf 
317d046b725SJosh Poimboeuf err_out:
31893281c4aSBorislav Petkov 	return -ENODATA;
319d046b725SJosh Poimboeuf }
320d046b725SJosh Poimboeuf 
321d046b725SJosh Poimboeuf /**
322d046b725SJosh Poimboeuf  * insn_get_modrm - collect ModRM byte, if any
323d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
324d046b725SJosh Poimboeuf  *
325d046b725SJosh Poimboeuf  * Populates @insn->modrm and updates @insn->next_byte to point past the
326d046b725SJosh Poimboeuf  * ModRM byte, if any.  If necessary, first collects the preceding bytes
327d046b725SJosh Poimboeuf  * (prefixes and opcode(s)).  No effect if @insn->modrm.got is already 1.
32893281c4aSBorislav Petkov  *
32993281c4aSBorislav Petkov  * Returns:
33093281c4aSBorislav Petkov  * 0:  on success
33193281c4aSBorislav Petkov  * < 0: on error
332d046b725SJosh Poimboeuf  */
insn_get_modrm(struct insn * insn)33393281c4aSBorislav Petkov int insn_get_modrm(struct insn *insn)
334d046b725SJosh Poimboeuf {
335d046b725SJosh Poimboeuf 	struct insn_field *modrm = &insn->modrm;
336d046b725SJosh Poimboeuf 	insn_byte_t pfx_id, mod;
33793281c4aSBorislav Petkov 	int ret;
33893281c4aSBorislav Petkov 
339d046b725SJosh Poimboeuf 	if (modrm->got)
34093281c4aSBorislav Petkov 		return 0;
34193281c4aSBorislav Petkov 
34293281c4aSBorislav Petkov 	if (!insn->opcode.got) {
34393281c4aSBorislav Petkov 		ret = insn_get_opcode(insn);
34493281c4aSBorislav Petkov 		if (ret)
34593281c4aSBorislav Petkov 			return ret;
34693281c4aSBorislav Petkov 	}
347d046b725SJosh Poimboeuf 
348d046b725SJosh Poimboeuf 	if (inat_has_modrm(insn->attr)) {
349d046b725SJosh Poimboeuf 		mod = get_next(insn_byte_t, insn);
3501d509f2aSMartin Schwidefsky 		insn_field_set(modrm, mod, 1);
351d046b725SJosh Poimboeuf 		if (inat_is_group(insn->attr)) {
352d046b725SJosh Poimboeuf 			pfx_id = insn_last_prefix_id(insn);
353d046b725SJosh Poimboeuf 			insn->attr = inat_get_group_attribute(mod, pfx_id,
354d046b725SJosh Poimboeuf 							      insn->attr);
35593281c4aSBorislav Petkov 			if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) {
35693281c4aSBorislav Petkov 				/* Bad insn */
35793281c4aSBorislav Petkov 				insn->attr = 0;
35893281c4aSBorislav Petkov 				return -EINVAL;
35993281c4aSBorislav Petkov 			}
360d046b725SJosh Poimboeuf 		}
361d046b725SJosh Poimboeuf 	}
362d046b725SJosh Poimboeuf 
363d046b725SJosh Poimboeuf 	if (insn->x86_64 && inat_is_force64(insn->attr))
364d046b725SJosh Poimboeuf 		insn->opnd_bytes = 8;
36593281c4aSBorislav Petkov 
366d046b725SJosh Poimboeuf 	modrm->got = 1;
36793281c4aSBorislav Petkov 	return 0;
368d046b725SJosh Poimboeuf 
369d046b725SJosh Poimboeuf err_out:
37093281c4aSBorislav Petkov 	return -ENODATA;
371d046b725SJosh Poimboeuf }
372d046b725SJosh Poimboeuf 
373d046b725SJosh Poimboeuf 
374d046b725SJosh Poimboeuf /**
375d046b725SJosh Poimboeuf  * insn_rip_relative() - Does instruction use RIP-relative addressing mode?
376d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
377d046b725SJosh Poimboeuf  *
378d046b725SJosh Poimboeuf  * If necessary, first collects the instruction up to and including the
379d046b725SJosh Poimboeuf  * ModRM byte.  No effect if @insn->x86_64 is 0.
380d046b725SJosh Poimboeuf  */
insn_rip_relative(struct insn * insn)381d046b725SJosh Poimboeuf int insn_rip_relative(struct insn *insn)
382d046b725SJosh Poimboeuf {
383d046b725SJosh Poimboeuf 	struct insn_field *modrm = &insn->modrm;
38493281c4aSBorislav Petkov 	int ret;
385d046b725SJosh Poimboeuf 
386d046b725SJosh Poimboeuf 	if (!insn->x86_64)
387d046b725SJosh Poimboeuf 		return 0;
38893281c4aSBorislav Petkov 
38993281c4aSBorislav Petkov 	if (!modrm->got) {
39093281c4aSBorislav Petkov 		ret = insn_get_modrm(insn);
39193281c4aSBorislav Petkov 		if (ret)
39293281c4aSBorislav Petkov 			return 0;
39393281c4aSBorislav Petkov 	}
394d046b725SJosh Poimboeuf 	/*
395d046b725SJosh Poimboeuf 	 * For rip-relative instructions, the mod field (top 2 bits)
396d046b725SJosh Poimboeuf 	 * is zero and the r/m field (bottom 3 bits) is 0x5.
397d046b725SJosh Poimboeuf 	 */
3981d509f2aSMartin Schwidefsky 	return (modrm->nbytes && (modrm->bytes[0] & 0xc7) == 0x5);
399d046b725SJosh Poimboeuf }
400d046b725SJosh Poimboeuf 
401d046b725SJosh Poimboeuf /**
402d046b725SJosh Poimboeuf  * insn_get_sib() - Get the SIB byte of instruction
403d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
404d046b725SJosh Poimboeuf  *
405d046b725SJosh Poimboeuf  * If necessary, first collects the instruction up to and including the
406d046b725SJosh Poimboeuf  * ModRM byte.
40793281c4aSBorislav Petkov  *
40893281c4aSBorislav Petkov  * Returns:
40993281c4aSBorislav Petkov  * 0: if decoding succeeded
41093281c4aSBorislav Petkov  * < 0: otherwise.
411d046b725SJosh Poimboeuf  */
insn_get_sib(struct insn * insn)41293281c4aSBorislav Petkov int insn_get_sib(struct insn *insn)
413d046b725SJosh Poimboeuf {
414d046b725SJosh Poimboeuf 	insn_byte_t modrm;
41593281c4aSBorislav Petkov 	int ret;
416d046b725SJosh Poimboeuf 
417d046b725SJosh Poimboeuf 	if (insn->sib.got)
41893281c4aSBorislav Petkov 		return 0;
41993281c4aSBorislav Petkov 
42093281c4aSBorislav Petkov 	if (!insn->modrm.got) {
42193281c4aSBorislav Petkov 		ret = insn_get_modrm(insn);
42293281c4aSBorislav Petkov 		if (ret)
42393281c4aSBorislav Petkov 			return ret;
42493281c4aSBorislav Petkov 	}
42593281c4aSBorislav Petkov 
426d046b725SJosh Poimboeuf 	if (insn->modrm.nbytes) {
4271d509f2aSMartin Schwidefsky 		modrm = insn->modrm.bytes[0];
428d046b725SJosh Poimboeuf 		if (insn->addr_bytes != 2 &&
429d046b725SJosh Poimboeuf 		    X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) {
4301d509f2aSMartin Schwidefsky 			insn_field_set(&insn->sib,
4311d509f2aSMartin Schwidefsky 				       get_next(insn_byte_t, insn), 1);
432d046b725SJosh Poimboeuf 		}
433d046b725SJosh Poimboeuf 	}
434d046b725SJosh Poimboeuf 	insn->sib.got = 1;
435d046b725SJosh Poimboeuf 
43693281c4aSBorislav Petkov 	return 0;
43793281c4aSBorislav Petkov 
438d046b725SJosh Poimboeuf err_out:
43993281c4aSBorislav Petkov 	return -ENODATA;
440d046b725SJosh Poimboeuf }
441d046b725SJosh Poimboeuf 
442d046b725SJosh Poimboeuf 
443d046b725SJosh Poimboeuf /**
444d046b725SJosh Poimboeuf  * insn_get_displacement() - Get the displacement of instruction
445d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
446d046b725SJosh Poimboeuf  *
447d046b725SJosh Poimboeuf  * If necessary, first collects the instruction up to and including the
448d046b725SJosh Poimboeuf  * SIB byte.
449d046b725SJosh Poimboeuf  * Displacement value is sign-expanded.
45093281c4aSBorislav Petkov  *
45193281c4aSBorislav Petkov  * * Returns:
45293281c4aSBorislav Petkov  * 0: if decoding succeeded
45393281c4aSBorislav Petkov  * < 0: otherwise.
454d046b725SJosh Poimboeuf  */
insn_get_displacement(struct insn * insn)45593281c4aSBorislav Petkov int insn_get_displacement(struct insn *insn)
456d046b725SJosh Poimboeuf {
457d046b725SJosh Poimboeuf 	insn_byte_t mod, rm, base;
45893281c4aSBorislav Petkov 	int ret;
459d046b725SJosh Poimboeuf 
460d046b725SJosh Poimboeuf 	if (insn->displacement.got)
46193281c4aSBorislav Petkov 		return 0;
46293281c4aSBorislav Petkov 
46393281c4aSBorislav Petkov 	if (!insn->sib.got) {
46493281c4aSBorislav Petkov 		ret = insn_get_sib(insn);
46593281c4aSBorislav Petkov 		if (ret)
46693281c4aSBorislav Petkov 			return ret;
46793281c4aSBorislav Petkov 	}
46893281c4aSBorislav Petkov 
469d046b725SJosh Poimboeuf 	if (insn->modrm.nbytes) {
470d046b725SJosh Poimboeuf 		/*
471d046b725SJosh Poimboeuf 		 * Interpreting the modrm byte:
472d046b725SJosh Poimboeuf 		 * mod = 00 - no displacement fields (exceptions below)
473d046b725SJosh Poimboeuf 		 * mod = 01 - 1-byte displacement field
474d046b725SJosh Poimboeuf 		 * mod = 10 - displacement field is 4 bytes, or 2 bytes if
475d046b725SJosh Poimboeuf 		 * 	address size = 2 (0x67 prefix in 32-bit mode)
476d046b725SJosh Poimboeuf 		 * mod = 11 - no memory operand
477d046b725SJosh Poimboeuf 		 *
478d046b725SJosh Poimboeuf 		 * If address size = 2...
479d046b725SJosh Poimboeuf 		 * mod = 00, r/m = 110 - displacement field is 2 bytes
480d046b725SJosh Poimboeuf 		 *
481d046b725SJosh Poimboeuf 		 * If address size != 2...
482d046b725SJosh Poimboeuf 		 * mod != 11, r/m = 100 - SIB byte exists
483d046b725SJosh Poimboeuf 		 * mod = 00, SIB base = 101 - displacement field is 4 bytes
484d046b725SJosh Poimboeuf 		 * mod = 00, r/m = 101 - rip-relative addressing, displacement
485d046b725SJosh Poimboeuf 		 * 	field is 4 bytes
486d046b725SJosh Poimboeuf 		 */
487d046b725SJosh Poimboeuf 		mod = X86_MODRM_MOD(insn->modrm.value);
488d046b725SJosh Poimboeuf 		rm = X86_MODRM_RM(insn->modrm.value);
489d046b725SJosh Poimboeuf 		base = X86_SIB_BASE(insn->sib.value);
490d046b725SJosh Poimboeuf 		if (mod == 3)
491d046b725SJosh Poimboeuf 			goto out;
492d046b725SJosh Poimboeuf 		if (mod == 1) {
4931d509f2aSMartin Schwidefsky 			insn_field_set(&insn->displacement,
4941d509f2aSMartin Schwidefsky 				       get_next(signed char, insn), 1);
495d046b725SJosh Poimboeuf 		} else if (insn->addr_bytes == 2) {
496d046b725SJosh Poimboeuf 			if ((mod == 0 && rm == 6) || mod == 2) {
4971d509f2aSMartin Schwidefsky 				insn_field_set(&insn->displacement,
4981d509f2aSMartin Schwidefsky 					       get_next(short, insn), 2);
499d046b725SJosh Poimboeuf 			}
500d046b725SJosh Poimboeuf 		} else {
501d046b725SJosh Poimboeuf 			if ((mod == 0 && rm == 5) || mod == 2 ||
502d046b725SJosh Poimboeuf 			    (mod == 0 && base == 5)) {
5031d509f2aSMartin Schwidefsky 				insn_field_set(&insn->displacement,
5041d509f2aSMartin Schwidefsky 					       get_next(int, insn), 4);
505d046b725SJosh Poimboeuf 			}
506d046b725SJosh Poimboeuf 		}
507d046b725SJosh Poimboeuf 	}
508d046b725SJosh Poimboeuf out:
509d046b725SJosh Poimboeuf 	insn->displacement.got = 1;
51093281c4aSBorislav Petkov 	return 0;
511d046b725SJosh Poimboeuf 
512d046b725SJosh Poimboeuf err_out:
51393281c4aSBorislav Petkov 	return -ENODATA;
514d046b725SJosh Poimboeuf }
515d046b725SJosh Poimboeuf 
516d046b725SJosh Poimboeuf /* Decode moffset16/32/64. Return 0 if failed */
__get_moffset(struct insn * insn)517d046b725SJosh Poimboeuf static int __get_moffset(struct insn *insn)
518d046b725SJosh Poimboeuf {
519d046b725SJosh Poimboeuf 	switch (insn->addr_bytes) {
520d046b725SJosh Poimboeuf 	case 2:
5211d509f2aSMartin Schwidefsky 		insn_field_set(&insn->moffset1, get_next(short, insn), 2);
522d046b725SJosh Poimboeuf 		break;
523d046b725SJosh Poimboeuf 	case 4:
5241d509f2aSMartin Schwidefsky 		insn_field_set(&insn->moffset1, get_next(int, insn), 4);
525d046b725SJosh Poimboeuf 		break;
526d046b725SJosh Poimboeuf 	case 8:
5271d509f2aSMartin Schwidefsky 		insn_field_set(&insn->moffset1, get_next(int, insn), 4);
5281d509f2aSMartin Schwidefsky 		insn_field_set(&insn->moffset2, get_next(int, insn), 4);
529d046b725SJosh Poimboeuf 		break;
530d046b725SJosh Poimboeuf 	default:	/* opnd_bytes must be modified manually */
531d046b725SJosh Poimboeuf 		goto err_out;
532d046b725SJosh Poimboeuf 	}
533d046b725SJosh Poimboeuf 	insn->moffset1.got = insn->moffset2.got = 1;
534d046b725SJosh Poimboeuf 
535d046b725SJosh Poimboeuf 	return 1;
536d046b725SJosh Poimboeuf 
537d046b725SJosh Poimboeuf err_out:
538d046b725SJosh Poimboeuf 	return 0;
539d046b725SJosh Poimboeuf }
540d046b725SJosh Poimboeuf 
541d046b725SJosh Poimboeuf /* Decode imm v32(Iz). Return 0 if failed */
__get_immv32(struct insn * insn)542d046b725SJosh Poimboeuf static int __get_immv32(struct insn *insn)
543d046b725SJosh Poimboeuf {
544d046b725SJosh Poimboeuf 	switch (insn->opnd_bytes) {
545d046b725SJosh Poimboeuf 	case 2:
5461d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate, get_next(short, insn), 2);
547d046b725SJosh Poimboeuf 		break;
548d046b725SJosh Poimboeuf 	case 4:
549d046b725SJosh Poimboeuf 	case 8:
5501d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate, get_next(int, insn), 4);
551d046b725SJosh Poimboeuf 		break;
552d046b725SJosh Poimboeuf 	default:	/* opnd_bytes must be modified manually */
553d046b725SJosh Poimboeuf 		goto err_out;
554d046b725SJosh Poimboeuf 	}
555d046b725SJosh Poimboeuf 
556d046b725SJosh Poimboeuf 	return 1;
557d046b725SJosh Poimboeuf 
558d046b725SJosh Poimboeuf err_out:
559d046b725SJosh Poimboeuf 	return 0;
560d046b725SJosh Poimboeuf }
561d046b725SJosh Poimboeuf 
562d046b725SJosh Poimboeuf /* Decode imm v64(Iv/Ov), Return 0 if failed */
__get_immv(struct insn * insn)563d046b725SJosh Poimboeuf static int __get_immv(struct insn *insn)
564d046b725SJosh Poimboeuf {
565d046b725SJosh Poimboeuf 	switch (insn->opnd_bytes) {
566d046b725SJosh Poimboeuf 	case 2:
5671d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(short, insn), 2);
568d046b725SJosh Poimboeuf 		break;
569d046b725SJosh Poimboeuf 	case 4:
5701d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(int, insn), 4);
571d046b725SJosh Poimboeuf 		insn->immediate1.nbytes = 4;
572d046b725SJosh Poimboeuf 		break;
573d046b725SJosh Poimboeuf 	case 8:
5741d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(int, insn), 4);
5751d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate2, get_next(int, insn), 4);
576d046b725SJosh Poimboeuf 		break;
577d046b725SJosh Poimboeuf 	default:	/* opnd_bytes must be modified manually */
578d046b725SJosh Poimboeuf 		goto err_out;
579d046b725SJosh Poimboeuf 	}
580d046b725SJosh Poimboeuf 	insn->immediate1.got = insn->immediate2.got = 1;
581d046b725SJosh Poimboeuf 
582d046b725SJosh Poimboeuf 	return 1;
583d046b725SJosh Poimboeuf err_out:
584d046b725SJosh Poimboeuf 	return 0;
585d046b725SJosh Poimboeuf }
586d046b725SJosh Poimboeuf 
587d046b725SJosh Poimboeuf /* Decode ptr16:16/32(Ap) */
__get_immptr(struct insn * insn)588d046b725SJosh Poimboeuf static int __get_immptr(struct insn *insn)
589d046b725SJosh Poimboeuf {
590d046b725SJosh Poimboeuf 	switch (insn->opnd_bytes) {
591d046b725SJosh Poimboeuf 	case 2:
5921d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(short, insn), 2);
593d046b725SJosh Poimboeuf 		break;
594d046b725SJosh Poimboeuf 	case 4:
5951d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(int, insn), 4);
596d046b725SJosh Poimboeuf 		break;
597d046b725SJosh Poimboeuf 	case 8:
598d046b725SJosh Poimboeuf 		/* ptr16:64 is not exist (no segment) */
599d046b725SJosh Poimboeuf 		return 0;
600d046b725SJosh Poimboeuf 	default:	/* opnd_bytes must be modified manually */
601d046b725SJosh Poimboeuf 		goto err_out;
602d046b725SJosh Poimboeuf 	}
6031d509f2aSMartin Schwidefsky 	insn_field_set(&insn->immediate2, get_next(unsigned short, insn), 2);
604d046b725SJosh Poimboeuf 	insn->immediate1.got = insn->immediate2.got = 1;
605d046b725SJosh Poimboeuf 
606d046b725SJosh Poimboeuf 	return 1;
607d046b725SJosh Poimboeuf err_out:
608d046b725SJosh Poimboeuf 	return 0;
609d046b725SJosh Poimboeuf }
610d046b725SJosh Poimboeuf 
611d046b725SJosh Poimboeuf /**
61293281c4aSBorislav Petkov  * insn_get_immediate() - Get the immediate in an instruction
613d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
614d046b725SJosh Poimboeuf  *
615d046b725SJosh Poimboeuf  * If necessary, first collects the instruction up to and including the
616d046b725SJosh Poimboeuf  * displacement bytes.
617d046b725SJosh Poimboeuf  * Basically, most of immediates are sign-expanded. Unsigned-value can be
61893281c4aSBorislav Petkov  * computed by bit masking with ((1 << (nbytes * 8)) - 1)
61993281c4aSBorislav Petkov  *
62093281c4aSBorislav Petkov  * Returns:
62193281c4aSBorislav Petkov  * 0:  on success
62293281c4aSBorislav Petkov  * < 0: on error
623d046b725SJosh Poimboeuf  */
insn_get_immediate(struct insn * insn)62493281c4aSBorislav Petkov int insn_get_immediate(struct insn *insn)
625d046b725SJosh Poimboeuf {
62693281c4aSBorislav Petkov 	int ret;
62793281c4aSBorislav Petkov 
628d046b725SJosh Poimboeuf 	if (insn->immediate.got)
62993281c4aSBorislav Petkov 		return 0;
63093281c4aSBorislav Petkov 
63193281c4aSBorislav Petkov 	if (!insn->displacement.got) {
63293281c4aSBorislav Petkov 		ret = insn_get_displacement(insn);
63393281c4aSBorislav Petkov 		if (ret)
63493281c4aSBorislav Petkov 			return ret;
63593281c4aSBorislav Petkov 	}
636d046b725SJosh Poimboeuf 
637d046b725SJosh Poimboeuf 	if (inat_has_moffset(insn->attr)) {
638d046b725SJosh Poimboeuf 		if (!__get_moffset(insn))
639d046b725SJosh Poimboeuf 			goto err_out;
640d046b725SJosh Poimboeuf 		goto done;
641d046b725SJosh Poimboeuf 	}
642d046b725SJosh Poimboeuf 
643d046b725SJosh Poimboeuf 	if (!inat_has_immediate(insn->attr))
644d046b725SJosh Poimboeuf 		/* no immediates */
645d046b725SJosh Poimboeuf 		goto done;
646d046b725SJosh Poimboeuf 
647d046b725SJosh Poimboeuf 	switch (inat_immediate_size(insn->attr)) {
648d046b725SJosh Poimboeuf 	case INAT_IMM_BYTE:
6491d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate, get_next(signed char, insn), 1);
650d046b725SJosh Poimboeuf 		break;
651d046b725SJosh Poimboeuf 	case INAT_IMM_WORD:
6521d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate, get_next(short, insn), 2);
653d046b725SJosh Poimboeuf 		break;
654d046b725SJosh Poimboeuf 	case INAT_IMM_DWORD:
6551d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate, get_next(int, insn), 4);
656d046b725SJosh Poimboeuf 		break;
657d046b725SJosh Poimboeuf 	case INAT_IMM_QWORD:
6581d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate1, get_next(int, insn), 4);
6591d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate2, get_next(int, insn), 4);
660d046b725SJosh Poimboeuf 		break;
661d046b725SJosh Poimboeuf 	case INAT_IMM_PTR:
662d046b725SJosh Poimboeuf 		if (!__get_immptr(insn))
663d046b725SJosh Poimboeuf 			goto err_out;
664d046b725SJosh Poimboeuf 		break;
665d046b725SJosh Poimboeuf 	case INAT_IMM_VWORD32:
666d046b725SJosh Poimboeuf 		if (!__get_immv32(insn))
667d046b725SJosh Poimboeuf 			goto err_out;
668d046b725SJosh Poimboeuf 		break;
669d046b725SJosh Poimboeuf 	case INAT_IMM_VWORD:
670d046b725SJosh Poimboeuf 		if (!__get_immv(insn))
671d046b725SJosh Poimboeuf 			goto err_out;
672d046b725SJosh Poimboeuf 		break;
673d046b725SJosh Poimboeuf 	default:
674d046b725SJosh Poimboeuf 		/* Here, insn must have an immediate, but failed */
675d046b725SJosh Poimboeuf 		goto err_out;
676d046b725SJosh Poimboeuf 	}
677d046b725SJosh Poimboeuf 	if (inat_has_second_immediate(insn->attr)) {
6781d509f2aSMartin Schwidefsky 		insn_field_set(&insn->immediate2, get_next(signed char, insn), 1);
679d046b725SJosh Poimboeuf 	}
680d046b725SJosh Poimboeuf done:
681d046b725SJosh Poimboeuf 	insn->immediate.got = 1;
68293281c4aSBorislav Petkov 	return 0;
683d046b725SJosh Poimboeuf 
684d046b725SJosh Poimboeuf err_out:
68593281c4aSBorislav Petkov 	return -ENODATA;
686d046b725SJosh Poimboeuf }
687d046b725SJosh Poimboeuf 
688d046b725SJosh Poimboeuf /**
689d046b725SJosh Poimboeuf  * insn_get_length() - Get the length of instruction
690d046b725SJosh Poimboeuf  * @insn:	&struct insn containing instruction
691d046b725SJosh Poimboeuf  *
692d046b725SJosh Poimboeuf  * If necessary, first collects the instruction up to and including the
693d046b725SJosh Poimboeuf  * immediates bytes.
69493281c4aSBorislav Petkov  *
69593281c4aSBorislav Petkov  * Returns:
69693281c4aSBorislav Petkov  *  - 0 on success
69793281c4aSBorislav Petkov  *  - < 0 on error
698d046b725SJosh Poimboeuf */
insn_get_length(struct insn * insn)69993281c4aSBorislav Petkov int insn_get_length(struct insn *insn)
700d046b725SJosh Poimboeuf {
70193281c4aSBorislav Petkov 	int ret;
70293281c4aSBorislav Petkov 
703d046b725SJosh Poimboeuf 	if (insn->length)
70493281c4aSBorislav Petkov 		return 0;
70593281c4aSBorislav Petkov 
70693281c4aSBorislav Petkov 	if (!insn->immediate.got) {
70793281c4aSBorislav Petkov 		ret = insn_get_immediate(insn);
70893281c4aSBorislav Petkov 		if (ret)
70993281c4aSBorislav Petkov 			return ret;
71093281c4aSBorislav Petkov 	}
71193281c4aSBorislav Petkov 
712d046b725SJosh Poimboeuf 	insn->length = (unsigned char)((unsigned long)insn->next_byte
713d046b725SJosh Poimboeuf 				     - (unsigned long)insn->kaddr);
71493281c4aSBorislav Petkov 
71593281c4aSBorislav Petkov 	return 0;
71693281c4aSBorislav Petkov }
71793281c4aSBorislav Petkov 
718f935178bSBorislav Petkov /* Ensure this instruction is decoded completely */
insn_complete(struct insn * insn)719f935178bSBorislav Petkov static inline int insn_complete(struct insn *insn)
720f935178bSBorislav Petkov {
721f935178bSBorislav Petkov 	return insn->opcode.got && insn->modrm.got && insn->sib.got &&
722f935178bSBorislav Petkov 		insn->displacement.got && insn->immediate.got;
723f935178bSBorislav Petkov }
724f935178bSBorislav Petkov 
72593281c4aSBorislav Petkov /**
72693281c4aSBorislav Petkov  * insn_decode() - Decode an x86 instruction
72793281c4aSBorislav Petkov  * @insn:	&struct insn to be initialized
72893281c4aSBorislav Petkov  * @kaddr:	address (in kernel memory) of instruction (or copy thereof)
72993281c4aSBorislav Petkov  * @buf_len:	length of the insn buffer at @kaddr
73093281c4aSBorislav Petkov  * @m:		insn mode, see enum insn_mode
73193281c4aSBorislav Petkov  *
73293281c4aSBorislav Petkov  * Returns:
73393281c4aSBorislav Petkov  * 0: if decoding succeeded
73493281c4aSBorislav Petkov  * < 0: otherwise.
73593281c4aSBorislav Petkov  */
insn_decode(struct insn * insn,const void * kaddr,int buf_len,enum insn_mode m)73693281c4aSBorislav Petkov int insn_decode(struct insn *insn, const void *kaddr, int buf_len, enum insn_mode m)
73793281c4aSBorislav Petkov {
73893281c4aSBorislav Petkov 	int ret;
73993281c4aSBorislav Petkov 
74093281c4aSBorislav Petkov #define INSN_MODE_KERN (enum insn_mode)-1 /* __ignore_sync_check__ mode is only valid in the kernel */
74193281c4aSBorislav Petkov 
74293281c4aSBorislav Petkov 	if (m == INSN_MODE_KERN)
74393281c4aSBorislav Petkov 		insn_init(insn, kaddr, buf_len, IS_ENABLED(CONFIG_X86_64));
74493281c4aSBorislav Petkov 	else
74593281c4aSBorislav Petkov 		insn_init(insn, kaddr, buf_len, m == INSN_MODE_64);
74693281c4aSBorislav Petkov 
74793281c4aSBorislav Petkov 	ret = insn_get_length(insn);
74893281c4aSBorislav Petkov 	if (ret)
74993281c4aSBorislav Petkov 		return ret;
75093281c4aSBorislav Petkov 
75193281c4aSBorislav Petkov 	if (insn_complete(insn))
75293281c4aSBorislav Petkov 		return 0;
75393281c4aSBorislav Petkov 
75493281c4aSBorislav Petkov 	return -EINVAL;
755d046b725SJosh Poimboeuf }
756