Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0e492e9 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Ingo Molnar
Browse files

x86: AVX instruction set decoder support



Add Intel AVX(Advanced Vector Extensions) instruction set
support to x86 instruction decoder. This adds insn.vex_prefix
field for storing VEX prefixes, and introduces some original
tags for expressing opcodes attributes.

Signed-off-by: default avatarMasami Hiramatsu <mhiramat@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
LKML-Reference: <20091027204226.30545.23451.stgit@harusame>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 82cb5702
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -32,8 +32,8 @@

/* Legacy last prefixes */
#define INAT_PFX_OPNDSZ	1	/* 0x66 */ /* LPFX1 */
#define INAT_PFX_REPNE	2	/* 0xF2 */ /* LPFX2 */
#define INAT_PFX_REPE	3	/* 0xF3 */ /* LPFX3 */
#define INAT_PFX_REPE	2	/* 0xF3 */ /* LPFX2 */
#define INAT_PFX_REPNE	3	/* 0xF2 */ /* LPFX3 */
/* Other Legacy prefixes */
#define INAT_PFX_LOCK	4	/* 0xF0 */
#define INAT_PFX_CS	5	/* 0x2E */
@@ -45,6 +45,9 @@
#define INAT_PFX_ADDRSZ	11	/* 0x67 */
/* x86-64 REX prefix */
#define INAT_PFX_REX	12	/* 0x4X */
/* AVX VEX prefixes */
#define INAT_PFX_VEX2	13	/* 2-bytes VEX prefix */
#define INAT_PFX_VEX3	14	/* 3-bytes VEX prefix */

#define INAT_LSTPFX_MAX	3
#define INAT_LGCPFX_MAX	11
@@ -84,6 +87,8 @@
#define INAT_SCNDIMM	(1 << (INAT_FLAG_OFFS + 2))
#define INAT_MOFFSET	(1 << (INAT_FLAG_OFFS + 3))
#define INAT_VARIANT	(1 << (INAT_FLAG_OFFS + 4))
#define INAT_VEXOK	(1 << (INAT_FLAG_OFFS + 5))
#define INAT_VEXONLY	(1 << (INAT_FLAG_OFFS + 6))
/* Attribute making macros for attribute tables */
#define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS)
#define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS)
@@ -98,6 +103,9 @@ extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode,
extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm,
					    insn_byte_t last_pfx,
					    insn_attr_t esc_attr);
extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode,
					  insn_byte_t vex_m,
					  insn_byte_t vex_pp);

/* Attribute checking functions */
static inline int inat_is_legacy_prefix(insn_attr_t attr)
@@ -129,6 +137,17 @@ static inline int inat_last_prefix_id(insn_attr_t attr)
		return attr & INAT_PFX_MASK;
}

static inline int inat_is_vex_prefix(insn_attr_t attr)
{
	attr &= INAT_PFX_MASK;
	return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3;
}

static inline int inat_is_vex3_prefix(insn_attr_t attr)
{
	return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3;
}

static inline int inat_is_escape(insn_attr_t attr)
{
	return attr & INAT_ESC_MASK;
@@ -189,4 +208,13 @@ static inline int inat_has_variant(insn_attr_t attr)
	return attr & INAT_VARIANT;
}

static inline int inat_accept_vex(insn_attr_t attr)
{
	return attr & INAT_VEXOK;
}

