Skip to content

Commit

Permalink
x86/insn: Fix vector instruction decoding on big endian cross-compiles
Browse files Browse the repository at this point in the history
Running instruction decoder posttest on an s390 host with an x86 target
with allyesconfig shows errors. Instructions used in a couple of kernel
objects could not be correctly decoded on big endian system.

  insn_decoder_test: warning: objdump says 6 bytes, but insn_get_length() says 5
  insn_decoder_test: warning: Found an x86 instruction decoder bug, please report this.
  insn_decoder_test: warning: ffffffff831eb4e1:    62 d1 fd 48 7f 04 24    vmovdqa64 %zmm0,(%r12)
  insn_decoder_test: warning: objdump says 7 bytes, but insn_get_length() says 6
  insn_decoder_test: warning: Found an x86 instruction decoder bug, please report this.
  insn_decoder_test: warning: ffffffff831eb4e8:    62 51 fd 48 7f 44 24 01         vmovdqa64 %zmm8,0x40(%r12)
  insn_decoder_test: warning: objdump says 8 bytes, but insn_get_length() says 6

This is because in a few places instruction field bytes are set directly
with further usage of "value". To address that introduce and use a
insn_set_byte() helper, which correctly updates "value" on big endian
systems.

Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
  • Loading branch information
Vasily Gorbik authored and Josh Poimboeuf committed Jan 14, 2021
1 parent 7786032 commit 5ed934e
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 18 deletions.
12 changes: 12 additions & 0 deletions arch/x86/include/asm/insn.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ static inline void insn_field_set(struct insn_field *p, insn_value_t v,
p->nbytes = n;
}

static inline void insn_set_byte(struct insn_field *p, unsigned char n,
insn_byte_t v)
{
p->bytes[n] = v;
}

#else

struct insn_field {
Expand All @@ -51,6 +57,12 @@ static inline void insn_field_set(struct insn_field *p, insn_value_t v,
p->nbytes = n;
}

static inline void insn_set_byte(struct insn_field *p, unsigned char n,
insn_byte_t v)
{
p->bytes[n] = v;
p->value = __le32_to_cpu(p->little);
}
#endif

struct insn {
Expand Down
18 changes: 9 additions & 9 deletions arch/x86/lib/insn.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ void insn_get_prefixes(struct insn *insn)
b = insn->prefixes.bytes[3];
for (i = 0; i < nb; i++)
if (prefixes->bytes[i] == lb)
prefixes->bytes[i] = b;
insn_set_byte(prefixes, i, b);
}
insn->prefixes.bytes[3] = lb;
insn_set_byte(&insn->prefixes, 3, lb);
}

/* Decode REX prefix */
Expand Down Expand Up @@ -194,21 +194,21 @@ void insn_get_prefixes(struct insn *insn)
if (X86_MODRM_MOD(b2) != 3)
goto vex_end;
}
insn->vex_prefix.bytes[0] = b;
insn->vex_prefix.bytes[1] = b2;
insn_set_byte(&insn->vex_prefix, 0, b);
insn_set_byte(&insn->vex_prefix, 1, b2);
if (inat_is_evex_prefix(attr)) {
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
insn->vex_prefix.bytes[2] = b2;
insn_set_byte(&insn->vex_prefix, 2, b2);
b2 = peek_nbyte_next(insn_byte_t, insn, 3);
insn->vex_prefix.bytes[3] = b2;
insn_set_byte(&insn->vex_prefix, 3, b2);
insn->vex_prefix.nbytes = 4;
insn->next_byte += 4;
if (insn->x86_64 && X86_VEX_W(b2))
/* VEX.W overrides opnd_size */
insn->opnd_bytes = 8;
} else if (inat_is_vex3_prefix(attr)) {
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
insn->vex_prefix.bytes[2] = b2;
insn_set_byte(&insn->vex_prefix, 2, b2);
insn->vex_prefix.nbytes = 3;
insn->next_byte += 3;
if (insn->x86_64 && X86_VEX_W(b2))
Expand All @@ -220,7 +220,7 @@ void insn_get_prefixes(struct insn *insn)
* Makes it easier to decode vex.W, vex.vvvv,
* vex.L and vex.pp. Masking with 0x7f sets vex.W == 0.
*/
insn->vex_prefix.bytes[2] = b2 & 0x7f;
insn_set_byte(&insn->vex_prefix, 2, b2 & 0x7f);
insn->vex_prefix.nbytes = 2;
insn->next_byte += 2;
}
Expand Down Expand Up @@ -256,7 +256,7 @@ void insn_get_opcode(struct insn *insn)

