Skip to content

Commit

Permalink
Added support for CMPS{B,W,D,Q} instructions
Browse files Browse the repository at this point in the history
- Renamed `INSN_MOV` to `INSN_DST_NR` for instructions that do not read
the destination operand: Instruction flags should signal behavioral
quirks of the instruction, not just an alternative way of identifying a
particular instruction (mov in this case).

- Created `INSN_DST_NW` flag for instructions that do not read the
destination operand. Name is consistent with the change mentioned above.

- Added `INSN_DST_NW` flag to cmp/test instructions as a small
optimization. It will prevent unnecessary (and possibly illegal?) memory
writes from occurring during these instructions.

- Implemented `cmps` in the instruction emulator, fixing issue intel#175.

- Added tests for `cmps` for both regular and REPx variants.

Signed-off-by: Alexandro Sanchez Bach <[email protected]>
  • Loading branch information
AlexAltea authored and raphaelning committed Feb 21, 2019
1 parent 5fdebb5 commit 7a5c392
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 31 deletions.
66 changes: 35 additions & 31 deletions core/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,26 @@
#include "include/emulate.h"

/* Instruction flags */
/* Instruction ignores destination original value */
#define INSN_MOV ((uint64_t)1 << 0)
/* Instruction does not read from destination */
#define INSN_DST_NR ((uint64_t)1 << 0)
/* Instruction does not write to destination */
#define INSN_DST_NW ((uint64_t)1 << 1)
/* Instruction expects ModRM byte */
#define INSN_MODRM ((uint64_t)1 << 1)
#define INSN_MODRM ((uint64_t)1 << 2)
/* Instruction accesses 1-byte registers */
#define INSN_BYTEOP ((uint64_t)1 << 2)
#define INSN_BYTEOP ((uint64_t)1 << 3)
/* Instruction opcode is extended via ModRM byte */
#define INSN_GROUP ((uint64_t)1 << 3)
#define INSN_GROUP ((uint64_t)1 << 4)
/* Instruction supports REP prefixes */
#define INSN_REP ((uint64_t)1 << 4)
#define INSN_REP ((uint64_t)1 << 5)
/* Instruction supports REPE/REPNE prefixes */
#define INSN_REPX ((uint64_t)1 << 5)
#define INSN_REPX ((uint64_t)1 << 6)
/* Instruction ignores flags */
#define INSN_NOFLAGS ((uint64_t)1 << 6)
#define INSN_NOFLAGS ((uint64_t)1 << 7)
/* Instruction has two memory operands */
#define INSN_TWOMEM ((uint64_t)1 << 7)
#define INSN_TWOMEM ((uint64_t)1 << 8)
/* Instruction takes bit test operands */
#define INSN_BITOP ((uint64_t)1 << 8)
#define INSN_BITOP ((uint64_t)1 << 9)
/* String instruction */
#define INSN_STRING (INSN_REP|INSN_REPX)

Expand Down Expand Up @@ -161,8 +163,8 @@ static const struct em_opcode_t opcode_group1[8] = {
};

static const struct em_opcode_t opcode_group3[8] = {
F(em_test, op_modrm_rm, op_simm, op_none, 0),
F(em_test, op_modrm_rm, op_simm, op_none, 0),
F(em_test, op_modrm_rm, op_simm, op_none, INSN_DST_NW),
F(em_test, op_modrm_rm, op_simm, op_none, INSN_DST_NW),
F(em_not, op_modrm_rm, op_none, op_none, 0),
F(em_neg, op_modrm_rm, op_none, op_none, 0),
};
Expand All @@ -176,7 +178,7 @@ static const struct em_opcode_t opcode_group8[8] = {
};

static const struct em_opcode_t opcode_group11[8] = {
I(em_mov, op_none, op_none, op_none, INSN_MOV),
I(em_mov, op_none, op_none, op_none, INSN_DST_NR),
};

