/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <sys/watchpoint.h>
#include <sys/schedctl.h>
/*
* Copy ops vector for watchpoints.
*/
static int watch_copyin(const void *, void *, size_t);
static int watch_xcopyin(const void *, void *, size_t);
static int watch_copyout(const void *, void *, size_t);
static int watch_xcopyout(const void *, void *, size_t);
static int watch_fuword8(const void *, uint8_t *);
static int watch_fuword16(const void *, uint16_t *);
static int watch_fuword32(const void *, uint32_t *);
static int watch_suword8(void *, uint8_t);
static int watch_suword16(void *, uint16_t);
static int watch_suword32(void *, uint32_t);
#ifdef _LP64
static int watch_fuword64(const void *, uint64_t *);
static int watch_suword64(void *, uint64_t);
#endif
#ifdef _LP64
#else
NULL,
#endif
#ifdef _LP64
#else
NULL,
#endif
};
/*
* Map the 'rw' argument to a protection flag.
*/
static int
{
switch (rw) {
case S_EXEC:
return (PROT_EXEC);
case S_READ:
return (PROT_READ);
case S_WRITE:
return (PROT_WRITE);
default:
return (PROT_NONE); /* can't happen */
}
}
/*
* The index follows the precedence order: exec .. write .. read
*/
static int
{
switch (rw) {
default: /* default case "can't happen" */
case S_EXEC:
return (0);
case S_WRITE:
return (1);
case S_READ:
return (2);
}
}
/*
* Map an index back to a seg_rw.
*/
};
#define X 0
#define W 1
#define R 2
#define sum(a) (a[X] + a[W] + a[R])
/*
* Common code for pr_mappage() and pr_unmappage().
*/
static int
{
int rv = 0;
return (0);
/*
* as->a_wpage can only be changed while the process is totally stopped.
* Don't grab p_lock here. Holding p_lock while grabbing the address
* space lock leads to deadlocks with the clock thread.
*
* p_maplock prevents simultaneous execution of this function. Under
* normal circumstances, holdwatch() will stop all other threads, so the
* lock isn't really needed. But there may be multiple threads within
* stop() when SWATCHOK is set, so we need to handle multiple threads
* at once. See holdwatch() for the details of this dance.
*/
mutex_enter(&p->p_maplock);
/*
* If the requested protection has not been
* removed, we need not remap this page.
*/
continue;
/*
* If the requested access does not exist in the page's
* original protections, we need not remap this page.
* If the page does not exist yet, we can't test it.
*/
continue;
continue;
}
if (mapin) {
/*
* Before mapping the page in, ensure that
* all other lwps are held in the kernel.
*/
if (p->p_mapcnt == 0) {
mutex_exit(&p->p_maplock);
if (holdwatch() != 0) {
/*
* We stopped in holdwatch().
* Start all over again because the
* watched page list may have changed.
*/
goto startover;
}
mutex_enter(&p->p_maplock);
}
p->p_mapcnt++;
}
rv++;
if (mapin) {
if (kernel)
else
/* cannot have exec-only protection */
/* cannot have write-only protection */
#if 0 /* damned broken mmu feature! */
#endif
} else {
if (kernel) {
} else {
}
else {
/* cannot have exec-only protection */
/* cannot have write-only protection */
#if 0 /* damned broken mmu feature! */
#endif
}
}
retrycnt++;
goto retry;
}
}
}
/*
* When all pages are mapped back to their normal state,
* continue the other lwps.
*/
if (!mapin) {
p->p_mapcnt--;
if (p->p_mapcnt == 0) {
mutex_exit(&p->p_maplock);
mutex_enter(&p->p_lock);
continuelwps(p);
mutex_exit(&p->p_lock);
mutex_enter(&p->p_maplock);
}
}
}
mutex_exit(&p->p_maplock);
return (rv);
}
/*
* Restore the original page protections on an address range.
* If 'kernel' is non-zero, just do it for the kernel.
* pr_mappage() returns non-zero if it actually changed anything.
*
* pr_mappage() and pr_unmappage() must be executed in matched pairs,
* but pairs may be nested within other pairs. The reference counts
* sort it all out. See pr_do_mappage(), above.
*/
static int
{
}
/*
* Set the modified page protections on a watched page.
* Inverse of pr_mappage().
* Needs to be called only if pr_mappage() returned non-zero.
*/
static void
{
}
/*
* Function called by an lwp after it resumes from stop().
*/
void
setallwatch(void)
{
return;
retrycnt = 0;
retrycnt++;
goto retry;
}
}
/*
* No watched areas remain in this page.
* Free the watched_page structure.
*/
} else {
}
}
}
int
{
int rv = 0;
switch (rw) {
case S_READ:
case S_WRITE:
case S_EXEC:
break;
default:
return (0);
}
/*
* as->a_wpage can only be modified while the process is totally
* stopped. We need, and should use, no locks here.
*/
switch (rw) {
case S_READ:
break;
case S_WRITE:
!= (PROT_USER|PROT_WRITE));
break;
case S_EXEC:
break;
default:
/* can't happen! */
break;
}
}
}
}
return (rv);
}
/*
* trap() calls here to determine if a fault is in a watched page.
*/
int
{
return (0);
}
/*
* trap() calls here to determine if a fault is a watchpoint.
*/
int
{
int rv = 0;
int ta = 0;
switch (rw) {
case S_READ:
case S_WRITE:
case S_EXEC:
break;
default:
*pta = 0;
return (0);
}
/*
* p->p_warea is protected by p->p_lock.
*/
mutex_enter(&p->p_lock);
/* BEGIN CSTYLED */
/*
* This loop is somewhat complicated because the fault region can span
* multiple watched areas. For example:
*
* addr eaddr
* +-----------------+
* | fault region |
* +-------+--------+----+---+------------+
* | prot not right | | prot correct |
* +----------------+ +----------------+
* wa_vaddr wa_eaddr
* wa_vaddr wa_eaddr
*
* We start at the area greater than or equal to the starting address.
* As long as some portion of the fault region overlaps the current
* area, we continue checking permissions until we find an appropriate
* match.
*/
/* END CSTYLED */
switch (rw) {
case S_READ:
rv = TRAP_RWATCH;
break;
case S_WRITE:
rv = TRAP_WWATCH;
break;
case S_EXEC:
rv = TRAP_XWATCH;
break;
default:
/* can't happen */
break;
}
/*
* If protections didn't match, check the next watched
* area
*/
if (rv != 0) {
ta = 1;
break;
}
}
mutex_exit(&p->p_lock);
return (rv);
}
/*
* Set up to perform a single-step at user level for the
* case of a trapafter watchpoint. Called from trap().
*/
void
{
/*
* Check to see if we are already performing this special
* watchpoint single-step. We must not do pr_mappage() twice.
*/
/* special check for two read traps on the same instruction */
pw++; /* use the extra S_READ struct */
}
}
} else {
}
}
/*
* Undo the effects of do_watch_step().
* Called from trap() after the single-step is finished.
* Also called from issig_forreal() and stop() with a NULL
* argument to avoid having these things set more than once.
*/
int
{
int fault = 0;
if (lwp->lwp_watchtrap) {
int i;
for (i = 0; i < 4; i++, pw++) {
continue;
0);
}
}
}
lwp->lwp_watchtrap = 0;
}
return (fault);
}
/*
* Handle a watchpoint that occurs while doing copyin()
* or copyout() in a system call.
* Return non-zero if the fault or signal is cleared
* by a debugger while the lwp is stopped.
*/
static int
{
int rval;
/* assert no locks are held */
/* ASSERT(curthread->t_nlocks == 0); */
mutex_enter(&p->p_lock);
/* this will be tested and cleared by the caller */
lwp->lwp_sysabort = 0;
if (lwp->lwp_curflt == 0) {
mutex_exit(&p->p_lock);
return (1);
}
lwp->lwp_curflt = 0;
}
/*
* post the SIGTRAP signal.
* Block all other signals so we only stop showing SIGTRAP.
*/
/* SIGTRAP is blocked or ignored, forget the rest. */
mutex_exit(&p->p_lock);
return (0);
}
mutex_exit(&p->p_lock);
/* restore the original signal mask */
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
return (rval);
}
/*
* Wrappers for the copyin()/copyout() functions to deal
* with watchpoints that fire while in system calls.
*/
static int
{
int error = 0;
int mapped;
int watchcode;
int ta;
watchcode = 0;
else {
vaddr = watch_uaddr;
}
/*
* Copy the initial part, up to a watched address, if any.
*/
if (part != 0) {
else
no_fault();
if (mapped)
watch_uaddr += part;
watch_kaddr += part;
}
/*
* If trapafter was specified, then copy through the
* watched area before taking the watchpoint trap.
*/
else
no_fault();
if (mapped)
watch_uaddr += part;
watch_kaddr += part;
}
/* if we hit a watched address, do the watchpoint logic */
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
break;
}
}
return (error);
}
static int
{
}
static int
{
int error = 0;
int watchcode;
int ta;
int mapped;
watchcode = 0;
else {
vaddr = watch_uaddr;
if (watchcode) {
if (ta == 0)
else {
}
}
}
/*
* Copy the initial part, up to a watched address, if any.
*/
if (part != 0) {
else
no_fault();
if (mapped)
watch_uaddr += part;
watch_kaddr += part;
}
/*
* If trapafter was specified, then copy through the
* watched area before taking the watchpoint trap.
*/
else
no_fault();
if (mapped)
watch_uaddr += part;
watch_kaddr += part;
}
/* if we hit a watched address, do the watchpoint logic */
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
break;
}
}
return (error);
}
static int
{
}
static int
const char *uaddr,
char *kaddr,
{
int error = 0;
return (ENAMETOOLONG);
int watchcode;
int ta;
int mapped;
watchcode = 0;
else {
if (watchcode) {
if (ta == 0)
else {
}
}
}
/*
* Copy the initial part, up to a watched address, if any.
*/
if (part != 0) {
else
&size);
no_fault();
if (mapped)
error = 0;
break; /* didn't reach the watched area */
}
/*
* If trapafter was specified, then copy through the
* watched area before taking the watchpoint trap.
*/
else
&size);
no_fault();
if (mapped)
error = 0;
}
/* if we hit a watched address, do the watchpoint logic */
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
break;
}
break;
}
return (error);
}
static int
const char *kaddr,
char *uaddr,
{
int error = 0;
return (ENAMETOOLONG);
int watchcode;
int ta;
int mapped;
watchcode = 0;
} else {
}
/*
* Copy the initial part, up to a watched address, if any.
*/
if (part != 0) {
else
&size);
no_fault();
if (mapped)
error = 0;
break; /* didn't reach the watched area */
}
/*
* If trapafter was specified, then copy through the
* watched area before taking the watchpoint trap.
*/
else
&size);
no_fault();
if (mapped)
error = 0;
}
/* if we hit a watched address, do the watchpoint logic */
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
break;
}
break;
}
return (error);
}
typedef int (*fuword_func)(const void *, void *);
/*
* Generic form of watch_fuword8(), watch_fuword16(), etc.
*/
static int
{
int watchcode;
int mapped;
int rv = 0;
int ta;
for (;;) {
rv = -1;
else
no_fault();
if (mapped)
}
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
rv = -1;
break;
}
break;
}
return (rv);
}
static int
{
sizeof (*dst)));
}
static int
{
sizeof (*dst)));
}
static int
{
sizeof (*dst)));
}
#ifdef _LP64
static int
{
sizeof (*dst)));
}
#endif
static int
{
int watchcode;
int mapped;
int rv = 0;
int ta;
for (;;) {
S_WRITE);
S_WRITE, 1);
rv = -1;
else
no_fault();
if (mapped)
S_WRITE, 1);
}
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
rv = -1;
break;
}
break;
}
return (rv);
}
static int
{
int watchcode;
int mapped;
int rv = 0;
int ta;
for (;;) {
S_WRITE);
S_WRITE, 1);
rv = -1;
else
no_fault();
if (mapped)
S_WRITE, 1);
}
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
rv = -1;
break;
}
break;
}
return (rv);
}
static int
{
int watchcode;
int mapped;
int rv = 0;
int ta;
for (;;) {
S_WRITE);
S_WRITE, 1);
rv = -1;
else
no_fault();
if (mapped)
S_WRITE, 1);
}
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
rv = -1;
break;
}
break;
}
return (rv);
}
#ifdef _LP64
static int
{
int watchcode;
int mapped;
int rv = 0;
int ta;
for (;;) {
S_WRITE);
S_WRITE, 1);
rv = -1;
else
no_fault();
if (mapped)
S_WRITE, 1);
}
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
rv = -1;
break;
}
break;
}
return (rv);
}
#endif /* _LP64 */
/*
* Check for watched addresses in the given address space.
* Return 1 if this is true, otherwise 0.
*/
static int
{
return (1);
}
return (0);
}
/*
* Wrapper for the physio() function.
* Splits one uio operation with multiple iovecs into uio operations with
* only one iovecs to do the watchpoint handling separately for each iovecs.
*/
static int
{
int seg_rw;
int error = 0;
while (uio->uio_iovcnt > 0) {
/*
* Make sure to return the uio structure with the
* same values as default_physio() does.
*/
uio->uio_iovcnt--;
continue;
}
/*
* The given memory references don't cover a
* watched page.
*/
&auio);
/* Update uio with values from auio. */
/*
* Return if an error occurred or not all data
* was copied.
*/
break;
uio->uio_iovcnt--;
} else {
/*
* Do the io if the given memory references
* don't cover a watched area (watchcode=0)
* or if WA_TRAPAFTER was specified.
*/
if (mapped)
}
/*
* If we hit a watched address, do the watchpoint logic.
*/
if (watchcode &&
lwp->lwp_sysabort)) {
lwp->lwp_sysabort = 0;
return (EFAULT);
}
/*
* Check for errors from default_physio().
*/
break;
uio->uio_iovcnt--;
}
}
}
return (error);
}
int
wa_compare(const void *a, const void *b)
{
return (-1);
return (1);
else
return (0);
}
int
wp_compare(const void *a, const void *b)
{
return (-1);
return (1);
else
return (0);
}
/*
* Given an address range, finds the first watched area which overlaps some or
* all of the range.
*/
{
/* First, check if there is an exact match. */
/* Check to see if we overlap with the previous area. */
}
/* Try the next area. */
}
if (where)
*where = real_where;
return (wap);
}
void
{
t->t_proc_flag |= TP_WATCHPT;
install_copyops(t, &watch_copyops);
}
void
{
t->t_proc_flag &= ~TP_WATCHPT;
remove_copyops(t);
}
int
{
if (watched)
return (ret);
}
int
{
if (watched)
return (ret);
}
#ifdef _LP64
int
{
if (watched)
return (ret);
}
#endif
int
{
if (watched)
return (ret);
}
#ifdef _LP64
int
{
if (watched)
return (ret);
}
#endif
int
{
if (watched)
return (ret);
}
int
{
if (pr_watch_active(curproc))
return (0);
}
void
{
if (pr_watch_active(curproc))
}