SVM: use generic instruction decoding

... instead of custom handling. To facilitate this break out init code
from _hvm_emulate_one() into the new hvm_emulate_init(), and make
hvmemul_insn_fetch( globally available.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
Jan Beulich committed 2016-09-30 17:11:56 +02:00
1 parent 82a9ae278c
commit 7e88dabffa
5 files changed
+234 -160

No files matched your search

+24 -12
View File
@@ -835,7 +835,7 @@ static int hvmemul_read(
container_of(ctxt, struct hvm_emulate_ctxt, ctxt));
}
static int hvmemul_insn_fetch(
int hvmemul_insn_fetch(
enum x86_segment seg,
unsigned long offset,
void *p_data,
@@ -1766,15 +1766,14 @@ static const struct x86_emulate_ops hvm_emulate_ops_no_write = {
.vmfunc = hvmemul_vmfunc,
};
static int _hvm_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt,
const struct x86_emulate_ops *ops)
void hvm_emulate_init(
struct hvm_emulate_ctxt *hvmemul_ctxt,
const unsigned char *insn_buf,
unsigned int insn_bytes)
{
struct cpu_user_regs *regs = hvmemul_ctxt->ctxt.regs;
struct vcpu *curr = current;
uint32_t new_intr_shadow, pfec = PFEC_page_present;
struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io;
unsigned int pfec = PFEC_page_present;
unsigned long addr;
int rc;
if ( hvm_long_mode_enabled(curr) &&
hvmemul_ctxt->seg_reg[x86_seg_cs].attr.fields.l )
@@ -1792,14 +1791,14 @@ static int _hvm_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt,
if ( hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3 )
pfec |= PFEC_user_mode;
hvmemul_ctxt->insn_buf_eip = regs->eip;
if ( !vio->mmio_insn_bytes )
hvmemul_ctxt->insn_buf_eip = hvmemul_ctxt->ctxt.regs->eip;
if ( !insn_bytes )
{
hvmemul_ctxt->insn_buf_bytes =
hvm_get_insn_bytes(curr, hvmemul_ctxt->insn_buf) ?:
(hvm_virtual_to_linear_addr(x86_seg_cs,
&hvmemul_ctxt->seg_reg[x86_seg_cs],
regs->eip,
hvmemul_ctxt->insn_buf_eip,
sizeof(hvmemul_ctxt->insn_buf),
hvm_access_insn_fetch,
hvmemul_ctxt->ctxt.addr_size,
@@ -1811,11 +1810,24 @@ static int _hvm_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt,
}
else
{
hvmemul_ctxt->insn_buf_bytes = vio->mmio_insn_bytes;
memcpy(hvmemul_ctxt->insn_buf, vio->mmio_insn, vio->mmio_insn_bytes);
hvmemul_ctxt->insn_buf_bytes = insn_bytes;
memcpy(hvmemul_ctxt->insn_buf, insn_buf, insn_bytes);
}
hvmemul_ctxt->exn_pending = 0;
}
static int _hvm_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt,
const struct x86_emulate_ops *ops)
{
const struct cpu_user_regs *regs = hvmemul_ctxt->ctxt.regs;
struct vcpu *curr = current;
uint32_t new_intr_shadow;
struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io;
int rc;
hvm_emulate_init(hvmemul_ctxt, vio->mmio_insn, vio->mmio_insn_bytes);
vio->mmio_retry = 0;
if ( cpu_has_vmx )
+70 -142
View File
@@ -15,7 +15,7 @@
* this program; If not, see <http://www.gnu.org/licenses/>.
*/
#include <xen/config.h>
#include <xen/err.h>
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/trace.h>
@@ -26,41 +26,6 @@
#include <asm/hvm/svm/vmcb.h>
#include <asm/hvm/svm/emulate.h>
static unsigned int is_prefix(u8 opc)
{
switch ( opc )
{
case 0x66:
case 0x67:
case 0x2E:
case 0x3E:
case 0x26:
case 0x64:
case 0x65:
case 0x36:
case 0xF0:
case 0xF3:
case 0xF2:
case 0x40 ... 0x4f:
return 1;
}
return 0;
}
static unsigned long svm_rip2pointer(struct vcpu *v, unsigned long *limit)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
unsigned long p = vmcb->cs.base + vmcb->rip;
if ( !(vmcb->cs.attr.fields.l && hvm_long_mode_enabled(v)) )
{
*limit = vmcb->cs.limit;
return (u32)p; /* mask to 32 bits */
}
*limit = ~0UL;
return p;
}
static unsigned long svm_nextrip_insn_length(struct vcpu *v)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
@@ -89,141 +54,104 @@ static unsigned long svm_nextrip_insn_length(struct vcpu *v)
return vmcb->nextrip - vmcb->rip;
}
/* First byte: Length. Following bytes: Opcode bytes. */
#define MAKE_INSTR(nm, ...) static const u8 OPCODE_##nm[] = { __VA_ARGS__ }
MAKE_INSTR(INVD, 2, 0x0f, 0x08);
MAKE_INSTR(WBINVD, 2, 0x0f, 0x09);
MAKE_INSTR(CPUID, 2, 0x0f, 0xa2);
MAKE_INSTR(RDMSR, 2, 0x0f, 0x32);
MAKE_INSTR(WRMSR, 2, 0x0f, 0x30);
MAKE_INSTR(VMCALL, 3, 0x0f, 0x01, 0xd9);
MAKE_INSTR(HLT, 1, 0xf4);
MAKE_INSTR(INT3, 1, 0xcc);
MAKE_INSTR(RDTSC, 2, 0x0f, 0x31);
MAKE_INSTR(PAUSE, 1, 0x90);
MAKE_INSTR(XSETBV, 3, 0x0f, 0x01, 0xd1);
MAKE_INSTR(VMRUN, 3, 0x0f, 0x01, 0xd8);
MAKE_INSTR(VMLOAD, 3, 0x0f, 0x01, 0xda);
MAKE_INSTR(VMSAVE, 3, 0x0f, 0x01, 0xdb);
MAKE_INSTR(STGI, 3, 0x0f, 0x01, 0xdc);
MAKE_INSTR(CLGI, 3, 0x0f, 0x01, 0xdd);
MAKE_INSTR(INVLPGA,3, 0x0f, 0x01, 0xdf);
static const u8 *const opc_bytes[INSTR_MAX_COUNT] =
{
[INSTR_INVD] = OPCODE_INVD,
[INSTR_WBINVD] = OPCODE_WBINVD,
[INSTR_CPUID] = OPCODE_CPUID,
[INSTR_RDMSR] = OPCODE_RDMSR,
[INSTR_WRMSR] = OPCODE_WRMSR,
[INSTR_VMCALL] = OPCODE_VMCALL,
[INSTR_HLT] = OPCODE_HLT,
[INSTR_INT3] = OPCODE_INT3,
[INSTR_RDTSC] = OPCODE_RDTSC,
[INSTR_PAUSE] = OPCODE_PAUSE,
[INSTR_XSETBV] = OPCODE_XSETBV,
[INSTR_VMRUN] = OPCODE_VMRUN,
[INSTR_VMLOAD] = OPCODE_VMLOAD,
[INSTR_VMSAVE] = OPCODE_VMSAVE,
[INSTR_STGI] = OPCODE_STGI,
[INSTR_CLGI] = OPCODE_CLGI,
[INSTR_INVLPGA] = OPCODE_INVLPGA,
static const struct {
unsigned int opcode;
struct {
unsigned int rm:3;
unsigned int reg:3;
unsigned int mod:2;
#define MODRM(mod, reg, rm) { rm, reg, mod }
} modrm;
} const opc_tab[INSTR_MAX_COUNT] = {
[INSTR_PAUSE] = { X86EMUL_OPC_F3(0, 0x90) },
[INSTR_INT3] = { X86EMUL_OPC( 0, 0xcc) },
[INSTR_HLT] = { X86EMUL_OPC( 0, 0xf4) },
[INSTR_XSETBV] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 2, 1) },
[INSTR_VMRUN] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 0) },
[INSTR_VMCALL] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 1) },
[INSTR_VMLOAD] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 2) },
[INSTR_VMSAVE] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 3) },
[INSTR_STGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 4) },
[INSTR_CLGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 5) },
[INSTR_INVLPGA] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 7) },
[INSTR_INVD] = { X86EMUL_OPC(0x0f, 0x08) },
[INSTR_WBINVD] = { X86EMUL_OPC(0x0f, 0x09) },
[INSTR_WRMSR] = { X86EMUL_OPC(0x0f, 0x30) },
[INSTR_RDTSC] = { X86EMUL_OPC(0x0f, 0x31) },
[INSTR_RDMSR] = { X86EMUL_OPC(0x0f, 0x32) },
[INSTR_CPUID] = { X86EMUL_OPC(0x0f, 0xa2) },
};
static bool_t fetch(const struct vmcb_struct *vmcb, u8 *buf,
unsigned long addr, unsigned int len)
{
uint32_t pfec = (vmcb_get_cpl(vmcb) == 3) ? PFEC_user_mode : 0;
switch ( hvm_fetch_from_guest_virt(buf, addr, len, pfec) )
{
case HVMCOPY_okay:
break;
case HVMCOPY_bad_gva_to_gfn:
/* OK just to give up; we'll have injected #PF already */
return 0;
default:
/* Not OK: fetches from non-RAM pages are not supportable. */
gdprintk(XENLOG_WARNING, "Bad instruction fetch at %#lx (%#lx)\n",
vmcb->rip, addr);
hvm_inject_hw_exception(TRAP_gp_fault, 0);
return 0;
}
return 1;
}
int __get_instruction_length_from_list(struct vcpu *v,
const enum instruction_index *list, unsigned int list_count)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
unsigned int i, j, inst_len = 0;
enum instruction_index instr = 0;
u8 buf[MAX_INST_LEN];
const u8 *opcode = NULL;
unsigned long fetch_addr, fetch_limit;
unsigned int fetch_len, max_len;
struct hvm_emulate_ctxt ctxt;
struct x86_emulate_state *state;
unsigned int inst_len, j, modrm_rm, modrm_reg;
int modrm_mod;
/*
* In debug builds, always use x86_decode_insn() and compare with
* hardware.
*/
#ifdef NDEBUG
if ( (inst_len = svm_nextrip_insn_length(v)) != 0 )
return inst_len;
if ( vmcb->exitcode == VMEXIT_IOIO )
return vmcb->exitinfo2 - vmcb->rip;
#endif
/* Fetch up to the next page break; we'll fetch from the next page
* later if we have to. */
fetch_addr = svm_rip2pointer(v, &fetch_limit);
if ( vmcb->rip > fetch_limit )
return 0;
max_len = min(fetch_limit - vmcb->rip + 1, MAX_INST_LEN + 0UL);
fetch_len = min_t(unsigned int, max_len,
PAGE_SIZE - (fetch_addr & ~PAGE_MASK));
if ( !fetch(vmcb, buf, fetch_addr, fetch_len) )
ASSERT(v == current);
hvm_emulate_prepare(&ctxt, guest_cpu_user_regs());
hvm_emulate_init(&ctxt, NULL, 0);
state = x86_decode_insn(&ctxt.ctxt, hvmemul_insn_fetch);
if ( IS_ERR_OR_NULL(state) )
return 0;
while ( (inst_len < max_len) && is_prefix(buf[inst_len]) )
inst_len = x86_insn_length(state, &ctxt.ctxt);
modrm_mod = x86_insn_modrm(state, &modrm_rm, &modrm_reg);
x86_emulate_free_state(state);
#ifndef NDEBUG
if ( vmcb->exitcode == VMEXIT_IOIO )
j = vmcb->exitinfo2 - vmcb->rip;
else
j = svm_nextrip_insn_length(v);
if ( j && j != inst_len )
{
inst_len++;
if ( inst_len >= fetch_len )
{
if ( !fetch(vmcb, buf + fetch_len, fetch_addr + fetch_len,
max_len - fetch_len) )
return 0;
fetch_len = max_len;
}
gprintk(XENLOG_WARNING, "insn-len[%02x]=%u (exp %u)\n",
ctxt.ctxt.opcode, inst_len, j);
return j;
}
#endif
for ( j = 0; j < list_count; j++ )
{
instr = list[j];
opcode = opc_bytes[instr];
unsigned int instr = list[j];
for ( i = 0; (i < opcode[0]) && ((inst_len + i) < max_len); i++ )
if ( instr >= ARRAY_SIZE(opc_tab) )
{
if ( (inst_len + i) >= fetch_len )
{
if ( !fetch(vmcb, buf + fetch_len, fetch_addr + fetch_len,
max_len - fetch_len) )
return 0;
fetch_len = max_len;
}
if ( buf[inst_len+i] != opcode[i+1] )
goto mismatch;
ASSERT_UNREACHABLE();
break;
}
if ( opc_tab[instr].opcode == ctxt.ctxt.opcode )
{
if ( !opc_tab[instr].modrm.mod )
return inst_len;
if ( modrm_mod == opc_tab[instr].modrm.mod &&
(modrm_rm & 7) == opc_tab[instr].modrm.rm &&
(modrm_reg & 7) == opc_tab[instr].modrm.reg )
return inst_len;
}
goto done;
mismatch: ;
}
gdprintk(XENLOG_WARNING,
"%s: Mismatch between expected and actual instruction bytes: "
"%s: Mismatch between expected and actual instruction: "
"eip = %lx\n", __func__, (unsigned long)vmcb->rip);
hvm_inject_hw_exception(TRAP_gp_fault, 0);
return 0;
done:
inst_len += opcode[0];
ASSERT(inst_len <= max_len);
return inst_len;
}
/*
+106 -6
View File
@@ -382,9 +382,9 @@ struct operand {
} mem;
};
#ifdef __x86_64__
#define REG_POISON ((unsigned long *) 0x8086000000008086UL) /* non-canonical */
#define PTR_POISON ((void *)0x8086000000008086UL) /* non-canonical */
#else
#define REG_POISON NULL /* 32-bit builds are for user-space, so NULL is OK. */
#define PTR_POISON NULL /* 32-bit builds are for user-space, so NULL is OK. */
#endif
typedef union {
@@ -1658,6 +1658,14 @@ struct x86_emulate_state {
unsigned long eip;
struct cpu_user_regs *regs;
#ifndef NDEBUG
/*
* Track caller of x86_decode_insn() to spot missing as well as
* premature calls to x86_emulate_free_state().
*/
void *caller;
#endif
};
/* Helper definitions. */
@@ -1685,6 +1693,11 @@ x86_decode_onebyte(
switch ( ctxt->opcode )
{
case 0x90: /* nop / pause */
if ( repe_prefix() )
ctxt->opcode |= X86EMUL_OPC_F3(0, 0);
break;
case 0x9a: /* call (far, absolute) */
case 0xea: /* jmp (far, absolute) */
generate_exception_if(mode_64bit(), EXC_UD, -1);
@@ -1792,7 +1805,7 @@ x86_decode(
override_seg = -1;
ea.type = OP_MEM;
ea.mem.seg = x86_seg_ds;
ea.reg = REG_POISON;
ea.reg = PTR_POISON;
state->regs = ctxt->regs;
state->eip = ctxt->regs->eip;
@@ -2304,8 +2317,8 @@ x86_emulate(
int rc;
uint8_t b, d;
bool tf = ctxt->regs->eflags & EFLG_TF;
struct operand src = { .reg = REG_POISON };
struct operand dst = { .reg = REG_POISON };
struct operand src = { .reg = PTR_POISON };
struct operand dst = { .reg = PTR_POISON };
enum x86_swint_type swint_type;
struct x86_emulate_stub stub = {};
DECLARE_ALIGNED(mmval_t, mmval);
@@ -2910,8 +2923,9 @@ x86_emulate(
break;
case 0x90: /* nop / xchg %%r8,%%rax */
case X86EMUL_OPC_F3(0, 0x90): /* pause / xchg %%r8,%%rax */
if ( !(rex_prefix & 1) )
break; /* nop */
break; /* nop / pause */
/* fall through */
case 0x91 ... 0x97: /* xchg reg,%%rax */
@@ -5312,3 +5326,89 @@ x86_emulate(
#undef vex
#undef override_seg
#undef ea
#ifdef __XEN__
#include <xen/err.h>
struct x86_emulate_state *
x86_decode_insn(
struct x86_emulate_ctxt *ctxt,
int (*insn_fetch)(
enum x86_segment seg, unsigned long offset,
void *p_data, unsigned int bytes,
struct x86_emulate_ctxt *ctxt))
{
static DEFINE_PER_CPU(struct x86_emulate_state, state);
struct x86_emulate_state *state = &this_cpu(state);
const struct x86_emulate_ops ops = {
.insn_fetch = insn_fetch,
.read = x86emul_unhandleable_rw,
.write = PTR_POISON,
.cmpxchg = PTR_POISON,
};
int rc = x86_decode(state, ctxt, &ops);
if ( unlikely(rc != X86EMUL_OKAY) )
return ERR_PTR(-rc);
#ifndef NDEBUG
/*
* While we avoid memory allocation (by use of per-CPU data) above,
* nevertheless make sure callers properly release the state structure
* for forward compatibility.
*/
if ( state->caller )
{
printk(XENLOG_ERR "Unreleased emulation state acquired by %ps\n",
state->caller);
dump_execution_state();
}
state->caller = __builtin_return_address(0);
#endif
return state;
}
static inline void check_state(const struct x86_emulate_state *state)
{
#ifndef NDEBUG
ASSERT(state->caller);
#endif
}
#ifndef NDEBUG
void x86_emulate_free_state(struct x86_emulate_state *state)
{
check_state(state);
state->caller = NULL;
}
#endif
int
x86_insn_modrm(const struct x86_emulate_state *state,
unsigned int *rm, unsigned int *reg)
{
check_state(state);
if ( !(state->desc & ModRM) )
return -EINVAL;
if ( rm )
*rm = state->modrm_rm;
if ( reg )
*reg = state->modrm_reg;
return state->modrm_mod;
}
unsigned int
x86_insn_length(const struct x86_emulate_state *state,
const struct x86_emulate_ctxt *ctxt)
{
check_state(state);
return state->eip - ctxt->regs->eip;
}
#endif
+25
View File
@@ -532,4 +532,29 @@ x86emul_unhandleable_rw(
unsigned int bytes,
struct x86_emulate_ctxt *ctxt);
#ifdef __XEN__
struct x86_emulate_state *
x86_decode_insn(
struct x86_emulate_ctxt *ctxt,
int (*insn_fetch)(
enum x86_segment seg, unsigned long offset,
void *p_data, unsigned int bytes,
struct x86_emulate_ctxt *ctxt));
int
x86_insn_modrm(const struct x86_emulate_state *state,
unsigned int *rm, unsigned int *reg);
unsigned int
x86_insn_length(const struct x86_emulate_state *state,
const struct x86_emulate_ctxt *ctxt);
#ifdef NDEBUG
static inline void x86_emulate_free_state(struct x86_emulate_state *state) {}
#else
void x86_emulate_free_state(struct x86_emulate_state *state);
#endif
#endif
#endif /* __X86_EMULATE_H__ */
+9
View File
@@ -54,6 +54,10 @@ void hvm_emulate_one_vm_event(enum emul_kind kind,
void hvm_emulate_prepare(
struct hvm_emulate_ctxt *hvmemul_ctxt,
struct cpu_user_regs *regs);
void hvm_emulate_init(
struct hvm_emulate_ctxt *hvmemul_ctxt,
const unsigned char *insn_buf,
unsigned int insn_bytes);
void hvm_emulate_writeback(
struct hvm_emulate_ctxt *hvmemul_ctxt);
struct segment_register *hvmemul_get_seg_reg(
@@ -61,6 +65,11 @@ struct segment_register *hvmemul_get_seg_reg(
struct hvm_emulate_ctxt *hvmemul_ctxt);
int hvm_emulate_one_mmio(unsigned long mfn, unsigned long gla);
int hvmemul_insn_fetch(enum x86_segment seg,
unsigned long offset,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt);
int hvmemul_do_pio_buffer(uint16_t port,
unsigned int size,
uint8_t dir,
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.