xref: /openbsd-src/sys/arch/alpha/alpha/db_interface.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /* $OpenBSD: db_interface.c,v 1.30 2024/11/07 16:02:29 miod Exp $ */
2 /* $NetBSD: db_interface.c,v 1.8 1999/10/12 17:08:57 jdolecek Exp $ */
3 
4 /*
5  * Mach Operating System
6  * Copyright (c) 1992,1991,1990 Carnegie Mellon University
7  * All Rights Reserved.
8  *
9  * Permission to use, copy, modify and distribute this software and its
10  * documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS ``AS IS''
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  *
29  *	db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
30  */
31 
32 /*
33  * Parts of this file are derived from Mach 3:
34  *
35  *	File: alpha_instruction.c
36  *	Author: Alessandro Forin, Carnegie Mellon University
37  *	Date:	6/92
38  */
39 
40 /*
41  * Interface to DDB.
42  *
43  * Modified for NetBSD/alpha by:
44  *
45  *	Christopher G. Demetriou, Carnegie Mellon University
46  *
47  *	Jason R. Thorpe, Numerical Aerospace Simulation Facility,
48  *	NASA Ames Research Center
49  */
50 
51 #include <sys/param.h>
52 #include <sys/proc.h>
53 #include <sys/reboot.h>
54 #include <sys/systm.h>
55 
56 #include <uvm/uvm_extern.h>
57 
58 #include <dev/cons.h>
59 
60 #include <machine/db_machdep.h>
61 #include <machine/pal.h>
62 #include <machine/prom.h>
63 
64 #include <alpha/alpha/db_instruction.h>
65 
66 #include <ddb/db_sym.h>
67 #include <ddb/db_command.h>
68 #include <ddb/db_extern.h>
69 #include <ddb/db_access.h>
70 #include <ddb/db_output.h>
71 #include <ddb/db_variables.h>
72 #include <ddb/db_interface.h>
73 
74 
75 extern label_t	*db_recover;
76 
77 #if 0
78 extern char *trap_type[];
79 extern int trap_types;
80 #endif
81 
82 db_regs_t ddb_regs;
83 
84 #if defined(MULTIPROCESSOR)
85 void	db_mach_cpu(db_expr_t, int, db_expr_t, char *);
86 #endif
87 
88 const struct db_command db_machine_command_table[] = {
89 #if defined(MULTIPROCESSOR)
90 	{ "ddbcpu",	db_mach_cpu,	0,	NULL },
91 #endif
92 	{ NULL,		NULL,		0,	NULL }
93 };
94 
95 struct db_variable db_regs[] = {
96 	{	"v0",	&ddb_regs.tf_regs[FRAME_V0],	FCN_NULL	},
97 	{	"t0",	&ddb_regs.tf_regs[FRAME_T0],	FCN_NULL	},
98 	{	"t1",	&ddb_regs.tf_regs[FRAME_T1],	FCN_NULL	},
99 	{	"t2",	&ddb_regs.tf_regs[FRAME_T2],	FCN_NULL	},
100 	{	"t3",	&ddb_regs.tf_regs[FRAME_T3],	FCN_NULL	},
101 	{	"t4",	&ddb_regs.tf_regs[FRAME_T4],	FCN_NULL	},
102 	{	"t5",	&ddb_regs.tf_regs[FRAME_T5],	FCN_NULL	},
103 	{	"t6",	&ddb_regs.tf_regs[FRAME_T6],	FCN_NULL	},
104 	{	"t7",	&ddb_regs.tf_regs[FRAME_T7],	FCN_NULL	},
105 	{	"s0",	&ddb_regs.tf_regs[FRAME_S0],	FCN_NULL	},
106 	{	"s1",	&ddb_regs.tf_regs[FRAME_S1],	FCN_NULL	},
107 	{	"s2",	&ddb_regs.tf_regs[FRAME_S2],	FCN_NULL	},
108 	{	"s3",	&ddb_regs.tf_regs[FRAME_S3],	FCN_NULL	},
109 	{	"s4",	&ddb_regs.tf_regs[FRAME_S4],	FCN_NULL	},
110 	{	"s5",	&ddb_regs.tf_regs[FRAME_S5],	FCN_NULL	},
111 	{	"s6",	&ddb_regs.tf_regs[FRAME_S6],	FCN_NULL	},
112 	{	"a0",	&ddb_regs.tf_regs[FRAME_A0],	FCN_NULL	},
113 	{	"a1",	&ddb_regs.tf_regs[FRAME_A1],	FCN_NULL	},
114 	{	"a2",	&ddb_regs.tf_regs[FRAME_A2],	FCN_NULL	},
115 	{	"a3",	&ddb_regs.tf_regs[FRAME_A3],	FCN_NULL	},
116 	{	"a4",	&ddb_regs.tf_regs[FRAME_A4],	FCN_NULL	},
117 	{	"a5",	&ddb_regs.tf_regs[FRAME_A5],	FCN_NULL	},
118 	{	"t8",	&ddb_regs.tf_regs[FRAME_T8],	FCN_NULL	},
119 	{	"t9",	&ddb_regs.tf_regs[FRAME_T9],	FCN_NULL	},
120 	{	"t10",	&ddb_regs.tf_regs[FRAME_T10],	FCN_NULL	},
121 	{	"t11",	&ddb_regs.tf_regs[FRAME_T11],	FCN_NULL	},
122 	{	"ra",	&ddb_regs.tf_regs[FRAME_RA],	FCN_NULL	},
123 	{	"t12",	&ddb_regs.tf_regs[FRAME_T12],	FCN_NULL	},
124 	{	"at",	&ddb_regs.tf_regs[FRAME_AT],	FCN_NULL	},
125 	{	"gp",	&ddb_regs.tf_regs[FRAME_GP],	FCN_NULL	},
126 	{	"sp",	&ddb_regs.tf_regs[FRAME_SP],	FCN_NULL	},
127 	{	"pc",	&ddb_regs.tf_regs[FRAME_PC],	FCN_NULL	},
128 	{	"ps",	&ddb_regs.tf_regs[FRAME_PS],	FCN_NULL	},
129 	{	"ai",	&ddb_regs.tf_regs[FRAME_T11],	FCN_NULL	},
130 	{	"pv",	&ddb_regs.tf_regs[FRAME_T12],	FCN_NULL	},
131 };
132 struct db_variable *db_eregs = db_regs + nitems(db_regs);
133 
134 /*
135  * ddb_trap - field a kernel trap
136  */
137 int
138 ddb_trap(a0, a1, a2, entry, regs)
139 	unsigned long a0, a1, a2, entry;
140 	db_regs_t *regs;
141 {
142 	struct cpu_info *ci = curcpu();
143 	int s;
144 
145 	if (entry != ALPHA_KENTRY_IF ||
146 	    (a0 != ALPHA_IF_CODE_BPT && a0 != ALPHA_IF_CODE_BUGCHK)) {
147 		if (db_recover != 0) {
148 			/* This will longjmp back into db_command_loop() */
149 			db_error("Caught exception in ddb.\n");
150 			/* NOTREACHED */
151 		}
152 
153 		/*
154 		 * Tell caller "We did NOT handle the trap."
155 		 * Caller should panic, or whatever.
156 		 */
157 		return (0);
158 	}
159 
160 	/*
161 	 * alpha_debug() switches us to the debugger stack.
162 	 */
163 
164 	ci->ci_db_regs = regs;
165 	ddb_regs = *regs;
166 
167 	s = splhigh();
168 
169 	db_active++;
170 	cnpollc(1);		/* Set polling mode, unblank video */
171 
172 	db_trap(entry, a0);	/* Where the work happens */
173 
174 	cnpollc(0);		/* Resume interrupt mode */
175 	db_active--;
176 
177 	splx(s);
178 
179 	*regs = ddb_regs;
180 
181 	/*
182 	 * Tell caller "We HAVE handled the trap."
183 	 */
184 	return (1);
185 }
186 
187 /*
188  * Read bytes from kernel address space for debugger.
189  */
190 void
191 db_read_bytes(addr, size, datap)
192 	vaddr_t		addr;
193 	register size_t	size;
194 	register void	*datap;
195 {
196 	register char	*data = datap, *src;
197 
198 	src = (char *)addr;
199 	while (size-- > 0)
200 		*data++ = *src++;
201 }
202 
203 /*
204  * Write bytes to kernel address space for debugger.
205  */
206 void
207 db_write_bytes(addr, size, datap)
208 	vaddr_t		addr;
209 	register size_t	size;
210 	register void	*datap;
211 {
212 	register char	*data = datap, *dst;
213 
214 	dst = (char *)addr;
215 	while (size-- > 0)
216 		*dst++ = *data++;
217 	alpha_pal_imb();
218 }
219 
220 void
221 db_enter()
222 {
223 
224 	__asm volatile("call_pal 0x81");		/* bugchk */
225 }
226 
227 /*
228  * Map Alpha register numbers to trapframe/db_regs_t offsets.
229  */
230 static int reg_to_frame[32] = {
231 	FRAME_V0,
232 	FRAME_T0,
233 	FRAME_T1,
234 	FRAME_T2,
235 	FRAME_T3,
236 	FRAME_T4,
237 	FRAME_T5,
238 	FRAME_T6,
239 	FRAME_T7,
240 
241 	FRAME_S0,
242 	FRAME_S1,
243 	FRAME_S2,
244 	FRAME_S3,
245 	FRAME_S4,
246 	FRAME_S5,
247 	FRAME_S6,
248 
249 	FRAME_A0,
250 	FRAME_A1,
251 	FRAME_A2,
252 	FRAME_A3,
253 	FRAME_A4,
254 	FRAME_A5,
255 
256 	FRAME_T8,
257 	FRAME_T9,
258 	FRAME_T10,
259 	FRAME_T11,
260 	FRAME_RA,
261 	FRAME_T12,
262 	FRAME_AT,
263 	FRAME_GP,
264 	FRAME_SP,
265 	-1,		/* zero */
266 };
267 
268 u_long
269 db_register_value(regs, regno)
270 	db_regs_t *regs;
271 	int regno;
272 {
273 
274 	if (regno > 31 || regno < 0) {
275 		db_printf(" **** STRANGE REGISTER NUMBER %d **** ", regno);
276 		return (0);
277 	}
278 
279 	if (regno == 31)
280 		return (0);
281 
282 	return (regs->tf_regs[reg_to_frame[regno]]);
283 }
284 
285 /*
286  * Support functions for software single-step.
287  */
288 
289 int
290 db_inst_call(int ins)
291 {
292 	alpha_instruction insn;
293 
294 	insn.bits = ins;
295 	return ((insn.branch_format.opcode == op_bsr) ||
296 	    ((insn.jump_format.opcode == op_j) &&
297 	     (insn.jump_format.action & 1)));
298 }
299 
300 int
301 db_inst_return(int ins)
302 {
303 	alpha_instruction insn;
304 
305 	insn.bits = ins;
306 	return ((insn.jump_format.opcode == op_j) &&
307 	    (insn.jump_format.action == op_ret));
308 }
309 
310 int
311 db_inst_trap_return(int ins)
312 {
313 	alpha_instruction insn;
314 
315 	insn.bits = ins;
316 	return ((insn.pal_format.opcode == op_pal) &&
317 	    (insn.pal_format.function == PAL_OSF1_rti));
318 }
319 
320 int
321 db_inst_branch(int ins)
322 {
323 	alpha_instruction insn;
324 
325 	insn.bits = ins;
326 	switch (insn.branch_format.opcode) {
327 	case op_j:
328 	case op_br:
329 	case op_fbeq:
330 	case op_fblt:
331 	case op_fble:
332 	case op_fbne:
333 	case op_fbge:
334 	case op_fbgt:
335 	case op_blbc:
336 	case op_beq:
337 	case op_blt:
338 	case op_ble:
339 	case op_blbs:
340 	case op_bne:
341 	case op_bge:
342 	case op_bgt:
343 		return 1;
344 	}
345 
346 	return 0;
347 }
348 
349 int
350 db_inst_unconditional_flow_transfer(int ins)
351 {
352 	alpha_instruction insn;
353 
354 	insn.bits = ins;
355 	switch (insn.branch_format.opcode) {
356 	case op_j:
357 	case op_br:
358 		return 1;
359 
360 	case op_pal:
361 		switch (insn.pal_format.function) {
362 		case PAL_OSF1_retsys:
363 		case PAL_OSF1_rti:
364 		case PAL_OSF1_callsys:
365 			return 1;
366 		}
367 	}
368 
369 	return 0;
370 }
371 
372 int
373 db_inst_load(int ins)
374 {
375 	alpha_instruction insn;
376 
377 	insn.bits = ins;
378 
379 	/* Loads. */
380 	if (insn.mem_format.opcode == op_ldbu ||
381 	    insn.mem_format.opcode == op_ldq_u ||
382 	    insn.mem_format.opcode == op_ldwu)
383 		return 1;
384 	if ((insn.mem_format.opcode >= op_ldf) &&
385 	    (insn.mem_format.opcode <= op_ldt))
386 		return 1;
387 	if ((insn.mem_format.opcode >= op_ldl) &&
388 	    (insn.mem_format.opcode <= op_ldq_l))
389 		return 1;
390 
391 	/* Prefetches. */
392 	if (insn.mem_format.opcode == op_special) {
393 		/* Note: MB is treated as a store. */
394 		if ((insn.mem_format.displacement == (short)op_fetch) ||
395 		    (insn.mem_format.displacement == (short)op_fetch_m))
396 			return 1;
397 	}
398 
399 	return 0;
400 }
401 
402 vaddr_t
403 db_branch_taken(ins, pc, regs)
404 	int ins;
405 	vaddr_t pc;
406 	db_regs_t *regs;
407 {
408 	long signed_immediate;
409 	alpha_instruction insn;
410 	vaddr_t newpc;
411 
412 	insn.bits = ins;
413 	switch (insn.branch_format.opcode) {
414 	/*
415 	 * Jump format: target PC is (contents of instruction's "RB") & ~3.
416 	 */
417 	case op_j:
418 		newpc = db_register_value(regs, insn.jump_format.rb) & ~3;
419 		break;
420 
421 	/*
422 	 * Branch format: target PC is
423 	 *	(new PC) + (4 * sign-ext(displacement)).
424 	 */
425 	case op_br:
426 	case op_fbeq:
427 	case op_fblt:
428 	case op_fble:
429 	case op_bsr:
430 	case op_fbne:
431 	case op_fbge:
432 	case op_fbgt:
433 	case op_blbc:
434 	case op_beq:
435 	case op_blt:
436 	case op_ble:
437 	case op_blbs:
438 	case op_bne:
439 	case op_bge:
440 	case op_bgt:
441 		signed_immediate = insn.branch_format.displacement;
442 		newpc = (pc + 4) + (signed_immediate << 2);
443 		break;
444 
445 	default:
446 		printf("DDB: db_inst_branch_taken on non-branch!\n");
447 		newpc = pc;	/* XXX */
448 	}
449 
450 	return (newpc);
451 }
452 
453 /*
454  * Validate an address for use as a breakpoint.  We cannot let some
455  * addresses have breakpoints as the ddb code itself uses that codepath.
456  * Recursion and kernel stack space exhaustion will follow.
457  */
458 int
459 db_valid_breakpoint(addr)
460 	vaddr_t addr;
461 {
462 	const char *name;
463 	db_expr_t offset;
464 
465 	db_find_sym_and_offset(addr, &name, &offset);
466 	if (name && strcmp(name, "alpha_pal_swpipl") == 0)
467 		return (0);
468 	return (1);
469 }
470 
471 vaddr_t
472 next_instr_address(pc, branch)
473 	vaddr_t pc;
474 	int branch;
475 {
476 	if (!branch)
477 		return (pc + sizeof(int));
478 	return (branch_taken(*(u_int *)pc, pc, getreg_val, &ddb_regs));
479 }
480 
481 #if defined(MULTIPROCESSOR)
482 void
483 db_mach_cpu(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
484 {
485 	struct cpu_info *ci;
486 	CPU_INFO_ITERATOR cii;
487 
488 	if (have_addr == 0) {
489 		db_printf("addr               dev   id flg mtx ipis "
490 		    "curproc            fpcurproc\n");
491 		CPU_INFO_FOREACH(cii, ci)
492 			db_printf("%p %-5s %02lu %03lx %03d %04lx %p %p\n",
493 			    ci, ci->ci_dev->dv_xname, ci->ci_cpuid,
494 			    ci->ci_flags, ci->ci_mutex_level, ci->ci_ipis,
495 			    ci->ci_curproc, ci->ci_fpcurproc);
496 		return;
497 	}
498 
499 	if (addr < 0 || addr >= ALPHA_MAXPROCS) {
500 		db_printf("CPU %ld out of range\n", addr);
501 		return;
502 	}
503 
504 	ci = cpu_info[addr];
505 	if (ci == NULL) {
506 		db_printf("CPU %ld is not configured\n", addr);
507 		return;
508 	}
509 
510 	if (ci != curcpu()) {
511 		if ((ci->ci_flags & CPUF_PAUSED) == 0) {
512 			db_printf("CPU %ld not paused\n", addr);
513 			return;
514 		}
515 	}
516 
517 	if (ci->ci_db_regs == NULL) {
518 		db_printf("CPU %ld has no register state\n", addr);
519 		return;
520 	}
521 
522 	db_printf("Using CPU %ld\n", addr);
523 	ddb_regs = *ci->ci_db_regs;	/* struct copy */
524 }
525 #endif /* MULTIPROCESSOR */
526 
527 void
528 db_machine_init()
529 {
530 }
531