xref: /titanic_44/usr/src/uts/common/os/watchpoint.c (revision fd435bccec40cb841f712413108d8577422fd9ab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
297c478bd9Sstevel@tonic-gate #include <sys/param.h>
307c478bd9Sstevel@tonic-gate #include <sys/cred.h>
317c478bd9Sstevel@tonic-gate #include <sys/debug.h>
327c478bd9Sstevel@tonic-gate #include <sys/inline.h>
337c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
347c478bd9Sstevel@tonic-gate #include <sys/proc.h>
357c478bd9Sstevel@tonic-gate #include <sys/regset.h>
367c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
377c478bd9Sstevel@tonic-gate #include <sys/systm.h>
387c478bd9Sstevel@tonic-gate #include <sys/prsystm.h>
397c478bd9Sstevel@tonic-gate #include <sys/buf.h>
407c478bd9Sstevel@tonic-gate #include <sys/signal.h>
417c478bd9Sstevel@tonic-gate #include <sys/user.h>
427c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate #include <sys/fault.h>
457c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
467c478bd9Sstevel@tonic-gate #include <sys/procfs.h>
477c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
487c478bd9Sstevel@tonic-gate #include <sys/stack.h>
497c478bd9Sstevel@tonic-gate #include <sys/watchpoint.h>
507c478bd9Sstevel@tonic-gate #include <sys/copyops.h>
517c478bd9Sstevel@tonic-gate #include <sys/schedctl.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include <sys/mman.h>
547c478bd9Sstevel@tonic-gate #include <vm/as.h>
557c478bd9Sstevel@tonic-gate #include <vm/seg.h>
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Copy ops vector for watchpoints.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate static int	watch_copyin(const void *, void *, size_t);
617c478bd9Sstevel@tonic-gate static int	watch_xcopyin(const void *, void *, size_t);
627c478bd9Sstevel@tonic-gate static int	watch_copyout(const void *, void *, size_t);
637c478bd9Sstevel@tonic-gate static int	watch_xcopyout(const void *, void *, size_t);
647c478bd9Sstevel@tonic-gate static int	watch_copyinstr(const char *, char *, size_t, size_t *);
657c478bd9Sstevel@tonic-gate static int	watch_copyoutstr(const char *, char *, size_t, size_t *);
667c478bd9Sstevel@tonic-gate static int	watch_fuword8(const void *, uint8_t *);
677c478bd9Sstevel@tonic-gate static int	watch_fuword16(const void *, uint16_t *);
687c478bd9Sstevel@tonic-gate static int	watch_fuword32(const void *, uint32_t *);
697c478bd9Sstevel@tonic-gate static int	watch_suword8(void *, uint8_t);
707c478bd9Sstevel@tonic-gate static int	watch_suword16(void *, uint16_t);
717c478bd9Sstevel@tonic-gate static int	watch_suword32(void *, uint32_t);
727c478bd9Sstevel@tonic-gate static int	watch_physio(int (*)(struct buf *), struct buf *,
737c478bd9Sstevel@tonic-gate     dev_t, int, void (*)(struct buf *), struct uio *);
747c478bd9Sstevel@tonic-gate #ifdef _LP64
757c478bd9Sstevel@tonic-gate static int	watch_fuword64(const void *, uint64_t *);
767c478bd9Sstevel@tonic-gate static int	watch_suword64(void *, uint64_t);
777c478bd9Sstevel@tonic-gate #endif
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate struct copyops watch_copyops = {
807c478bd9Sstevel@tonic-gate 	watch_copyin,
817c478bd9Sstevel@tonic-gate 	watch_xcopyin,
827c478bd9Sstevel@tonic-gate 	watch_copyout,
837c478bd9Sstevel@tonic-gate 	watch_xcopyout,
847c478bd9Sstevel@tonic-gate 	watch_copyinstr,
857c478bd9Sstevel@tonic-gate 	watch_copyoutstr,
867c478bd9Sstevel@tonic-gate 	watch_fuword8,
877c478bd9Sstevel@tonic-gate 	watch_fuword16,
887c478bd9Sstevel@tonic-gate 	watch_fuword32,
897c478bd9Sstevel@tonic-gate #ifdef _LP64
907c478bd9Sstevel@tonic-gate 	watch_fuword64,
917c478bd9Sstevel@tonic-gate #else
927c478bd9Sstevel@tonic-gate 	NULL,
937c478bd9Sstevel@tonic-gate #endif
947c478bd9Sstevel@tonic-gate 	watch_suword8,
957c478bd9Sstevel@tonic-gate 	watch_suword16,
967c478bd9Sstevel@tonic-gate 	watch_suword32,
977c478bd9Sstevel@tonic-gate #ifdef _LP64
987c478bd9Sstevel@tonic-gate 	watch_suword64,
997c478bd9Sstevel@tonic-gate #else
1007c478bd9Sstevel@tonic-gate 	NULL,
1017c478bd9Sstevel@tonic-gate #endif
1027c478bd9Sstevel@tonic-gate 	watch_physio
1037c478bd9Sstevel@tonic-gate };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Map the 'rw' argument to a protection flag.
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate static int
rw_to_prot(enum seg_rw rw)1097c478bd9Sstevel@tonic-gate rw_to_prot(enum seg_rw rw)
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	switch (rw) {
1127c478bd9Sstevel@tonic-gate 	case S_EXEC:
1137c478bd9Sstevel@tonic-gate 		return (PROT_EXEC);
1147c478bd9Sstevel@tonic-gate 	case S_READ:
1157c478bd9Sstevel@tonic-gate 		return (PROT_READ);
1167c478bd9Sstevel@tonic-gate 	case S_WRITE:
1177c478bd9Sstevel@tonic-gate 		return (PROT_WRITE);
1187c478bd9Sstevel@tonic-gate 	default:
1197c478bd9Sstevel@tonic-gate 		return (PROT_NONE);	/* can't happen */
1207c478bd9Sstevel@tonic-gate 	}
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /*
1247c478bd9Sstevel@tonic-gate  * Map the 'rw' argument to an index into an array of exec/write/read things.
1257c478bd9Sstevel@tonic-gate  * The index follows the precedence order:  exec .. write .. read
1267c478bd9Sstevel@tonic-gate  */
1277c478bd9Sstevel@tonic-gate static int
rw_to_index(enum seg_rw rw)1287c478bd9Sstevel@tonic-gate rw_to_index(enum seg_rw rw)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate 	switch (rw) {
1317c478bd9Sstevel@tonic-gate 	default:	/* default case "can't happen" */
1327c478bd9Sstevel@tonic-gate 	case S_EXEC:
1337c478bd9Sstevel@tonic-gate 		return (0);
1347c478bd9Sstevel@tonic-gate 	case S_WRITE:
1357c478bd9Sstevel@tonic-gate 		return (1);
1367c478bd9Sstevel@tonic-gate 	case S_READ:
1377c478bd9Sstevel@tonic-gate 		return (2);
1387c478bd9Sstevel@tonic-gate 	}
1397c478bd9Sstevel@tonic-gate }
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /*
1427c478bd9Sstevel@tonic-gate  * Map an index back to a seg_rw.
1437c478bd9Sstevel@tonic-gate  */
1447c478bd9Sstevel@tonic-gate static enum seg_rw S_rw[4] = {
1457c478bd9Sstevel@tonic-gate 	S_EXEC,
1467c478bd9Sstevel@tonic-gate 	S_WRITE,
1477c478bd9Sstevel@tonic-gate 	S_READ,
1487c478bd9Sstevel@tonic-gate 	S_READ,
1497c478bd9Sstevel@tonic-gate };
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate #define	X	0
1527c478bd9Sstevel@tonic-gate #define	W	1
1537c478bd9Sstevel@tonic-gate #define	R	2
1547c478bd9Sstevel@tonic-gate #define	sum(a)	(a[X] + a[W] + a[R])
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate  * Common code for pr_mappage() and pr_unmappage().
1587c478bd9Sstevel@tonic-gate  */
1597c478bd9Sstevel@tonic-gate static int
pr_do_mappage(caddr_t addr,size_t size,int mapin,enum seg_rw rw,int kernel)1607c478bd9Sstevel@tonic-gate pr_do_mappage(caddr_t addr, size_t size, int mapin, enum seg_rw rw, int kernel)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
1637c478bd9Sstevel@tonic-gate 	struct as *as = p->p_as;
1647c478bd9Sstevel@tonic-gate 	char *eaddr = addr + size;
1657c478bd9Sstevel@tonic-gate 	int prot_rw = rw_to_prot(rw);
1667c478bd9Sstevel@tonic-gate 	int xrw = rw_to_index(rw);
1677c478bd9Sstevel@tonic-gate 	int rv = 0;
1687c478bd9Sstevel@tonic-gate 	struct watched_page *pwp;
1697c478bd9Sstevel@tonic-gate 	struct watched_page tpw;
1707c478bd9Sstevel@tonic-gate 	avl_index_t where;
1717c478bd9Sstevel@tonic-gate 	uint_t prot;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	ASSERT(as != &kas);
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate startover:
1767c478bd9Sstevel@tonic-gate 	ASSERT(rv == 0);
1777c478bd9Sstevel@tonic-gate 	if (avl_numnodes(&as->a_wpage) == 0)
1787c478bd9Sstevel@tonic-gate 		return (0);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	/*
1817c478bd9Sstevel@tonic-gate 	 * as->a_wpage can only be changed while the process is totally stopped.
1827c478bd9Sstevel@tonic-gate 	 * Don't grab p_lock here.  Holding p_lock while grabbing the address
1837c478bd9Sstevel@tonic-gate 	 * space lock leads to deadlocks with the clock thread.  Note that if an
1847c478bd9Sstevel@tonic-gate 	 * as_fault() is servicing a fault to a watched page on behalf of an
1857c478bd9Sstevel@tonic-gate 	 * XHAT provider, watchpoint will be temporarily cleared (and wp_prot
1867c478bd9Sstevel@tonic-gate 	 * will be set to wp_oprot).  Since this is done while holding as writer
1877c478bd9Sstevel@tonic-gate 	 * lock, we need to grab as lock (reader lock is good enough).
1887c478bd9Sstevel@tonic-gate 	 *
1897c478bd9Sstevel@tonic-gate 	 * p_maplock prevents simultaneous execution of this function.  Under
1907c478bd9Sstevel@tonic-gate 	 * normal circumstances, holdwatch() will stop all other threads, so the
1917c478bd9Sstevel@tonic-gate 	 * lock isn't really needed.  But there may be multiple threads within
1927c478bd9Sstevel@tonic-gate 	 * stop() when SWATCHOK is set, so we need to handle multiple threads
1937c478bd9Sstevel@tonic-gate 	 * at once.  See holdwatch() for the details of this dance.
1947c478bd9Sstevel@tonic-gate 	 */
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_maplock);
197*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_ENTER(as, RW_READER);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
2007c478bd9Sstevel@tonic-gate 	if ((pwp = avl_find(&as->a_wpage, &tpw, &where)) == NULL)
2017c478bd9Sstevel@tonic-gate 		pwp = avl_nearest(&as->a_wpage, where, AVL_AFTER);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	for (; pwp != NULL && pwp->wp_vaddr < eaddr;
2047c478bd9Sstevel@tonic-gate 	    pwp = AVL_NEXT(&as->a_wpage, pwp)) {
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 		/*
2077c478bd9Sstevel@tonic-gate 		 * If the requested protection has not been
2087c478bd9Sstevel@tonic-gate 		 * removed, we need not remap this page.
2097c478bd9Sstevel@tonic-gate 		 */
2107c478bd9Sstevel@tonic-gate 		prot = pwp->wp_prot;
2117c478bd9Sstevel@tonic-gate 		if (kernel || (prot & PROT_USER))
2127c478bd9Sstevel@tonic-gate 			if (prot & prot_rw)
2137c478bd9Sstevel@tonic-gate 				continue;
2147c478bd9Sstevel@tonic-gate 		/*
2157c478bd9Sstevel@tonic-gate 		 * If the requested access does not exist in the page's
2167c478bd9Sstevel@tonic-gate 		 * original protections, we need not remap this page.
2177c478bd9Sstevel@tonic-gate 		 * If the page does not exist yet, we can't test it.
2187c478bd9Sstevel@tonic-gate 		 */
2197c478bd9Sstevel@tonic-gate 		if ((prot = pwp->wp_oprot) != 0) {
2207c478bd9Sstevel@tonic-gate 			if (!(kernel || (prot & PROT_USER)))
2217c478bd9Sstevel@tonic-gate 				continue;
2227c478bd9Sstevel@tonic-gate 			if (!(prot & prot_rw))
2237c478bd9Sstevel@tonic-gate 				continue;
2247c478bd9Sstevel@tonic-gate 		}
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 		if (mapin) {
2277c478bd9Sstevel@tonic-gate 			/*
2287c478bd9Sstevel@tonic-gate 			 * Before mapping the page in, ensure that
2297c478bd9Sstevel@tonic-gate 			 * all other lwps are held in the kernel.
2307c478bd9Sstevel@tonic-gate 			 */
2317c478bd9Sstevel@tonic-gate 			if (p->p_mapcnt == 0) {
2327c478bd9Sstevel@tonic-gate 				/*
2337c478bd9Sstevel@tonic-gate 				 * Release as lock while in holdwatch()
2347c478bd9Sstevel@tonic-gate 				 * in case other threads need to grab it.
2357c478bd9Sstevel@tonic-gate 				 */
236*fd435bccSJosef 'Jeff' Sipek 				AS_LOCK_EXIT(as);
2377c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_maplock);
2387c478bd9Sstevel@tonic-gate 				if (holdwatch() != 0) {
2397c478bd9Sstevel@tonic-gate 					/*
2407c478bd9Sstevel@tonic-gate 					 * We stopped in holdwatch().
2417c478bd9Sstevel@tonic-gate 					 * Start all over again because the
2427c478bd9Sstevel@tonic-gate 					 * watched page list may have changed.
2437c478bd9Sstevel@tonic-gate 					 */
2447c478bd9Sstevel@tonic-gate 					goto startover;
2457c478bd9Sstevel@tonic-gate 				}
2467c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_maplock);
247*fd435bccSJosef 'Jeff' Sipek 				AS_LOCK_ENTER(as, RW_READER);
2487c478bd9Sstevel@tonic-gate 			}
2497c478bd9Sstevel@tonic-gate 			p->p_mapcnt++;
2507c478bd9Sstevel@tonic-gate 		}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		addr = pwp->wp_vaddr;
2537c478bd9Sstevel@tonic-gate 		rv++;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		prot = pwp->wp_prot;
2567c478bd9Sstevel@tonic-gate 		if (mapin) {
2577c478bd9Sstevel@tonic-gate 			if (kernel)
2587c478bd9Sstevel@tonic-gate 				pwp->wp_kmap[xrw]++;
2597c478bd9Sstevel@tonic-gate 			else
2607c478bd9Sstevel@tonic-gate 				pwp->wp_umap[xrw]++;
2617c478bd9Sstevel@tonic-gate 			pwp->wp_flags |= WP_NOWATCH;
2627c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[X] + pwp->wp_umap[X])
2637c478bd9Sstevel@tonic-gate 				/* cannot have exec-only protection */
2647c478bd9Sstevel@tonic-gate 				prot |= PROT_READ|PROT_EXEC;
2657c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[R] + pwp->wp_umap[R])
2667c478bd9Sstevel@tonic-gate 				prot |= PROT_READ;
2677c478bd9Sstevel@tonic-gate 			if (pwp->wp_kmap[W] + pwp->wp_umap[W])
2687c478bd9Sstevel@tonic-gate 				/* cannot have write-only protection */
2697c478bd9Sstevel@tonic-gate 				prot |= PROT_READ|PROT_WRITE;
2707c478bd9Sstevel@tonic-gate #if 0	/* damned broken mmu feature! */
2717c478bd9Sstevel@tonic-gate 			if (sum(pwp->wp_umap) == 0)
2727c478bd9Sstevel@tonic-gate 				prot &= ~PROT_USER;
2737c478bd9Sstevel@tonic-gate #endif
2747c478bd9Sstevel@tonic-gate 		} else {
2757c478bd9Sstevel@tonic-gate 			ASSERT(pwp->wp_flags & WP_NOWATCH);
2767c478bd9Sstevel@tonic-gate 			if (kernel) {
2777c478bd9Sstevel@tonic-gate 				ASSERT(pwp->wp_kmap[xrw] != 0);
2787c478bd9Sstevel@tonic-gate 				--pwp->wp_kmap[xrw];
2797c478bd9Sstevel@tonic-gate 			} else {
2807c478bd9Sstevel@tonic-gate 				ASSERT(pwp->wp_umap[xrw] != 0);
2817c478bd9Sstevel@tonic-gate 				--pwp->wp_umap[xrw];
2827c478bd9Sstevel@tonic-gate 			}
2837c478bd9Sstevel@tonic-gate 			if (sum(pwp->wp_kmap) + sum(pwp->wp_umap) == 0)
2847c478bd9Sstevel@tonic-gate 				pwp->wp_flags &= ~WP_NOWATCH;
2857c478bd9Sstevel@tonic-gate 			else {
2867c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[X] + pwp->wp_umap[X])
2877c478bd9Sstevel@tonic-gate 					/* cannot have exec-only protection */
2887c478bd9Sstevel@tonic-gate 					prot |= PROT_READ|PROT_EXEC;
2897c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[R] + pwp->wp_umap[R])
2907c478bd9Sstevel@tonic-gate 					prot |= PROT_READ;
2917c478bd9Sstevel@tonic-gate 				if (pwp->wp_kmap[W] + pwp->wp_umap[W])
2927c478bd9Sstevel@tonic-gate 					/* cannot have write-only protection */
2937c478bd9Sstevel@tonic-gate 					prot |= PROT_READ|PROT_WRITE;
2947c478bd9Sstevel@tonic-gate #if 0	/* damned broken mmu feature! */
2957c478bd9Sstevel@tonic-gate 				if (sum(pwp->wp_umap) == 0)
2967c478bd9Sstevel@tonic-gate 					prot &= ~PROT_USER;
2977c478bd9Sstevel@tonic-gate #endif
2987c478bd9Sstevel@tonic-gate 			}
2997c478bd9Sstevel@tonic-gate 		}
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 		if (pwp->wp_oprot != 0) {	/* if page exists */
3037c478bd9Sstevel@tonic-gate 			struct seg *seg;
3047c478bd9Sstevel@tonic-gate 			uint_t oprot;
3057c478bd9Sstevel@tonic-gate 			int err, retrycnt = 0;
3067c478bd9Sstevel@tonic-gate 
307*fd435bccSJosef 'Jeff' Sipek 			AS_LOCK_EXIT(as);
308*fd435bccSJosef 'Jeff' Sipek 			AS_LOCK_ENTER(as, RW_WRITER);
3097c478bd9Sstevel@tonic-gate 		retry:
3107c478bd9Sstevel@tonic-gate 			seg = as_segat(as, addr);
3117c478bd9Sstevel@tonic-gate 			ASSERT(seg != NULL);
3127c478bd9Sstevel@tonic-gate 			SEGOP_GETPROT(seg, addr, 0, &oprot);
3137c478bd9Sstevel@tonic-gate 			if (prot != oprot) {
3147c478bd9Sstevel@tonic-gate 				err = SEGOP_SETPROT(seg, addr, PAGESIZE, prot);
3157c478bd9Sstevel@tonic-gate 				if (err == IE_RETRY) {
3167c478bd9Sstevel@tonic-gate 					ASSERT(retrycnt == 0);
3177c478bd9Sstevel@tonic-gate 					retrycnt++;
3187c478bd9Sstevel@tonic-gate 					goto retry;
3197c478bd9Sstevel@tonic-gate 				}
3207c478bd9Sstevel@tonic-gate 			}
321*fd435bccSJosef 'Jeff' Sipek 			AS_LOCK_EXIT(as);
3227c478bd9Sstevel@tonic-gate 		} else
323*fd435bccSJosef 'Jeff' Sipek 			AS_LOCK_EXIT(as);
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 		/*
3267c478bd9Sstevel@tonic-gate 		 * When all pages are mapped back to their normal state,
3277c478bd9Sstevel@tonic-gate 		 * continue the other lwps.
3287c478bd9Sstevel@tonic-gate 		 */
3297c478bd9Sstevel@tonic-gate 		if (!mapin) {
3307c478bd9Sstevel@tonic-gate 			ASSERT(p->p_mapcnt > 0);
3317c478bd9Sstevel@tonic-gate 			p->p_mapcnt--;
3327c478bd9Sstevel@tonic-gate 			if (p->p_mapcnt == 0) {
3337c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_maplock);
3347c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_lock);
3357c478bd9Sstevel@tonic-gate 				continuelwps(p);
3367c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
3377c478bd9Sstevel@tonic-gate 				mutex_enter(&p->p_maplock);
3387c478bd9Sstevel@tonic-gate 			}
3397c478bd9Sstevel@tonic-gate 		}
3407c478bd9Sstevel@tonic-gate 
341*fd435bccSJosef 'Jeff' Sipek 		AS_LOCK_ENTER(as, RW_READER);
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
344*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_EXIT(as);
3457c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_maplock);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	return (rv);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * Restore the original page protections on an address range.
3527c478bd9Sstevel@tonic-gate  * If 'kernel' is non-zero, just do it for the kernel.
3537c478bd9Sstevel@tonic-gate  * pr_mappage() returns non-zero if it actually changed anything.
3547c478bd9Sstevel@tonic-gate  *
3557c478bd9Sstevel@tonic-gate  * pr_mappage() and pr_unmappage() must be executed in matched pairs,
3567c478bd9Sstevel@tonic-gate  * but pairs may be nested within other pairs.  The reference counts
3577c478bd9Sstevel@tonic-gate  * sort it all out.  See pr_do_mappage(), above.
3587c478bd9Sstevel@tonic-gate  */
3597c478bd9Sstevel@tonic-gate static int
pr_mappage(const caddr_t addr,size_t size,enum seg_rw rw,int kernel)3607c478bd9Sstevel@tonic-gate pr_mappage(const caddr_t addr, size_t size, enum seg_rw rw, int kernel)
3617c478bd9Sstevel@tonic-gate {
3627c478bd9Sstevel@tonic-gate 	return (pr_do_mappage(addr, size, 1, rw, kernel));
3637c478bd9Sstevel@tonic-gate }
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate /*
3667c478bd9Sstevel@tonic-gate  * Set the modified page protections on a watched page.
3677c478bd9Sstevel@tonic-gate  * Inverse of pr_mappage().
3687c478bd9Sstevel@tonic-gate  * Needs to be called only if pr_mappage() returned non-zero.
3697c478bd9Sstevel@tonic-gate  */
3707c478bd9Sstevel@tonic-gate static void
pr_unmappage(const caddr_t addr,size_t size,enum seg_rw rw,int kernel)3717c478bd9Sstevel@tonic-gate pr_unmappage(const caddr_t addr, size_t size, enum seg_rw rw, int kernel)
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate 	(void) pr_do_mappage(addr, size, 0, rw, kernel);
3747c478bd9Sstevel@tonic-gate }
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate /*
3777c478bd9Sstevel@tonic-gate  * Function called by an lwp after it resumes from stop().
3787c478bd9Sstevel@tonic-gate  */
3797c478bd9Sstevel@tonic-gate void
setallwatch(void)3807c478bd9Sstevel@tonic-gate setallwatch(void)
3817c478bd9Sstevel@tonic-gate {
3827c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
3837c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
3847c478bd9Sstevel@tonic-gate 	struct watched_page *pwp, *next;
3857c478bd9Sstevel@tonic-gate 	struct seg *seg;
3867c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
3877c478bd9Sstevel@tonic-gate 	uint_t prot;
3887c478bd9Sstevel@tonic-gate 	int err, retrycnt;
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	if (p->p_wprot == NULL)
3917c478bd9Sstevel@tonic-gate 		return;
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
3947c478bd9Sstevel@tonic-gate 
395*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_ENTER(as, RW_WRITER);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	pwp = p->p_wprot;
3987c478bd9Sstevel@tonic-gate 	while (pwp != NULL) {
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 		vaddr = pwp->wp_vaddr;
4017c478bd9Sstevel@tonic-gate 		retrycnt = 0;
4027c478bd9Sstevel@tonic-gate 	retry:
4037c478bd9Sstevel@tonic-gate 		ASSERT(pwp->wp_flags & WP_SETPROT);
4047c478bd9Sstevel@tonic-gate 		if ((seg = as_segat(as, vaddr)) != NULL &&
4057c478bd9Sstevel@tonic-gate 		    !(pwp->wp_flags & WP_NOWATCH)) {
4067c478bd9Sstevel@tonic-gate 			prot = pwp->wp_prot;
4077c478bd9Sstevel@tonic-gate 			err = SEGOP_SETPROT(seg, vaddr, PAGESIZE, prot);
4087c478bd9Sstevel@tonic-gate 			if (err == IE_RETRY) {
4097c478bd9Sstevel@tonic-gate 				ASSERT(retrycnt == 0);
4107c478bd9Sstevel@tonic-gate 				retrycnt++;
4117c478bd9Sstevel@tonic-gate 				goto retry;
4127c478bd9Sstevel@tonic-gate 			}
4137c478bd9Sstevel@tonic-gate 		}
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 		next = pwp->wp_list;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		if (pwp->wp_read + pwp->wp_write + pwp->wp_exec == 0) {
4187c478bd9Sstevel@tonic-gate 			/*
4197c478bd9Sstevel@tonic-gate 			 * No watched areas remain in this page.
4207c478bd9Sstevel@tonic-gate 			 * Free the watched_page structure.
4217c478bd9Sstevel@tonic-gate 			 */
4227c478bd9Sstevel@tonic-gate 			avl_remove(&as->a_wpage, pwp);
4237c478bd9Sstevel@tonic-gate 			kmem_free(pwp, sizeof (struct watched_page));
4247c478bd9Sstevel@tonic-gate 		} else {
4257c478bd9Sstevel@tonic-gate 			pwp->wp_flags &= ~WP_SETPROT;
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		pwp = next;
4297c478bd9Sstevel@tonic-gate 	}
4307c478bd9Sstevel@tonic-gate 	p->p_wprot = NULL;
4317c478bd9Sstevel@tonic-gate 
432*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_EXIT(as);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate /* Must be called with as lock held */
4387c478bd9Sstevel@tonic-gate int
pr_is_watchpage_as(caddr_t addr,enum seg_rw rw,struct as * as)4397c478bd9Sstevel@tonic-gate pr_is_watchpage_as(caddr_t addr, enum seg_rw rw, struct as *as)
4407c478bd9Sstevel@tonic-gate {
4417c478bd9Sstevel@tonic-gate 	register struct watched_page *pwp;
4427c478bd9Sstevel@tonic-gate 	struct watched_page tpw;
4437c478bd9Sstevel@tonic-gate 	uint_t prot;
4447c478bd9Sstevel@tonic-gate 	int rv = 0;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	switch (rw) {
4477c478bd9Sstevel@tonic-gate 	case S_READ:
4487c478bd9Sstevel@tonic-gate 	case S_WRITE:
4497c478bd9Sstevel@tonic-gate 	case S_EXEC:
4507c478bd9Sstevel@tonic-gate 		break;
4517c478bd9Sstevel@tonic-gate 	default:
4527c478bd9Sstevel@tonic-gate 		return (0);
4537c478bd9Sstevel@tonic-gate 	}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	/*
4567c478bd9Sstevel@tonic-gate 	 * as->a_wpage can only be modified while the process is totally
4577c478bd9Sstevel@tonic-gate 	 * stopped.  We need, and should use, no locks here.
4587c478bd9Sstevel@tonic-gate 	 */
4597c478bd9Sstevel@tonic-gate 	if (as != &kas && avl_numnodes(&as->a_wpage) != 0) {
4607c478bd9Sstevel@tonic-gate 		tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
4617c478bd9Sstevel@tonic-gate 		pwp = avl_find(&as->a_wpage, &tpw, NULL);
4627c478bd9Sstevel@tonic-gate 		if (pwp != NULL) {
4637c478bd9Sstevel@tonic-gate 			ASSERT(addr >= pwp->wp_vaddr &&
4647c478bd9Sstevel@tonic-gate 			    addr < pwp->wp_vaddr + PAGESIZE);
4657c478bd9Sstevel@tonic-gate 			if (pwp->wp_oprot != 0) {
4667c478bd9Sstevel@tonic-gate 				prot = pwp->wp_prot;
4677c478bd9Sstevel@tonic-gate 				switch (rw) {
4687c478bd9Sstevel@tonic-gate 				case S_READ:
4697c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_READ))
4707c478bd9Sstevel@tonic-gate 					    != (PROT_USER|PROT_READ));
4717c478bd9Sstevel@tonic-gate 					break;
4727c478bd9Sstevel@tonic-gate 				case S_WRITE:
4737c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_WRITE))
4747c478bd9Sstevel@tonic-gate 					    != (PROT_USER|PROT_WRITE));
4757c478bd9Sstevel@tonic-gate 					break;
4767c478bd9Sstevel@tonic-gate 				case S_EXEC:
4777c478bd9Sstevel@tonic-gate 					rv = ((prot & (PROT_USER|PROT_EXEC))
4787c478bd9Sstevel@tonic-gate 					    != (PROT_USER|PROT_EXEC));
4797c478bd9Sstevel@tonic-gate 					break;
4807c478bd9Sstevel@tonic-gate 				default:
4817c478bd9Sstevel@tonic-gate 					/* can't happen! */
4827c478bd9Sstevel@tonic-gate 					break;
4837c478bd9Sstevel@tonic-gate 				}
4847c478bd9Sstevel@tonic-gate 			}
4857c478bd9Sstevel@tonic-gate 		}
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	return (rv);
4897c478bd9Sstevel@tonic-gate }
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate /*
4937c478bd9Sstevel@tonic-gate  * trap() calls here to determine if a fault is in a watched page.
4947c478bd9Sstevel@tonic-gate  * We return nonzero if this is true and the load/store would fail.
4957c478bd9Sstevel@tonic-gate  */
4967c478bd9Sstevel@tonic-gate int
pr_is_watchpage(caddr_t addr,enum seg_rw rw)4977c478bd9Sstevel@tonic-gate pr_is_watchpage(caddr_t addr, enum seg_rw rw)
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
5007c478bd9Sstevel@tonic-gate 	int rv;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	if ((as == &kas) || avl_numnodes(&as->a_wpage) == 0)
5037c478bd9Sstevel@tonic-gate 		return (0);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/* Grab the lock because of XHAT (see comment in pr_mappage()) */
506*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_ENTER(as, RW_READER);
5077c478bd9Sstevel@tonic-gate 	rv = pr_is_watchpage_as(addr, rw, as);
508*fd435bccSJosef 'Jeff' Sipek 	AS_LOCK_EXIT(as);
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	return (rv);
5117c478bd9Sstevel@tonic-gate }
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate /*
5167c478bd9Sstevel@tonic-gate  * trap() calls here to determine if a fault is a watchpoint.
5177c478bd9Sstevel@tonic-gate  */
5187c478bd9Sstevel@tonic-gate int
pr_is_watchpoint(caddr_t * paddr,int * pta,size_t size,size_t * plen,enum seg_rw rw)5197c478bd9Sstevel@tonic-gate pr_is_watchpoint(caddr_t *paddr, int *pta, size_t size, size_t *plen,
5207c478bd9Sstevel@tonic-gate 	enum seg_rw rw)
5217c478bd9Sstevel@tonic-gate {
5227c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
5237c478bd9Sstevel@tonic-gate 	caddr_t addr = *paddr;
5247c478bd9Sstevel@tonic-gate 	caddr_t eaddr = addr + size;
5257c478bd9Sstevel@tonic-gate 	register struct watched_area *pwa;
5267c478bd9Sstevel@tonic-gate 	struct watched_area twa;
5277c478bd9Sstevel@tonic-gate 	int rv = 0;
5287c478bd9Sstevel@tonic-gate 	int ta = 0;
5297c478bd9Sstevel@tonic-gate 	size_t len = 0;
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	switch (rw) {
5327c478bd9Sstevel@tonic-gate 	case S_READ:
5337c478bd9Sstevel@tonic-gate 	case S_WRITE:
5347c478bd9Sstevel@tonic-gate 	case S_EXEC:
5357c478bd9Sstevel@tonic-gate 		break;
5367c478bd9Sstevel@tonic-gate 	default:
5377c478bd9Sstevel@tonic-gate 		*pta = 0;
5387c478bd9Sstevel@tonic-gate 		return (0);
5397c478bd9Sstevel@tonic-gate 	}
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	/*
5427c478bd9Sstevel@tonic-gate 	 * p->p_warea is protected by p->p_lock.
5437c478bd9Sstevel@tonic-gate 	 */
5447c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/* BEGIN CSTYLED */
5477c478bd9Sstevel@tonic-gate 	/*
5487c478bd9Sstevel@tonic-gate 	 * This loop is somewhat complicated because the fault region can span
5497c478bd9Sstevel@tonic-gate 	 * multiple watched areas.  For example:
5507c478bd9Sstevel@tonic-gate 	 *
5517c478bd9Sstevel@tonic-gate 	 *            addr              eaddr
5527c478bd9Sstevel@tonic-gate 	 * 		+-----------------+
5537c478bd9Sstevel@tonic-gate 	 * 		| fault region    |
5547c478bd9Sstevel@tonic-gate 	 * 	+-------+--------+----+---+------------+
5557c478bd9Sstevel@tonic-gate 	 *      | prot not right |    | prot correct   |
5567c478bd9Sstevel@tonic-gate 	 *      +----------------+    +----------------+
5577c478bd9Sstevel@tonic-gate 	 *    wa_vaddr	      wa_eaddr
5587c478bd9Sstevel@tonic-gate 	 *    		      wa_vaddr		wa_eaddr
5597c478bd9Sstevel@tonic-gate 	 *
5607c478bd9Sstevel@tonic-gate 	 * We start at the area greater than or equal to the starting address.
5617c478bd9Sstevel@tonic-gate 	 * As long as some portion of the fault region overlaps the current
5627c478bd9Sstevel@tonic-gate 	 * area, we continue checking permissions until we find an appropriate
5637c478bd9Sstevel@tonic-gate 	 * match.
5647c478bd9Sstevel@tonic-gate 	 */
5657c478bd9Sstevel@tonic-gate 	/* END CSTYLED */
5667c478bd9Sstevel@tonic-gate 	twa.wa_vaddr = addr;
5677c478bd9Sstevel@tonic-gate 	twa.wa_eaddr = eaddr;
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	for (pwa = pr_find_watched_area(p, &twa, NULL);
5707c478bd9Sstevel@tonic-gate 	    pwa != NULL && eaddr > pwa->wa_vaddr && addr < pwa->wa_eaddr;
5717c478bd9Sstevel@tonic-gate 	    pwa = AVL_NEXT(&p->p_warea, pwa)) {
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		switch (rw) {
5747c478bd9Sstevel@tonic-gate 		case S_READ:
5757c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_READ)
5767c478bd9Sstevel@tonic-gate 				rv = TRAP_RWATCH;
5777c478bd9Sstevel@tonic-gate 			break;
5787c478bd9Sstevel@tonic-gate 		case S_WRITE:
5797c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_WRITE)
5807c478bd9Sstevel@tonic-gate 				rv = TRAP_WWATCH;
5817c478bd9Sstevel@tonic-gate 			break;
5827c478bd9Sstevel@tonic-gate 		case S_EXEC:
5837c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_EXEC)
5847c478bd9Sstevel@tonic-gate 				rv = TRAP_XWATCH;
5857c478bd9Sstevel@tonic-gate 			break;
5867c478bd9Sstevel@tonic-gate 		default:
5877c478bd9Sstevel@tonic-gate 			/* can't happen */
5887c478bd9Sstevel@tonic-gate 			break;
5897c478bd9Sstevel@tonic-gate 		}
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		/*
5927c478bd9Sstevel@tonic-gate 		 * If protections didn't match, check the next watched
5937c478bd9Sstevel@tonic-gate 		 * area
5947c478bd9Sstevel@tonic-gate 		 */
5957c478bd9Sstevel@tonic-gate 		if (rv != 0) {
5967c478bd9Sstevel@tonic-gate 			if (addr < pwa->wa_vaddr)
5977c478bd9Sstevel@tonic-gate 				addr = pwa->wa_vaddr;
5987c478bd9Sstevel@tonic-gate 			len = pwa->wa_eaddr - addr;
5997c478bd9Sstevel@tonic-gate 			if (pwa->wa_flags & WA_TRAPAFTER)
6007c478bd9Sstevel@tonic-gate 				ta = 1;
6017c478bd9Sstevel@tonic-gate 			break;
6027c478bd9Sstevel@tonic-gate 		}
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	*paddr = addr;
6087c478bd9Sstevel@tonic-gate 	*pta = ta;
6097c478bd9Sstevel@tonic-gate 	if (plen != NULL)
6107c478bd9Sstevel@tonic-gate 		*plen = len;
6117c478bd9Sstevel@tonic-gate 	return (rv);
6127c478bd9Sstevel@tonic-gate }
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate /*
6157c478bd9Sstevel@tonic-gate  * Set up to perform a single-step at user level for the
6167c478bd9Sstevel@tonic-gate  * case of a trapafter watchpoint.  Called from trap().
6177c478bd9Sstevel@tonic-gate  */
6187c478bd9Sstevel@tonic-gate void
do_watch_step(caddr_t vaddr,size_t sz,enum seg_rw rw,int watchcode,greg_t pc)6197c478bd9Sstevel@tonic-gate do_watch_step(caddr_t vaddr, size_t sz, enum seg_rw rw,
6207c478bd9Sstevel@tonic-gate 	int watchcode, greg_t pc)
6217c478bd9Sstevel@tonic-gate {
6227c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
6237c478bd9Sstevel@tonic-gate 	struct lwp_watch *pw = &lwp->lwp_watch[rw_to_index(rw)];
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	/*
6267c478bd9Sstevel@tonic-gate 	 * Check to see if we are already performing this special
6277c478bd9Sstevel@tonic-gate 	 * watchpoint single-step.  We must not do pr_mappage() twice.
6287c478bd9Sstevel@tonic-gate 	 */
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	/* special check for two read traps on the same instruction */
6317c478bd9Sstevel@tonic-gate 	if (rw == S_READ && pw->wpaddr != NULL &&
6327c478bd9Sstevel@tonic-gate 	    !(pw->wpaddr <= vaddr && vaddr < pw->wpaddr + pw->wpsize)) {
6337c478bd9Sstevel@tonic-gate 		ASSERT(lwp->lwp_watchtrap != 0);
6347c478bd9Sstevel@tonic-gate 		pw++;	/* use the extra S_READ struct */
6357c478bd9Sstevel@tonic-gate 	}
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	if (pw->wpaddr != NULL) {
6387c478bd9Sstevel@tonic-gate 		ASSERT(lwp->lwp_watchtrap != 0);
6397c478bd9Sstevel@tonic-gate 		ASSERT(pw->wpaddr <= vaddr && vaddr < pw->wpaddr + pw->wpsize);
6407c478bd9Sstevel@tonic-gate 		if (pw->wpcode == 0) {
6417c478bd9Sstevel@tonic-gate 			pw->wpcode = watchcode;
6427c478bd9Sstevel@tonic-gate 			pw->wppc = pc;
6437c478bd9Sstevel@tonic-gate 		}
6447c478bd9Sstevel@tonic-gate 	} else {
6457c478bd9Sstevel@tonic-gate 		int mapped = pr_mappage(vaddr, sz, rw, 0);
6467c478bd9Sstevel@tonic-gate 		prstep(lwp, 1);
6477c478bd9Sstevel@tonic-gate 		lwp->lwp_watchtrap = 1;
6487c478bd9Sstevel@tonic-gate 		pw->wpaddr = vaddr;
6497c478bd9Sstevel@tonic-gate 		pw->wpsize = sz;
6507c478bd9Sstevel@tonic-gate 		pw->wpcode = watchcode;
6517c478bd9Sstevel@tonic-gate 		pw->wpmapped = mapped;
6527c478bd9Sstevel@tonic-gate 		pw->wppc = pc;
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate /*
6577c478bd9Sstevel@tonic-gate  * Undo the effects of do_watch_step().
6587c478bd9Sstevel@tonic-gate  * Called from trap() after the single-step is finished.
6597c478bd9Sstevel@tonic-gate  * Also called from issig_forreal() and stop() with a NULL
6607c478bd9Sstevel@tonic-gate  * argument to avoid having these things set more than once.
6617c478bd9Sstevel@tonic-gate  */
6627c478bd9Sstevel@tonic-gate int
undo_watch_step(k_siginfo_t * sip)6637c478bd9Sstevel@tonic-gate undo_watch_step(k_siginfo_t *sip)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
6667c478bd9Sstevel@tonic-gate 	int fault = 0;
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	if (lwp->lwp_watchtrap) {
6697c478bd9Sstevel@tonic-gate 		struct lwp_watch *pw = lwp->lwp_watch;
6707c478bd9Sstevel@tonic-gate 		int i;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 		for (i = 0; i < 4; i++, pw++) {
6737c478bd9Sstevel@tonic-gate 			if (pw->wpaddr == NULL)
6747c478bd9Sstevel@tonic-gate 				continue;
6757c478bd9Sstevel@tonic-gate 			if (pw->wpmapped)
6767c478bd9Sstevel@tonic-gate 				pr_unmappage(pw->wpaddr, pw->wpsize, S_rw[i],
6777c478bd9Sstevel@tonic-gate 				    0);
6787c478bd9Sstevel@tonic-gate 			if (pw->wpcode != 0) {
6797c478bd9Sstevel@tonic-gate 				if (sip != NULL) {
6807c478bd9Sstevel@tonic-gate 					sip->si_signo = SIGTRAP;
6817c478bd9Sstevel@tonic-gate 					sip->si_code = pw->wpcode;
6827c478bd9Sstevel@tonic-gate 					sip->si_addr = pw->wpaddr;
6837c478bd9Sstevel@tonic-gate 					sip->si_trapafter = 1;
6847c478bd9Sstevel@tonic-gate 					sip->si_pc = (caddr_t)pw->wppc;
6857c478bd9Sstevel@tonic-gate 				}
6867c478bd9Sstevel@tonic-gate 				fault = FLTWATCH;
6877c478bd9Sstevel@tonic-gate 				pw->wpcode = 0;
6887c478bd9Sstevel@tonic-gate 			}
6897c478bd9Sstevel@tonic-gate 			pw->wpaddr = NULL;
6907c478bd9Sstevel@tonic-gate 			pw->wpsize = 0;
6917c478bd9Sstevel@tonic-gate 			pw->wpmapped = 0;
6927c478bd9Sstevel@tonic-gate 		}
6937c478bd9Sstevel@tonic-gate 		lwp->lwp_watchtrap = 0;
6947c478bd9Sstevel@tonic-gate 	}
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	return (fault);
6977c478bd9Sstevel@tonic-gate }
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate /*
7007c478bd9Sstevel@tonic-gate  * Handle a watchpoint that occurs while doing copyin()
7017c478bd9Sstevel@tonic-gate  * or copyout() in a system call.
7027c478bd9Sstevel@tonic-gate  * Return non-zero if the fault or signal is cleared
7037c478bd9Sstevel@tonic-gate  * by a debugger while the lwp is stopped.
7047c478bd9Sstevel@tonic-gate  */
7057c478bd9Sstevel@tonic-gate static int
sys_watchpoint(caddr_t addr,int watchcode,int ta)7067c478bd9Sstevel@tonic-gate sys_watchpoint(caddr_t addr, int watchcode, int ta)
7077c478bd9Sstevel@tonic-gate {
7087c478bd9Sstevel@tonic-gate 	extern greg_t getuserpc(void);	/* XXX header file */
7097c478bd9Sstevel@tonic-gate 	k_sigset_t smask;
7107c478bd9Sstevel@tonic-gate 	register proc_t *p = ttoproc(curthread);
7117c478bd9Sstevel@tonic-gate 	register klwp_t *lwp = ttolwp(curthread);
7127c478bd9Sstevel@tonic-gate 	register sigqueue_t *sqp;
7137c478bd9Sstevel@tonic-gate 	int rval;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	/* assert no locks are held */
7167c478bd9Sstevel@tonic-gate 	/* ASSERT(curthread->t_nlocks == 0); */
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
7197c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_signo = SIGTRAP;
7207c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_code = watchcode;
7217c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_addr = addr;
7227c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_trapafter = ta;
7237c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_pc = (caddr_t)getuserpc();
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/* this will be tested and cleared by the caller */
7287c478bd9Sstevel@tonic-gate 	lwp->lwp_sysabort = 0;
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	if (prismember(&p->p_fltmask, FLTWATCH)) {
7317c478bd9Sstevel@tonic-gate 		lwp->lwp_curflt = (uchar_t)FLTWATCH;
7327c478bd9Sstevel@tonic-gate 		lwp->lwp_siginfo = sqp->sq_info;
7337c478bd9Sstevel@tonic-gate 		stop(PR_FAULTED, FLTWATCH);
7347c478bd9Sstevel@tonic-gate 		if (lwp->lwp_curflt == 0) {
7357c478bd9Sstevel@tonic-gate 			mutex_exit(&p->p_lock);
7367c478bd9Sstevel@tonic-gate 			kmem_free(sqp, sizeof (sigqueue_t));
7377c478bd9Sstevel@tonic-gate 			return (1);
7387c478bd9Sstevel@tonic-gate 		}
7397c478bd9Sstevel@tonic-gate 		lwp->lwp_curflt = 0;
7407c478bd9Sstevel@tonic-gate 	}
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	/*
7437c478bd9Sstevel@tonic-gate 	 * post the SIGTRAP signal.
7447c478bd9Sstevel@tonic-gate 	 * Block all other signals so we only stop showing SIGTRAP.
7457c478bd9Sstevel@tonic-gate 	 */
7467c478bd9Sstevel@tonic-gate 	if (signal_is_blocked(curthread, SIGTRAP) ||
7477c478bd9Sstevel@tonic-gate 	    sigismember(&p->p_ignore, SIGTRAP)) {
7487c478bd9Sstevel@tonic-gate 		/* SIGTRAP is blocked or ignored, forget the rest. */
7497c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
7507c478bd9Sstevel@tonic-gate 		kmem_free(sqp, sizeof (sigqueue_t));
7517c478bd9Sstevel@tonic-gate 		return (0);
7527c478bd9Sstevel@tonic-gate 	}
7537c478bd9Sstevel@tonic-gate 	sigdelq(p, curthread, SIGTRAP);
7547c478bd9Sstevel@tonic-gate 	sigaddqa(p, curthread, sqp);
7557c478bd9Sstevel@tonic-gate 	schedctl_finish_sigblock(curthread);
7567c478bd9Sstevel@tonic-gate 	smask = curthread->t_hold;
7577c478bd9Sstevel@tonic-gate 	sigfillset(&curthread->t_hold);
7587c478bd9Sstevel@tonic-gate 	sigdiffset(&curthread->t_hold, &cantmask);
7597c478bd9Sstevel@tonic-gate 	sigdelset(&curthread->t_hold, SIGTRAP);
7607c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	rval = ((ISSIG_FAST(curthread, lwp, p, FORREAL))? 0 : 1);
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	/* restore the original signal mask */
7657c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
7667c478bd9Sstevel@tonic-gate 	curthread->t_hold = smask;
7677c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	return (rval);
7707c478bd9Sstevel@tonic-gate }
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate /*
7737c478bd9Sstevel@tonic-gate  * Wrappers for the copyin()/copyout() functions to deal
7747c478bd9Sstevel@tonic-gate  * with watchpoints that fire while in system calls.
7757c478bd9Sstevel@tonic-gate  */
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate static int
watch_xcopyin(const void * uaddr,void * kaddr,size_t count)7787c478bd9Sstevel@tonic-gate watch_xcopyin(const void *uaddr, void *kaddr, size_t count)
7797c478bd9Sstevel@tonic-gate {
7807c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
7817c478bd9Sstevel@tonic-gate 	caddr_t watch_uaddr = (caddr_t)uaddr;
7827c478bd9Sstevel@tonic-gate 	caddr_t watch_kaddr = (caddr_t)kaddr;
7837c478bd9Sstevel@tonic-gate 	int error = 0;
7847c478bd9Sstevel@tonic-gate 	label_t ljb;
7857c478bd9Sstevel@tonic-gate 	size_t part;
7867c478bd9Sstevel@tonic-gate 	int mapped;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	while (count && error == 0) {
7897c478bd9Sstevel@tonic-gate 		int watchcode;
7907c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
7917c478bd9Sstevel@tonic-gate 		size_t len;
7927c478bd9Sstevel@tonic-gate 		int ta;
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
7957c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > count)
7967c478bd9Sstevel@tonic-gate 			part = count;
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(watch_uaddr, S_READ))
7997c478bd9Sstevel@tonic-gate 			watchcode = 0;
8007c478bd9Sstevel@tonic-gate 		else {
8017c478bd9Sstevel@tonic-gate 			vaddr = watch_uaddr;
8027c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
8037c478bd9Sstevel@tonic-gate 			    part, &len, S_READ);
8047c478bd9Sstevel@tonic-gate 			if (watchcode && ta == 0)
8057c478bd9Sstevel@tonic-gate 				part = vaddr - watch_uaddr;
8067c478bd9Sstevel@tonic-gate 		}
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 		/*
8097c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
8107c478bd9Sstevel@tonic-gate 		 */
8117c478bd9Sstevel@tonic-gate 		if (part != 0) {
8127c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_READ, 1);
8137c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
8147c478bd9Sstevel@tonic-gate 				error = EFAULT;
8157c478bd9Sstevel@tonic-gate 			else
8167c478bd9Sstevel@tonic-gate 				copyin_noerr(watch_uaddr, watch_kaddr, part);
8177c478bd9Sstevel@tonic-gate 			no_fault();
8187c478bd9Sstevel@tonic-gate 			if (mapped)
8197c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_READ, 1);
8207c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
8217c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
8227c478bd9Sstevel@tonic-gate 			count -= part;
8237c478bd9Sstevel@tonic-gate 		}
8247c478bd9Sstevel@tonic-gate 		/*
8257c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
8267c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
8277c478bd9Sstevel@tonic-gate 		 */
8287c478bd9Sstevel@tonic-gate 		while (count && watchcode && ta && len > part && error == 0) {
8297c478bd9Sstevel@tonic-gate 			len -= part;
8307c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > count)
8317c478bd9Sstevel@tonic-gate 				part = count;
8327c478bd9Sstevel@tonic-gate 			if (part > len)
8337c478bd9Sstevel@tonic-gate 				part = len;
8347c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_READ, 1);
8357c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
8367c478bd9Sstevel@tonic-gate 				error = EFAULT;
8377c478bd9Sstevel@tonic-gate 			else
8387c478bd9Sstevel@tonic-gate 				copyin_noerr(watch_uaddr, watch_kaddr, part);
8397c478bd9Sstevel@tonic-gate 			no_fault();
8407c478bd9Sstevel@tonic-gate 			if (mapped)
8417c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_READ, 1);
8427c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
8437c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
8447c478bd9Sstevel@tonic-gate 			count -= part;
8457c478bd9Sstevel@tonic-gate 		}
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate error:
8487c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
8497c478bd9Sstevel@tonic-gate 		if (watchcode &&
8507c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
8517c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
8527c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
8537c478bd9Sstevel@tonic-gate 			error = EFAULT;
8547c478bd9Sstevel@tonic-gate 			break;
8557c478bd9Sstevel@tonic-gate 		}
8567c478bd9Sstevel@tonic-gate 	}
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	return (error);
8597c478bd9Sstevel@tonic-gate }
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate static int
watch_copyin(const void * kaddr,void * uaddr,size_t count)8627c478bd9Sstevel@tonic-gate watch_copyin(const void *kaddr, void *uaddr, size_t count)
8637c478bd9Sstevel@tonic-gate {
8647c478bd9Sstevel@tonic-gate 	return (watch_xcopyin(kaddr, uaddr, count) ? -1 : 0);
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate static int
watch_xcopyout(const void * kaddr,void * uaddr,size_t count)8697c478bd9Sstevel@tonic-gate watch_xcopyout(const void *kaddr, void *uaddr, size_t count)
8707c478bd9Sstevel@tonic-gate {
8717c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
8727c478bd9Sstevel@tonic-gate 	caddr_t watch_uaddr = (caddr_t)uaddr;
8737c478bd9Sstevel@tonic-gate 	caddr_t watch_kaddr = (caddr_t)kaddr;
8747c478bd9Sstevel@tonic-gate 	int error = 0;
8757c478bd9Sstevel@tonic-gate 	label_t ljb;
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	while (count && error == 0) {
8787c478bd9Sstevel@tonic-gate 		int watchcode;
8797c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
8807c478bd9Sstevel@tonic-gate 		size_t part;
8817c478bd9Sstevel@tonic-gate 		size_t len;
8827c478bd9Sstevel@tonic-gate 		int ta;
8837c478bd9Sstevel@tonic-gate 		int mapped;
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
8867c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > count)
8877c478bd9Sstevel@tonic-gate 			part = count;
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(watch_uaddr, S_WRITE))
8907c478bd9Sstevel@tonic-gate 			watchcode = 0;
8917c478bd9Sstevel@tonic-gate 		else {
8927c478bd9Sstevel@tonic-gate 			vaddr = watch_uaddr;
8937c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
8947c478bd9Sstevel@tonic-gate 			    part, &len, S_WRITE);
8957c478bd9Sstevel@tonic-gate 			if (watchcode) {
8967c478bd9Sstevel@tonic-gate 				if (ta == 0)
8977c478bd9Sstevel@tonic-gate 					part = vaddr - watch_uaddr;
8987c478bd9Sstevel@tonic-gate 				else {
8997c478bd9Sstevel@tonic-gate 					len += vaddr - watch_uaddr;
9007c478bd9Sstevel@tonic-gate 					if (part > len)
9017c478bd9Sstevel@tonic-gate 						part = len;
9027c478bd9Sstevel@tonic-gate 				}
9037c478bd9Sstevel@tonic-gate 			}
9047c478bd9Sstevel@tonic-gate 		}
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		/*
9077c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
9087c478bd9Sstevel@tonic-gate 		 */
9097c478bd9Sstevel@tonic-gate 		if (part != 0) {
9107c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1);
9117c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
9127c478bd9Sstevel@tonic-gate 				error = EFAULT;
9137c478bd9Sstevel@tonic-gate 			else
9147c478bd9Sstevel@tonic-gate 				copyout_noerr(watch_kaddr, watch_uaddr, part);
9157c478bd9Sstevel@tonic-gate 			no_fault();
9167c478bd9Sstevel@tonic-gate 			if (mapped)
9177c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_WRITE, 1);
9187c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
9197c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
9207c478bd9Sstevel@tonic-gate 			count -= part;
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 		/*
9247c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
9257c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
9267c478bd9Sstevel@tonic-gate 		 */
9277c478bd9Sstevel@tonic-gate 		while (count && watchcode && ta && len > part && error == 0) {
9287c478bd9Sstevel@tonic-gate 			len -= part;
9297c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > count)
9307c478bd9Sstevel@tonic-gate 				part = count;
9317c478bd9Sstevel@tonic-gate 			if (part > len)
9327c478bd9Sstevel@tonic-gate 				part = len;
9337c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1);
9347c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
9357c478bd9Sstevel@tonic-gate 				error = EFAULT;
9367c478bd9Sstevel@tonic-gate 			else
9377c478bd9Sstevel@tonic-gate 				copyout_noerr(watch_kaddr, watch_uaddr, part);
9387c478bd9Sstevel@tonic-gate 			no_fault();
9397c478bd9Sstevel@tonic-gate 			if (mapped)
9407c478bd9Sstevel@tonic-gate 				pr_unmappage(watch_uaddr, part, S_WRITE, 1);
9417c478bd9Sstevel@tonic-gate 			watch_uaddr += part;
9427c478bd9Sstevel@tonic-gate 			watch_kaddr += part;
9437c478bd9Sstevel@tonic-gate 			count -= part;
9447c478bd9Sstevel@tonic-gate 		}
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
9477c478bd9Sstevel@tonic-gate 		if (watchcode &&
9487c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
9497c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
9507c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
9517c478bd9Sstevel@tonic-gate 			error = EFAULT;
9527c478bd9Sstevel@tonic-gate 			break;
9537c478bd9Sstevel@tonic-gate 		}
9547c478bd9Sstevel@tonic-gate 	}
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 	return (error);
9577c478bd9Sstevel@tonic-gate }
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate static int
watch_copyout(const void * kaddr,void * uaddr,size_t count)9607c478bd9Sstevel@tonic-gate watch_copyout(const void *kaddr, void *uaddr, size_t count)
9617c478bd9Sstevel@tonic-gate {
9627c478bd9Sstevel@tonic-gate 	return (watch_xcopyout(kaddr, uaddr, count) ? -1 : 0);
9637c478bd9Sstevel@tonic-gate }
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate static int
watch_copyinstr(const char * uaddr,char * kaddr,size_t maxlength,size_t * lencopied)9667c478bd9Sstevel@tonic-gate watch_copyinstr(
9677c478bd9Sstevel@tonic-gate 	const char *uaddr,
9687c478bd9Sstevel@tonic-gate 	char *kaddr,
9697c478bd9Sstevel@tonic-gate 	size_t maxlength,
9707c478bd9Sstevel@tonic-gate 	size_t *lencopied)
9717c478bd9Sstevel@tonic-gate {
9727c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
9737c478bd9Sstevel@tonic-gate 	size_t resid;
9747c478bd9Sstevel@tonic-gate 	int error = 0;
9757c478bd9Sstevel@tonic-gate 	label_t ljb;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	if ((resid = maxlength) == 0)
9787c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 	while (resid && error == 0) {
9817c478bd9Sstevel@tonic-gate 		int watchcode;
9827c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
9837c478bd9Sstevel@tonic-gate 		size_t part;
9847c478bd9Sstevel@tonic-gate 		size_t len;
9857c478bd9Sstevel@tonic-gate 		size_t size;
9867c478bd9Sstevel@tonic-gate 		int ta;
9877c478bd9Sstevel@tonic-gate 		int mapped;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
9907c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > resid)
9917c478bd9Sstevel@tonic-gate 			part = resid;
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage((caddr_t)uaddr, S_READ))
9947c478bd9Sstevel@tonic-gate 			watchcode = 0;
9957c478bd9Sstevel@tonic-gate 		else {
9967c478bd9Sstevel@tonic-gate 			vaddr = (caddr_t)uaddr;
9977c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
9987c478bd9Sstevel@tonic-gate 			    part, &len, S_READ);
9997c478bd9Sstevel@tonic-gate 			if (watchcode) {
10007c478bd9Sstevel@tonic-gate 				if (ta == 0)
10017c478bd9Sstevel@tonic-gate 					part = vaddr - uaddr;
10027c478bd9Sstevel@tonic-gate 				else {
10037c478bd9Sstevel@tonic-gate 					len += vaddr - uaddr;
10047c478bd9Sstevel@tonic-gate 					if (part > len)
10057c478bd9Sstevel@tonic-gate 						part = len;
10067c478bd9Sstevel@tonic-gate 				}
10077c478bd9Sstevel@tonic-gate 			}
10087c478bd9Sstevel@tonic-gate 		}
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 		/*
10117c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
10127c478bd9Sstevel@tonic-gate 		 */
10137c478bd9Sstevel@tonic-gate 		if (part != 0) {
10147c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1);
10157c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
10167c478bd9Sstevel@tonic-gate 				error = EFAULT;
10177c478bd9Sstevel@tonic-gate 			else
10187c478bd9Sstevel@tonic-gate 				error = copyinstr_noerr(uaddr, kaddr, part,
10197c478bd9Sstevel@tonic-gate 				    &size);
10207c478bd9Sstevel@tonic-gate 			no_fault();
10217c478bd9Sstevel@tonic-gate 			if (mapped)
10227c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)uaddr, part, S_READ, 1);
10237c478bd9Sstevel@tonic-gate 			uaddr += size;
10247c478bd9Sstevel@tonic-gate 			kaddr += size;
10257c478bd9Sstevel@tonic-gate 			resid -= size;
10267c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
10277c478bd9Sstevel@tonic-gate 				error = 0;
10287c478bd9Sstevel@tonic-gate 			if (error != 0 || (watchcode &&
10297c478bd9Sstevel@tonic-gate 			    (uaddr < vaddr || kaddr[-1] == '\0')))
10307c478bd9Sstevel@tonic-gate 				break;	/* didn't reach the watched area */
10317c478bd9Sstevel@tonic-gate 		}
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 		/*
10347c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
10357c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
10367c478bd9Sstevel@tonic-gate 		 */
10377c478bd9Sstevel@tonic-gate 		while (resid && watchcode && ta && len > part && error == 0 &&
10387c478bd9Sstevel@tonic-gate 		    size == part && kaddr[-1] != '\0') {
10397c478bd9Sstevel@tonic-gate 			len -= part;
10407c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > resid)
10417c478bd9Sstevel@tonic-gate 				part = resid;
10427c478bd9Sstevel@tonic-gate 			if (part > len)
10437c478bd9Sstevel@tonic-gate 				part = len;
10447c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1);
10457c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
10467c478bd9Sstevel@tonic-gate 				error = EFAULT;
10477c478bd9Sstevel@tonic-gate 			else
10487c478bd9Sstevel@tonic-gate 				error = copyinstr_noerr(uaddr, kaddr, part,
10497c478bd9Sstevel@tonic-gate 				    &size);
10507c478bd9Sstevel@tonic-gate 			no_fault();
10517c478bd9Sstevel@tonic-gate 			if (mapped)
10527c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)uaddr, part, S_READ, 1);
10537c478bd9Sstevel@tonic-gate 			uaddr += size;
10547c478bd9Sstevel@tonic-gate 			kaddr += size;
10557c478bd9Sstevel@tonic-gate 			resid -= size;
10567c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
10577c478bd9Sstevel@tonic-gate 				error = 0;
10587c478bd9Sstevel@tonic-gate 		}
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
10617c478bd9Sstevel@tonic-gate 		if (watchcode &&
10627c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
10637c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
10647c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
10657c478bd9Sstevel@tonic-gate 			error = EFAULT;
10667c478bd9Sstevel@tonic-gate 			break;
10677c478bd9Sstevel@tonic-gate 		}
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 		if (error == 0 && part != 0 &&
10707c478bd9Sstevel@tonic-gate 		    (size < part || kaddr[-1] == '\0'))
10717c478bd9Sstevel@tonic-gate 			break;
10727c478bd9Sstevel@tonic-gate 	}
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 	if (error != EFAULT && lencopied)
10757c478bd9Sstevel@tonic-gate 		*lencopied = maxlength - resid;
10767c478bd9Sstevel@tonic-gate 	return (error);
10777c478bd9Sstevel@tonic-gate }
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate static int
watch_copyoutstr(const char * kaddr,char * uaddr,size_t maxlength,size_t * lencopied)10807c478bd9Sstevel@tonic-gate watch_copyoutstr(
10817c478bd9Sstevel@tonic-gate 	const char *kaddr,
10827c478bd9Sstevel@tonic-gate 	char *uaddr,
10837c478bd9Sstevel@tonic-gate 	size_t maxlength,
10847c478bd9Sstevel@tonic-gate 	size_t *lencopied)
10857c478bd9Sstevel@tonic-gate {
10867c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
10877c478bd9Sstevel@tonic-gate 	size_t resid;
10887c478bd9Sstevel@tonic-gate 	int error = 0;
10897c478bd9Sstevel@tonic-gate 	label_t ljb;
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	if ((resid = maxlength) == 0)
10927c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	while (resid && error == 0) {
10957c478bd9Sstevel@tonic-gate 		int watchcode;
10967c478bd9Sstevel@tonic-gate 		caddr_t vaddr;
10977c478bd9Sstevel@tonic-gate 		size_t part;
10987c478bd9Sstevel@tonic-gate 		size_t len;
10997c478bd9Sstevel@tonic-gate 		size_t size;
11007c478bd9Sstevel@tonic-gate 		int ta;
11017c478bd9Sstevel@tonic-gate 		int mapped;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 		if ((part = PAGESIZE -
11047c478bd9Sstevel@tonic-gate 		    (((uintptr_t)uaddr) & PAGEOFFSET)) > resid)
11057c478bd9Sstevel@tonic-gate 			part = resid;
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate 		if (!pr_is_watchpage(uaddr, S_WRITE)) {
11087c478bd9Sstevel@tonic-gate 			watchcode = 0;
11097c478bd9Sstevel@tonic-gate 		} else {
11107c478bd9Sstevel@tonic-gate 			vaddr = uaddr;
11117c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta,
11127c478bd9Sstevel@tonic-gate 			    part, &len, S_WRITE);
11137c478bd9Sstevel@tonic-gate 			if (watchcode && ta == 0)
11147c478bd9Sstevel@tonic-gate 				part = vaddr - uaddr;
11157c478bd9Sstevel@tonic-gate 		}
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 		/*
11187c478bd9Sstevel@tonic-gate 		 * Copy the initial part, up to a watched address, if any.
11197c478bd9Sstevel@tonic-gate 		 */
11207c478bd9Sstevel@tonic-gate 		if (part != 0) {
11217c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(uaddr, part, S_WRITE, 1);
11227c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
11237c478bd9Sstevel@tonic-gate 				error = EFAULT;
11247c478bd9Sstevel@tonic-gate 			else
11257c478bd9Sstevel@tonic-gate 				error = copyoutstr_noerr(kaddr, uaddr, part,
11267c478bd9Sstevel@tonic-gate 				    &size);
11277c478bd9Sstevel@tonic-gate 			no_fault();
11287c478bd9Sstevel@tonic-gate 			if (mapped)
11297c478bd9Sstevel@tonic-gate 				pr_unmappage(uaddr, part, S_WRITE, 1);
11307c478bd9Sstevel@tonic-gate 			uaddr += size;
11317c478bd9Sstevel@tonic-gate 			kaddr += size;
11327c478bd9Sstevel@tonic-gate 			resid -= size;
11337c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
11347c478bd9Sstevel@tonic-gate 				error = 0;
11357c478bd9Sstevel@tonic-gate 			if (error != 0 || (watchcode &&
11367c478bd9Sstevel@tonic-gate 			    (uaddr < vaddr || kaddr[-1] == '\0')))
11377c478bd9Sstevel@tonic-gate 				break;	/* didn't reach the watched area */
11387c478bd9Sstevel@tonic-gate 		}
11397c478bd9Sstevel@tonic-gate 
11407c478bd9Sstevel@tonic-gate 		/*
11417c478bd9Sstevel@tonic-gate 		 * If trapafter was specified, then copy through the
11427c478bd9Sstevel@tonic-gate 		 * watched area before taking the watchpoint trap.
11437c478bd9Sstevel@tonic-gate 		 */
11447c478bd9Sstevel@tonic-gate 		while (resid && watchcode && ta && len > part && error == 0 &&
11457c478bd9Sstevel@tonic-gate 		    size == part && kaddr[-1] != '\0') {
11467c478bd9Sstevel@tonic-gate 			len -= part;
11477c478bd9Sstevel@tonic-gate 			if ((part = PAGESIZE) > resid)
11487c478bd9Sstevel@tonic-gate 				part = resid;
11497c478bd9Sstevel@tonic-gate 			if (part > len)
11507c478bd9Sstevel@tonic-gate 				part = len;
11517c478bd9Sstevel@tonic-gate 			mapped = pr_mappage(uaddr, part, S_WRITE, 1);
11527c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
11537c478bd9Sstevel@tonic-gate 				error = EFAULT;
11547c478bd9Sstevel@tonic-gate 			else
11557c478bd9Sstevel@tonic-gate 				error = copyoutstr_noerr(kaddr, uaddr, part,
11567c478bd9Sstevel@tonic-gate 				    &size);
11577c478bd9Sstevel@tonic-gate 			no_fault();
11587c478bd9Sstevel@tonic-gate 			if (mapped)
11597c478bd9Sstevel@tonic-gate 				pr_unmappage(uaddr, part, S_WRITE, 1);
11607c478bd9Sstevel@tonic-gate 			uaddr += size;
11617c478bd9Sstevel@tonic-gate 			kaddr += size;
11627c478bd9Sstevel@tonic-gate 			resid -= size;
11637c478bd9Sstevel@tonic-gate 			if (error == ENAMETOOLONG && resid > 0)
11647c478bd9Sstevel@tonic-gate 				error = 0;
11657c478bd9Sstevel@tonic-gate 		}
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 		/* if we hit a watched address, do the watchpoint logic */
11687c478bd9Sstevel@tonic-gate 		if (watchcode &&
11697c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
11707c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
11717c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
11727c478bd9Sstevel@tonic-gate 			error = EFAULT;
11737c478bd9Sstevel@tonic-gate 			break;
11747c478bd9Sstevel@tonic-gate 		}
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 		if (error == 0 && part != 0 &&
11777c478bd9Sstevel@tonic-gate 		    (size < part || kaddr[-1] == '\0'))
11787c478bd9Sstevel@tonic-gate 			break;
11797c478bd9Sstevel@tonic-gate 	}
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	if (error != EFAULT && lencopied)
11827c478bd9Sstevel@tonic-gate 		*lencopied = maxlength - resid;
11837c478bd9Sstevel@tonic-gate 	return (error);
11847c478bd9Sstevel@tonic-gate }
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate typedef int (*fuword_func)(const void *, void *);
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate /*
11897c478bd9Sstevel@tonic-gate  * Generic form of watch_fuword8(), watch_fuword16(), etc.
11907c478bd9Sstevel@tonic-gate  */
11917c478bd9Sstevel@tonic-gate static int
watch_fuword(const void * addr,void * dst,fuword_func func,size_t size)11927c478bd9Sstevel@tonic-gate watch_fuword(const void *addr, void *dst, fuword_func func, size_t size)
11937c478bd9Sstevel@tonic-gate {
11947c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
11957c478bd9Sstevel@tonic-gate 	int watchcode;
11967c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
11977c478bd9Sstevel@tonic-gate 	int mapped;
11987c478bd9Sstevel@tonic-gate 	int rv = 0;
11997c478bd9Sstevel@tonic-gate 	int ta;
12007c478bd9Sstevel@tonic-gate 	label_t ljb;
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 	for (;;) {
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
12057c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, size, NULL, S_READ);
12067c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
12077c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, size, S_READ, 1);
12087c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
12097c478bd9Sstevel@tonic-gate 				rv = -1;
12107c478bd9Sstevel@tonic-gate 			else
12117c478bd9Sstevel@tonic-gate 				(*func)(addr, dst);
12127c478bd9Sstevel@tonic-gate 			no_fault();
12137c478bd9Sstevel@tonic-gate 			if (mapped)
12147c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, size, S_READ, 1);
12157c478bd9Sstevel@tonic-gate 		}
12167c478bd9Sstevel@tonic-gate 		if (watchcode &&
12177c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
12187c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
12197c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
12207c478bd9Sstevel@tonic-gate 			rv = -1;
12217c478bd9Sstevel@tonic-gate 			break;
12227c478bd9Sstevel@tonic-gate 		}
12237c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
12247c478bd9Sstevel@tonic-gate 			break;
12257c478bd9Sstevel@tonic-gate 	}
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 	return (rv);
12287c478bd9Sstevel@tonic-gate }
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate static int
watch_fuword8(const void * addr,uint8_t * dst)12317c478bd9Sstevel@tonic-gate watch_fuword8(const void *addr, uint8_t *dst)
12327c478bd9Sstevel@tonic-gate {
12337c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword8_noerr,
12347c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
12357c478bd9Sstevel@tonic-gate }
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate static int
watch_fuword16(const void * addr,uint16_t * dst)12387c478bd9Sstevel@tonic-gate watch_fuword16(const void *addr, uint16_t *dst)
12397c478bd9Sstevel@tonic-gate {
12407c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword16_noerr,
12417c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
12427c478bd9Sstevel@tonic-gate }
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate static int
watch_fuword32(const void * addr,uint32_t * dst)12457c478bd9Sstevel@tonic-gate watch_fuword32(const void *addr, uint32_t *dst)
12467c478bd9Sstevel@tonic-gate {
12477c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword32_noerr,
12487c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
12497c478bd9Sstevel@tonic-gate }
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate #ifdef _LP64
12527c478bd9Sstevel@tonic-gate static int
watch_fuword64(const void * addr,uint64_t * dst)12537c478bd9Sstevel@tonic-gate watch_fuword64(const void *addr, uint64_t *dst)
12547c478bd9Sstevel@tonic-gate {
12557c478bd9Sstevel@tonic-gate 	return (watch_fuword(addr, dst, (fuword_func)fuword64_noerr,
12567c478bd9Sstevel@tonic-gate 	    sizeof (*dst)));
12577c478bd9Sstevel@tonic-gate }
12587c478bd9Sstevel@tonic-gate #endif
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate static int
watch_suword8(void * addr,uint8_t value)12627c478bd9Sstevel@tonic-gate watch_suword8(void *addr, uint8_t value)
12637c478bd9Sstevel@tonic-gate {
12647c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
12657c478bd9Sstevel@tonic-gate 	int watchcode;
12667c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
12677c478bd9Sstevel@tonic-gate 	int mapped;
12687c478bd9Sstevel@tonic-gate 	int rv = 0;
12697c478bd9Sstevel@tonic-gate 	int ta;
12707c478bd9Sstevel@tonic-gate 	label_t ljb;
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	for (;;) {
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
12757c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
12767c478bd9Sstevel@tonic-gate 		    S_WRITE);
12777c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
12787c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
12797c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
12807c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
12817c478bd9Sstevel@tonic-gate 				rv = -1;
12827c478bd9Sstevel@tonic-gate 			else
12837c478bd9Sstevel@tonic-gate 				suword8_noerr(addr, value);
12847c478bd9Sstevel@tonic-gate 			no_fault();
12857c478bd9Sstevel@tonic-gate 			if (mapped)
12867c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
12877c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
12887c478bd9Sstevel@tonic-gate 		}
12897c478bd9Sstevel@tonic-gate 		if (watchcode &&
12907c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
12917c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
12927c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
12937c478bd9Sstevel@tonic-gate 			rv = -1;
12947c478bd9Sstevel@tonic-gate 			break;
12957c478bd9Sstevel@tonic-gate 		}
12967c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
12977c478bd9Sstevel@tonic-gate 			break;
12987c478bd9Sstevel@tonic-gate 	}
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 	return (rv);
13017c478bd9Sstevel@tonic-gate }
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate static int
watch_suword16(void * addr,uint16_t value)13047c478bd9Sstevel@tonic-gate watch_suword16(void *addr, uint16_t value)
13057c478bd9Sstevel@tonic-gate {
13067c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
13077c478bd9Sstevel@tonic-gate 	int watchcode;
13087c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
13097c478bd9Sstevel@tonic-gate 	int mapped;
13107c478bd9Sstevel@tonic-gate 	int rv = 0;
13117c478bd9Sstevel@tonic-gate 	int ta;
13127c478bd9Sstevel@tonic-gate 	label_t ljb;
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 	for (;;) {
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
13177c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
13187c478bd9Sstevel@tonic-gate 		    S_WRITE);
13197c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
13207c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
13217c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
13227c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
13237c478bd9Sstevel@tonic-gate 				rv = -1;
13247c478bd9Sstevel@tonic-gate 			else
13257c478bd9Sstevel@tonic-gate 				suword16_noerr(addr, value);
13267c478bd9Sstevel@tonic-gate 			no_fault();
13277c478bd9Sstevel@tonic-gate 			if (mapped)
13287c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
13297c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
13307c478bd9Sstevel@tonic-gate 		}
13317c478bd9Sstevel@tonic-gate 		if (watchcode &&
13327c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
13337c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
13347c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
13357c478bd9Sstevel@tonic-gate 			rv = -1;
13367c478bd9Sstevel@tonic-gate 			break;
13377c478bd9Sstevel@tonic-gate 		}
13387c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
13397c478bd9Sstevel@tonic-gate 			break;
13407c478bd9Sstevel@tonic-gate 	}
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 	return (rv);
13437c478bd9Sstevel@tonic-gate }
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate static int
watch_suword32(void * addr,uint32_t value)13467c478bd9Sstevel@tonic-gate watch_suword32(void *addr, uint32_t value)
13477c478bd9Sstevel@tonic-gate {
13487c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
13497c478bd9Sstevel@tonic-gate 	int watchcode;
13507c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
13517c478bd9Sstevel@tonic-gate 	int mapped;
13527c478bd9Sstevel@tonic-gate 	int rv = 0;
13537c478bd9Sstevel@tonic-gate 	int ta;
13547c478bd9Sstevel@tonic-gate 	label_t ljb;
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	for (;;) {
13577c478bd9Sstevel@tonic-gate 
13587c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
13597c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
13607c478bd9Sstevel@tonic-gate 		    S_WRITE);
13617c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
13627c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
13637c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
13647c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
13657c478bd9Sstevel@tonic-gate 				rv = -1;
13667c478bd9Sstevel@tonic-gate 			else
13677c478bd9Sstevel@tonic-gate 				suword32_noerr(addr, value);
13687c478bd9Sstevel@tonic-gate 			no_fault();
13697c478bd9Sstevel@tonic-gate 			if (mapped)
13707c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
13717c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
13727c478bd9Sstevel@tonic-gate 		}
13737c478bd9Sstevel@tonic-gate 		if (watchcode &&
13747c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
13757c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
13767c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
13777c478bd9Sstevel@tonic-gate 			rv = -1;
13787c478bd9Sstevel@tonic-gate 			break;
13797c478bd9Sstevel@tonic-gate 		}
13807c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
13817c478bd9Sstevel@tonic-gate 			break;
13827c478bd9Sstevel@tonic-gate 	}
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	return (rv);
13857c478bd9Sstevel@tonic-gate }
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate #ifdef _LP64
13887c478bd9Sstevel@tonic-gate static int
watch_suword64(void * addr,uint64_t value)13897c478bd9Sstevel@tonic-gate watch_suword64(void *addr, uint64_t value)
13907c478bd9Sstevel@tonic-gate {
13917c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
13927c478bd9Sstevel@tonic-gate 	int watchcode;
13937c478bd9Sstevel@tonic-gate 	caddr_t vaddr;
13947c478bd9Sstevel@tonic-gate 	int mapped;
13957c478bd9Sstevel@tonic-gate 	int rv = 0;
13967c478bd9Sstevel@tonic-gate 	int ta;
13977c478bd9Sstevel@tonic-gate 	label_t ljb;
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate 	for (;;) {
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate 		vaddr = (caddr_t)addr;
14027c478bd9Sstevel@tonic-gate 		watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL,
14037c478bd9Sstevel@tonic-gate 		    S_WRITE);
14047c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0) {
14057c478bd9Sstevel@tonic-gate 			mapped = pr_mappage((caddr_t)addr, sizeof (value),
14067c478bd9Sstevel@tonic-gate 			    S_WRITE, 1);
14077c478bd9Sstevel@tonic-gate 			if (on_fault(&ljb))
14087c478bd9Sstevel@tonic-gate 				rv = -1;
14097c478bd9Sstevel@tonic-gate 			else
14107c478bd9Sstevel@tonic-gate 				suword64_noerr(addr, value);
14117c478bd9Sstevel@tonic-gate 			no_fault();
14127c478bd9Sstevel@tonic-gate 			if (mapped)
14137c478bd9Sstevel@tonic-gate 				pr_unmappage((caddr_t)addr, sizeof (value),
14147c478bd9Sstevel@tonic-gate 				    S_WRITE, 1);
14157c478bd9Sstevel@tonic-gate 		}
14167c478bd9Sstevel@tonic-gate 		if (watchcode &&
14177c478bd9Sstevel@tonic-gate 		    (!sys_watchpoint(vaddr, watchcode, ta) ||
14187c478bd9Sstevel@tonic-gate 		    lwp->lwp_sysabort)) {
14197c478bd9Sstevel@tonic-gate 			lwp->lwp_sysabort = 0;
14207c478bd9Sstevel@tonic-gate 			rv = -1;
14217c478bd9Sstevel@tonic-gate 			break;
14227c478bd9Sstevel@tonic-gate 		}
14237c478bd9Sstevel@tonic-gate 		if (watchcode == 0 || ta != 0)
14247c478bd9Sstevel@tonic-gate 			break;
14257c478bd9Sstevel@tonic-gate 	}
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 	return (rv);
14287c478bd9Sstevel@tonic-gate }
14297c478bd9Sstevel@tonic-gate #endif /* _LP64 */
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate /*
14327c478bd9Sstevel@tonic-gate  * Check for watched addresses in the given address space.
14337c478bd9Sstevel@tonic-gate  * Return 1 if this is true, otherwise 0.
14347c478bd9Sstevel@tonic-gate  */
14357c478bd9Sstevel@tonic-gate static int
pr_is_watched(caddr_t base,size_t len,int rw)14367c478bd9Sstevel@tonic-gate pr_is_watched(caddr_t base, size_t len, int rw)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate 	caddr_t saddr = (caddr_t)((uintptr_t)base & (uintptr_t)PAGEMASK);
14397c478bd9Sstevel@tonic-gate 	caddr_t eaddr = base + len;
14407c478bd9Sstevel@tonic-gate 	caddr_t paddr;
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	for (paddr = saddr; paddr < eaddr; paddr += PAGESIZE) {
14437c478bd9Sstevel@tonic-gate 		if (pr_is_watchpage(paddr, rw))
14447c478bd9Sstevel@tonic-gate 			return (1);
14457c478bd9Sstevel@tonic-gate 	}
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	return (0);
14487c478bd9Sstevel@tonic-gate }
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate /*
14517c478bd9Sstevel@tonic-gate  * Wrapper for the physio() function.
14527c478bd9Sstevel@tonic-gate  * Splits one uio operation with multiple iovecs into uio operations with
14537c478bd9Sstevel@tonic-gate  * only one iovecs to do the watchpoint handling separately for each iovecs.
14547c478bd9Sstevel@tonic-gate  */
14557c478bd9Sstevel@tonic-gate static int
watch_physio(int (* strat)(struct buf *),struct buf * bp,dev_t dev,int rw,void (* mincnt)(struct buf *),struct uio * uio)14567c478bd9Sstevel@tonic-gate watch_physio(int (*strat)(struct buf *), struct buf *bp, dev_t dev,
14577c478bd9Sstevel@tonic-gate     int rw, void (*mincnt)(struct buf *), struct uio *uio)
14587c478bd9Sstevel@tonic-gate {
14597c478bd9Sstevel@tonic-gate 	struct uio auio;
14607c478bd9Sstevel@tonic-gate 	struct iovec *iov;
14617c478bd9Sstevel@tonic-gate 	caddr_t  base;
14627c478bd9Sstevel@tonic-gate 	size_t len;
14637c478bd9Sstevel@tonic-gate 	int seg_rw;
14647c478bd9Sstevel@tonic-gate 	int error = 0;
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	if (uio->uio_segflg == UIO_SYSSPACE)
14677c478bd9Sstevel@tonic-gate 		return (default_physio(strat, bp, dev, rw, mincnt, uio));
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	seg_rw = (rw == B_READ) ? S_WRITE : S_READ;
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	while (uio->uio_iovcnt > 0) {
14727c478bd9Sstevel@tonic-gate 		if (uio->uio_resid == 0) {
14737c478bd9Sstevel@tonic-gate 			/*
14747c478bd9Sstevel@tonic-gate 			 * Make sure to return the uio structure with the
14757c478bd9Sstevel@tonic-gate 			 * same values as default_physio() does.
14767c478bd9Sstevel@tonic-gate 			 */
14777c478bd9Sstevel@tonic-gate 			uio->uio_iov++;
14787c478bd9Sstevel@tonic-gate 			uio->uio_iovcnt--;
14797c478bd9Sstevel@tonic-gate 			continue;
14807c478bd9Sstevel@tonic-gate 		}
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 		iov = uio->uio_iov;
14837c478bd9Sstevel@tonic-gate 		len = MIN(iov->iov_len, uio->uio_resid);
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate 		auio.uio_iovcnt = 1;
14867c478bd9Sstevel@tonic-gate 		auio.uio_iov = iov;
14877c478bd9Sstevel@tonic-gate 		auio.uio_resid = len;
14887c478bd9Sstevel@tonic-gate 		auio.uio_loffset = uio->uio_loffset;
14897c478bd9Sstevel@tonic-gate 		auio.uio_llimit = uio->uio_llimit;
14907c478bd9Sstevel@tonic-gate 		auio.uio_fmode = uio->uio_fmode;
14917c478bd9Sstevel@tonic-gate 		auio.uio_extflg = uio->uio_extflg;
14927c478bd9Sstevel@tonic-gate 		auio.uio_segflg = uio->uio_segflg;
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 		base = iov->iov_base;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 		if (!pr_is_watched(base, len, seg_rw)) {
14977c478bd9Sstevel@tonic-gate 			/*
14987c478bd9Sstevel@tonic-gate 			 * The given memory references don't cover a
14997c478bd9Sstevel@tonic-gate 			 * watched page.
15007c478bd9Sstevel@tonic-gate 			 */
15017c478bd9Sstevel@tonic-gate 			error = default_physio(strat, bp, dev, rw, mincnt,
15027c478bd9Sstevel@tonic-gate 			    &auio);
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 			/* Update uio with values from auio. */
15057c478bd9Sstevel@tonic-gate 			len -= auio.uio_resid;
15067c478bd9Sstevel@tonic-gate 			uio->uio_resid -= len;
15077c478bd9Sstevel@tonic-gate 			uio->uio_loffset += len;
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 			/*
15107c478bd9Sstevel@tonic-gate 			 * Return if an error occurred or not all data
15117c478bd9Sstevel@tonic-gate 			 * was copied.
15127c478bd9Sstevel@tonic-gate 			 */
15137c478bd9Sstevel@tonic-gate 			if (auio.uio_resid || error)
15147c478bd9Sstevel@tonic-gate 				break;
15157c478bd9Sstevel@tonic-gate 			uio->uio_iov++;
15167c478bd9Sstevel@tonic-gate 			uio->uio_iovcnt--;
15177c478bd9Sstevel@tonic-gate 		} else {
15187c478bd9Sstevel@tonic-gate 			int mapped, watchcode, ta;
15197c478bd9Sstevel@tonic-gate 			caddr_t vaddr = base;
15207c478bd9Sstevel@tonic-gate 			klwp_t *lwp = ttolwp(curthread);
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 			watchcode = pr_is_watchpoint(&vaddr, &ta, len,
15237c478bd9Sstevel@tonic-gate 			    NULL, seg_rw);
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 			if (watchcode == 0 || ta != 0) {
15267c478bd9Sstevel@tonic-gate 				/*
15277c478bd9Sstevel@tonic-gate 				 * Do the io if the given memory references
15287c478bd9Sstevel@tonic-gate 				 * don't cover a watched area (watchcode=0)
15297c478bd9Sstevel@tonic-gate 				 * or if WA_TRAPAFTER was specified.
15307c478bd9Sstevel@tonic-gate 				 */
15317c478bd9Sstevel@tonic-gate 				mapped = pr_mappage(base, len, seg_rw, 1);
15327c478bd9Sstevel@tonic-gate 				error = default_physio(strat, bp, dev, rw,
15337c478bd9Sstevel@tonic-gate 				    mincnt, &auio);
15347c478bd9Sstevel@tonic-gate 				if (mapped)
15357c478bd9Sstevel@tonic-gate 					pr_unmappage(base, len, seg_rw, 1);
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 				len -= auio.uio_resid;
15387c478bd9Sstevel@tonic-gate 				uio->uio_resid -= len;
15397c478bd9Sstevel@tonic-gate 				uio->uio_loffset += len;
15407c478bd9Sstevel@tonic-gate 			}
15417c478bd9Sstevel@tonic-gate 
15427c478bd9Sstevel@tonic-gate 			/*
15437c478bd9Sstevel@tonic-gate 			 * If we hit a watched address, do the watchpoint logic.
15447c478bd9Sstevel@tonic-gate 			 */
15457c478bd9Sstevel@tonic-gate 			if (watchcode &&
15467c478bd9Sstevel@tonic-gate 			    (!sys_watchpoint(vaddr, watchcode, ta) ||
15477c478bd9Sstevel@tonic-gate 			    lwp->lwp_sysabort)) {
15487c478bd9Sstevel@tonic-gate 				lwp->lwp_sysabort = 0;
15497c478bd9Sstevel@tonic-gate 				return (EFAULT);
15507c478bd9Sstevel@tonic-gate 			}
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 			/*
15537c478bd9Sstevel@tonic-gate 			 * Check for errors from default_physio().
15547c478bd9Sstevel@tonic-gate 			 */
15557c478bd9Sstevel@tonic-gate 			if (watchcode == 0 || ta != 0) {
15567c478bd9Sstevel@tonic-gate 				if (auio.uio_resid || error)
15577c478bd9Sstevel@tonic-gate 					break;
15587c478bd9Sstevel@tonic-gate 				uio->uio_iov++;
15597c478bd9Sstevel@tonic-gate 				uio->uio_iovcnt--;
15607c478bd9Sstevel@tonic-gate 			}
15617c478bd9Sstevel@tonic-gate 		}
15627c478bd9Sstevel@tonic-gate 	}
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	return (error);
15657c478bd9Sstevel@tonic-gate }
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate int
wa_compare(const void * a,const void * b)15687c478bd9Sstevel@tonic-gate wa_compare(const void *a, const void *b)
15697c478bd9Sstevel@tonic-gate {
15707c478bd9Sstevel@tonic-gate 	const watched_area_t *pa = a;
15717c478bd9Sstevel@tonic-gate 	const watched_area_t *pb = b;
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 	if (pa->wa_vaddr < pb->wa_vaddr)
15747c478bd9Sstevel@tonic-gate 		return (-1);
15757c478bd9Sstevel@tonic-gate 	else if (pa->wa_vaddr > pb->wa_vaddr)
15767c478bd9Sstevel@tonic-gate 		return (1);
15777c478bd9Sstevel@tonic-gate 	else
15787c478bd9Sstevel@tonic-gate 		return (0);
15797c478bd9Sstevel@tonic-gate }
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate int
wp_compare(const void * a,const void * b)15827c478bd9Sstevel@tonic-gate wp_compare(const void *a, const void *b)
15837c478bd9Sstevel@tonic-gate {
15847c478bd9Sstevel@tonic-gate 	const watched_page_t *pa = a;
15857c478bd9Sstevel@tonic-gate 	const watched_page_t *pb = b;
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 	if (pa->wp_vaddr < pb->wp_vaddr)
15887c478bd9Sstevel@tonic-gate 		return (-1);
15897c478bd9Sstevel@tonic-gate 	else if (pa->wp_vaddr > pb->wp_vaddr)
15907c478bd9Sstevel@tonic-gate 		return (1);
15917c478bd9Sstevel@tonic-gate 	else
15927c478bd9Sstevel@tonic-gate 		return (0);
15937c478bd9Sstevel@tonic-gate }
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate /*
15967c478bd9Sstevel@tonic-gate  * Given an address range, finds the first watched area which overlaps some or
15977c478bd9Sstevel@tonic-gate  * all of the range.
15987c478bd9Sstevel@tonic-gate  */
15997c478bd9Sstevel@tonic-gate watched_area_t *
pr_find_watched_area(proc_t * p,watched_area_t * pwa,avl_index_t * where)16007c478bd9Sstevel@tonic-gate pr_find_watched_area(proc_t *p, watched_area_t *pwa, avl_index_t *where)
16017c478bd9Sstevel@tonic-gate {
16027c478bd9Sstevel@tonic-gate 	caddr_t vaddr = pwa->wa_vaddr;
16037c478bd9Sstevel@tonic-gate 	caddr_t eaddr = pwa->wa_eaddr;
16047c478bd9Sstevel@tonic-gate 	watched_area_t *wap;
16057c478bd9Sstevel@tonic-gate 	avl_index_t real_where;
16067c478bd9Sstevel@tonic-gate 
16077c478bd9Sstevel@tonic-gate 	/* First, check if there is an exact match.  */
16087c478bd9Sstevel@tonic-gate 	wap = avl_find(&p->p_warea, pwa, &real_where);
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	/* Check to see if we overlap with the previous area.  */
16127c478bd9Sstevel@tonic-gate 	if (wap == NULL) {
16137c478bd9Sstevel@tonic-gate 		wap = avl_nearest(&p->p_warea, real_where, AVL_BEFORE);
16147c478bd9Sstevel@tonic-gate 		if (wap != NULL &&
16157c478bd9Sstevel@tonic-gate 		    (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr))
16167c478bd9Sstevel@tonic-gate 			wap = NULL;
16177c478bd9Sstevel@tonic-gate 	}
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	/* Try the next area.  */
16207c478bd9Sstevel@tonic-gate 	if (wap == NULL) {
16217c478bd9Sstevel@tonic-gate 		wap = avl_nearest(&p->p_warea, real_where, AVL_AFTER);
16227c478bd9Sstevel@tonic-gate 		if (wap != NULL &&
16237c478bd9Sstevel@tonic-gate 		    (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr))
16247c478bd9Sstevel@tonic-gate 			wap = NULL;
16257c478bd9Sstevel@tonic-gate 	}
16267c478bd9Sstevel@tonic-gate 
16277c478bd9Sstevel@tonic-gate 	if (where)
16287c478bd9Sstevel@tonic-gate 		*where = real_where;
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 	return (wap);
16317c478bd9Sstevel@tonic-gate }
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate void
watch_enable(kthread_id_t t)16347c478bd9Sstevel@tonic-gate watch_enable(kthread_id_t t)
16357c478bd9Sstevel@tonic-gate {
16367c478bd9Sstevel@tonic-gate 	t->t_proc_flag |= TP_WATCHPT;
16377c478bd9Sstevel@tonic-gate 	install_copyops(t, &watch_copyops);
16387c478bd9Sstevel@tonic-gate }
16397c478bd9Sstevel@tonic-gate 
16407c478bd9Sstevel@tonic-gate void
watch_disable(kthread_id_t t)16417c478bd9Sstevel@tonic-gate watch_disable(kthread_id_t t)
16427c478bd9Sstevel@tonic-gate {
16437c478bd9Sstevel@tonic-gate 	t->t_proc_flag &= ~TP_WATCHPT;
16447c478bd9Sstevel@tonic-gate 	remove_copyops(t);
16457c478bd9Sstevel@tonic-gate }
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate int
copyin_nowatch(const void * uaddr,void * kaddr,size_t len)16487c478bd9Sstevel@tonic-gate copyin_nowatch(const void *uaddr, void *kaddr, size_t len)
16497c478bd9Sstevel@tonic-gate {
16507c478bd9Sstevel@tonic-gate 	int watched, ret;
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(uaddr, len, S_READ);
16537c478bd9Sstevel@tonic-gate 	ret = copyin(uaddr, kaddr, len);
16547c478bd9Sstevel@tonic-gate 	if (watched)
16557c478bd9Sstevel@tonic-gate 		watch_enable_addr(uaddr, len, S_READ);
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	return (ret);
16587c478bd9Sstevel@tonic-gate }
16597c478bd9Sstevel@tonic-gate 
16607c478bd9Sstevel@tonic-gate int
copyout_nowatch(const void * kaddr,void * uaddr,size_t len)16617c478bd9Sstevel@tonic-gate copyout_nowatch(const void *kaddr, void *uaddr, size_t len)
16627c478bd9Sstevel@tonic-gate {
16637c478bd9Sstevel@tonic-gate 	int watched, ret;
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(uaddr, len, S_WRITE);
16667c478bd9Sstevel@tonic-gate 	ret = copyout(kaddr, uaddr, len);
16677c478bd9Sstevel@tonic-gate 	if (watched)
16687c478bd9Sstevel@tonic-gate 		watch_enable_addr(uaddr, len, S_WRITE);
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	return (ret);
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate #ifdef _LP64
16747c478bd9Sstevel@tonic-gate int
fuword64_nowatch(const void * addr,uint64_t * value)16757c478bd9Sstevel@tonic-gate fuword64_nowatch(const void *addr, uint64_t *value)
16767c478bd9Sstevel@tonic-gate {
16777c478bd9Sstevel@tonic-gate 	int watched, ret;
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (*value), S_READ);
16807c478bd9Sstevel@tonic-gate 	ret = fuword64(addr, value);
16817c478bd9Sstevel@tonic-gate 	if (watched)
16827c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (*value), S_READ);
16837c478bd9Sstevel@tonic-gate 
16847c478bd9Sstevel@tonic-gate 	return (ret);
16857c478bd9Sstevel@tonic-gate }
16867c478bd9Sstevel@tonic-gate #endif
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate int
fuword32_nowatch(const void * addr,uint32_t * value)16897c478bd9Sstevel@tonic-gate fuword32_nowatch(const void *addr, uint32_t *value)
16907c478bd9Sstevel@tonic-gate {
16917c478bd9Sstevel@tonic-gate 	int watched, ret;
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (*value), S_READ);
16947c478bd9Sstevel@tonic-gate 	ret = fuword32(addr, value);
16957c478bd9Sstevel@tonic-gate 	if (watched)
16967c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (*value), S_READ);
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	return (ret);
16997c478bd9Sstevel@tonic-gate }
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate #ifdef _LP64
17027c478bd9Sstevel@tonic-gate int
suword64_nowatch(void * addr,uint64_t value)17037c478bd9Sstevel@tonic-gate suword64_nowatch(void *addr, uint64_t value)
17047c478bd9Sstevel@tonic-gate {
17057c478bd9Sstevel@tonic-gate 	int watched, ret;
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (value), S_WRITE);
17087c478bd9Sstevel@tonic-gate 	ret = suword64(addr, value);
17097c478bd9Sstevel@tonic-gate 	if (watched)
17107c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (value), S_WRITE);
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate 	return (ret);
17137c478bd9Sstevel@tonic-gate }
17147c478bd9Sstevel@tonic-gate #endif
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate int
suword32_nowatch(void * addr,uint32_t value)17177c478bd9Sstevel@tonic-gate suword32_nowatch(void *addr, uint32_t value)
17187c478bd9Sstevel@tonic-gate {
17197c478bd9Sstevel@tonic-gate 	int watched, ret;
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	watched = watch_disable_addr(addr, sizeof (value), S_WRITE);
17227c478bd9Sstevel@tonic-gate 	ret = suword32(addr, value);
17237c478bd9Sstevel@tonic-gate 	if (watched)
17247c478bd9Sstevel@tonic-gate 		watch_enable_addr(addr, sizeof (value), S_WRITE);
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 	return (ret);
17277c478bd9Sstevel@tonic-gate }
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate int
watch_disable_addr(const void * addr,size_t len,enum seg_rw rw)17307c478bd9Sstevel@tonic-gate watch_disable_addr(const void *addr, size_t len, enum seg_rw rw)
17317c478bd9Sstevel@tonic-gate {
17327c478bd9Sstevel@tonic-gate 	if (pr_watch_active(curproc))
17337c478bd9Sstevel@tonic-gate 		return (pr_mappage((caddr_t)addr, len, rw, 1));
17347c478bd9Sstevel@tonic-gate 	return (0);
17357c478bd9Sstevel@tonic-gate }
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate void
watch_enable_addr(const void * addr,size_t len,enum seg_rw rw)17387c478bd9Sstevel@tonic-gate watch_enable_addr(const void *addr, size_t len, enum seg_rw rw)
17397c478bd9Sstevel@tonic-gate {
17407c478bd9Sstevel@tonic-gate 	if (pr_watch_active(curproc))
17417c478bd9Sstevel@tonic-gate 		pr_unmappage((caddr_t)addr, len, rw, 1);
17427c478bd9Sstevel@tonic-gate }
1743