mirror of
https://github.com/opnsense/src.git
synced 2026-06-10 17:22:46 -04:00
Emulate the "TEST r/m{16,32,64}, imm{16,32,32}" instructions (opcode F7H).
This adds emulation for: test r/m16, imm16 test r/m32, imm32 test r/m64, imm32 sign-extended to 64 OpenBSD guests compiled with clang 8.0.0 use TEST directly against a Local APIC register instead of separate read via MOV followed by a TEST against the register. PR: 238794 Submitted by: jhb Reported by: Jason Tubnor jason@tubnor.net Tested by: Jason Tubnor jason@tubnor.net Reviewed by: markj, Patrick Mooney patrick.mooney@joyent.com MFC after: 3 days Differential Revision: https://reviews.freebsd.org/D20755
This commit is contained in:
parent
5baf985da7
commit
e4da41f932
1 changed files with 95 additions and 0 deletions
|
|
@ -78,6 +78,7 @@ enum {
|
|||
VIE_OP_TYPE_BITTEST,
|
||||
VIE_OP_TYPE_TWOB_GRP15,
|
||||
VIE_OP_TYPE_ADD,
|
||||
VIE_OP_TYPE_TEST,
|
||||
VIE_OP_TYPE_LAST
|
||||
};
|
||||
|
||||
|
|
@ -221,6 +222,12 @@ static const struct vie_op one_byte_opcodes[256] = {
|
|||
.op_byte = 0x8F,
|
||||
.op_type = VIE_OP_TYPE_POP,
|
||||
},
|
||||
[0xF7] = {
|
||||
/* XXX Group 3 extended opcode - not just TEST */
|
||||
.op_byte = 0xF7,
|
||||
.op_type = VIE_OP_TYPE_TEST,
|
||||
.op_flags = VIE_OP_F_IMM,
|
||||
},
|
||||
[0xFF] = {
|
||||
/* XXX Group 5 extended opcode - not just PUSH */
|
||||
.op_byte = 0xFF,
|
||||
|
|
@ -450,6 +457,41 @@ getaddflags(int opsize, uint64_t x, uint64_t y)
|
|||
return (getaddflags64(x, y));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the status flags that would result from doing (x & y).
|
||||
*/
|
||||
#define GETANDFLAGS(sz) \
|
||||
static u_long \
|
||||
getandflags##sz(uint##sz##_t x, uint##sz##_t y) \
|
||||
{ \
|
||||
u_long rflags; \
|
||||
\
|
||||
__asm __volatile("and %2,%1; pushfq; popq %0" : \
|
||||
"=r" (rflags), "+r" (x) : "m" (y)); \
|
||||
return (rflags); \
|
||||
} struct __hack
|
||||
|
||||
GETANDFLAGS(8);
|
||||
GETANDFLAGS(16);
|
||||
GETANDFLAGS(32);
|
||||
GETANDFLAGS(64);
|
||||
|
||||
static u_long
|
||||
getandflags(int opsize, uint64_t x, uint64_t y)
|
||||
{
|
||||
KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
|
||||
("getandflags: invalid operand size %d", opsize));
|
||||
|
||||
if (opsize == 1)
|
||||
return (getandflags8(x, y));
|
||||
else if (opsize == 2)
|
||||
return (getandflags16(x, y));
|
||||
else if (opsize == 4)
|
||||
return (getandflags32(x, y));
|
||||
else
|
||||
return (getandflags64(x, y));
|
||||
}
|
||||
|
||||
static int
|
||||
emulate_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
|
||||
|
|
@ -1218,6 +1260,55 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
|||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
emulate_test(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
|
||||
{
|
||||
int error, size;
|
||||
uint64_t op1, rflags, rflags2;
|
||||
|
||||
size = vie->opsize;
|
||||
error = EINVAL;
|
||||
|
||||
switch (vie->op.op_byte) {
|
||||
case 0xF7:
|
||||
/*
|
||||
* F7 /0 test r/m16, imm16
|
||||
* F7 /0 test r/m32, imm32
|
||||
* REX.W + F7 /0 test r/m64, imm32 sign-extended to 64
|
||||
*
|
||||
* Test mem (ModRM:r/m) with immediate and set status
|
||||
* flags according to the results. The comparison is
|
||||
* performed by anding the immediate from the first
|
||||
* operand and then setting the status flags.
|
||||
*/
|
||||
if ((vie->reg & 7) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
error = memread(vm, vcpuid, gpa, &op1, size, arg);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
rflags2 = getandflags(size, op1, vie->immediate);
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* OF and CF are cleared; the SF, ZF and PF flags are set according
|
||||
* to the result; AF is undefined.
|
||||
*/
|
||||
rflags &= ~RFLAGS_STATUS_BITS;
|
||||
rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
|
||||
|
||||
error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
emulate_add(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
|
||||
|
|
@ -1643,6 +1734,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
|||
error = emulate_add(vm, vcpuid, gpa, vie, memread,
|
||||
memwrite, memarg);
|
||||
break;
|
||||
case VIE_OP_TYPE_TEST:
|
||||
error = emulate_test(vm, vcpuid, gpa, vie,
|
||||
memread, memwrite, memarg);
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in a new issue