diff options
Diffstat (limited to 'target/riscv/debug.c')
| -rw-r--r-- | target/riscv/debug.c | 484 |
1 files changed, 386 insertions, 98 deletions
diff --git a/target/riscv/debug.c b/target/riscv/debug.c index fc6e13222f..26ea764407 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -37,11 +37,9 @@ * - tdata1 * - tdata2 * - tdata3 + * - tinfo * - * We don't support writable 'type' field in the tdata1 register, so there is - * no need to implement the "tinfo" CSR. - * - * The following triggers are implemented: + * The following triggers are initialized by default: * * Index | Type | tdata mapping | Description * ------+------+------------------------+------------ @@ -52,8 +50,15 @@ /* tdata availability of a trigger */ typedef bool tdata_avail[TDATA_NUM]; -static tdata_avail tdata_mapping[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false }, +static tdata_avail tdata_mapping[TRIGGER_TYPE_NUM] = { + [TRIGGER_TYPE_NO_EXIST] = { false, false, false }, + [TRIGGER_TYPE_AD_MATCH] = { true, true, true }, + [TRIGGER_TYPE_INST_CNT] = { true, false, true }, + [TRIGGER_TYPE_INT] = { true, true, true }, + [TRIGGER_TYPE_EXCP] = { true, true, true }, + [TRIGGER_TYPE_AD_MATCH6] = { true, true, true }, + [TRIGGER_TYPE_EXT_SRC] = { true, false, false }, + [TRIGGER_TYPE_UNAVAIL] = { true, true, true } }; /* only breakpoint size 1/2/4/8 supported */ @@ -67,18 +72,76 @@ static int access_size[SIZE_NUM] = { [6 ... 15] = -1, }; -static inline target_ulong trigger_type(CPURISCVState *env, - trigger_type_t type) +static inline target_ulong extract_trigger_type(CPURISCVState *env, + target_ulong tdata1) +{ + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + return extract32(tdata1, 28, 4); + case MXL_RV64: + case MXL_RV128: + return extract64(tdata1, 60, 4); + default: + g_assert_not_reached(); + } +} + +static inline target_ulong get_trigger_type(CPURISCVState *env, + target_ulong trigger_index) +{ + return extract_trigger_type(env, env->tdata1[trigger_index]); +} + +static trigger_action_t get_trigger_action(CPURISCVState *env, + target_ulong trigger_index) +{ + target_ulong tdata1 = env->tdata1[trigger_index]; + int trigger_type = get_trigger_type(env, trigger_index); + trigger_action_t action = DBG_ACTION_NONE; + + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + action = (tdata1 & TYPE2_ACTION) >> 12; + break; + case TRIGGER_TYPE_AD_MATCH6: + action = (tdata1 & TYPE6_ACTION) >> 12; + break; + case TRIGGER_TYPE_INST_CNT: + case TRIGGER_TYPE_INT: + case TRIGGER_TYPE_EXCP: + case TRIGGER_TYPE_EXT_SRC: + qemu_log_mask(LOG_UNIMP, "trigger type: %d is not supported\n", + trigger_type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exit\n", + trigger_type); + break; + default: + g_assert_not_reached(); + } + + return action; +} + +static inline target_ulong build_tdata1(CPURISCVState *env, + trigger_type_t type, + bool dmode, target_ulong data) { target_ulong tdata1; switch (riscv_cpu_mxl(env)) { case MXL_RV32: - tdata1 = RV32_TYPE(type); + tdata1 = RV32_TYPE(type) | + (dmode ? RV32_DMODE : 0) | + (data & RV32_DATA_MASK); break; case MXL_RV64: case MXL_RV128: - tdata1 = RV64_TYPE(type); + tdata1 = RV64_TYPE(type) | + (dmode ? RV64_DMODE : 0) | + (data & RV64_DATA_MASK); break; default: g_assert_not_reached(); @@ -89,15 +152,13 @@ static inline target_ulong trigger_type(CPURISCVState *env, bool tdata_available(CPURISCVState *env, int tdata_index) { - if (unlikely(tdata_index >= TDATA_NUM)) { - return false; - } + int trigger_type = get_trigger_type(env, env->trigger_cur); - if (unlikely(env->trigger_cur >= TRIGGER_NUM)) { + if (unlikely(tdata_index >= TDATA_NUM)) { return false; } - return tdata_mapping[env->trigger_cur][tdata_index]; + return tdata_mapping[trigger_type][tdata_index]; } target_ulong tselect_csr_read(CPURISCVState *env) @@ -107,8 +168,9 @@ target_ulong tselect_csr_read(CPURISCVState *env) void tselect_csr_write(CPURISCVState *env, target_ulong val) { - /* all target_ulong bits of tselect are implemented */ - env->trigger_cur = val; + if (val < RV_MAX_TRIGGERS) { + env->trigger_cur = val; + } } static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, @@ -137,6 +199,7 @@ static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, qemu_log_mask(LOG_GUEST_ERROR, "ignoring type write to tdata1 register\n"); } + if (dmode != 0) { qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n"); } @@ -152,6 +215,32 @@ static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, } } +static void do_trigger_action(CPURISCVState *env, target_ulong trigger_index) +{ + trigger_action_t action = get_trigger_action(env, trigger_index); + + switch (action) { + case DBG_ACTION_NONE: + break; + case DBG_ACTION_BP: + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + break; + case DBG_ACTION_DBG_MODE: + case DBG_ACTION_TRACE0: + case DBG_ACTION_TRACE1: + case DBG_ACTION_TRACE2: + case DBG_ACTION_TRACE3: + case DBG_ACTION_EXT_DBG0: + case DBG_ACTION_EXT_DBG1: + qemu_log_mask(LOG_UNIMP, "action: %d is not supported\n", action); + break; + default: + g_assert_not_reached(); + } +} + +/* type 2 trigger */ + static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) { uint32_t size, sizelo, sizehi = 0; @@ -211,8 +300,8 @@ static target_ulong type2_mcontrol_validate(CPURISCVState *env, static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) { - target_ulong ctrl = env->type2_trig[index].mcontrol; - target_ulong addr = env->type2_trig[index].maddress; + target_ulong ctrl = env->tdata1[index]; + target_ulong addr = env->tdata2[index]; bool enabled = type2_breakpoint_enabled(ctrl); CPUState *cs = env_cpu(env); int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; @@ -223,7 +312,7 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) } if (ctrl & TYPE2_EXEC) { - cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp); + cpu_breakpoint_insert(cs, addr, flags, &env->cpu_breakpoint[index]); } if (ctrl & TYPE2_LOAD) { @@ -237,10 +326,10 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) size = type2_breakpoint_size(env, ctrl); if (size != 0) { cpu_watchpoint_insert(cs, addr, size, flags, - &env->type2_trig[index].wp); + &env->cpu_watchpoint[index]); } else { cpu_watchpoint_insert(cs, addr, 8, flags, - &env->type2_trig[index].wp); + &env->cpu_watchpoint[index]); } } } @@ -249,59 +338,159 @@ static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index) { CPUState *cs = env_cpu(env); - if (env->type2_trig[index].bp) { - cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp); - env->type2_trig[index].bp = NULL; + if (env->cpu_breakpoint[index]) { + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); + env->cpu_breakpoint[index] = NULL; } - if (env->type2_trig[index].wp) { - cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp); - env->type2_trig[index].wp = NULL; + if (env->cpu_watchpoint[index]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + env->cpu_watchpoint[index] = NULL; } } -static target_ulong type2_reg_read(CPURISCVState *env, - target_ulong trigger_index, int tdata_index) +static void type2_reg_write(CPURISCVState *env, target_ulong index, + int tdata_index, target_ulong val) { - uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; - target_ulong tdata; + target_ulong new_val; switch (tdata_index) { case TDATA1: - tdata = env->type2_trig[index].mcontrol; + new_val = type2_mcontrol_validate(env, val); + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } break; case TDATA2: - tdata = env->type2_trig[index].maddress; + if (val != env->tdata2[index]) { + env->tdata2[index] = val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } + break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for type 2 trigger\n"); break; default: g_assert_not_reached(); } - return tdata; + return; +} + +/* type 6 trigger */ + +static inline bool type6_breakpoint_enabled(target_ulong ctrl) +{ + bool mode = !!(ctrl & (TYPE6_VU | TYPE6_VS | TYPE6_U | TYPE6_S | TYPE6_M)); + bool rwx = !!(ctrl & (TYPE6_LOAD | TYPE6_STORE | TYPE6_EXEC)); + + return mode && rwx; } -static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, +static target_ulong type6_mcontrol6_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + uint32_t size; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH6); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, TYPE6_MATCH, "match"); + warn_always_zero_bit(ctrl, TYPE6_CHAIN, "chain"); + warn_always_zero_bit(ctrl, TYPE6_ACTION, "action"); + warn_always_zero_bit(ctrl, TYPE6_TIMING, "timing"); + warn_always_zero_bit(ctrl, TYPE6_SELECT, "select"); + warn_always_zero_bit(ctrl, TYPE6_HIT, "hit"); + + /* validate size encoding */ + size = extract32(ctrl, 16, 4); + if (access_size[size] == -1) { + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n", + size); + } else { + val |= (ctrl & TYPE6_SIZE); + } + + /* keep the mode and attribute bits */ + val |= (ctrl & (TYPE6_VU | TYPE6_VS | TYPE6_U | TYPE6_S | TYPE6_M | + TYPE6_LOAD | TYPE6_STORE | TYPE6_EXEC)); + + return val; +} + +static void type6_breakpoint_insert(CPURISCVState *env, target_ulong index) +{ + target_ulong ctrl = env->tdata1[index]; + target_ulong addr = env->tdata2[index]; + bool enabled = type6_breakpoint_enabled(ctrl); + CPUState *cs = env_cpu(env); + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t size; + + if (!enabled) { + return; + } + + if (ctrl & TYPE6_EXEC) { + cpu_breakpoint_insert(cs, addr, flags, &env->cpu_breakpoint[index]); + } + + if (ctrl & TYPE6_LOAD) { + flags |= BP_MEM_READ; + } + + if (ctrl & TYPE6_STORE) { + flags |= BP_MEM_WRITE; + } + + if (flags & BP_MEM_ACCESS) { + size = extract32(ctrl, 16, 4); + if (size != 0) { + cpu_watchpoint_insert(cs, addr, size, flags, + &env->cpu_watchpoint[index]); + } else { + cpu_watchpoint_insert(cs, addr, 8, flags, + &env->cpu_watchpoint[index]); + } + } +} + +static void type6_breakpoint_remove(CPURISCVState *env, target_ulong index) +{ + type2_breakpoint_remove(env, index); +} + +static void type6_reg_write(CPURISCVState *env, target_ulong index, int tdata_index, target_ulong val) { - uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; target_ulong new_val; switch (tdata_index) { case TDATA1: - new_val = type2_mcontrol_validate(env, val); - if (new_val != env->type2_trig[index].mcontrol) { - env->type2_trig[index].mcontrol = new_val; - type2_breakpoint_remove(env, index); - type2_breakpoint_insert(env, index); + new_val = type6_mcontrol6_validate(env, val); + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; + type6_breakpoint_remove(env, index); + type6_breakpoint_insert(env, index); } break; case TDATA2: - if (val != env->type2_trig[index].maddress) { - env->type2_trig[index].maddress = val; - type2_breakpoint_remove(env, index); - type2_breakpoint_insert(env, index); + if (val != env->tdata2[index]) { + env->tdata2[index] = val; + type6_breakpoint_remove(env, index); + type6_breakpoint_insert(env, index); } break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for type 6 trigger\n"); + break; default: g_assert_not_reached(); } @@ -309,35 +498,59 @@ static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, return; } -typedef target_ulong (*tdata_read_func)(CPURISCVState *env, - target_ulong trigger_index, - int tdata_index); - -static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read, -}; - -typedef void (*tdata_write_func)(CPURISCVState *env, - target_ulong trigger_index, - int tdata_index, - target_ulong val); - -static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write, -}; - target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) { - tdata_read_func read_func = trigger_read_funcs[env->trigger_cur]; - - return read_func(env, env->trigger_cur, tdata_index); + switch (tdata_index) { + case TDATA1: + return env->tdata1[env->trigger_cur]; + case TDATA2: + return env->tdata2[env->trigger_cur]; + case TDATA3: + return env->tdata3[env->trigger_cur]; + default: + g_assert_not_reached(); + } } void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) { - tdata_write_func write_func = trigger_write_funcs[env->trigger_cur]; + int trigger_type; + + if (tdata_index == TDATA1) { + trigger_type = extract_trigger_type(env, val); + } else { + trigger_type = get_trigger_type(env, env->trigger_cur); + } + + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + type2_reg_write(env, env->trigger_cur, tdata_index, val); + break; + case TRIGGER_TYPE_AD_MATCH6: + type6_reg_write(env, env->trigger_cur, tdata_index, val); + break; + case TRIGGER_TYPE_INST_CNT: + case TRIGGER_TYPE_INT: + case TRIGGER_TYPE_EXCP: + case TRIGGER_TYPE_EXT_SRC: + qemu_log_mask(LOG_UNIMP, "trigger type: %d is not supported\n", + trigger_type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exit\n", + trigger_type); + break; + default: + g_assert_not_reached(); + } +} - return write_func(env, env->trigger_cur, tdata_index, val); +target_ulong tinfo_csr_read(CPURISCVState *env) +{ + /* assume all triggers support the same types of triggers */ + return BIT(TRIGGER_TYPE_AD_MATCH) | + BIT(TRIGGER_TYPE_AD_MATCH6); } void riscv_cpu_debug_excp_handler(CPUState *cs) @@ -348,11 +561,11 @@ void riscv_cpu_debug_excp_handler(CPUState *cs) if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { cs->watchpoint_hit = NULL; - riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + do_trigger_action(env, DBG_ACTION_BP); } } else { if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) { - riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + do_trigger_action(env, DBG_ACTION_BP); } } } @@ -364,18 +577,51 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) CPUBreakpoint *bp; target_ulong ctrl; target_ulong pc; + int trigger_type; int i; QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { - ctrl = env->type2_trig[i].mcontrol; - pc = env->type2_trig[i].maddress; + for (i = 0; i < RV_MAX_TRIGGERS; i++) { + trigger_type = get_trigger_type(env, i); + + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (riscv_cpu_virt_enabled(env)) { + return false; + } - if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; + ctrl = env->tdata1[i]; + pc = env->tdata2[i]; + + if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } } + break; + case TRIGGER_TYPE_AD_MATCH6: + ctrl = env->tdata1[i]; + pc = env->tdata2[i]; + + if ((ctrl & TYPE6_EXEC) && (bp->pc == pc)) { + if (riscv_cpu_virt_enabled(env)) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 23) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + break; + default: + /* other trigger types are not supported or irrelevant */ + break; } } } @@ -389,26 +635,67 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) CPURISCVState *env = &cpu->env; target_ulong ctrl; target_ulong addr; + int trigger_type; int flags; int i; - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { - ctrl = env->type2_trig[i].mcontrol; - addr = env->type2_trig[i].maddress; - flags = 0; + for (i = 0; i < RV_MAX_TRIGGERS; i++) { + trigger_type = get_trigger_type(env, i); - if (ctrl & TYPE2_LOAD) { - flags |= BP_MEM_READ; - } - if (ctrl & TYPE2_STORE) { - flags |= BP_MEM_WRITE; - } + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (riscv_cpu_virt_enabled(env)) { + return false; + } + + ctrl = env->tdata1[i]; + addr = env->tdata2[i]; + flags = 0; + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if ((wp->flags & flags) && (wp->vaddr == addr)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + break; + case TRIGGER_TYPE_AD_MATCH6: + ctrl = env->tdata1[i]; + addr = env->tdata2[i]; + flags = 0; + + if (ctrl & TYPE6_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE6_STORE) { + flags |= BP_MEM_WRITE; + } - if ((wp->flags & flags) && (wp->vaddr == addr)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; + if ((wp->flags & flags) && (wp->vaddr == addr)) { + if (riscv_cpu_virt_enabled(env)) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 23) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } } + break; + default: + /* other trigger types are not supported */ + break; } } @@ -417,11 +704,11 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) void riscv_trigger_init(CPURISCVState *env) { - target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH); + target_ulong tdata1 = build_tdata1(env, TRIGGER_TYPE_AD_MATCH, 0, 0); int i; - /* type 2 triggers */ - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + /* init to type 2 triggers */ + for (i = 0; i < RV_MAX_TRIGGERS; i++) { /* * type = TRIGGER_TYPE_AD_MATCH * dmode = 0 (both debug and M-mode can write tdata) @@ -435,9 +722,10 @@ void riscv_trigger_init(CPURISCVState *env) * chain = 0 (unimplemented, always 0) * match = 0 (always 0, when any compare value equals tdata2) */ - env->type2_trig[i].mcontrol = type2; - env->type2_trig[i].maddress = 0; - env->type2_trig[i].bp = NULL; - env->type2_trig[i].wp = NULL; + env->tdata1[i] = tdata1; + env->tdata2[i] = 0; + env->tdata3[i] = 0; + env->cpu_breakpoint[i] = NULL; + env->cpu_watchpoint[i] = NULL; } } |