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:
Jan Beulich committed 2016-12-14 09:54:03 +01:00
1 parent 36a808eeb2
commit 4999bf3e8b
3 files changed
+664 -496

No files matched your search

+572 -482
View File
File diff suppressed because it is too large. Load diff
+74 -14
View File
@@ -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();
+18
View File
@@ -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.
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.