xref: /freebsd/sys/x86/x86/dbreg.c (revision b7924341)
1c02c04f1SMitchell Horne /*-
2c02c04f1SMitchell Horne  * Mach Operating System
3c02c04f1SMitchell Horne  * Copyright (c) 1991,1990 Carnegie Mellon University
4c02c04f1SMitchell Horne  * All Rights Reserved.
5c02c04f1SMitchell Horne  *
6c02c04f1SMitchell Horne  * Permission to use, copy, modify and distribute this software and its
7c02c04f1SMitchell Horne  * documentation is hereby granted, provided that both the copyright
8c02c04f1SMitchell Horne  * notice and this permission notice appear in all copies of the
9c02c04f1SMitchell Horne  * software, derivative works or modified versions, and any portions
10c02c04f1SMitchell Horne  * thereof, and that both notices appear in supporting documentation.
11c02c04f1SMitchell Horne  *
12c02c04f1SMitchell Horne  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13c02c04f1SMitchell Horne  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14c02c04f1SMitchell Horne  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15c02c04f1SMitchell Horne  *
16c02c04f1SMitchell Horne  * Carnegie Mellon requests users of this software to return to
17c02c04f1SMitchell Horne  *
18c02c04f1SMitchell Horne  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19c02c04f1SMitchell Horne  *  School of Computer Science
20c02c04f1SMitchell Horne  *  Carnegie Mellon University
21c02c04f1SMitchell Horne  *  Pittsburgh PA 15213-3890
22c02c04f1SMitchell Horne  *
23c02c04f1SMitchell Horne  * any improvements or extensions that they make and grant Carnegie the
24c02c04f1SMitchell Horne  * rights to redistribute these changes.
25c02c04f1SMitchell Horne  */
26c02c04f1SMitchell Horne 
27c02c04f1SMitchell Horne #include "opt_ddb.h"
28c02c04f1SMitchell Horne 
29c02c04f1SMitchell Horne #include <sys/types.h>
3015dc1d44SMitchell Horne #include <sys/kdb.h>
31c02c04f1SMitchell Horne #include <sys/pcpu.h>
32b7924341SAndrew Turner #include <sys/reg.h>
33c02c04f1SMitchell Horne #include <sys/smp.h>
34c02c04f1SMitchell Horne #include <sys/systm.h>
35c02c04f1SMitchell Horne 
36c02c04f1SMitchell Horne #include <machine/frame.h>
3715dc1d44SMitchell Horne #include <machine/kdb.h>
38c02c04f1SMitchell Horne #include <machine/md_var.h>
39c02c04f1SMitchell Horne 
40c02c04f1SMitchell Horne #include <ddb/ddb.h>
41c02c04f1SMitchell Horne #include <ddb/db_sym.h>
42c02c04f1SMitchell Horne 
43c02c04f1SMitchell Horne #define NDBREGS		4
44c02c04f1SMitchell Horne #ifdef __amd64__
45c02c04f1SMitchell Horne #define	MAXWATCHSIZE	8
46c02c04f1SMitchell Horne #else
47c02c04f1SMitchell Horne #define	MAXWATCHSIZE	4
48c02c04f1SMitchell Horne #endif
49c02c04f1SMitchell Horne 
50c02c04f1SMitchell Horne /*
51c02c04f1SMitchell Horne  * Set a watchpoint in the debug register denoted by 'watchnum'.
52c02c04f1SMitchell Horne  */
53c02c04f1SMitchell Horne static void
dbreg_set_watchreg(int watchnum,vm_offset_t watchaddr,vm_size_t size,int access,struct dbreg * d)54c02c04f1SMitchell Horne dbreg_set_watchreg(int watchnum, vm_offset_t watchaddr, vm_size_t size,
55c02c04f1SMitchell Horne     int access, struct dbreg *d)
56c02c04f1SMitchell Horne {
57c02c04f1SMitchell Horne 	int len;
58c02c04f1SMitchell Horne 
59c02c04f1SMitchell Horne 	MPASS(watchnum >= 0 && watchnum < NDBREGS);
60c02c04f1SMitchell Horne 
61c02c04f1SMitchell Horne 	/* size must be 1 for an execution breakpoint */
62c02c04f1SMitchell Horne 	if (access == DBREG_DR7_EXEC)
63c02c04f1SMitchell Horne 		size = 1;
64c02c04f1SMitchell Horne 
65c02c04f1SMitchell Horne 	/*
66c02c04f1SMitchell Horne 	 * we can watch a 1, 2, or 4 byte sized location
67c02c04f1SMitchell Horne 	 */
68c02c04f1SMitchell Horne 	switch (size) {
69c02c04f1SMitchell Horne 	case 1:
70c02c04f1SMitchell Horne 		len = DBREG_DR7_LEN_1;
71c02c04f1SMitchell Horne 		break;
72c02c04f1SMitchell Horne 	case 2:
73c02c04f1SMitchell Horne 		len = DBREG_DR7_LEN_2;
74c02c04f1SMitchell Horne 		break;
75c02c04f1SMitchell Horne 	case 4:
76c02c04f1SMitchell Horne 		len = DBREG_DR7_LEN_4;
77c02c04f1SMitchell Horne 		break;
78c02c04f1SMitchell Horne #if MAXWATCHSIZE >= 8
79c02c04f1SMitchell Horne 	case 8:
80c02c04f1SMitchell Horne 		len = DBREG_DR7_LEN_8;
81c02c04f1SMitchell Horne 		break;
82c02c04f1SMitchell Horne #endif
83c02c04f1SMitchell Horne 	default:
84c02c04f1SMitchell Horne 		return;
85c02c04f1SMitchell Horne 	}
86c02c04f1SMitchell Horne 
87c02c04f1SMitchell Horne 	/* clear the bits we are about to affect */
88c02c04f1SMitchell Horne 	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
89c02c04f1SMitchell Horne 
90c02c04f1SMitchell Horne 	/* set drN register to the address, N=watchnum */
91c02c04f1SMitchell Horne 	DBREG_DRX(d, watchnum) = watchaddr;
92c02c04f1SMitchell Horne 
93c02c04f1SMitchell Horne 	/* enable the watchpoint */
94c02c04f1SMitchell Horne 	d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
95c02c04f1SMitchell Horne 	    DBREG_DR7_GLOBAL_ENABLE);
96c02c04f1SMitchell Horne }
97c02c04f1SMitchell Horne 
98c02c04f1SMitchell Horne /*
99c02c04f1SMitchell Horne  * Remove a watchpoint from the debug register denoted by 'watchnum'.
100c02c04f1SMitchell Horne  */
101c02c04f1SMitchell Horne static void
dbreg_clr_watchreg(int watchnum,struct dbreg * d)102c02c04f1SMitchell Horne dbreg_clr_watchreg(int watchnum, struct dbreg *d)
103c02c04f1SMitchell Horne {
104c02c04f1SMitchell Horne 	MPASS(watchnum >= 0 && watchnum < NDBREGS);
105c02c04f1SMitchell Horne 
106c02c04f1SMitchell Horne 	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
107c02c04f1SMitchell Horne 	DBREG_DRX(d, watchnum) = 0;
108c02c04f1SMitchell Horne }
109c02c04f1SMitchell Horne 
110c02c04f1SMitchell Horne /*
111c02c04f1SMitchell Horne  * Sync the debug registers. Other cores will read these values from the PCPU
112c02c04f1SMitchell Horne  * area when they resume. See amd64_db_resume_dbreg() below.
113c02c04f1SMitchell Horne  */
114c02c04f1SMitchell Horne static void
dbreg_sync(struct dbreg * dp)115c02c04f1SMitchell Horne dbreg_sync(struct dbreg *dp)
116c02c04f1SMitchell Horne {
117c02c04f1SMitchell Horne #ifdef __amd64__
118c02c04f1SMitchell Horne 	struct pcpu *pc;
119c02c04f1SMitchell Horne 	int cpu, c;
120c02c04f1SMitchell Horne 
121c02c04f1SMitchell Horne 	cpu = PCPU_GET(cpuid);
122c02c04f1SMitchell Horne 	CPU_FOREACH(c) {
123c02c04f1SMitchell Horne 		if (c == cpu)
124c02c04f1SMitchell Horne 			continue;
125c02c04f1SMitchell Horne 		pc = pcpu_find(c);
126c02c04f1SMitchell Horne 		memcpy(pc->pc_dbreg, dp, sizeof(*dp));
127c02c04f1SMitchell Horne 		pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
128c02c04f1SMitchell Horne 	}
129c02c04f1SMitchell Horne #endif
130c02c04f1SMitchell Horne }
131c02c04f1SMitchell Horne 
132c02c04f1SMitchell Horne int
dbreg_set_watchpoint(vm_offset_t addr,vm_size_t size,int access)13315dc1d44SMitchell Horne dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size, int access)
134c02c04f1SMitchell Horne {
135c02c04f1SMitchell Horne 	struct dbreg *d;
136c02c04f1SMitchell Horne 	int avail, i, wsize;
137c02c04f1SMitchell Horne 
138c02c04f1SMitchell Horne #ifdef __amd64__
139c02c04f1SMitchell Horne 	d = (struct dbreg *)PCPU_PTR(dbreg);
140c02c04f1SMitchell Horne #else
141c02c04f1SMitchell Horne 	/* debug registers aren't stored in PCPU on i386. */
142c02c04f1SMitchell Horne 	struct dbreg d_temp;
143c02c04f1SMitchell Horne 	d = &d_temp;
144c02c04f1SMitchell Horne #endif
145c02c04f1SMitchell Horne 
14615dc1d44SMitchell Horne 	/* Validate the access type */
14715dc1d44SMitchell Horne 	if (access != DBREG_DR7_EXEC && access != DBREG_DR7_WRONLY &&
14815dc1d44SMitchell Horne 	    access != DBREG_DR7_RDWR)
14915dc1d44SMitchell Horne 		return (EINVAL);
15015dc1d44SMitchell Horne 
151c02c04f1SMitchell Horne 	fill_dbregs(NULL, d);
152c02c04f1SMitchell Horne 
153c02c04f1SMitchell Horne 	/*
154c02c04f1SMitchell Horne 	 * Check if there are enough available registers to cover the desired
155c02c04f1SMitchell Horne 	 * area.
156c02c04f1SMitchell Horne 	 */
157c02c04f1SMitchell Horne 	avail = 0;
158c02c04f1SMitchell Horne 	for (i = 0; i < NDBREGS; i++) {
159c02c04f1SMitchell Horne 		if (!DBREG_DR7_ENABLED(d->dr[7], i))
160c02c04f1SMitchell Horne 			avail++;
161c02c04f1SMitchell Horne 	}
162c02c04f1SMitchell Horne 
163c02c04f1SMitchell Horne 	if (avail * MAXWATCHSIZE < size)
16415dc1d44SMitchell Horne 		return (EBUSY);
165c02c04f1SMitchell Horne 
166c02c04f1SMitchell Horne 	for (i = 0; i < NDBREGS && size > 0; i++) {
167c02c04f1SMitchell Horne 		if (!DBREG_DR7_ENABLED(d->dr[7], i)) {
168c02c04f1SMitchell Horne 			if ((size >= 8 || (avail == 1 && size > 4)) &&
169c02c04f1SMitchell Horne 			    MAXWATCHSIZE == 8)
170c02c04f1SMitchell Horne 				wsize = 8;
171c02c04f1SMitchell Horne 			else if (size > 2)
172c02c04f1SMitchell Horne 				wsize = 4;
173c02c04f1SMitchell Horne 			else
174c02c04f1SMitchell Horne 				wsize = size;
17515dc1d44SMitchell Horne 			dbreg_set_watchreg(i, addr, wsize, access, d);
176c02c04f1SMitchell Horne 			addr += wsize;
177c02c04f1SMitchell Horne 			size -= wsize;
178c02c04f1SMitchell Horne 			avail--;
179c02c04f1SMitchell Horne 		}
180c02c04f1SMitchell Horne 	}
181c02c04f1SMitchell Horne 
182c02c04f1SMitchell Horne 	set_dbregs(NULL, d);
183c02c04f1SMitchell Horne 	dbreg_sync(d);
184c02c04f1SMitchell Horne 
185c02c04f1SMitchell Horne 	return (0);
186c02c04f1SMitchell Horne }
187c02c04f1SMitchell Horne 
188c02c04f1SMitchell Horne int
dbreg_clr_watchpoint(vm_offset_t addr,vm_size_t size)189c02c04f1SMitchell Horne dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size)
190c02c04f1SMitchell Horne {
191c02c04f1SMitchell Horne 	struct dbreg *d;
192c02c04f1SMitchell Horne 	int i;
193c02c04f1SMitchell Horne 
194c02c04f1SMitchell Horne #ifdef __amd64__
195c02c04f1SMitchell Horne 	d = (struct dbreg *)PCPU_PTR(dbreg);
196c02c04f1SMitchell Horne #else
197c02c04f1SMitchell Horne 	/* debug registers aren't stored in PCPU on i386. */
198c02c04f1SMitchell Horne 	struct dbreg d_temp;
199c02c04f1SMitchell Horne 	d = &d_temp;
200c02c04f1SMitchell Horne #endif
201c02c04f1SMitchell Horne 	fill_dbregs(NULL, d);
202c02c04f1SMitchell Horne 
203c02c04f1SMitchell Horne 	for (i = 0; i < NDBREGS; i++) {
204c02c04f1SMitchell Horne 		if (DBREG_DR7_ENABLED(d->dr[7], i)) {
205c02c04f1SMitchell Horne 			if (DBREG_DRX((d), i) >= addr &&
206c02c04f1SMitchell Horne 			    DBREG_DRX((d), i) < addr + size)
207c02c04f1SMitchell Horne 				dbreg_clr_watchreg(i, d);
208c02c04f1SMitchell Horne 		}
209c02c04f1SMitchell Horne 	}
210c02c04f1SMitchell Horne 
211c02c04f1SMitchell Horne 	set_dbregs(NULL, d);
212c02c04f1SMitchell Horne 	dbreg_sync(d);
213c02c04f1SMitchell Horne 
214c02c04f1SMitchell Horne 	return (0);
215c02c04f1SMitchell Horne }
216c02c04f1SMitchell Horne 
217c02c04f1SMitchell Horne #ifdef DDB
218c02c04f1SMitchell Horne static const char *
watchtype_str(int type)219c02c04f1SMitchell Horne watchtype_str(int type)
220c02c04f1SMitchell Horne {
221c02c04f1SMitchell Horne 
222c02c04f1SMitchell Horne 	switch (type) {
223c02c04f1SMitchell Horne 	case DBREG_DR7_EXEC:
224c02c04f1SMitchell Horne 		return ("execute");
225c02c04f1SMitchell Horne 	case DBREG_DR7_RDWR:
226c02c04f1SMitchell Horne 		return ("read/write");
227c02c04f1SMitchell Horne 	case DBREG_DR7_WRONLY:
228c02c04f1SMitchell Horne 		return ("write");
229c02c04f1SMitchell Horne 	default:
230c02c04f1SMitchell Horne 		return ("invalid");
231c02c04f1SMitchell Horne 	}
232c02c04f1SMitchell Horne }
233c02c04f1SMitchell Horne 
234c02c04f1SMitchell Horne void
dbreg_list_watchpoints(void)235c02c04f1SMitchell Horne dbreg_list_watchpoints(void)
236c02c04f1SMitchell Horne {
237c02c04f1SMitchell Horne 	struct dbreg d;
238c02c04f1SMitchell Horne 	int i, len, type;
239c02c04f1SMitchell Horne 
240c02c04f1SMitchell Horne 	fill_dbregs(NULL, &d);
241c02c04f1SMitchell Horne 
242c02c04f1SMitchell Horne 	db_printf("\nhardware watchpoints:\n");
243c02c04f1SMitchell Horne 	db_printf("  watch    status        type  len     address\n");
244c02c04f1SMitchell Horne 	db_printf("  -----  --------  ----------  ---  ----------\n");
245c02c04f1SMitchell Horne 	for (i = 0; i < NDBREGS; i++) {
246c02c04f1SMitchell Horne 		if (DBREG_DR7_ENABLED(d.dr[7], i)) {
247c02c04f1SMitchell Horne 			type = DBREG_DR7_ACCESS(d.dr[7], i);
248c02c04f1SMitchell Horne 			len = DBREG_DR7_LEN(d.dr[7], i);
249c02c04f1SMitchell Horne 			db_printf("  %-5d  %-8s  %10s  %3d  ",
250c02c04f1SMitchell Horne 			    i, "enabled", watchtype_str(type), len + 1);
251c02c04f1SMitchell Horne 			db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY);
252c02c04f1SMitchell Horne 			db_printf("\n");
253c02c04f1SMitchell Horne 		} else {
254c02c04f1SMitchell Horne 			db_printf("  %-5d  disabled\n", i);
255c02c04f1SMitchell Horne 		}
256c02c04f1SMitchell Horne 	}
257c02c04f1SMitchell Horne }
258c02c04f1SMitchell Horne #endif
259c02c04f1SMitchell Horne 
260c02c04f1SMitchell Horne #ifdef __amd64__
261c02c04f1SMitchell Horne /* Sync debug registers when resuming from debugger. */
262c02c04f1SMitchell Horne void
amd64_db_resume_dbreg(void)263c02c04f1SMitchell Horne amd64_db_resume_dbreg(void)
264c02c04f1SMitchell Horne {
265c02c04f1SMitchell Horne 	struct dbreg *d;
266c02c04f1SMitchell Horne 
267c02c04f1SMitchell Horne 	switch (PCPU_GET(dbreg_cmd)) {
268c02c04f1SMitchell Horne 	case PC_DBREG_CMD_LOAD:
269c02c04f1SMitchell Horne 		d = (struct dbreg *)PCPU_PTR(dbreg);
270c02c04f1SMitchell Horne 		set_dbregs(NULL, d);
271c02c04f1SMitchell Horne 		PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
272c02c04f1SMitchell Horne 		break;
273c02c04f1SMitchell Horne 	}
274c02c04f1SMitchell Horne }
275c02c04f1SMitchell Horne #endif
27615dc1d44SMitchell Horne 
27715dc1d44SMitchell Horne int
kdb_cpu_set_watchpoint(vm_offset_t addr,vm_size_t size,int access)27815dc1d44SMitchell Horne kdb_cpu_set_watchpoint(vm_offset_t addr, vm_size_t size, int access)
27915dc1d44SMitchell Horne {
28015dc1d44SMitchell Horne 
28115dc1d44SMitchell Horne 	/* Convert the KDB access type */
28215dc1d44SMitchell Horne 	switch (access) {
28315dc1d44SMitchell Horne 	case KDB_DBG_ACCESS_W:
28415dc1d44SMitchell Horne 		access = DBREG_DR7_WRONLY;
28515dc1d44SMitchell Horne 		break;
28615dc1d44SMitchell Horne 	case KDB_DBG_ACCESS_RW:
28715dc1d44SMitchell Horne 		access = DBREG_DR7_RDWR;
28815dc1d44SMitchell Horne 		break;
28915dc1d44SMitchell Horne 	case KDB_DBG_ACCESS_R:
29015dc1d44SMitchell Horne 		/* FALLTHROUGH: read-only not supported */
29115dc1d44SMitchell Horne 	default:
29215dc1d44SMitchell Horne 		return (EINVAL);
29315dc1d44SMitchell Horne 	}
29415dc1d44SMitchell Horne 
29515dc1d44SMitchell Horne 	return (dbreg_set_watchpoint(addr, size, access));
29615dc1d44SMitchell Horne }
29715dc1d44SMitchell Horne 
29815dc1d44SMitchell Horne int
kdb_cpu_clr_watchpoint(vm_offset_t addr,vm_size_t size)29915dc1d44SMitchell Horne kdb_cpu_clr_watchpoint(vm_offset_t addr, vm_size_t size)
30015dc1d44SMitchell Horne {
30115dc1d44SMitchell Horne 
30215dc1d44SMitchell Horne 	return (dbreg_clr_watchpoint(addr, size));
30315dc1d44SMitchell Horne }
304