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