6060662475d64976b4e2f0d1d36936f610fa707a
[fw/openocd] / src / target / espressif / esp_xtensa_smp.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   ESP Xtensa SMP target API for OpenOCD                                 *
5  *   Copyright (C) 2020 Espressif Systems Ltd. Co                          *
6  ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "assert.h"
13 #include <target/target.h>
14 #include <target/target_type.h>
15 #include <target/smp.h>
16 #include "esp_xtensa_smp.h"
17
18 /*
19 Multiprocessor stuff common:
20
21 The ESP Xtensa chip can have several cores in it, which can run in SMP-mode if an
22 SMP-capable OS is running. The hardware has a few features which makes
23 SMP debugging much easier.
24
25 First of all, there's something called a 'break network', consisting of a
26 BreakIn input  and a BreakOut output on each CPU. The idea is that as soon
27 as a CPU goes into debug mode for whatever reason, it'll signal that using
28 its DebugOut pin. This signal is connected to the other CPU's DebugIn
29 input, causing this CPU also to go into debugging mode. To resume execution
30 when using only this break network, we will need to manually resume both
31 CPUs.
32
33 An alternative to this is the XOCDMode output and the RunStall (or DebugStall)
34 input. When these are cross-connected, a CPU that goes into debug mode will
35 halt execution entirely on the other CPU. Execution on the other CPU can be
36 resumed by either the first CPU going out of debug mode, or the second CPU
37 going into debug mode: the stall is temporarily lifted as long as the stalled
38 CPU is in debug mode.
39
40 A third, separate, signal is CrossTrigger. This is connected in the same way
41 as the breakIn/breakOut network, but is for the TRAX (trace memory) feature;
42 it does not affect OCD in any way.
43 */
44
45 /*
46 Multiprocessor stuff:
47
48 The ESP Xtensa chip has several Xtensa cores inside, but represent themself to the OCD
49 as one chip that works in multithreading mode under FreeRTOS OS.
50 The core that initiate the stop condition will be defined as an active cpu.
51 When one core stops, then other core will be stopped automatically by smpbreak.
52 The core that initiates stop condition will be defined as an active core, and
53 registers of this core will be transferred.
54 */
55
56 #define ESP_XTENSA_SMP_EXAMINE_OTHER_CORES      5
57
58 static int esp_xtensa_smp_update_halt_gdb(struct target *target, bool *need_resume);
59
60 static inline struct esp_xtensa_smp_common *target_to_esp_xtensa_smp(struct target *target)
61 {
62         return container_of(target->arch_info, struct esp_xtensa_smp_common, esp_xtensa);
63 }
64
65 int esp_xtensa_smp_assert_reset(struct target *target)
66 {
67         return ERROR_OK;
68 }
69
70 int esp_xtensa_smp_deassert_reset(struct target *target)
71 {
72         LOG_TARGET_DEBUG(target, "begin");
73
74         int ret = xtensa_deassert_reset(target);
75         if (ret != ERROR_OK)
76                 return ret;
77         /* in SMP mode when chip was running single-core app the other core can be left un-examined,
78            because examination is done before SOC reset. But after SOC reset it is functional and should be handled.
79            So try to examine un-examined core just after SOC reset */
80         if (target->smp && !target_was_examined(target))
81                 ret = xtensa_examine(target);
82         return ret;
83 }
84
85 int esp_xtensa_smp_soft_reset_halt(struct target *target)
86 {
87         int res;
88         struct target_list *head;
89         struct esp_xtensa_smp_common *esp_xtensa_smp = target_to_esp_xtensa_smp(target);
90
91         LOG_TARGET_DEBUG(target, "begin");
92         /* in SMP mode we need to ensure that at first we reset SOC on PRO-CPU
93            and then call xtensa_assert_reset() for all cores */
94         if (target->smp && target->coreid != 0)
95                 return ERROR_OK;
96         /* Reset the SoC first */
97         if (esp_xtensa_smp->chip_ops->reset) {
98                 res = esp_xtensa_smp->chip_ops->reset(target);
99                 if (res != ERROR_OK)
100                         return res;
101         }
102         if (!target->smp)
103                 return xtensa_assert_reset(target);
104
105         foreach_smp_target(head, target->smp_targets) {
106                 res = xtensa_assert_reset(head->target);
107                 if (res != ERROR_OK)
108                         return res;
109         }
110         return ERROR_OK;
111 }
112
113 static struct target *get_halted_esp_xtensa_smp(struct target *target, int32_t coreid)
114 {
115         struct target_list *head;
116         struct target *curr;
117
118         foreach_smp_target(head, target->smp_targets) {
119                 curr = head->target;
120                 if ((curr->coreid == coreid) && (curr->state == TARGET_HALTED))
121                         return curr;
122         }
123
124         return target;
125 }
126
127 int esp_xtensa_smp_poll(struct target *target)
128 {
129         enum target_state old_state = target->state;
130         struct esp_xtensa_smp_common *esp_xtensa_smp = target_to_esp_xtensa_smp(target);
131         struct target_list *head;
132         struct target *curr;
133         bool other_core_resume_req = false;
134
135         if (target->state == TARGET_HALTED && target->smp && target->gdb_service && !target->gdb_service->target) {
136                 target->gdb_service->target = get_halted_esp_xtensa_smp(target, target->gdb_service->core[1]);
137                 LOG_INFO("Switch GDB target to '%s'", target_name(target->gdb_service->target));
138                 if (esp_xtensa_smp->chip_ops->on_halt)
139                         esp_xtensa_smp->chip_ops->on_halt(target);
140                 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
141                 return ERROR_OK;
142         }
143
144         int ret = esp_xtensa_poll(target);
145         if (ret != ERROR_OK)
146                 return ret;
147
148         if (target->smp) {
149                 if (target->state == TARGET_RESET) {
150                         esp_xtensa_smp->examine_other_cores = ESP_XTENSA_SMP_EXAMINE_OTHER_CORES;
151                 } else if (esp_xtensa_smp->examine_other_cores > 0 &&
152                         (target->state == TARGET_RUNNING || target->state == TARGET_HALTED)) {
153                         LOG_TARGET_DEBUG(target, "Check for unexamined cores after reset");
154                         bool all_examined = true;
155                         foreach_smp_target(head, target->smp_targets) {
156                                 curr = head->target;
157                                 if (curr == target)
158                                         continue;
159                                 if (!target_was_examined(curr)) {
160                                         if (target_examine_one(curr) != ERROR_OK) {
161                                                 LOG_DEBUG("Failed to examine!");
162                                                 all_examined = false;
163                                         }
164                                 }
165                         }
166                         if (all_examined)
167                                 esp_xtensa_smp->examine_other_cores = 0;
168                         else
169                                 esp_xtensa_smp->examine_other_cores--;
170                 }
171         }
172
173         if (old_state != TARGET_HALTED && target->state == TARGET_HALTED) {
174                 if (target->smp) {
175                         ret = esp_xtensa_smp_update_halt_gdb(target, &other_core_resume_req);
176                         if (ret != ERROR_OK)
177                                 return ret;
178                 }
179                 /* Call any event callbacks that are applicable */
180                 if (old_state == TARGET_DEBUG_RUNNING) {
181                         target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
182                 } else {
183                         /* check whether any core polled by esp_xtensa_smp_update_halt_gdb() requested resume */
184                         if (target->smp && other_core_resume_req) {
185                                 /* Resume xtensa_resume will handle BREAK instruction. */
186                                 ret = target_resume(target, 1, 0, 1, 0);
187                                 if (ret != ERROR_OK) {
188                                         LOG_ERROR("Failed to resume target");
189                                         return ret;
190                                 }
191                                 return ERROR_OK;
192                         }
193                         if (esp_xtensa_smp->chip_ops->on_halt)
194                                 esp_xtensa_smp->chip_ops->on_halt(target);
195                         target_call_event_callbacks(target, TARGET_EVENT_HALTED);
196                 }
197         }
198
199         return ERROR_OK;
200 }
201
202 static int esp_xtensa_smp_update_halt_gdb(struct target *target, bool *need_resume)
203 {
204         struct esp_xtensa_smp_common *esp_xtensa_smp;
205         struct target *gdb_target = NULL;
206         struct target_list *head;
207         struct target *curr;
208         int ret = ERROR_OK;
209
210         *need_resume = false;
211
212         if (target->gdb_service && target->gdb_service->target)
213                 LOG_DEBUG("GDB target '%s'", target_name(target->gdb_service->target));
214
215         if (target->gdb_service && target->gdb_service->core[0] == -1) {
216                 target->gdb_service->target = target;
217                 target->gdb_service->core[0] = target->coreid;
218                 LOG_INFO("Set GDB target to '%s'", target_name(target));
219         }
220
221         if (target->gdb_service)
222                 gdb_target = target->gdb_service->target;
223
224         /* due to smpbreak config other cores can also go to HALTED state */
225         foreach_smp_target(head, target->smp_targets) {
226                 curr = head->target;
227                 LOG_DEBUG("Check target '%s'", target_name(curr));
228                 /* skip calling context */
229                 if (curr == target)
230                         continue;
231                 if (!target_was_examined(curr)) {
232                         curr->state = TARGET_HALTED;
233                         continue;
234                 }
235                 /* skip targets that were already halted */
236                 if (curr->state == TARGET_HALTED)
237                         continue;
238                 /* Skip gdb_target; it alerts GDB so has to be polled as last one */
239                 if (curr == gdb_target)
240                         continue;
241                 LOG_DEBUG("Poll target '%s'", target_name(curr));
242
243                 esp_xtensa_smp = target_to_esp_xtensa_smp(curr);
244                 /* avoid auto-resume after syscall, it will be done later */
245                 esp_xtensa_smp->other_core_does_resume = true;
246                 /* avoid recursion in esp_xtensa_smp_poll() */
247                 curr->smp = 0;
248                 if (esp_xtensa_smp->chip_ops->poll)
249                         ret = esp_xtensa_smp->chip_ops->poll(curr);
250                 else
251                         ret = esp_xtensa_smp_poll(curr);
252                 curr->smp = 1;
253                 if (ret != ERROR_OK)
254                         return ret;
255                 esp_xtensa_smp->other_core_does_resume = false;
256         }
257
258         /* after all targets were updated, poll the gdb serving target */
259         if (gdb_target && gdb_target != target) {
260                 esp_xtensa_smp = target_to_esp_xtensa_smp(gdb_target);
261                 if (esp_xtensa_smp->chip_ops->poll)
262                         ret = esp_xtensa_smp->chip_ops->poll(gdb_target);
263                 else
264                         ret = esp_xtensa_smp_poll(gdb_target);
265         }
266
267         LOG_DEBUG("exit");
268
269         return ret;
270 }
271
272 static inline int esp_xtensa_smp_smpbreak_disable(struct target *target, uint32_t *smp_break)
273 {
274         int res = xtensa_smpbreak_get(target, smp_break);
275         if (res != ERROR_OK)
276                 return res;
277         return xtensa_smpbreak_set(target, 0);
278 }
279
280 static inline int esp_xtensa_smp_smpbreak_restore(struct target *target, uint32_t smp_break)
281 {
282         return xtensa_smpbreak_set(target, smp_break);
283 }
284
285 static int esp_xtensa_smp_resume_cores(struct target *target,
286         int handle_breakpoints,
287         int debug_execution)
288 {
289         struct target_list *head;
290         struct target *curr;
291
292         LOG_TARGET_DEBUG(target, "begin");
293
294         foreach_smp_target(head, target->smp_targets) {
295                 curr = head->target;
296                 /* in single-core mode disabled core cannot be examined, but need to be resumed too*/
297                 if ((curr != target) && (curr->state != TARGET_RUNNING) && target_was_examined(curr)) {
298                         /*  resume current address, not in SMP mode */
299                         curr->smp = 0;
300                         int res = esp_xtensa_smp_resume(curr, 1, 0, handle_breakpoints, debug_execution);
301                         curr->smp = 1;
302                         if (res != ERROR_OK)
303                                 return res;
304                 }
305         }
306         return ERROR_OK;
307 }
308
309 int esp_xtensa_smp_resume(struct target *target,
310         int current,
311         target_addr_t address,
312         int handle_breakpoints,
313         int debug_execution)
314 {
315         int res;
316         uint32_t smp_break;
317
318         xtensa_smpbreak_get(target, &smp_break);
319         LOG_TARGET_DEBUG(target, "smp_break=0x%" PRIx32, smp_break);
320
321         /* dummy resume for smp toggle in order to reduce gdb impact  */
322         if ((target->smp) && (target->gdb_service) && (target->gdb_service->core[1] != -1)) {
323                 /* simulate a start and halt of target */
324                 target->gdb_service->target = NULL;
325                 target->gdb_service->core[0] = target->gdb_service->core[1];
326                 /* fake resume at next poll we play the  target core[1], see poll*/
327                 LOG_TARGET_DEBUG(target, "Fake resume");
328                 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
329                 return ERROR_OK;
330         }
331
332         /* xtensa_prepare_resume() can step over breakpoint/watchpoint and generate signals on BreakInOut circuit for
333          * other cores. So disconnect this core from BreakInOut circuit and do xtensa_prepare_resume(). */
334         res = esp_xtensa_smp_smpbreak_disable(target, &smp_break);
335         if (res != ERROR_OK)
336                 return res;
337         res = xtensa_prepare_resume(target, current, address, handle_breakpoints, debug_execution);
338         /* restore configured BreakInOut signals config */
339         int ret = esp_xtensa_smp_smpbreak_restore(target, smp_break);
340         if (ret != ERROR_OK)
341                 return ret;
342         if (res != ERROR_OK) {
343                 LOG_TARGET_ERROR(target, "Failed to prepare for resume!");
344                 return res;
345         }
346
347         if (target->smp) {
348                 if (target->gdb_service)
349                         target->gdb_service->core[0] = -1;
350                 res = esp_xtensa_smp_resume_cores(target, handle_breakpoints, debug_execution);
351                 if (res != ERROR_OK)
352                         return res;
353         }
354
355         res = xtensa_do_resume(target);
356         if (res != ERROR_OK) {
357                 LOG_TARGET_ERROR(target, "Failed to resume!");
358                 return res;
359         }
360
361         target->debug_reason = DBG_REASON_NOTHALTED;
362         if (!debug_execution)
363                 target->state = TARGET_RUNNING;
364         else
365                 target->state = TARGET_DEBUG_RUNNING;
366
367         target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
368         return ERROR_OK;
369 }
370
371 int esp_xtensa_smp_step(struct target *target,
372         int current,
373         target_addr_t address,
374         int handle_breakpoints)
375 {
376         int res;
377         uint32_t smp_break = 0;
378         struct esp_xtensa_smp_common *esp_xtensa_smp = target_to_esp_xtensa_smp(target);
379
380         if (target->smp) {
381                 res = esp_xtensa_smp_smpbreak_disable(target, &smp_break);
382                 if (res != ERROR_OK)
383                         return res;
384         }
385         res = xtensa_step(target, current, address, handle_breakpoints);
386
387         if (res == ERROR_OK) {
388                 if (esp_xtensa_smp->chip_ops->on_halt)
389                         esp_xtensa_smp->chip_ops->on_halt(target);
390                 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
391         }
392
393         if (target->smp) {
394                 int ret = esp_xtensa_smp_smpbreak_restore(target, smp_break);
395                 if (ret != ERROR_OK)
396                         return ret;
397         }
398
399         return res;
400 }
401
402 int esp_xtensa_smp_watchpoint_add(struct target *target, struct watchpoint *watchpoint)
403 {
404         int res = xtensa_watchpoint_add(target, watchpoint);
405         if (res != ERROR_OK)
406                 return res;
407
408         if (!target->smp)
409                 return ERROR_OK;
410
411         struct target_list *head;
412         foreach_smp_target(head, target->smp_targets) {
413                 struct target *curr = head->target;
414                 if (curr == target || !target_was_examined(curr))
415                         continue;
416                 /* Need to use high level API here because every target for core contains list of watchpoints.
417                  * GDB works with active core only, so we need to duplicate every watchpoint on other cores,
418                  * otherwise watchpoint_free() on active core can fail if WP has been initially added on another core. */
419                 curr->smp = 0;
420                 res = watchpoint_add(curr, watchpoint->address, watchpoint->length,
421                         watchpoint->rw, watchpoint->value, watchpoint->mask);
422                 curr->smp = 1;
423                 if (res != ERROR_OK)
424                         return res;
425         }
426         return ERROR_OK;
427 }
428
429 int esp_xtensa_smp_watchpoint_remove(struct target *target, struct watchpoint *watchpoint)
430 {
431         int res = xtensa_watchpoint_remove(target, watchpoint);
432         if (res != ERROR_OK)
433                 return res;
434
435         if (!target->smp)
436                 return ERROR_OK;
437
438         struct target_list *head;
439         foreach_smp_target(head, target->smp_targets) {
440                 struct target *curr = head->target;
441                 if (curr == target)
442                         continue;
443                 /* see big comment in esp_xtensa_smp_watchpoint_add() */
444                 curr->smp = 0;
445                 watchpoint_remove(curr, watchpoint->address);
446                 curr->smp = 1;
447         }
448         return ERROR_OK;
449 }
450
451 int esp_xtensa_smp_init_arch_info(struct target *target,
452         struct esp_xtensa_smp_common *esp_xtensa_smp,
453         const struct xtensa_config *xtensa_cfg,
454         struct xtensa_debug_module_config *dm_cfg,
455         const struct esp_xtensa_smp_chip_ops *chip_ops)
456 {
457         int ret = esp_xtensa_init_arch_info(target, &esp_xtensa_smp->esp_xtensa, xtensa_cfg, dm_cfg);
458         if (ret != ERROR_OK)
459                 return ret;
460         esp_xtensa_smp->chip_ops = chip_ops;
461         esp_xtensa_smp->examine_other_cores = ESP_XTENSA_SMP_EXAMINE_OTHER_CORES;
462         return ERROR_OK;
463 }
464
465 int esp_xtensa_smp_target_init(struct command_context *cmd_ctx, struct target *target)
466 {
467         return esp_xtensa_target_init(cmd_ctx, target);
468 }
469
470 COMMAND_HANDLER(esp_xtensa_smp_cmd_permissive_mode)
471 {
472         struct target *target = get_current_target(CMD_CTX);
473         if (target->smp && CMD_ARGC > 0) {
474                 struct target_list *head;
475                 struct target *curr;
476                 foreach_smp_target(head, target->smp_targets) {
477                         curr = head->target;
478                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do,
479                                 target_to_xtensa(curr));
480                         if (ret != ERROR_OK)
481                                 return ret;
482                 }
483                 return ERROR_OK;
484         }
485         return CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do,
486                 target_to_xtensa(target));
487 }
488
489 COMMAND_HANDLER(esp_xtensa_smp_cmd_smpbreak)
490 {
491         struct target *target = get_current_target(CMD_CTX);
492         if (target->smp && CMD_ARGC > 0) {
493                 struct target_list *head;
494                 struct target *curr;
495                 foreach_smp_target(head, target->smp_targets) {
496                         curr = head->target;
497                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do, curr);
498                         if (ret != ERROR_OK)
499                                 return ret;
500                 }
501                 return ERROR_OK;
502         }
503         return CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do, target);
504 }
505
506 COMMAND_HANDLER(esp_xtensa_smp_cmd_mask_interrupts)
507 {
508         struct target *target = get_current_target(CMD_CTX);
509         if (target->smp && CMD_ARGC > 0) {
510                 struct target_list *head;
511                 struct target *curr;
512                 foreach_smp_target(head, target->smp_targets) {
513                         curr = head->target;
514                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do,
515                                 target_to_xtensa(curr));
516                         if (ret != ERROR_OK)
517                                 return ret;
518                 }
519                 return ERROR_OK;
520         }
521         return CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do,
522                 target_to_xtensa(target));
523 }
524
525 COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_enable)
526 {
527         struct target *target = get_current_target(CMD_CTX);
528         if (target->smp && CMD_ARGC > 0) {
529                 struct target_list *head;
530                 struct target *curr;
531                 foreach_smp_target(head, target->smp_targets) {
532                         curr = head->target;
533                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do,
534                                 target_to_xtensa(curr));
535                         if (ret != ERROR_OK)
536                                 return ret;
537                 }
538                 return ERROR_OK;
539         }
540         return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do,
541                 target_to_xtensa(target));
542 }
543
544 COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_dump)
545 {
546         struct target *target = get_current_target(CMD_CTX);
547         if (target->smp) {
548                 struct target_list *head;
549                 struct target *curr;
550                 foreach_smp_target(head, target->smp_targets) {
551                         curr = head->target;
552                         LOG_INFO("CPU%d:", curr->coreid);
553                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do,
554                                 target_to_xtensa(curr));
555                         if (ret != ERROR_OK)
556                                 return ret;
557                 }
558                 return ERROR_OK;
559         }
560         return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do,
561                 target_to_xtensa(target));
562 }
563
564 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracestart)
565 {
566         struct target *target = get_current_target(CMD_CTX);
567         if (target->smp) {
568                 struct target_list *head;
569                 struct target *curr;
570                 foreach_smp_target(head, target->smp_targets) {
571                         curr = head->target;
572                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do,
573                                 target_to_xtensa(curr));
574                         if (ret != ERROR_OK)
575                                 return ret;
576                 }
577                 return ERROR_OK;
578         }
579         return CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do,
580                 target_to_xtensa(target));
581 }
582
583 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracestop)
584 {
585         struct target *target = get_current_target(CMD_CTX);
586         if (target->smp) {
587                 struct target_list *head;
588                 struct target *curr;
589                 foreach_smp_target(head, target->smp_targets) {
590                         curr = head->target;
591                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do,
592                                 target_to_xtensa(curr));
593                         if (ret != ERROR_OK)
594                                 return ret;
595                 }
596                 return ERROR_OK;
597         }
598         return CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do,
599                 target_to_xtensa(target));
600 }
601
602 COMMAND_HANDLER(esp_xtensa_smp_cmd_tracedump)
603 {
604         struct target *target = get_current_target(CMD_CTX);
605         if (target->smp) {
606                 struct target_list *head;
607                 struct target *curr;
608                 int32_t cores_max_id = 0;
609                 /* assume that core IDs are assigned to SMP targets sequentially: 0,1,2... */
610                 foreach_smp_target(head, target->smp_targets) {
611                         curr = head->target;
612                         if (cores_max_id < curr->coreid)
613                                 cores_max_id = curr->coreid;
614                 }
615                 if (CMD_ARGC < ((uint32_t)cores_max_id + 1)) {
616                         command_print(CMD,
617                                 "Need %d filenames to dump to as output!",
618                                 cores_max_id + 1);
619                         return ERROR_FAIL;
620                 }
621                 foreach_smp_target(head, target->smp_targets) {
622                         curr = head->target;
623                         int ret = CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do,
624                                 target_to_xtensa(curr), CMD_ARGV[curr->coreid]);
625                         if (ret != ERROR_OK)
626                                 return ret;
627                 }
628                 return ERROR_OK;
629         }
630         return CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do,
631                 target_to_xtensa(target), CMD_ARGV[0]);
632 }
633
634 const struct command_registration esp_xtensa_smp_xtensa_command_handlers[] = {
635         {
636                 .name = "set_permissive",
637                 .handler = esp_xtensa_smp_cmd_permissive_mode,
638                 .mode = COMMAND_ANY,
639                 .help = "When set to 1, enable Xtensa permissive mode (less client-side checks)",
640                 .usage = "[0|1]",
641         },
642         {
643                 .name = "maskisr",
644                 .handler = esp_xtensa_smp_cmd_mask_interrupts,
645                 .mode = COMMAND_ANY,
646                 .help = "mask Xtensa interrupts at step",
647                 .usage = "['on'|'off']",
648         },
649         {
650                 .name = "smpbreak",
651                 .handler = esp_xtensa_smp_cmd_smpbreak,
652                 .mode = COMMAND_ANY,
653                 .help = "Set the way the CPU chains OCD breaks",
654                 .usage =
655                         "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]",
656         },
657         {
658                 .name = "perfmon_enable",
659                 .handler = esp_xtensa_smp_cmd_perfmon_enable,
660                 .mode = COMMAND_EXEC,
661                 .help = "Enable and start performance counter",
662                 .usage = "<counter_id> <select> [mask] [kernelcnt] [tracelevel]",
663         },
664         {
665                 .name = "perfmon_dump",
666                 .handler = esp_xtensa_smp_cmd_perfmon_dump,
667                 .mode = COMMAND_EXEC,
668                 .help =
669                         "Dump performance counter value. If no argument specified, dumps all counters.",
670                 .usage = "[counter_id]",
671         },
672         {
673                 .name = "tracestart",
674                 .handler = esp_xtensa_smp_cmd_tracestart,
675                 .mode = COMMAND_EXEC,
676                 .help =
677                         "Tracing: Set up and start a trace. Optionally set stop trigger address and amount of data captured after.",
678                 .usage = "[pc <pcval>/[maskbitcount]] [after <n> [ins|words]]",
679         },
680         {
681                 .name = "tracestop",
682                 .handler = esp_xtensa_smp_cmd_tracestop,
683                 .mode = COMMAND_EXEC,
684                 .help = "Tracing: Stop current trace as started by the tracestart command",
685                 .usage = "",
686         },
687         {
688                 .name = "tracedump",
689                 .handler = esp_xtensa_smp_cmd_tracedump,
690                 .mode = COMMAND_EXEC,
691                 .help = "Tracing: Dump trace memory to a files. One file per core.",
692                 .usage = "<outfile1> <outfile2>",
693         },
694         COMMAND_REGISTRATION_DONE
695 };
696
697 const struct command_registration esp_xtensa_smp_command_handlers[] = {
698         {
699                 .name = "xtensa",
700                 .usage = "",
701                 .chain = esp_xtensa_smp_xtensa_command_handlers,
702         },
703         COMMAND_REGISTRATION_DONE
704 };