target-i386: Handle I/O breakpoints

Backports commit 5223a9423c5fb9e32b0c3eaaa2c0bf8c5cfd6866 from qemu
This commit is contained in:
Eduardo Habkost 2018-02-16 16:17:05 -05:00 committed by Lioncash
parent d1cfcb6d79
commit c6bfe2a03d
No known key found for this signature in database
GPG Key ID: 4E3C3CC1031BA9C7
4 changed files with 96 additions and 28 deletions

View File

@ -48,68 +48,81 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index)
return (len == 2) ? 8 : len + 1; return (len == 2) ? 8 : len + 1;
} }
static void hw_breakpoint_insert(CPUX86State *env, int index) static int hw_breakpoint_insert(CPUX86State *env, int index)
{ {
CPUState *cs = CPU(x86_env_get_cpu(env)); CPUState *cs = CPU(x86_env_get_cpu(env));
int type = 0, err = 0; target_ulong dr7 = env->dr[7];
target_ulong drN = env->dr[index];
int err = 0;
switch (hw_breakpoint_type(env->dr[7], index)) { switch (hw_breakpoint_type(dr7, index)) {
case DR7_TYPE_BP_INST: case DR7_TYPE_BP_INST:
if (hw_breakpoint_enabled(env->dr[7], index)) { if (hw_breakpoint_enabled(dr7, index)) {
err = cpu_breakpoint_insert(cs, env->dr[index], BP_CPU, err = cpu_breakpoint_insert(cs, drN, BP_CPU,
&env->cpu_breakpoint[index]); &env->cpu_breakpoint[index]);
} }
break; break;
case DR7_TYPE_DATA_WR:
type = BP_CPU | BP_MEM_WRITE;
break;
case DR7_TYPE_IO_RW: case DR7_TYPE_IO_RW:
/* No support for I/O watchpoints yet */ /* Notice when we should enable calls to bpt_io. */
return hw_breakpoint_enabled(env->dr[7], index)
? HF_IOBPT_MASK : 0;
case DR7_TYPE_DATA_WR:
if (hw_breakpoint_enabled(dr7, index)) {
err = cpu_watchpoint_insert(cs, drN,
hw_breakpoint_len(dr7, index),
BP_CPU | BP_MEM_WRITE,
&env->cpu_watchpoint[index]);
}
break; break;
case DR7_TYPE_DATA_RW: case DR7_TYPE_DATA_RW:
type = BP_CPU | BP_MEM_ACCESS; if (hw_breakpoint_enabled(dr7, index)) {
err = cpu_watchpoint_insert(cs, drN,
hw_breakpoint_len(dr7, index),
BP_CPU | BP_MEM_ACCESS,
&env->cpu_watchpoint[index]);
}
break; break;
} }
if (type != 0) {
err = cpu_watchpoint_insert(cs, env->dr[index],
hw_breakpoint_len(env->dr[7], index),
type, &env->cpu_watchpoint[index]);
}
if (err) { if (err) {
env->cpu_breakpoint[index] = NULL; env->cpu_breakpoint[index] = NULL;
} }
return 0;
} }
static void hw_breakpoint_remove(CPUX86State *env, int index) static void hw_breakpoint_remove(CPUX86State *env, int index)
{ {
CPUState *cs; CPUState *cs = CPU(x86_env_get_cpu(env));
if (!env->cpu_breakpoint[index]) {
return;
}
cs = CPU(x86_env_get_cpu(env));
switch (hw_breakpoint_type(env->dr[7], index)) { switch (hw_breakpoint_type(env->dr[7], index)) {
case DR7_TYPE_BP_INST: case DR7_TYPE_BP_INST:
if (hw_breakpoint_enabled(env->dr[7], index)) { if (env->cpu_breakpoint[index]) {
cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]);
env->cpu_breakpoint[index] = NULL;
} }
break; break;
case DR7_TYPE_DATA_WR: case DR7_TYPE_DATA_WR:
case DR7_TYPE_DATA_RW: case DR7_TYPE_DATA_RW:
if (env->cpu_breakpoint[index]) {
cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]);
env->cpu_breakpoint[index] = NULL;
}
break; break;
case DR7_TYPE_IO_RW: case DR7_TYPE_IO_RW:
/* No support for I/O watchpoints yet */ /* HF_IOBPT_MASK cleared elsewhere. */
break; break;
} }
} }
void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7)
{ {
int i;
target_ulong old_dr7 = env->dr[7]; target_ulong old_dr7 = env->dr[7];
int iobpt = 0;
int i;
new_dr7 |= DR7_FIXED_1; new_dr7 |= DR7_FIXED_1;
@ -123,7 +136,10 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7)
for (i = 0; i < DR7_MAX_BP; i++) { for (i = 0; i < DR7_MAX_BP; i++) {
if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) {
hw_breakpoint_remove(env, i); iobpt |= hw_breakpoint_insert(env, i);
} else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW
&& hw_breakpoint_enabled(new_dr7, i)) {
iobpt |= HF_IOBPT_MASK;
} }
} }
env->dr[7] = new_dr7; env->dr[7] = new_dr7;
@ -138,9 +154,11 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7)
} }
env->dr[7] = new_dr7; env->dr[7] = new_dr7;
for (i = 0; i < DR7_MAX_BP; i++) { for (i = 0; i < DR7_MAX_BP; i++) {
hw_breakpoint_insert(env, i); iobpt |= hw_breakpoint_insert(env, i);
} }
} }
env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt;
} }
static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update)
@ -242,3 +260,30 @@ void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0)
} }
#endif #endif
} }
/* Check if Port I/O is trapped by a breakpoint. */
void helper_bpt_io(CPUX86State *env, uint32_t port,
uint32_t size, target_ulong next_eip)
{
#ifndef CONFIG_USER_ONLY
target_ulong dr7 = env->dr[7];
int i, hit = 0;
for (i = 0; i < DR7_MAX_BP; ++i) {
if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW
&& hw_breakpoint_enabled(dr7, i)) {
int bpt_len = hw_breakpoint_len(dr7, i);
if (port + size - 1 >= env->dr[i]
&& port <= env->dr[i] + bpt_len - 1) {
hit |= 1 << i;
}
}
}
if (hit) {
env->dr[6] = (env->dr[6] & ~0xf) | hit;
env->eip = next_eip;
raise_exception(env, EXCP01_DB);
}
#endif
}

