diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index beeeedcce..8eacf8c38 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -705,7 +705,9 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec * that are to be ignored. */ if (gdb_connection->frontend_state == TARGET_RUNNING) { - char sig_reply[4]; + char sig_reply[20]; + char stop_reason[20]; + int sig_reply_len; int signal_var; /* stop forwarding log packets! */ @@ -717,8 +719,36 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec } else signal_var = gdb_last_signal(target); - snprintf(sig_reply, 4, "T%2.2x", signal_var); - gdb_put_packet(connection, sig_reply, 3); + stop_reason[0] = '\0'; + if (target->debug_reason == DBG_REASON_WATCHPOINT) { + enum watchpoint_rw hit_wp_type; + uint32_t hit_wp_address; + + if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) { + + switch (hit_wp_type) { + case WPT_WRITE: + snprintf(stop_reason, sizeof(stop_reason), + "watch:%08x;", hit_wp_address); + break; + case WPT_READ: + snprintf(stop_reason, sizeof(stop_reason), + "rwatch:%08x;", hit_wp_address); + break; + case WPT_ACCESS: + snprintf(stop_reason, sizeof(stop_reason), + "awatch:%08x;", hit_wp_address); + break; + default: + break; + } + } + } + + sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s", + signal_var, stop_reason); + + gdb_put_packet(connection, sig_reply, sig_reply_len); gdb_connection->frontend_state = TARGET_HALTED; rtos_update_threads(target); } diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c index 436acddcf..422705bc9 100644 --- a/src/target/breakpoints.c +++ b/src/target/breakpoints.c @@ -500,3 +500,22 @@ void watchpoint_clear_target(struct target *target) while (target->watchpoints != NULL) watchpoint_free(target, target->watchpoints); } + +int watchpoint_hit(struct target *target, enum watchpoint_rw *rw, uint32_t *address) +{ + int retval; + struct watchpoint *hit_watchpoint; + + retval = target_hit_watchpoint(target, &hit_watchpoint); + if (retval != ERROR_OK) + return ERROR_FAIL; + + *rw = hit_watchpoint->rw; + *address = hit_watchpoint->address; + + LOG_DEBUG("Found hit watchpoint at 0x%8.8" PRIx32 " (WPID: %d)", + hit_watchpoint->address, + hit_watchpoint->unique_id); + + return ERROR_OK; +} diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h index d9f4ba5d9..0246acd3d 100644 --- a/src/target/breakpoints.h +++ b/src/target/breakpoints.h @@ -72,4 +72,7 @@ int watchpoint_add(struct target *target, enum watchpoint_rw rw, uint32_t value, uint32_t mask); void watchpoint_remove(struct target *target, uint32_t address); +/* report type and address of just hit watchpoint */ +int watchpoint_hit(struct target *target, enum watchpoint_rw *rw, uint32_t *address); + #endif /* BREAKPOINTS_H */ diff --git a/src/target/nds32_v2.c b/src/target/nds32_v2.c index 90961d7d8..3f5f636fd 100644 --- a/src/target/nds32_v2.c +++ b/src/target/nds32_v2.c @@ -534,6 +534,42 @@ static int nds32_v2_get_exception_address(struct nds32 *nds32, return ERROR_OK; } +/** + * find out which watchpoint hits + * get exception address and compare the address to watchpoints + */ +static int nds32_v2_hit_watchpoint(struct target *target, + struct watchpoint **hit_watchpoint) +{ + uint32_t exception_address; + struct watchpoint *wp; + static struct watchpoint scan_all_watchpoint; + struct nds32 *nds32 = target_to_nds32(target); + + scan_all_watchpoint.address = 0; + scan_all_watchpoint.rw = WPT_WRITE; + scan_all_watchpoint.next = 0; + scan_all_watchpoint.unique_id = 0x5CA8; + + exception_address = nds32->watched_address; + + if (exception_address == 0) { + /* send watch:0 to tell GDB to do software scan for hitting multiple watchpoints */ + *hit_watchpoint = &scan_all_watchpoint; + return ERROR_OK; + } + + for (wp = target->watchpoints; wp; wp = wp->next) { + if (((exception_address ^ wp->address) & (~wp->mask)) == 0) { + /* TODO: dispel false match */ + *hit_watchpoint = wp; + return ERROR_OK; + } + } + + return ERROR_FAIL; +} + static int nds32_v2_run_algorithm(struct target *target, int num_mem_params, struct mem_param *mem_params, @@ -747,6 +783,7 @@ struct target_type nds32_v2_target = { .remove_breakpoint = nds32_v2_remove_breakpoint, .add_watchpoint = nds32_v2_add_watchpoint, .remove_watchpoint = nds32_v2_remove_watchpoint, + .hit_watchpoint = nds32_v2_hit_watchpoint, /* MMU */ .mmu = nds32_mmu, diff --git a/src/target/nds32_v3.c b/src/target/nds32_v3.c index dc0ca5a67..868260dc9 100644 --- a/src/target/nds32_v3.c +++ b/src/target/nds32_v3.c @@ -506,6 +506,7 @@ struct target_type nds32_v3_target = { .remove_breakpoint = nds32_v3_remove_breakpoint, .add_watchpoint = nds32_v3_add_watchpoint, .remove_watchpoint = nds32_v3_remove_watchpoint, + .hit_watchpoint = nds32_v3_hit_watchpoint, /* MMU */ .mmu = nds32_mmu, diff --git a/src/target/nds32_v3_common.c b/src/target/nds32_v3_common.c index 49d841311..2fbd1a378 100644 --- a/src/target/nds32_v3_common.c +++ b/src/target/nds32_v3_common.c @@ -294,6 +294,45 @@ int nds32_v3_checksum_memory(struct target *target, return ERROR_FAIL; } +/** + * find out which watchpoint hits + * get exception address and compare the address to watchpoints + */ +int nds32_v3_hit_watchpoint(struct target *target, + struct watchpoint **hit_watchpoint) +{ + static struct watchpoint scan_all_watchpoint; + + uint32_t exception_address; + struct watchpoint *wp; + struct nds32 *nds32 = target_to_nds32(target); + + exception_address = nds32->watched_address; + + if (exception_address == 0xFFFFFFFF) + return ERROR_FAIL; + + if (exception_address == 0) { + scan_all_watchpoint.address = 0; + scan_all_watchpoint.rw = WPT_WRITE; + scan_all_watchpoint.next = 0; + scan_all_watchpoint.unique_id = 0x5CA8; + + *hit_watchpoint = &scan_all_watchpoint; + return ERROR_OK; + } + + for (wp = target->watchpoints; wp; wp = wp->next) { + if (((exception_address ^ wp->address) & (~wp->mask)) == 0) { + *hit_watchpoint = wp; + + return ERROR_OK; + } + } + + return ERROR_FAIL; +} + int nds32_v3_target_create_common(struct target *target, struct nds32 *nds32) { nds32->register_map = nds32_v3_register_mapping; diff --git a/src/target/nds32_v3m.c b/src/target/nds32_v3m.c index 189873232..c37798afb 100644 --- a/src/target/nds32_v3m.c +++ b/src/target/nds32_v3m.c @@ -494,6 +494,7 @@ struct target_type nds32_v3m_target = { .remove_breakpoint = nds32_v3m_remove_breakpoint, .add_watchpoint = nds32_v3m_add_watchpoint, .remove_watchpoint = nds32_v3m_remove_watchpoint, + .hit_watchpoint = nds32_v3_hit_watchpoint, /* MMU */ .mmu = nds32_mmu, diff --git a/src/target/target.c b/src/target/target.c index c5b80d647..4c31fbea2 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -1035,6 +1035,23 @@ int target_remove_watchpoint(struct target *target, { return target->type->remove_watchpoint(target, watchpoint); } +int target_hit_watchpoint(struct target *target, + struct watchpoint **hit_watchpoint) +{ + if (target->state != TARGET_HALTED) { + LOG_WARNING("target %s is not halted", target->cmd_name); + return ERROR_TARGET_NOT_HALTED; + } + + if (target->type->hit_watchpoint == NULL) { + /* For backward compatible, if hit_watchpoint is not implemented, + * return ERROR_FAIL such that gdb_server will not take the nonsense + * information. */ + return ERROR_FAIL; + } + + return target->type->hit_watchpoint(target, hit_watchpoint); +} int target_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, diff --git a/src/target/target.h b/src/target/target.h index 42414c692..09895bbab 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -399,6 +399,14 @@ int target_add_watchpoint(struct target *target, int target_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); +/** + * Find out the just hit @a watchpoint for @a target. + * + * This routine is a wrapper for target->type->hit_watchpoint. + */ +int target_hit_watchpoint(struct target *target, + struct watchpoint **watchpoint); + /** * Obtain the registers for GDB. * diff --git a/src/target/target_type.h b/src/target/target_type.h index 4d9a33f99..0b8d5daa1 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -174,6 +174,11 @@ struct target_type { */ int (*remove_watchpoint)(struct target *target, struct watchpoint *watchpoint); + /* Find out just hit watchpoint. After the target hits a watchpoint, the + * information could assist gdb to locate where the modified/accessed memory is. + */ + int (*hit_watchpoint)(struct target *target, struct watchpoint **hit_watchpoint); + /** * Target algorithm support. Do @b not call this method directly, * use target_run_algorithm() instead.