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