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