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