vmm(4): Decode and emulate BEXTR
Clang 10 -march=native kernels on znver1 emit BEXTR for APIC reads, apparently. Decode and emulate the instruction. Reviewed by: grehan Differential Revision: https://reviews.freebsd.org/D24463
This commit is contained in:
parent
cfdea69d24
commit
47332982bc
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=360177
@ -84,6 +84,7 @@ enum {
|
||||
VIE_OP_TYPE_TWOB_GRP15,
|
||||
VIE_OP_TYPE_ADD,
|
||||
VIE_OP_TYPE_TEST,
|
||||
VIE_OP_TYPE_BEXTR,
|
||||
VIE_OP_TYPE_LAST
|
||||
};
|
||||
|
||||
@ -95,6 +96,10 @@ enum {
|
||||
#define VIE_OP_F_NO_GLA_VERIFICATION (1 << 4)
|
||||
|
||||
static const struct vie_op three_byte_opcodes_0f38[256] = {
|
||||
[0xF7] = {
|
||||
.op_byte = 0xF7,
|
||||
.op_type = VIE_OP_TYPE_BEXTR,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct vie_op two_byte_opcodes[256] = {
|
||||
@ -1317,6 +1322,83 @@ emulate_test(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
emulate_bextr(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
struct vm_guest_paging *paging, mem_region_read_t memread,
|
||||
mem_region_write_t memwrite, void *arg)
|
||||
{
|
||||
uint64_t src1, src2, dst, rflags;
|
||||
unsigned start, len;
|
||||
int error, size;
|
||||
|
||||
size = vie->opsize;
|
||||
error = EINVAL;
|
||||
|
||||
/*
|
||||
* VEX.LZ.0F38.W0 F7 /r BEXTR r32a, r/m32, r32b
|
||||
* VEX.LZ.0F38.W1 F7 /r BEXTR r64a, r/m64, r64b
|
||||
*
|
||||
* Destination operand is ModRM:reg. Source operands are ModRM:r/m and
|
||||
* Vex.vvvv.
|
||||
*
|
||||
* Operand size is always 32-bit if not in 64-bit mode (W1 is ignored).
|
||||
*/
|
||||
if (size != 4 && paging->cpu_mode != CPU_MODE_64BIT)
|
||||
size = 4;
|
||||
|
||||
/*
|
||||
* Extracts contiguous bits from the first /source/ operand (second
|
||||
* operand) using an index and length specified in the second /source/
|
||||
* operand (third operand).
|
||||
*/
|
||||
error = memread(vm, vcpuid, gpa, &src1, size, arg);
|
||||
if (error)
|
||||
return (error);
|
||||
error = vie_read_register(vm, vcpuid, gpr_map[vie->vex_reg], &src2);
|
||||
if (error)
|
||||
return (error);
|
||||
error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
start = (src2 & 0xff);
|
||||
len = (src2 & 0xff00) >> 8;
|
||||
|
||||
/* If no bits are extracted, the destination register is cleared. */
|
||||
dst = 0;
|
||||
|
||||
/* If START exceeds the operand size, no bits are extracted. */
|
||||
if (start > size * 8)
|
||||
goto done;
|
||||
/* Length is bounded by both the destination size and start offset. */
|
||||
if (start + len > size * 8)
|
||||
len = (size * 8) - start;
|
||||
if (len == 0)
|
||||
goto done;
|
||||
|
||||
if (start > 0)
|
||||
src1 = (src1 >> start);
|
||||
if (len < 64)
|
||||
src1 = src1 & ((1ull << len) - 1);
|
||||
dst = src1;
|
||||
|
||||
done:
|
||||
error = vie_update_register(vm, vcpuid, gpr_map[vie->reg], dst, size);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* AMD: OF, CF cleared; SF/AF/PF undefined; ZF set by result.
|
||||
* Intel: ZF is set by result; AF/SF/PF undefined; all others cleared.
|
||||
*/
|
||||
rflags &= ~RFLAGS_STATUS_BITS;
|
||||
if (dst == 0)
|
||||
rflags |= PSL_Z;
|
||||
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)
|
||||
@ -1746,6 +1828,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
error = emulate_test(vm, vcpuid, gpa, vie,
|
||||
memread, memwrite, memarg);
|
||||
break;
|
||||
case VIE_OP_TYPE_BEXTR:
|
||||
error = emulate_bextr(vm, vcpuid, gpa, vie, paging,
|
||||
memread, memwrite, memarg);
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user