1/*-
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/i386/i386/db_trace.c 308418 2016-11-07 12:10:17Z kib $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kdb.h>
33#include <sys/proc.h>
34#include <sys/sysent.h>
35
36#include <machine/cpu.h>
37#include <machine/frame.h>
38#include <machine/md_var.h>
39#include <machine/pcb.h>
40#include <machine/reg.h>
41#include <machine/stack.h>
42
43#include <vm/vm.h>
44#include <vm/vm_param.h>
45#include <vm/pmap.h>
46
47#include <ddb/ddb.h>
48#include <ddb/db_access.h>
49#include <ddb/db_sym.h>
50#include <ddb/db_variables.h>
51
52static db_varfcn_t db_esp;
53static db_varfcn_t db_frame;
54static db_varfcn_t db_frame_seg;
55static db_varfcn_t db_gs;
56static db_varfcn_t db_ss;
57
58/*
59 * Machine register set.
60 */
61#define	DB_OFFSET(x)	(db_expr_t *)offsetof(struct trapframe, x)
62struct db_variable db_regs[] = {
63	{ "cs",		DB_OFFSET(tf_cs),	db_frame_seg },
64	{ "ds",		DB_OFFSET(tf_ds),	db_frame_seg },
65	{ "es",		DB_OFFSET(tf_es),	db_frame_seg },
66	{ "fs",		DB_OFFSET(tf_fs),	db_frame_seg },
67	{ "gs",		NULL,			db_gs },
68	{ "ss",		NULL,			db_ss },
69	{ "eax",	DB_OFFSET(tf_eax),	db_frame },
70	{ "ecx",	DB_OFFSET(tf_ecx),	db_frame },
71	{ "edx",	DB_OFFSET(tf_edx),	db_frame },
72	{ "ebx",	DB_OFFSET(tf_ebx),	db_frame },
73	{ "esp",	NULL,			db_esp },
74	{ "ebp",	DB_OFFSET(tf_ebp),	db_frame },
75	{ "esi",	DB_OFFSET(tf_esi),	db_frame },
76	{ "edi",	DB_OFFSET(tf_edi),	db_frame },
77	{ "eip",	DB_OFFSET(tf_eip),	db_frame },
78	{ "efl",	DB_OFFSET(tf_eflags),	db_frame },
79};
80struct db_variable *db_eregs = db_regs + nitems(db_regs);
81
82static __inline int
83get_esp(struct trapframe *tf)
84{
85	return (TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp);
86}
87
88static int
89db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
90{
91	int *reg;
92
93	if (kdb_frame == NULL)
94		return (0);
95
96	reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep);
97	if (op == DB_VAR_GET)
98		*valuep = *reg;
99	else
100		*reg = *valuep;
101	return (1);
102}
103
104static int
105db_frame_seg(struct db_variable *vp, db_expr_t *valuep, int op)
106{
107	struct trapframe_vm86 *tfp;
108	int off;
109	uint16_t *reg;
110
111	if (kdb_frame == NULL)
112		return (0);
113
114	off = (intptr_t)vp->valuep;
115	if (kdb_frame->tf_eflags & PSL_VM) {
116		tfp = (void *)kdb_frame;
117		switch ((intptr_t)vp->valuep) {
118		case (intptr_t)DB_OFFSET(tf_cs):
119			reg = (uint16_t *)&tfp->tf_cs;
120			break;
121		case (intptr_t)DB_OFFSET(tf_ds):
122			reg = (uint16_t *)&tfp->tf_vm86_ds;
123			break;
124		case (intptr_t)DB_OFFSET(tf_es):
125			reg = (uint16_t *)&tfp->tf_vm86_es;
126			break;
127		case (intptr_t)DB_OFFSET(tf_fs):
128			reg = (uint16_t *)&tfp->tf_vm86_fs;
129			break;
130		}
131	} else
132		reg = (uint16_t *)((uintptr_t)kdb_frame + off);
133	if (op == DB_VAR_GET)
134		*valuep = *reg;
135	else
136		*reg = *valuep;
137	return (1);
138}
139
140static int
141db_esp(struct db_variable *vp, db_expr_t *valuep, int op)
142{
143
144	if (kdb_frame == NULL)
145		return (0);
146
147	if (op == DB_VAR_GET)
148		*valuep = get_esp(kdb_frame);
149	else if (TF_HAS_STACKREGS(kdb_frame))
150		kdb_frame->tf_esp = *valuep;
151	return (1);
152}
153
154static int
155db_gs(struct db_variable *vp, db_expr_t *valuep, int op)
156{
157	struct trapframe_vm86 *tfp;
158
159	if (kdb_frame != NULL && kdb_frame->tf_eflags & PSL_VM) {
160		tfp = (void *)kdb_frame;
161		if (op == DB_VAR_GET)
162			*valuep = tfp->tf_vm86_gs;
163		else
164			tfp->tf_vm86_gs = *valuep;
165		return (1);
166	}
167	if (op == DB_VAR_GET)
168		*valuep = rgs();
169	else
170		load_gs(*valuep);
171	return (1);
172}
173
174static int
175db_ss(struct db_variable *vp, db_expr_t *valuep, int op)
176{
177
178	if (kdb_frame == NULL)
179		return (0);
180
181	if (op == DB_VAR_GET)
182		*valuep = TF_HAS_STACKREGS(kdb_frame) ? kdb_frame->tf_ss :
183		    rss();
184	else if (TF_HAS_STACKREGS(kdb_frame))
185		kdb_frame->tf_ss = *valuep;
186	return (1);
187}
188
189#define NORMAL		0
190#define	TRAP		1
191#define	INTERRUPT	2
192#define	SYSCALL		3
193#define	DOUBLE_FAULT	4
194#define	TRAP_INTERRUPT	5
195#define	TRAP_TIMERINT	6
196
197static void db_nextframe(struct i386_frame **, db_addr_t *, struct thread *);
198static int db_numargs(struct i386_frame *);
199static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t,
200    void *);
201static void decode_syscall(int, struct thread *);
202
203static const char * watchtype_str(int type);
204int  i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access,
205		    struct dbreg *d);
206int  i386_clr_watch(int watchnum, struct dbreg *d);
207
208/*
209 * Figure out how many arguments were passed into the frame at "fp".
210 */
211static int
212db_numargs(fp)
213	struct i386_frame *fp;
214{
215	char   *argp;
216	int	inst;
217	int	args;
218
219	argp = (char *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
220	/*
221	 * XXX etext is wrong for LKMs.  We should attempt to interpret
222	 * the instruction at the return address in all cases.  This
223	 * may require better fault handling.
224	 */
225	if (argp < btext || argp >= etext) {
226		args = -1;
227	} else {
228retry:
229		inst = db_get_value((int)argp, 4, FALSE);
230		if ((inst & 0xff) == 0x59)	/* popl %ecx */
231			args = 1;
232		else if ((inst & 0xffff) == 0xc483)	/* addl $Ibs, %esp */
233			args = ((inst >> 16) & 0xff) / 4;
234		else if ((inst & 0xf8ff) == 0xc089) {	/* movl %eax, %Reg */
235			argp += 2;
236			goto retry;
237		} else
238			args = -1;
239	}
240	return (args);
241}
242
243static void
244db_print_stack_entry(name, narg, argnp, argp, callpc, frame)
245	const char *name;
246	int narg;
247	char **argnp;
248	int *argp;
249	db_addr_t callpc;
250	void *frame;
251{
252	int n = narg >= 0 ? narg : 5;
253
254	db_printf("%s(", name);
255	while (n) {
256		if (argnp)
257			db_printf("%s=", *argnp++);
258		db_printf("%r", db_get_value((int)argp, 4, FALSE));
259		argp++;
260		if (--n != 0)
261			db_printf(",");
262	}
263	if (narg < 0)
264		db_printf(",...");
265	db_printf(") at ");
266	db_printsym(callpc, DB_STGY_PROC);
267	if (frame != NULL)
268		db_printf("/frame 0x%r", (register_t)frame);
269	db_printf("\n");
270}
271
272static void
273decode_syscall(int number, struct thread *td)
274{
275	struct proc *p;
276	c_db_sym_t sym;
277	db_expr_t diff;
278	sy_call_t *f;
279	const char *symname;
280
281	db_printf(" (%d", number);
282	p = (td != NULL) ? td->td_proc : NULL;
283	if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) {
284		f = p->p_sysent->sv_table[number].sy_call;
285		sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff);
286		if (sym != DB_SYM_NULL && diff == 0) {
287			db_symbol_values(sym, &symname, NULL);
288			db_printf(", %s, %s", p->p_sysent->sv_name, symname);
289		}
290	}
291	db_printf(")");
292}
293
294/*
295 * Figure out the next frame up in the call stack.
296 */
297static void
298db_nextframe(struct i386_frame **fp, db_addr_t *ip, struct thread *td)
299{
300	struct trapframe *tf;
301	int frame_type;
302	int eip, esp, ebp;
303	db_expr_t offset;
304	c_db_sym_t sym;
305	const char *name;
306
307	eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
308	ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE);
309
310	/*
311	 * Figure out frame type.  We look at the address just before
312	 * the saved instruction pointer as the saved EIP is after the
313	 * call function, and if the function being called is marked as
314	 * dead (such as panic() at the end of dblfault_handler()), then
315	 * the instruction at the saved EIP will be part of a different
316	 * function (syscall() in this example) rather than the one that
317	 * actually made the call.
318	 */
319	frame_type = NORMAL;
320	sym = db_search_symbol(eip - 1, DB_STGY_ANY, &offset);
321	db_symbol_values(sym, &name, NULL);
322	if (name != NULL) {
323		if (strcmp(name, "calltrap") == 0 ||
324		    strcmp(name, "fork_trampoline") == 0)
325			frame_type = TRAP;
326		else if (strncmp(name, "Xatpic_intr", 11) == 0 ||
327		    strncmp(name, "Xapic_isr", 9) == 0)
328			frame_type = INTERRUPT;
329		else if (strcmp(name, "Xlcall_syscall") == 0 ||
330		    strcmp(name, "Xint0x80_syscall") == 0)
331			frame_type = SYSCALL;
332		else if (strcmp(name, "dblfault_handler") == 0)
333			frame_type = DOUBLE_FAULT;
334		/* XXX: These are interrupts with trap frames. */
335		else if (strcmp(name, "Xtimerint") == 0)
336			frame_type = TRAP_TIMERINT;
337		else if (strcmp(name, "Xcpustop") == 0 ||
338		    strcmp(name, "Xrendezvous") == 0 ||
339		    strcmp(name, "Xipi_intr_bitmap_handler") == 0)
340			frame_type = TRAP_INTERRUPT;
341	}
342
343	/*
344	 * Normal frames need no special processing.
345	 */
346	if (frame_type == NORMAL) {
347		*ip = (db_addr_t) eip;
348		*fp = (struct i386_frame *) ebp;
349		return;
350	}
351
352	db_print_stack_entry(name, 0, 0, 0, eip, &(*fp)->f_frame);
353
354	/*
355	 * For a double fault, we have to snag the values from the
356	 * previous TSS since a double fault uses a task gate to
357	 * switch to a known good state.
358	 */
359	if (frame_type == DOUBLE_FAULT) {
360		esp = PCPU_GET(common_tss.tss_esp);
361		eip = PCPU_GET(common_tss.tss_eip);
362		ebp = PCPU_GET(common_tss.tss_ebp);
363		db_printf(
364		    "--- trap 0x17, eip = %#r, esp = %#r, ebp = %#r ---\n",
365		    eip, esp, ebp);
366		*ip = (db_addr_t) eip;
367		*fp = (struct i386_frame *) ebp;
368		return;
369	}
370
371	/*
372	 * Point to base of trapframe which is just above the
373	 * current frame.
374	 */
375	if (frame_type == INTERRUPT)
376		tf = (struct trapframe *)((int)*fp + 16);
377	else if (frame_type == TRAP_INTERRUPT)
378		tf = (struct trapframe *)((int)*fp + 8);
379	else
380		tf = (struct trapframe *)((int)*fp + 12);
381
382	if (INKERNEL((int) tf)) {
383		esp = get_esp(tf);
384		eip = tf->tf_eip;
385		ebp = tf->tf_ebp;
386		switch (frame_type) {
387		case TRAP:
388			db_printf("--- trap %#r", tf->tf_trapno);
389			break;
390		case SYSCALL:
391			db_printf("--- syscall");
392			decode_syscall(tf->tf_eax, td);
393			break;
394		case TRAP_TIMERINT:
395		case TRAP_INTERRUPT:
396		case INTERRUPT:
397			db_printf("--- interrupt");
398			break;
399		default:
400			panic("The moon has moved again.");
401		}
402		db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip,
403		    esp, ebp);
404	}
405
406	*ip = (db_addr_t) eip;
407	*fp = (struct i386_frame *) ebp;
408}
409
410static int
411db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame,
412    db_addr_t pc, register_t sp, int count)
413{
414	struct i386_frame *actframe;
415#define MAXNARG	16
416	char *argnames[MAXNARG], **argnp = NULL;
417	const char *name;
418	int *argp;
419	db_expr_t offset;
420	c_db_sym_t sym;
421	int instr, narg;
422	boolean_t first;
423
424	if (db_segsize(tf) == 16) {
425		db_printf(
426"--- 16-bit%s, cs:eip = %#x:%#x, ss:esp = %#x:%#x, ebp = %#x, tf = %p ---\n",
427		    (tf->tf_eflags & PSL_VM) ? " (vm86)" : "",
428		    tf->tf_cs, tf->tf_eip,
429		    TF_HAS_STACKREGS(tf) ? tf->tf_ss : rss(),
430		    TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp,
431		    tf->tf_ebp, tf);
432		return (0);
433	}
434
435	/*
436	 * If an indirect call via an invalid pointer caused a trap,
437	 * %pc contains the invalid address while the return address
438	 * of the unlucky caller has been saved by CPU on the stack
439	 * just before the trap frame.  In this case, try to recover
440	 * the caller's address so that the first frame is assigned
441	 * to the right spot in the right function, for that is where
442	 * the failure actually happened.
443	 *
444	 * This trick depends on the fault address stashed in tf_err
445	 * by trap_fatal() before entering KDB.
446	 */
447	if (kdb_frame && pc == kdb_frame->tf_err) {
448		/*
449		 * Find where the trap frame actually ends.
450		 * It won't contain tf_esp or tf_ss unless crossing rings.
451		 */
452		if (TF_HAS_STACKREGS(kdb_frame))
453			instr = (int)(kdb_frame + 1);
454		else
455			instr = (int)&kdb_frame->tf_esp;
456		pc = db_get_value(instr, 4, FALSE);
457	}
458
459	if (count == -1)
460		count = 1024;
461
462	first = TRUE;
463	while (count-- && !db_pager_quit) {
464		sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
465		db_symbol_values(sym, &name, NULL);
466
467		/*
468		 * Attempt to determine a (possibly fake) frame that gives
469		 * the caller's pc.  It may differ from `frame' if the
470		 * current function never sets up a standard frame or hasn't
471		 * set one up yet or has just discarded one.  The last two
472		 * cases can be guessed fairly reliably for code generated
473		 * by gcc.  The first case is too much trouble to handle in
474		 * general because the amount of junk on the stack depends
475		 * on the pc (the special handling of "calltrap", etc. in
476		 * db_nextframe() works because the `next' pc is special).
477		 */
478		actframe = frame;
479		if (first) {
480			first = FALSE;
481			if (sym == C_DB_SYM_NULL && sp != 0) {
482				/*
483				 * If a symbol couldn't be found, we've probably
484				 * jumped to a bogus location, so try and use
485				 * the return address to find our caller.
486				 */
487				db_print_stack_entry(name, 0, 0, 0, pc,
488				    NULL);
489				pc = db_get_value(sp, 4, FALSE);
490				if (db_search_symbol(pc, DB_STGY_PROC,
491				    &offset) == C_DB_SYM_NULL)
492					break;
493				continue;
494			} else if (tf != NULL) {
495				instr = db_get_value(pc, 4, FALSE);
496				if ((instr & 0xffffff) == 0x00e58955) {
497					/* pushl %ebp; movl %esp, %ebp */
498					actframe = (void *)(get_esp(tf) - 4);
499				} else if ((instr & 0xffff) == 0x0000e589) {
500					/* movl %esp, %ebp */
501					actframe = (void *)get_esp(tf);
502					if (tf->tf_ebp == 0) {
503						/* Fake frame better. */
504						frame = actframe;
505					}
506				} else if ((instr & 0xff) == 0x000000c3) {
507					/* ret */
508					actframe = (void *)(get_esp(tf) - 4);
509				} else if (offset == 0) {
510					/* Probably an assembler symbol. */
511					actframe = (void *)(get_esp(tf) - 4);
512				}
513			} else if (strcmp(name, "fork_trampoline") == 0) {
514				/*
515				 * Don't try to walk back on a stack for a
516				 * process that hasn't actually been run yet.
517				 */
518				db_print_stack_entry(name, 0, 0, 0, pc,
519				    actframe);
520				break;
521			}
522		}
523
524		argp = &actframe->f_arg0;
525		narg = MAXNARG;
526		if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
527			argnp = argnames;
528		} else {
529			narg = db_numargs(frame);
530		}
531
532		db_print_stack_entry(name, narg, argnp, argp, pc, actframe);
533
534		if (actframe != frame) {
535			/* `frame' belongs to caller. */
536			pc = (db_addr_t)
537			    db_get_value((int)&actframe->f_retaddr, 4, FALSE);
538			continue;
539		}
540
541		db_nextframe(&frame, &pc, td);
542
543		if (INKERNEL((int)pc) && !INKERNEL((int) frame)) {
544			sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
545			db_symbol_values(sym, &name, NULL);
546			db_print_stack_entry(name, 0, 0, 0, pc, frame);
547			break;
548		}
549		if (!INKERNEL((int) frame)) {
550			break;
551		}
552	}
553
554	return (0);
555}
556
557void
558db_trace_self(void)
559{
560	struct i386_frame *frame;
561	db_addr_t callpc;
562	register_t ebp;
563
564	__asm __volatile("movl %%ebp,%0" : "=r" (ebp));
565	frame = (struct i386_frame *)ebp;
566	callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
567	frame = frame->f_frame;
568	db_backtrace(curthread, NULL, frame, callpc, 0, -1);
569}
570
571int
572db_trace_thread(struct thread *thr, int count)
573{
574	struct pcb *ctx;
575	struct trapframe *tf;
576
577	ctx = kdb_thr_ctx(thr);
578	tf = thr == kdb_thread ? kdb_frame : NULL;
579	return (db_backtrace(thr, tf, (struct i386_frame *)ctx->pcb_ebp,
580	    ctx->pcb_eip, ctx->pcb_esp, count));
581}
582
583int
584i386_set_watch(watchnum, watchaddr, size, access, d)
585	int watchnum;
586	unsigned int watchaddr;
587	int size;
588	int access;
589	struct dbreg *d;
590{
591	int i, len;
592
593	if (watchnum == -1) {
594		for (i = 0; i < 4; i++)
595			if (!DBREG_DR7_ENABLED(d->dr[7], i))
596				break;
597		if (i < 4)
598			watchnum = i;
599		else
600			return (-1);
601	}
602
603	switch (access) {
604	case DBREG_DR7_EXEC:
605		size = 1; /* size must be 1 for an execution breakpoint */
606		/* fall through */
607	case DBREG_DR7_WRONLY:
608	case DBREG_DR7_RDWR:
609		break;
610	default:
611		return (-1);
612	}
613
614	/*
615	 * we can watch a 1, 2, or 4 byte sized location
616	 */
617	switch (size) {
618	case 1:
619		len = DBREG_DR7_LEN_1;
620		break;
621	case 2:
622		len = DBREG_DR7_LEN_2;
623		break;
624	case 4:
625		len = DBREG_DR7_LEN_4;
626		break;
627	default:
628		return (-1);
629	}
630
631	/* clear the bits we are about to affect */
632	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
633
634	/* set drN register to the address, N=watchnum */
635	DBREG_DRX(d, watchnum) = watchaddr;
636
637	/* enable the watchpoint */
638	d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
639	    DBREG_DR7_GLOBAL_ENABLE);
640
641	return (watchnum);
642}
643
644
645int
646i386_clr_watch(watchnum, d)
647	int watchnum;
648	struct dbreg *d;
649{
650
651	if (watchnum < 0 || watchnum >= 4)
652		return (-1);
653
654	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
655	DBREG_DRX(d, watchnum) = 0;
656
657	return (0);
658}
659
660
661int
662db_md_set_watchpoint(addr, size)
663	db_expr_t addr;
664	db_expr_t size;
665{
666	struct dbreg d;
667	int avail, i, wsize;
668
669	fill_dbregs(NULL, &d);
670
671	avail = 0;
672	for(i = 0; i < 4; i++) {
673		if (!DBREG_DR7_ENABLED(d.dr[7], i))
674			avail++;
675	}
676
677	if (avail * 4 < size)
678		return (-1);
679
680	for (i = 0; i < 4 && (size > 0); i++) {
681		if (!DBREG_DR7_ENABLED(d.dr[7], i)) {
682			if (size > 2)
683				wsize = 4;
684			else
685				wsize = size;
686			i386_set_watch(i, addr, wsize,
687				       DBREG_DR7_WRONLY, &d);
688			addr += wsize;
689			size -= wsize;
690		}
691	}
692
693	set_dbregs(NULL, &d);
694
695	return(0);
696}
697
698
699int
700db_md_clr_watchpoint(addr, size)
701	db_expr_t addr;
702	db_expr_t size;
703{
704	struct dbreg d;
705	int i;
706
707	fill_dbregs(NULL, &d);
708
709	for(i = 0; i < 4; i++) {
710		if (DBREG_DR7_ENABLED(d.dr[7], i)) {
711			if ((DBREG_DRX((&d), i) >= addr) &&
712			    (DBREG_DRX((&d), i) < addr+size))
713				i386_clr_watch(i, &d);
714
715		}
716	}
717
718	set_dbregs(NULL, &d);
719
720	return(0);
721}
722
723
724static const char *
725watchtype_str(type)
726	int type;
727{
728	switch (type) {
729		case DBREG_DR7_EXEC   : return "execute";    break;
730		case DBREG_DR7_RDWR   : return "read/write"; break;
731		case DBREG_DR7_WRONLY : return "write";	     break;
732		default		      : return "invalid";    break;
733	}
734}
735
736
737void
738db_md_list_watchpoints()
739{
740	struct dbreg d;
741	int i, len, type;
742
743	fill_dbregs(NULL, &d);
744
745	db_printf("\nhardware watchpoints:\n");
746	db_printf("  watch    status        type  len     address\n");
747	db_printf("  -----  --------  ----------  ---  ----------\n");
748	for (i = 0; i < 4; i++) {
749		if (DBREG_DR7_ENABLED(d.dr[7], i)) {
750			type = DBREG_DR7_ACCESS(d.dr[7], i);
751			len = DBREG_DR7_LEN(d.dr[7], i);
752			db_printf("  %-5d  %-8s  %10s  %3d  ",
753			    i, "enabled", watchtype_str(type), len + 1);
754			db_printsym((db_addr_t)DBREG_DRX((&d), i), DB_STGY_ANY);
755			db_printf("\n");
756		} else {
757			db_printf("  %-5d  disabled\n", i);
758		}
759	}
760
761	db_printf("\ndebug register values:\n");
762	for (i = 0; i < 8; i++) {
763		db_printf("  dr%d 0x%08x\n", i, DBREG_DRX((&d), i));
764	}
765	db_printf("\n");
766}
767
768
769