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