Emulate the "ADD reg, r/m" instruction (opcode 03H).

OVMF's flash variable storage is using add instructions when indexing
the variable store bootrom location.

Submitted by:	D Scott Phillips <d.scott.phillips@intel.com>
Reviewed by:	rgrimes
MFC after:	1 week
Sponsored by:	Intel Corporation
Differential Revision:	https://reviews.freebsd.org/D19975
This commit is contained in:
jhb 2019-05-03 21:48:42 +00:00
parent e28330c77b
commit e24f761963

View File

@ -77,6 +77,7 @@ enum {
VIE_OP_TYPE_STOS,
VIE_OP_TYPE_BITTEST,
VIE_OP_TYPE_TWOB_GRP15,
VIE_OP_TYPE_ADD,
VIE_OP_TYPE_LAST
};
@ -112,6 +113,10 @@ static const struct vie_op two_byte_opcodes[256] = {
};
static const struct vie_op one_byte_opcodes[256] = {
[0x03] = {
.op_byte = 0x03,
.op_type = VIE_OP_TYPE_ADD,
},
[0x0F] = {
.op_byte = 0x0F,
.op_type = VIE_OP_TYPE_TWO_BYTE
@ -410,6 +415,41 @@ getcc(int opsize, uint64_t x, uint64_t y)
return (getcc64(x, y));
}
/*
* Macro creation of functions getaddflags{8,16,32,64}
*/
#define GETADDFLAGS(sz) \
static u_long \
getaddflags##sz(uint##sz##_t x, uint##sz##_t y) \
{ \
u_long rflags; \
\
__asm __volatile("add %2,%1; pushfq; popq %0" : \
"=r" (rflags), "+r" (x) : "m" (y)); \
return (rflags); \
} struct __hack
GETADDFLAGS(8);
GETADDFLAGS(16);
GETADDFLAGS(32);
GETADDFLAGS(64);
static u_long
getaddflags(int opsize, uint64_t x, uint64_t y)
{
KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
("getaddflags: invalid operand size %d", opsize));
if (opsize == 1)
return (getaddflags8(x, y));
else if (opsize == 2)
return (getaddflags16(x, y));
else if (opsize == 4)
return (getaddflags32(x, y));
else
return (getaddflags64(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)
@ -1178,6 +1218,62 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
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)
{
int error, size;
uint64_t nval, rflags, rflags2, val1, val2;
enum vm_reg_name reg;
size = vie->opsize;
error = EINVAL;
switch (vie->op.op_byte) {
case 0x03:
/*
* ADD r/m to r and store the result in r
*
* 03/r ADD r16, r/m16
* 03/r ADD r32, r/m32
* REX.W + 03/r ADD r64, r/m64
*/
/* get the first operand */
reg = gpr_map[vie->reg];
error = vie_read_register(vm, vcpuid, reg, &val1);
if (error)
break;
/* get the second operand */
error = memread(vm, vcpuid, gpa, &val2, size, arg);
if (error)
break;
/* perform the operation and write the result */
nval = val1 + val2;
error = vie_update_register(vm, vcpuid, reg, nval, size);
break;
default:
break;
}
if (!error) {
rflags2 = getaddflags(size, val1, val2);
error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
&rflags);
if (error)
return (error);
rflags &= ~RFLAGS_STATUS_BITS;
rflags |= rflags2 & RFLAGS_STATUS_BITS;
error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
rflags, 8);
}
return (error);
}
static int
emulate_sub(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
@ -1543,6 +1639,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
error = emulate_twob_group15(vm, vcpuid, gpa, vie,
memread, memwrite, memarg);
break;
case VIE_OP_TYPE_ADD:
error = emulate_add(vm, vcpuid, gpa, vie, memread,
memwrite, memarg);
break;
default:
error = EINVAL;
break;