xref: /netbsd-src/sys/arch/alpha/alpha/db_interface.c (revision 6e3d02bbcd6ad76dfee4cfb9687a4fd87c0e86d6)
1 /* $NetBSD: db_interface.c,v 1.43 2024/02/05 22:08:04 andvar Exp $ */
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1992,1991,1990 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS ``AS IS''
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  *
28  *	db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
29  */
30 
31 /*
32  * Parts of this file are derived from Mach 3:
33  *
34  *	File: alpha_instruction.c
35  *	Author: Alessandro Forin, Carnegie Mellon University
36  *	Date:	6/92
37  */
38 
39 /*
40  * Interface to DDB.
41  *
42  * Modified for NetBSD/alpha by:
43  *
44  *	Christopher G. Demetriou, Carnegie Mellon University
45  *
46  *	Jason R. Thorpe, Numerical Aerospace Simulation Facility,
47  *	NASA Ames Research Center
48  */
49 
50 #ifdef _KERNEL_OPT
51 #include "opt_ddb.h"
52 #include "opt_multiprocessor.h"
53 #endif
54 
55 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
56 
57 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.43 2024/02/05 22:08:04 andvar Exp $");
58 
59 #include <sys/param.h>
60 #include <sys/proc.h>
61 #include <sys/reboot.h>
62 #include <sys/systm.h>
63 
64 #include <dev/cons.h>
65 
66 #include <machine/alpha.h>
67 #include <machine/db_machdep.h>
68 #include <machine/pal.h>
69 #include <machine/prom.h>
70 
71 #include <machine/alpha_instruction.h>
72 
73 #include <ddb/db_user.h>
74 #include <ddb/db_active.h>
75 #include <ddb/db_sym.h>
76 #include <ddb/db_command.h>
77 #include <ddb/db_extern.h>
78 #include <ddb/db_access.h>
79 #include <ddb/db_output.h>
80 #include <ddb/db_variables.h>
81 #include <ddb/db_interface.h>
82 
83 #if 0
84 extern char *trap_type[];
85 extern int trap_types;
86 #endif
87 
88 int	db_active = 0;
89 
90 db_regs_t *ddb_regp;
91 
92 #if defined(MULTIPROCESSOR)
93 void	db_mach_cpu(db_expr_t, bool, db_expr_t, const char *);
94 #endif
95 
96 const struct db_command db_machine_command_table[] = {
97 #if defined(MULTIPROCESSOR)
98 	{ DDB_ADD_CMD("cpu",	db_mach_cpu,	0,
99 	  "switch to another cpu", "cpu-no", NULL) },
100 #endif
101 	{ DDB_END_CMD },
102 };
103 
104 static int db_alpha_regop(const struct db_variable *, db_expr_t *, int);
105 
106 #define	dbreg(xx)	((long *)(xx))
107 
108 #define	DBREG(n, r)						\
109 	{	.name = __STRING(n),				\
110 		.valuep = ((long *)(r)),			\
111 		.fcn = db_alpha_regop,				\
112 		.modif = NULL, }
113 
114 const struct db_variable db_regs[] = {
115 	DBREG(v0,	FRAME_V0),
116 	DBREG(t0,	FRAME_T0),
117 	DBREG(t1,	FRAME_T1),
118 	DBREG(t2,	FRAME_T2),
119 	DBREG(t3,	FRAME_T3),
120 	DBREG(t4,	FRAME_T4),
121 	DBREG(t5,	FRAME_T5),
122 	DBREG(t6,	FRAME_T6),
123 	DBREG(t7,	FRAME_T7),
124 	DBREG(s0,	FRAME_S0),
125 	DBREG(s1,	FRAME_S1),
126 	DBREG(s2,	FRAME_S2),
127 	DBREG(s3,	FRAME_S3),
128 	DBREG(s4,	FRAME_S4),
129 	DBREG(s5,	FRAME_S5),
130 	DBREG(s6,	FRAME_S6),
131 	DBREG(a0,	FRAME_A0),
132 	DBREG(a1,	FRAME_A1),
133 	DBREG(a2,	FRAME_A2),
134 	DBREG(a3,	FRAME_A3),
135 	DBREG(a4,	FRAME_A4),
136 	DBREG(a5,	FRAME_A5),
137 	DBREG(t8,	FRAME_T8),
138 	DBREG(t9,	FRAME_T9),
139 	DBREG(t10,	FRAME_T10),
140 	DBREG(t11,	FRAME_T11),
141 	DBREG(ra,	FRAME_RA),
142 	DBREG(t12,	FRAME_T12),
143 	DBREG(at,	FRAME_AT),
144 	DBREG(gp,	FRAME_GP),
145 	DBREG(sp,	FRAME_SP),
146 	DBREG(pc,	FRAME_PC),
147 	DBREG(ps,	FRAME_PS),
148 	DBREG(ai,	FRAME_T11),
149 	DBREG(pv,	FRAME_T12),
150 };
151 const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
152 
153 #undef DBREG
154 
155 static int
db_alpha_regop(const struct db_variable * vp,db_expr_t * val,int opcode)156 db_alpha_regop(const struct db_variable *vp, db_expr_t *val, int opcode)
157 {
158 	unsigned long *tfaddr;
159 	unsigned long zeroval = 0;
160 	struct trapframe *f = NULL;
161 
162 #ifdef _KERNEL			/* XXX ?? */
163 	if (vp->modif != NULL && *vp->modif == 'u') {
164 		if (curlwp != NULL)
165 			f = curlwp->l_md.md_tf;
166 	} else
167 #endif /* _KERNEL */
168 		f = DDB_REGS;
169 	tfaddr = f == NULL ? &zeroval : &f->tf_regs[(u_long)vp->valuep];
170 	switch (opcode) {
171 	case DB_VAR_GET:
172 		*val = *tfaddr;
173 		break;
174 
175 	case DB_VAR_SET:
176 		*tfaddr = *val;
177 		break;
178 
179 	default:
180 #ifdef _KERNEL
181 		panic("db_alpha_regop: unknown op %d", opcode);
182 #endif
183 		break;
184 	}
185 
186 	return (0);
187 }
188 
189 #ifdef _KERNEL
190 /*
191  * ddb_trap - field a kernel trap
192  */
193 int
ddb_trap(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long entry,db_regs_t * regs)194 ddb_trap(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long entry, db_regs_t *regs)
195 {
196 	struct cpu_info *ci = curcpu();
197 	unsigned long psl;
198 
199 	if (entry != ALPHA_KENTRY_IF ||
200 	    (a0 != ALPHA_IF_CODE_BPT && a0 != ALPHA_IF_CODE_BUGCHK)) {
201 		if (db_recover != 0) {
202 			/* This will longjmp back into db_command_loop() */
203 			db_error("Caught exception in ddb.\n");
204 			/* NOTREACHED */
205 		}
206 
207 		/*
208 		 * Tell caller "We did NOT handle the trap."
209 		 * Caller should panic, or whatever.
210 		 */
211 		return (0);
212 	}
213 
214 	/*
215 	 * alpha_debug() switches us to the debugger stack.
216 	 */
217 
218 	/* Our register state is simply the trapframe. */
219 	ddb_regp = ci->ci_db_regs = regs;
220 
221 	/*
222 	 * Use SWPIPL directly; we want to avoid processing
223 	 * software interrupts when we go back.  Soft ints
224 	 * will be caught later, so not to worry.
225 	 */
226 	psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
227 
228 	db_active++;
229 	cnpollc(true);		/* Set polling mode, unblank video */
230 
231 	db_trap(entry, a0);	/* Where the work happens */
232 
233 	cnpollc(false);		/* Resume interrupt mode */
234 	db_active--;
235 
236 	alpha_pal_swpipl(psl);
237 
238 	ddb_regp = ci->ci_db_regs = NULL;
239 
240 	/*
241 	 * Tell caller "We HAVE handled the trap."
242 	 */
243 	return (1);
244 }
245 
246 /*
247  * Read bytes from kernel address space for debugger.
248  */
249 void
db_read_bytes(vaddr_t addr,register size_t size,register char * data)250 db_read_bytes(vaddr_t addr, register size_t size, register char *data)
251 {
252 	register char	*src;
253 
254 	src = (char *)addr;
255 	while (size-- > 0)
256 		*data++ = *src++;
257 }
258 
259 /*
260  * Write bytes to kernel address space for debugger.
261  */
262 void
db_write_bytes(vaddr_t addr,register size_t size,register const char * data)263 db_write_bytes(vaddr_t addr, register size_t size, register const char *data)
264 {
265 	register char	*dst;
266 
267 	dst = (char *)addr;
268 	while (size-- > 0)
269 		*dst++ = *data++;
270 	alpha_pal_imb();
271 }
272 
273 void
cpu_Debugger(void)274 cpu_Debugger(void)
275 {
276 
277 	__asm volatile("call_pal 0x81");		/* bugchk */
278 }
279 #endif /* _KERNEL */
280 
281 /*
282  * Alpha-specific ddb commands:
283  *
284  *	cpu		tell DDB to use register state from the
285  *			CPU specified (MULTIPROCESSOR)
286  */
287 
288 #if defined(MULTIPROCESSOR)
289 void
db_mach_cpu(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)290 db_mach_cpu(db_expr_t addr, bool have_addr, db_expr_t count, const char * modif)
291 {
292 	struct cpu_info *ci;
293 
294 	if (!have_addr) {
295 		cpu_debug_dump();
296 		return;
297 	}
298 
299 	if (addr < 0 || addr >= ALPHA_MAXPROCS) {
300 		db_printf("CPU %ld out of range\n", addr);
301 		return;
302 	}
303 
304 	ci = cpu_info[addr];
305 	if (ci == NULL) {
306 		db_printf("CPU %ld is not configured\n", addr);
307 		return;
308 	}
309 
310 	if (ci != curcpu()) {
311 		if ((ci->ci_flags & CPUF_PAUSED) == 0) {
312 			db_printf("CPU %ld not paused\n", addr);
313 			return;
314 		}
315 	}
316 
317 	if (ci->ci_db_regs == NULL) {
318 		db_printf("CPU %ld has no register state\n", addr);
319 		return;
320 	}
321 
322 	db_printf("Using CPU %ld\n", addr);
323 	ddb_regp = ci->ci_db_regs;
324 }
325 #endif /* MULTIPROCESSOR */
326 
327 /*
328  * Map Alpha register numbers to trapframe/db_regs_t offsets.
329  */
330 static int reg_to_frame[32] = {
331 	FRAME_V0,
332 	FRAME_T0,
333 	FRAME_T1,
334 	FRAME_T2,
335 	FRAME_T3,
336 	FRAME_T4,
337 	FRAME_T5,
338 	FRAME_T6,
339 	FRAME_T7,
340 
341 	FRAME_S0,
342 	FRAME_S1,
343 	FRAME_S2,
344 	FRAME_S3,
345 	FRAME_S4,
346 	FRAME_S5,
347 	FRAME_S6,
348 
349 	FRAME_A0,
350 	FRAME_A1,
351 	FRAME_A2,
352 	FRAME_A3,
353 	FRAME_A4,
354 	FRAME_A5,
355 
356 	FRAME_T8,
357 	FRAME_T9,
358 	FRAME_T10,
359 	FRAME_T11,
360 	FRAME_RA,
361 	FRAME_T12,
362 	FRAME_AT,
363 	FRAME_GP,
364 	FRAME_SP,
365 	-1,		/* zero */
366 };
367 
368 u_long
db_register_value(db_regs_t * regs,int regno)369 db_register_value(db_regs_t *regs, int regno)
370 {
371 
372 	if (regno > 31 || regno < 0) {
373 		db_printf(" **** STRANGE REGISTER NUMBER %d **** ", regno);
374 		return (0);
375 	}
376 
377 	if (regno == 31)
378 		return (0);
379 
380 	return (regs->tf_regs[reg_to_frame[regno]]);
381 }
382 
383 #ifdef _KERNEL
384 /*
385  * Support functions for software single-step.
386  */
387 
388 bool
db_inst_call(int ins)389 db_inst_call(int ins)
390 {
391 	alpha_instruction insn;
392 
393 	insn.bits = ins;
394 	return ((insn.branch_format.opcode == op_bsr) ||
395 	    ((insn.jump_format.opcode == op_j) &&
396 	     (insn.jump_format.action & 1)));
397 }
398 
399 bool
db_inst_return(int ins)400 db_inst_return(int ins)
401 {
402 	alpha_instruction insn;
403 
404 	insn.bits = ins;
405 	return ((insn.jump_format.opcode == op_j) &&
406 	    (insn.jump_format.action == op_ret));
407 }
408 
409 bool
db_inst_trap_return(int ins)410 db_inst_trap_return(int ins)
411 {
412 	alpha_instruction insn;
413 
414 	insn.bits = ins;
415 	return ((insn.pal_format.opcode == op_pal) &&
416 	    (insn.pal_format.function == PAL_OSF1_rti));
417 }
418 
419 bool
db_inst_branch(int ins)420 db_inst_branch(int ins)
421 {
422 	alpha_instruction insn;
423 
424 	insn.bits = ins;
425 	switch (insn.branch_format.opcode) {
426 	case op_j:
427 	case op_br:
428 	case op_fbeq:
429 	case op_fblt:
430 	case op_fble:
431 	case op_fbne:
432 	case op_fbge:
433 	case op_fbgt:
434 	case op_blbc:
435 	case op_beq:
436 	case op_blt:
437 	case op_ble:
438 	case op_blbs:
439 	case op_bne:
440 	case op_bge:
441 	case op_bgt:
442 		return (true);
443 	}
444 
445 	return (false);
446 }
447 
448 bool
db_inst_unconditional_flow_transfer(int ins)449 db_inst_unconditional_flow_transfer(int ins)
450 {
451 	alpha_instruction insn;
452 
453 	insn.bits = ins;
454 	switch (insn.branch_format.opcode) {
455 	case op_j:
456 	case op_br:
457 		return (true);
458 
459 	case op_pal:
460 		switch (insn.pal_format.function) {
461 		case PAL_OSF1_retsys:
462 		case PAL_OSF1_rti:
463 		case PAL_OSF1_callsys:
464 			return (true);
465 		}
466 	}
467 
468 	return (false);
469 }
470 
471 #if 0
472 bool
473 db_inst_spill(int ins, int regn)
474 {
475 	alpha_instruction insn;
476 
477 	insn.bits = ins;
478 	return ((insn.mem_format.opcode == op_stq) &&
479 	    (insn.mem_format.rd == regn));
480 }
481 #endif
482 
483 bool
db_inst_load(int ins)484 db_inst_load(int ins)
485 {
486 	alpha_instruction insn;
487 
488 	insn.bits = ins;
489 
490 	/* Loads. */
491 	if (insn.mem_format.opcode == op_ldbu ||
492 	    insn.mem_format.opcode == op_ldq_u ||
493 	    insn.mem_format.opcode == op_ldwu)
494 		return (true);
495 	if ((insn.mem_format.opcode >= op_ldf) &&
496 	    (insn.mem_format.opcode <= op_ldt))
497 		return (true);
498 	if ((insn.mem_format.opcode >= op_ldl) &&
499 	    (insn.mem_format.opcode <= op_ldq_l))
500 		return (true);
501 
502 	/* Prefetches. */
503 	if (insn.mem_format.opcode == op_special) {
504 		/* Note: MB is treated as a store. */
505 		if ((insn.mem_format.displacement == (short)op_fetch) ||
506 		    (insn.mem_format.displacement == (short)op_fetch_m))
507 			return (true);
508 	}
509 
510 	return (false);
511 }
512 
513 bool
db_inst_store(int ins)514 db_inst_store(int ins)
515 {
516 	alpha_instruction insn;
517 
518 	insn.bits = ins;
519 
520 	/* Stores. */
521 	if (insn.mem_format.opcode == op_stw ||
522 	    insn.mem_format.opcode == op_stb ||
523 	    insn.mem_format.opcode == op_stq_u)
524 		return (true);
525 	if ((insn.mem_format.opcode >= op_stf) &&
526 	    (insn.mem_format.opcode <= op_stt))
527 		return (true);
528 	if ((insn.mem_format.opcode >= op_stl) &&
529 	    (insn.mem_format.opcode <= op_stq_c))
530 		return (true);
531 
532 	/* Barriers. */
533 	if (insn.mem_format.opcode == op_special) {
534 		if (insn.mem_format.displacement == op_mb)
535 			return (true);
536 	}
537 
538 	return (false);
539 }
540 
541 db_addr_t
db_branch_taken(int ins,db_addr_t pc,db_regs_t * regs)542 db_branch_taken(int ins, db_addr_t pc, db_regs_t *regs)
543 {
544 	long signed_immediate;
545 	alpha_instruction insn;
546 	db_addr_t newpc;
547 
548 	insn.bits = ins;
549 	switch (insn.branch_format.opcode) {
550 	/*
551 	 * Jump format: target PC is (contents of instruction's "RB") & ~3.
552 	 */
553 	case op_j:
554 		newpc = db_register_value(regs, insn.jump_format.rb) & ~3;
555 		break;
556 
557 	/*
558 	 * Branch format: target PC is
559 	 *	(new PC) + (4 * sign-ext(displacement)).
560 	 */
561 	case op_br:
562 	case op_fbeq:
563 	case op_fblt:
564 	case op_fble:
565 	case op_bsr:
566 	case op_fbne:
567 	case op_fbge:
568 	case op_fbgt:
569 	case op_blbc:
570 	case op_beq:
571 	case op_blt:
572 	case op_ble:
573 	case op_blbs:
574 	case op_bne:
575 	case op_bge:
576 	case op_bgt:
577 		signed_immediate = insn.branch_format.displacement;
578 		newpc = (pc + 4) + (signed_immediate << 2);
579 		break;
580 
581 	default:
582 		printf("DDB: db_inst_branch_taken on non-branch!\n");
583 		newpc = pc;	/* XXX */
584 	}
585 
586 	return (newpc);
587 }
588 #endif /* _KERNEL */
589 
590 unsigned long
db_alpha_read_saved_reg(unsigned long * regp)591 db_alpha_read_saved_reg(unsigned long *regp)
592 {
593 	unsigned long reg;
594 
595 	db_read_bytes((db_addr_t)regp, sizeof(reg), (char *)&reg);
596 	return reg;
597 }
598 
599 unsigned long
db_alpha_tf_reg(struct trapframe * tf,unsigned int regno)600 db_alpha_tf_reg(struct trapframe *tf, unsigned int regno)
601 {
602 	return db_alpha_read_saved_reg(&tf->tf_regs[regno]);
603 }
604 
605 /*
606  * Alpha special symbol handling.
607  */
608 db_alpha_nlist db_alpha_nl[] = {
609 	DB_ALPHA_SYM(SYM_XentArith, XentArith),
610 	DB_ALPHA_SYM(SYM_XentIF, XentIF),
611 	DB_ALPHA_SYM(SYM_XentInt, XentInt),
612 	DB_ALPHA_SYM(SYM_XentMM, XentMM),
613 	DB_ALPHA_SYM(SYM_XentSys, XentSys),
614 	DB_ALPHA_SYM(SYM_XentUna, XentUna),
615 	DB_ALPHA_SYM(SYM_XentRestart, XentRestart),
616 	DB_ALPHA_SYM(SYM_exception_return, exception_return),
617 	DB_ALPHA_SYM(SYM_alpha_kthread_backstop, alpha_kthread_backstop),
618 #ifndef _KERNEL
619 	DB_ALPHA_SYM(SYM_dumppcb, dumppcb),
620 #endif /* _KERNEL */
621 	DB_ALPHA_SYM_EOL
622 };
623 
624 static int
db_alpha_nlist_lookup(db_addr_t addr)625 db_alpha_nlist_lookup(db_addr_t addr)
626 {
627 	int i;
628 
629 	for (i = 0; i < SYM___eol; i++) {
630 		if (db_alpha_nl[i].n_value == addr) {
631 			return i;
632 		}
633 	}
634 	return -1;
635 }
636 
637 bool
db_alpha_sym_is_trap(db_addr_t addr)638 db_alpha_sym_is_trap(db_addr_t addr)
639 {
640 	int i = db_alpha_nlist_lookup(addr);
641 	return i >= SYM_XentArith && i <= SYM_exception_return;
642 }
643 
644 bool
db_alpha_sym_is_backstop(db_addr_t addr)645 db_alpha_sym_is_backstop(db_addr_t addr)
646 {
647 	return db_alpha_nlist_lookup(addr) == SYM_alpha_kthread_backstop;
648 }
649 
650 bool
db_alpha_sym_is_syscall(db_addr_t addr)651 db_alpha_sym_is_syscall(db_addr_t addr)
652 {
653 	return db_alpha_nlist_lookup(addr) == SYM_XentSys;
654 }
655 
656 const char *
db_alpha_trapsym_description(db_addr_t addr)657 db_alpha_trapsym_description(db_addr_t addr)
658 {
659 	static const char * const trap_descriptions[] = {
660 	[SYM_XentArith]		=	"arithmetic trap",
661 	[SYM_XentIF]		=	"instruction fault",
662 	[SYM_XentInt]		=	"interrupt",
663 	[SYM_XentMM]		=	"memory management fault",
664 	[SYM_XentSys]		=	"syscall",
665 	[SYM_XentUna]		=	"unaligned access fault",
666 	[SYM_XentRestart]	=	"console restart",
667 	[SYM_exception_return]	=	"(exception return)",
668 	};
669 
670 	int i = db_alpha_nlist_lookup(addr);
671 	if (i >= SYM_XentArith && i <= SYM_exception_return) {
672 		return trap_descriptions[i];
673 	}
674 	return "??? trap ???";
675 }
676