target-xtensa: implement instruction breakpoints
Add IBREAKA/IBREAKENABLE SRs and implement debug exception, BREAK and BREAK.N instructions and IBREAK breakpoints. IBREAK breakpoint address is considered constant for TB lifetime. On IBREAKA/IBREAKENABLE change corresponding TBs are invalidated. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
This commit is contained in:
parent
ab58c5b4fd
commit
e61dc8f72c
@ -126,6 +126,8 @@ enum {
|
|||||||
RASID = 90,
|
RASID = 90,
|
||||||
ITLBCFG = 91,
|
ITLBCFG = 91,
|
||||||
DTLBCFG = 92,
|
DTLBCFG = 92,
|
||||||
|
IBREAKENABLE = 96,
|
||||||
|
IBREAKA = 128,
|
||||||
EPC1 = 177,
|
EPC1 = 177,
|
||||||
DEPC = 192,
|
DEPC = 192,
|
||||||
EPS2 = 194,
|
EPS2 = 194,
|
||||||
@ -196,6 +198,7 @@ enum {
|
|||||||
EXC_KERNEL,
|
EXC_KERNEL,
|
||||||
EXC_USER,
|
EXC_USER,
|
||||||
EXC_DOUBLE,
|
EXC_DOUBLE,
|
||||||
|
EXC_DEBUG,
|
||||||
EXC_MAX
|
EXC_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -425,6 +428,7 @@ static inline int cpu_mmu_index(CPUState *env)
|
|||||||
#define XTENSA_TBFLAG_RING_MASK 0x3
|
#define XTENSA_TBFLAG_RING_MASK 0x3
|
||||||
#define XTENSA_TBFLAG_EXCM 0x4
|
#define XTENSA_TBFLAG_EXCM 0x4
|
||||||
#define XTENSA_TBFLAG_LITBASE 0x8
|
#define XTENSA_TBFLAG_LITBASE 0x8
|
||||||
|
#define XTENSA_TBFLAG_DEBUG 0x10
|
||||||
|
|
||||||
static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
|
static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
|
||||||
target_ulong *cs_base, int *flags)
|
target_ulong *cs_base, int *flags)
|
||||||
@ -440,6 +444,11 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
|
|||||||
(env->sregs[LITBASE] & 1)) {
|
(env->sregs[LITBASE] & 1)) {
|
||||||
*flags |= XTENSA_TBFLAG_LITBASE;
|
*flags |= XTENSA_TBFLAG_LITBASE;
|
||||||
}
|
}
|
||||||
|
if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) {
|
||||||
|
if (xtensa_get_cintlevel(env) < env->config->debug_level) {
|
||||||
|
*flags |= XTENSA_TBFLAG_DEBUG;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "cpu-all.h"
|
#include "cpu-all.h"
|
||||||
|
@ -44,6 +44,7 @@ void cpu_reset(CPUXtensaState *env)
|
|||||||
env->sregs[PS] = xtensa_option_enabled(env->config,
|
env->sregs[PS] = xtensa_option_enabled(env->config,
|
||||||
XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
|
XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
|
||||||
env->sregs[VECBASE] = env->config->vecbase;
|
env->sregs[VECBASE] = env->config->vecbase;
|
||||||
|
env->sregs[IBREAKENABLE] = 0;
|
||||||
|
|
||||||
env->pending_irq_level = 0;
|
env->pending_irq_level = 0;
|
||||||
reset_mmu(env);
|
reset_mmu(env);
|
||||||
@ -193,6 +194,7 @@ void do_interrupt(CPUState *env)
|
|||||||
case EXC_KERNEL:
|
case EXC_KERNEL:
|
||||||
case EXC_USER:
|
case EXC_USER:
|
||||||
case EXC_DOUBLE:
|
case EXC_DOUBLE:
|
||||||
|
case EXC_DEBUG:
|
||||||
qemu_log_mask(CPU_LOG_INT, "%s(%d) "
|
qemu_log_mask(CPU_LOG_INT, "%s(%d) "
|
||||||
"pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
|
"pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
|
||||||
__func__, env->exception_index,
|
__func__, env->exception_index,
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
DEF_HELPER_1(exception, void, i32)
|
DEF_HELPER_1(exception, void, i32)
|
||||||
DEF_HELPER_2(exception_cause, void, i32, i32)
|
DEF_HELPER_2(exception_cause, void, i32, i32)
|
||||||
DEF_HELPER_3(exception_cause_vaddr, void, i32, i32, i32)
|
DEF_HELPER_3(exception_cause_vaddr, void, i32, i32, i32)
|
||||||
|
DEF_HELPER_2(debug_exception, void, i32, i32)
|
||||||
|
|
||||||
DEF_HELPER_1(nsa, i32, i32)
|
DEF_HELPER_1(nsa, i32, i32)
|
||||||
DEF_HELPER_1(nsau, i32, i32)
|
DEF_HELPER_1(nsau, i32, i32)
|
||||||
DEF_HELPER_1(wsr_windowbase, void, i32)
|
DEF_HELPER_1(wsr_windowbase, void, i32)
|
||||||
@ -29,4 +31,7 @@ DEF_HELPER_2(itlb, void, i32, i32)
|
|||||||
DEF_HELPER_2(ptlb, i32, i32, i32)
|
DEF_HELPER_2(ptlb, i32, i32, i32)
|
||||||
DEF_HELPER_3(wtlb, void, i32, i32, i32)
|
DEF_HELPER_3(wtlb, void, i32, i32, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_1(wsr_ibreakenable, void, i32)
|
||||||
|
DEF_HELPER_2(wsr_ibreaka, void, i32, i32)
|
||||||
|
|
||||||
#include "def-helper.h"
|
#include "def-helper.h"
|
||||||
|
@ -134,6 +134,19 @@ void HELPER(exception_cause_vaddr)(uint32_t pc, uint32_t cause, uint32_t vaddr)
|
|||||||
HELPER(exception_cause)(pc, cause);
|
HELPER(exception_cause)(pc, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HELPER(debug_exception)(uint32_t pc, uint32_t cause)
|
||||||
|
{
|
||||||
|
unsigned level = env->config->debug_level;
|
||||||
|
|
||||||
|
env->pc = pc;
|
||||||
|
env->sregs[DEBUGCAUSE] = cause;
|
||||||
|
env->sregs[EPC1 + level - 1] = pc;
|
||||||
|
env->sregs[EPS2 + level - 2] = env->sregs[PS];
|
||||||
|
env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM |
|
||||||
|
(level << PS_INTLEVEL_SHIFT);
|
||||||
|
HELPER(exception)(EXC_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t HELPER(nsa)(uint32_t v)
|
uint32_t HELPER(nsa)(uint32_t v)
|
||||||
{
|
{
|
||||||
if (v & 0x80000000) {
|
if (v & 0x80000000) {
|
||||||
@ -662,3 +675,28 @@ void HELPER(wtlb)(uint32_t p, uint32_t v, uint32_t dtlb)
|
|||||||
split_tlb_entry_spec(v, dtlb, &vpn, &wi, &ei);
|
split_tlb_entry_spec(v, dtlb, &vpn, &wi, &ei);
|
||||||
xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
|
xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HELPER(wsr_ibreakenable)(uint32_t v)
|
||||||
|
{
|
||||||
|
uint32_t change = v ^ env->sregs[IBREAKENABLE];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < env->config->nibreak; ++i) {
|
||||||
|
if (change & (1 << i)) {
|
||||||
|
tb_invalidate_phys_page_range(
|
||||||
|
env->sregs[IBREAKA + i], env->sregs[IBREAKA + i] + 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(wsr_ibreaka)(uint32_t i, uint32_t v)
|
||||||
|
{
|
||||||
|
if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) {
|
||||||
|
tb_invalidate_phys_page_range(
|
||||||
|
env->sregs[IBREAKA + i], env->sregs[IBREAKA + i] + 1, 0);
|
||||||
|
tb_invalidate_phys_page_range(v, v + 1, 0);
|
||||||
|
}
|
||||||
|
env->sregs[IBREAKA + i] = v;
|
||||||
|
}
|
||||||
|
@ -61,6 +61,8 @@ typedef struct DisasContext {
|
|||||||
|
|
||||||
uint32_t ccount_delta;
|
uint32_t ccount_delta;
|
||||||
unsigned used_window;
|
unsigned used_window;
|
||||||
|
|
||||||
|
bool debug;
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
|
||||||
static TCGv_ptr cpu_env;
|
static TCGv_ptr cpu_env;
|
||||||
@ -91,6 +93,9 @@ static const char * const sregnames[256] = {
|
|||||||
[RASID] = "RASID",
|
[RASID] = "RASID",
|
||||||
[ITLBCFG] = "ITLBCFG",
|
[ITLBCFG] = "ITLBCFG",
|
||||||
[DTLBCFG] = "DTLBCFG",
|
[DTLBCFG] = "DTLBCFG",
|
||||||
|
[IBREAKENABLE] = "IBREAKENABLE",
|
||||||
|
[IBREAKA] = "IBREAKA0",
|
||||||
|
[IBREAKA + 1] = "IBREAKA1",
|
||||||
[EPC1] = "EPC1",
|
[EPC1] = "EPC1",
|
||||||
[EPC1 + 1] = "EPC2",
|
[EPC1 + 1] = "EPC2",
|
||||||
[EPC1 + 2] = "EPC3",
|
[EPC1 + 2] = "EPC3",
|
||||||
@ -284,6 +289,19 @@ static void gen_exception_cause_vaddr(DisasContext *dc, uint32_t cause,
|
|||||||
tcg_temp_free(tcause);
|
tcg_temp_free(tcause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_debug_exception(DisasContext *dc, uint32_t cause)
|
||||||
|
{
|
||||||
|
TCGv_i32 tpc = tcg_const_i32(dc->pc);
|
||||||
|
TCGv_i32 tcause = tcg_const_i32(cause);
|
||||||
|
gen_advance_ccount(dc);
|
||||||
|
gen_helper_debug_exception(tpc, tcause);
|
||||||
|
tcg_temp_free(tpc);
|
||||||
|
tcg_temp_free(tcause);
|
||||||
|
if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) {
|
||||||
|
dc->is_jmp = DISAS_UPDATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_check_privilege(DisasContext *dc)
|
static void gen_check_privilege(DisasContext *dc)
|
||||||
{
|
{
|
||||||
if (dc->cring) {
|
if (dc->cring) {
|
||||||
@ -493,6 +511,24 @@ static void gen_wsr_tlbcfg(DisasContext *dc, uint32_t sr, TCGv_i32 v)
|
|||||||
tcg_gen_andi_i32(cpu_SR[sr], v, 0x01130000);
|
tcg_gen_andi_i32(cpu_SR[sr], v, 0x01130000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_wsr_ibreakenable(DisasContext *dc, uint32_t sr, TCGv_i32 v)
|
||||||
|
{
|
||||||
|
gen_helper_wsr_ibreakenable(v);
|
||||||
|
gen_jumpi_check_loop_end(dc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_wsr_ibreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v)
|
||||||
|
{
|
||||||
|
unsigned id = sr - IBREAKA;
|
||||||
|
|
||||||
|
if (id < dc->config->nibreak) {
|
||||||
|
TCGv_i32 tmp = tcg_const_i32(id);
|
||||||
|
gen_helper_wsr_ibreaka(tmp, v);
|
||||||
|
tcg_temp_free(tmp);
|
||||||
|
gen_jumpi_check_loop_end(dc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v)
|
static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v)
|
||||||
{
|
{
|
||||||
tcg_gen_andi_i32(cpu_SR[sr], v,
|
tcg_gen_andi_i32(cpu_SR[sr], v,
|
||||||
@ -572,6 +608,9 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
|
|||||||
[RASID] = gen_wsr_rasid,
|
[RASID] = gen_wsr_rasid,
|
||||||
[ITLBCFG] = gen_wsr_tlbcfg,
|
[ITLBCFG] = gen_wsr_tlbcfg,
|
||||||
[DTLBCFG] = gen_wsr_tlbcfg,
|
[DTLBCFG] = gen_wsr_tlbcfg,
|
||||||
|
[IBREAKENABLE] = gen_wsr_ibreakenable,
|
||||||
|
[IBREAKA] = gen_wsr_ibreaka,
|
||||||
|
[IBREAKA + 1] = gen_wsr_ibreaka,
|
||||||
[INTSET] = gen_wsr_intset,
|
[INTSET] = gen_wsr_intset,
|
||||||
[INTCLEAR] = gen_wsr_intclear,
|
[INTCLEAR] = gen_wsr_intclear,
|
||||||
[INTENABLE] = gen_wsr_intenable,
|
[INTENABLE] = gen_wsr_intenable,
|
||||||
@ -975,8 +1014,10 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: /*BREAKx*/
|
case 4: /*BREAKx*/
|
||||||
HAS_OPTION(XTENSA_OPTION_EXCEPTION);
|
HAS_OPTION(XTENSA_OPTION_DEBUG);
|
||||||
TBD();
|
if (dc->debug) {
|
||||||
|
gen_debug_exception(dc, DEBUGCAUSE_BI);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: /*SYSCALLx*/
|
case 5: /*SYSCALLx*/
|
||||||
@ -2356,7 +2397,10 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: /*BREAK.Nn*/
|
case 2: /*BREAK.Nn*/
|
||||||
TBD();
|
HAS_OPTION(XTENSA_OPTION_DEBUG);
|
||||||
|
if (dc->debug) {
|
||||||
|
gen_debug_exception(dc, DEBUGCAUSE_BN);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /*NOP.Nn*/
|
case 3: /*NOP.Nn*/
|
||||||
@ -2409,6 +2453,19 @@ static void check_breakpoint(CPUState *env, DisasContext *dc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_ibreak_check(CPUState *env, DisasContext *dc)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < dc->config->nibreak; ++i) {
|
||||||
|
if ((env->sregs[IBREAKENABLE] & (1 << i)) &&
|
||||||
|
env->sregs[IBREAKA + i] == dc->pc) {
|
||||||
|
gen_debug_exception(dc, DEBUGCAUSE_IB);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_intermediate_code_internal(
|
static void gen_intermediate_code_internal(
|
||||||
CPUState *env, TranslationBlock *tb, int search_pc)
|
CPUState *env, TranslationBlock *tb, int search_pc)
|
||||||
{
|
{
|
||||||
@ -2435,6 +2492,7 @@ static void gen_intermediate_code_internal(
|
|||||||
dc.lend = env->sregs[LEND];
|
dc.lend = env->sregs[LEND];
|
||||||
dc.is_jmp = DISAS_NEXT;
|
dc.is_jmp = DISAS_NEXT;
|
||||||
dc.ccount_delta = 0;
|
dc.ccount_delta = 0;
|
||||||
|
dc.debug = tb->flags & XTENSA_TBFLAG_DEBUG;
|
||||||
|
|
||||||
init_litbase(&dc);
|
init_litbase(&dc);
|
||||||
init_sar_tracker(&dc);
|
init_sar_tracker(&dc);
|
||||||
@ -2474,6 +2532,10 @@ static void gen_intermediate_code_internal(
|
|||||||
gen_io_start();
|
gen_io_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dc.debug) {
|
||||||
|
gen_ibreak_check(env, &dc);
|
||||||
|
}
|
||||||
|
|
||||||
disas_xtensa_insn(&dc);
|
disas_xtensa_insn(&dc);
|
||||||
++insn_count;
|
++insn_count;
|
||||||
if (env->singlestep_enabled) {
|
if (env->singlestep_enabled) {
|
||||||
|
Loading…
Reference in New Issue
Block a user