@@ -527,6 +527,8 @@ static void riscv_deinit_target(struct target *target)
527527 if (!info )
528528 return ;
529529
530+ free (info -> reserved_triggers );
531+
530532 range_list_t * entry , * tmp ;
531533 list_for_each_entry_safe (entry , tmp , & info -> hide_csr , list ) {
532534 free (entry -> name );
@@ -620,6 +622,15 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)
620622static int set_trigger (struct target * target , unsigned int idx , riscv_reg_t tdata1 , riscv_reg_t tdata2 ,
621623 riscv_reg_t tdata1_ignore_mask )
622624{
625+ RISCV_INFO (r );
626+ assert (r -> reserved_triggers );
627+ assert (idx < r -> trigger_count );
628+ if (r -> reserved_triggers [idx ]) {
629+ LOG_TARGET_DEBUG (target ,
630+ "Trigger %u is reserved by 'reserve_trigger' command." , idx );
631+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE ;
632+ }
633+
623634 riscv_reg_t tdata1_rb , tdata2_rb ;
624635 // Select which trigger to use
625636 if (riscv_reg_set (target , GDB_REGNO_TSELECT , idx ) != ERROR_OK )
@@ -2453,87 +2464,48 @@ static int riscv_deassert_reset(struct target *target)
24532464 return tt -> deassert_reset (target );
24542465}
24552466
2456- /* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
2457- static int disable_triggers (struct target * target , riscv_reg_t * state )
2467+ /* "wp_is_set" array must have at least "r->trigger_count" items. */
2468+ static int disable_watchpoints (struct target * target , bool * wp_is_set )
24582469{
24592470 RISCV_INFO (r );
2460-
24612471 LOG_TARGET_DEBUG (target , "Disabling triggers." );
24622472
2463- if ( riscv_enumerate_triggers ( target ) != ERROR_OK )
2464- return ERROR_FAIL ;
2465-
2466- if ( r -> manual_hwbp_set ) {
2467- /* Look at every trigger that may have been set. */
2468- riscv_reg_t tselect ;
2469- if ( riscv_reg_get ( target , & tselect , GDB_REGNO_TSELECT ) != ERROR_OK )
2470- return ERROR_FAIL ;
2471- for ( unsigned int t = 0 ; t < r -> trigger_count ; t ++ ) {
2472- if ( riscv_reg_set ( target , GDB_REGNO_TSELECT , t ) != ERROR_OK )
2473- return ERROR_FAIL ;
2474- riscv_reg_t tdata1 ;
2475- if (riscv_reg_get (target , & tdata1 , GDB_REGNO_TDATA1 ) != ERROR_OK )
2473+ /* TODO: The algorithm is flawed and may result in a situation described in
2474+ * https://github.com/riscv-collab/riscv-openocd/issues/1108
2475+ */
2476+ memset ( wp_is_set , false, r -> trigger_count );
2477+ struct watchpoint * watchpoint = target -> watchpoints ;
2478+ int i = 0 ;
2479+ while ( watchpoint ) {
2480+ LOG_TARGET_DEBUG ( target , "Watchpoint %" PRIu32 ": set=%s" ,
2481+ watchpoint -> unique_id ,
2482+ wp_is_set [ i ] ? "true" : "false" );
2483+ wp_is_set [ i ] = watchpoint -> is_set ;
2484+ if ( watchpoint -> is_set ) {
2485+ if (riscv_remove_watchpoint (target , watchpoint ) != ERROR_OK )
24762486 return ERROR_FAIL ;
2477- if (tdata1 & CSR_TDATA1_DMODE (riscv_xlen (target ))) {
2478- state [t ] = tdata1 ;
2479- if (riscv_reg_set (target , GDB_REGNO_TDATA1 , 0 ) != ERROR_OK )
2480- return ERROR_FAIL ;
2481- }
2482- }
2483- if (riscv_reg_set (target , GDB_REGNO_TSELECT , tselect ) != ERROR_OK )
2484- return ERROR_FAIL ;
2485-
2486- } else {
2487- /* Just go through the triggers we manage. */
2488- struct watchpoint * watchpoint = target -> watchpoints ;
2489- int i = 0 ;
2490- while (watchpoint ) {
2491- LOG_TARGET_DEBUG (target , "Watchpoint %d: set=%d" , i , watchpoint -> is_set );
2492- state [i ] = watchpoint -> is_set ;
2493- if (watchpoint -> is_set ) {
2494- if (riscv_remove_watchpoint (target , watchpoint ) != ERROR_OK )
2495- return ERROR_FAIL ;
2496- }
2497- watchpoint = watchpoint -> next ;
2498- i ++ ;
24992487 }
2488+ watchpoint = watchpoint -> next ;
2489+ i ++ ;
25002490 }
25012491
25022492 return ERROR_OK ;
25032493}
25042494
2505- static int enable_triggers (struct target * target , riscv_reg_t * state )
2495+ static int enable_watchpoints (struct target * target , bool * wp_is_set )
25062496{
2507- RISCV_INFO (r );
2508-
2509- if (r -> manual_hwbp_set ) {
2510- /* Look at every trigger that may have been set. */
2511- riscv_reg_t tselect ;
2512- if (riscv_reg_get (target , & tselect , GDB_REGNO_TSELECT ) != ERROR_OK )
2513- return ERROR_FAIL ;
2514- for (unsigned int t = 0 ; t < r -> trigger_count ; t ++ ) {
2515- if (state [t ] != 0 ) {
2516- if (riscv_reg_set (target , GDB_REGNO_TSELECT , t ) != ERROR_OK )
2517- return ERROR_FAIL ;
2518- if (riscv_reg_set (target , GDB_REGNO_TDATA1 , state [t ]) != ERROR_OK )
2519- return ERROR_FAIL ;
2520- }
2521- }
2522- if (riscv_reg_set (target , GDB_REGNO_TSELECT , tselect ) != ERROR_OK )
2523- return ERROR_FAIL ;
2524-
2525- } else {
2526- struct watchpoint * watchpoint = target -> watchpoints ;
2527- int i = 0 ;
2528- while (watchpoint ) {
2529- LOG_TARGET_DEBUG (target , "Watchpoint %d: cleared=%" PRId64 , i , state [i ]);
2530- if (state [i ]) {
2531- if (riscv_add_watchpoint (target , watchpoint ) != ERROR_OK )
2532- return ERROR_FAIL ;
2533- }
2534- watchpoint = watchpoint -> next ;
2535- i ++ ;
2497+ struct watchpoint * watchpoint = target -> watchpoints ;
2498+ int i = 0 ;
2499+ while (watchpoint ) {
2500+ LOG_TARGET_DEBUG (target , "Watchpoint %" PRIu32
2501+ ": %s to be re-enabled." , watchpoint -> unique_id ,
2502+ wp_is_set [i ] ? "needs " : "does not need" );
2503+ if (wp_is_set [i ]) {
2504+ if (riscv_add_watchpoint (target , watchpoint ) != ERROR_OK )
2505+ return ERROR_FAIL ;
25362506 }
2507+ watchpoint = watchpoint -> next ;
2508+ i ++ ;
25372509 }
25382510
25392511 return ERROR_OK ;
@@ -3781,10 +3753,17 @@ static int riscv_openocd_step_impl(struct target *target, int current,
37813753 return ERROR_FAIL ;
37823754 }
37833755
3784- riscv_reg_t trigger_state [RISCV_MAX_HWBPS ] = {0 };
3785- if (disable_triggers (target , trigger_state ) != ERROR_OK )
3756+ if (riscv_enumerate_triggers (target ) != ERROR_OK )
37863757 return ERROR_FAIL ;
37873758
3759+ RISCV_INFO (r );
3760+ bool * wps_to_enable = calloc (sizeof (* wps_to_enable ), r -> trigger_count );
3761+ if (disable_watchpoints (target , wps_to_enable ) != ERROR_OK ) {
3762+ LOG_TARGET_ERROR (target , "Failed to temporarily disable "
3763+ "watchpoints before single-step." );
3764+ return ERROR_FAIL ;
3765+ }
3766+
37883767 bool success = true;
37893768 uint64_t current_mstatus ;
37903769 RISCV_INFO (info );
@@ -3814,9 +3793,10 @@ static int riscv_openocd_step_impl(struct target *target, int current,
38143793 }
38153794
38163795_exit :
3817- if (enable_triggers (target , trigger_state ) != ERROR_OK ) {
3796+ if (enable_watchpoints (target , wps_to_enable ) != ERROR_OK ) {
38183797 success = false;
3819- LOG_TARGET_ERROR (target , "Unable to enable triggers." );
3798+ LOG_TARGET_ERROR (target , "Failed to re-enable watchpoints "
3799+ "after single-step." );
38203800 }
38213801
38223802 if (breakpoint && (riscv_add_breakpoint (target , breakpoint ) != ERROR_OK )) {
@@ -5031,6 +5011,57 @@ COMMAND_HANDLER(riscv_set_enable_trigger_feature)
50315011 return ERROR_OK ;
50325012}
50335013
5014+ static COMMAND_HELPER (report_reserved_triggers , struct target * target )
5015+ {
5016+ RISCV_INFO (r );
5017+ if (riscv_enumerate_triggers (target ) != ERROR_OK )
5018+ return ERROR_FAIL ;
5019+ const char * separator = "" ;
5020+ for (riscv_reg_t t = 0 ; t < r -> trigger_count ; ++ t ) {
5021+ if (r -> reserved_triggers [t ]) {
5022+ command_print_sameline (CMD , "%s%" PRIu64 , separator , t );
5023+ separator = " " ;
5024+ }
5025+ }
5026+ command_print_sameline (CMD , "\n" );
5027+ return ERROR_OK ;
5028+ }
5029+
5030+ COMMAND_HANDLER (handle_reserve_trigger )
5031+ {
5032+ struct target * target = get_current_target (CMD_CTX );
5033+ if (CMD_ARGC == 0 )
5034+ return CALL_COMMAND_HANDLER (report_reserved_triggers , target );
5035+
5036+ if (CMD_ARGC != 2 )
5037+ return ERROR_COMMAND_SYNTAX_ERROR ;
5038+
5039+ riscv_reg_t t ;
5040+ COMMAND_PARSE_NUMBER (u64 , CMD_ARGV [0 ], t );
5041+
5042+ if (riscv_enumerate_triggers (target ) != ERROR_OK )
5043+ return ERROR_FAIL ;
5044+ RISCV_INFO (r );
5045+ if (r -> trigger_count == 0 ) {
5046+ command_print (CMD , "Error: There are no triggers on the target." );
5047+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE ;
5048+ }
5049+ if (t >= r -> trigger_count ) {
5050+ command_print (CMD , "Error: trigger with index %" PRIu64
5051+ " does not exist. There are only %u triggers"
5052+ " on the target (with indexes 0 .. %u)." ,
5053+ t , r -> trigger_count , r -> trigger_count - 1 );
5054+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE ;
5055+ }
5056+ if (r -> trigger_unique_id [t ] != -1 ) {
5057+ command_print (CMD , "Error: trigger with index %" PRIu64
5058+ " is already in use and can not be reserved." , t );
5059+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE ;
5060+ }
5061+ COMMAND_PARSE_ON_OFF (CMD_ARGV [1 ], r -> reserved_triggers [t ]);
5062+ return ERROR_OK ;
5063+ }
5064+
50345065static const struct command_registration riscv_exec_command_handlers [] = {
50355066 {
50365067 .name = "dump_sample_buf" ,
@@ -5287,6 +5318,14 @@ static const struct command_registration riscv_exec_command_handlers[] = {
52875318 .usage = "[('eq'|'napot'|'ge_lt'|'all') ('wp'|'none')]" ,
52885319 .help = "Control whether OpenOCD is allowed to use certain RISC-V trigger features for watchpoints."
52895320 },
5321+ {
5322+ .name = "reserve_trigger" ,
5323+ .handler = handle_reserve_trigger ,
5324+ /* TODO: Move this to COMMAND_ANY */
5325+ .mode = COMMAND_EXEC ,
5326+ .usage = "[index ('on'|'off')]" ,
5327+ .help = "Controls which RISC-V triggers shall not be touched by OpenOCD." ,
5328+ },
52905329 COMMAND_REGISTRATION_DONE
52915330};
52925331
@@ -5711,6 +5750,8 @@ int riscv_enumerate_triggers(struct target *target)
57115750 "Assuming that triggers are not implemented." );
57125751 r -> triggers_enumerated = true;
57135752 r -> trigger_count = 0 ;
5753+ free (r -> reserved_triggers );
5754+ r -> reserved_triggers = NULL ;
57145755 return ERROR_OK ;
57155756 }
57165757
@@ -5745,6 +5786,8 @@ int riscv_enumerate_triggers(struct target *target)
57455786 r -> triggers_enumerated = true;
57465787 r -> trigger_count = t ;
57475788 LOG_TARGET_INFO (target , "Found %d triggers" , r -> trigger_count );
5789+ free (r -> reserved_triggers );
5790+ r -> reserved_triggers = calloc (sizeof (* r -> reserved_triggers ), t );
57485791 create_wp_trigger_cache (target );
57495792 return ERROR_OK ;
57505793}
0 commit comments