xref: /netbsd/sys/arch/alpha/alpha/db_interface.c (revision e00aa033)
1 /* $NetBSD: db_interface.c,v 1.37 2022/10/26 23:38:05 riastradh 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 #include "opt_ddb.h"
51 #include "opt_multiprocessor.h"
52 
53 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
54 
55 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.37 2022/10/26 23:38:05 riastradh Exp $");
56 
57 #include <sys/param.h>
58 #include <sys/proc.h>
59 #include <sys/reboot.h>
60 #include <sys/systm.h>
61 
62 #include <dev/cons.h>
63 
64 #include <machine/alpha.h>
65 #include <machine/db_machdep.h>
66 #include <machine/pal.h>
67 #include <machine/prom.h>
68 
69 #include <alpha/alpha/db_instruction.h>
70 
71 #include <ddb/db_active.h>
72 #include <ddb/db_sym.h>
73 #include <ddb/db_command.h>
74 #include <ddb/db_extern.h>
75 #include <ddb/db_access.h>
76 #include <ddb/db_output.h>
77 #include <ddb/db_variables.h>
78 #include <ddb/db_interface.h>
79 
80 
81 #if 0
82 extern char *trap_type[];
83 extern int trap_types;
84 #endif
85 
86 int	db_active = 0;
87 
88 db_regs_t *ddb_regp;
89 
90 #if defined(MULTIPROCESSOR)
91 void	db_mach_cpu(db_expr_t, bool, db_expr_t, const char *);
92 #endif
93 
94 const struct db_command db_machine_command_table[] = {
95 #if defined(MULTIPROCESSOR)
96 	{ DDB_ADD_CMD("cpu",	db_mach_cpu,	0,
97 	  "switch to another cpu", "cpu-no", NULL) },
98 #endif
99 	{ DDB_END_CMD },
100 };
101 
102 static int db_alpha_regop(const struct db_variable *, db_expr_t *, int);
103 
104 #define	dbreg(xx)	((long *)(xx))
105 
106 const struct db_variable db_regs[] = {
107 	{	"v0",	dbreg(FRAME_V0),	db_alpha_regop	},
108 	{	"t0",	dbreg(FRAME_T0),	db_alpha_regop	},
109 	{	"t1",	dbreg(FRAME_T1),	db_alpha_regop	},
110 	{	"t2",	dbreg(FRAME_T2),	db_alpha_regop	},
111 	{	"t3",	dbreg(FRAME_T3),	db_alpha_regop	},
112 	{	"t4",	dbreg(FRAME_T4),	db_alpha_regop	},
113 	{	"t5",	dbreg(FRAME_T5),	db_alpha_regop	},
114 	{	"t6",	dbreg(FRAME_T6),	db_alpha_regop	},
115 	{	"t7",	dbreg(FRAME_T7),	db_alpha_regop	},
116 	{	"s0",	dbreg(FRAME_S0),	db_alpha_regop	},
117 	{	"s1",	dbreg(FRAME_S1),	db_alpha_regop	},
118 	{	"s2",	dbreg(FRAME_S2),	db_alpha_regop	},
119 	{	"s3",	dbreg(FRAME_S3),	db_alpha_regop	},
120 	{	"s4",	dbreg(FRAME_S4),	db_alpha_regop	},
121 	{	"s5",	dbreg(FRAME_S5),	db_alpha_regop	},
122 	{	"s6",	dbreg(FRAME_S6),	db_alpha_regop	},
123 	{	"a0",	dbreg(FRAME_A0),	db_alpha_regop	},
124 	{	"a1",	dbreg(FRAME_A1),	db_alpha_regop	},
125 	{	"a2",	dbreg(FRAME_A2),	db_alpha_regop	},
126 	{	"a3",	dbreg(FRAME_A3),	db_alpha_regop	},
127 	{	"a4",	dbreg(FRAME_A4),	db_alpha_regop	},
128 	{	"a5",	dbreg(FRAME_A5),	db_alpha_regop	},
129 	{	"t8",	dbreg(FRAME_T8),	db_alpha_regop	},
130 	{	"t9",	dbreg(FRAME_T9),	db_alpha_regop	},
131 	{	"t10",	dbreg(FRAME_T10),	db_alpha_regop	},
132 	{	"t11",	dbreg(FRAME_T11),	db_alpha_regop	},
133 	{	"ra",	dbreg(FRAME_RA),	db_alpha_regop	},
134 	{	"t12",	dbreg(FRAME_T12),	db_alpha_regop	},
135 	{	"at",	dbreg(FRAME_AT),	db_alpha_regop	},
136 	{	"gp",	dbreg(FRAME_GP),	db_alpha_regop	},
137 	{	"sp",	dbreg(FRAME_SP),	db_alpha_regop	},
138 	{	"pc",	dbreg(FRAME_PC),	db_alpha_regop	},
139 	{	"ps",	dbreg(FRAME_PS),	db_alpha_regop	},
140 	{	"ai",	dbreg(FRAME_T11),	db_alpha_regop	},
141 	{	"pv",	dbreg(FRAME_T12),	db_alpha_regop	},
142 };
143 const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
144 
145 static int
db_alpha_regop(const struct db_variable * vp,db_expr_t * val,int opcode)146 db_alpha_regop(const struct db_variable *vp, db_expr_t *val, int opcode)
147 {
148 	unsigned long *tfaddr;
149 	unsigned long zeroval = 0;
150 	struct trapframe *f = NULL;
151 
152 	if (vp->modif != NULL && *vp->modif == 'u') {
153 		if (curlwp != NULL)
154 			f = curlwp->l_md.md_tf;
155 	} else	f = DDB_REGS;
156 	tfaddr = f == NULL ? &zeroval : &f->tf_regs[(u_long)vp->valuep];
157 	switch (opcode) {
158 	case DB_VAR_GET:
159 		*val = *tfaddr;
160 		break;
161 
162 	case DB_VAR_SET:
163 		*tfaddr = *val;
164 		break;
165 
166 	default:
167 		panic("db_alpha_regop: unknown op %d", opcode);
168 	}
169 
170 	return (0);
171 }
172 
173 /*
174  * ddb_trap - field a kernel trap
175  */
176 int
ddb_trap(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long entry,db_regs_t * regs)177 ddb_trap(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long entry, db_regs_t *regs)
178 {
179 	struct cpu_info *ci = curcpu();
180 	unsigned long psl;
181 
182 	if (entry != ALPHA_KENTRY_IF ||
183 	    (a0 != ALPHA_IF_CODE_BPT && a0 != ALPHA_IF_CODE_BUGCHK)) {
184 		if (db_recover != 0) {
185 			/* This will longjmp back into db_command_loop() */
186 			db_error("Caught exception in ddb.\n");
187 			/* NOTREACHED */
188 		}
189 
190 		/*
191 		 * Tell caller "We did NOT handle the trap."
192 		 * Caller should panic, or whatever.
193 		 */
194 		return (0);
195 	}
196 
197 	/*
198 	 * alpha_debug() switches us to the debugger stack.
199 	 */
200 
201 	/* Our register state is simply the trapframe. */
202 	ddb_regp = ci->ci_db_regs = regs;
203 
204 	/*
205 	 * Use SWPIPL directly; we want to avoid processing
206 	 * software interrrupts when we go back.  Soft ints
207 	 * will be caught later, so not to worry.
208 	 */
209 	psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
210 
211 	db_active++;
212 	cnpollc(true);		/* Set polling mode, unblank video */
213 
214 	db_trap(entry, a0);	/* Where the work happens */
215 
216 	cnpollc(false);		/* Resume interrupt mode */
217 	db_active--;
218 
219 	alpha_pal_swpipl(psl);
220 
221 	ddb_regp = ci->ci_db_regs = NULL;
222 
223 	/*
224 	 * Tell caller "We HAVE handled the trap."
225 	 */
226 	return (1);
227 }
228 
229 /*
230  * Read bytes from kernel address space for debugger.
231  */
232 void
db_read_bytes(vaddr_t addr,register size_t size,register char * data)233 db_read_bytes(vaddr_t addr, register size_t size, register char *data)
234 {
235 	register char	*src;
236 
237 	src = (char *)addr;
238 	while (size-- > 0)
239 		*data++ = *src++;
240 }
241 
242 /*
243  * Write bytes to kernel address space for debugger.
244  */
245 void
db_write_bytes(vaddr_t addr,register size_t size,register const char * data)246 db_write_bytes(vaddr_t addr, register size_t size, register const char *data)
247 {
248 	register char	*dst;
249 
250 	dst = (char *)addr;
251 	while (size-- > 0)
252 		*dst++ = *data++;
253 	alpha_pal_imb();
254 }
255 
256 void
cpu_Debugger(void)257 cpu_Debugger(void)
258 {
259 
260 	__asm volatile("call_pal 0x81");		/* bugchk */
261 }
262 
263 /*
264  * Alpha-specific ddb commands:
265  *
266  *	cpu		tell DDB to use register state from the
267  *			CPU specified (MULTIPROCESSOR)
268  */
269 
270 #if defined(MULTIPROCESSOR)
271 void
db_mach_cpu(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)272 db_mach_cpu(db_expr_t addr, bool have_addr, db_expr_t count, const char * modif)
273 {
274 	struct cpu_info *ci;
275 
276 	if (!have_addr) {
277 		cpu_debug_dump();
278 		return;
279 	}
280 
281 	if (addr < 0 || addr >= ALPHA_MAXPROCS) {
282 		db_printf("CPU %ld out of range\n", addr);
283 		return;
284 	}
285 
286 	ci = cpu_info[addr];
287 	if (ci == NULL) {
288 		db_printf("CPU %ld is not configured\n", addr);
289 		return;
290 	}
291 
292 	if (ci != curcpu()) {
293 		if ((ci->ci_flags & CPUF_PAUSED) == 0) {
294 			db_printf("CPU %ld not paused\n", addr);
295 			return;
296 		}
297 	}
298 
299 	if (ci->ci_db_regs == NULL) {
300 		db_printf("CPU %ld has no register state\n", addr);
301 		return;
302 	}
303 
304 	db_printf("Using CPU %ld\n", addr);
305 	ddb_regp = ci->ci_db_regs;
306 }
307 #endif /* MULTIPROCESSOR */
308 
309 /*
310  * Map Alpha register numbers to trapframe/db_regs_t offsets.
311  */
312 static int reg_to_frame[32] = {
313 	FRAME_V0,
314 	FRAME_T0,
315 	FRAME_T1,
316 	FRAME_T2,
317 	FRAME_T3,
318 	FRAME_T4,
319 	FRAME_T5,
320 	FRAME_T6,
321 	FRAME_T7,
322 
323 	FRAME_S0,
324 	FRAME_S1,
325 	FRAME_S2,
326 	FRAME_S3,
327 	FRAME_S4,
328 	FRAME_S5,
329 	FRAME_S6,
330 
331 	FRAME_A0,
332 	FRAME_A1,
333 	FRAME_A2,
334 	FRAME_A3,
335 	FRAME_A4,
336 	FRAME_A5,
337 
338 	FRAME_T8,
339 	FRAME_T9,
340 	FRAME_T10,
341 	FRAME_T11,
342 	FRAME_RA,
343 	FRAME_T12,
344 	FRAME_AT,
345 	FRAME_GP,
346 	FRAME_SP,
347 	-1,		/* zero */
348 };
349 
350 u_long
db_register_value(db_regs_t * regs,int regno)351 db_register_value(db_regs_t *regs, int regno)
352 {
353 
354 	if (regno > 31 || regno < 0) {
355 		db_printf(" **** STRANGE REGISTER NUMBER %d **** ", regno);
356 		return (0);
357 	}
358 
359 	if (regno == 31)
360 		return (0);
361 
362 	return (regs->tf_regs[reg_to_frame[regno]]);
363 }
364 
365 /*
366  * Support functions for software single-step.
367  */
368 
369 bool
db_inst_call(int ins)370 db_inst_call(int ins)
371 {
372 	alpha_instruction insn;
373 
374 	insn.bits = ins;
375 	return ((insn.branch_format.opcode == op_bsr) ||
376 	    ((insn.jump_format.opcode == op_j) &&
377 	     (insn.jump_format.action & 1)));
378 }
379 
380 bool
db_inst_return(int ins)381 db_inst_return(int ins)
382 {
383 	alpha_instruction insn;
384 
385 	insn.bits = ins;
386 	return ((insn.jump_format.opcode == op_j) &&
387 	    (insn.jump_format.action == op_ret));
388 }
389 
390 bool
db_inst_trap_return(int ins)391 db_inst_trap_return(int ins)
392 {
393 	alpha_instruction insn;
394 
395 	insn.bits = ins;
396 	return ((insn.pal_format.opcode == op_pal) &&
397 	    (insn.pal_format.function == PAL_OSF1_rti));
398 }
399 
400 bool
db_inst_branch(int ins)401 db_inst_branch(int ins)
402 {
403 	alpha_instruction insn;
404 
405 	insn.bits = ins;
406 	switch (insn.branch_format.opcode) {
407 	case op_j:
408 	case op_br:
409 	case op_fbeq:
410 	case op_fblt:
411 	case op_fble:
412 	case op_fbne:
413 	case op_fbge:
414 	case op_fbgt:
415 	case op_blbc:
416 	case op_beq:
417 	case op_blt:
418 	case op_ble:
419 	case op_blbs:
420 	case op_bne:
421 	case op_bge:
422 	case op_bgt:
423 		return (true);
424 	}
425 
426 	return (false);
427 }
428 
429 bool
db_inst_unconditional_flow_transfer(int ins)430 db_inst_unconditional_flow_transfer(int ins)
431 {
432 	alpha_instruction insn;
433 
434 	insn.bits = ins;
435 	switch (insn.branch_format.opcode) {
436 	case op_j:
437 	case op_br:
438 		return (true);
439 
440 	case op_pal:
441 		switch (insn.pal_format.function) {
442 		case PAL_OSF1_retsys:
443 		case PAL_OSF1_rti:
444 		case PAL_OSF1_callsys:
445 			return (true);
446 		}
447 	}
448 
449 	return (false);
450 }
451 
452 #if 0
453 bool
454 db_inst_spill(int ins, int regn)
455 {
456 	alpha_instruction insn;
457 
458 	insn.bits = ins;
459 	return ((insn.mem_format.opcode == op_stq) &&
460 	    (insn.mem_format.rd == regn));
461 }
462 #endif
463 
464 bool
db_inst_load(int ins)465 db_inst_load(int ins)
466 {
467 	alpha_instruction insn;
468 
469 	insn.bits = ins;
470 
471 	/* Loads. */
472 	if (insn.mem_format.opcode == op_ldbu ||
473 	    insn.mem_format.opcode == op_ldq_u ||
474 	    insn.mem_format.opcode == op_ldwu)
475 		return (true);
476 	if ((insn.mem_format.opcode >= op_ldf) &&
477 	    (insn.mem_format.opcode <= op_ldt))
478 		return (true);
479 	if ((insn.mem_format.opcode >= op_ldl) &&
480 	    (insn.mem_format.opcode <= op_ldq_l))
481 		return (true);
482 
483 	/* Prefetches. */
484 	if (insn.mem_format.opcode == op_special) {
485 		/* Note: MB is treated as a store. */
486 		if ((insn.mem_format.displacement == (short)op_fetch) ||
487 		    (insn.mem_format.displacement == (short)op_fetch_m))
488 			return (true);
489 	}
490 
491 	return (false);
492 }
493 
494 bool
db_inst_store(int ins)495 db_inst_store(int ins)
496 {
497 	alpha_instruction insn;
498 
499 	insn.bits = ins;
500 
501 	/* Stores. */
502 	if (insn.mem_format.opcode == op_stw ||
503 	    insn.mem_format.opcode == op_stb ||
504 	    insn.mem_format.opcode == op_stq_u)
505 		return (true);
506 	if ((insn.mem_format.opcode >= op_stf) &&
507 	    (insn.mem_format.opcode <= op_stt))
508 		return (true);
509 	if ((insn.mem_format.opcode >= op_stl) &&
510 	    (insn.mem_format.opcode <= op_stq_c))
511 		return (true);
512 
513 	/* Barriers. */
514 	if (insn.mem_format.opcode == op_special) {
515 		if (insn.mem_format.displacement == op_mb)
516 			return (true);
517 	}
518 
519 	return (false);
520 }
521 
522 db_addr_t
db_branch_taken(int ins,db_addr_t pc,db_regs_t * regs)523 db_branch_taken(int ins, db_addr_t pc, db_regs_t *regs)
524 {
525 	long signed_immediate;
526 	alpha_instruction insn;
527 	db_addr_t newpc;
528 
529 	insn.bits = ins;
530 	switch (insn.branch_format.opcode) {
531 	/*
532 	 * Jump format: target PC is (contents of instruction's "RB") & ~3.
533 	 */
534 	case op_j:
535 		newpc = db_register_value(regs, insn.jump_format.rb) & ~3;
536 		break;
537 
538 	/*
539 	 * Branch format: target PC is
540 	 *	(new PC) + (4 * sign-ext(displacement)).
541 	 */
542 	case op_br:
543 	case op_fbeq:
544 	case op_fblt:
545 	case op_fble:
546 	case op_bsr:
547 	case op_fbne:
548 	case op_fbge:
549 	case op_fbgt:
550 	case op_blbc:
551 	case op_beq:
552 	case op_blt:
553 	case op_ble:
554 	case op_blbs:
555 	case op_bne:
556 	case op_bge:
557 	case op_bgt:
558 		signed_immediate = insn.branch_format.displacement;
559 		newpc = (pc + 4) + (signed_immediate << 2);
560 		break;
561 
562 	default:
563 		printf("DDB: db_inst_branch_taken on non-branch!\n");
564 		newpc = pc;	/* XXX */
565 	}
566 
567 	return (newpc);
568 }
569