View File

@ -156,6 +156,7 @@
#define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ #define HF_SVMI_SHIFT 21 /* SVM intercepts are active */
#define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */
#define HF_SMAP_SHIFT 23 /* CR4.SMAP */ #define HF_SMAP_SHIFT 23 /* CR4.SMAP */
#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */
#define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_CPL_MASK (3 << HF_CPL_SHIFT)
#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
@ -179,6 +180,7 @@
#define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) #define HF_SVMI_MASK (1 << HF_SVMI_SHIFT)
#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) #define HF_SMAP_MASK (1 << HF_SMAP_SHIFT)
#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT)
/* hflags2 */ /* hflags2 */

View File

@ -94,6 +94,7 @@ DEF_HELPER_3(outw, void, env, i32, i32)
DEF_HELPER_2(inw, tl, env, i32) DEF_HELPER_2(inw, tl, env, i32)
DEF_HELPER_3(outl, void, env, i32, i32) DEF_HELPER_3(outl, void, env, i32, i32)
DEF_HELPER_2(inl, tl, env, i32) DEF_HELPER_2(inl, tl, env, i32)
DEF_HELPER_FLAGS_4(bpt_io, TCG_CALL_NO_WG, void, env, i32, i32, tl)
DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64)
DEF_HELPER_3(vmexit, void, env, i32, i64) DEF_HELPER_3(vmexit, void, env, i32, i64)

View File

