kern_ras.c revision 1.38.18.1 1 /* $NetBSD: kern_ras.c,v 1.38.18.1 2020/04/13 08:05:03 martin 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.38.18.1 2020/04/13 08:05:03 martin 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