a27c1cc3d26be8c4884cc3f21f4faef9dcc21cd5
[fw/openocd] / src / target / nds32_v3.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2013 Andes Technology                                   *
5  *   Hsiangkai Wang <hkwang@andestech.com>                                 *
6  ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "breakpoints.h"
13 #include "nds32_cmd.h"
14 #include "nds32_aice.h"
15 #include "nds32_v3.h"
16 #include "nds32_v3_common.h"
17
18 static int nds32_v3_activate_hardware_breakpoint(struct target *target)
19 {
20         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
21         struct aice_port_s *aice = target_to_aice(target);
22         struct breakpoint *bp;
23         int32_t hbr_index = nds32_v3->next_hbr_index;
24
25         for (bp = target->breakpoints; bp; bp = bp->next) {
26                 if (bp->type == BKPT_SOFT) {
27                         /* already set at nds32_v3_add_breakpoint() */
28                         continue;
29                 } else if (bp->type == BKPT_HARD) {
30                         hbr_index--;
31                         /* set address */
32                         aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + hbr_index, bp->address);
33                         /* set mask */
34                         aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + hbr_index, 0);
35                         /* set value */
36                         aice_write_debug_reg(aice, NDS_EDM_SR_BPV0 + hbr_index, 0);
37
38                         if (nds32_v3->nds32.memory.address_translation)
39                                 /* enable breakpoint (virtual address) */
40                                 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0x2);
41                         else
42                                 /* enable breakpoint (physical address) */
43                                 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0xA);
44
45                         LOG_DEBUG("Add hardware BP %" PRId32 " at %08" TARGET_PRIxADDR, hbr_index,
46                                         bp->address);
47                 } else {
48                         return ERROR_FAIL;
49                 }
50         }
51
52         return ERROR_OK;
53 }
54
55 static int nds32_v3_deactivate_hardware_breakpoint(struct target *target)
56 {
57         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
58         struct aice_port_s *aice = target_to_aice(target);
59         struct breakpoint *bp;
60         int32_t hbr_index = nds32_v3->next_hbr_index;
61
62         for (bp = target->breakpoints; bp; bp = bp->next) {
63                 if (bp->type == BKPT_SOFT) {
64                         continue;
65                 } else if (bp->type == BKPT_HARD) {
66                         hbr_index--;
67                         /* disable breakpoint */
68                         aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0x0);
69                 } else {
70                         return ERROR_FAIL;
71                 }
72
73                 LOG_DEBUG("Remove hardware BP %" PRId32 " at %08" TARGET_PRIxADDR, hbr_index,
74                                 bp->address);
75         }
76
77         return ERROR_OK;
78 }
79
80 static int nds32_v3_activate_hardware_watchpoint(struct target *target)
81 {
82         struct aice_port_s *aice = target_to_aice(target);
83         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
84         struct watchpoint *wp;
85         int32_t wp_num = 0;
86         uint32_t wp_config = 0;
87         bool ld_stop, st_stop;
88
89         if (nds32_v3->nds32.global_stop)
90                 ld_stop = st_stop = false;
91
92         for (wp = target->watchpoints; wp; wp = wp->next) {
93
94                 if (wp_num < nds32_v3->used_n_wp) {
95                         wp->mask = wp->length - 1;
96                         if ((wp->address % wp->length) != 0)
97                                 wp->mask = (wp->mask << 1) + 1;
98
99                         if (wp->rw == WPT_READ)
100                                 wp_config = 0x3;
101                         else if (wp->rw == WPT_WRITE)
102                                 wp_config = 0x5;
103                         else if (wp->rw == WPT_ACCESS)
104                                 wp_config = 0x7;
105
106                         /* set/unset physical address bit of BPCn according to PSW.DT */
107                         if (nds32_v3->nds32.memory.address_translation == false)
108                                 wp_config |= 0x8;
109
110                         /* set address */
111                         aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + wp_num,
112                                         wp->address - (wp->address % wp->length));
113                         /* set mask */
114                         aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + wp_num, wp->mask);
115                         /* enable watchpoint */
116                         aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, wp_config);
117                         /* set value */
118                         aice_write_debug_reg(aice, NDS_EDM_SR_BPV0 + wp_num, 0);
119
120                         LOG_DEBUG("Add hardware watchpoint %" PRId32 " at %08" TARGET_PRIxADDR " mask %08" PRIx32,
121                                         wp_num, wp->address, wp->mask);
122
123                         wp_num++;
124                 } else if (nds32_v3->nds32.global_stop) {
125                         if (wp->rw == WPT_READ)
126                                 ld_stop = true;
127                         else if (wp->rw == WPT_WRITE)
128                                 st_stop = true;
129                         else if (wp->rw == WPT_ACCESS)
130                                 ld_stop = st_stop = true;
131                 }
132         }
133
134         if (nds32_v3->nds32.global_stop) {
135                 uint32_t edm_ctl;
136                 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &edm_ctl);
137                 if (ld_stop)
138                         edm_ctl |= 0x10;
139                 if (st_stop)
140                         edm_ctl |= 0x20;
141                 aice_write_debug_reg(aice, NDS_EDM_SR_EDM_CTL, edm_ctl);
142         }
143
144         return ERROR_OK;
145 }
146
147 static int nds32_v3_deactivate_hardware_watchpoint(struct target *target)
148 {
149         struct aice_port_s *aice = target_to_aice(target);
150         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
151         int32_t wp_num = 0;
152         struct watchpoint *wp;
153         bool clean_global_stop = false;
154
155         for (wp = target->watchpoints; wp; wp = wp->next) {
156
157                 if (wp_num < nds32_v3->used_n_wp) {
158                         /* disable watchpoint */
159                         aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, 0x0);
160
161                         LOG_DEBUG("Remove hardware watchpoint %" PRId32 " at %08" TARGET_PRIxADDR
162                                         " mask %08" PRIx32, wp_num,
163                                         wp->address, wp->mask);
164                         wp_num++;
165                 } else if (nds32_v3->nds32.global_stop) {
166                         clean_global_stop = true;
167                 }
168         }
169
170         if (clean_global_stop) {
171                 uint32_t edm_ctl;
172                 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &edm_ctl);
173                 edm_ctl = edm_ctl & (~0x30);
174                 aice_write_debug_reg(aice, NDS_EDM_SR_EDM_CTL, edm_ctl);
175         }
176
177         return ERROR_OK;
178 }
179
180 static int nds32_v3_check_interrupt_stack(struct nds32 *nds32)
181 {
182         uint32_t val_ir0;
183         uint32_t value;
184
185         /* Save interrupt level */
186         nds32_get_mapped_reg(nds32, IR0, &val_ir0);
187         nds32->current_interrupt_level = (val_ir0 >> 1) & 0x3;
188
189         if (nds32_reach_max_interrupt_level(nds32))
190                 LOG_ERROR("<-- TARGET ERROR! Reaching the max interrupt stack level %" PRIu32 ". -->",
191                                 nds32->current_interrupt_level);
192
193         /* backup $ir4 & $ir6 to avoid suppressed exception overwrite */
194         nds32_get_mapped_reg(nds32, IR4, &value);
195         nds32_get_mapped_reg(nds32, IR6, &value);
196
197         return ERROR_OK;
198 }
199
200 static int nds32_v3_restore_interrupt_stack(struct nds32 *nds32)
201 {
202         uint32_t value;
203
204         /* get backup value from cache */
205         /* then set back to make the register dirty */
206         nds32_get_mapped_reg(nds32, IR0, &value);
207         nds32_set_mapped_reg(nds32, IR0, value);
208
209         nds32_get_mapped_reg(nds32, IR4, &value);
210         nds32_set_mapped_reg(nds32, IR4, value);
211
212         nds32_get_mapped_reg(nds32, IR6, &value);
213         nds32_set_mapped_reg(nds32, IR6, value);
214
215         return ERROR_OK;
216 }
217
218 static int nds32_v3_deassert_reset(struct target *target)
219 {
220         int retval;
221         struct aice_port_s *aice = target_to_aice(target);
222         bool switch_to_v3_stack = false;
223         uint32_t value_edm_ctl;
224
225         aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &value_edm_ctl);
226         if (((value_edm_ctl >> 6) & 0x1) == 0) { /* reset to V2 EDM mode */
227                 aice_write_debug_reg(aice, NDS_EDM_SR_EDM_CTL, value_edm_ctl | (0x1 << 6));
228                 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &value_edm_ctl);
229                 if (((value_edm_ctl >> 6) & 0x1) == 1)
230                         switch_to_v3_stack = true;
231         } else
232                 switch_to_v3_stack = false;
233
234         CHECK_RETVAL(nds32_poll(target));
235
236         if (target->state != TARGET_HALTED) {
237                 /* reset only */
238                 LOG_WARNING("%s: ran after reset and before halt ...",
239                                 target_name(target));
240                 retval = target_halt(target);
241                 if (retval != ERROR_OK)
242                         return retval;
243
244         } else {
245                 /* reset-halt */
246                 struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
247                 struct nds32 *nds32 = &(nds32_v3->nds32);
248                 uint32_t value;
249                 uint32_t interrupt_level;
250
251                 if (switch_to_v3_stack == true) {
252                         /* PSW.INTL-- */
253                         nds32_get_mapped_reg(nds32, IR0, &value);
254                         interrupt_level = (value >> 1) & 0x3;
255                         interrupt_level--;
256                         value &= ~(0x6);
257                         value |= (interrupt_level << 1);
258                         value |= 0x400;  /* set PSW.DEX */
259                         nds32_set_mapped_reg(nds32, IR0, value);
260
261                         /* copy IPC to OIPC */
262                         if ((interrupt_level + 1) < nds32->max_interrupt_level) {
263                                 nds32_get_mapped_reg(nds32, IR9, &value);
264                                 nds32_set_mapped_reg(nds32, IR11, value);
265                         }
266                 }
267         }
268
269         return ERROR_OK;
270 }
271
272 static int nds32_v3_add_breakpoint(struct target *target,
273                 struct breakpoint *breakpoint)
274 {
275         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
276         struct nds32 *nds32 = &(nds32_v3->nds32);
277         int result;
278
279         if (breakpoint->type == BKPT_HARD) {
280                 /* check hardware resource */
281                 if (nds32_v3->n_hbr <= nds32_v3->next_hbr_index) {
282                         LOG_WARNING("<-- TARGET WARNING! Insert too many "
283                                         "hardware breakpoints/watchpoints! "
284                                         "The limit of combined hardware "
285                                         "breakpoints/watchpoints is %" PRId32 ". -->",
286                                         nds32_v3->n_hbr);
287                         LOG_WARNING("<-- TARGET STATUS: Inserted number of "
288                                         "hardware breakpoint: %" PRId32 ", hardware "
289                                         "watchpoints: %" PRId32 ". -->",
290                                         nds32_v3->next_hbr_index - nds32_v3->used_n_wp,
291                                         nds32_v3->used_n_wp);
292                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
293                 }
294
295                 /* update next place to put hardware breakpoint */
296                 nds32_v3->next_hbr_index++;
297
298                 /* hardware breakpoint insertion occurs before 'continue' actually */
299                 return ERROR_OK;
300         } else if (breakpoint->type == BKPT_SOFT) {
301                 result = nds32_add_software_breakpoint(target, breakpoint);
302                 if (result != ERROR_OK) {
303                         /* auto convert to hardware breakpoint if failed */
304                         if (nds32->auto_convert_hw_bp) {
305                                 /* convert to hardware breakpoint */
306                                 breakpoint->type = BKPT_HARD;
307
308                                 return nds32_v3_add_breakpoint(target, breakpoint);
309                         }
310                 }
311
312                 return result;
313         } else /* unrecognized breakpoint type */
314                 return ERROR_FAIL;
315
316         return ERROR_OK;
317 }
318
319 static int nds32_v3_remove_breakpoint(struct target *target,
320                 struct breakpoint *breakpoint)
321 {
322         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
323
324         if (breakpoint->type == BKPT_HARD) {
325                 if (nds32_v3->next_hbr_index <= 0)
326                         return ERROR_FAIL;
327
328                 /* update next place to put hardware breakpoint */
329                 nds32_v3->next_hbr_index--;
330
331                 /* hardware breakpoint removal occurs after 'halted' actually */
332                 return ERROR_OK;
333         } else if (breakpoint->type == BKPT_SOFT) {
334                 return nds32_remove_software_breakpoint(target, breakpoint);
335         } else /* unrecognized breakpoint type */
336                 return ERROR_FAIL;
337
338         return ERROR_OK;
339 }
340
341 static int nds32_v3_add_watchpoint(struct target *target,
342                 struct watchpoint *watchpoint)
343 {
344         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
345
346         /* check hardware resource */
347         if (nds32_v3->n_hbr <= nds32_v3->next_hbr_index) {
348                 /* No hardware resource */
349                 if (nds32_v3->nds32.global_stop) {
350                         LOG_WARNING("<-- TARGET WARNING! The number of "
351                                         "watchpoints exceeds the hardware "
352                                         "resources. Stop at every load/store "
353                                         "instruction to check for watchpoint matches. -->");
354                         return ERROR_OK;
355                 }
356
357                 LOG_WARNING("<-- TARGET WARNING! Insert too many hardware "
358                                 "breakpoints/watchpoints! The limit of combined "
359                                 "hardware breakpoints/watchpoints is %" PRId32 ". -->",
360                                 nds32_v3->n_hbr);
361                 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
362                                 "hardware breakpoint: %" PRId32 ", hardware "
363                                 "watchpoints: %" PRId32 ". -->",
364                                 nds32_v3->next_hbr_index - nds32_v3->used_n_wp,
365                                 nds32_v3->used_n_wp);
366
367                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
368         }
369
370         /* update next place to put hardware watchpoint */
371         nds32_v3->next_hbr_index++;
372         nds32_v3->used_n_wp++;
373
374         return ERROR_OK;
375 }
376
377 static int nds32_v3_remove_watchpoint(struct target *target,
378                 struct watchpoint *watchpoint)
379 {
380         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
381
382         if (nds32_v3->next_hbr_index <= 0) {
383                 if (nds32_v3->nds32.global_stop)
384                         return ERROR_OK;
385
386                 return ERROR_FAIL;
387         }
388
389         /* update next place to put hardware breakpoint */
390         nds32_v3->next_hbr_index--;
391         nds32_v3->used_n_wp--;
392
393         return ERROR_OK;
394 }
395
396 static struct nds32_v3_common_callback nds32_v3_common_callback = {
397         .check_interrupt_stack = nds32_v3_check_interrupt_stack,
398         .restore_interrupt_stack = nds32_v3_restore_interrupt_stack,
399         .activate_hardware_breakpoint = nds32_v3_activate_hardware_breakpoint,
400         .activate_hardware_watchpoint = nds32_v3_activate_hardware_watchpoint,
401         .deactivate_hardware_breakpoint = nds32_v3_deactivate_hardware_breakpoint,
402         .deactivate_hardware_watchpoint = nds32_v3_deactivate_hardware_watchpoint,
403 };
404
405 static int nds32_v3_target_create(struct target *target, Jim_Interp *interp)
406 {
407         struct nds32_v3_common *nds32_v3;
408
409         nds32_v3 = calloc(1, sizeof(*nds32_v3));
410         if (!nds32_v3)
411                 return ERROR_FAIL;
412
413         nds32_v3_common_register_callback(&nds32_v3_common_callback);
414         nds32_v3_target_create_common(target, &(nds32_v3->nds32));
415
416         return ERROR_OK;
417 }
418
419 /* talk to the target and set things up */
420 static int nds32_v3_examine(struct target *target)
421 {
422         struct nds32_v3_common *nds32_v3 = target_to_nds32_v3(target);
423         struct nds32 *nds32 = &(nds32_v3->nds32);
424         struct aice_port_s *aice = target_to_aice(target);
425
426         if (!target_was_examined(target)) {
427                 CHECK_RETVAL(nds32_edm_config(nds32));
428
429                 if (nds32->reset_halt_as_examine)
430                         CHECK_RETVAL(nds32_reset_halt(nds32));
431         }
432
433         uint32_t edm_cfg;
434         aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CFG, &edm_cfg);
435
436         /* get the number of hardware breakpoints */
437         nds32_v3->n_hbr = (edm_cfg & 0x7) + 1;
438
439         /* low interference profiling */
440         if (edm_cfg & 0x100)
441                 nds32_v3->low_interference_profile = true;
442         else
443                 nds32_v3->low_interference_profile = false;
444
445         nds32_v3->next_hbr_index = 0;
446         nds32_v3->used_n_wp = 0;
447
448         LOG_INFO("%s: total hardware breakpoint %" PRId32, target_name(target),
449                         nds32_v3->n_hbr);
450
451         nds32->target->state = TARGET_RUNNING;
452         nds32->target->debug_reason = DBG_REASON_NOTHALTED;
453
454         target_set_examined(target);
455
456         return ERROR_OK;
457 }
458
459 /** Holds methods for Andes1337 targets. */
460 struct target_type nds32_v3_target = {
461         .name = "nds32_v3",
462
463         .poll = nds32_poll,
464         .arch_state = nds32_arch_state,
465
466         .target_request_data = nds32_v3_target_request_data,
467
468         .halt = nds32_halt,
469         .resume = nds32_resume,
470         .step = nds32_step,
471
472         .assert_reset = nds32_assert_reset,
473         .deassert_reset = nds32_v3_deassert_reset,
474
475         /* register access */
476         .get_gdb_reg_list = nds32_get_gdb_reg_list,
477
478         /* memory access */
479         .read_buffer = nds32_v3_read_buffer,
480         .write_buffer = nds32_v3_write_buffer,
481         .read_memory = nds32_v3_read_memory,
482         .write_memory = nds32_v3_write_memory,
483
484         .checksum_memory = nds32_v3_checksum_memory,
485
486         /* breakpoint/watchpoint */
487         .add_breakpoint = nds32_v3_add_breakpoint,
488         .remove_breakpoint = nds32_v3_remove_breakpoint,
489         .add_watchpoint = nds32_v3_add_watchpoint,
490         .remove_watchpoint = nds32_v3_remove_watchpoint,
491         .hit_watchpoint = nds32_v3_hit_watchpoint,
492
493         /* MMU */
494         .mmu = nds32_mmu,
495         .virt2phys = nds32_virtual_to_physical,
496         .read_phys_memory = nds32_read_phys_memory,
497         .write_phys_memory = nds32_write_phys_memory,
498
499         .run_algorithm = nds32_v3_run_algorithm,
500
501         .commands = nds32_command_handlers,
502         .target_create = nds32_v3_target_create,
503         .init_target = nds32_v3_init_target,
504         .examine = nds32_v3_examine,
505
506         .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
507         .gdb_fileio_end = nds32_gdb_fileio_end,
508
509         .profiling = nds32_profiling,
510 };