static inline int inat_must_vex(insn_attr_t attr)
{
	return attr & INAT_VEXONLY;
}
#endif
+42 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ struct insn {
					 * prefixes.bytes[3]: last prefix
					 */
	struct insn_field rex_prefix;	/* REX prefix */
	struct insn_field vex_prefix;	/* VEX prefix */
	struct insn_field opcode;	/*
					 * opcode.bytes[0]: opcode1
					 * opcode.bytes[1]: opcode2
@@ -80,6 +81,19 @@ struct insn {
#define X86_REX_X(rex) ((rex) & 2)
#define X86_REX_B(rex) ((rex) & 1)

/* VEX bit flags  */
#define X86_VEX_W(vex)	((vex) & 0x80)	/* VEX3 Byte2 */
#define X86_VEX_R(vex)	((vex) & 0x80)	/* VEX2/3 Byte1 */
#define X86_VEX_X(vex)	((vex) & 0x40)	/* VEX3 Byte1 */
#define X86_VEX_B(vex)	((vex) & 0x20)	/* VEX3 Byte1 */
#define X86_VEX_L(vex)	((vex) & 0x04)	/* VEX3 Byte2, VEX2 Byte1 */
/* VEX bit fields */
#define X86_VEX3_M(vex)	((vex) & 0x1f)		/* VEX3 Byte1 */
#define X86_VEX2_M	1			/* VEX2.M always 1 */
#define X86_VEX_V(vex)	(((vex) & 0x78) >> 3)	/* VEX3 Byte2, VEX2 Byte1 */
#define X86_VEX_P(vex)	((vex) & 0x03)		/* VEX3 Byte2, VEX2 Byte1 */
#define X86_VEX_M_MAX	0x1f			/* VEX3.M Maximum value */

/* The last prefix is needed for two-byte and three-byte opcodes */
static inline insn_byte_t insn_last_prefix(struct insn *insn)
{
@@ -114,15 +128,42 @@ static inline void kernel_insn_init(struct insn *insn, const void *kaddr)
#endif
}

static inline int insn_is_avx(struct insn *insn)
{
	if (!insn->prefixes.got)
		insn_get_prefixes(insn);
	return (insn->vex_prefix.value != 0);
}

static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
{
	if (insn->vex_prefix.nbytes == 2)	/* 2 bytes VEX */
		return X86_VEX2_M;
	else
		return X86_VEX3_M(insn->vex_prefix.bytes[1]);
}

static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
{
	if (insn->vex_prefix.nbytes == 2)	/* 2 bytes VEX */
		return X86_VEX_P(insn->vex_prefix.bytes[1]);
	else
		return X86_VEX_P(insn->vex_prefix.bytes[2]);
}

/* Offset of each field from kaddr */
static inline int insn_offset_rex_prefix(struct insn *insn)
{
	return insn->prefixes.nbytes;
}
static inline int insn_offset_opcode(struct insn *insn)
static inline int insn_offset_vex_prefix(struct insn *insn)
{
	return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes;
}
static inline int insn_offset_opcode(struct insn *insn)
{
	return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes;
}
static inline int insn_offset_modrm(struct insn *insn)
{
	return insn_offset_opcode(insn) + insn->opcode.nbytes;
+12 −0
Original line number Diff line number Diff line
@@ -76,3 +76,15 @@ insn_attr_t inat_get_group_attribute(insn_byte_t modrm, insn_byte_t last_pfx,
	       inat_group_common_attribute(grp_attr);
}

insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
				   insn_byte_t vex_p)
{
	const insn_attr_t *table;
	if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
		return 0;
	table = inat_avx_tables[vex_m][vex_p];
	if (!table)
		return 0;
	return table[opcode];
}
+52 −0
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@
#define peek_next(t, insn)	\
	({t r; r = *(t*)insn->next_byte; r; })

#define peek_nbyte_next(t, insn, n)	\
	({t r; r = *(t*)((insn)->next_byte + n); r; })

/**
 * insn_init() - initialize struct insn
 * @insn:	&struct insn to be initialized
@@ -107,6 +110,7 @@ void insn_get_prefixes(struct insn *insn)
		insn->prefixes.bytes[3] = lb;
	}

	/* Decode REX prefix */
	if (insn->x86_64) {
		b = peek_next(insn_byte_t, insn);
		attr = inat_get_opcode_attribute(b);
@@ -120,6 +124,39 @@ void insn_get_prefixes(struct insn *insn)
		}
	}
	insn->rex_prefix.got = 1;

	/* Decode VEX prefix */
	b = peek_next(insn_byte_t, insn);
	attr = inat_get_opcode_attribute(b);
	if (inat_is_vex_prefix(attr)) {
		insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1);
		if (!insn->x86_64) {
			/*
			 * In 32-bits mode, if the [7:6] bits (mod bits of
			 * ModRM) on the second byte are not 11b, it is
			 * LDS or LES.
			 */
			if (X86_MODRM_MOD(b2) != 3)
				goto vex_end;
		}
		insn->vex_prefix.bytes[0] = b;
		insn->vex_prefix.bytes[1] = b2;
		if (inat_is_vex3_prefix(attr)) {
			b2 = peek_nbyte_next(insn_byte_t, insn, 2);
			insn->vex_prefix.bytes[2] = b2;
			insn->vex_prefix.nbytes = 3;
			insn->next_byte += 3;
			if (insn->x86_64 && X86_VEX_W(b2))
				/* VEX.W overrides opnd_size */
				insn->opnd_bytes = 8;
		} else {
			insn->vex_prefix.nbytes = 2;
			insn->next_byte += 2;
		}
	}
vex_end:
	insn->vex_prefix.got = 1;

	prefixes->got = 1;
	return;
}
@@ -147,6 +184,18 @@ void insn_get_opcode(struct insn *insn)
	op = get_next(insn_byte_t, insn);
	opcode->bytes[0] = op;
	opcode->nbytes = 1;

	/* Check if there is VEX prefix or not */
	if (insn_is_avx(insn)) {
		insn_byte_t m, p;
		m = insn_vex_m_bits(insn);
		p = insn_vex_p_bits(insn);
		insn->attr = inat_get_avx_attribute(op, m, p);
		if (!inat_accept_vex(insn->attr))
			insn->attr = 0;	/* This instruction is bad */
		goto end;	/* VEX has only 1 byte for opcode */
	}

	insn->attr = inat_get_opcode_attribute(op);
	while (inat_is_escape(insn->attr)) {
		/* Get escaped opcode */
@@ -155,6 +204,9 @@ void insn_get_opcode(struct insn *insn)
		pfx = insn_last_prefix(insn);
		insn->attr = inat_get_escape_attribute(op, pfx, insn->attr);
	}
	if (inat_must_vex(insn->attr))
		insn->attr = 0;	/* This instruction is bad */
end:
	opcode->got = 1;
}

+225 −206

File changed.

Preview size limit exceeded, changes collapsed.

Loading