xref: /titanic_50/usr/src/uts/common/os/watchpoint.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/inline.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/proc.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/regset.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/prsystm.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/buf.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/signal.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/user.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate #include <sys/fault.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/procfs.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/stack.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/watchpoint.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/copyops.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/schedctl.h>
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate #include <sys/mman.h>
56*7c478bd9Sstevel@tonic-gate #include <vm/as.h>
57*7c478bd9Sstevel@tonic-gate #include <vm/seg.h>
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate /*
60*7c478bd9Sstevel@tonic-gate  * Copy ops vector for watchpoints.
61*7c478bd9Sstevel@tonic-gate  */
62*7c478bd9Sstevel@tonic-gate static int	watch_copyin(const void *, void *, size_t);
63*7c478bd9Sstevel@tonic-gate static int	watch_xcopyin(const void *, void *, size_t);
64*7c478bd9Sstevel@tonic-gate static int	watch_copyout(const void *, void *, size_t);
65*7c478bd9Sstevel@tonic-gate static int	watch_xcopyout(const void *, void *, size_t);
66*7c478bd9Sstevel@tonic-gate static int	watch_copyinstr(const char *, char *, size_t, size_t *);
67*7c478bd9Sstevel@tonic-gate static int	watch_copyoutstr(const char *, char *, size_t, size_t *);
68*7c478bd9Sstevel@tonic-gate static int	watch_fuword8(const void *, uint8_t *);
69*7c478bd9Sstevel@tonic-gate static int	watch_fuword16(const void *, uint16_t *);
70*7c478bd9Sstevel@tonic-gate static int	watch_fuword32(const void *, uint32_t *);
71*7c478bd9Sstevel@tonic-gate static int	watch_suword8(void *, uint8_t);
72*7c478bd9Sstevel@tonic-gate static int	watch_suword16(void *, uint16_t);
73*7c478bd9Sstevel@tonic-gate static int	watch_suword32(void *, uint32_t);
74*7c478bd9Sstevel@tonic-gate static int	watch_physio(int (*)(struct buf *), struct buf *,
75*7c478bd9Sstevel@tonic-gate     dev_t, int, void (*)(struct buf *), struct uio *);
76*7c478bd9Sstevel@tonic-gate #ifdef _LP64
77*7c478bd9Sstevel@tonic-gate static int	watch_fuword64(const void *, uint64_t *);
78*7c478bd9Sstevel@tonic-gate static int	watch_suword64(void *, uint64_t);
79*7c478bd9Sstevel@tonic-gate #endif
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate struct copyops watch_copyops = {
82*7c478bd9Sstevel@tonic-gate 	watch_copyin,
83*7c478bd9Sstevel@tonic-gate 	watch_xcopyin,
84*7c478bd9Sstevel@tonic-gate 	watch_copyout,
85*7c478bd9Sstevel@tonic-gate 	watch_xcopyout,
86*7c478bd9Sstevel@tonic-gate 	watch_copyinstr,
87*7c478bd9Sstevel@tonic-gate 	watch_copyoutstr,
88*7c478bd9Sstevel@tonic-gate 	watch_fuword8,
89*7c478bd9Sstevel@tonic-gate 	watch_fuword16,
90*7c478bd9Sstevel@tonic-gate 	watch_fuword32,
91*7c478bd9Sstevel@tonic-gate #ifdef _LP64
92*7c478bd9Sstevel@tonic-gate 	watch_fuword64,
93*7c478bd9Sstevel@tonic-gate #else
94*7c478bd9Sstevel@tonic-gate 	NULL,
95*7c478bd9Sstevel@tonic-gate #endif
96*7c478bd9Sstevel@tonic-gate 	watch_suword8,
97*7c478bd9Sstevel@tonic-gate 	watch_suword16,
98*7c478bd9Sstevel@tonic-gate 	watch_suword32,
99*7c478bd9Sstevel@tonic-gate #ifdef _LP64
100*7c478bd9Sstevel@tonic-gate 	watch_suword64,
101*7c478bd9Sstevel@tonic-gate #else
102*7c478bd9Sstevel@tonic-gate 	NULL,
103*7c478bd9Sstevel@tonic-gate #endif
104*7c478bd9Sstevel@tonic-gate 	watch_physio
105*7c478bd9Sstevel@tonic-gate };
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate /*
108*7c478bd9Sstevel@tonic-gate  * Map the 'rw' argument to a protection flag.
109*7c478bd9Sstevel@tonic-gate  */
110*7c478bd9Sstevel@tonic-gate static int
111*7c478bd9Sstevel@tonic-gate rw_to_prot(enum seg_rw rw)
112*7c478bd9Sstevel@tonic-gate {
113*7c478bd9Sstevel@tonic-gate 	switch (rw) {
114*7c478bd9Sstevel@tonic-gate 	case S_EXEC:
115*7c478bd9Sstevel@tonic-gate 		return (PROT_EXEC);
116*7c478bd9Sstevel@tonic-gate 	case S_READ:
117*7c478bd9Sstevel@tonic-gate 		return (PROT_READ);
118*7c478bd9Sstevel@tonic-gate 	case S_WRITE:
119*7c478bd9Sstevel@tonic-gate 		return (PROT_WRITE);
120*7c478bd9Sstevel@tonic-gate 	default:
121*7c478bd9Sstevel@tonic-gate 		return (PROT_NONE);	/* can't happen */
122*7c478bd9Sstevel@tonic-gate 	}
123*7c478bd9Sstevel@tonic-gate }
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate /*
126*7c478bd9Sstevel@tonic-gate  * Map the 'rw' argument to an index into an array of exec/write/read things.
127*7c478bd9Sstevel@tonic-gate  * The index follows the precedence order:  exec .. write .. read
128*7c478bd9Sstevel@tonic-gate  */
129*7c478bd9Sstevel@tonic-gate static int
130*7c478bd9Sstevel@tonic-gate rw_to_index(enum seg_rw rw)
131*7c478bd9Sstevel@tonic-gate {
132*7c478bd9Sstevel@tonic-gate 	switch (rw) {
133*7c478bd9Sstevel@tonic-gate 	default:	/* default case "can't happen" */
134*7c478bd9Sstevel@tonic-gate 	case S_EXEC:
135*7c478bd9Sstevel@tonic-gate 		return (0);
136*7c478bd9Sstevel@tonic-gate 	case S_WRITE:
137*7c478bd9Sstevel@tonic-gate 		return (1);
138*7c478bd9Sstevel@tonic-gate 	case S_READ:
139*7c478bd9Sstevel@tonic-gate 		return (2);
140*7c478bd9Sstevel@tonic-gate 	}
141*7c478bd9Sstevel@tonic-gate }
142*7c478bd9Sstevel@tonic-gate 
143*7c478bd9Sstevel@tonic-gate /*
144*7c478bd9Sstevel@tonic-gate  * Map an index back to a seg_rw.
145*7c478bd9Sstevel@tonic-gate  */
146*7c478bd9Sstevel@tonic-gate static enum seg_rw S_rw[4] = {
147*7c478bd9Sstevel@tonic-gate 	S_EXEC,
148*7c478bd9Sstevel@tonic-gate 	S_WRITE,
149*7c478bd9Sstevel@tonic-gate 	S_READ,
150*7c478bd9Sstevel@tonic-gate 	S_READ,
151*7c478bd9Sstevel@tonic-gate };
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate #define	X	0
154*7c478bd9Sstevel@tonic-gate #define	W	1
155*7c478bd9Sstevel@tonic-gate #define	R	2
156*7c478bd9Sstevel@tonic-gate #define	sum(a)	(a[X] + a[W] + a[R])
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate /*
159*7c478bd9Sstevel@tonic-gate  * Common code for pr_mappage() and pr_unmappage().
160*7c478bd9Sstevel@tonic-gate  */
161*7c478bd9Sstevel@tonic-gate static int
162*7c478bd9Sstevel@tonic-gate pr_do_mappage(caddr_t addr, size_t size, int mapin, enum seg_rw rw, int kernel)
163*7c478bd9Sstevel@tonic-gate {
164*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
165*7c478bd9Sstevel@tonic-gate 	struct as *as = p->p_as;
166*7c478bd9Sstevel@tonic-gate 	char *eaddr = addr + size;
167*7c478bd9Sstevel@tonic-gate 	int prot_rw = rw_to_prot(rw);
168*7c478bd9Sstevel@tonic-gate 	int xrw = rw_to_index(rw);
169*7c478bd9Sstevel@tonic-gate 	int rv = 0;
170*7c478bd9Sstevel@tonic-gate 	struct watched_page *pwp;
171*7c478bd9Sstevel@tonic-gate 	struct watched_page tpw;
172*7c478bd9Sstevel@tonic-gate 	avl_index_t where;
173*7c478bd9Sstevel@tonic-gate 	uint_t prot;
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	ASSERT(as != &kas);
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate startover:
178*7c478bd9Sstevel@tonic-gate 	ASSERT(rv == 0);
179*7c478bd9Sstevel@tonic-gate 	if (avl_numnodes(&as->a_wpage) == 0)
180*7c478bd9Sstevel@tonic-gate 		return (0);
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 	/*
183*7c478bd9Sstevel@tonic-gate 	 * as->a_wpage can only be changed while the process is totally stopped.
184*7c478bd9Sstevel@tonic-gate 	 * Don't grab p_lock here.  Holding p_lock while grabbing the address
185*7c478bd9Sstevel@tonic-gate 	 * space lock leads to deadlocks with the clock thread.  Note that if an
186*7c478bd9Sstevel@tonic-gate 	 * as_fault() is servicing a fault to a watched page on behalf of an
187*7c478bd9Sstevel@tonic-gate 	 * XHAT provider, watchpoint will be temporarily cleared (and wp_prot
188*7c478bd9Sstevel@tonic-gate 	 * will be set to wp_oprot).  Since this is done while holding as writer
189*7c478bd9Sstevel@tonic-gate 	 * lock, we need to grab as lock (reader lock is good enough).
190*7c478bd9Sstevel@tonic-gate 	 *
191*7c478bd9Sstevel@tonic-gate 	 * p_maplock prevents simultaneous execution of this function.  Under
192*7c478bd9Sstevel@tonic-gate 	 * normal circumstances, holdwatch() will stop all other threads, so the
193*7c478bd9Sstevel@tonic-gate 	 * lock isn't really needed.  But there may be multiple threads within
194*7c478bd9Sstevel@tonic-gate 	 * stop() when SWATCHOK is set, so we need to handle multiple threads
195*7c478bd9Sstevel@tonic-gate 	 * at once.  See holdwatch() for the details of this dance.
196*7c478bd9Sstevel@tonic-gate 	 */
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_maplock);
199*7c478bd9Sstevel@tonic-gate 	AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 	tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
202*7c478bd9Sstevel@tonic-gate 	if ((pwp = avl_find(&as->a_wpage, &tpw, &where)) == NULL)
203*7c478bd9Sstevel@tonic-gate 		pwp = avl_nearest(&as->a_wpage, where, AVL_AFTER);
204*7c478bd9Sstevel@tonic-gate 
205*7c478bd9Sstevel@tonic-gate 	for (; pwp != NULL && pwp->wp_vaddr < eaddr;
206*7c478bd9Sstevel@tonic-gate 		pwp = AVL_NEXT(&as->a_wpage, pwp)) {
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 		/*
209*7c478bd9Sstevel@tonic-gate 		 * If the requested protection has not been
210*7c478bd9Sstevel@tonic-gate 		 * removed, we need not remap this page.
211*7c478bd9Sstevel@tonic-gate 		 */
212*7c478bd9Sstevel@tonic-gate 		prot = pwp->wp_prot;
213*7c478bd9Sstevel@tonic-gate 		if (kernel || (prot & PROT_USER))
214*7c478bd9Sstevel@tonic-gate 			if (prot & prot_rw)
215*7c478bd9Sstevel@tonic-gate 				continue;
216*7c478bd9Sstevel@tonic-gate 		/*
217*7c478bd9Sstevel@tonic-gate 		 * If the requested access does not exist in the page's
218*7c478bd9Sstevel@tonic-gate 		 * original protections, we need not remap this page.
219*7c478bd9Sstevel@tonic-gate 		 * If the page does not exist yet, we can't test it.
220*7c478bd9Sstevel@tonic-gate 		 */
221*7c478bd9Sstevel@tonic-gate 		if ((prot = pwp->wp_oprot) != 0) {
222*7c478bd9Sstevel@tonic-gate 			if (!(kernel || (prot & PROT_USER)))
223*7c478bd9Sstevel@tonic-gate 				continue;
224*7c478bd9Sstevel@tonic-gate 			if (!(prot & prot_rw))
225*7c478bd9Sstevel@tonic-gate 				continue;
226*7c478bd9Sstevel@tonic-gate 		}
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 		if (mapin) {
229*7c478bd9Sstevel@tonic-gate 			/*
230*7c478bd9Sstevel@tonic-gate 			 * Before mapping the page in, ensure that
231*7c478bd9Sstevel@tonic-gate 			 * all other lwps are held in the kernel.
232*7c478bd9Sstevel@tonic-gate 			 */
233*7c478bd9Sstevel@tonic-gate 			if (p->p_mapcnt == 0) {
234*7c478bd9Sstevel@tonic-gate 				/*
235*7c478bd9Sstevel@tonic-gate 				 * Release as lock while in holdwatch()
236*7c478bd9Sstevel@tonic-gate 				 * in case other threads need to grab it.
237*7c478bd9Sstevel@tonic-gate 				 */
238*7c478bd9Sstevel@tonic-gate 				AS_LOCK_EXIT(as, &as->a_lock);
239*7c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_maplock);
240*7c478bd9Sstevel@tonic-gate 				if (holdwatch() != 0) {
241*7c478bd9Sstevel@tonic-gate 					/*
242*7c478bd9Sstevel@tonic-gate 					 * We stopped in holdwatch().
243*7c478bd9Sstevel@tonic-gate 					 * Start all over again because the
244*7c478bd9Sstevel@tonic-gate 					 * watched page list may have changed.
245*7c478bd9Sstevel@tonic-gate 					 */
246*7c478bd9Sstevel@tonic-gate 					goto startover;
247*7c478bd9Sstevel@tonic-gate 				}
248*7c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_maplock);
249*7c478bd9Sstevel@tonic-gate 				AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
250*7c478bd9Sstevel@tonic-gate 			}
251*7c478bd9Sstevel@tonic-gate 			p->p_mapcnt++;
252*7c478bd9Sstevel@tonic-gate 		}
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 		addr = pwp->wp_vaddr;
255*7c478bd9Sstevel@tonic-gate 		rv++;
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 		prot = pwp->wp_prot;
258*7c478bd9Sstevel@tonic-gate 		if (mapin) {
259*7c478bd9Sstevel@tonic-gate 			if (kernel)
260*7c478bd9Sstevel@tonic-gate 				pwp->wp_kmap[xrw]++;
261*7c478bd9Sstevel@tonic-gate 			else
262*7c478bd9Sstevel@tonic-gate 				pwp->wp_umap[xrw]++;
263*7c478bd9Sstevel@tonic-gate 			pwp->wp_flags |= WP_NOWATCH;
264*7c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[X] + pwp->wp_umap[X])
265*7c478bd9Sstevel@tonic-gate 				/* cannot have exec-only protection */
266*7c478bd9Sstevel@tonic-gate 				prot |= PROT_READ|PROT_EXEC;
267*7c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[R] + pwp->wp_umap[R])
268*7c478bd9Sstevel@tonic-gate 				prot |= PROT_READ;
269*7c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[W] + pwp->wp_umap[W])
270*7c478bd9Sstevel@tonic-gate 				/* cannot have write-only protection */
271*7c478bd9Sstevel@tonic-gate 				prot |= PROT_READ|PROT_WRITE;
272*7c478bd9Sstevel@tonic-gate #if 0	/* damned broken mmu feature! */
273*7c478bd9Sstevel@tonic-gate 			if (sum(pwp->wp_umap) == 0)
274*7c478bd9Sstevel@tonic-gate 				prot &= ~PROT_USER;
275*7c478bd9Sstevel@tonic-gate #endif
276*7c478bd9Sstevel@tonic-gate 		} else {
277*7c478bd9Sstevel@tonic-gate 			ASSERT(pwp->wp_flags & WP_NOWATCH);
278*7c478bd9Sstevel@tonic-gate 			if (kernel) {
279*7c478bd9Sstevel@tonic-gate 				ASSERT(pwp->wp_kmap[xrw] != 0);
280*7c478bd9Sstevel@tonic-gate 				--pwp->wp_kmap[xrw];
281*7c478bd9Sstevel@tonic-gate 			} else {
282*7c478bd9Sstevel@tonic-gate 				ASSERT(pwp->wp_umap[xrw] != 0);
283*7c478bd9Sstevel@tonic-gate 				--pwp->wp_umap[xrw];
284*7c478bd9Sstevel@tonic-gate 			}
285*7c478bd9Sstevel@tonic-gate 			if (sum(pwp->wp_kmap) + sum(pwp->wp_umap) == 0)
286*7c478bd9Sstevel@tonic-gate 				pwp->wp_flags &= ~WP_NOWATCH;
287*7c478bd9Sstevel@tonic-gate 			else {
288*7c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[X] + pwp->wp_umap[X])
289*7c478bd9Sstevel@tonic-gate 					/* cannot have exec-only protection */
290*7c478bd9Sstevel@tonic-gate 					prot |= PROT_READ|PROT_EXEC;
291*7c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[R] + pwp->wp_umap[R])
292*7c478bd9Sstevel@tonic-gate 					prot |= PROT_READ;
293*7c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[W] + pwp->wp_umap[W])
294*7c478bd9Sstevel@tonic-gate 					/* cannot have write-only protection */
295*7c478bd9Sstevel@tonic-gate 					prot |= PROT_READ|PROT_WRITE;
296*7c478bd9Sstevel@tonic-gate #if 0	/* damned broken mmu feature! */
297*7c478bd9Sstevel@tonic-gate 				if (sum(pwp->wp_umap) == 0)
298*7c478bd9Sstevel@tonic-gate 					prot &= ~PROT_USER;
299*7c478bd9Sstevel@tonic-gate #endif
300*7c478bd9Sstevel@tonic-gate 			}
301*7c478bd9Sstevel@tonic-gate 		}
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 		if (pwp->wp_oprot != 0) {	/* if page exists */
305*7c478bd9Sstevel@tonic-gate 			struct seg *seg;
306*7c478bd9Sstevel@tonic-gate 			uint_t oprot;
307*7c478bd9Sstevel@tonic-gate 			int err, retrycnt = 0;
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 			AS_LOCK_EXIT(as, &as->a_lock);
310*7c478bd9Sstevel@tonic-gate 			AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER);
311*7c478bd9Sstevel@tonic-gate 		retry:
312*7c478bd9Sstevel@tonic-gate 			seg = as_segat(as, addr);
313*7c478bd9Sstevel@tonic-gate 			ASSERT(seg != NULL);
314*7c478bd9Sstevel@tonic-gate 			SEGOP_GETPROT(seg, addr, 0, &oprot);
315*7c478bd9Sstevel@tonic-gate 			if (prot != oprot) {
316*7c478bd9Sstevel@tonic-gate 				err = SEGOP_SETPROT(seg, addr, PAGESIZE, prot);
317*7c478bd9Sstevel@tonic-gate 				if (err == IE_RETRY) {
318*7c478bd9Sstevel@tonic-gate 					ASSERT(retrycnt == 0);
319*7c478bd9Sstevel@tonic-gate 					retrycnt++;
320*7c478bd9Sstevel@tonic-gate 					goto retry;
321*7c478bd9Sstevel@tonic-gate 				}
322*7c478bd9Sstevel@tonic-gate 			}
323*7c478bd9Sstevel@tonic-gate 			AS_LOCK_EXIT(as, &as->a_lock);
324*7c478bd9Sstevel@tonic-gate 		} else
325*7c478bd9Sstevel@tonic-gate 			AS_LOCK_EXIT(as, &as->a_lock);
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		/*
328*7c478bd9Sstevel@tonic-gate 		 * When all pages are mapped back to their normal state,
329*7c478bd9Sstevel@tonic-gate 		 * continue the other lwps.
330*7c478bd9Sstevel@tonic-gate 		 */
331*7c478bd9Sstevel@tonic-gate 		if (!mapin) {
332*7c478bd9Sstevel@tonic-gate 			ASSERT(p->p_mapcnt > 0);
333*7c478bd9Sstevel@tonic-gate 			p->p_mapcnt--;
334*7c478bd9Sstevel@tonic-gate 			if (p->p_mapcnt == 0) {
335*7c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_maplock);
336*7c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_lock);
337*7c478bd9Sstevel@tonic-gate 				continuelwps(p);
338*7c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
339*7c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_maplock);
340*7c478bd9Sstevel@tonic-gate 			}
341*7c478bd9Sstevel@tonic-gate 		}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 		AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
344*7c478bd9Sstevel@tonic-gate 	}
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate 	AS_LOCK_EXIT(as, &as->a_lock);
347*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_maplock);
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	return (rv);
350*7c478bd9Sstevel@tonic-gate }
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate /*
353*7c478bd9Sstevel@tonic-gate  * Restore the original page protections on an address range.
354*7c478bd9Sstevel@tonic-gate  * If 'kernel' is non-zero, just do it for the kernel.
355*7c478bd9Sstevel@tonic-gate  * pr_mappage() returns non-zero if it actually changed anything.
356*7c478bd9Sstevel@tonic-gate  *
357*7c478bd9Sstevel@tonic-gate  * pr_mappage() and pr_unmappage() must be executed in matched pairs,
358*7c478bd9Sstevel@tonic-gate  * but pairs may be nested within other pairs.  The reference counts
359*7c478bd9Sstevel@tonic-gate  * sort it all out.  See pr_do_mappage(), above.
360*7c478bd9Sstevel@tonic-gate  */
361*7c478bd9Sstevel@tonic-gate static int
362*7c478bd9Sstevel@tonic-gate pr_mappage(const caddr_t addr, size_t size, enum seg_rw rw, int kernel)
363*7c478bd9Sstevel@tonic-gate {
364*7c478bd9Sstevel@tonic-gate 	return (pr_do_mappage(addr, size, 1, rw, kernel));
365*7c478bd9Sstevel@tonic-gate }
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate /*
368*7c478bd9Sstevel@tonic-gate  * Set the modified page protections on a watched page.
369*7c478bd9Sstevel@tonic-gate  * Inverse of pr_mappage().
370*7c478bd9Sstevel@tonic-gate  * Needs to be called only if pr_mappage() returned non-zero.
371*7c478bd9Sstevel@tonic-gate  */
372*7c478bd9Sstevel@tonic-gate static void
373*7c478bd9Sstevel@tonic-gate pr_unmappage(const caddr_t addr, size_t size, enum seg_rw rw, int kernel)
374*7c478bd9Sstevel@tonic-gate {
375*7c478bd9Sstevel@tonic-gate 	(void) pr_do_mappage(addr, size, 0, rw, kernel);
376*7c478bd9Sstevel@tonic-gate }
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate /*
379*7c478bd9Sstevel@tonic-gate  * Function called by an lwp after it resumes from stop().
380*7c478bd9Sstevel@tonic-gate  */
381*7c478bd9Sstevel@tonic-gate void
382*7c478bd9Sstevel@tonic-gate setallwatch(void)
383*7c478bd9Sstevel@tonic-gate {
384*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
385*7c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
386*7c478bd9Sstevel@tonic-gate 	struct watched_page *pwp, *next;
387*7c478bd9Sstevel@tonic-gate 	struct seg *seg;
388*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
389*7c478bd9Sstevel@tonic-gate 	uint_t prot;
390*7c478bd9Sstevel@tonic-gate 	int err, retrycnt;
391*7c478bd9Sstevel@tonic-gate 
392*7c478bd9Sstevel@tonic-gate 	if (p->p_wprot == NULL)
393*7c478bd9Sstevel@tonic-gate 		return;
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate 	AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER);
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	pwp = p->p_wprot;
400*7c478bd9Sstevel@tonic-gate 	while (pwp != NULL) {
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 		vaddr = pwp->wp_vaddr;
403*7c478bd9Sstevel@tonic-gate 		retrycnt = 0;
404*7c478bd9Sstevel@tonic-gate 	retry:
405*7c478bd9Sstevel@tonic-gate 		ASSERT(pwp->wp_flags & WP_SETPROT);
406*7c478bd9Sstevel@tonic-gate 		if ((seg = as_segat(as, vaddr)) != NULL &&
407*7c478bd9Sstevel@tonic-gate 		    !(pwp->wp_flags & WP_NOWATCH)) {
408*7c478bd9Sstevel@tonic-gate 			prot = pwp->wp_prot;
409*7c478bd9Sstevel@tonic-gate 			err = SEGOP_SETPROT(seg, vaddr, PAGESIZE, prot);
410*7c478bd9Sstevel@tonic-gate 			if (err == IE_RETRY) {
411*7c478bd9Sstevel@tonic-gate 				ASSERT(retrycnt == 0);
412*7c478bd9Sstevel@tonic-gate 				retrycnt++;
413*7c478bd9Sstevel@tonic-gate 				goto retry;
414*7c478bd9Sstevel@tonic-gate 			}
415*7c478bd9Sstevel@tonic-gate 		}
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 		next = pwp->wp_list;
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 		if (pwp->wp_read + pwp->wp_write + pwp->wp_exec == 0) {
420*7c478bd9Sstevel@tonic-gate 			/*
421*7c478bd9Sstevel@tonic-gate 			 * No watched areas remain in this page.
422*7c478bd9Sstevel@tonic-gate 			 * Free the watched_page structure.
423*7c478bd9Sstevel@tonic-gate 			 */
424*7c478bd9Sstevel@tonic-gate 			avl_remove(&as->a_wpage, pwp);
425*7c478bd9Sstevel@tonic-gate 			kmem_free(pwp, sizeof (struct watched_page));
426*7c478bd9Sstevel@tonic-gate 		} else {
427*7c478bd9Sstevel@tonic-gate 			pwp->wp_flags &= ~WP_SETPROT;
428*7c478bd9Sstevel@tonic-gate 		}
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 		pwp = next;
431*7c478bd9Sstevel@tonic-gate 	}
432*7c478bd9Sstevel@tonic-gate 	p->p_wprot = NULL;
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate 	AS_LOCK_EXIT(as, &as->a_lock);
435*7c478bd9Sstevel@tonic-gate }
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate /* Must be called with as lock held */
440*7c478bd9Sstevel@tonic-gate int
441*7c478bd9Sstevel@tonic-gate pr_is_watchpage_as(caddr_t addr, enum seg_rw rw, struct as *as)
442*7c478bd9Sstevel@tonic-gate {
443*7c478bd9Sstevel@tonic-gate 	register struct watched_page *pwp;
444*7c478bd9Sstevel@tonic-gate 	struct watched_page tpw;
445*7c478bd9Sstevel@tonic-gate 	uint_t prot;
446*7c478bd9Sstevel@tonic-gate 	int rv = 0;
447*7c478bd9Sstevel@tonic-gate 
448*7c478bd9Sstevel@tonic-gate 	switch (rw) {
449*7c478bd9Sstevel@tonic-gate 	case S_READ:
450*7c478bd9Sstevel@tonic-gate 	case S_WRITE:
451*7c478bd9Sstevel@tonic-gate 	case S_EXEC:
452*7c478bd9Sstevel@tonic-gate 		break;
453*7c478bd9Sstevel@tonic-gate 	default:
454*7c478bd9Sstevel@tonic-gate 		return (0);
455*7c478bd9Sstevel@tonic-gate 	}
456*7c478bd9Sstevel@tonic-gate 
457*7c478bd9Sstevel@tonic-gate 	/*
458*7c478bd9Sstevel@tonic-gate 	 * as->a_wpage can only be modified while the process is totally
459*7c478bd9Sstevel@tonic-gate 	 * stopped.  We need, and should use, no locks here.
460*7c478bd9Sstevel@tonic-gate 	 */
461*7c478bd9Sstevel@tonic-gate 	if (as != &kas && avl_numnodes(&as->a_wpage) != 0) {
462*7c478bd9Sstevel@tonic-gate 		tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
463*7c478bd9Sstevel@tonic-gate 		pwp = avl_find(&as->a_wpage, &tpw, NULL);
464*7c478bd9Sstevel@tonic-gate 		if (pwp != NULL) {
465*7c478bd9Sstevel@tonic-gate 			ASSERT(addr >= pwp->wp_vaddr &&
466*7c478bd9Sstevel@tonic-gate 			    addr < pwp->wp_vaddr + PAGESIZE);
467*7c478bd9Sstevel@tonic-gate 			if (pwp->wp_oprot != 0) {
468*7c478bd9Sstevel@tonic-gate 				prot = pwp->wp_prot;
469*7c478bd9Sstevel@tonic-gate 				switch (rw) {
470*7c478bd9Sstevel@tonic-gate 				case S_READ:
471*7c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_READ))
472*7c478bd9Sstevel@tonic-gate 						!= (PROT_USER|PROT_READ));
473*7c478bd9Sstevel@tonic-gate 					break;
474*7c478bd9Sstevel@tonic-gate 				case S_WRITE:
475*7c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_WRITE))
476*7c478bd9Sstevel@tonic-gate 						!= (PROT_USER|PROT_WRITE));
477*7c478bd9Sstevel@tonic-gate 					break;
478*7c478bd9Sstevel@tonic-gate 				case S_EXEC:
479*7c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_EXEC))
480*7c478bd9Sstevel@tonic-gate 						!= (PROT_USER|PROT_EXEC));
481*7c478bd9Sstevel@tonic-gate 					break;
482*7c478bd9Sstevel@tonic-gate 				default:
483*7c478bd9Sstevel@tonic-gate 					/* can't happen! */
484*7c478bd9Sstevel@tonic-gate 					break;
485*7c478bd9Sstevel@tonic-gate 				}
486*7c478bd9Sstevel@tonic-gate 			}
487*7c478bd9Sstevel@tonic-gate 		}
488*7c478bd9Sstevel@tonic-gate 	}
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	return (rv);
491*7c478bd9Sstevel@tonic-gate }
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate /*
495*7c478bd9Sstevel@tonic-gate  * trap() calls here to determine if a fault is in a watched page.
496*7c478bd9Sstevel@tonic-gate  * We return nonzero if this is true and the load/store would fail.
497*7c478bd9Sstevel@tonic-gate  */
498*7c478bd9Sstevel@tonic-gate int
499*7c478bd9Sstevel@tonic-gate pr_is_watchpage(caddr_t addr, enum seg_rw rw)
500*7c478bd9Sstevel@tonic-gate {
501*7c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
502*7c478bd9Sstevel@tonic-gate 	int rv;
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate 	if ((as == &kas) || avl_numnodes(&as->a_wpage) == 0)
505*7c478bd9Sstevel@tonic-gate 		return (0);
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate 	/* Grab the lock because of XHAT (see comment in pr_mappage()) */
508*7c478bd9Sstevel@tonic-gate 	AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
509*7c478bd9Sstevel@tonic-gate 	rv = pr_is_watchpage_as(addr, rw, as);
510*7c478bd9Sstevel@tonic-gate 	AS_LOCK_EXIT(as, &as->a_lock);
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 	return (rv);
513*7c478bd9Sstevel@tonic-gate }
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate /*
518*7c478bd9Sstevel@tonic-gate  * trap() calls here to determine if a fault is a watchpoint.
519*7c478bd9Sstevel@tonic-gate  */
520*7c478bd9Sstevel@tonic-gate int
521*7c478bd9Sstevel@tonic-gate pr_is_watchpoint(caddr_t *paddr, int *pta, size_t size, size_t *plen,
522*7c478bd9Sstevel@tonic-gate 	enum seg_rw rw)
523*7c478bd9Sstevel@tonic-gate {
524*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
525*7c478bd9Sstevel@tonic-gate 	caddr_t addr = *paddr;
526*7c478bd9Sstevel@tonic-gate 	caddr_t eaddr = addr + size;
527*7c478bd9Sstevel@tonic-gate 	register struct watched_area *pwa;
528*7c478bd9Sstevel@tonic-gate 	struct watched_area twa;
529*7c478bd9Sstevel@tonic-gate 	int rv = 0;
530*7c478bd9Sstevel@tonic-gate 	int ta = 0;
531*7c478bd9Sstevel@tonic-gate 	size_t len = 0;
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 	switch (rw) {
534*7c478bd9Sstevel@tonic-gate 	case S_READ:
535*7c478bd9Sstevel@tonic-gate 	case S_WRITE:
536*7c478bd9Sstevel@tonic-gate 	case S_EXEC:
537*7c478bd9Sstevel@tonic-gate 		break;
538*7c478bd9Sstevel@tonic-gate 	default:
539*7c478bd9Sstevel@tonic-gate 		*pta = 0;
540*7c478bd9Sstevel@tonic-gate 		return (0);
541*7c478bd9Sstevel@tonic-gate 	}
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 	/*
544*7c478bd9Sstevel@tonic-gate 	 * p->p_warea is protected by p->p_lock.
545*7c478bd9Sstevel@tonic-gate 	 */
546*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
547*7c478bd9Sstevel@tonic-gate 
548*7c478bd9Sstevel@tonic-gate 	/* BEGIN CSTYLED */
549*7c478bd9Sstevel@tonic-gate 	/*
550*7c478bd9Sstevel@tonic-gate 	 * This loop is somewhat complicated because the fault region can span
551*7c478bd9Sstevel@tonic-gate 	 * multiple watched areas.  For example:
552*7c478bd9Sstevel@tonic-gate 	 *
553*7c478bd9Sstevel@tonic-gate 	 *            addr              eaddr
554*7c478bd9Sstevel@tonic-gate 	 * 		+-----------------+
555*7c478bd9Sstevel@tonic-gate 	 * 		| fault region    |
556*7c478bd9Sstevel@tonic-gate 	 * 	+-------+--------+----+---+------------+
557*7c478bd9Sstevel@tonic-gate 	 *      | prot not right |    | prot correct   |
558*7c478bd9Sstevel@tonic-gate 	 *      +----------------+    +----------------+
559*7c478bd9Sstevel@tonic-gate 	 *    wa_vaddr	      wa_eaddr
560*7c478bd9Sstevel@tonic-gate 	 *    		      wa_vaddr		wa_eaddr
561*7c478bd9Sstevel@tonic-gate 	 *
562*7c478bd9Sstevel@tonic-gate 	 * We start at the area greater than or equal to the starting address.
563*7c478bd9Sstevel@tonic-gate 	 * As long as some portion of the fault region overlaps the current
564*7c478bd9Sstevel@tonic-gate 	 * area, we continue checking permissions until we find an appropriate
565*7c478bd9Sstevel@tonic-gate 	 * match.
566*7c478bd9Sstevel@tonic-gate 	 */
567*7c478bd9Sstevel@tonic-gate 	/* END CSTYLED */
568*7c478bd9Sstevel@tonic-gate 	twa.wa_vaddr = addr;
569*7c478bd9Sstevel@tonic-gate 	twa.wa_eaddr = eaddr;
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate 	for (pwa = pr_find_watched_area(p, &twa, NULL);
572*7c478bd9Sstevel@tonic-gate 	    pwa != NULL && eaddr > pwa->wa_vaddr && addr < pwa->wa_eaddr;
573*7c478bd9Sstevel@tonic-gate 	    pwa = AVL_NEXT(&p->p_warea, pwa)) {
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 		switch (rw) {
576*7c478bd9Sstevel@tonic-gate 		case S_READ:
577*7c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_READ)
578*7c478bd9Sstevel@tonic-gate 				rv = TRAP_RWATCH;
579*7c478bd9Sstevel@tonic-gate 			break;
580*7c478bd9Sstevel@tonic-gate 		case S_WRITE:
581*7c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_WRITE)
582*7c478bd9Sstevel@tonic-gate 				rv = TRAP_WWATCH;
583*7c478bd9Sstevel@tonic-gate 			break;
584*7c478bd9Sstevel@tonic-gate 		case S_EXEC:
585*7c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_EXEC)
586*7c478bd9Sstevel@tonic-gate 				rv = TRAP_XWATCH;
587*7c478bd9Sstevel@tonic-gate 			break;
588*7c478bd9Sstevel@tonic-gate 		default:
589*7c478bd9Sstevel@tonic-gate 			/* can't happen */
590*7c478bd9Sstevel@tonic-gate 			break;
591*7c478bd9Sstevel@tonic-gate 		}
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 		/*
594*7c478bd9Sstevel@tonic-gate 		 * If protections didn't match, check the next watched
595*7c478bd9Sstevel@tonic-gate 		 * area
596*7c478bd9Sstevel@tonic-gate 		 */
597*7c478bd9Sstevel@tonic-gate 		if (rv != 0) {
598*7c478bd9Sstevel@tonic-gate 			if (addr < pwa->wa_vaddr)
599*7c478bd9Sstevel@tonic-gate 				addr = pwa->wa_vaddr;
600*7c478bd9Sstevel@tonic-gate 			len = pwa->wa_eaddr - addr;
601*7c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_TRAPAFTER)
602*7c478bd9Sstevel@tonic-gate 				ta = 1;
603*7c478bd9Sstevel@tonic-gate 			break;
604*7c478bd9Sstevel@tonic-gate 		}
605*7c478bd9Sstevel@tonic-gate 	}
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate 	*paddr = addr;
610*7c478bd9Sstevel@tonic-gate 	*pta = ta;
611*7c478bd9Sstevel@tonic-gate 	if (plen != NULL)
612*7c478bd9Sstevel@tonic-gate 		*plen = len;
613*7c478bd9Sstevel@tonic-gate 	return (rv);
614*7c478bd9Sstevel@tonic-gate }
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate /*
617*7c478bd9Sstevel@tonic-gate  * Set up to perform a single-step at user level for the
618*7c478bd9Sstevel@tonic-gate  * case of a trapafter watchpoint.  Called from trap().
619*7c478bd9Sstevel@tonic-gate  */
620*7c478bd9Sstevel@tonic-gate void
621*7c478bd9Sstevel@tonic-gate do_watch_step(caddr_t vaddr, size_t sz, enum seg_rw rw,
622*7c478bd9Sstevel@tonic-gate 	int watchcode, greg_t pc)
623*7c478bd9Sstevel@tonic-gate {
624*7c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
625*7c478bd9Sstevel@tonic-gate 	struct lwp_watch *pw = &lwp->lwp_watch[rw_to_index(rw)];
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	/*
628*7c478bd9Sstevel@tonic-gate 	 * Check to see if we are already performing this special
629*7c478bd9Sstevel@tonic-gate 	 * watchpoint single-step.  We must not do pr_mappage() twice.
630*7c478bd9Sstevel@tonic-gate 	 */
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 	/* special check for two read traps on the same instruction */
633*7c478bd9Sstevel@tonic-gate 	if (rw == S_READ && pw->wpaddr != NULL &&
634*7c478bd9Sstevel@tonic-gate 	    !(pw->wpaddr <= vaddr && vaddr < pw->wpaddr + pw->wpsize)) {
635*7c478bd9Sstevel@tonic-gate 		ASSERT(lwp->lwp_watchtrap != 0);
636*7c478bd9Sstevel@tonic-gate 		pw++;	/* use the extra S_READ struct */
637*7c478bd9Sstevel@tonic-gate 	}
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	if (pw->wpaddr != NULL) {
640*7c478bd9Sstevel@tonic-gate 		ASSERT(lwp->lwp_watchtrap != 0);
641*7c478bd9Sstevel@tonic-gate 		ASSERT(pw->wpaddr <= vaddr && vaddr < pw->wpaddr + pw->wpsize);
642*7c478bd9Sstevel@tonic-gate 		if (pw->wpcode == 0) {
643*7c478bd9Sstevel@tonic-gate 			pw->wpcode = watchcode;
644*7c478bd9Sstevel@tonic-gate 			pw->wppc = pc;
645*7c478bd9Sstevel@tonic-gate 		}
646*7c478bd9Sstevel@tonic-gate 	} else {
647*7c478bd9Sstevel@tonic-gate 		int mapped = pr_mappage(vaddr, sz, rw, 0);
648*7c478bd9Sstevel@tonic-gate 		prstep(lwp, 1);
649*7c478bd9Sstevel@tonic-gate 		lwp->lwp_watchtrap = 1;
650*7c478bd9Sstevel@tonic-gate 		pw->wpaddr = vaddr;
651*7c478bd9Sstevel@tonic-gate 		pw->wpsize = sz;
652*7c478bd9Sstevel@tonic-gate 		pw->wpcode = watchcode;
653*7c478bd9Sstevel@tonic-gate 		pw->wpmapped = mapped;
654*7c478bd9Sstevel@tonic-gate 		pw->wppc = pc;
655*7c478bd9Sstevel@tonic-gate 	}
656*7c478bd9Sstevel@tonic-gate }
657*7c478bd9Sstevel@tonic-gate 
658*7c478bd9Sstevel@tonic-gate /*
659*7c478bd9Sstevel@tonic-gate  * Undo the effects of do_watch_step().
660*7c478bd9Sstevel@tonic-gate  * Called from trap() after the single-step is finished.
661*7c478bd9Sstevel@tonic-gate  * Also called from issig_forreal() and stop() with a NULL
662*7c478bd9Sstevel@tonic-gate  * argument to avoid having these things set more than once.
663*7c478bd9Sstevel@tonic-gate  */
664*7c478bd9Sstevel@tonic-gate int
665*7c478bd9Sstevel@tonic-gate undo_watch_step(k_siginfo_t *sip)
666*7c478bd9Sstevel@tonic-gate {
667*7c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
668*7c478bd9Sstevel@tonic-gate 	int fault = 0;
669*7c478bd9Sstevel@tonic-gate 
670*7c478bd9Sstevel@tonic-gate 	if (lwp->lwp_watchtrap) {
671*7c478bd9Sstevel@tonic-gate 		struct lwp_watch *pw = lwp->lwp_watch;
672*7c478bd9Sstevel@tonic-gate 		int i;
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < 4; i++, pw++) {
675*7c478bd9Sstevel@tonic-gate 			if (pw->wpaddr == NULL)
676*7c478bd9Sstevel@tonic-gate 				continue;
677*7c478bd9Sstevel@tonic-gate 			if (pw->wpmapped)
678*7c478bd9Sstevel@tonic-gate 				pr_unmappage(pw->wpaddr, pw->wpsize, S_rw[i],
679*7c478bd9Sstevel@tonic-gate 				    0);
680*7c478bd9Sstevel@tonic-gate 			if (pw->wpcode != 0) {
681*7c478bd9Sstevel@tonic-gate 				if (sip != NULL) {
682*7c478bd9Sstevel@tonic-gate 					sip->si_signo = SIGTRAP;
683*7c478bd9Sstevel@tonic-gate 					sip->si_code = pw->wpcode;
684*7c478bd9Sstevel@tonic-gate 					sip->si_addr = pw->wpaddr;
685*7c478bd9Sstevel@tonic-gate 					sip->si_trapafter = 1;
686*7c478bd9Sstevel@tonic-gate 					sip->si_pc = (caddr_t)pw->wppc;
687*7c478bd9Sstevel@tonic-gate 				}
688*7c478bd9Sstevel@tonic-gate 				fault = FLTWATCH;
689*7c478bd9Sstevel@tonic-gate 				pw->wpcode = 0;
690*7c478bd9Sstevel@tonic-gate 			}
691*7c478bd9Sstevel@tonic-gate 			pw->wpaddr = NULL;
692*7c478bd9Sstevel@tonic-gate 			pw->wpsize = 0;
693*7c478bd9Sstevel@tonic-gate 			pw->wpmapped = 0;
694*7c478bd9Sstevel@tonic-gate 		}
695*7c478bd9Sstevel@tonic-gate 		lwp->lwp_watchtrap = 0;
696*7c478bd9Sstevel@tonic-gate 	}
697*7c478bd9Sstevel@tonic-gate 
698*7c478bd9Sstevel@tonic-gate 	return (fault);
699*7c478bd9Sstevel@tonic-gate }
700*7c478bd9Sstevel@tonic-gate 
701*7c478bd9Sstevel@tonic-gate /*
702*7c478bd9Sstevel@tonic-gate  * Handle a watchpoint that occurs while doing copyin()
703*7c478bd9Sstevel@tonic-gate  * or copyout() in a system call.
704*7c478bd9Sstevel@tonic-gate  * Return non-zero if the fault or signal is cleared
705*7c478bd9Sstevel@tonic-gate  * by a debugger while the lwp is stopped.
706*7c478bd9Sstevel@tonic-gate  */
707*7c478bd9Sstevel@tonic-gate static int
708*7c478bd9Sstevel@tonic-gate sys_watchpoint(caddr_t addr, int watchcode, int ta)
709*7c478bd9Sstevel@tonic-gate {
710*7c478bd9Sstevel@tonic-gate 	extern greg_t getuserpc(void);	/* XXX header file */
711*7c478bd9Sstevel@tonic-gate 	k_sigset_t smask;
712*7c478bd9Sstevel@tonic-gate 	register proc_t *p = ttoproc(curthread);
713*7c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
714*7c478bd9Sstevel@tonic-gate 	register sigqueue_t *sqp;
715*7c478bd9Sstevel@tonic-gate 	int rval;
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 	/* assert no locks are held */
718*7c478bd9Sstevel@tonic-gate 	/* ASSERT(curthread->t_nlocks == 0); */
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate 	sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
721*7c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_signo = SIGTRAP;
722*7c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_code = watchcode;
723*7c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_addr = addr;
724*7c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_trapafter = ta;
725*7c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_pc = (caddr_t)getuserpc();
726*7c478bd9Sstevel@tonic-gate 
727*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
728*7c478bd9Sstevel@tonic-gate 
729*7c478bd9Sstevel@tonic-gate 	/* this will be tested and cleared by the caller */
730*7c478bd9Sstevel@tonic-gate 	lwp->lwp_sysabort = 0;
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 	if (prismember(&p->p_fltmask, FLTWATCH)) {
733*7c478bd9Sstevel@tonic-gate 		lwp->lwp_curflt = (uchar_t)FLTWATCH;
734*7c478bd9Sstevel@tonic-gate 		lwp->lwp_siginfo = sqp->sq_info;
735*7c478bd9Sstevel@tonic-gate 		stop(PR_FAULTED, FLTWATCH);
736*7c478bd9Sstevel@tonic-gate 		if (lwp->lwp_curflt == 0) {
737*7c478bd9Sstevel@tonic-gate 			mutex_exit(&p->p_lock);
738*7c478bd9Sstevel@tonic-gate 			kmem_free(sqp, sizeof (sigqueue_t));
739*7c478bd9Sstevel@tonic-gate 			return (1);
740*7c478bd9Sstevel@tonic-gate 		}
741*7c478bd9Sstevel@tonic-gate 		lwp->lwp_curflt = 0;
742*7c478bd9Sstevel@tonic-gate 	}
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate 	/*
745*7c478bd9Sstevel@tonic-gate 	 * post the SIGTRAP signal.
746*7c478bd9Sstevel@tonic-gate 	 * Block all other signals so we only stop showing SIGTRAP.
747*7c478bd9Sstevel@tonic-gate 	 */
748*7c478bd9Sstevel@tonic-gate 	if (signal_is_blocked(curthread, SIGTRAP) ||
749*7c478bd9Sstevel@tonic-gate 	    sigismember(&p->p_ignore, SIGTRAP)) {
750*7c478bd9Sstevel@tonic-gate 		/* SIGTRAP is blocked or ignored, forget the rest. */
751*7c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
752*7c478bd9Sstevel@tonic-gate 		kmem_free(sqp, sizeof (sigqueue_t));
753*7c478bd9Sstevel@tonic-gate 		return (0);
754*7c478bd9Sstevel@tonic-gate 	}
755*7c478bd9Sstevel@tonic-gate 	sigdelq(p, curthread, SIGTRAP);
756*7c478bd9Sstevel@tonic-gate 	sigaddqa(p, curthread, sqp);
757*7c478bd9Sstevel@tonic-gate 	schedctl_finish_sigblock(curthread);
758*7c478bd9Sstevel@tonic-gate 	smask = curthread->t_hold;
759*7c478bd9Sstevel@tonic-gate 	sigfillset(&curthread->t_hold);
760*7c478bd9Sstevel@tonic-gate 	sigdiffset(&curthread->t_hold, &cantmask);
761*7c478bd9Sstevel@tonic-gate 	sigdelset(&curthread->t_hold, SIGTRAP);
762*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
763*7c478bd9Sstevel@tonic-gate 
764*7c478bd9Sstevel@tonic-gate 	rval = ((ISSIG_FAST(curthread, lwp, p, FORREAL))? 0 : 1);
765*7c478bd9Sstevel@tonic-gate 
766*7c478bd9Sstevel@tonic-gate 	/* restore the original signal mask */
767*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
768*7c478bd9Sstevel@tonic-gate 	curthread->t_hold = smask;
769*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
770*7c478bd9Sstevel@tonic-gate 
771*7c478bd9Sstevel@tonic-gate 	return (rval);
772*7c478bd9Sstevel@tonic-gate }
773*7c478bd9Sstevel@tonic-gate 
774*7c478bd9Sstevel@tonic-gate /*
775*7c478bd9Sstevel@tonic-gate  * Wrappers for the copyin()/copyout() functions to deal
776*7c478bd9Sstevel@tonic-gate  * with watchpoints that fire while in system calls.
777*7c478bd9Sstevel@tonic-gate  */
778*7c478bd9Sstevel@tonic-gate 
779*7c478bd9Sstevel@tonic-gate static int
780*7c478bd9Sstevel@tonic-gate watch_xcopyin(const void *uaddr, void *kaddr, size_t count)
781*7c478bd9Sstevel@tonic-gate {
782*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
783*7c478bd9Sstevel@tonic-gate 	caddr_t watch_uaddr = (caddr_t)uaddr;
784*7c478bd9Sstevel@tonic-gate 	caddr_t watch_kaddr = (caddr_t)kaddr;
785*7c478bd9Sstevel@tonic-gate 	int error = 0;
786*7c478bd9Sstevel@tonic-gate 	label_t ljb;
787*7c478bd9Sstevel@tonic-gate 	size_t part;
788*7c478bd9Sstevel@tonic-gate 	int mapped;
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate 	while (count && error == 0) {
791*7c478bd9Sstevel@tonic-gate 		int watchcode;
792*7c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
793*7c478bd9Sstevel@tonic-gate 		size_t len;
794*7c478bd9Sstevel@tonic-gate 		int ta;
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
797*7c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > count)
798*7c478bd9Sstevel@tonic-gate 			part = count;
799*7c478bd9Sstevel@tonic-gate 
800*7c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(watch_uaddr, S_READ))
801*7c478bd9Sstevel@tonic-gate 			watchcode = 0;
802*7c478bd9Sstevel@tonic-gate 		else {
803*7c478bd9Sstevel@tonic-gate 			vaddr = watch_uaddr;
804*7c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
805*7c478bd9Sstevel@tonic-gate 			    part, &len, S_READ);
806*7c478bd9Sstevel@tonic-gate 			if (watchcode && ta == 0)
807*7c478bd9Sstevel@tonic-gate 				part = vaddr - watch_uaddr;
808*7c478bd9Sstevel@tonic-gate 		}
809*7c478bd9Sstevel@tonic-gate 
810*7c478bd9Sstevel@tonic-gate 		/*
811*7c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
812*7c478bd9Sstevel@tonic-gate 		 */
813*7c478bd9Sstevel@tonic-gate 		if (part != 0) {
814*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_READ, 1);
815*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
816*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
817*7c478bd9Sstevel@tonic-gate 			else
818*7c478bd9Sstevel@tonic-gate 				copyin_noerr(watch_uaddr, watch_kaddr, part);
819*7c478bd9Sstevel@tonic-gate 			no_fault();
820*7c478bd9Sstevel@tonic-gate 			if (mapped)
821*7c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_READ, 1);
822*7c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
823*7c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
824*7c478bd9Sstevel@tonic-gate 			count -= part;
825*7c478bd9Sstevel@tonic-gate 		}
826*7c478bd9Sstevel@tonic-gate 		/*
827*7c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
828*7c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
829*7c478bd9Sstevel@tonic-gate 		 */
830*7c478bd9Sstevel@tonic-gate 		while (count && watchcode && ta && len > part && error == 0) {
831*7c478bd9Sstevel@tonic-gate 			len -= part;
832*7c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > count)
833*7c478bd9Sstevel@tonic-gate 				part = count;
834*7c478bd9Sstevel@tonic-gate 			if (part > len)
835*7c478bd9Sstevel@tonic-gate 				part = len;
836*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_READ, 1);
837*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
838*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
839*7c478bd9Sstevel@tonic-gate 			else
840*7c478bd9Sstevel@tonic-gate 				copyin_noerr(watch_uaddr, watch_kaddr, part);
841*7c478bd9Sstevel@tonic-gate 			no_fault();
842*7c478bd9Sstevel@tonic-gate 			if (mapped)
843*7c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_READ, 1);
844*7c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
845*7c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
846*7c478bd9Sstevel@tonic-gate 			count -= part;
847*7c478bd9Sstevel@tonic-gate 		}
848*7c478bd9Sstevel@tonic-gate 
849*7c478bd9Sstevel@tonic-gate error:
850*7c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
851*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
852*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
853*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
854*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
855*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
856*7c478bd9Sstevel@tonic-gate 			break;
857*7c478bd9Sstevel@tonic-gate 		}
858*7c478bd9Sstevel@tonic-gate 	}
859*7c478bd9Sstevel@tonic-gate 
860*7c478bd9Sstevel@tonic-gate 	return (error);
861*7c478bd9Sstevel@tonic-gate }
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate static int
864*7c478bd9Sstevel@tonic-gate watch_copyin(const void *kaddr, void *uaddr, size_t count)
865*7c478bd9Sstevel@tonic-gate {
866*7c478bd9Sstevel@tonic-gate 	return (watch_xcopyin(kaddr, uaddr, count) ? -1 : 0);
867*7c478bd9Sstevel@tonic-gate }
868*7c478bd9Sstevel@tonic-gate 
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate static int
871*7c478bd9Sstevel@tonic-gate watch_xcopyout(const void *kaddr, void *uaddr, size_t count)
872*7c478bd9Sstevel@tonic-gate {
873*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
874*7c478bd9Sstevel@tonic-gate 	caddr_t watch_uaddr = (caddr_t)uaddr;
875*7c478bd9Sstevel@tonic-gate 	caddr_t watch_kaddr = (caddr_t)kaddr;
876*7c478bd9Sstevel@tonic-gate 	int error = 0;
877*7c478bd9Sstevel@tonic-gate 	label_t ljb;
878*7c478bd9Sstevel@tonic-gate 
879*7c478bd9Sstevel@tonic-gate 	while (count && error == 0) {
880*7c478bd9Sstevel@tonic-gate 		int watchcode;
881*7c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
882*7c478bd9Sstevel@tonic-gate 		size_t part;
883*7c478bd9Sstevel@tonic-gate 		size_t len;
884*7c478bd9Sstevel@tonic-gate 		int ta;
885*7c478bd9Sstevel@tonic-gate 		int mapped;
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
888*7c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > count)
889*7c478bd9Sstevel@tonic-gate 			part = count;
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(watch_uaddr, S_WRITE))
892*7c478bd9Sstevel@tonic-gate 			watchcode = 0;
893*7c478bd9Sstevel@tonic-gate 		else {
894*7c478bd9Sstevel@tonic-gate 			vaddr = watch_uaddr;
895*7c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
896*7c478bd9Sstevel@tonic-gate 			    part, &len, S_WRITE);
897*7c478bd9Sstevel@tonic-gate 			if (watchcode) {
898*7c478bd9Sstevel@tonic-gate 				if (ta == 0)
899*7c478bd9Sstevel@tonic-gate 					part = vaddr - watch_uaddr;
900*7c478bd9Sstevel@tonic-gate 				else {
901*7c478bd9Sstevel@tonic-gate 					len += vaddr - watch_uaddr;
902*7c478bd9Sstevel@tonic-gate 					if (part > len)
903*7c478bd9Sstevel@tonic-gate 						part = len;
904*7c478bd9Sstevel@tonic-gate 				}
905*7c478bd9Sstevel@tonic-gate 			}
906*7c478bd9Sstevel@tonic-gate 		}
907*7c478bd9Sstevel@tonic-gate 
908*7c478bd9Sstevel@tonic-gate 		/*
909*7c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
910*7c478bd9Sstevel@tonic-gate 		 */
911*7c478bd9Sstevel@tonic-gate 		if (part != 0) {
912*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1);
913*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
914*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
915*7c478bd9Sstevel@tonic-gate 			else
916*7c478bd9Sstevel@tonic-gate 				copyout_noerr(watch_kaddr, watch_uaddr, part);
917*7c478bd9Sstevel@tonic-gate 			no_fault();
918*7c478bd9Sstevel@tonic-gate 			if (mapped)
919*7c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_WRITE, 1);
920*7c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
921*7c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
922*7c478bd9Sstevel@tonic-gate 			count -= part;
923*7c478bd9Sstevel@tonic-gate 		}
924*7c478bd9Sstevel@tonic-gate 
925*7c478bd9Sstevel@tonic-gate 		/*
926*7c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
927*7c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
928*7c478bd9Sstevel@tonic-gate 		 */
929*7c478bd9Sstevel@tonic-gate 		while (count && watchcode && ta && len > part && error == 0) {
930*7c478bd9Sstevel@tonic-gate 			len -= part;
931*7c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > count)
932*7c478bd9Sstevel@tonic-gate 				part = count;
933*7c478bd9Sstevel@tonic-gate 			if (part > len)
934*7c478bd9Sstevel@tonic-gate 				part = len;
935*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1);
936*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
937*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
938*7c478bd9Sstevel@tonic-gate 			else
939*7c478bd9Sstevel@tonic-gate 				copyout_noerr(watch_kaddr, watch_uaddr, part);
940*7c478bd9Sstevel@tonic-gate 			no_fault();
941*7c478bd9Sstevel@tonic-gate 			if (mapped)
942*7c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_WRITE, 1);
943*7c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
944*7c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
945*7c478bd9Sstevel@tonic-gate 			count -= part;
946*7c478bd9Sstevel@tonic-gate 		}
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
949*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
950*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
951*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
952*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
953*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
954*7c478bd9Sstevel@tonic-gate 			break;
955*7c478bd9Sstevel@tonic-gate 		}
956*7c478bd9Sstevel@tonic-gate 	}
957*7c478bd9Sstevel@tonic-gate 
958*7c478bd9Sstevel@tonic-gate 	return (error);
959*7c478bd9Sstevel@tonic-gate }
960*7c478bd9Sstevel@tonic-gate 
961*7c478bd9Sstevel@tonic-gate static int
962*7c478bd9Sstevel@tonic-gate watch_copyout(const void *kaddr, void *uaddr, size_t count)
963*7c478bd9Sstevel@tonic-gate {
964*7c478bd9Sstevel@tonic-gate 	return (watch_xcopyout(kaddr, uaddr, count) ? -1 : 0);
965*7c478bd9Sstevel@tonic-gate }
966*7c478bd9Sstevel@tonic-gate 
967*7c478bd9Sstevel@tonic-gate static int
968*7c478bd9Sstevel@tonic-gate watch_copyinstr(
969*7c478bd9Sstevel@tonic-gate 	const char *uaddr,
970*7c478bd9Sstevel@tonic-gate 	char *kaddr,
971*7c478bd9Sstevel@tonic-gate 	size_t maxlength,
972*7c478bd9Sstevel@tonic-gate 	size_t *lencopied)
973*7c478bd9Sstevel@tonic-gate {
974*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
975*7c478bd9Sstevel@tonic-gate 	size_t resid;
976*7c478bd9Sstevel@tonic-gate 	int error = 0;
977*7c478bd9Sstevel@tonic-gate 	label_t ljb;
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 	if ((resid = maxlength) == 0)
980*7c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
981*7c478bd9Sstevel@tonic-gate 
982*7c478bd9Sstevel@tonic-gate 	while (resid && error == 0) {
983*7c478bd9Sstevel@tonic-gate 		int watchcode;
984*7c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
985*7c478bd9Sstevel@tonic-gate 		size_t part;
986*7c478bd9Sstevel@tonic-gate 		size_t len;
987*7c478bd9Sstevel@tonic-gate 		size_t size;
988*7c478bd9Sstevel@tonic-gate 		int ta;
989*7c478bd9Sstevel@tonic-gate 		int mapped;
990*7c478bd9Sstevel@tonic-gate 
991*7c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
992*7c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > resid)
993*7c478bd9Sstevel@tonic-gate 			part = resid;
994*7c478bd9Sstevel@tonic-gate 
995*7c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage((caddr_t)uaddr, S_READ))
996*7c478bd9Sstevel@tonic-gate 			watchcode = 0;
997*7c478bd9Sstevel@tonic-gate 		else {
998*7c478bd9Sstevel@tonic-gate 			vaddr = (caddr_t)uaddr;
999*7c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
1000*7c478bd9Sstevel@tonic-gate 			    part, &len, S_READ);
1001*7c478bd9Sstevel@tonic-gate 			if (watchcode) {
1002*7c478bd9Sstevel@tonic-gate 				if (ta == 0)
1003*7c478bd9Sstevel@tonic-gate 					part = vaddr - uaddr;
1004*7c478bd9Sstevel@tonic-gate 				else {
1005*7c478bd9Sstevel@tonic-gate 					len += vaddr - uaddr;
1006*7c478bd9Sstevel@tonic-gate 					if (part > len)
1007*7c478bd9Sstevel@tonic-gate 						part = len;
1008*7c478bd9Sstevel@tonic-gate 				}
1009*7c478bd9Sstevel@tonic-gate 			}
1010*7c478bd9Sstevel@tonic-gate 		}
1011*7c478bd9Sstevel@tonic-gate 
1012*7c478bd9Sstevel@tonic-gate 		/*
1013*7c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
1014*7c478bd9Sstevel@tonic-gate 		 */
1015*7c478bd9Sstevel@tonic-gate 		if (part != 0) {
1016*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1);
1017*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1018*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
1019*7c478bd9Sstevel@tonic-gate 			else
1020*7c478bd9Sstevel@tonic-gate 				error = copyinstr_noerr(uaddr, kaddr, part,
1021*7c478bd9Sstevel@tonic-gate 				    &size);
1022*7c478bd9Sstevel@tonic-gate 			no_fault();
1023*7c478bd9Sstevel@tonic-gate 			if (mapped)
1024*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)uaddr, part, S_READ, 1);
1025*7c478bd9Sstevel@tonic-gate 			uaddr += size;
1026*7c478bd9Sstevel@tonic-gate 			kaddr += size;
1027*7c478bd9Sstevel@tonic-gate 			resid -= size;
1028*7c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
1029*7c478bd9Sstevel@tonic-gate 			    error = 0;
1030*7c478bd9Sstevel@tonic-gate 			if (error != 0 || (watchcode &&
1031*7c478bd9Sstevel@tonic-gate 			    (uaddr < vaddr || kaddr[-1] == '\0')))
1032*7c478bd9Sstevel@tonic-gate 				break;	/* didn't reach the watched area */
1033*7c478bd9Sstevel@tonic-gate 		}
1034*7c478bd9Sstevel@tonic-gate 
1035*7c478bd9Sstevel@tonic-gate 		/*
1036*7c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
1037*7c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
1038*7c478bd9Sstevel@tonic-gate 		 */
1039*7c478bd9Sstevel@tonic-gate 		while (resid && watchcode && ta && len > part && error == 0 &&
1040*7c478bd9Sstevel@tonic-gate 		    size == part && kaddr[-1] != '\0') {
1041*7c478bd9Sstevel@tonic-gate 			len -= part;
1042*7c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > resid)
1043*7c478bd9Sstevel@tonic-gate 				part = resid;
1044*7c478bd9Sstevel@tonic-gate 			if (part > len)
1045*7c478bd9Sstevel@tonic-gate 				part = len;
1046*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1);
1047*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1048*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
1049*7c478bd9Sstevel@tonic-gate 			else
1050*7c478bd9Sstevel@tonic-gate 				error = copyinstr_noerr(uaddr, kaddr, part,
1051*7c478bd9Sstevel@tonic-gate 				    &size);
1052*7c478bd9Sstevel@tonic-gate 			no_fault();
1053*7c478bd9Sstevel@tonic-gate 			if (mapped)
1054*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)uaddr, part, S_READ, 1);
1055*7c478bd9Sstevel@tonic-gate 			uaddr += size;
1056*7c478bd9Sstevel@tonic-gate 			kaddr += size;
1057*7c478bd9Sstevel@tonic-gate 			resid -= size;
1058*7c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
1059*7c478bd9Sstevel@tonic-gate 			    error = 0;
1060*7c478bd9Sstevel@tonic-gate 		}
1061*7c478bd9Sstevel@tonic-gate 
1062*7c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
1063*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1064*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1065*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1066*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1067*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
1068*7c478bd9Sstevel@tonic-gate 			break;
1069*7c478bd9Sstevel@tonic-gate 		}
1070*7c478bd9Sstevel@tonic-gate 
1071*7c478bd9Sstevel@tonic-gate 		if (error == 0 && part != 0 &&
1072*7c478bd9Sstevel@tonic-gate 		    (size < part || kaddr[-1] == '\0'))
1073*7c478bd9Sstevel@tonic-gate 			break;
1074*7c478bd9Sstevel@tonic-gate 	}
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	if (error != EFAULT && lencopied)
1077*7c478bd9Sstevel@tonic-gate 		*lencopied = maxlength - resid;
1078*7c478bd9Sstevel@tonic-gate 	return (error);
1079*7c478bd9Sstevel@tonic-gate }
1080*7c478bd9Sstevel@tonic-gate 
1081*7c478bd9Sstevel@tonic-gate static int
1082*7c478bd9Sstevel@tonic-gate watch_copyoutstr(
1083*7c478bd9Sstevel@tonic-gate 	const char *kaddr,
1084*7c478bd9Sstevel@tonic-gate 	char *uaddr,
1085*7c478bd9Sstevel@tonic-gate 	size_t maxlength,
1086*7c478bd9Sstevel@tonic-gate 	size_t *lencopied)
1087*7c478bd9Sstevel@tonic-gate {
1088*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1089*7c478bd9Sstevel@tonic-gate 	size_t resid;
1090*7c478bd9Sstevel@tonic-gate 	int error = 0;
1091*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1092*7c478bd9Sstevel@tonic-gate 
1093*7c478bd9Sstevel@tonic-gate 	if ((resid = maxlength) == 0)
1094*7c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
1095*7c478bd9Sstevel@tonic-gate 
1096*7c478bd9Sstevel@tonic-gate 	while (resid && error == 0) {
1097*7c478bd9Sstevel@tonic-gate 		int watchcode;
1098*7c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
1099*7c478bd9Sstevel@tonic-gate 		size_t part;
1100*7c478bd9Sstevel@tonic-gate 		size_t len;
1101*7c478bd9Sstevel@tonic-gate 		size_t size;
1102*7c478bd9Sstevel@tonic-gate 		int ta;
1103*7c478bd9Sstevel@tonic-gate 		int mapped;
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
1106*7c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > resid)
1107*7c478bd9Sstevel@tonic-gate 			part = resid;
1108*7c478bd9Sstevel@tonic-gate 
1109*7c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(uaddr, S_WRITE)) {
1110*7c478bd9Sstevel@tonic-gate 			watchcode = 0;
1111*7c478bd9Sstevel@tonic-gate 		} else {
1112*7c478bd9Sstevel@tonic-gate 			vaddr = uaddr;
1113*7c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
1114*7c478bd9Sstevel@tonic-gate 			    part, &len, S_WRITE);
1115*7c478bd9Sstevel@tonic-gate 			if (watchcode && ta == 0)
1116*7c478bd9Sstevel@tonic-gate 				part = vaddr - uaddr;
1117*7c478bd9Sstevel@tonic-gate 		}
1118*7c478bd9Sstevel@tonic-gate 
1119*7c478bd9Sstevel@tonic-gate 		/*
1120*7c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
1121*7c478bd9Sstevel@tonic-gate 		 */
1122*7c478bd9Sstevel@tonic-gate 		if (part != 0) {
1123*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(uaddr, part, S_WRITE, 1);
1124*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1125*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
1126*7c478bd9Sstevel@tonic-gate 			else
1127*7c478bd9Sstevel@tonic-gate 				error = copyoutstr_noerr(kaddr, uaddr, part,
1128*7c478bd9Sstevel@tonic-gate 				    &size);
1129*7c478bd9Sstevel@tonic-gate 			no_fault();
1130*7c478bd9Sstevel@tonic-gate 			if (mapped)
1131*7c478bd9Sstevel@tonic-gate 				pr_unmappage(uaddr, part, S_WRITE, 1);
1132*7c478bd9Sstevel@tonic-gate 			uaddr += size;
1133*7c478bd9Sstevel@tonic-gate 			kaddr += size;
1134*7c478bd9Sstevel@tonic-gate 			resid -= size;
1135*7c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
1136*7c478bd9Sstevel@tonic-gate 			    error = 0;
1137*7c478bd9Sstevel@tonic-gate 			if (error != 0 || (watchcode &&
1138*7c478bd9Sstevel@tonic-gate 			    (uaddr < vaddr || kaddr[-1] == '\0')))
1139*7c478bd9Sstevel@tonic-gate 				break;	/* didn't reach the watched area */
1140*7c478bd9Sstevel@tonic-gate 		}
1141*7c478bd9Sstevel@tonic-gate 
1142*7c478bd9Sstevel@tonic-gate 		/*
1143*7c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
1144*7c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
1145*7c478bd9Sstevel@tonic-gate 		 */
1146*7c478bd9Sstevel@tonic-gate 		while (resid && watchcode && ta && len > part && error == 0 &&
1147*7c478bd9Sstevel@tonic-gate 		    size == part && kaddr[-1] != '\0') {
1148*7c478bd9Sstevel@tonic-gate 			len -= part;
1149*7c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > resid)
1150*7c478bd9Sstevel@tonic-gate 				part = resid;
1151*7c478bd9Sstevel@tonic-gate 			if (part > len)
1152*7c478bd9Sstevel@tonic-gate 				part = len;
1153*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(uaddr, part, S_WRITE, 1);
1154*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1155*7c478bd9Sstevel@tonic-gate 				error = EFAULT;
1156*7c478bd9Sstevel@tonic-gate 			else
1157*7c478bd9Sstevel@tonic-gate 				error = copyoutstr_noerr(kaddr, uaddr, part,
1158*7c478bd9Sstevel@tonic-gate 				    &size);
1159*7c478bd9Sstevel@tonic-gate 			no_fault();
1160*7c478bd9Sstevel@tonic-gate 			if (mapped)
1161*7c478bd9Sstevel@tonic-gate 				pr_unmappage(uaddr, part, S_WRITE, 1);
1162*7c478bd9Sstevel@tonic-gate 			uaddr += size;
1163*7c478bd9Sstevel@tonic-gate 			kaddr += size;
1164*7c478bd9Sstevel@tonic-gate 			resid -= size;
1165*7c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
1166*7c478bd9Sstevel@tonic-gate 			    error = 0;
1167*7c478bd9Sstevel@tonic-gate 		}
1168*7c478bd9Sstevel@tonic-gate 
1169*7c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
1170*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1171*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1172*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1173*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1174*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
1175*7c478bd9Sstevel@tonic-gate 			break;
1176*7c478bd9Sstevel@tonic-gate 		}
1177*7c478bd9Sstevel@tonic-gate 
1178*7c478bd9Sstevel@tonic-gate 		if (error == 0 && part != 0 &&
1179*7c478bd9Sstevel@tonic-gate 		    (size < part || kaddr[-1] == '\0'))
1180*7c478bd9Sstevel@tonic-gate 			break;
1181*7c478bd9Sstevel@tonic-gate 	}
1182*7c478bd9Sstevel@tonic-gate 
1183*7c478bd9Sstevel@tonic-gate 	if (error != EFAULT && lencopied)
1184*7c478bd9Sstevel@tonic-gate 		*lencopied = maxlength - resid;
1185*7c478bd9Sstevel@tonic-gate 	return (error);
1186*7c478bd9Sstevel@tonic-gate }
1187*7c478bd9Sstevel@tonic-gate 
1188*7c478bd9Sstevel@tonic-gate typedef int (*fuword_func)(const void *, void *);
1189*7c478bd9Sstevel@tonic-gate 
1190*7c478bd9Sstevel@tonic-gate /*
1191*7c478bd9Sstevel@tonic-gate  * Generic form of watch_fuword8(), watch_fuword16(), etc.
1192*7c478bd9Sstevel@tonic-gate  */
1193*7c478bd9Sstevel@tonic-gate static int
1194*7c478bd9Sstevel@tonic-gate watch_fuword(const void *addr, void *dst, fuword_func func, size_t size)
1195*7c478bd9Sstevel@tonic-gate {
1196*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1197*7c478bd9Sstevel@tonic-gate 	int watchcode;
1198*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
1199*7c478bd9Sstevel@tonic-gate 	int mapped;
1200*7c478bd9Sstevel@tonic-gate 	int rv = 0;
1201*7c478bd9Sstevel@tonic-gate 	int ta;
1202*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1203*7c478bd9Sstevel@tonic-gate 
1204*7c478bd9Sstevel@tonic-gate 	for (;;) {
1205*7c478bd9Sstevel@tonic-gate 
1206*7c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
1207*7c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, size, NULL, S_READ);
1208*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
1209*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, size, S_READ, 1);
1210*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1211*7c478bd9Sstevel@tonic-gate 				rv = -1;
1212*7c478bd9Sstevel@tonic-gate 			else
1213*7c478bd9Sstevel@tonic-gate 				(*func)(addr, dst);
1214*7c478bd9Sstevel@tonic-gate 			no_fault();
1215*7c478bd9Sstevel@tonic-gate 			if (mapped)
1216*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, size, S_READ, 1);
1217*7c478bd9Sstevel@tonic-gate 		}
1218*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1219*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1220*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1221*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1222*7c478bd9Sstevel@tonic-gate 			rv = -1;
1223*7c478bd9Sstevel@tonic-gate 			break;
1224*7c478bd9Sstevel@tonic-gate 		}
1225*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
1226*7c478bd9Sstevel@tonic-gate 			break;
1227*7c478bd9Sstevel@tonic-gate 	}
1228*7c478bd9Sstevel@tonic-gate 
1229*7c478bd9Sstevel@tonic-gate 	return (rv);
1230*7c478bd9Sstevel@tonic-gate }
1231*7c478bd9Sstevel@tonic-gate 
1232*7c478bd9Sstevel@tonic-gate static int
1233*7c478bd9Sstevel@tonic-gate watch_fuword8(const void *addr, uint8_t *dst)
1234*7c478bd9Sstevel@tonic-gate {
1235*7c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword8_noerr,
1236*7c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
1237*7c478bd9Sstevel@tonic-gate }
1238*7c478bd9Sstevel@tonic-gate 
1239*7c478bd9Sstevel@tonic-gate static int
1240*7c478bd9Sstevel@tonic-gate watch_fuword16(const void *addr, uint16_t *dst)
1241*7c478bd9Sstevel@tonic-gate {
1242*7c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword16_noerr,
1243*7c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
1244*7c478bd9Sstevel@tonic-gate }
1245*7c478bd9Sstevel@tonic-gate 
1246*7c478bd9Sstevel@tonic-gate static int
1247*7c478bd9Sstevel@tonic-gate watch_fuword32(const void *addr, uint32_t *dst)
1248*7c478bd9Sstevel@tonic-gate {
1249*7c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword32_noerr,
1250*7c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
1251*7c478bd9Sstevel@tonic-gate }
1252*7c478bd9Sstevel@tonic-gate 
1253*7c478bd9Sstevel@tonic-gate #ifdef _LP64
1254*7c478bd9Sstevel@tonic-gate static int
1255*7c478bd9Sstevel@tonic-gate watch_fuword64(const void *addr, uint64_t *dst)
1256*7c478bd9Sstevel@tonic-gate {
1257*7c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword64_noerr,
1258*7c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
1259*7c478bd9Sstevel@tonic-gate }
1260*7c478bd9Sstevel@tonic-gate #endif
1261*7c478bd9Sstevel@tonic-gate 
1262*7c478bd9Sstevel@tonic-gate 
1263*7c478bd9Sstevel@tonic-gate static int
1264*7c478bd9Sstevel@tonic-gate watch_suword8(void *addr, uint8_t value)
1265*7c478bd9Sstevel@tonic-gate {
1266*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1267*7c478bd9Sstevel@tonic-gate 	int watchcode;
1268*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
1269*7c478bd9Sstevel@tonic-gate 	int mapped;
1270*7c478bd9Sstevel@tonic-gate 	int rv = 0;
1271*7c478bd9Sstevel@tonic-gate 	int ta;
1272*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1273*7c478bd9Sstevel@tonic-gate 
1274*7c478bd9Sstevel@tonic-gate 	for (;;) {
1275*7c478bd9Sstevel@tonic-gate 
1276*7c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
1277*7c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
1278*7c478bd9Sstevel@tonic-gate 		    S_WRITE);
1279*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
1280*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
1281*7c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
1282*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1283*7c478bd9Sstevel@tonic-gate 				rv = -1;
1284*7c478bd9Sstevel@tonic-gate 			else
1285*7c478bd9Sstevel@tonic-gate 				suword8_noerr(addr, value);
1286*7c478bd9Sstevel@tonic-gate 			no_fault();
1287*7c478bd9Sstevel@tonic-gate 			if (mapped)
1288*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
1289*7c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
1290*7c478bd9Sstevel@tonic-gate 		}
1291*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1292*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1293*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1294*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1295*7c478bd9Sstevel@tonic-gate 			rv = -1;
1296*7c478bd9Sstevel@tonic-gate 			break;
1297*7c478bd9Sstevel@tonic-gate 		}
1298*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
1299*7c478bd9Sstevel@tonic-gate 			break;
1300*7c478bd9Sstevel@tonic-gate 	}
1301*7c478bd9Sstevel@tonic-gate 
1302*7c478bd9Sstevel@tonic-gate 	return (rv);
1303*7c478bd9Sstevel@tonic-gate }
1304*7c478bd9Sstevel@tonic-gate 
1305*7c478bd9Sstevel@tonic-gate static int
1306*7c478bd9Sstevel@tonic-gate watch_suword16(void *addr, uint16_t value)
1307*7c478bd9Sstevel@tonic-gate {
1308*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1309*7c478bd9Sstevel@tonic-gate 	int watchcode;
1310*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
1311*7c478bd9Sstevel@tonic-gate 	int mapped;
1312*7c478bd9Sstevel@tonic-gate 	int rv = 0;
1313*7c478bd9Sstevel@tonic-gate 	int ta;
1314*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1315*7c478bd9Sstevel@tonic-gate 
1316*7c478bd9Sstevel@tonic-gate 	for (;;) {
1317*7c478bd9Sstevel@tonic-gate 
1318*7c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
1319*7c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
1320*7c478bd9Sstevel@tonic-gate 		    S_WRITE);
1321*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
1322*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
1323*7c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
1324*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1325*7c478bd9Sstevel@tonic-gate 				rv = -1;
1326*7c478bd9Sstevel@tonic-gate 			else
1327*7c478bd9Sstevel@tonic-gate 				suword16_noerr(addr, value);
1328*7c478bd9Sstevel@tonic-gate 			no_fault();
1329*7c478bd9Sstevel@tonic-gate 			if (mapped)
1330*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
1331*7c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
1332*7c478bd9Sstevel@tonic-gate 		}
1333*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1334*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1335*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1336*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1337*7c478bd9Sstevel@tonic-gate 			rv = -1;
1338*7c478bd9Sstevel@tonic-gate 			break;
1339*7c478bd9Sstevel@tonic-gate 		}
1340*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
1341*7c478bd9Sstevel@tonic-gate 			break;
1342*7c478bd9Sstevel@tonic-gate 	}
1343*7c478bd9Sstevel@tonic-gate 
1344*7c478bd9Sstevel@tonic-gate 	return (rv);
1345*7c478bd9Sstevel@tonic-gate }
1346*7c478bd9Sstevel@tonic-gate 
1347*7c478bd9Sstevel@tonic-gate static int
1348*7c478bd9Sstevel@tonic-gate watch_suword32(void *addr, uint32_t value)
1349*7c478bd9Sstevel@tonic-gate {
1350*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1351*7c478bd9Sstevel@tonic-gate 	int watchcode;
1352*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
1353*7c478bd9Sstevel@tonic-gate 	int mapped;
1354*7c478bd9Sstevel@tonic-gate 	int rv = 0;
1355*7c478bd9Sstevel@tonic-gate 	int ta;
1356*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1357*7c478bd9Sstevel@tonic-gate 
1358*7c478bd9Sstevel@tonic-gate 	for (;;) {
1359*7c478bd9Sstevel@tonic-gate 
1360*7c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
1361*7c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
1362*7c478bd9Sstevel@tonic-gate 		    S_WRITE);
1363*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
1364*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
1365*7c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
1366*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1367*7c478bd9Sstevel@tonic-gate 				rv = -1;
1368*7c478bd9Sstevel@tonic-gate 			else
1369*7c478bd9Sstevel@tonic-gate 				suword32_noerr(addr, value);
1370*7c478bd9Sstevel@tonic-gate 			no_fault();
1371*7c478bd9Sstevel@tonic-gate 			if (mapped)
1372*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
1373*7c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
1374*7c478bd9Sstevel@tonic-gate 		}
1375*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1376*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1377*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1378*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1379*7c478bd9Sstevel@tonic-gate 			rv = -1;
1380*7c478bd9Sstevel@tonic-gate 			break;
1381*7c478bd9Sstevel@tonic-gate 		}
1382*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
1383*7c478bd9Sstevel@tonic-gate 			break;
1384*7c478bd9Sstevel@tonic-gate 	}
1385*7c478bd9Sstevel@tonic-gate 
1386*7c478bd9Sstevel@tonic-gate 	return (rv);
1387*7c478bd9Sstevel@tonic-gate }
1388*7c478bd9Sstevel@tonic-gate 
1389*7c478bd9Sstevel@tonic-gate #ifdef _LP64
1390*7c478bd9Sstevel@tonic-gate static int
1391*7c478bd9Sstevel@tonic-gate watch_suword64(void *addr, uint64_t value)
1392*7c478bd9Sstevel@tonic-gate {
1393*7c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
1394*7c478bd9Sstevel@tonic-gate 	int watchcode;
1395*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
1396*7c478bd9Sstevel@tonic-gate 	int mapped;
1397*7c478bd9Sstevel@tonic-gate 	int rv = 0;
1398*7c478bd9Sstevel@tonic-gate 	int ta;
1399*7c478bd9Sstevel@tonic-gate 	label_t ljb;
1400*7c478bd9Sstevel@tonic-gate 
1401*7c478bd9Sstevel@tonic-gate 	for (;;) {
1402*7c478bd9Sstevel@tonic-gate 
1403*7c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
1404*7c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
1405*7c478bd9Sstevel@tonic-gate 		    S_WRITE);
1406*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
1407*7c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
1408*7c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
1409*7c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
1410*7c478bd9Sstevel@tonic-gate 				rv = -1;
1411*7c478bd9Sstevel@tonic-gate 			else
1412*7c478bd9Sstevel@tonic-gate 				suword64_noerr(addr, value);
1413*7c478bd9Sstevel@tonic-gate 			no_fault();
1414*7c478bd9Sstevel@tonic-gate 			if (mapped)
1415*7c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
1416*7c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
1417*7c478bd9Sstevel@tonic-gate 		}
1418*7c478bd9Sstevel@tonic-gate 		if (watchcode &&
1419*7c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
1420*7c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
1421*7c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
1422*7c478bd9Sstevel@tonic-gate 			rv = -1;
1423*7c478bd9Sstevel@tonic-gate 			break;
1424*7c478bd9Sstevel@tonic-gate 		}
1425*7c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
1426*7c478bd9Sstevel@tonic-gate 			break;
1427*7c478bd9Sstevel@tonic-gate 	}
1428*7c478bd9Sstevel@tonic-gate 
1429*7c478bd9Sstevel@tonic-gate 	return (rv);
1430*7c478bd9Sstevel@tonic-gate }
1431*7c478bd9Sstevel@tonic-gate #endif /* _LP64 */
1432*7c478bd9Sstevel@tonic-gate 
1433*7c478bd9Sstevel@tonic-gate /*
1434*7c478bd9Sstevel@tonic-gate  * Check for watched addresses in the given address space.
1435*7c478bd9Sstevel@tonic-gate  * Return 1 if this is true, otherwise 0.
1436*7c478bd9Sstevel@tonic-gate  */
1437*7c478bd9Sstevel@tonic-gate static int
1438*7c478bd9Sstevel@tonic-gate pr_is_watched(caddr_t base, size_t len, int rw)
1439*7c478bd9Sstevel@tonic-gate {
1440*7c478bd9Sstevel@tonic-gate 	caddr_t saddr = (caddr_t)((uintptr_t)base & (uintptr_t)PAGEMASK);
1441*7c478bd9Sstevel@tonic-gate 	caddr_t eaddr = base + len;
1442*7c478bd9Sstevel@tonic-gate 	caddr_t paddr;
1443*7c478bd9Sstevel@tonic-gate 
1444*7c478bd9Sstevel@tonic-gate 	for (paddr = saddr; paddr < eaddr; paddr += PAGESIZE) {
1445*7c478bd9Sstevel@tonic-gate 		if (pr_is_watchpage(paddr, rw))
1446*7c478bd9Sstevel@tonic-gate 			return (1);
1447*7c478bd9Sstevel@tonic-gate 	}
1448*7c478bd9Sstevel@tonic-gate 
1449*7c478bd9Sstevel@tonic-gate 	return (0);
1450*7c478bd9Sstevel@tonic-gate }
1451*7c478bd9Sstevel@tonic-gate 
1452*7c478bd9Sstevel@tonic-gate /*
1453*7c478bd9Sstevel@tonic-gate  * Wrapper for the physio() function.
1454*7c478bd9Sstevel@tonic-gate  * Splits one uio operation with multiple iovecs into uio operations with
1455*7c478bd9Sstevel@tonic-gate  * only one iovecs to do the watchpoint handling separately for each iovecs.
1456*7c478bd9Sstevel@tonic-gate  */
1457*7c478bd9Sstevel@tonic-gate static int
1458*7c478bd9Sstevel@tonic-gate watch_physio(int (*strat)(struct buf *), struct buf *bp, dev_t dev,
1459*7c478bd9Sstevel@tonic-gate     int rw, void (*mincnt)(struct buf *), struct uio *uio)
1460*7c478bd9Sstevel@tonic-gate {
1461*7c478bd9Sstevel@tonic-gate 	struct uio auio;
1462*7c478bd9Sstevel@tonic-gate 	struct iovec *iov;
1463*7c478bd9Sstevel@tonic-gate 	caddr_t  base;
1464*7c478bd9Sstevel@tonic-gate 	size_t len;
1465*7c478bd9Sstevel@tonic-gate 	int seg_rw;
1466*7c478bd9Sstevel@tonic-gate 	int error = 0;
1467*7c478bd9Sstevel@tonic-gate 
1468*7c478bd9Sstevel@tonic-gate 	if (uio->uio_segflg == UIO_SYSSPACE)
1469*7c478bd9Sstevel@tonic-gate 		return (default_physio(strat, bp, dev, rw, mincnt, uio));
1470*7c478bd9Sstevel@tonic-gate 
1471*7c478bd9Sstevel@tonic-gate 	seg_rw = (rw == B_READ) ? S_WRITE : S_READ;
1472*7c478bd9Sstevel@tonic-gate 
1473*7c478bd9Sstevel@tonic-gate 	while (uio->uio_iovcnt > 0) {
1474*7c478bd9Sstevel@tonic-gate 		if (uio->uio_resid == 0) {
1475*7c478bd9Sstevel@tonic-gate 			/*
1476*7c478bd9Sstevel@tonic-gate 			 * Make sure to return the uio structure with the
1477*7c478bd9Sstevel@tonic-gate 			 * same values as default_physio() does.
1478*7c478bd9Sstevel@tonic-gate 			 */
1479*7c478bd9Sstevel@tonic-gate 			uio->uio_iov++;
1480*7c478bd9Sstevel@tonic-gate 			uio->uio_iovcnt--;
1481*7c478bd9Sstevel@tonic-gate 			continue;
1482*7c478bd9Sstevel@tonic-gate 		}
1483*7c478bd9Sstevel@tonic-gate 
1484*7c478bd9Sstevel@tonic-gate 		iov = uio->uio_iov;
1485*7c478bd9Sstevel@tonic-gate 		len = MIN(iov->iov_len, uio->uio_resid);
1486*7c478bd9Sstevel@tonic-gate 
1487*7c478bd9Sstevel@tonic-gate 		auio.uio_iovcnt = 1;
1488*7c478bd9Sstevel@tonic-gate 		auio.uio_iov = iov;
1489*7c478bd9Sstevel@tonic-gate 		auio.uio_resid = len;
1490*7c478bd9Sstevel@tonic-gate 		auio.uio_loffset = uio->uio_loffset;
1491*7c478bd9Sstevel@tonic-gate 		auio.uio_llimit = uio->uio_llimit;
1492*7c478bd9Sstevel@tonic-gate 		auio.uio_fmode = uio->uio_fmode;
1493*7c478bd9Sstevel@tonic-gate 		auio.uio_extflg = uio->uio_extflg;
1494*7c478bd9Sstevel@tonic-gate 		auio.uio_segflg = uio->uio_segflg;
1495*7c478bd9Sstevel@tonic-gate 
1496*7c478bd9Sstevel@tonic-gate 		base = iov->iov_base;
1497*7c478bd9Sstevel@tonic-gate 
1498*7c478bd9Sstevel@tonic-gate 		if (!pr_is_watched(base, len, seg_rw)) {
1499*7c478bd9Sstevel@tonic-gate 			/*
1500*7c478bd9Sstevel@tonic-gate 			 * The given memory references don't cover a
1501*7c478bd9Sstevel@tonic-gate 			 * watched page.
1502*7c478bd9Sstevel@tonic-gate 			 */
1503*7c478bd9Sstevel@tonic-gate 			error = default_physio(strat, bp, dev, rw, mincnt,
1504*7c478bd9Sstevel@tonic-gate 			    &auio);
1505*7c478bd9Sstevel@tonic-gate 
1506*7c478bd9Sstevel@tonic-gate 			/* Update uio with values from auio. */
1507*7c478bd9Sstevel@tonic-gate 			len -= auio.uio_resid;
1508*7c478bd9Sstevel@tonic-gate 			uio->uio_resid -= len;
1509*7c478bd9Sstevel@tonic-gate 			uio->uio_loffset += len;
1510*7c478bd9Sstevel@tonic-gate 
1511*7c478bd9Sstevel@tonic-gate 			/*
1512*7c478bd9Sstevel@tonic-gate 			 * Return if an error occurred or not all data
1513*7c478bd9Sstevel@tonic-gate 			 * was copied.
1514*7c478bd9Sstevel@tonic-gate 			 */
1515*7c478bd9Sstevel@tonic-gate 			if (auio.uio_resid || error)
1516*7c478bd9Sstevel@tonic-gate 				break;
1517*7c478bd9Sstevel@tonic-gate 			uio->uio_iov++;
1518*7c478bd9Sstevel@tonic-gate 			uio->uio_iovcnt--;
1519*7c478bd9Sstevel@tonic-gate 		} else {
1520*7c478bd9Sstevel@tonic-gate 			int mapped, watchcode, ta;
1521*7c478bd9Sstevel@tonic-gate 			caddr_t vaddr = base;
1522*7c478bd9Sstevel@tonic-gate 			klwp_t *lwp = ttolwp(curthread);
1523*7c478bd9Sstevel@tonic-gate 
1524*7c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta, len,
1525*7c478bd9Sstevel@tonic-gate 			    NULL, seg_rw);
1526*7c478bd9Sstevel@tonic-gate 
1527*7c478bd9Sstevel@tonic-gate 			if (watchcode == 0 || ta != 0) {
1528*7c478bd9Sstevel@tonic-gate 				/*
1529*7c478bd9Sstevel@tonic-gate 				 * Do the io if the given memory references
1530*7c478bd9Sstevel@tonic-gate 				 * don't cover a watched area (watchcode=0)
1531*7c478bd9Sstevel@tonic-gate 				 * or if WA_TRAPAFTER was specified.
1532*7c478bd9Sstevel@tonic-gate 				 */
1533*7c478bd9Sstevel@tonic-gate 				mapped = pr_mappage(base, len, seg_rw, 1);
1534*7c478bd9Sstevel@tonic-gate 				error = default_physio(strat, bp, dev, rw,
1535*7c478bd9Sstevel@tonic-gate 				    mincnt, &auio);
1536*7c478bd9Sstevel@tonic-gate 				if (mapped)
1537*7c478bd9Sstevel@tonic-gate 					pr_unmappage(base, len, seg_rw, 1);
1538*7c478bd9Sstevel@tonic-gate 
1539*7c478bd9Sstevel@tonic-gate 				len -= auio.uio_resid;
1540*7c478bd9Sstevel@tonic-gate 				uio->uio_resid -= len;
1541*7c478bd9Sstevel@tonic-gate 				uio->uio_loffset += len;
1542*7c478bd9Sstevel@tonic-gate 			}
1543*7c478bd9Sstevel@tonic-gate 
1544*7c478bd9Sstevel@tonic-gate 			/*
1545*7c478bd9Sstevel@tonic-gate 			 * If we hit a watched address, do the watchpoint logic.
1546*7c478bd9Sstevel@tonic-gate 			 */
1547*7c478bd9Sstevel@tonic-gate 			if (watchcode &&
1548*7c478bd9Sstevel@tonic-gate 			    (!sys_watchpoint(vaddr, watchcode, ta) ||
1549*7c478bd9Sstevel@tonic-gate 			    lwp->lwp_sysabort)) {
1550*7c478bd9Sstevel@tonic-gate 				lwp->lwp_sysabort = 0;
1551*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
1552*7c478bd9Sstevel@tonic-gate 			}
1553*7c478bd9Sstevel@tonic-gate 
1554*7c478bd9Sstevel@tonic-gate 			/*
1555*7c478bd9Sstevel@tonic-gate 			 * Check for errors from default_physio().
1556*7c478bd9Sstevel@tonic-gate 			 */
1557*7c478bd9Sstevel@tonic-gate 			if (watchcode == 0 || ta != 0) {
1558*7c478bd9Sstevel@tonic-gate 				if (auio.uio_resid || error)
1559*7c478bd9Sstevel@tonic-gate 					break;
1560*7c478bd9Sstevel@tonic-gate 				uio->uio_iov++;
1561*7c478bd9Sstevel@tonic-gate 				uio->uio_iovcnt--;
1562*7c478bd9Sstevel@tonic-gate 			}
1563*7c478bd9Sstevel@tonic-gate 		}
1564*7c478bd9Sstevel@tonic-gate 	}
1565*7c478bd9Sstevel@tonic-gate 
1566*7c478bd9Sstevel@tonic-gate 	return (error);
1567*7c478bd9Sstevel@tonic-gate }
1568*7c478bd9Sstevel@tonic-gate 
1569*7c478bd9Sstevel@tonic-gate int
1570*7c478bd9Sstevel@tonic-gate wa_compare(const void *a, const void *b)
1571*7c478bd9Sstevel@tonic-gate {
1572*7c478bd9Sstevel@tonic-gate 	const watched_area_t *pa = a;
1573*7c478bd9Sstevel@tonic-gate 	const watched_area_t *pb = b;
1574*7c478bd9Sstevel@tonic-gate 
1575*7c478bd9Sstevel@tonic-gate 	if (pa->wa_vaddr < pb->wa_vaddr)
1576*7c478bd9Sstevel@tonic-gate 		return (-1);
1577*7c478bd9Sstevel@tonic-gate 	else if (pa->wa_vaddr > pb->wa_vaddr)
1578*7c478bd9Sstevel@tonic-gate 		return (1);
1579*7c478bd9Sstevel@tonic-gate 	else
1580*7c478bd9Sstevel@tonic-gate 		return (0);
1581*7c478bd9Sstevel@tonic-gate }
1582*7c478bd9Sstevel@tonic-gate 
1583*7c478bd9Sstevel@tonic-gate int
1584*7c478bd9Sstevel@tonic-gate wp_compare(const void *a, const void *b)
1585*7c478bd9Sstevel@tonic-gate {
1586*7c478bd9Sstevel@tonic-gate 	const watched_page_t *pa = a;
1587*7c478bd9Sstevel@tonic-gate 	const watched_page_t *pb = b;
1588*7c478bd9Sstevel@tonic-gate 
1589*7c478bd9Sstevel@tonic-gate 	if (pa->wp_vaddr < pb->wp_vaddr)
1590*7c478bd9Sstevel@tonic-gate 		return (-1);
1591*7c478bd9Sstevel@tonic-gate 	else if (pa->wp_vaddr > pb->wp_vaddr)
1592*7c478bd9Sstevel@tonic-gate 		return (1);
1593*7c478bd9Sstevel@tonic-gate 	else
1594*7c478bd9Sstevel@tonic-gate 		return (0);
1595*7c478bd9Sstevel@tonic-gate }
1596*7c478bd9Sstevel@tonic-gate 
1597*7c478bd9Sstevel@tonic-gate /*
1598*7c478bd9Sstevel@tonic-gate  * Given an address range, finds the first watched area which overlaps some or
1599*7c478bd9Sstevel@tonic-gate  * all of the range.
1600*7c478bd9Sstevel@tonic-gate  */
1601*7c478bd9Sstevel@tonic-gate watched_area_t *
1602*7c478bd9Sstevel@tonic-gate pr_find_watched_area(proc_t *p, watched_area_t *pwa, avl_index_t *where)
1603*7c478bd9Sstevel@tonic-gate {
1604*7c478bd9Sstevel@tonic-gate 	caddr_t vaddr = pwa->wa_vaddr;
1605*7c478bd9Sstevel@tonic-gate 	caddr_t eaddr = pwa->wa_eaddr;
1606*7c478bd9Sstevel@tonic-gate 	watched_area_t *wap;
1607*7c478bd9Sstevel@tonic-gate 	avl_index_t real_where;
1608*7c478bd9Sstevel@tonic-gate 
1609*7c478bd9Sstevel@tonic-gate 	/* First, check if there is an exact match.  */
1610*7c478bd9Sstevel@tonic-gate 	wap = avl_find(&p->p_warea, pwa, &real_where);
1611*7c478bd9Sstevel@tonic-gate 
1612*7c478bd9Sstevel@tonic-gate 
1613*7c478bd9Sstevel@tonic-gate 	/* Check to see if we overlap with the previous area.  */
1614*7c478bd9Sstevel@tonic-gate 	if (wap == NULL) {
1615*7c478bd9Sstevel@tonic-gate 		wap = avl_nearest(&p->p_warea, real_where, AVL_BEFORE);
1616*7c478bd9Sstevel@tonic-gate 		if (wap != NULL &&
1617*7c478bd9Sstevel@tonic-gate 		    (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr))
1618*7c478bd9Sstevel@tonic-gate 			wap = NULL;
1619*7c478bd9Sstevel@tonic-gate 	}
1620*7c478bd9Sstevel@tonic-gate 
1621*7c478bd9Sstevel@tonic-gate 	/* Try the next area.  */
1622*7c478bd9Sstevel@tonic-gate 	if (wap == NULL) {
1623*7c478bd9Sstevel@tonic-gate 		wap = avl_nearest(&p->p_warea, real_where, AVL_AFTER);
1624*7c478bd9Sstevel@tonic-gate 		if (wap != NULL &&
1625*7c478bd9Sstevel@tonic-gate 		    (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr))
1626*7c478bd9Sstevel@tonic-gate 			wap = NULL;
1627*7c478bd9Sstevel@tonic-gate 	}
1628*7c478bd9Sstevel@tonic-gate 
1629*7c478bd9Sstevel@tonic-gate 	if (where)
1630*7c478bd9Sstevel@tonic-gate 		*where = real_where;
1631*7c478bd9Sstevel@tonic-gate 
1632*7c478bd9Sstevel@tonic-gate 	return (wap);
1633*7c478bd9Sstevel@tonic-gate }
1634*7c478bd9Sstevel@tonic-gate 
1635*7c478bd9Sstevel@tonic-gate void
1636*7c478bd9Sstevel@tonic-gate watch_enable(kthread_id_t t)
1637*7c478bd9Sstevel@tonic-gate {
1638*7c478bd9Sstevel@tonic-gate 	t->t_proc_flag |= TP_WATCHPT;
1639*7c478bd9Sstevel@tonic-gate 	install_copyops(t, &watch_copyops);
1640*7c478bd9Sstevel@tonic-gate }
1641*7c478bd9Sstevel@tonic-gate 
1642*7c478bd9Sstevel@tonic-gate void
1643*7c478bd9Sstevel@tonic-gate watch_disable(kthread_id_t t)
1644*7c478bd9Sstevel@tonic-gate {
1645*7c478bd9Sstevel@tonic-gate 	t->t_proc_flag &= ~TP_WATCHPT;
1646*7c478bd9Sstevel@tonic-gate 	remove_copyops(t);
1647*7c478bd9Sstevel@tonic-gate }
1648*7c478bd9Sstevel@tonic-gate 
1649*7c478bd9Sstevel@tonic-gate int
1650*7c478bd9Sstevel@tonic-gate copyin_nowatch(const void *uaddr, void *kaddr, size_t len)
1651*7c478bd9Sstevel@tonic-gate {
1652*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1653*7c478bd9Sstevel@tonic-gate 
1654*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(uaddr, len, S_READ);
1655*7c478bd9Sstevel@tonic-gate 	ret = copyin(uaddr, kaddr, len);
1656*7c478bd9Sstevel@tonic-gate 	if (watched)
1657*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(uaddr, len, S_READ);
1658*7c478bd9Sstevel@tonic-gate 
1659*7c478bd9Sstevel@tonic-gate 	return (ret);
1660*7c478bd9Sstevel@tonic-gate }
1661*7c478bd9Sstevel@tonic-gate 
1662*7c478bd9Sstevel@tonic-gate int
1663*7c478bd9Sstevel@tonic-gate copyout_nowatch(const void *kaddr, void *uaddr, size_t len)
1664*7c478bd9Sstevel@tonic-gate {
1665*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1666*7c478bd9Sstevel@tonic-gate 
1667*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(uaddr, len, S_WRITE);
1668*7c478bd9Sstevel@tonic-gate 	ret = copyout(kaddr, uaddr, len);
1669*7c478bd9Sstevel@tonic-gate 	if (watched)
1670*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(uaddr, len, S_WRITE);
1671*7c478bd9Sstevel@tonic-gate 
1672*7c478bd9Sstevel@tonic-gate 	return (ret);
1673*7c478bd9Sstevel@tonic-gate }
1674*7c478bd9Sstevel@tonic-gate 
1675*7c478bd9Sstevel@tonic-gate #ifdef _LP64
1676*7c478bd9Sstevel@tonic-gate int
1677*7c478bd9Sstevel@tonic-gate fuword64_nowatch(const void *addr, uint64_t *value)
1678*7c478bd9Sstevel@tonic-gate {
1679*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1680*7c478bd9Sstevel@tonic-gate 
1681*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (*value), S_READ);
1682*7c478bd9Sstevel@tonic-gate 	ret = fuword64(addr, value);
1683*7c478bd9Sstevel@tonic-gate 	if (watched)
1684*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (*value), S_READ);
1685*7c478bd9Sstevel@tonic-gate 
1686*7c478bd9Sstevel@tonic-gate 	return (ret);
1687*7c478bd9Sstevel@tonic-gate }
1688*7c478bd9Sstevel@tonic-gate #endif
1689*7c478bd9Sstevel@tonic-gate 
1690*7c478bd9Sstevel@tonic-gate int
1691*7c478bd9Sstevel@tonic-gate fuword32_nowatch(const void *addr, uint32_t *value)
1692*7c478bd9Sstevel@tonic-gate {
1693*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (*value), S_READ);
1696*7c478bd9Sstevel@tonic-gate 	ret = fuword32(addr, value);
1697*7c478bd9Sstevel@tonic-gate 	if (watched)
1698*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (*value), S_READ);
1699*7c478bd9Sstevel@tonic-gate 
1700*7c478bd9Sstevel@tonic-gate 	return (ret);
1701*7c478bd9Sstevel@tonic-gate }
1702*7c478bd9Sstevel@tonic-gate 
1703*7c478bd9Sstevel@tonic-gate #ifdef _LP64
1704*7c478bd9Sstevel@tonic-gate int
1705*7c478bd9Sstevel@tonic-gate suword64_nowatch(void *addr, uint64_t value)
1706*7c478bd9Sstevel@tonic-gate {
1707*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1708*7c478bd9Sstevel@tonic-gate 
1709*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (value), S_WRITE);
1710*7c478bd9Sstevel@tonic-gate 	ret = suword64(addr, value);
1711*7c478bd9Sstevel@tonic-gate 	if (watched)
1712*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (value), S_WRITE);
1713*7c478bd9Sstevel@tonic-gate 
1714*7c478bd9Sstevel@tonic-gate 	return (ret);
1715*7c478bd9Sstevel@tonic-gate }
1716*7c478bd9Sstevel@tonic-gate #endif
1717*7c478bd9Sstevel@tonic-gate 
1718*7c478bd9Sstevel@tonic-gate int
1719*7c478bd9Sstevel@tonic-gate suword32_nowatch(void *addr, uint32_t value)
1720*7c478bd9Sstevel@tonic-gate {
1721*7c478bd9Sstevel@tonic-gate 	int watched, ret;
1722*7c478bd9Sstevel@tonic-gate 
1723*7c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (value), S_WRITE);
1724*7c478bd9Sstevel@tonic-gate 	ret = suword32(addr, value);
1725*7c478bd9Sstevel@tonic-gate 	if (watched)
1726*7c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (value), S_WRITE);
1727*7c478bd9Sstevel@tonic-gate 
1728*7c478bd9Sstevel@tonic-gate 	return (ret);
1729*7c478bd9Sstevel@tonic-gate }
1730*7c478bd9Sstevel@tonic-gate 
1731*7c478bd9Sstevel@tonic-gate int
1732*7c478bd9Sstevel@tonic-gate watch_disable_addr(const void *addr, size_t len, enum seg_rw rw)
1733*7c478bd9Sstevel@tonic-gate {
1734*7c478bd9Sstevel@tonic-gate 	if (pr_watch_active(curproc))
1735*7c478bd9Sstevel@tonic-gate 		return (pr_mappage((caddr_t)addr, len, rw, 1));
1736*7c478bd9Sstevel@tonic-gate 	return (0);
1737*7c478bd9Sstevel@tonic-gate }
1738*7c478bd9Sstevel@tonic-gate 
1739*7c478bd9Sstevel@tonic-gate void
1740*7c478bd9Sstevel@tonic-gate watch_enable_addr(const void *addr, size_t len, enum seg_rw rw)
1741*7c478bd9Sstevel@tonic-gate {
1742*7c478bd9Sstevel@tonic-gate 	if (pr_watch_active(curproc))
1743*7c478bd9Sstevel@tonic-gate 		pr_unmappage((caddr_t)addr, len, rw, 1);
1744*7c478bd9Sstevel@tonic-gate }
1745