This is an automated email from Gerrit. Eric Hoffman ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/4224
-- gerrit commit 28d856a607471e95d39815faf070b04143699bc0 Author: Eric Hoffman <[email protected]> Date: Mon Sep 11 12:07:43 2017 -0400 mips32 - Handle PrAcc execution exceptions The PrAcc low-level routine now handle execution exceptions. Doing a simple thing as a memory read/write to an unmapped address will create an exception (nested debug exception). Before the fix, when this occurred, this would leave the MIPS in a state in which the PrAcc execution handler would not recover, and, as a result, you would lose any further possibility to execute debug instructions on the target, including resuming the target. So, basically, you would lose the target until it was hard-rebooted. The fix simply handle the possibiliy that an exception can occur while executing debug instructions (whatever the nature of the intruction queue is), recover gracefully and transparently, and report a new error type: ERROR_EXCEPTION. Plus, I added, to the 'md' and 'mw' CLI commands a proper error message if this is detected: > mwb 0 1 Memory write exception > mdw 0 Memory read exception! > mdw 0x80000000 0x80000000: 00000001 Change-Id: Ib1d47b622b20d8bc71adf2c2a9960e48c0202ed5 Signed-off-by: Eric Hoffman <[email protected]> diff --git a/src/helper/log.h b/src/helper/log.h index 512bcc5..a3fdbcc 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -149,6 +149,7 @@ extern int debug_level; */ #define ERROR_FAIL (-4) #define ERROR_WAIT (-5) +#define ERROR_EXCEPTION (-6) #endif /* OPENOCD_HELPER_LOG_H */ diff --git a/src/target/mips32_pracc.c b/src/target/mips32_pracc.c index 790c8dc..d3b6d9d 100644 --- a/src/target/mips32_pracc.c +++ b/src/target/mips32_pracc.c @@ -172,6 +172,32 @@ int mips32_pracc_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ct bool pass = 0; /* to check the pass through pracc text after function code sent */ int retval; + /* Ensure we are at text start. Don't assume that the last run left us at + text start. In fact that DOES occur in normal circumstances, and it's + not because of error in the previous caller's code. This most usually + occur because of a read/write memory access exception, creating a nested + exception (execption occuring while already in debug mode), which jump + us back to text start while queuing our PrAcc stream. And in turn, + those memory access exception usually occur because the GDB client sent + a request to our server part to read/write somewhere in memory that + does not exists (no mapped TLB), or which there's no write priviledge. */ + retval = mips32_pracc_read_ctrl_addr(ejtag_info); /* update current pa info: control and address */ + if (retval != ERROR_OK) { + LOG_ERROR("Unable to get pracc ctrl/addr"); + return retval; + } + + if ((ejtag_info->pa_ctrl & EJTAG_CTRL_PRNW) || + (ejtag_info->pa_addr != MIPS32_PRACC_TEXT)) { + /* Unclean state. Do a clean jump to to text start. */ + LOG_DEBUG("Last operation did not return to dmseg text start, attempting to jump to it..."); + retval = mips32_pracc_clean_text_jump(ejtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to jump to pracc text start"); + return retval; + } + } + while (1) { if (restart) { if (restart_count < 3) { /* max 3 restarts allowed */ @@ -223,21 +249,30 @@ int mips32_pracc_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ct if (!final_check) { /* executing function code */ /* check address */ if (ejtag_info->pa_addr != (MIPS32_PRACC_TEXT + code_count * 4)) { - LOG_DEBUG("reading at unexpected address %" PRIx32 ", expected %x", - ejtag_info->pa_addr, MIPS32_PRACC_TEXT + code_count * 4); - - /* restart code execution only in some cases */ - if (code_count == 1 && ejtag_info->pa_addr == MIPS32_PRACC_TEXT && - restart_count == 0) { - LOG_DEBUG("restarting, without clean jump"); - restart_count++; - code_count = 0; - continue; - } else if (code_count < 2) { - restart = 1; - continue; + /* This can occur if the last instruction created an exception, + in which case the CPU create nested debug exception, and + jump back to pracc text start. */ + if (ejtag_info->pa_addr == MIPS32_PRACC_TEXT) { + /* We can reasonably assume we've got an exception */ + LOG_DEBUG("Exception during pracc execution"); + retval = ERROR_EXCEPTION; + } else { + LOG_DEBUG("reading at unexpected address %" PRIx32 ", expected %x", + ejtag_info->pa_addr, MIPS32_PRACC_TEXT + code_count * 4); + + /* restart code execution only in some cases */ + if (code_count < 2) { /* allow for restart */ + restart = 1; + continue; + } else { + retval = ERROR_JTAG_DEVICE_ERROR; + } } - return ERROR_JTAG_DEVICE_ERROR; + /* Unclean state. Do a clean jump to to text start before we leave */ + /* Do not check for error, return the error code we set above */ + if (mips32_pracc_clean_text_jump(ejtag_info) != ERROR_OK) + LOG_ERROR("Unable to jump to dmseg text start"); + return retval; } /* check for store instruction at dmseg */ uint32_t store_addr = ctx->pracc_list[code_count].addr; @@ -353,6 +388,8 @@ inline void pracc_queue_free(struct pracc_queue_info *ctx) int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx, uint32_t *buf, bool check_last) { + int retval = ERROR_OK; + if (ctx->retval != ERROR_OK) { LOG_ERROR("Out of memory"); return ERROR_FAIL; @@ -365,6 +402,32 @@ int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_in if (ejtag_info->mode == 0) return mips32_pracc_exec(ejtag_info, ctx, buf, check_last); + /* Ensure we are at text start. Don't assume that the last run left us at + text start. In fact that DOES occur in normal circumstances, and it's + not because of error in the previous caller's code. This most usually + occur because of a read/write memory access exception, creating a nested + exception (execption occuring while already in debug mode), which jump + us back to text start while queuing our PrAcc stream. And in turn, + those memory access exception usually occur because the GDB client sent + a request to our server part to read/write somewhere in memory that + does not exists (no mapped TLB), or which there's no write priviledge. */ + retval = mips32_pracc_read_ctrl_addr(ejtag_info); /* update current pa info: control and address */ + if (retval != ERROR_OK) { + LOG_ERROR("Unable to get pracc ctrl/addr"); + return retval; + } + + if ((ejtag_info->pa_ctrl & EJTAG_CTRL_PRNW) || + (ejtag_info->pa_addr != MIPS32_PRACC_TEXT)) { + /* Unclean state. Do a clean jump to to text start. */ + LOG_DEBUG("Last operation did not return to dmseg text start, attempting to jump to it..."); + retval = mips32_pracc_clean_text_jump(ejtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to jump to pracc text start"); + return retval; + } + } + union scan_in { uint8_t scan_96[12]; struct { @@ -398,7 +461,7 @@ int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_in } } - int retval = jtag_execute_queue(); /* execute queued scans */ + retval = jtag_execute_queue(); /* execute queued scans */ if (retval != ERROR_OK) goto exit; @@ -419,9 +482,22 @@ int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_in goto exit; } if (addr != fetch_addr) { - LOG_ERROR("Fetch addr mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d", - addr, fetch_addr, scan_count); - retval = ERROR_FAIL; + /* This can occur if the last instruction created an exception, + in which case the CPU create nested debug exception, and + jump back to pracc text start. */ + if (addr == MIPS32_PRACC_TEXT) { + /* We can reasonably assume we've got an exception */ + LOG_DEBUG("Exception during pracc execution"); + retval = ERROR_EXCEPTION; + } else { + LOG_ERROR("Fetch addr mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d", + addr, fetch_addr, scan_count); + retval = ERROR_FAIL; + } + /* Unclean state. Do a clean jump to to text start. */ + /* Do not check for error, return the error code we set above */ + if (mips32_pracc_clean_text_jump(ejtag_info) != ERROR_OK) + LOG_ERROR("Unable to jump to dmseg text start"); goto exit; } fetch_addr += 4; diff --git a/src/target/target.c b/src/target/target.c index 8f97666..8b8bfa7 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -3123,6 +3123,10 @@ COMMAND_HANDLER(handle_md_command) int retval = fn(target, address, size, count, buffer); if (ERROR_OK == retval) handle_md_output(CMD_CTX, target, address, size, count, buffer); + else { + if (ERROR_EXCEPTION == retval) + command_print(CMD_CTX, "Memory read exception!"); + } free(buffer); @@ -3190,6 +3194,8 @@ static int target_fill_mem(struct target *target, COMMAND_HANDLER(handle_mw_command) { + int retval = ERROR_OK; + if (CMD_ARGC < 2) return ERROR_COMMAND_SYNTAX_ERROR; bool physical = strcmp(CMD_ARGV[0], "phys") == 0; @@ -3232,7 +3238,13 @@ COMMAND_HANDLER(handle_mw_command) return ERROR_COMMAND_SYNTAX_ERROR; } - return target_fill_mem(target, address, fn, wordsize, value, count); + retval = target_fill_mem(target, address, fn, wordsize, value, count); + if (retval != ERROR_OK) { + if (ERROR_EXCEPTION == retval) + command_print(CMD_CTX, "Memory write exception"); + } + + return retval; } static COMMAND_HELPER(parse_load_image_command_CMD_ARGV, struct image *image, -- ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
