@@ -415,6 +415,8 @@ typedef union {
#define MSR_SYSENTER_CS 0x00000174
#define MSR_SYSENTER_ESP 0x00000175
#define MSR_SYSENTER_EIP 0x00000176
+#define MSR_DEBUGCTL 0x000001d9
+#define DEBUGCTL_BTF (1 << 1)
#define MSR_EFER 0xc0000080
#define MSR_STAR 0xc0000081
#define MSR_LSTAR 0xc0000082
@@ -751,6 +753,8 @@ do {
rc = ops->insn_fetch(x86_seg_cs, ip, NULL, 0, ctxt); \
if ( rc ) goto done; \
_regs.eip = ip; \
+ if ( _regs.eflags & EFLG_TF ) \
+ ctxt->retire.singlestep = true; \
} while (0)
#define validate_far_branch(cs, ip) ({ \
@@ -767,6 +771,8 @@ do {
#define commit_far_branch(cs, ip) ({ \
validate_far_branch(cs, ip); \
_regs.eip = (ip); \
+ if ( _regs.eflags & EFLG_TF ) \
+ ctxt->retire.singlestep = true; \
ops->write_segment(x86_seg_cs, cs, ctxt); \
})
@@ -948,6 +954,9 @@ static inline void put_loop_count(
} \
goto no_writeback; \
} \
+ if ( max_reps > 1 && (_regs.eflags & EFLG_TF) && \
+ !is_branch_step(ctxt, ops) ) \
+ max_reps = 1; \
max_reps; \
})
@@ -1637,6 +1646,16 @@ static bool is_aligned(enum x86_segment
return !((reg.base + offs) & (size - 1));
}
+static bool is_branch_step(struct x86_emulate_ctxt *ctxt,
+ const struct x86_emulate_ops *ops)
+{
+ uint64_t debugctl;
+
+ return ops->read_msr &&
+ ops->read_msr(MSR_DEBUGCTL, &debugctl, ctxt) == X86EMUL_OKAY &&
+ (debugctl & DEBUGCTL_BTF);
+}
+
static bool umip_active(struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
@@ -3132,6 +3151,8 @@ x86_emulate(
goto done;
_regs.eip = imm1;
+ if ( _regs.eflags & EFLG_TF )
+ ctxt->retire.singlestep = true;
break;
case 0x9b: /* wait/fwait */
@@ -4608,6 +4629,8 @@ x86_emulate(
(rc = ops->write_segment(x86_seg_ss, &sreg, ctxt)) )
goto done;
+ if ( ctxt->regs->eflags & EFLG_TF )
+ ctxt->retire.singlestep = true;
break;
}
@@ -4875,6 +4898,8 @@ x86_emulate(
goto done;
_regs.esp = lm ? msr_content : (uint32_t)msr_content;
+ if ( _regs.eflags & EFLG_TF )
+ ctxt->retire.singlestep = true;
break;
}
@@ -4914,6 +4939,9 @@ x86_emulate(
_regs.eip = user64 ? _regs.edx : (uint32_t)_regs.edx;
_regs.esp = user64 ? _regs.ecx : (uint32_t)_regs.ecx;
+
+ if ( _regs.eflags & EFLG_TF )
+ ctxt->retire.singlestep = true;
break;
}
@@ -5400,7 +5428,9 @@ x86_emulate(
break;
#endif
default:
- goto cannot_emulate;
+ cannot_emulate:
+ rc = X86EMUL_UNHANDLEABLE;
+ goto done;
}
switch ( dst.type )
@@ -5445,7 +5475,8 @@ x86_emulate(
_regs.eip = (uint32_t)_regs.eip;
/* Was singestepping active at the start of this instruction? */
- if ( (rc == X86EMUL_OKAY) && (ctxt->regs->eflags & EFLG_TF) )
+ if ( (rc == X86EMUL_OKAY) && (ctxt->regs->eflags & EFLG_TF) &&
+ !is_branch_step(ctxt, ops) && !ctxt->retire.mov_ss )
ctxt->retire.singlestep = true;
*ctxt->regs = _regs;
@@ -5461,12 +5492,17 @@ x86_emulate(
done:
_put_fpu();
put_stub(stub);
- return rc;
- cannot_emulate:
- _put_fpu();
- put_stub(stub);
- return X86EMUL_UNHANDLEABLE;
+ /*
+ * We may have set the single step flag ahead of the last possible point
+ * of failure (unavoidably with the current near CALL code flow, but also
+ * used on some far branch paths to keep the code simple), so to satisfy
+ * x86_emulate_wrapper()'s ASSERT() we may need to clear it here again.
+ */
+ if ( rc != X86EMUL_OKAY )
+ ctxt->retire.singlestep = false;
+
+ return rc;
#undef state
}