Home | History | Annotate | Line # | Download | only in kern
kern_ras.c revision 1.29
      1 /*	$NetBSD: kern_ras.c,v 1.29 2008/04/28 15:36:01 ad Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2002, 2006, 2007 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Gregory McGarry, and by Andrew Doran.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: kern_ras.c,v 1.29 2008/04/28 15:36:01 ad Exp $");
     41 
     42 #include <sys/param.h>
     43 #include <sys/systm.h>
     44 #include <sys/kernel.h>
     45 #include <sys/pool.h>
     46 #include <sys/proc.h>
     47 #include <sys/ras.h>
     48 #include <sys/xcall.h>
     49 #include <sys/syscallargs.h>
     50 
     51 #include <uvm/uvm_extern.h>
     52 
     53 POOL_INIT(ras_pool, sizeof(struct ras), 0, 0, 0, "raspl",
     54     &pool_allocator_nointr, IPL_NONE);
     55 
     56 #define MAX_RAS_PER_PROC	16
     57 
     58 u_int ras_per_proc = MAX_RAS_PER_PROC;
     59 
     60 #ifdef DEBUG
     61 int ras_debug = 0;
     62 #define DPRINTF(x)	if (ras_debug) printf x
     63 #else
     64 #define DPRINTF(x)	/* nothing */
     65 #endif
     66 
     67 /*
     68  * Force all CPUs through cpu_switchto(), waiting until complete.
     69  * Context switching will drain the write buffer on the calling
     70  * CPU.
     71  */
     72 static void
     73 ras_sync(void)
     74 {
     75 
     76 	/* No need to sync if exiting or single threaded. */
     77 	if (curproc->p_nlwps > 1 && ncpu > 1) {
     78 #ifdef NO_SOFTWARE_PATENTS
     79 		uint64_t where;
     80 		where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL);
     81 		xc_wait(where);
     82 #else
     83 		/*
     84 		 * Assumptions:
     85 		 *
     86 		 * o preemption is disabled by the thread in
     87 		 *   ras_lookup().
     88 		 * o proc::p_raslist is only inspected with
     89 		 *   preemption disabled.
     90 		 * o ras_lookup() plus loads reordered in advance
     91 		 *   will take no longer than 1/8s to complete.
     92 		 */
     93 		const int delta = hz >> 3;
     94 		int target = hardclock_ticks + delta;
     95 		do {
     96 			kpause("ras", false, delta, NULL);
     97 		} while (hardclock_ticks < target);
     98 #endif
     99 	}
    100 }
    101 
    102 /*
    103  * Check the specified address to see if it is within the
    104  * sequence.  If it is found, we return the restart address,
    105  * otherwise we return -1.  If we do perform a restart, we
    106  * mark the sequence as hit.
    107  *
    108  * No locking required: we disable preemption and ras_sync()
    109  * guarantees that individual entries are valid while we still
    110  * have visibility of them.
    111  */
    112 void *
    113 ras_lookup(struct proc *p, void *addr)
    114 {
    115 	struct ras *rp;
    116 	void *startaddr;
    117 	lwp_t *l;
    118 
    119 	startaddr = (void *)-1;
    120 	l = curlwp;
    121 
    122 	KPREEMPT_DISABLE(l);
    123 	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
    124 		if (addr > rp->ras_startaddr && addr < rp->ras_endaddr) {
    125 			startaddr = rp->ras_startaddr;
    126 			DPRINTF(("RAS hit: p=%p %p\n", p, addr));
    127 			break;
    128 		}
    129 	}
    130 	KPREEMPT_ENABLE(l);
    131 
    132 	return startaddr;
    133 }
    134 
    135 /*
    136  * During a fork, we copy all of the sequences from parent p1 to
    137  * the child p2.
    138  *
    139  * No locking required as the parent must be paused.
    140  */
    141 int
    142 ras_fork(struct proc *p1, struct proc *p2)
    143 {
    144 	struct ras *rp, *nrp;
    145 
    146 	for (rp = p1->p_raslist; rp != NULL; rp = rp->ras_next) {
    147 		nrp = pool_get(&ras_pool, PR_WAITOK);
    148 		nrp->ras_startaddr = rp->ras_startaddr;
    149 		nrp->ras_endaddr = rp->ras_endaddr;
    150 		nrp->ras_next = p2->p_raslist;
    151 		p2->p_raslist = nrp;
    152 	}
    153 
    154 	DPRINTF(("ras_fork: p1=%p, p2=%p\n", p1, p2));
    155 
    156 	return 0;
    157 }
    158 
    159 /*
    160  * Nuke all sequences for this process.
    161  */
    162 int
    163 ras_purgeall(void)
    164 {
    165 	struct ras *rp, *nrp;
    166 	proc_t *p;
    167 
    168 	p = curproc;
    169 
    170 	mutex_enter(&p->p_auxlock);
    171 	if ((rp = p->p_raslist) != NULL) {
    172 		p->p_raslist = NULL;
    173 		ras_sync();
    174 		for(; rp != NULL; rp = nrp) {
    175 			nrp = rp->ras_next;
    176 			pool_put(&ras_pool, rp);
    177 		}
    178 	}
    179 	mutex_exit(&p->p_auxlock);
    180 
    181 	return 0;
    182 }
    183 
    184 #if defined(__HAVE_RAS)
    185 
    186 /*
    187  * Install the new sequence.  If it already exists, return
    188  * an error.
    189  */
    190 static int
    191 ras_install(void *addr, size_t len)
    192 {
    193 	struct ras *rp;
    194 	struct ras *newrp;
    195 	void *endaddr;
    196 	int nras, error;
    197 	proc_t *p;
    198 
    199 	endaddr = (char *)addr + len;
    200 
    201 	if (addr < (void *)VM_MIN_ADDRESS ||
    202 	    endaddr > (void *)VM_MAXUSER_ADDRESS)
    203 		return (EINVAL);
    204 
    205 	if (len <= 0)
    206 		return (EINVAL);
    207 
    208 	newrp = pool_get(&ras_pool, PR_WAITOK);
    209 	newrp->ras_startaddr = addr;
    210 	newrp->ras_endaddr = endaddr;
    211 	error = 0;
    212 	nras = 0;
    213 	p = curproc;
    214 
    215 	mutex_enter(&p->p_auxlock);
    216 	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
    217 		if (++nras >= ras_per_proc) {
    218 			error = EINVAL;
    219 			break;
    220 		}
    221 		if (addr < rp->ras_endaddr && endaddr > rp->ras_startaddr) {
    222 			error = EEXIST;
    223 			break;
    224 		}
    225 	}
    226 	if (rp == NULL) {
    227 		newrp->ras_next = p->p_raslist;
    228 		p->p_raslist = newrp;
    229 		ras_sync();
    230 	 	mutex_exit(&p->p_auxlock);
    231 	} else {
    232 	 	mutex_exit(&p->p_auxlock);
    233  		pool_put(&ras_pool, newrp);
    234 	}
    235 
    236 	return error;
    237 }
    238 
    239 /*
    240  * Nuke the specified sequence.  Both address and len must
    241  * match, otherwise we return an error.
    242  */
    243 static int
    244 ras_purge(void *addr, size_t len)
    245 {
    246 	struct ras *rp, **link;
    247 	void *endaddr;
    248 	proc_t *p;
    249 
    250 	endaddr = (char *)addr + len;
    251 	p = curproc;
    252 
    253 	mutex_enter(&p->p_auxlock);
    254 	link = &p->p_raslist;
    255 	for (rp = *link; rp != NULL; link = &rp->ras_next, rp = *link) {
    256 		if (addr == rp->ras_startaddr && endaddr == rp->ras_endaddr)
    257 			break;
    258 	}
    259 	if (rp != NULL) {
    260 		*link = rp->ras_next;
    261 		ras_sync();
    262 		mutex_exit(&p->p_auxlock);
    263 		pool_put(&ras_pool, rp);
    264 		return 0;
    265 	} else {
    266 		mutex_exit(&p->p_auxlock);
    267 		return ESRCH;
    268 	}
    269 }
    270 
    271 #endif /* defined(__HAVE_RAS) */
    272 
    273 /*ARGSUSED*/
    274 int
    275 sys_rasctl(struct lwp *l, const struct sys_rasctl_args *uap, register_t *retval)
    276 {
    277 
    278 #if defined(__HAVE_RAS)
    279 	/* {
    280 		syscallarg(void *) addr;
    281 		syscallarg(size_t) len;
    282 		syscallarg(int) op;
    283 	} */
    284 	void *addr;
    285 	size_t len;
    286 	int op;
    287 	int error;
    288 
    289 	/*
    290 	 * first, extract syscall args from the uap.
    291 	 */
    292 
    293 	addr = (void *)SCARG(uap, addr);
    294 	len = (size_t)SCARG(uap, len);
    295 	op = SCARG(uap, op);
    296 
    297 	DPRINTF(("sys_rasctl: p=%p addr=%p, len=%ld, op=0x%x\n",
    298 	    curproc, addr, (long)len, op));
    299 
    300 	switch (op) {
    301 	case RAS_INSTALL:
    302 		error = ras_install(addr, len);
    303 		break;
    304 	case RAS_PURGE:
    305 		error = ras_purge(addr, len);
    306 		break;
    307 	case RAS_PURGE_ALL:
    308 		error = ras_purgeall();
    309 		break;
    310 	default:
    311 		error = EINVAL;
    312 		break;
    313 	}
    314 
    315 	return (error);
    316 
    317 #else
    318 
    319 	return (EOPNOTSUPP);
    320 
    321 #endif
    322 
    323 }
    324