gdb_server: support File-I/O Remote Protocol Extension
[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                 /* call target_poll() to avoid "Halt timed out" */
239                 CHECK_RETVAL(target_poll(target));
240         } else {
241                 jtag_poll_set_enabled(false);
242         }
243
244         return ERROR_OK;
245 }
246
247 static int nds32_v3m_add_breakpoint(struct target *target,
248                 struct breakpoint *breakpoint)
249 {
250         struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
251         struct nds32 *nds32 = &(nds32_v3m->nds32);
252         int result;
253
254         if (breakpoint->type == BKPT_HARD) {
255                 /* check hardware resource */
256                 if (nds32_v3m->next_hbr_index < nds32_v3m->next_hwp_index) {
257                         LOG_WARNING("<-- TARGET WARNING! Insert too many "
258                                         "hardware breakpoints/watchpoints! "
259                                         "The limit of combined hardware "
260                                         "breakpoints/watchpoints is %d. -->",
261                                         nds32_v3m->n_hbr);
262                         LOG_WARNING("<-- TARGET STATUS: Inserted number of "
263                                         "hardware breakpoint: %d, hardware "
264                                         "watchpoints: %d. -->",
265                                         nds32_v3m->n_hbr - nds32_v3m->next_hbr_index - 1,
266                                         nds32_v3m->used_n_wp);
267                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
268                 }
269
270                 /* update next place to put hardware breakpoint */
271                 nds32_v3m->next_hbr_index--;
272
273                 /* hardware breakpoint insertion occurs before 'continue' actually */
274                 return ERROR_OK;
275         } else if (breakpoint->type == BKPT_SOFT) {
276                 result = nds32_add_software_breakpoint(target, breakpoint);
277                 if (ERROR_OK != result) {
278                         /* auto convert to hardware breakpoint if failed */
279                         if (nds32->auto_convert_hw_bp) {
280                                 /* convert to hardware breakpoint */
281                                 breakpoint->type = BKPT_HARD;
282
283                                 return nds32_v3m_add_breakpoint(target, breakpoint);
284                         }
285                 }
286
287                 return result;
288         } else /* unrecognized breakpoint type */
289                 return ERROR_FAIL;
290
291         return ERROR_OK;
292 }
293
294 static int nds32_v3m_remove_breakpoint(struct target *target,
295                 struct breakpoint *breakpoint)
296 {
297         struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
298
299         if (breakpoint->type == BKPT_HARD) {
300                 if (nds32_v3m->next_hbr_index >= nds32_v3m->n_hbr - 1)
301                         return ERROR_FAIL;
302
303                 /* update next place to put hardware breakpoint */
304                 nds32_v3m->next_hbr_index++;
305
306                 /* hardware breakpoint removal occurs after 'halted' actually */
307                 return ERROR_OK;
308         } else if (breakpoint->type == BKPT_SOFT) {
309                 return nds32_remove_software_breakpoint(target, breakpoint);
310         } else /* unrecognized breakpoint type */
311                 return ERROR_FAIL;
312
313         return ERROR_OK;
314 }
315
316 static int nds32_v3m_add_watchpoint(struct target *target,
317                 struct watchpoint *watchpoint)
318 {
319         struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
320
321         /* check hardware resource */
322         if (nds32_v3m->next_hwp_index >= nds32_v3m->n_hwp) {
323                 /* No hardware resource */
324                 if (nds32_v3m->nds32.global_stop) {
325                         LOG_WARNING("<-- TARGET WARNING! The number of "
326                                         "watchpoints exceeds the hardware "
327                                         "resources. Stop at every load/store "
328                                         "instruction to check for watchpoint matches. -->");
329                         return ERROR_OK;
330                 }
331
332                 LOG_WARNING("<-- TARGET WARNING! Insert too many hardware "
333                                 "watchpoints! The limit of hardware watchpoints "
334                                 "is %d. -->", nds32_v3m->n_hwp);
335                 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
336                                 "hardware watchpoint: %d. -->",
337                                 nds32_v3m->used_n_wp);
338                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
339         }
340
341         if (nds32_v3m->next_hwp_index > nds32_v3m->next_hbr_index) {
342                 /* No hardware resource */
343                 if (nds32_v3m->nds32.global_stop) {
344                         LOG_WARNING("<-- TARGET WARNING! The number of "
345                                         "watchpoints exceeds the hardware "
346                                         "resources. Stop at every load/store "
347                                         "instruction to check for watchpoint matches. -->");
348                         return ERROR_OK;
349                 }
350
351                 LOG_WARNING("<-- TARGET WARNING! Insert too many hardware "
352                                 "breakpoints/watchpoints! The limit of combined "
353                                 "hardware breakpoints/watchpoints is %d. -->",
354                                 nds32_v3m->n_hbr);
355                 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
356                                 "hardware breakpoint: %d, hardware "
357                                 "watchpoints: %d. -->",
358                                 nds32_v3m->n_hbr - nds32_v3m->next_hbr_index - 1,
359                                 nds32_v3m->used_n_wp);
360                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
361         }
362
363         /* update next place to put hardware watchpoint */
364         nds32_v3m->next_hwp_index++;
365         nds32_v3m->used_n_wp++;
366
367         return ERROR_OK;
368 }
369
370 static int nds32_v3m_remove_watchpoint(struct target *target,
371                 struct watchpoint *watchpoint)
372 {
373         struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
374
375         if (nds32_v3m->next_hwp_index <= 0) {
376                 if (nds32_v3m->nds32.global_stop)
377                         return ERROR_OK;
378
379                 return ERROR_FAIL;
380         }
381
382         /* update next place to put hardware watchpoint */
383         nds32_v3m->next_hwp_index--;
384         nds32_v3m->used_n_wp--;
385
386         return ERROR_OK;
387 }
388
389 struct nds32_v3_common_callback nds32_v3m_common_callback = {
390         .check_interrupt_stack = nds32_v3m_check_interrupt_stack,
391         .restore_interrupt_stack = nds32_v3m_restore_interrupt_stack,
392         .activate_hardware_breakpoint = nds32_v3m_activate_hardware_breakpoint,
393         .activate_hardware_watchpoint = nds32_v3m_activate_hardware_watchpoint,
394         .deactivate_hardware_breakpoint = nds32_v3m_deactivate_hardware_breakpoint,
395         .deactivate_hardware_watchpoint = nds32_v3m_deactivate_hardware_watchpoint,
396 };
397
398 static int nds32_v3m_target_create(struct target *target, Jim_Interp *interp)
399 {
400         struct nds32_v3m_common *nds32_v3m;
401
402         nds32_v3m = calloc(1, sizeof(*nds32_v3m));
403         if (!nds32_v3m)
404                 return ERROR_FAIL;
405
406         nds32_v3_common_register_callback(&nds32_v3m_common_callback);
407         nds32_v3_target_create_common(target, &(nds32_v3m->nds32));
408
409         return ERROR_OK;
410 }
411
412 /* talk to the target and set things up */
413 static int nds32_v3m_examine(struct target *target)
414 {
415         struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
416         struct nds32 *nds32 = &(nds32_v3m->nds32);
417         struct aice_port_s *aice = target_to_aice(target);
418
419         if (!target_was_examined(target)) {
420                 CHECK_RETVAL(nds32_edm_config(nds32));
421
422                 if (nds32->reset_halt_as_examine)
423                         CHECK_RETVAL(nds32_reset_halt(nds32));
424         }
425
426         uint32_t edm_cfg;
427         aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CFG, &edm_cfg);
428
429         /* get the number of hardware breakpoints */
430         nds32_v3m->n_hbr = (edm_cfg & 0x7) + 1;
431         nds32_v3m->used_n_wp = 0;
432
433         /* get the number of hardware watchpoints */
434         /* If the WP field is hardwired to zero, it means this is a
435          * simple breakpoint.  Otherwise, if the WP field is writable
436          * then it means this is a regular watchpoints. */
437         nds32_v3m->n_hwp = 0;
438         for (int32_t i = 0 ; i < nds32_v3m->n_hbr ; i++) {
439                 /** check the hardware breakpoint is simple or not */
440                 uint32_t tmp_value;
441                 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + i, 0x1);
442                 aice_read_debug_reg(aice, NDS_EDM_SR_BPC0 + i, &tmp_value);
443
444                 if (tmp_value)
445                         nds32_v3m->n_hwp++;
446         }
447         /* hardware breakpoint is inserted from high index to low index */
448         nds32_v3m->next_hbr_index = nds32_v3m->n_hbr - 1;
449         /* hardware watchpoint is inserted from low index to high index */
450         nds32_v3m->next_hwp_index = 0;
451
452         LOG_INFO("%s: total hardware breakpoint %d (simple breakpoint %d)",
453                         target_name(target), nds32_v3m->n_hbr, nds32_v3m->n_hbr - nds32_v3m->n_hwp);
454         LOG_INFO("%s: total hardware watchpoint %d", target_name(target), nds32_v3m->n_hwp);
455
456         nds32->target->state = TARGET_RUNNING;
457         nds32->target->debug_reason = DBG_REASON_NOTHALTED;
458
459         target_set_examined(target);
460
461         return ERROR_OK;
462 }
463
464 /** Holds methods for NDS32 V3m targets. */
465 struct target_type nds32_v3m_target = {
466         .name = "nds32_v3m",
467
468         .poll = nds32_poll,
469         .arch_state = nds32_arch_state,
470
471         .target_request_data = nds32_v3_target_request_data,
472
473         .halt = nds32_halt,
474         .resume = nds32_resume,
475         .step = nds32_step,
476
477         .assert_reset = nds32_assert_reset,
478         .deassert_reset = nds32_v3m_deassert_reset,
479         .soft_reset_halt = nds32_v3_soft_reset_halt,
480
481         /* register access */
482         .get_gdb_reg_list = nds32_get_gdb_reg_list,
483
484         /* memory access */
485         .read_buffer = nds32_v3_read_buffer,
486         .write_buffer = nds32_v3_write_buffer,
487         .read_memory = nds32_v3_read_memory,
488         .write_memory = nds32_v3_write_memory,
489
490         .checksum_memory = nds32_v3_checksum_memory,
491
492         /* breakpoint/watchpoint */
493         .add_breakpoint = nds32_v3m_add_breakpoint,
494         .remove_breakpoint = nds32_v3m_remove_breakpoint,
495         .add_watchpoint = nds32_v3m_add_watchpoint,
496         .remove_watchpoint = nds32_v3m_remove_watchpoint,
497         .hit_watchpoint = nds32_v3_hit_watchpoint,
498
499         /* MMU */
500         .mmu = nds32_mmu,
501         .virt2phys = nds32_virtual_to_physical,
502         .read_phys_memory = nds32_read_phys_memory,
503         .write_phys_memory = nds32_write_phys_memory,
504
505         .run_algorithm = nds32_v3_run_algorithm,
506
507         .commands = nds32_command_handlers,
508         .target_create = nds32_v3m_target_create,
509         .init_target = nds32_v3_init_target,
510         .examine = nds32_v3m_examine,
511
512         .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
513         .gdb_fileio_end = nds32_gdb_fileio_end,
514 };