target/riscv: Add itrigger support when icount is not enabled
When icount is not enabled, there is no API in QEMU that can get the guest instruction number. Translate the guest code in a way that each TB only has one instruction. After executing the instruction, decrease the count by 1 until it reaches 0 where the itrigger fires. Note that only when priviledge matches the itrigger configuration, the count will decrease. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-Id: <20221013062946.7530-2-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
fb3f3730e4
commit
2c9d747121
|
@ -625,6 +625,8 @@ FIELD(TB_FLAGS, PM_MASK_ENABLED, 22, 1)
|
||||||
FIELD(TB_FLAGS, PM_BASE_ENABLED, 23, 1)
|
FIELD(TB_FLAGS, PM_BASE_ENABLED, 23, 1)
|
||||||
FIELD(TB_FLAGS, VTA, 24, 1)
|
FIELD(TB_FLAGS, VTA, 24, 1)
|
||||||
FIELD(TB_FLAGS, VMA, 25, 1)
|
FIELD(TB_FLAGS, VMA, 25, 1)
|
||||||
|
/* Native debug itrigger */
|
||||||
|
FIELD(TB_FLAGS, ITRIGGER, 26, 1)
|
||||||
|
|
||||||
#ifdef TARGET_RISCV32
|
#ifdef TARGET_RISCV32
|
||||||
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
|
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
#include "tcg/tcg-op.h"
|
#include "tcg/tcg-op.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "semihosting/common-semi.h"
|
#include "semihosting/common-semi.h"
|
||||||
|
#include "sysemu/cpu-timers.h"
|
||||||
#include "cpu_bits.h"
|
#include "cpu_bits.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
|
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
|
||||||
{
|
{
|
||||||
|
@ -103,6 +105,10 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
|
||||||
flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_VS,
|
flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_VS,
|
||||||
get_field(env->mstatus_hs, MSTATUS_VS));
|
get_field(env->mstatus_hs, MSTATUS_VS));
|
||||||
}
|
}
|
||||||
|
if (riscv_feature(env, RISCV_FEATURE_DEBUG) && !icount_enabled()) {
|
||||||
|
flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER,
|
||||||
|
riscv_itrigger_enabled(env));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl);
|
flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl);
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
|
#include "exec/helper-proto.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following M-mode trigger CSRs are implemented:
|
* The following M-mode trigger CSRs are implemented:
|
||||||
|
@ -496,6 +497,76 @@ static void type6_reg_write(CPURISCVState *env, target_ulong index,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* icount trigger type */
|
||||||
|
static inline int
|
||||||
|
itrigger_get_count(CPURISCVState *env, int index)
|
||||||
|
{
|
||||||
|
return get_field(env->tdata1[index], ITRIGGER_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
itrigger_set_count(CPURISCVState *env, int index, int value)
|
||||||
|
{
|
||||||
|
env->tdata1[index] = set_field(env->tdata1[index],
|
||||||
|
ITRIGGER_COUNT, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_itrigger_priv(CPURISCVState *env, int index)
|
||||||
|
{
|
||||||
|
target_ulong tdata1 = env->tdata1[index];
|
||||||
|
if (riscv_cpu_virt_enabled(env)) {
|
||||||
|
/* check VU/VS bit against current privilege level */
|
||||||
|
return (get_field(tdata1, ITRIGGER_VS) == env->priv) ||
|
||||||
|
(get_field(tdata1, ITRIGGER_VU) == env->priv);
|
||||||
|
} else {
|
||||||
|
/* check U/S/M bit against current privilege level */
|
||||||
|
return (get_field(tdata1, ITRIGGER_M) == env->priv) ||
|
||||||
|
(get_field(tdata1, ITRIGGER_S) == env->priv) ||
|
||||||
|
(get_field(tdata1, ITRIGGER_U) == env->priv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool riscv_itrigger_enabled(CPURISCVState *env)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
for (int i = 0; i < RV_MAX_TRIGGERS; i++) {
|
||||||
|
if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (check_itrigger_priv(env, i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
count = itrigger_get_count(env, i);
|
||||||
|
if (!count) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_itrigger_match(CPURISCVState *env)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
for (int i = 0; i < RV_MAX_TRIGGERS; i++) {
|
||||||
|
if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (check_itrigger_priv(env, i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
count = itrigger_get_count(env, i);
|
||||||
|
if (!count) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
itrigger_set_count(env, i, count--);
|
||||||
|
if (!count) {
|
||||||
|
do_trigger_action(env, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index)
|
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index)
|
||||||
{
|
{
|
||||||
switch (tdata_index) {
|
switch (tdata_index) {
|
||||||
|
|
|
@ -118,6 +118,17 @@ enum {
|
||||||
SIZE_NUM = 16
|
SIZE_NUM = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* itrigger filed masks */
|
||||||
|
#define ITRIGGER_ACTION 0x3f
|
||||||
|
#define ITRIGGER_U BIT(6)
|
||||||
|
#define ITRIGGER_S BIT(7)
|
||||||
|
#define ITRIGGER_PENDING BIT(8)
|
||||||
|
#define ITRIGGER_M BIT(9)
|
||||||
|
#define ITRIGGER_COUNT (0x3fff << 10)
|
||||||
|
#define ITRIGGER_HIT BIT(24)
|
||||||
|
#define ITRIGGER_VU BIT(25)
|
||||||
|
#define ITRIGGER_VS BIT(26)
|
||||||
|
|
||||||
bool tdata_available(CPURISCVState *env, int tdata_index);
|
bool tdata_available(CPURISCVState *env, int tdata_index);
|
||||||
|
|
||||||
target_ulong tselect_csr_read(CPURISCVState *env);
|
target_ulong tselect_csr_read(CPURISCVState *env);
|
||||||
|
@ -134,4 +145,5 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
|
||||||
|
|
||||||
void riscv_trigger_init(CPURISCVState *env);
|
void riscv_trigger_init(CPURISCVState *env);
|
||||||
|
|
||||||
|
bool riscv_itrigger_enabled(CPURISCVState *env);
|
||||||
#endif /* RISCV_DEBUG_H */
|
#endif /* RISCV_DEBUG_H */
|
||||||
|
|
|
@ -109,6 +109,8 @@ DEF_HELPER_1(sret, tl, env)
|
||||||
DEF_HELPER_1(mret, tl, env)
|
DEF_HELPER_1(mret, tl, env)
|
||||||
DEF_HELPER_1(wfi, void, env)
|
DEF_HELPER_1(wfi, void, env)
|
||||||
DEF_HELPER_1(tlb_flush, void, env)
|
DEF_HELPER_1(tlb_flush, void, env)
|
||||||
|
/* Native Debug */
|
||||||
|
DEF_HELPER_1(itrigger_match, void, env)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Hypervisor functions */
|
/* Hypervisor functions */
|
||||||
|
|
|
@ -78,7 +78,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
|
||||||
if (has_ext(ctx, RVS)) {
|
if (has_ext(ctx, RVS)) {
|
||||||
decode_save_opc(ctx);
|
decode_save_opc(ctx);
|
||||||
gen_helper_sret(cpu_pc, cpu_env);
|
gen_helper_sret(cpu_pc, cpu_env);
|
||||||
tcg_gen_exit_tb(NULL, 0); /* no chaining */
|
exit_tb(ctx); /* no chaining */
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -94,7 +94,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
decode_save_opc(ctx);
|
decode_save_opc(ctx);
|
||||||
gen_helper_mret(cpu_pc, cpu_env);
|
gen_helper_mret(cpu_pc, cpu_env);
|
||||||
tcg_gen_exit_tb(NULL, 0); /* no chaining */
|
exit_tb(ctx); /* no chaining */
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -66,7 +66,7 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn);
|
gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn);
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
lookup_and_goto_ptr(ctx);
|
||||||
|
|
||||||
if (misaligned) {
|
if (misaligned) {
|
||||||
gen_set_label(misaligned);
|
gen_set_label(misaligned);
|
||||||
|
@ -803,7 +803,7 @@ static bool trans_pause(DisasContext *ctx, arg_pause *a)
|
||||||
* end the TB and return to main loop
|
* end the TB and return to main loop
|
||||||
*/
|
*/
|
||||||
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
||||||
tcg_gen_exit_tb(NULL, 0);
|
exit_tb(ctx);
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -827,7 +827,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a)
|
||||||
* however we need to end the translation block
|
* however we need to end the translation block
|
||||||
*/
|
*/
|
||||||
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
||||||
tcg_gen_exit_tb(NULL, 0);
|
exit_tb(ctx);
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -838,7 +838,7 @@ static bool do_csr_post(DisasContext *ctx)
|
||||||
decode_save_opc(ctx);
|
decode_save_opc(ctx);
|
||||||
/* We may have changed important cpu state -- exit to main loop. */
|
/* We may have changed important cpu state -- exit to main loop. */
|
||||||
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
gen_set_pc_imm(ctx, ctx->pc_succ_insn);
|
||||||
tcg_gen_exit_tb(NULL, 0);
|
exit_tb(ctx);
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2)
|
||||||
mark_vs_dirty(s);
|
mark_vs_dirty(s);
|
||||||
|
|
||||||
gen_set_pc_imm(s, s->pc_succ_insn);
|
gen_set_pc_imm(s, s->pc_succ_insn);
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
lookup_and_goto_ptr(s);
|
||||||
s->base.is_jmp = DISAS_NORETURN;
|
s->base.is_jmp = DISAS_NORETURN;
|
||||||
|
|
||||||
if (rd == 0 && rs1 == 0) {
|
if (rd == 0 && rs1 == 0) {
|
||||||
|
@ -222,7 +222,7 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2)
|
||||||
gen_set_gpr(s, rd, dst);
|
gen_set_gpr(s, rd, dst);
|
||||||
mark_vs_dirty(s);
|
mark_vs_dirty(s);
|
||||||
gen_set_pc_imm(s, s->pc_succ_insn);
|
gen_set_pc_imm(s, s->pc_succ_insn);
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
lookup_and_goto_ptr(s);
|
||||||
s->base.is_jmp = DISAS_NORETURN;
|
s->base.is_jmp = DISAS_NORETURN;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -112,6 +112,8 @@ typedef struct DisasContext {
|
||||||
/* PointerMasking extension */
|
/* PointerMasking extension */
|
||||||
bool pm_mask_enabled;
|
bool pm_mask_enabled;
|
||||||
bool pm_base_enabled;
|
bool pm_base_enabled;
|
||||||
|
/* Use icount trigger for native debug */
|
||||||
|
bool itrigger;
|
||||||
/* TCG of the current insn_start */
|
/* TCG of the current insn_start */
|
||||||
TCGOp *insn_start;
|
TCGOp *insn_start;
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
@ -257,15 +259,39 @@ static void gen_exception_inst_addr_mis(DisasContext *ctx)
|
||||||
generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS);
|
generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lookup_and_goto_ptr(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
if (ctx->itrigger) {
|
||||||
|
gen_helper_itrigger_match(cpu_env);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
tcg_gen_lookup_and_goto_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exit_tb(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
if (ctx->itrigger) {
|
||||||
|
gen_helper_itrigger_match(cpu_env);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
tcg_gen_exit_tb(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||||
{
|
{
|
||||||
if (translator_use_goto_tb(&ctx->base, dest)) {
|
/*
|
||||||
|
* Under itrigger, instruction executes one by one like singlestep,
|
||||||
|
* direct block chain benefits will be small.
|
||||||
|
*/
|
||||||
|
if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) {
|
||||||
tcg_gen_goto_tb(n);
|
tcg_gen_goto_tb(n);
|
||||||
gen_set_pc_imm(ctx, dest);
|
gen_set_pc_imm(ctx, dest);
|
||||||
tcg_gen_exit_tb(ctx->base.tb, n);
|
tcg_gen_exit_tb(ctx->base.tb, n);
|
||||||
} else {
|
} else {
|
||||||
gen_set_pc_imm(ctx, dest);
|
gen_set_pc_imm(ctx, dest);
|
||||||
tcg_gen_lookup_and_goto_ptr();
|
lookup_and_goto_ptr(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1142,6 +1168,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||||
memset(ctx->ftemp, 0, sizeof(ctx->ftemp));
|
memset(ctx->ftemp, 0, sizeof(ctx->ftemp));
|
||||||
ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
|
ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
|
||||||
ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
|
ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
|
||||||
|
ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
|
||||||
ctx->zero = tcg_constant_tl(0);
|
ctx->zero = tcg_constant_tl(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1181,7 +1208,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
|
||||||
|
|
||||||
/* Only the first insn within a TB is allowed to cross a page boundary. */
|
/* Only the first insn within a TB is allowed to cross a page boundary. */
|
||||||
if (ctx->base.is_jmp == DISAS_NEXT) {
|
if (ctx->base.is_jmp == DISAS_NEXT) {
|
||||||
if (!is_same_page(&ctx->base, ctx->base.pc_next)) {
|
if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) {
|
||||||
ctx->base.is_jmp = DISAS_TOO_MANY;
|
ctx->base.is_jmp = DISAS_TOO_MANY;
|
||||||
} else {
|
} else {
|
||||||
unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK;
|
unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK;
|
||||||
|
|
Loading…
Reference in New Issue