]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_syscalls.c
unbound: Vendor import 1.19.1
[FreeBSD/FreeBSD.git] / sys / kern / kern_syscalls.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1999 Assar Westerlund
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/lock.h>
32 #include <sys/module.h>
33 #include <sys/mutex.h>
34 #include <sys/proc.h>
35 #include <sys/resourcevar.h>
36 #include <sys/sx.h>
37 #include <sys/syscall.h>
38 #include <sys/sysent.h>
39 #include <sys/sysproto.h>
40 #include <sys/systm.h>
41 #include <machine/atomic.h>
42
43 /*
44  * Acts like "nosys" but can be identified in sysent for dynamic call
45  * number assignment for a limited number of calls.
46  *
47  * Place holder for system call slots reserved for loadable modules.
48  */
49 int
50 lkmnosys(struct thread *td, struct nosys_args *args)
51 {
52
53         return (nosys(td, args));
54 }
55
56 int
57 lkmressys(struct thread *td, struct nosys_args *args)
58 {
59
60         return (nosys(td, args));
61 }
62
63 struct sysent nosys_sysent = {
64         .sy_call =      (sy_call_t *)nosys,
65         .sy_systrace_args_func = NULL,
66         .sy_narg =      0,
67         .sy_flags =     SYF_CAPENABLED,
68         .sy_auevent =   AUE_NULL,
69         .sy_entry =     0, /* DTRACE_IDNONE */
70         .sy_return =    0,
71         .sy_thrcnt =    SY_THR_STATIC,
72 };
73
74 static void
75 syscall_thread_drain(struct sysent *se)
76 {
77         uint32_t cnt, oldcnt;
78
79         do {
80                 oldcnt = se->sy_thrcnt;
81                 KASSERT((oldcnt & SY_THR_STATIC) == 0,
82                     ("drain on static syscall"));
83                 cnt = oldcnt | SY_THR_DRAINING;
84         } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
85         while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
86             SY_THR_ABSENT) == 0)
87                 pause("scdrn", hz/2);
88 }
89
90 int
91 syscall_thread_enter(struct thread *td, struct sysent **se)
92 {
93         uint32_t cnt, oldcnt;
94
95         KASSERT(((*se)->sy_thrcnt & SY_THR_STATIC) == 0,
96             ("%s: not a static syscall", __func__));
97
98         do {
99                 oldcnt = (*se)->sy_thrcnt;
100                 if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0) {
101                         *se = &nosys_sysent;
102                         return (0);
103                 }
104                 cnt = oldcnt + SY_THR_INCR;
105         } while (atomic_cmpset_acq_32(&(*se)->sy_thrcnt, oldcnt, cnt) == 0);
106         return (0);
107 }
108
109 void
110 syscall_thread_exit(struct thread *td, struct sysent *se)
111 {
112         uint32_t cnt, oldcnt;
113
114         KASSERT((se->sy_thrcnt & SY_THR_STATIC) == 0,
115             ("%s: not a static syscall", __func__));
116
117         do {
118                 oldcnt = se->sy_thrcnt;
119                 cnt = oldcnt - SY_THR_INCR;
120         } while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
121 }
122
123 int
124 kern_syscall_register(struct sysent *sysents, int *offset,
125     struct sysent *new_sysent, struct sysent *old_sysent, int flags)
126 {
127         int i;
128
129         if ((flags & ~SY_THR_STATIC) != 0)
130                 return (EINVAL);
131
132         if (*offset == NO_SYSCALL) {
133                 for (i = 1; i < SYS_MAXSYSCALL; ++i)
134                         if (sysents[i].sy_call == (sy_call_t *)lkmnosys)
135                                 break;
136                 if (i == SYS_MAXSYSCALL)
137                         return (ENFILE);
138                 *offset = i;
139         } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) {
140                 return (EINVAL);
141         } else if (sysents[*offset].sy_call != (sy_call_t *)lkmnosys &&
142             sysents[*offset].sy_call != (sy_call_t *)lkmressys) {
143                 KASSERT(sysents[*offset].sy_call != NULL,
144                     ("undefined syscall %d", *offset));
145                 return (EEXIST);
146         }
147
148         KASSERT(sysents[*offset].sy_thrcnt == SY_THR_ABSENT,
149             ("dynamic syscall is not protected"));
150         *old_sysent = sysents[*offset];
151         new_sysent->sy_thrcnt = SY_THR_ABSENT;
152         sysents[*offset] = *new_sysent;
153         atomic_store_rel_32(&sysents[*offset].sy_thrcnt, flags);
154         return (0);
155 }
156
157 int
158 kern_syscall_deregister(struct sysent *sysents, int offset,
159     const struct sysent *old_sysent)
160 {
161         struct sysent *se;
162
163         if (offset == 0)
164                 return (0); /* XXX? */
165
166         se = &sysents[offset];
167         if ((se->sy_thrcnt & SY_THR_STATIC) != 0)
168                 return (EINVAL);
169         syscall_thread_drain(se);
170         sysents[offset] = *old_sysent;
171         return (0);
172 }
173
174 int
175 syscall_module_handler(struct module *mod, int what, void *arg)
176 {
177
178         return (kern_syscall_module_handler(sysent, mod, what, arg));
179 }
180
181 int
182 kern_syscall_module_handler(struct sysent *sysents, struct module *mod,
183     int what, void *arg)
184 {
185         struct syscall_module_data *data = arg;
186         modspecific_t ms;
187         int error;
188
189         bzero(&ms, sizeof(ms));
190         switch (what) {
191         case MOD_LOAD:
192                 error = kern_syscall_register(sysents, data->offset,
193                     data->new_sysent, &data->old_sysent, data->flags);
194                 if (error) {
195                         /* Leave a mark so we know to safely unload below. */
196                         data->offset = NULL;
197                         return (error);
198                 }
199                 ms.intval = *data->offset;
200                 MOD_XLOCK;
201                 module_setspecific(mod, &ms);
202                 MOD_XUNLOCK;
203                 if (data->chainevh)
204                         error = data->chainevh(mod, what, data->chainarg);
205                 return (error);
206         case MOD_UNLOAD:
207                 /*
208                  * MOD_LOAD failed, so just return without calling the
209                  * chained handler since we didn't pass along the MOD_LOAD
210                  * event.
211                  */
212                 if (data->offset == NULL)
213                         return (0);
214                 if (data->chainevh) {
215                         error = data->chainevh(mod, what, data->chainarg);
216                         if (error)
217                                 return error;
218                 }
219                 error = kern_syscall_deregister(sysents, *data->offset,
220                     &data->old_sysent);
221                 return (error);
222         default:
223                 if (data->chainevh)
224                         return (data->chainevh(mod, what, data->chainarg));
225                 return (EOPNOTSUPP);
226         }
227
228         /* NOTREACHED */
229 }
230
231 int
232 syscall_helper_register(struct syscall_helper_data *sd, int flags)
233 {
234
235         return (kern_syscall_helper_register(sysent, sd, flags));
236 }
237
238 int
239 kern_syscall_helper_register(struct sysent *sysents,
240     struct syscall_helper_data *sd, int flags)
241 {
242         struct syscall_helper_data *sd1;
243         int error;
244
245         for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) {
246                 error = kern_syscall_register(sysents, &sd1->syscall_no,
247                     &sd1->new_sysent, &sd1->old_sysent, flags);
248                 if (error != 0) {
249                         kern_syscall_helper_unregister(sysents, sd);
250                         return (error);
251                 }
252                 sd1->registered = 1;
253         }
254         return (0);
255 }
256
257 int
258 syscall_helper_unregister(struct syscall_helper_data *sd)
259 {
260
261         return (kern_syscall_helper_unregister(sysent, sd));
262 }
263
264 int
265 kern_syscall_helper_unregister(struct sysent *sysents,
266     struct syscall_helper_data *sd)
267 {
268         struct syscall_helper_data *sd1;
269
270         for (sd1 = sd; sd1->registered != 0; sd1++) {
271                 kern_syscall_deregister(sysents, sd1->syscall_no,
272                     &sd1->old_sysent);
273                 sd1->registered = 0;
274         }
275         return (0);
276 }