mirror of
https://github.com/xen-project/xen.git
synced 2026-10-30 03:00:05 +01:00
x86/PV: use generic emulator for privileged instruction handling
There's a new emulator return code being added to allow bypassing certain operations (see the code comment). Another small tweak to the emulator is to single iteration handling of INS and OUTS: Since we don't want to handle any other memory access instructions, we want these to be handled by the rep_ins() / rep_outs() hooks here too. And then long-mode related bits now get hidden from the guest. This should have been that way from the beginning, but becomes a requirement now as the emulator's in_longmode() needs this to reflect guest view. Signed-off-by: Jan Beulich <jbeulich@suse.com> Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
This commit is contained in:
1 parent
36a808eeb2
commit
4999bf3e8b
3 files changed
+664
-496
No files matched your search
+572
-482
File diff suppressed because it is too large.
Load diff
@@ -1184,8 +1184,12 @@ static int ioport_access_check(
|
||||
return X86EMUL_OKAY;
|
||||
|
||||
fail_if(ops->read_segment == NULL);
|
||||
/*
|
||||
* X86EMUL_DONE coming back here may be used to defer the port
|
||||
* permission check to the respective ioport hook.
|
||||
*/
|
||||
if ( (rc = ops->read_segment(x86_seg_tr, &tr, ctxt)) != 0 )
|
||||
return rc;
|
||||
return rc == X86EMUL_DONE ? X86EMUL_OKAY : rc;
|
||||
|
||||
/* Ensure the TSS has an io-bitmap-offset field. */
|
||||
generate_exception_if(tr.attr.fields.type != 0xb, EXC_GP, 0);
|
||||
@@ -2506,6 +2510,21 @@ x86_emulate(
|
||||
/* Sync rIP to post decode value. */
|
||||
_regs.eip = state.eip;
|
||||
|
||||
if ( ops->validate )
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
state.caller = __builtin_return_address(0);
|
||||
#endif
|
||||
rc = ops->validate(&state, ctxt);
|
||||
#ifndef NDEBUG
|
||||
state.caller = NULL;
|
||||
#endif
|
||||
if ( rc == X86EMUL_DONE )
|
||||
goto no_writeback;
|
||||
if ( rc != X86EMUL_OKAY )
|
||||
return rc;
|
||||
}
|
||||
|
||||
b = ctxt->opcode;
|
||||
d = state.desc;
|
||||
#define state (&state)
|
||||
@@ -2935,13 +2954,28 @@ x86_emulate(
|
||||
dst.mem.off = truncate_ea_and_reps(_regs.edi, nr_reps, dst.bytes);
|
||||
if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
|
||||
goto done;
|
||||
if ( (nr_reps == 1) || !ops->rep_ins ||
|
||||
((rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
|
||||
&nr_reps, ctxt)) == X86EMUL_UNHANDLEABLE) )
|
||||
/* Try the presumably most efficient approach first. */
|
||||
if ( !ops->rep_ins )
|
||||
nr_reps = 1;
|
||||
rc = X86EMUL_UNHANDLEABLE;
|
||||
if ( nr_reps == 1 && ops->read_io && ops->write )
|
||||
{
|
||||
fail_if(ops->read_io == NULL);
|
||||
rc = ops->read_io(port, dst.bytes, &dst.val, ctxt);
|
||||
if ( rc == X86EMUL_OKAY )
|
||||
nr_reps = 0;
|
||||
}
|
||||
if ( (nr_reps > 1 || rc == X86EMUL_UNHANDLEABLE) && ops->rep_ins )
|
||||
rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
|
||||
&nr_reps, ctxt);
|
||||
if ( nr_reps >= 1 && rc == X86EMUL_UNHANDLEABLE )
|
||||
{
|
||||
fail_if(!ops->read_io || !ops->write);
|
||||
if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
|
||||
goto done;
|
||||
nr_reps = 0;
|
||||
}
|
||||
if ( !nr_reps && rc == X86EMUL_OKAY )
|
||||
{
|
||||
dst.type = OP_MEM;
|
||||
nr_reps = 1;
|
||||
}
|
||||
@@ -2959,14 +2993,30 @@ x86_emulate(
|
||||
ea.mem.off = truncate_ea_and_reps(_regs.esi, nr_reps, dst.bytes);
|
||||
if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
|
||||
goto done;
|
||||
if ( (nr_reps == 1) || !ops->rep_outs ||
|
||||
((rc = ops->rep_outs(ea.mem.seg, ea.mem.off, port, dst.bytes,
|
||||
&nr_reps, ctxt)) == X86EMUL_UNHANDLEABLE) )
|
||||
/* Try the presumably most efficient approach first. */
|
||||
if ( !ops->rep_outs )
|
||||
nr_reps = 1;
|
||||
rc = X86EMUL_UNHANDLEABLE;
|
||||
if ( nr_reps == 1 && ops->write_io )
|
||||
{
|
||||
if ( (rc = read_ulong(ea.mem.seg, truncate_ea(_regs.esi),
|
||||
&dst.val, dst.bytes, ctxt, ops)) != 0 )
|
||||
rc = read_ulong(ea.mem.seg, ea.mem.off, &dst.val, dst.bytes,
|
||||
ctxt, ops);
|
||||
if ( rc == X86EMUL_OKAY )
|
||||
nr_reps = 0;
|
||||
}
|
||||
if ( (nr_reps > 1 || rc == X86EMUL_UNHANDLEABLE) && ops->rep_outs )
|
||||
rc = ops->rep_outs(ea.mem.seg, ea.mem.off, port, dst.bytes,
|
||||
&nr_reps, ctxt);
|
||||
if ( nr_reps >= 1 && rc == X86EMUL_UNHANDLEABLE )
|
||||
{
|
||||
if ( (rc = read_ulong(ea.mem.seg, ea.mem.off, &dst.val,
|
||||
dst.bytes, ctxt, ops)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
fail_if(ops->write_io == NULL);
|
||||
nr_reps = 0;
|
||||
}
|
||||
if ( !nr_reps && rc == X86EMUL_OKAY )
|
||||
{
|
||||
if ( (rc = ops->write_io(port, dst.bytes, dst.val, ctxt)) != 0 )
|
||||
goto done;
|
||||
nr_reps = 1;
|
||||
@@ -4040,7 +4090,11 @@ x86_emulate(
|
||||
rc = ops->read_io(port, dst.bytes, &dst.val, ctxt);
|
||||
}
|
||||
if ( rc != 0 )
|
||||
{
|
||||
if ( rc == X86EMUL_DONE )
|
||||
goto no_writeback;
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5447,9 +5501,7 @@ x86_emulate(
|
||||
break;
|
||||
}
|
||||
|
||||
no_writeback:
|
||||
/* Commit shadow register state. */
|
||||
_regs.eflags &= ~EFLG_RF;
|
||||
no_writeback: /* Commit shadow register state. */
|
||||
|
||||
/* Zero the upper 32 bits of %rip if not in 64-bit mode. */
|
||||
if ( !mode_64bit() )
|
||||
@@ -5459,7 +5511,15 @@ x86_emulate(
|
||||
if ( (rc == X86EMUL_OKAY) && (ctxt->regs->eflags & EFLG_TF) )
|
||||
ctxt->retire.singlestep = true;
|
||||
|
||||
*ctxt->regs = _regs;
|
||||
if ( rc != X86EMUL_DONE )
|
||||
*ctxt->regs = _regs;
|
||||
else
|
||||
{
|
||||
ctxt->regs->eip = _regs.eip;
|
||||
rc = X86EMUL_OKAY;
|
||||
}
|
||||
|
||||
ctxt->regs->eflags &= ~EFLG_RF;
|
||||
|
||||
done:
|
||||
_put_fpu();
|
||||
|
||||
@@ -146,6 +146,14 @@ struct __attribute__((__packed__)) segment_register {
|
||||
#define X86EMUL_EXCEPTION 2
|
||||
/* Retry the emulation for some reason. No state modified. */
|
||||
#define X86EMUL_RETRY 3
|
||||
/*
|
||||
* Operation fully done by one of the hooks:
|
||||
* - validate(): operation completed (except common insn retire logic)
|
||||
* - read_segment(x86_seg_tr, ...): bypass I/O bitmap access
|
||||
* - read_io() / write_io(): bypass GPR update (non-string insns only)
|
||||
* Undefined behavior when used anywhere else.
|
||||
*/
|
||||
#define X86EMUL_DONE 4
|
||||
|
||||
/* FPU sub-types which may be requested via ->get_fpu(). */
|
||||
enum x86_emulate_fpu_type {
|
||||
@@ -156,6 +164,8 @@ enum x86_emulate_fpu_type {
|
||||
X86EMUL_FPU_ymm /* AVX/XOP instruction set (%ymm0-%ymm7/15) */
|
||||
};
|
||||
|
||||
struct x86_emulate_state;
|
||||
|
||||
/*
|
||||
* These operations represent the instruction emulator's interface to memory,
|
||||
* I/O ports, privileged state... pretty much everything other than GPRs.
|
||||
@@ -237,6 +247,14 @@ struct x86_emulate_ops
|
||||
unsigned int bytes,
|
||||
struct x86_emulate_ctxt *ctxt);
|
||||
|
||||
/*
|
||||
* validate: Post-decode, pre-emulate hook to allow caller controlled
|
||||
* filtering.
|
||||
*/
|
||||
int (*validate)(
|
||||
const struct x86_emulate_state *state,
|
||||
struct x86_emulate_ctxt *ctxt);
|
||||
|
||||
/*
|
||||
* rep_ins: Emulate INS: <src_port> -> <dst_seg:dst_offset>.
|
||||
* @bytes_per_rep: [IN ] Bytes transferred per repetition.
|
||||
|
||||