@ -1364,6 +1364,20 @@ static inline void gen_cmps(DisasContext *s, TCGMemOp ot)
gen_op_add_reg_T0(tcg_ctx, s->aflag, R_EDI); gen_op_add_reg_T0(tcg_ctx, s->aflag, R_EDI);
} }
static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot)
{
TCGContext *tcg_ctx = s->uc->tcg_ctx;
if (s->flags & HF_IOBPT_MASK) {
TCGv_i32 t_size = tcg_const_i32(tcg_ctx, 1 << ot);
TCGv t_next = tcg_const_tl(tcg_ctx, s->pc - s->cs_base);
gen_helper_bpt_io(tcg_ctx, tcg_ctx->cpu_env, t_port, t_size, t_next);
tcg_temp_free_i32(tcg_ctx, t_size);
tcg_temp_free(tcg_ctx, t_next);
}
}
static inline void gen_ins(DisasContext *s, TCGMemOp ot) static inline void gen_ins(DisasContext *s, TCGMemOp ot)
{ {
TCGContext *tcg_ctx = s->uc->tcg_ctx; TCGContext *tcg_ctx = s->uc->tcg_ctx;
@ -1383,6 +1397,7 @@ static inline void gen_ins(DisasContext *s, TCGMemOp ot)
gen_op_st_v(s, ot, *cpu_T[0], cpu_A0); gen_op_st_v(s, ot, *cpu_T[0], cpu_A0);
gen_op_movl_T0_Dshift(tcg_ctx, ot); gen_op_movl_T0_Dshift(tcg_ctx, ot);
gen_op_add_reg_T0(tcg_ctx, s->aflag, R_EDI); gen_op_add_reg_T0(tcg_ctx, s->aflag, R_EDI);
gen_bpt_io(s, cpu_tmp2_i32, ot);
} }
static inline void gen_outs(DisasContext *s, TCGMemOp ot) static inline void gen_outs(DisasContext *s, TCGMemOp ot)
@ -1404,6 +1419,7 @@ static inline void gen_outs(DisasContext *s, TCGMemOp ot)
gen_op_movl_T0_Dshift(tcg_ctx, ot); gen_op_movl_T0_Dshift(tcg_ctx, ot);
gen_op_add_reg_T0(tcg_ctx, s->aflag, R_ESI); gen_op_add_reg_T0(tcg_ctx, s->aflag, R_ESI);
gen_bpt_io(s, cpu_tmp2_i32, ot);
} }
/* same method as Valgrind : we generate jumps to current or next /* same method as Valgrind : we generate jumps to current or next
@ -6952,6 +6968,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_movi_i32(tcg_ctx, cpu_tmp2_i32, val); tcg_gen_movi_i32(tcg_ctx, cpu_tmp2_i32, val);
gen_helper_in_func(tcg_ctx, ot, *cpu_T[1], cpu_tmp2_i32); gen_helper_in_func(tcg_ctx, ot, *cpu_T[1], cpu_tmp2_i32);
gen_op_mov_reg_v(tcg_ctx, ot, R_EAX, *cpu_T[1]); gen_op_mov_reg_v(tcg_ctx, ot, R_EAX, *cpu_T[1]);
gen_bpt_io(s, cpu_tmp2_i32, ot);
break; break;
case 0xe6: case 0xe6:
case 0xe7: case 0xe7:
@ -6965,6 +6982,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_movi_i32(tcg_ctx, cpu_tmp2_i32, val); tcg_gen_movi_i32(tcg_ctx, cpu_tmp2_i32, val);
tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp3_i32, *cpu_T[1]); tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp3_i32, *cpu_T[1]);
gen_helper_out_func(tcg_ctx, ot, cpu_tmp2_i32, cpu_tmp3_i32); gen_helper_out_func(tcg_ctx, ot, cpu_tmp2_i32, cpu_tmp3_i32);
gen_bpt_io(s, cpu_tmp2_i32, ot);
break; break;
case 0xec: case 0xec:
case 0xed: case 0xed:
@ -6975,6 +6993,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp2_i32, *cpu_T[0]); tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp2_i32, *cpu_T[0]);
gen_helper_in_func(tcg_ctx, ot, *cpu_T[1], cpu_tmp2_i32); gen_helper_in_func(tcg_ctx, ot, *cpu_T[1], cpu_tmp2_i32);
gen_op_mov_reg_v(tcg_ctx, ot, R_EAX, *cpu_T[1]); gen_op_mov_reg_v(tcg_ctx, ot, R_EAX, *cpu_T[1]);
gen_bpt_io(s, cpu_tmp2_i32, ot);
break; break;
case 0xee: case 0xee:
case 0xef: case 0xef:
@ -6987,6 +7006,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp2_i32, *cpu_T[0]); tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp2_i32, *cpu_T[0]);
tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp3_i32, *cpu_T[1]); tcg_gen_trunc_tl_i32(tcg_ctx, cpu_tmp3_i32, *cpu_T[1]);
gen_helper_out_func(tcg_ctx, ot, cpu_tmp2_i32, cpu_tmp3_i32); gen_helper_out_func(tcg_ctx, ot, cpu_tmp2_i32, cpu_tmp3_i32);
gen_bpt_io(s, cpu_tmp2_i32, ot);
break; break;
/************************/ /************************/