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 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 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 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 183*0d5ae8c1SJosef '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; 1997c478bd9Sstevel@tonic-gate 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); 310*0d5ae8c1SJosef '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 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 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 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 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)) 4537c478bd9Sstevel@tonic-gate != (PROT_USER|PROT_READ)); 4547c478bd9Sstevel@tonic-gate break; 4557c478bd9Sstevel@tonic-gate case S_WRITE: 4567c478bd9Sstevel@tonic-gate rv = ((prot & (PROT_USER|PROT_WRITE)) 4577c478bd9Sstevel@tonic-gate != (PROT_USER|PROT_WRITE)); 4587c478bd9Sstevel@tonic-gate break; 4597c478bd9Sstevel@tonic-gate case S_EXEC: 4607c478bd9Sstevel@tonic-gate rv = ((prot & (PROT_USER|PROT_EXEC)) 4617c478bd9Sstevel@tonic-gate != (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 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 487*0d5ae8c1SJosef '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 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 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 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 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 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 error: 8257c478bd9Sstevel@tonic-gate /* if we hit a watched address, do the watchpoint logic */ 8267c478bd9Sstevel@tonic-gate if (watchcode && 8277c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 8287c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 8297c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 8307c478bd9Sstevel@tonic-gate error = EFAULT; 8317c478bd9Sstevel@tonic-gate break; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate } 8347c478bd9Sstevel@tonic-gate 8357c478bd9Sstevel@tonic-gate return (error); 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate static int 8397c478bd9Sstevel@tonic-gate watch_copyin(const void *kaddr, void *uaddr, size_t count) 8407c478bd9Sstevel@tonic-gate { 8417c478bd9Sstevel@tonic-gate return (watch_xcopyin(kaddr, uaddr, count) ? -1 : 0); 8427c478bd9Sstevel@tonic-gate } 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate static int 8467c478bd9Sstevel@tonic-gate watch_xcopyout(const void *kaddr, void *uaddr, size_t count) 8477c478bd9Sstevel@tonic-gate { 8487c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 8497c478bd9Sstevel@tonic-gate caddr_t watch_uaddr = (caddr_t)uaddr; 8507c478bd9Sstevel@tonic-gate caddr_t watch_kaddr = (caddr_t)kaddr; 8517c478bd9Sstevel@tonic-gate int error = 0; 8527c478bd9Sstevel@tonic-gate label_t ljb; 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate while (count && error == 0) { 8557c478bd9Sstevel@tonic-gate int watchcode; 8567c478bd9Sstevel@tonic-gate caddr_t vaddr; 8577c478bd9Sstevel@tonic-gate size_t part; 8587c478bd9Sstevel@tonic-gate size_t len; 8597c478bd9Sstevel@tonic-gate int ta; 8607c478bd9Sstevel@tonic-gate int mapped; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate if ((part = PAGESIZE - 8637c478bd9Sstevel@tonic-gate (((uintptr_t)uaddr) & PAGEOFFSET)) > count) 8647c478bd9Sstevel@tonic-gate part = count; 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate if (!pr_is_watchpage(watch_uaddr, S_WRITE)) 8677c478bd9Sstevel@tonic-gate watchcode = 0; 8687c478bd9Sstevel@tonic-gate else { 8697c478bd9Sstevel@tonic-gate vaddr = watch_uaddr; 8707c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, 8717c478bd9Sstevel@tonic-gate part, &len, S_WRITE); 8727c478bd9Sstevel@tonic-gate if (watchcode) { 8737c478bd9Sstevel@tonic-gate if (ta == 0) 8747c478bd9Sstevel@tonic-gate part = vaddr - watch_uaddr; 8757c478bd9Sstevel@tonic-gate else { 8767c478bd9Sstevel@tonic-gate len += vaddr - watch_uaddr; 8777c478bd9Sstevel@tonic-gate if (part > len) 8787c478bd9Sstevel@tonic-gate part = len; 8797c478bd9Sstevel@tonic-gate } 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate } 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate /* 8847c478bd9Sstevel@tonic-gate * Copy the initial part, up to a watched address, if any. 8857c478bd9Sstevel@tonic-gate */ 8867c478bd9Sstevel@tonic-gate if (part != 0) { 8877c478bd9Sstevel@tonic-gate mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1); 8887c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 8897c478bd9Sstevel@tonic-gate error = EFAULT; 8907c478bd9Sstevel@tonic-gate else 8917c478bd9Sstevel@tonic-gate copyout_noerr(watch_kaddr, watch_uaddr, part); 8927c478bd9Sstevel@tonic-gate no_fault(); 8937c478bd9Sstevel@tonic-gate if (mapped) 8947c478bd9Sstevel@tonic-gate pr_unmappage(watch_uaddr, part, S_WRITE, 1); 8957c478bd9Sstevel@tonic-gate watch_uaddr += part; 8967c478bd9Sstevel@tonic-gate watch_kaddr += part; 8977c478bd9Sstevel@tonic-gate count -= part; 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate /* 9017c478bd9Sstevel@tonic-gate * If trapafter was specified, then copy through the 9027c478bd9Sstevel@tonic-gate * watched area before taking the watchpoint trap. 9037c478bd9Sstevel@tonic-gate */ 9047c478bd9Sstevel@tonic-gate while (count && watchcode && ta && len > part && error == 0) { 9057c478bd9Sstevel@tonic-gate len -= part; 9067c478bd9Sstevel@tonic-gate if ((part = PAGESIZE) > count) 9077c478bd9Sstevel@tonic-gate part = count; 9087c478bd9Sstevel@tonic-gate if (part > len) 9097c478bd9Sstevel@tonic-gate part = len; 9107c478bd9Sstevel@tonic-gate mapped = pr_mappage(watch_uaddr, part, S_WRITE, 1); 9117c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 9127c478bd9Sstevel@tonic-gate error = EFAULT; 9137c478bd9Sstevel@tonic-gate else 9147c478bd9Sstevel@tonic-gate copyout_noerr(watch_kaddr, watch_uaddr, part); 9157c478bd9Sstevel@tonic-gate no_fault(); 9167c478bd9Sstevel@tonic-gate if (mapped) 9177c478bd9Sstevel@tonic-gate pr_unmappage(watch_uaddr, part, S_WRITE, 1); 9187c478bd9Sstevel@tonic-gate watch_uaddr += part; 9197c478bd9Sstevel@tonic-gate watch_kaddr += part; 9207c478bd9Sstevel@tonic-gate count -= part; 9217c478bd9Sstevel@tonic-gate } 9227c478bd9Sstevel@tonic-gate 9237c478bd9Sstevel@tonic-gate /* if we hit a watched address, do the watchpoint logic */ 9247c478bd9Sstevel@tonic-gate if (watchcode && 9257c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 9267c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 9277c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 9287c478bd9Sstevel@tonic-gate error = EFAULT; 9297c478bd9Sstevel@tonic-gate break; 9307c478bd9Sstevel@tonic-gate } 9317c478bd9Sstevel@tonic-gate } 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate return (error); 9347c478bd9Sstevel@tonic-gate } 9357c478bd9Sstevel@tonic-gate 9367c478bd9Sstevel@tonic-gate static int 9377c478bd9Sstevel@tonic-gate watch_copyout(const void *kaddr, void *uaddr, size_t count) 9387c478bd9Sstevel@tonic-gate { 9397c478bd9Sstevel@tonic-gate return (watch_xcopyout(kaddr, uaddr, count) ? -1 : 0); 9407c478bd9Sstevel@tonic-gate } 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate static int 9437c478bd9Sstevel@tonic-gate watch_copyinstr( 9447c478bd9Sstevel@tonic-gate const char *uaddr, 9457c478bd9Sstevel@tonic-gate char *kaddr, 9467c478bd9Sstevel@tonic-gate size_t maxlength, 9477c478bd9Sstevel@tonic-gate size_t *lencopied) 9487c478bd9Sstevel@tonic-gate { 9497c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 9507c478bd9Sstevel@tonic-gate size_t resid; 9517c478bd9Sstevel@tonic-gate int error = 0; 9527c478bd9Sstevel@tonic-gate label_t ljb; 9537c478bd9Sstevel@tonic-gate 9547c478bd9Sstevel@tonic-gate if ((resid = maxlength) == 0) 9557c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 9567c478bd9Sstevel@tonic-gate 9577c478bd9Sstevel@tonic-gate while (resid && error == 0) { 9587c478bd9Sstevel@tonic-gate int watchcode; 9597c478bd9Sstevel@tonic-gate caddr_t vaddr; 9607c478bd9Sstevel@tonic-gate size_t part; 9617c478bd9Sstevel@tonic-gate size_t len; 9627c478bd9Sstevel@tonic-gate size_t size; 9637c478bd9Sstevel@tonic-gate int ta; 9647c478bd9Sstevel@tonic-gate int mapped; 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate if ((part = PAGESIZE - 9677c478bd9Sstevel@tonic-gate (((uintptr_t)uaddr) & PAGEOFFSET)) > resid) 9687c478bd9Sstevel@tonic-gate part = resid; 9697c478bd9Sstevel@tonic-gate 9707c478bd9Sstevel@tonic-gate if (!pr_is_watchpage((caddr_t)uaddr, S_READ)) 9717c478bd9Sstevel@tonic-gate watchcode = 0; 9727c478bd9Sstevel@tonic-gate else { 9737c478bd9Sstevel@tonic-gate vaddr = (caddr_t)uaddr; 9747c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, 9757c478bd9Sstevel@tonic-gate part, &len, S_READ); 9767c478bd9Sstevel@tonic-gate if (watchcode) { 9777c478bd9Sstevel@tonic-gate if (ta == 0) 9787c478bd9Sstevel@tonic-gate part = vaddr - uaddr; 9797c478bd9Sstevel@tonic-gate else { 9807c478bd9Sstevel@tonic-gate len += vaddr - uaddr; 9817c478bd9Sstevel@tonic-gate if (part > len) 9827c478bd9Sstevel@tonic-gate part = len; 9837c478bd9Sstevel@tonic-gate } 9847c478bd9Sstevel@tonic-gate } 9857c478bd9Sstevel@tonic-gate } 9867c478bd9Sstevel@tonic-gate 9877c478bd9Sstevel@tonic-gate /* 9887c478bd9Sstevel@tonic-gate * Copy the initial part, up to a watched address, if any. 9897c478bd9Sstevel@tonic-gate */ 9907c478bd9Sstevel@tonic-gate if (part != 0) { 9917c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1); 9927c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 9937c478bd9Sstevel@tonic-gate error = EFAULT; 9947c478bd9Sstevel@tonic-gate else 9957c478bd9Sstevel@tonic-gate error = copyinstr_noerr(uaddr, kaddr, part, 9967c478bd9Sstevel@tonic-gate &size); 9977c478bd9Sstevel@tonic-gate no_fault(); 9987c478bd9Sstevel@tonic-gate if (mapped) 9997c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)uaddr, part, S_READ, 1); 10007c478bd9Sstevel@tonic-gate uaddr += size; 10017c478bd9Sstevel@tonic-gate kaddr += size; 10027c478bd9Sstevel@tonic-gate resid -= size; 10037c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG && resid > 0) 10047c478bd9Sstevel@tonic-gate error = 0; 10057c478bd9Sstevel@tonic-gate if (error != 0 || (watchcode && 10067c478bd9Sstevel@tonic-gate (uaddr < vaddr || kaddr[-1] == '\0'))) 10077c478bd9Sstevel@tonic-gate break; /* didn't reach the watched area */ 10087c478bd9Sstevel@tonic-gate } 10097c478bd9Sstevel@tonic-gate 10107c478bd9Sstevel@tonic-gate /* 10117c478bd9Sstevel@tonic-gate * If trapafter was specified, then copy through the 10127c478bd9Sstevel@tonic-gate * watched area before taking the watchpoint trap. 10137c478bd9Sstevel@tonic-gate */ 10147c478bd9Sstevel@tonic-gate while (resid && watchcode && ta && len > part && error == 0 && 10157c478bd9Sstevel@tonic-gate size == part && kaddr[-1] != '\0') { 10167c478bd9Sstevel@tonic-gate len -= part; 10177c478bd9Sstevel@tonic-gate if ((part = PAGESIZE) > resid) 10187c478bd9Sstevel@tonic-gate part = resid; 10197c478bd9Sstevel@tonic-gate if (part > len) 10207c478bd9Sstevel@tonic-gate part = len; 10217c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)uaddr, part, S_READ, 1); 10227c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 10237c478bd9Sstevel@tonic-gate error = EFAULT; 10247c478bd9Sstevel@tonic-gate else 10257c478bd9Sstevel@tonic-gate error = copyinstr_noerr(uaddr, kaddr, part, 10267c478bd9Sstevel@tonic-gate &size); 10277c478bd9Sstevel@tonic-gate no_fault(); 10287c478bd9Sstevel@tonic-gate if (mapped) 10297c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)uaddr, part, S_READ, 1); 10307c478bd9Sstevel@tonic-gate uaddr += size; 10317c478bd9Sstevel@tonic-gate kaddr += size; 10327c478bd9Sstevel@tonic-gate resid -= size; 10337c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG && resid > 0) 10347c478bd9Sstevel@tonic-gate error = 0; 10357c478bd9Sstevel@tonic-gate } 10367c478bd9Sstevel@tonic-gate 10377c478bd9Sstevel@tonic-gate /* if we hit a watched address, do the watchpoint logic */ 10387c478bd9Sstevel@tonic-gate if (watchcode && 10397c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 10407c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 10417c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 10427c478bd9Sstevel@tonic-gate error = EFAULT; 10437c478bd9Sstevel@tonic-gate break; 10447c478bd9Sstevel@tonic-gate } 10457c478bd9Sstevel@tonic-gate 10467c478bd9Sstevel@tonic-gate if (error == 0 && part != 0 && 10477c478bd9Sstevel@tonic-gate (size < part || kaddr[-1] == '\0')) 10487c478bd9Sstevel@tonic-gate break; 10497c478bd9Sstevel@tonic-gate } 10507c478bd9Sstevel@tonic-gate 10517c478bd9Sstevel@tonic-gate if (error != EFAULT && lencopied) 10527c478bd9Sstevel@tonic-gate *lencopied = maxlength - resid; 10537c478bd9Sstevel@tonic-gate return (error); 10547c478bd9Sstevel@tonic-gate } 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate static int 10577c478bd9Sstevel@tonic-gate watch_copyoutstr( 10587c478bd9Sstevel@tonic-gate const char *kaddr, 10597c478bd9Sstevel@tonic-gate char *uaddr, 10607c478bd9Sstevel@tonic-gate size_t maxlength, 10617c478bd9Sstevel@tonic-gate size_t *lencopied) 10627c478bd9Sstevel@tonic-gate { 10637c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 10647c478bd9Sstevel@tonic-gate size_t resid; 10657c478bd9Sstevel@tonic-gate int error = 0; 10667c478bd9Sstevel@tonic-gate label_t ljb; 10677c478bd9Sstevel@tonic-gate 10687c478bd9Sstevel@tonic-gate if ((resid = maxlength) == 0) 10697c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 10707c478bd9Sstevel@tonic-gate 10717c478bd9Sstevel@tonic-gate while (resid && error == 0) { 10727c478bd9Sstevel@tonic-gate int watchcode; 10737c478bd9Sstevel@tonic-gate caddr_t vaddr; 10747c478bd9Sstevel@tonic-gate size_t part; 10757c478bd9Sstevel@tonic-gate size_t len; 10767c478bd9Sstevel@tonic-gate size_t size; 10777c478bd9Sstevel@tonic-gate int ta; 10787c478bd9Sstevel@tonic-gate int mapped; 10797c478bd9Sstevel@tonic-gate 10807c478bd9Sstevel@tonic-gate if ((part = PAGESIZE - 10817c478bd9Sstevel@tonic-gate (((uintptr_t)uaddr) & PAGEOFFSET)) > resid) 10827c478bd9Sstevel@tonic-gate part = resid; 10837c478bd9Sstevel@tonic-gate 10847c478bd9Sstevel@tonic-gate if (!pr_is_watchpage(uaddr, S_WRITE)) { 10857c478bd9Sstevel@tonic-gate watchcode = 0; 10867c478bd9Sstevel@tonic-gate } else { 10877c478bd9Sstevel@tonic-gate vaddr = uaddr; 10887c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, 10897c478bd9Sstevel@tonic-gate part, &len, S_WRITE); 10907c478bd9Sstevel@tonic-gate if (watchcode && ta == 0) 10917c478bd9Sstevel@tonic-gate part = vaddr - uaddr; 10927c478bd9Sstevel@tonic-gate } 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate /* 10957c478bd9Sstevel@tonic-gate * Copy the initial part, up to a watched address, if any. 10967c478bd9Sstevel@tonic-gate */ 10977c478bd9Sstevel@tonic-gate if (part != 0) { 10987c478bd9Sstevel@tonic-gate mapped = pr_mappage(uaddr, part, S_WRITE, 1); 10997c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 11007c478bd9Sstevel@tonic-gate error = EFAULT; 11017c478bd9Sstevel@tonic-gate else 11027c478bd9Sstevel@tonic-gate error = copyoutstr_noerr(kaddr, uaddr, part, 11037c478bd9Sstevel@tonic-gate &size); 11047c478bd9Sstevel@tonic-gate no_fault(); 11057c478bd9Sstevel@tonic-gate if (mapped) 11067c478bd9Sstevel@tonic-gate pr_unmappage(uaddr, part, S_WRITE, 1); 11077c478bd9Sstevel@tonic-gate uaddr += size; 11087c478bd9Sstevel@tonic-gate kaddr += size; 11097c478bd9Sstevel@tonic-gate resid -= size; 11107c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG && resid > 0) 11117c478bd9Sstevel@tonic-gate error = 0; 11127c478bd9Sstevel@tonic-gate if (error != 0 || (watchcode && 11137c478bd9Sstevel@tonic-gate (uaddr < vaddr || kaddr[-1] == '\0'))) 11147c478bd9Sstevel@tonic-gate break; /* didn't reach the watched area */ 11157c478bd9Sstevel@tonic-gate } 11167c478bd9Sstevel@tonic-gate 11177c478bd9Sstevel@tonic-gate /* 11187c478bd9Sstevel@tonic-gate * If trapafter was specified, then copy through the 11197c478bd9Sstevel@tonic-gate * watched area before taking the watchpoint trap. 11207c478bd9Sstevel@tonic-gate */ 11217c478bd9Sstevel@tonic-gate while (resid && watchcode && ta && len > part && error == 0 && 11227c478bd9Sstevel@tonic-gate size == part && kaddr[-1] != '\0') { 11237c478bd9Sstevel@tonic-gate len -= part; 11247c478bd9Sstevel@tonic-gate if ((part = PAGESIZE) > resid) 11257c478bd9Sstevel@tonic-gate part = resid; 11267c478bd9Sstevel@tonic-gate if (part > len) 11277c478bd9Sstevel@tonic-gate part = len; 11287c478bd9Sstevel@tonic-gate mapped = pr_mappage(uaddr, part, S_WRITE, 1); 11297c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 11307c478bd9Sstevel@tonic-gate error = EFAULT; 11317c478bd9Sstevel@tonic-gate else 11327c478bd9Sstevel@tonic-gate error = copyoutstr_noerr(kaddr, uaddr, part, 11337c478bd9Sstevel@tonic-gate &size); 11347c478bd9Sstevel@tonic-gate no_fault(); 11357c478bd9Sstevel@tonic-gate if (mapped) 11367c478bd9Sstevel@tonic-gate pr_unmappage(uaddr, part, S_WRITE, 1); 11377c478bd9Sstevel@tonic-gate uaddr += size; 11387c478bd9Sstevel@tonic-gate kaddr += size; 11397c478bd9Sstevel@tonic-gate resid -= size; 11407c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG && resid > 0) 11417c478bd9Sstevel@tonic-gate error = 0; 11427c478bd9Sstevel@tonic-gate } 11437c478bd9Sstevel@tonic-gate 11447c478bd9Sstevel@tonic-gate /* if we hit a watched address, do the watchpoint logic */ 11457c478bd9Sstevel@tonic-gate if (watchcode && 11467c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 11477c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 11487c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 11497c478bd9Sstevel@tonic-gate error = EFAULT; 11507c478bd9Sstevel@tonic-gate break; 11517c478bd9Sstevel@tonic-gate } 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate if (error == 0 && part != 0 && 11547c478bd9Sstevel@tonic-gate (size < part || kaddr[-1] == '\0')) 11557c478bd9Sstevel@tonic-gate break; 11567c478bd9Sstevel@tonic-gate } 11577c478bd9Sstevel@tonic-gate 11587c478bd9Sstevel@tonic-gate if (error != EFAULT && lencopied) 11597c478bd9Sstevel@tonic-gate *lencopied = maxlength - resid; 11607c478bd9Sstevel@tonic-gate return (error); 11617c478bd9Sstevel@tonic-gate } 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate typedef int (*fuword_func)(const void *, void *); 11647c478bd9Sstevel@tonic-gate 11657c478bd9Sstevel@tonic-gate /* 11667c478bd9Sstevel@tonic-gate * Generic form of watch_fuword8(), watch_fuword16(), etc. 11677c478bd9Sstevel@tonic-gate */ 11687c478bd9Sstevel@tonic-gate static int 11697c478bd9Sstevel@tonic-gate watch_fuword(const void *addr, void *dst, fuword_func func, size_t size) 11707c478bd9Sstevel@tonic-gate { 11717c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 11727c478bd9Sstevel@tonic-gate int watchcode; 11737c478bd9Sstevel@tonic-gate caddr_t vaddr; 11747c478bd9Sstevel@tonic-gate int mapped; 11757c478bd9Sstevel@tonic-gate int rv = 0; 11767c478bd9Sstevel@tonic-gate int ta; 11777c478bd9Sstevel@tonic-gate label_t ljb; 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate for (;;) { 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate vaddr = (caddr_t)addr; 11827c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, size, NULL, S_READ); 11837c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 11847c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)addr, size, S_READ, 1); 11857c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 11867c478bd9Sstevel@tonic-gate rv = -1; 11877c478bd9Sstevel@tonic-gate else 11887c478bd9Sstevel@tonic-gate (*func)(addr, dst); 11897c478bd9Sstevel@tonic-gate no_fault(); 11907c478bd9Sstevel@tonic-gate if (mapped) 11917c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, size, S_READ, 1); 11927c478bd9Sstevel@tonic-gate } 11937c478bd9Sstevel@tonic-gate if (watchcode && 11947c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 11957c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 11967c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 11977c478bd9Sstevel@tonic-gate rv = -1; 11987c478bd9Sstevel@tonic-gate break; 11997c478bd9Sstevel@tonic-gate } 12007c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) 12017c478bd9Sstevel@tonic-gate break; 12027c478bd9Sstevel@tonic-gate } 12037c478bd9Sstevel@tonic-gate 12047c478bd9Sstevel@tonic-gate return (rv); 12057c478bd9Sstevel@tonic-gate } 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate static int 12087c478bd9Sstevel@tonic-gate watch_fuword8(const void *addr, uint8_t *dst) 12097c478bd9Sstevel@tonic-gate { 12107c478bd9Sstevel@tonic-gate return (watch_fuword(addr, dst, (fuword_func)fuword8_noerr, 12117c478bd9Sstevel@tonic-gate sizeof (*dst))); 12127c478bd9Sstevel@tonic-gate } 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate static int 12157c478bd9Sstevel@tonic-gate watch_fuword16(const void *addr, uint16_t *dst) 12167c478bd9Sstevel@tonic-gate { 12177c478bd9Sstevel@tonic-gate return (watch_fuword(addr, dst, (fuword_func)fuword16_noerr, 12187c478bd9Sstevel@tonic-gate sizeof (*dst))); 12197c478bd9Sstevel@tonic-gate } 12207c478bd9Sstevel@tonic-gate 12217c478bd9Sstevel@tonic-gate static int 12227c478bd9Sstevel@tonic-gate watch_fuword32(const void *addr, uint32_t *dst) 12237c478bd9Sstevel@tonic-gate { 12247c478bd9Sstevel@tonic-gate return (watch_fuword(addr, dst, (fuword_func)fuword32_noerr, 12257c478bd9Sstevel@tonic-gate sizeof (*dst))); 12267c478bd9Sstevel@tonic-gate } 12277c478bd9Sstevel@tonic-gate 12287c478bd9Sstevel@tonic-gate #ifdef _LP64 12297c478bd9Sstevel@tonic-gate static int 12307c478bd9Sstevel@tonic-gate watch_fuword64(const void *addr, uint64_t *dst) 12317c478bd9Sstevel@tonic-gate { 12327c478bd9Sstevel@tonic-gate return (watch_fuword(addr, dst, (fuword_func)fuword64_noerr, 12337c478bd9Sstevel@tonic-gate sizeof (*dst))); 12347c478bd9Sstevel@tonic-gate } 12357c478bd9Sstevel@tonic-gate #endif 12367c478bd9Sstevel@tonic-gate 12377c478bd9Sstevel@tonic-gate 12387c478bd9Sstevel@tonic-gate static int 12397c478bd9Sstevel@tonic-gate watch_suword8(void *addr, uint8_t value) 12407c478bd9Sstevel@tonic-gate { 12417c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 12427c478bd9Sstevel@tonic-gate int watchcode; 12437c478bd9Sstevel@tonic-gate caddr_t vaddr; 12447c478bd9Sstevel@tonic-gate int mapped; 12457c478bd9Sstevel@tonic-gate int rv = 0; 12467c478bd9Sstevel@tonic-gate int ta; 12477c478bd9Sstevel@tonic-gate label_t ljb; 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate for (;;) { 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate vaddr = (caddr_t)addr; 12527c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL, 12537c478bd9Sstevel@tonic-gate S_WRITE); 12547c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 12557c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)addr, sizeof (value), 12567c478bd9Sstevel@tonic-gate S_WRITE, 1); 12577c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 12587c478bd9Sstevel@tonic-gate rv = -1; 12597c478bd9Sstevel@tonic-gate else 12607c478bd9Sstevel@tonic-gate suword8_noerr(addr, value); 12617c478bd9Sstevel@tonic-gate no_fault(); 12627c478bd9Sstevel@tonic-gate if (mapped) 12637c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, sizeof (value), 12647c478bd9Sstevel@tonic-gate S_WRITE, 1); 12657c478bd9Sstevel@tonic-gate } 12667c478bd9Sstevel@tonic-gate if (watchcode && 12677c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 12687c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 12697c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 12707c478bd9Sstevel@tonic-gate rv = -1; 12717c478bd9Sstevel@tonic-gate break; 12727c478bd9Sstevel@tonic-gate } 12737c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) 12747c478bd9Sstevel@tonic-gate break; 12757c478bd9Sstevel@tonic-gate } 12767c478bd9Sstevel@tonic-gate 12777c478bd9Sstevel@tonic-gate return (rv); 12787c478bd9Sstevel@tonic-gate } 12797c478bd9Sstevel@tonic-gate 12807c478bd9Sstevel@tonic-gate static int 12817c478bd9Sstevel@tonic-gate watch_suword16(void *addr, uint16_t value) 12827c478bd9Sstevel@tonic-gate { 12837c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 12847c478bd9Sstevel@tonic-gate int watchcode; 12857c478bd9Sstevel@tonic-gate caddr_t vaddr; 12867c478bd9Sstevel@tonic-gate int mapped; 12877c478bd9Sstevel@tonic-gate int rv = 0; 12887c478bd9Sstevel@tonic-gate int ta; 12897c478bd9Sstevel@tonic-gate label_t ljb; 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate for (;;) { 12927c478bd9Sstevel@tonic-gate 12937c478bd9Sstevel@tonic-gate vaddr = (caddr_t)addr; 12947c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL, 12957c478bd9Sstevel@tonic-gate S_WRITE); 12967c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 12977c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)addr, sizeof (value), 12987c478bd9Sstevel@tonic-gate S_WRITE, 1); 12997c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 13007c478bd9Sstevel@tonic-gate rv = -1; 13017c478bd9Sstevel@tonic-gate else 13027c478bd9Sstevel@tonic-gate suword16_noerr(addr, value); 13037c478bd9Sstevel@tonic-gate no_fault(); 13047c478bd9Sstevel@tonic-gate if (mapped) 13057c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, sizeof (value), 13067c478bd9Sstevel@tonic-gate S_WRITE, 1); 13077c478bd9Sstevel@tonic-gate } 13087c478bd9Sstevel@tonic-gate if (watchcode && 13097c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 13107c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 13117c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 13127c478bd9Sstevel@tonic-gate rv = -1; 13137c478bd9Sstevel@tonic-gate break; 13147c478bd9Sstevel@tonic-gate } 13157c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) 13167c478bd9Sstevel@tonic-gate break; 13177c478bd9Sstevel@tonic-gate } 13187c478bd9Sstevel@tonic-gate 13197c478bd9Sstevel@tonic-gate return (rv); 13207c478bd9Sstevel@tonic-gate } 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate static int 13237c478bd9Sstevel@tonic-gate watch_suword32(void *addr, uint32_t value) 13247c478bd9Sstevel@tonic-gate { 13257c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 13267c478bd9Sstevel@tonic-gate int watchcode; 13277c478bd9Sstevel@tonic-gate caddr_t vaddr; 13287c478bd9Sstevel@tonic-gate int mapped; 13297c478bd9Sstevel@tonic-gate int rv = 0; 13307c478bd9Sstevel@tonic-gate int ta; 13317c478bd9Sstevel@tonic-gate label_t ljb; 13327c478bd9Sstevel@tonic-gate 13337c478bd9Sstevel@tonic-gate for (;;) { 13347c478bd9Sstevel@tonic-gate 13357c478bd9Sstevel@tonic-gate vaddr = (caddr_t)addr; 13367c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL, 13377c478bd9Sstevel@tonic-gate S_WRITE); 13387c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 13397c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)addr, sizeof (value), 13407c478bd9Sstevel@tonic-gate S_WRITE, 1); 13417c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 13427c478bd9Sstevel@tonic-gate rv = -1; 13437c478bd9Sstevel@tonic-gate else 13447c478bd9Sstevel@tonic-gate suword32_noerr(addr, value); 13457c478bd9Sstevel@tonic-gate no_fault(); 13467c478bd9Sstevel@tonic-gate if (mapped) 13477c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, sizeof (value), 13487c478bd9Sstevel@tonic-gate S_WRITE, 1); 13497c478bd9Sstevel@tonic-gate } 13507c478bd9Sstevel@tonic-gate if (watchcode && 13517c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 13527c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 13537c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 13547c478bd9Sstevel@tonic-gate rv = -1; 13557c478bd9Sstevel@tonic-gate break; 13567c478bd9Sstevel@tonic-gate } 13577c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) 13587c478bd9Sstevel@tonic-gate break; 13597c478bd9Sstevel@tonic-gate } 13607c478bd9Sstevel@tonic-gate 13617c478bd9Sstevel@tonic-gate return (rv); 13627c478bd9Sstevel@tonic-gate } 13637c478bd9Sstevel@tonic-gate 13647c478bd9Sstevel@tonic-gate #ifdef _LP64 13657c478bd9Sstevel@tonic-gate static int 13667c478bd9Sstevel@tonic-gate watch_suword64(void *addr, uint64_t value) 13677c478bd9Sstevel@tonic-gate { 13687c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 13697c478bd9Sstevel@tonic-gate int watchcode; 13707c478bd9Sstevel@tonic-gate caddr_t vaddr; 13717c478bd9Sstevel@tonic-gate int mapped; 13727c478bd9Sstevel@tonic-gate int rv = 0; 13737c478bd9Sstevel@tonic-gate int ta; 13747c478bd9Sstevel@tonic-gate label_t ljb; 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate for (;;) { 13777c478bd9Sstevel@tonic-gate 13787c478bd9Sstevel@tonic-gate vaddr = (caddr_t)addr; 13797c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, sizeof (value), NULL, 13807c478bd9Sstevel@tonic-gate S_WRITE); 13817c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 13827c478bd9Sstevel@tonic-gate mapped = pr_mappage((caddr_t)addr, sizeof (value), 13837c478bd9Sstevel@tonic-gate S_WRITE, 1); 13847c478bd9Sstevel@tonic-gate if (on_fault(&ljb)) 13857c478bd9Sstevel@tonic-gate rv = -1; 13867c478bd9Sstevel@tonic-gate else 13877c478bd9Sstevel@tonic-gate suword64_noerr(addr, value); 13887c478bd9Sstevel@tonic-gate no_fault(); 13897c478bd9Sstevel@tonic-gate if (mapped) 13907c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, sizeof (value), 13917c478bd9Sstevel@tonic-gate S_WRITE, 1); 13927c478bd9Sstevel@tonic-gate } 13937c478bd9Sstevel@tonic-gate if (watchcode && 13947c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 13957c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 13967c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 13977c478bd9Sstevel@tonic-gate rv = -1; 13987c478bd9Sstevel@tonic-gate break; 13997c478bd9Sstevel@tonic-gate } 14007c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) 14017c478bd9Sstevel@tonic-gate break; 14027c478bd9Sstevel@tonic-gate } 14037c478bd9Sstevel@tonic-gate 14047c478bd9Sstevel@tonic-gate return (rv); 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate #endif /* _LP64 */ 14077c478bd9Sstevel@tonic-gate 14087c478bd9Sstevel@tonic-gate /* 14097c478bd9Sstevel@tonic-gate * Check for watched addresses in the given address space. 14107c478bd9Sstevel@tonic-gate * Return 1 if this is true, otherwise 0. 14117c478bd9Sstevel@tonic-gate */ 14127c478bd9Sstevel@tonic-gate static int 14137c478bd9Sstevel@tonic-gate pr_is_watched(caddr_t base, size_t len, int rw) 14147c478bd9Sstevel@tonic-gate { 14157c478bd9Sstevel@tonic-gate caddr_t saddr = (caddr_t)((uintptr_t)base & (uintptr_t)PAGEMASK); 14167c478bd9Sstevel@tonic-gate caddr_t eaddr = base + len; 14177c478bd9Sstevel@tonic-gate caddr_t paddr; 14187c478bd9Sstevel@tonic-gate 14197c478bd9Sstevel@tonic-gate for (paddr = saddr; paddr < eaddr; paddr += PAGESIZE) { 14207c478bd9Sstevel@tonic-gate if (pr_is_watchpage(paddr, rw)) 14217c478bd9Sstevel@tonic-gate return (1); 14227c478bd9Sstevel@tonic-gate } 14237c478bd9Sstevel@tonic-gate 14247c478bd9Sstevel@tonic-gate return (0); 14257c478bd9Sstevel@tonic-gate } 14267c478bd9Sstevel@tonic-gate 14277c478bd9Sstevel@tonic-gate /* 14287c478bd9Sstevel@tonic-gate * Wrapper for the physio() function. 14297c478bd9Sstevel@tonic-gate * Splits one uio operation with multiple iovecs into uio operations with 14307c478bd9Sstevel@tonic-gate * only one iovecs to do the watchpoint handling separately for each iovecs. 14317c478bd9Sstevel@tonic-gate */ 14327c478bd9Sstevel@tonic-gate static int 14337c478bd9Sstevel@tonic-gate watch_physio(int (*strat)(struct buf *), struct buf *bp, dev_t dev, 14347c478bd9Sstevel@tonic-gate int rw, void (*mincnt)(struct buf *), struct uio *uio) 14357c478bd9Sstevel@tonic-gate { 14367c478bd9Sstevel@tonic-gate struct uio auio; 14377c478bd9Sstevel@tonic-gate struct iovec *iov; 14387c478bd9Sstevel@tonic-gate caddr_t base; 14397c478bd9Sstevel@tonic-gate size_t len; 14407c478bd9Sstevel@tonic-gate int seg_rw; 14417c478bd9Sstevel@tonic-gate int error = 0; 14427c478bd9Sstevel@tonic-gate 14437c478bd9Sstevel@tonic-gate if (uio->uio_segflg == UIO_SYSSPACE) 14447c478bd9Sstevel@tonic-gate return (default_physio(strat, bp, dev, rw, mincnt, uio)); 14457c478bd9Sstevel@tonic-gate 14467c478bd9Sstevel@tonic-gate seg_rw = (rw == B_READ) ? S_WRITE : S_READ; 14477c478bd9Sstevel@tonic-gate 14487c478bd9Sstevel@tonic-gate while (uio->uio_iovcnt > 0) { 14497c478bd9Sstevel@tonic-gate if (uio->uio_resid == 0) { 14507c478bd9Sstevel@tonic-gate /* 14517c478bd9Sstevel@tonic-gate * Make sure to return the uio structure with the 14527c478bd9Sstevel@tonic-gate * same values as default_physio() does. 14537c478bd9Sstevel@tonic-gate */ 14547c478bd9Sstevel@tonic-gate uio->uio_iov++; 14557c478bd9Sstevel@tonic-gate uio->uio_iovcnt--; 14567c478bd9Sstevel@tonic-gate continue; 14577c478bd9Sstevel@tonic-gate } 14587c478bd9Sstevel@tonic-gate 14597c478bd9Sstevel@tonic-gate iov = uio->uio_iov; 14607c478bd9Sstevel@tonic-gate len = MIN(iov->iov_len, uio->uio_resid); 14617c478bd9Sstevel@tonic-gate 14627c478bd9Sstevel@tonic-gate auio.uio_iovcnt = 1; 14637c478bd9Sstevel@tonic-gate auio.uio_iov = iov; 14647c478bd9Sstevel@tonic-gate auio.uio_resid = len; 14657c478bd9Sstevel@tonic-gate auio.uio_loffset = uio->uio_loffset; 14667c478bd9Sstevel@tonic-gate auio.uio_llimit = uio->uio_llimit; 14677c478bd9Sstevel@tonic-gate auio.uio_fmode = uio->uio_fmode; 14687c478bd9Sstevel@tonic-gate auio.uio_extflg = uio->uio_extflg; 14697c478bd9Sstevel@tonic-gate auio.uio_segflg = uio->uio_segflg; 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate base = iov->iov_base; 14727c478bd9Sstevel@tonic-gate 14737c478bd9Sstevel@tonic-gate if (!pr_is_watched(base, len, seg_rw)) { 14747c478bd9Sstevel@tonic-gate /* 14757c478bd9Sstevel@tonic-gate * The given memory references don't cover a 14767c478bd9Sstevel@tonic-gate * watched page. 14777c478bd9Sstevel@tonic-gate */ 14787c478bd9Sstevel@tonic-gate error = default_physio(strat, bp, dev, rw, mincnt, 14797c478bd9Sstevel@tonic-gate &auio); 14807c478bd9Sstevel@tonic-gate 14817c478bd9Sstevel@tonic-gate /* Update uio with values from auio. */ 14827c478bd9Sstevel@tonic-gate len -= auio.uio_resid; 14837c478bd9Sstevel@tonic-gate uio->uio_resid -= len; 14847c478bd9Sstevel@tonic-gate uio->uio_loffset += len; 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate /* 14877c478bd9Sstevel@tonic-gate * Return if an error occurred or not all data 14887c478bd9Sstevel@tonic-gate * was copied. 14897c478bd9Sstevel@tonic-gate */ 14907c478bd9Sstevel@tonic-gate if (auio.uio_resid || error) 14917c478bd9Sstevel@tonic-gate break; 14927c478bd9Sstevel@tonic-gate uio->uio_iov++; 14937c478bd9Sstevel@tonic-gate uio->uio_iovcnt--; 14947c478bd9Sstevel@tonic-gate } else { 14957c478bd9Sstevel@tonic-gate int mapped, watchcode, ta; 14967c478bd9Sstevel@tonic-gate caddr_t vaddr = base; 14977c478bd9Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 14987c478bd9Sstevel@tonic-gate 14997c478bd9Sstevel@tonic-gate watchcode = pr_is_watchpoint(&vaddr, &ta, len, 15007c478bd9Sstevel@tonic-gate NULL, seg_rw); 15017c478bd9Sstevel@tonic-gate 15027c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 15037c478bd9Sstevel@tonic-gate /* 15047c478bd9Sstevel@tonic-gate * Do the io if the given memory references 15057c478bd9Sstevel@tonic-gate * don't cover a watched area (watchcode=0) 15067c478bd9Sstevel@tonic-gate * or if WA_TRAPAFTER was specified. 15077c478bd9Sstevel@tonic-gate */ 15087c478bd9Sstevel@tonic-gate mapped = pr_mappage(base, len, seg_rw, 1); 15097c478bd9Sstevel@tonic-gate error = default_physio(strat, bp, dev, rw, 15107c478bd9Sstevel@tonic-gate mincnt, &auio); 15117c478bd9Sstevel@tonic-gate if (mapped) 15127c478bd9Sstevel@tonic-gate pr_unmappage(base, len, seg_rw, 1); 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate len -= auio.uio_resid; 15157c478bd9Sstevel@tonic-gate uio->uio_resid -= len; 15167c478bd9Sstevel@tonic-gate uio->uio_loffset += len; 15177c478bd9Sstevel@tonic-gate } 15187c478bd9Sstevel@tonic-gate 15197c478bd9Sstevel@tonic-gate /* 15207c478bd9Sstevel@tonic-gate * If we hit a watched address, do the watchpoint logic. 15217c478bd9Sstevel@tonic-gate */ 15227c478bd9Sstevel@tonic-gate if (watchcode && 15237c478bd9Sstevel@tonic-gate (!sys_watchpoint(vaddr, watchcode, ta) || 15247c478bd9Sstevel@tonic-gate lwp->lwp_sysabort)) { 15257c478bd9Sstevel@tonic-gate lwp->lwp_sysabort = 0; 15267c478bd9Sstevel@tonic-gate return (EFAULT); 15277c478bd9Sstevel@tonic-gate } 15287c478bd9Sstevel@tonic-gate 15297c478bd9Sstevel@tonic-gate /* 15307c478bd9Sstevel@tonic-gate * Check for errors from default_physio(). 15317c478bd9Sstevel@tonic-gate */ 15327c478bd9Sstevel@tonic-gate if (watchcode == 0 || ta != 0) { 15337c478bd9Sstevel@tonic-gate if (auio.uio_resid || error) 15347c478bd9Sstevel@tonic-gate break; 15357c478bd9Sstevel@tonic-gate uio->uio_iov++; 15367c478bd9Sstevel@tonic-gate uio->uio_iovcnt--; 15377c478bd9Sstevel@tonic-gate } 15387c478bd9Sstevel@tonic-gate } 15397c478bd9Sstevel@tonic-gate } 15407c478bd9Sstevel@tonic-gate 15417c478bd9Sstevel@tonic-gate return (error); 15427c478bd9Sstevel@tonic-gate } 15437c478bd9Sstevel@tonic-gate 15447c478bd9Sstevel@tonic-gate int 15457c478bd9Sstevel@tonic-gate wa_compare(const void *a, const void *b) 15467c478bd9Sstevel@tonic-gate { 15477c478bd9Sstevel@tonic-gate const watched_area_t *pa = a; 15487c478bd9Sstevel@tonic-gate const watched_area_t *pb = b; 15497c478bd9Sstevel@tonic-gate 15507c478bd9Sstevel@tonic-gate if (pa->wa_vaddr < pb->wa_vaddr) 15517c478bd9Sstevel@tonic-gate return (-1); 15527c478bd9Sstevel@tonic-gate else if (pa->wa_vaddr > pb->wa_vaddr) 15537c478bd9Sstevel@tonic-gate return (1); 15547c478bd9Sstevel@tonic-gate else 15557c478bd9Sstevel@tonic-gate return (0); 15567c478bd9Sstevel@tonic-gate } 15577c478bd9Sstevel@tonic-gate 15587c478bd9Sstevel@tonic-gate int 15597c478bd9Sstevel@tonic-gate wp_compare(const void *a, const void *b) 15607c478bd9Sstevel@tonic-gate { 15617c478bd9Sstevel@tonic-gate const watched_page_t *pa = a; 15627c478bd9Sstevel@tonic-gate const watched_page_t *pb = b; 15637c478bd9Sstevel@tonic-gate 15647c478bd9Sstevel@tonic-gate if (pa->wp_vaddr < pb->wp_vaddr) 15657c478bd9Sstevel@tonic-gate return (-1); 15667c478bd9Sstevel@tonic-gate else if (pa->wp_vaddr > pb->wp_vaddr) 15677c478bd9Sstevel@tonic-gate return (1); 15687c478bd9Sstevel@tonic-gate else 15697c478bd9Sstevel@tonic-gate return (0); 15707c478bd9Sstevel@tonic-gate } 15717c478bd9Sstevel@tonic-gate 15727c478bd9Sstevel@tonic-gate /* 15737c478bd9Sstevel@tonic-gate * Given an address range, finds the first watched area which overlaps some or 15747c478bd9Sstevel@tonic-gate * all of the range. 15757c478bd9Sstevel@tonic-gate */ 15767c478bd9Sstevel@tonic-gate watched_area_t * 15777c478bd9Sstevel@tonic-gate pr_find_watched_area(proc_t *p, watched_area_t *pwa, avl_index_t *where) 15787c478bd9Sstevel@tonic-gate { 15797c478bd9Sstevel@tonic-gate caddr_t vaddr = pwa->wa_vaddr; 15807c478bd9Sstevel@tonic-gate caddr_t eaddr = pwa->wa_eaddr; 15817c478bd9Sstevel@tonic-gate watched_area_t *wap; 15827c478bd9Sstevel@tonic-gate avl_index_t real_where; 15837c478bd9Sstevel@tonic-gate 15847c478bd9Sstevel@tonic-gate /* First, check if there is an exact match. */ 15857c478bd9Sstevel@tonic-gate wap = avl_find(&p->p_warea, pwa, &real_where); 15867c478bd9Sstevel@tonic-gate 15877c478bd9Sstevel@tonic-gate 15887c478bd9Sstevel@tonic-gate /* Check to see if we overlap with the previous area. */ 15897c478bd9Sstevel@tonic-gate if (wap == NULL) { 15907c478bd9Sstevel@tonic-gate wap = avl_nearest(&p->p_warea, real_where, AVL_BEFORE); 15917c478bd9Sstevel@tonic-gate if (wap != NULL && 15927c478bd9Sstevel@tonic-gate (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr)) 15937c478bd9Sstevel@tonic-gate wap = NULL; 15947c478bd9Sstevel@tonic-gate } 15957c478bd9Sstevel@tonic-gate 15967c478bd9Sstevel@tonic-gate /* Try the next area. */ 15977c478bd9Sstevel@tonic-gate if (wap == NULL) { 15987c478bd9Sstevel@tonic-gate wap = avl_nearest(&p->p_warea, real_where, AVL_AFTER); 15997c478bd9Sstevel@tonic-gate if (wap != NULL && 16007c478bd9Sstevel@tonic-gate (vaddr >= wap->wa_eaddr || eaddr <= wap->wa_vaddr)) 16017c478bd9Sstevel@tonic-gate wap = NULL; 16027c478bd9Sstevel@tonic-gate } 16037c478bd9Sstevel@tonic-gate 16047c478bd9Sstevel@tonic-gate if (where) 16057c478bd9Sstevel@tonic-gate *where = real_where; 16067c478bd9Sstevel@tonic-gate 16077c478bd9Sstevel@tonic-gate return (wap); 16087c478bd9Sstevel@tonic-gate } 16097c478bd9Sstevel@tonic-gate 16107c478bd9Sstevel@tonic-gate void 16117c478bd9Sstevel@tonic-gate watch_enable(kthread_id_t t) 16127c478bd9Sstevel@tonic-gate { 16137c478bd9Sstevel@tonic-gate t->t_proc_flag |= TP_WATCHPT; 16147c478bd9Sstevel@tonic-gate install_copyops(t, &watch_copyops); 16157c478bd9Sstevel@tonic-gate } 16167c478bd9Sstevel@tonic-gate 16177c478bd9Sstevel@tonic-gate void 16187c478bd9Sstevel@tonic-gate watch_disable(kthread_id_t t) 16197c478bd9Sstevel@tonic-gate { 16207c478bd9Sstevel@tonic-gate t->t_proc_flag &= ~TP_WATCHPT; 16217c478bd9Sstevel@tonic-gate remove_copyops(t); 16227c478bd9Sstevel@tonic-gate } 16237c478bd9Sstevel@tonic-gate 16247c478bd9Sstevel@tonic-gate int 16257c478bd9Sstevel@tonic-gate copyin_nowatch(const void *uaddr, void *kaddr, size_t len) 16267c478bd9Sstevel@tonic-gate { 16277c478bd9Sstevel@tonic-gate int watched, ret; 16287c478bd9Sstevel@tonic-gate 16297c478bd9Sstevel@tonic-gate watched = watch_disable_addr(uaddr, len, S_READ); 16307c478bd9Sstevel@tonic-gate ret = copyin(uaddr, kaddr, len); 16317c478bd9Sstevel@tonic-gate if (watched) 16327c478bd9Sstevel@tonic-gate watch_enable_addr(uaddr, len, S_READ); 16337c478bd9Sstevel@tonic-gate 16347c478bd9Sstevel@tonic-gate return (ret); 16357c478bd9Sstevel@tonic-gate } 16367c478bd9Sstevel@tonic-gate 16377c478bd9Sstevel@tonic-gate int 16387c478bd9Sstevel@tonic-gate copyout_nowatch(const void *kaddr, void *uaddr, size_t len) 16397c478bd9Sstevel@tonic-gate { 16407c478bd9Sstevel@tonic-gate int watched, ret; 16417c478bd9Sstevel@tonic-gate 16427c478bd9Sstevel@tonic-gate watched = watch_disable_addr(uaddr, len, S_WRITE); 16437c478bd9Sstevel@tonic-gate ret = copyout(kaddr, uaddr, len); 16447c478bd9Sstevel@tonic-gate if (watched) 16457c478bd9Sstevel@tonic-gate watch_enable_addr(uaddr, len, S_WRITE); 16467c478bd9Sstevel@tonic-gate 16477c478bd9Sstevel@tonic-gate return (ret); 16487c478bd9Sstevel@tonic-gate } 16497c478bd9Sstevel@tonic-gate 16507c478bd9Sstevel@tonic-gate #ifdef _LP64 16517c478bd9Sstevel@tonic-gate int 16527c478bd9Sstevel@tonic-gate fuword64_nowatch(const void *addr, uint64_t *value) 16537c478bd9Sstevel@tonic-gate { 16547c478bd9Sstevel@tonic-gate int watched, ret; 16557c478bd9Sstevel@tonic-gate 16567c478bd9Sstevel@tonic-gate watched = watch_disable_addr(addr, sizeof (*value), S_READ); 16577c478bd9Sstevel@tonic-gate ret = fuword64(addr, value); 16587c478bd9Sstevel@tonic-gate if (watched) 16597c478bd9Sstevel@tonic-gate watch_enable_addr(addr, sizeof (*value), S_READ); 16607c478bd9Sstevel@tonic-gate 16617c478bd9Sstevel@tonic-gate return (ret); 16627c478bd9Sstevel@tonic-gate } 16637c478bd9Sstevel@tonic-gate #endif 16647c478bd9Sstevel@tonic-gate 16657c478bd9Sstevel@tonic-gate int 16667c478bd9Sstevel@tonic-gate fuword32_nowatch(const void *addr, uint32_t *value) 16677c478bd9Sstevel@tonic-gate { 16687c478bd9Sstevel@tonic-gate int watched, ret; 16697c478bd9Sstevel@tonic-gate 16707c478bd9Sstevel@tonic-gate watched = watch_disable_addr(addr, sizeof (*value), S_READ); 16717c478bd9Sstevel@tonic-gate ret = fuword32(addr, value); 16727c478bd9Sstevel@tonic-gate if (watched) 16737c478bd9Sstevel@tonic-gate watch_enable_addr(addr, sizeof (*value), S_READ); 16747c478bd9Sstevel@tonic-gate 16757c478bd9Sstevel@tonic-gate return (ret); 16767c478bd9Sstevel@tonic-gate } 16777c478bd9Sstevel@tonic-gate 16787c478bd9Sstevel@tonic-gate #ifdef _LP64 16797c478bd9Sstevel@tonic-gate int 16807c478bd9Sstevel@tonic-gate suword64_nowatch(void *addr, uint64_t value) 16817c478bd9Sstevel@tonic-gate { 16827c478bd9Sstevel@tonic-gate int watched, ret; 16837c478bd9Sstevel@tonic-gate 16847c478bd9Sstevel@tonic-gate watched = watch_disable_addr(addr, sizeof (value), S_WRITE); 16857c478bd9Sstevel@tonic-gate ret = suword64(addr, value); 16867c478bd9Sstevel@tonic-gate if (watched) 16877c478bd9Sstevel@tonic-gate watch_enable_addr(addr, sizeof (value), S_WRITE); 16887c478bd9Sstevel@tonic-gate 16897c478bd9Sstevel@tonic-gate return (ret); 16907c478bd9Sstevel@tonic-gate } 16917c478bd9Sstevel@tonic-gate #endif 16927c478bd9Sstevel@tonic-gate 16937c478bd9Sstevel@tonic-gate int 16947c478bd9Sstevel@tonic-gate suword32_nowatch(void *addr, uint32_t value) 16957c478bd9Sstevel@tonic-gate { 16967c478bd9Sstevel@tonic-gate int watched, ret; 16977c478bd9Sstevel@tonic-gate 16987c478bd9Sstevel@tonic-gate watched = watch_disable_addr(addr, sizeof (value), S_WRITE); 16997c478bd9Sstevel@tonic-gate ret = suword32(addr, value); 17007c478bd9Sstevel@tonic-gate if (watched) 17017c478bd9Sstevel@tonic-gate watch_enable_addr(addr, sizeof (value), S_WRITE); 17027c478bd9Sstevel@tonic-gate 17037c478bd9Sstevel@tonic-gate return (ret); 17047c478bd9Sstevel@tonic-gate } 17057c478bd9Sstevel@tonic-gate 17067c478bd9Sstevel@tonic-gate int 17077c478bd9Sstevel@tonic-gate watch_disable_addr(const void *addr, size_t len, enum seg_rw rw) 17087c478bd9Sstevel@tonic-gate { 17097c478bd9Sstevel@tonic-gate if (pr_watch_active(curproc)) 17107c478bd9Sstevel@tonic-gate return (pr_mappage((caddr_t)addr, len, rw, 1)); 17117c478bd9Sstevel@tonic-gate return (0); 17127c478bd9Sstevel@tonic-gate } 17137c478bd9Sstevel@tonic-gate 17147c478bd9Sstevel@tonic-gate void 17157c478bd9Sstevel@tonic-gate watch_enable_addr(const void *addr, size_t len, enum seg_rw rw) 17167c478bd9Sstevel@tonic-gate { 17177c478bd9Sstevel@tonic-gate if (pr_watch_active(curproc)) 17187c478bd9Sstevel@tonic-gate pr_unmappage((caddr_t)addr, len, rw, 1); 17197c478bd9Sstevel@tonic-gate } 1720