FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_ras.c
1 /* $NetBSD: kern_ras.c,v 1.42 2022/08/08 22:31:45 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.42 2022/08/08 22:31:45 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 /*
160 * Install the new sequence. If it already exists, return
161 * an error.
162 */
163 static int
164 ras_install(void *addr, size_t len)
165 {
166 struct ras *rp;
167 struct ras *newrp;
168 void *endaddr;
169 int nras, error;
170 proc_t *p;
171
172 if (len == 0)
173 return EINVAL;
174
175 if ((uintptr_t)addr < VM_MIN_ADDRESS ||
176 (uintptr_t)addr > VM_MAXUSER_ADDRESS)
177 return EINVAL;
178 if (len > VM_MAXUSER_ADDRESS - (uintptr_t)addr)
179 return EINVAL;
180 endaddr = (char *)addr + len;
181
182 newrp = kmem_alloc(sizeof(*newrp), KM_SLEEP);
183 newrp->ras_startaddr = addr;
184 newrp->ras_endaddr = endaddr;
185 error = 0;
186 nras = 0;
187 p = curproc;
188
189 mutex_enter(&p->p_auxlock);
190 for (rp = p->p_raslist; rp != NULL; rp = rp->ras_next) {
191 if (++nras >= ras_per_proc) {
192 error = EINVAL;
193 break;
194 }
195 if (addr < rp->ras_endaddr && endaddr > rp->ras_startaddr) {
196 error = EEXIST;
197 break;
198 }
199 }
200 if (rp == NULL) {
201 newrp->ras_next = p->p_raslist;
202 p->p_raslist = newrp;
203 ras_sync();
204 mutex_exit(&p->p_auxlock);
205 } else {
206 mutex_exit(&p->p_auxlock);
207 kmem_free(newrp, sizeof(*newrp));
208 }
209
210 return error;
211 }
212
213 /*
214 * Nuke the specified sequence. Both address and len must
215 * match, otherwise we return an error.
216 */
217 static int
218 ras_purge(void *addr, size_t len)
219 {
220 struct ras *rp, **link;
221 proc_t *p;
222
223 p = curproc;
224
225 mutex_enter(&p->p_auxlock);
226 link = &p->p_raslist;
227 for (rp = *link; rp != NULL; link = &rp->ras_next, rp = *link) {
228 if (addr == rp->ras_startaddr &&
229 (char *)rp->ras_endaddr - (char *)rp->ras_startaddr == len)
230 break;
231 }
232 if (rp != NULL) {
233 *link = rp->ras_next;
234 ras_sync();
235 mutex_exit(&p->p_auxlock);
236 kmem_free(rp, sizeof(*rp));
237 return 0;
238 } else {
239 mutex_exit(&p->p_auxlock);
240 return ESRCH;
241 }
242 }
243
244 #endif /* defined(__HAVE_RAS) */
245
246 /*ARGSUSED*/
247 int
248 sys_rasctl(struct lwp *l, const struct sys_rasctl_args *uap, register_t *retval)
249 {
250 #if defined(__HAVE_RAS)
251 /* {
252 syscallarg(void *) addr;
253 syscallarg(size_t) len;
254 syscallarg(int) op;
255 } */
256 void *addr;
257 size_t len;
258 int op;
259 int error;
260
261 /*
262 * first, extract syscall args from the uap.
263 */
264
265 addr = (void *)SCARG(uap, addr);
266 len = (size_t)SCARG(uap, len);
267 op = SCARG(uap, op);
268
269 DPRINTF(("sys_rasctl: p=%p addr=%p, len=%ld, op=0x%x\n",
270 curproc, addr, (long)len, op));
271
272 switch (op) {
273 case RAS_INSTALL:
274 error = ras_install(addr, len);
275 break;
276 case RAS_PURGE:
277 error = ras_purge(addr, len);
278 break;
279 case RAS_PURGE_ALL:
280 error = ras_purgeall();
281 break;
282 default:
283 error = EINVAL;
284 break;
285 }
286
287 return (error);
288 #else
289 return (EOPNOTSUPP);
290 #endif
291 }
Cache object: e48dedc780b83aa03311a65d26344555
|