/* Get first opcode */
op = get_next(insn_byte_t, insn);
opcode->bytes[0] = op;
insn_set_byte(opcode, 0, op);
opcode->nbytes = 1;

/* Check if there is VEX prefix or not */
Expand Down
12 changes: 12 additions & 0 deletions tools/arch/x86/include/asm/insn.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ static inline void insn_field_set(struct insn_field *p, insn_value_t v,
p->nbytes = n;
}

static inline void insn_set_byte(struct insn_field *p, unsigned char n,
insn_byte_t v)
{
p->bytes[n] = v;
}

#else

struct insn_field {
Expand All @@ -51,6 +57,12 @@ static inline void insn_field_set(struct insn_field *p, insn_value_t v,
p->nbytes = n;
}

static inline void insn_set_byte(struct insn_field *p, unsigned char n,
insn_byte_t v)
{
p->bytes[n] = v;
p->value = __le32_to_cpu(p->little);
}
#endif

struct insn {
Expand Down
18 changes: 9 additions & 9 deletions tools/arch/x86/lib/insn.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ void insn_get_prefixes(struct insn *insn)
b = insn->prefixes.bytes[3];
for (i = 0; i < nb; i++)
if (prefixes->bytes[i] == lb)
prefixes->bytes[i] = b;
insn_set_byte(prefixes, i, b);
}
insn->prefixes.bytes[3] = lb;
insn_set_byte(&insn->prefixes, 3, lb);
}

/* Decode REX prefix */
Expand Down Expand Up @@ -194,21 +194,21 @@ void insn_get_prefixes(struct insn *insn)
if (X86_MODRM_MOD(b2) != 3)
goto vex_end;
}
insn->vex_prefix.bytes[0] = b;
insn->vex_prefix.bytes[1] = b2;
insn_set_byte(&insn->vex_prefix, 0, b);
insn_set_byte(&insn->vex_prefix, 1, b2);
if (inat_is_evex_prefix(attr)) {
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
insn->vex_prefix.bytes[2] = b2;
insn_set_byte(&insn->vex_prefix, 2, b2);
b2 = peek_nbyte_next(insn_byte_t, insn, 3);
insn->vex_prefix.bytes[3] = b2;
insn_set_byte(&insn->vex_prefix, 3, b2);
insn->vex_prefix.nbytes = 4;
insn->next_byte += 4;
if (insn->x86_64 && X86_VEX_W(b2))
/* VEX.W overrides opnd_size */
insn->opnd_bytes = 8;
} else if (inat_is_vex3_prefix(attr)) {
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
insn->vex_prefix.bytes[2] = b2;
insn_set_byte(&insn->vex_prefix, 2, b2);
insn->vex_prefix.nbytes = 3;
insn->next_byte += 3;
if (insn->x86_64 && X86_VEX_W(b2))
Expand All @@ -220,7 +220,7 @@ void insn_get_prefixes(struct insn *insn)
* Makes it easier to decode vex.W, vex.vvvv,
* vex.L and vex.pp. Masking with 0x7f sets vex.W == 0.
*/
insn->vex_prefix.bytes[2] = b2 & 0x7f;
insn_set_byte(&insn->vex_prefix, 2, b2 & 0x7f);
insn->vex_prefix.nbytes = 2;
insn->next_byte += 2;
}
Expand Down Expand Up @@ -256,7 +256,7 @@ void insn_get_opcode(struct insn *insn)

/* Get first opcode */
op = get_next(insn_byte_t, insn);
opcode->bytes[0] = op;
insn_set_byte(opcode, 0, op);
opcode->nbytes = 1;

/* Check if there is VEX prefix or not */
Expand Down

0 comments on commit 5ed934e

Please sign in to comment.