kern_ras.c revision 1.26
1/*	$NetBSD: kern_ras.c,v 1.26 2008/01/04 21:18:10 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.26 2008/01/04 21:18:10 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
53POOL_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
58u_int ras_per_proc = MAX_RAS_PER_PROC;
59
60#ifdef DEBUG
61int 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 */
72static void
73ras_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 */
112void *
113ras_lookup(struct proc *p, void *addr)
114{
115	struct ras *rp;
116	void *startaddr;
117
118	startaddr = (void *)-1;
119
120	crit_enter();
121	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
122		if (addr > rp->ras_startaddr && addr < rp->ras_endaddr) {
123			startaddr = rp->ras_startaddr;
124			DPRINTF(("RAS hit: p=%p %p\n", p, addr));
125			break;
126		}
127	}
128	crit_exit();
129
130	return startaddr;
131}
132
133/*
134 * During a fork, we copy all of the sequences from parent p1 to
135 * the child p2.
136 *
137 * No locking required as the parent must be paused.
138 */
139int
140ras_fork(struct proc *p1, struct proc *p2)
141{
142	struct ras *rp, *nrp;
143
144	for (rp = p1->p_raslist; rp != NULL; rp = rp->ras_next) {
145		nrp = pool_get(&ras_pool, PR_WAITOK);
146		nrp->ras_startaddr = rp->ras_startaddr;
147		nrp->ras_endaddr = rp->ras_endaddr;
148		nrp = p2->p_raslist;
149		p2->p_raslist = nrp;
150	}
151
152	DPRINTF(("ras_fork: p1=%p, p2=%p\n", p1, p2));
153
154	return 0;
155}
156
157/*
158 * Nuke all sequences for this process.
159 */
160int
161ras_purgeall(void)
162{
163	struct ras *rp, *nrp;
164	proc_t *p;
165
166	p = curproc;
167
168	mutex_enter(&p->p_auxlock);
169	if ((rp = p->p_raslist) != NULL) {
170		p->p_raslist = NULL;
171		ras_sync();
172		for(; rp != NULL; rp = nrp) {
173			nrp = rp->ras_next;
174			pool_put(&ras_pool, rp);
175		}
176	}
177	mutex_exit(&p->p_auxlock);
178
179	return 0;
180}
181
182#if defined(__HAVE_RAS)
183
184/*
185 * Install the new sequence.  If it already exists, return
186 * an error.
187 */
188static int
189ras_install(void *addr, size_t len)
190{
191	struct ras *rp;
192	struct ras *newrp;
193	void *endaddr;
194	int nras, error;
195	proc_t *p;
196
197	endaddr = (char *)addr + len;
198
199	if (addr < (void *)VM_MIN_ADDRESS ||
200	    endaddr > (void *)VM_MAXUSER_ADDRESS)
201		return (EINVAL);
202
203	if (len <= 0)
204		return (EINVAL);
205
206	newrp = pool_get(&ras_pool, PR_WAITOK);
207	newrp->ras_startaddr = addr;
208	newrp->ras_endaddr = endaddr;
209	error = 0;
210	nras = 0;
211	p = curproc;
212
213	mutex_enter(&p->p_auxlock);
214	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
215		if (++nras >= ras_per_proc) {
216			error = EINVAL;
217			break;
218		}
219		if (addr < rp->ras_endaddr && endaddr > rp->ras_startaddr) {
220			error = EEXIST;
221			break;
222		}
223	}
224	if (rp == NULL) {
225		newrp->ras_next = p->p_raslist;
226		p->p_raslist = newrp;
227		ras_sync();
228	 	mutex_exit(&p->p_auxlock);
229	} else {
230	 	mutex_exit(&p->p_auxlock);
231 		pool_put(&ras_pool, newrp);
232	}
233
234	return error;
235}
236
237/*
238 * Nuke the specified sequence.  Both address and len must
239 * match, otherwise we return an error.
240 */
241static int
242ras_purge(void *addr, size_t len)
243{
244	struct ras *rp, **link;
245	void *endaddr;
246	proc_t *p;
247
248	endaddr = (char *)addr + len;
249	p = curproc;
250
251	mutex_enter(&p->p_auxlock);
252	link = &p->p_raslist;
253	for (rp = *link; rp != NULL; link = &rp->ras_next, rp = *link) {
254		if (addr == rp->ras_startaddr && endaddr == rp->ras_endaddr)
255			break;
256	}
257	if (rp != NULL) {
258		*link = rp->ras_next;
259		ras_sync();
260		mutex_exit(&p->p_auxlock);
261		pool_put(&ras_pool, rp);
262		return 0;
263	} else {
264		mutex_exit(&p->p_auxlock);
265		return ESRCH;
266	}
267}
268
269#endif /* defined(__HAVE_RAS) */
270
271/*ARGSUSED*/
272int
273sys_rasctl(struct lwp *l, const struct sys_rasctl_args *uap, register_t *retval)
274{
275
276#if defined(__HAVE_RAS)
277	/* {
278		syscallarg(void *) addr;
279		syscallarg(size_t) len;
280		syscallarg(int) op;
281	} */
282	void *addr;
283	size_t len;
284	int op;
285	int error;
286
287	/*
288	 * first, extract syscall args from the uap.
289	 */
290
291	addr = (void *)SCARG(uap, addr);
292	len = (size_t)SCARG(uap, len);
293	op = SCARG(uap, op);
294
295	DPRINTF(("sys_rasctl: p=%p addr=%p, len=%ld, op=0x%x\n",
296	    curproc, addr, (long)len, op));
297
298	switch (op) {
299	case RAS_INSTALL:
300		error = ras_install(addr, len);
301		break;
302	case RAS_PURGE:
303		error = ras_purge(addr, len);
304		break;
305	case RAS_PURGE_ALL:
306		error = ras_purgeall();
307		break;
308	default:
309		error = EINVAL;
310		break;
311	}
312
313	return (error);
314
315#else
316
317	return (EOPNOTSUPP);
318
319#endif
320
321}
322