1 | /* $NetBSD: kern_ras.c,v 1.38 2016/07/04 07:56:07 maxv 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 2016/07/04 07:56:07 maxv 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 | #ifdef NO_SOFTWARE_PATENTS |
69 | uint64_t where; |
70 | where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); |
71 | xc_wait(where); |
72 | #else |
73 | /* |
74 | * Assumptions: |
75 | * |
76 | * o preemption is disabled by the thread in |
77 | * ras_lookup(). |
78 | * o proc::p_raslist is only inspected with |
79 | * preemption disabled. |
80 | * o ras_lookup() plus loads reordered in advance |
81 | * will take no longer than 1/8s to complete. |
82 | */ |
83 | const int delta = hz >> 3; |
84 | int target = hardclock_ticks + delta; |
85 | do { |
86 | kpause("ras" , false, delta, NULL); |
87 | } while (hardclock_ticks < target); |
88 | #endif |
89 | } |
90 | } |
91 | |
92 | /* |
93 | * Check the specified address to see if it is within the |
94 | * sequence. If it is found, we return the restart address, |
95 | * otherwise we return -1. If we do perform a restart, we |
96 | * mark the sequence as hit. |
97 | * |
98 | * No locking required: we disable preemption and ras_sync() |
99 | * guarantees that individual entries are valid while we still |
100 | * have visibility of them. |
101 | */ |
102 | void * |
103 | ras_lookup(struct proc *p, void *addr) |
104 | { |
105 | struct ras *rp; |
106 | void *startaddr; |
107 | lwp_t *l; |
108 | |
109 | startaddr = (void *)-1; |
110 | l = curlwp; |
111 | |
112 | KPREEMPT_DISABLE(l); |
113 | for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) { |
114 | if (addr > rp->ras_startaddr && addr < rp->ras_endaddr) { |
115 | startaddr = rp->ras_startaddr; |
116 | DPRINTF(("RAS hit: p=%p %p\n" , p, addr)); |
117 | break; |
118 | } |
119 | } |
120 | KPREEMPT_ENABLE(l); |
121 | |
122 | return startaddr; |
123 | } |
124 | |
125 | /* |
126 | * During a fork, we copy all of the sequences from parent p1 to |
127 | * the child p2. |
128 | * |
129 | * No locking required as the parent must be paused. |
130 | */ |
131 | int |
132 | ras_fork(struct proc *p1, struct proc *p2) |
133 | { |
134 | struct ras *rp, *nrp; |
135 | |
136 | for (rp = p1->p_raslist; rp != NULL; rp = rp->ras_next) { |
137 | nrp = kmem_alloc(sizeof(*nrp), KM_SLEEP); |
138 | nrp->ras_startaddr = rp->ras_startaddr; |
139 | nrp->ras_endaddr = rp->ras_endaddr; |
140 | nrp->ras_next = p2->p_raslist; |
141 | p2->p_raslist = nrp; |
142 | } |
143 | |
144 | DPRINTF(("ras_fork: p1=%p, p2=%p\n" , p1, p2)); |
145 | |
146 | return 0; |
147 | } |
148 | |
149 | /* |
150 | * Nuke all sequences for this process. |
151 | */ |
152 | int |
153 | ras_purgeall(void) |
154 | { |
155 | struct ras *rp, *nrp; |
156 | proc_t *p; |
157 | |
158 | p = curproc; |
159 | |
160 | if (p->p_raslist == NULL) |
161 | return 0; |
162 | |
163 | mutex_enter(&p->p_auxlock); |
164 | if ((rp = p->p_raslist) != NULL) { |
165 | p->p_raslist = NULL; |
166 | ras_sync(); |
167 | for(; rp != NULL; rp = nrp) { |
168 | nrp = rp->ras_next; |
169 | kmem_free(rp, sizeof(*rp)); |
170 | } |
171 | } |
172 | mutex_exit(&p->p_auxlock); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | #if defined(__HAVE_RAS) |
178 | |
179 | #if __GNUC_PREREQ__(4, 8) |
180 | #define __WARNING_PUSH_LESS_NULL_PTR _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wextra\"") |
181 | #define __WARNING_POP_LESS_NULL_PTR _Pragma("GCC diagnostic pop") |
182 | #else |
183 | #define __WARNING_PUSH_LESS_NULL_PTR |
184 | #define __WARNING_POP_LESS_NULL_PTR |
185 | #endif |
186 | |
187 | /* |
188 | * Install the new sequence. If it already exists, return |
189 | * an error. |
190 | */ |
191 | static int |
192 | ras_install(void *addr, size_t len) |
193 | { |
194 | struct ras *rp; |
195 | struct ras *newrp; |
196 | void *endaddr; |
197 | int nras, error; |
198 | proc_t *p; |
199 | |
200 | if (len == 0) |
201 | return EINVAL; |
202 | |
203 | endaddr = (char *)addr + len; |
204 | |
205 | /* Do not warn about < NULL pointer comparison */ |
206 | __WARNING_PUSH_LESS_NULL_PTR |
207 | if (addr < (void *)VM_MIN_ADDRESS || addr > (void *)VM_MAXUSER_ADDRESS) |
208 | return EINVAL; |
209 | if (endaddr > (void *)VM_MAXUSER_ADDRESS) |
210 | return EINVAL; |
211 | if (endaddr < addr) |
212 | return EINVAL; |
213 | __WARNING_POP_LESS_NULL_PTR |
214 | |
215 | newrp = kmem_alloc(sizeof(*newrp), KM_SLEEP); |
216 | newrp->ras_startaddr = addr; |
217 | newrp->ras_endaddr = endaddr; |
218 | error = 0; |
219 | nras = 0; |
220 | p = curproc; |
221 | |
222 | mutex_enter(&p->p_auxlock); |
223 | for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) { |
224 | if (++nras >= ras_per_proc) { |
225 | error = EINVAL; |
226 | break; |
227 | } |
228 | if (addr < rp->ras_endaddr && endaddr > rp->ras_startaddr) { |
229 | error = EEXIST; |
230 | break; |
231 | } |
232 | } |
233 | if (rp == NULL) { |
234 | newrp->ras_next = p->p_raslist; |
235 | p->p_raslist = newrp; |
236 | ras_sync(); |
237 | mutex_exit(&p->p_auxlock); |
238 | } else { |
239 | mutex_exit(&p->p_auxlock); |
240 | kmem_free(newrp, sizeof(*newrp)); |
241 | } |
242 | |
243 | return error; |
244 | } |
245 | |
246 | /* |
247 | * Nuke the specified sequence. Both address and len must |
248 | * match, otherwise we return an error. |
249 | */ |
250 | static int |
251 | ras_purge(void *addr, size_t len) |
252 | { |
253 | struct ras *rp, **link; |
254 | void *endaddr; |
255 | proc_t *p; |
256 | |
257 | endaddr = (char *)addr + len; |
258 | p = curproc; |
259 | |
260 | mutex_enter(&p->p_auxlock); |
261 | link = &p->p_raslist; |
262 | for (rp = *link; rp != NULL; link = &rp->ras_next, rp = *link) { |
263 | if (addr == rp->ras_startaddr && endaddr == rp->ras_endaddr) |
264 | break; |
265 | } |
266 | if (rp != NULL) { |
267 | *link = rp->ras_next; |
268 | ras_sync(); |
269 | mutex_exit(&p->p_auxlock); |
270 | kmem_free(rp, sizeof(*rp)); |
271 | return 0; |
272 | } else { |
273 | mutex_exit(&p->p_auxlock); |
274 | return ESRCH; |
275 | } |
276 | } |
277 | |
278 | #endif /* defined(__HAVE_RAS) */ |
279 | |
280 | /*ARGSUSED*/ |
281 | int |
282 | sys_rasctl(struct lwp *l, const struct sys_rasctl_args *uap, register_t *retval) |
283 | { |
284 | #if defined(__HAVE_RAS) |
285 | /* { |
286 | syscallarg(void *) addr; |
287 | syscallarg(size_t) len; |
288 | syscallarg(int) op; |
289 | } */ |
290 | void *addr; |
291 | size_t len; |
292 | int op; |
293 | int error; |
294 | |
295 | /* |
296 | * first, extract syscall args from the uap. |
297 | */ |
298 | |
299 | addr = (void *)SCARG(uap, addr); |
300 | len = (size_t)SCARG(uap, len); |
301 | op = SCARG(uap, op); |
302 | |
303 | DPRINTF(("sys_rasctl: p=%p addr=%p, len=%ld, op=0x%x\n" , |
304 | curproc, addr, (long)len, op)); |
305 | |
306 | switch (op) { |
307 | case RAS_INSTALL: |
308 | error = ras_install(addr, len); |
309 | break; |
310 | case RAS_PURGE: |
311 | error = ras_purge(addr, len); |
312 | break; |
313 | case RAS_PURGE_ALL: |
314 | error = ras_purgeall(); |
315 | break; |
316 | default: |
317 | error = EINVAL; |
318 | break; |
319 | } |
320 | |
321 | return (error); |
322 | #else |
323 | return (EOPNOTSUPP); |
324 | #endif |
325 | } |
326 | |