xref: /netbsd-src/sys/kern/kern_ras.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: kern_ras.c,v 1.40 2019/12/14 16:58:25 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002, 2006, 2007, 2008 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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: kern_ras.c,v 1.40 2019/12/14 16:58:25 riastradh Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/kmem.h>
39 #include <sys/proc.h>
40 #include <sys/ras.h>
41 #include <sys/xcall.h>
42 #include <sys/syscallargs.h>
43 
44 #include <uvm/uvm_extern.h>
45 
46 #define MAX_RAS_PER_PROC	16
47 
48 u_int ras_per_proc = MAX_RAS_PER_PROC;
49 
50 #ifdef DEBUG
51 int ras_debug = 0;
52 #define DPRINTF(x)	if (ras_debug) printf x
53 #else
54 #define DPRINTF(x)	/* nothing */
55 #endif
56 
57 /*
58  * Force all CPUs through cpu_switchto(), waiting until complete.
59  * Context switching will drain the write buffer on the calling
60  * CPU.
61  */
62 static void
63 ras_sync(void)
64 {
65 
66 	/* No need to sync if exiting or single threaded. */
67 	if (curproc->p_nlwps > 1 && ncpu > 1) {
68 		xc_barrier(0);
69 	}
70 }
71 
72 /*
73  * Check the specified address to see if it is within the
74  * sequence.  If it is found, we return the restart address,
75  * otherwise we return -1.  If we do perform a restart, we
76  * mark the sequence as hit.
77  *
78  * No locking required: we disable preemption and ras_sync()
79  * guarantees that individual entries are valid while we still
80  * have visibility of them.
81  */
82 void *
83 ras_lookup(struct proc *p, void *addr)
84 {
85 	struct ras *rp;
86 	void *startaddr;
87 	lwp_t *l;
88 
89 	startaddr = (void *)-1;
90 	l = curlwp;
91 
92 	KPREEMPT_DISABLE(l);
93 	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
94 		if (addr > rp->ras_startaddr && addr < rp->ras_endaddr) {
95 			startaddr = rp->ras_startaddr;
96 			DPRINTF(("RAS hit: p=%p %p\n", p, addr));
97 			break;
98 		}
99 	}
100 	KPREEMPT_ENABLE(l);
101 
102 	return startaddr;
103 }
104 
105 /*
106  * During a fork, we copy all of the sequences from parent p1 to
107  * the child p2.
108  *
109  * No locking required as the parent must be paused.
110  */
111 int
112 ras_fork(struct proc *p1, struct proc *p2)
113 {
114 	struct ras *rp, *nrp;
115 
116 	for (rp = p1->p_raslist; rp != NULL; rp = rp->ras_next) {
117 		nrp = kmem_alloc(sizeof(*nrp), KM_SLEEP);
118 		nrp->ras_startaddr = rp->ras_startaddr;
119 		nrp->ras_endaddr = rp->ras_endaddr;
120 		nrp->ras_next = p2->p_raslist;
121 		p2->p_raslist = nrp;
122 	}
123 
124 	DPRINTF(("ras_fork: p1=%p, p2=%p\n", p1, p2));
125 
126 	return 0;
127 }
128 
129 /*
130  * Nuke all sequences for this process.
131  */
132 int
133 ras_purgeall(void)
134 {
135 	struct ras *rp, *nrp;
136 	proc_t *p;
137 
138 	p = curproc;
139 
140 	if (p->p_raslist == NULL)
141 		return 0;
142 
143 	mutex_enter(&p->p_auxlock);
144 	if ((rp = p->p_raslist) != NULL) {
145 		p->p_raslist = NULL;
146 		ras_sync();
147 		for(; rp != NULL; rp = nrp) {
148 			nrp = rp->ras_next;
149 			kmem_free(rp, sizeof(*rp));
150 		}
151 	}
152 	mutex_exit(&p->p_auxlock);
153 
154 	return 0;
155 }
156 
157 #if defined(__HAVE_RAS)
158 
159 #if __GNUC_PREREQ__(4, 8)
160 #define	__WARNING_PUSH_LESS_NULL_PTR	_Pragma("GCC diagnostic push") 	_Pragma("GCC diagnostic ignored \"-Wextra\"")
161 #define	__WARNING_POP_LESS_NULL_PTR	_Pragma("GCC diagnostic pop")
162 #else
163 #define	__WARNING_PUSH_LESS_NULL_PTR
164 #define	__WARNING_POP_LESS_NULL_PTR
165 #endif
166 
167 /*
168  * Install the new sequence.  If it already exists, return
169  * an error.
170  */
171 static int
172 ras_install(void *addr, size_t len)
173 {
174 	struct ras *rp;
175 	struct ras *newrp;
176 	void *endaddr;
177 	int nras, error;
178 	proc_t *p;
179 
180 	if (len == 0)
181 		return EINVAL;
182 
183 	endaddr = (char *)addr + len;
184 
185 	/* Do not warn about < NULL pointer comparison */
186 	__WARNING_PUSH_LESS_NULL_PTR
187 	if (addr < (void *)VM_MIN_ADDRESS || addr > (void *)VM_MAXUSER_ADDRESS)
188 		return EINVAL;
189 	if (endaddr > (void *)VM_MAXUSER_ADDRESS)
190 		return EINVAL;
191 	if (endaddr < addr)
192 		return EINVAL;
193 	__WARNING_POP_LESS_NULL_PTR
194 
195 	newrp = kmem_alloc(sizeof(*newrp), KM_SLEEP);
196 	newrp->ras_startaddr = addr;
197 	newrp->ras_endaddr = endaddr;
198 	error = 0;
199 	nras = 0;
200 	p = curproc;
201 
202 	mutex_enter(&p->p_auxlock);
203 	for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
204 		if (++nras >= ras_per_proc) {
205 			error = EINVAL;
206 			break;
207 		}
208 		if (addr < rp->ras_endaddr && endaddr > rp->ras_startaddr) {
209 			error = EEXIST;
210 			break;
211 		}
212 	}
213 	if (rp == NULL) {
214 		newrp->ras_next = p->p_raslist;
215 		p->p_raslist = newrp;
216 		ras_sync();
217 	 	mutex_exit(&p->p_auxlock);
218 	} else {
219 	 	mutex_exit(&p->p_auxlock);
220  		kmem_free(newrp, sizeof(*newrp));
221 	}
222 
223 	return error;
224 }
225 
226 /*
227  * Nuke the specified sequence.  Both address and len must
228  * match, otherwise we return an error.
229  */
230 static int
231 ras_purge(void *addr, size_t len)
232 {
233 	struct ras *rp, **link;
234 	void *endaddr;
235 	proc_t *p;
236 
237 	endaddr = (char *)addr + len;
238 	p = curproc;
239 
240 	mutex_enter(&p->p_auxlock);
241 	link = &p->p_raslist;
242 	for (rp = *link; rp != NULL; link = &rp->ras_next, rp = *link) {
243 		if (addr == rp->ras_startaddr && endaddr == rp->ras_endaddr)
244 			break;
245 	}
246 	if (rp != NULL) {
247 		*link = rp->ras_next;
248 		ras_sync();
249 		mutex_exit(&p->p_auxlock);
250 		kmem_free(rp, sizeof(*rp));
251 		return 0;
252 	} else {
253 		mutex_exit(&p->p_auxlock);
254 		return ESRCH;
255 	}
256 }
257 
258 #endif /* defined(__HAVE_RAS) */
259 
260 /*ARGSUSED*/
261 int
262 sys_rasctl(struct lwp *l, const struct sys_rasctl_args *uap, register_t *retval)
263 {
264 #if defined(__HAVE_RAS)
265 	/* {
266 		syscallarg(void *) addr;
267 		syscallarg(size_t) len;
268 		syscallarg(int) op;
269 	} */
270 	void *addr;
271 	size_t len;
272 	int op;
273 	int error;
274 
275 	/*
276 	 * first, extract syscall args from the uap.
277 	 */
278 
279 	addr = (void *)SCARG(uap, addr);
280 	len = (size_t)SCARG(uap, len);
281 	op = SCARG(uap, op);
282 
283 	DPRINTF(("sys_rasctl: p=%p addr=%p, len=%ld, op=0x%x\n",
284 	    curproc, addr, (long)len, op));
285 
286 	switch (op) {
287 	case RAS_INSTALL:
288 		error = ras_install(addr, len);
289 		break;
290 	case RAS_PURGE:
291 		error = ras_purge(addr, len);
292 		break;
293 	case RAS_PURGE_ALL:
294 		error = ras_purgeall();
295 		break;
296 	default:
297 		error = EINVAL;
298 		break;
299 	}
300 
301 	return (error);
302 #else
303 	return (EOPNOTSUPP);
304 #endif
305 }
306