From 87c0cda00fad8ec5c42e0efb3789a2ac8808d5bc Mon Sep 17 00:00:00 2001 From: Erhan Kurubas <43336369+erhankur@users.noreply.github.com> Date: Tue, 1 Mar 2022 19:05:54 +0100 Subject: [PATCH] riscv: implement maskisr steponly command (#681) * riscv: implement maskisr steponly command Change-Id: I1a3b666d466b064460c3acc307a36485ce165601 Signed-off-by: Erhan Kurubas * riscv: restore triggers and irq mask inside step function Change-Id: I4e1b0665f4f2f75e42a6191c61634bdfa19ae2fb Signed-off-by: Erhan Kurubas * doc: update for riscv set_maskisr command Change-Id: Ia7d3a6df846cfc4568d79558f719e93f038aee9b Signed-off-by: Erhan Kurubas --- doc/openocd.texi | 7 ++ src/target/riscv/riscv.c | 153 +++++++++++++++++++++++++++++++-------- src/target/riscv/riscv.h | 12 +++ 3 files changed, 140 insertions(+), 32 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 9e2f5e8f2..a94e4be29 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -10424,6 +10424,13 @@ tunneled DR scan consists of: @end deffn +@deffn {Command} {riscv set_maskisr} [@option{off}|@option{steponly}] +Selects whether interrupts will be disabled when single stepping. The default configuration is @option{off}. +This feature is only useful on hardware that always steps into interrupts and doesn't support dcsr.stepie=0. +Keep in mind, disabling the option does not guarantee that single stepping will go into interrupt handlers. +To make that happen, dcsr.stepie would have to be written to 1 as well. +@end deffn + @deffn {Command} {riscv set_ebreakm} on|off Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 91d1020da..cba4bf92f 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1953,26 +1953,11 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } } - /* Disable Interrupts before attempting to run the algorithm. */ uint64_t current_mstatus; - uint8_t mstatus_bytes[8] = { 0 }; - - LOG_DEBUG("Disabling Interrupts"); - struct reg *reg_mstatus = register_get_by_name(target->reg_cache, - "mstatus", true); - if (!reg_mstatus) { - LOG_ERROR("Couldn't find mstatus!"); + uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; + if (riscv_interrupts_disable(target, irq_disabled_mask, ¤t_mstatus) != ERROR_OK) return ERROR_FAIL; - } - - reg_mstatus->type->get(reg_mstatus); - current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); - uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; - buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus, - ie_mask, 0)); - - reg_mstatus->type->set(reg_mstatus, mstatus_bytes); /* Run algorithm */ LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point); @@ -2028,9 +2013,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } /* Restore Interrupts */ - LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus); - reg_mstatus->type->set(reg_mstatus, mstatus_bytes); + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) + return ERROR_FAIL; /* Restore registers */ uint8_t buf[8] = { 0 }; @@ -2366,7 +2350,7 @@ int riscv_openocd_poll(struct target *target) } int riscv_openocd_step(struct target *target, int current, - target_addr_t address, int handle_breakpoints) + target_addr_t address, int handle_breakpoints) { LOG_DEBUG("stepping rtos hart"); @@ -2377,23 +2361,48 @@ int riscv_openocd_step(struct target *target, int current, if (disable_triggers(target, trigger_state) != ERROR_OK) return ERROR_FAIL; - int out = riscv_step_rtos_hart(target); - if (out != ERROR_OK) { + bool success = true; + uint64_t current_mstatus; + RISCV_INFO(info); + + if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) { + /* Disable Interrupts before stepping. */ + uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; + if (riscv_interrupts_disable(target, irq_disabled_mask, + ¤t_mstatus) != ERROR_OK) { + success = false; + LOG_ERROR("unable to disable interrupts"); + goto _exit; + } + } + + if (riscv_step_rtos_hart(target) != ERROR_OK) { + success = false; LOG_ERROR("unable to step rtos hart"); - return out; } register_cache_invalidate(target->reg_cache); - if (enable_triggers(target, trigger_state) != ERROR_OK) - return ERROR_FAIL; + if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) { + success = false; + LOG_ERROR("unable to restore interrupts"); + } - target->state = TARGET_RUNNING; - target_call_event_callbacks(target, TARGET_EVENT_RESUMED); - target->state = TARGET_HALTED; - target->debug_reason = DBG_REASON_SINGLESTEP; - target_call_event_callbacks(target, TARGET_EVENT_HALTED); - return out; +_exit: + if (enable_triggers(target, trigger_state) != ERROR_OK) { + success = false; + LOG_ERROR("unable to enable triggers"); + } + + if (success) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + target->state = TARGET_HALTED; + target->debug_reason = DBG_REASON_SINGLESTEP; + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + return success ? ERROR_OK : ERROR_FAIL; } /* Command Handlers */ @@ -2936,6 +2945,31 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_maskisr) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + + static const struct jim_nvp nvp_maskisr_modes[] = { + { .name = "off", .value = RISCV_ISRMASK_OFF }, + { .name = "steponly", .value = RISCV_ISRMASK_STEPONLY }, + { .name = NULL, .value = -1 }, + }; + const struct jim_nvp *n; + + if (CMD_ARGC > 0) { + n = jim_nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]); + if (!n->name) + return ERROR_COMMAND_SYNTAX_ERROR; + info->isrmask_mode = n->value; + } else { + n = jim_nvp_value2name_simple(nvp_maskisr_modes, info->isrmask_mode); + command_print(CMD, "riscv interrupt mask %s", n->name); + } + + return ERROR_OK; +} + COMMAND_HANDLER(riscv_set_enable_virt2phys) { if (CMD_ARGC != 1) { @@ -3345,6 +3379,13 @@ static const struct command_registration riscv_exec_command_handlers[] = { "(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , " "1: DATA_REGISTER}" }, + { + .name = "set_maskisr", + .handler = riscv_set_maskisr, + .mode = COMMAND_EXEC, + .help = "mask riscv interrupts", + .usage = "['off'|'steponly']", + }, { .name = "set_enable_virt2phys", .handler = riscv_set_enable_virt2phys, @@ -3489,6 +3530,8 @@ void riscv_info_init(struct target *target, riscv_info_t *r) r->xlen = -1; + r->isrmask_mode = RISCV_ISRMASK_OFF; + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; @@ -3520,6 +3563,52 @@ static int riscv_resume_go_all_harts(struct target *target) return ERROR_OK; } +int riscv_interrupts_disable(struct target *target, uint64_t irq_mask, uint64_t *old_mstatus) +{ + LOG_DEBUG("Disabling Interrupts"); + struct reg *reg_mstatus = register_get_by_name(target->reg_cache, + "mstatus", true); + if (!reg_mstatus) { + LOG_ERROR("Couldn't find mstatus!"); + return ERROR_FAIL; + } + + int retval = reg_mstatus->type->get(reg_mstatus); + if (retval != ERROR_OK) + return retval; + + RISCV_INFO(info); + uint8_t mstatus_bytes[8] = { 0 }; + uint64_t current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); + buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus, + irq_mask, 0)); + + retval = reg_mstatus->type->set(reg_mstatus, mstatus_bytes); + if (retval != ERROR_OK) + return retval; + + if (old_mstatus) + *old_mstatus = current_mstatus; + + return ERROR_OK; +} + +int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus) +{ + LOG_DEBUG("Restore Interrupts"); + struct reg *reg_mstatus = register_get_by_name(target->reg_cache, + "mstatus", true); + if (!reg_mstatus) { + LOG_ERROR("Couldn't find mstatus!"); + return ERROR_FAIL; + } + + RISCV_INFO(info); + uint8_t mstatus_bytes[8]; + buf_set_u64(mstatus_bytes, 0, info->xlen, old_mstatus); + return reg_mstatus->type->set(reg_mstatus, mstatus_bytes); +} + int riscv_step_rtos_hart(struct target *target) { RISCV_INFO(r); diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index f1104e35b..5dc2152c6 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -56,6 +56,13 @@ enum riscv_halt_reason { RISCV_HALT_ERROR }; +enum riscv_isrmasking_mode { + /* RISCV_ISRMASK_AUTO, */ /* not supported yet */ + RISCV_ISRMASK_OFF, + /* RISCV_ISRMASK_ON, */ /* not supported yet */ + RISCV_ISRMASK_STEPONLY, +}; + typedef struct { struct target *target; unsigned custom_number; @@ -135,6 +142,8 @@ typedef struct { /* This target was selected using hasel. */ bool selected; + enum riscv_isrmasking_mode isrmask_mode; + /* Helper functions that target the various RISC-V debug spec * implementations. */ int (*get_register)(struct target *target, riscv_reg_t *value, int regid); @@ -403,4 +412,7 @@ void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *fie int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); +int riscv_interrupts_disable(struct target *target, uint64_t ie_mask, uint64_t *old_mstatus); +int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus); + #endif