static const struct em_opcode_t opcode_table[256] = {
Expand All @@ -195,7 +197,7 @@ static const struct em_opcode_t opcode_table[256] = {
/* 0x30 - 0x37 */
F6_ALU(em_xor, 0), X2(N),
/* 0x38 - 0x3F */
F6_ALU(em_cmp, 0), X2(N),
F6_ALU(em_cmp, INSN_DST_NW), X2(N),
/* 0x40 - 0x47 */
X8(F(em_inc, op_modrm_reg, op_none, op_none, 0)),
/* 0x48 - 0x4F */
Expand All @@ -207,21 +209,21 @@ static const struct em_opcode_t opcode_table[256] = {
G(opcode_group1, op_modrm_rm, op_simm, op_none, 0),
G(opcode_group1, op_modrm_rm, op_simm, op_none, INSN_BYTEOP),
G(opcode_group1, op_modrm_rm, op_simm8, op_none, 0),
F2_BV(em_test, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM),
F2_BV(em_test, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM | INSN_DST_NW),
X2(N), /* TODO: 0x86 & 0x87 (XCHG) */
I2_BV(em_mov, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM | INSN_MOV),
I2_BV(em_mov, op_modrm_reg, op_modrm_rm, op_none, INSN_MODRM | INSN_MOV),
I2_BV(em_mov, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM | INSN_DST_NR),
I2_BV(em_mov, op_modrm_reg, op_modrm_rm, op_none, INSN_MODRM | INSN_DST_NR),
X4(N),
/* 0x90 - 0x9F */
X16(N),
/* 0xA0 - 0xAF */
I2_BV(em_mov, op_acc, op_moffs, op_none, INSN_MOV),
I2_BV(em_mov, op_moffs, op_acc, op_none, INSN_MOV),
I2_BV(em_mov, op_di, op_si, op_none, INSN_MOV | INSN_REP | INSN_TWOMEM), /* movs{b,w,d,q} */
I2_BV(em_mov, op_acc, op_moffs, op_none, INSN_DST_NR),
I2_BV(em_mov, op_moffs, op_acc, op_none, INSN_DST_NR),
I2_BV(em_mov, op_di, op_si, op_none, INSN_DST_NR | INSN_REP | INSN_TWOMEM), /* movs{b,w,d,q} */
F2_BV(em_cmp, op_di, op_si, op_none, INSN_DST_NW | INSN_REPX | INSN_TWOMEM), /* cmps{b,w,d,q} */
X2(N),
X2(N),
I2_BV(em_mov, op_di, op_acc, op_none, INSN_MOV | INSN_REP), /* stos{b,w,d,q} */
I2_BV(em_mov, op_acc, op_si, op_none, INSN_MOV | INSN_REP), /* lods{b,w,d,q} */
I2_BV(em_mov, op_di, op_acc, op_none, INSN_DST_NR | INSN_REP), /* stos{b,w,d,q} */
I2_BV(em_mov, op_acc, op_si, op_none, INSN_DST_NR | INSN_REP), /* lods{b,w,d,q} */
X2(N),
/* 0xB0 - 0xBF */
X16(N),
Expand Down Expand Up @@ -256,14 +258,14 @@ static const struct em_opcode_t opcode_table_0F[256] = {
X3(N),
F(em_btr, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM | INSN_BITOP),
X2(N),
I(em_movzx, op_modrm_reg, op_modrm_rm8, op_none, INSN_MODRM | INSN_MOV),
I(em_movzx, op_modrm_reg, op_modrm_rm16, op_none, INSN_MODRM | INSN_MOV),
I(em_movzx, op_modrm_reg, op_modrm_rm8, op_none, INSN_MODRM | INSN_DST_NR),
I(em_movzx, op_modrm_reg, op_modrm_rm16, op_none, INSN_MODRM | INSN_DST_NR),
X2(N),
G(opcode_group8, op_modrm_rm, op_simm8, op_none, INSN_BITOP),
F(em_btc, op_modrm_rm, op_modrm_reg, op_none, INSN_MODRM | INSN_BITOP),
X2(N),
I(em_movsx, op_modrm_reg, op_modrm_rm8, op_none, INSN_MODRM | INSN_MOV),
I(em_movsx, op_modrm_reg, op_modrm_rm16, op_none, INSN_MODRM | INSN_MOV),
I(em_movsx, op_modrm_reg, op_modrm_rm8, op_none, INSN_MODRM | INSN_DST_NR),
I(em_movsx, op_modrm_reg, op_modrm_rm16, op_none, INSN_MODRM | INSN_DST_NR),
/* 0xC0 - 0xFF */
X16(N), X16(N), X16(N), X16(N),
};
Expand Down Expand Up @@ -1115,7 +1117,7 @@ em_status_t EMCALL em_emulate_insn(struct em_context_t *ctxt)
if (rc != EM_CONTINUE)
goto exit;
} else {
if (!(opcode->flags & INSN_MOV)) {
if (!(opcode->flags & INSN_DST_NR)) {
rc = operand_read(ctxt, &ctxt->dst);
if (rc != EM_CONTINUE)
goto exit;
Expand Down Expand Up @@ -1151,9 +1153,11 @@ em_status_t EMCALL em_emulate_insn(struct em_context_t *ctxt)
if (!(opcode->flags & INSN_NOFLAGS)) {
ctxt->ops->write_rflags(ctxt->vcpu, ctxt->rflags);
}
rc = operand_write(ctxt, &ctxt->dst);
if (rc != EM_CONTINUE)
goto exit;
if (!(opcode->flags & INSN_DST_NW)) {
rc = operand_write(ctxt, &ctxt->dst);
if (rc != EM_CONTINUE)
goto exit;
}

if (opcode->decode_dst == decode_op_di) {
register_add(ctxt, REG_RDI, ctxt->operand_size *
Expand Down
34 changes: 34 additions & 0 deletions tests/test_emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,40 @@ TEST_F(EmulatorTest, insn_bts) {
run("bts [rcx + 0x08], rax", vcpu_original, vcpu_expected);
}

TEST_F(EmulatorTest, insn_cmps) {
test_cpu_t vcpu_original;
test_cpu_t vcpu_expected;

// Test: cmpsw, without-rep, with-df
vcpu_original = {};
vcpu_original.gpr[REG_RSI] = 0x10;
vcpu_original.gpr[REG_RDI] = 0x50;
vcpu_original.flags = RFLAGS_DF;
(uint16_t&)vcpu_original.mem[0x10] = 0x1234;
(uint16_t&)vcpu_original.mem[0x50] = 0x1234;
vcpu_expected = vcpu_original;
vcpu_expected.gpr[REG_RSI] -= 2;
vcpu_expected.gpr[REG_RDI] -= 2;
vcpu_expected.flags = RFLAGS_DF | RFLAGS_ZF | RFLAGS_PF;
run("cmpsw", vcpu_original, vcpu_expected);

// Test: cmpsw, with-rep, without-df
vcpu_original = {};
vcpu_original.gpr[REG_RSI] = 0x20;
vcpu_original.gpr[REG_RDI] = 0x80;
vcpu_original.gpr[REG_RCX] = 0x3;
vcpu_original.mem[0x20] = 0x11;
vcpu_original.mem[0x21] = 0x22;
vcpu_original.mem[0x80] = 0x11;
vcpu_original.mem[0x81] = 0x33;
vcpu_expected = vcpu_original;
vcpu_expected.gpr[REG_RSI] += 0x2;
vcpu_expected.gpr[REG_RDI] += 0x2;
vcpu_expected.gpr[REG_RCX] = 0x1;
vcpu_expected.flags = RFLAGS_PF;
run("repe cmpsb", vcpu_original, vcpu_expected);
}

TEST_F(EmulatorTest, insn_movs) {
test_cpu_t vcpu_original;
test_cpu_t vcpu_expected;
Expand Down

0 comments on commit 7a5c392

Please sign in to comment.