]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_syscalls.c
vmware: Fix a typo in a source code comment
[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/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/lock.h>
33 #include <sys/module.h>
34 #include <sys/mutex.h>
35 #include <sys/proc.h>
36 #include <sys/resourcevar.h>
37 #include <sys/sx.h>
38 #include <sys/syscall.h>
39 #include <sys/sysent.h>
40 #include <sys/sysproto.h>
41 #include <sys/systm.h>
42 #include <machine/atomic.h>
43
44 /*
45  * Acts like "nosys" but can be identified in sysent for dynamic call
46  * number assignment for a limited number of calls.
47  *
48  * Place holder for system call slots reserved for loadable modules.
49  */
50 int
51 lkmnosys(struct thread *td, struct nosys_args *args)
52 {
53
54         return (nosys(td, args));
55 }
56
57 int
58 lkmressys(struct thread *td, struct nosys_args *args)
59 {
60
61         return (nosys(td, args));
62 }
63
64 struct sysent nosys_sysent = {
65         .sy_call =      (sy_call_t *)nosys,
66         .sy_systrace_args_func = NULL,
67         .sy_narg =      0,
68         .sy_flags =     SYF_CAPENABLED,
69         .sy_auevent =   AUE_NULL,
70         .sy_entry =     0, /* DTRACE_IDNONE */
71         .sy_return =    0,
72         .sy_thrcnt =    SY_THR_STATIC,
73 };
74
75 static void
76 syscall_thread_drain(struct sysent *se)
77 {
78         u_int32_t cnt, oldcnt;
79
80         do {
81                 oldcnt = se->sy_thrcnt;
82                 KASSERT((oldcnt & SY_THR_STATIC) == 0,
83                     ("drain on static syscall"));
84                 cnt = oldcnt | SY_THR_DRAINING;
85         } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
86         while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
87             SY_THR_ABSENT) == 0)
88                 pause("scdrn", hz/2);
89 }
90
91 int
92 syscall_thread_enter(struct thread *td, struct sysent **se)
93 {
94         u_int32_t cnt, oldcnt;
95
96         KASSERT(((*se)->sy_thrcnt & SY_THR_STATIC) == 0,
97             ("%s: not a static syscall", __func__));
98
99         do {
100                 oldcnt = (*se)->sy_thrcnt;
101                 if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0) {
102                         *se = &nosys_sysent;
103                         return (0);
104                 }
105                 cnt = oldcnt + SY_THR_INCR;
106         } while (atomic_cmpset_acq_32(&(*se)->sy_thrcnt, oldcnt, cnt) == 0);
107         return (0);
108 }
109
110 void
111 syscall_thread_exit(struct thread *td, struct sysent *se)
112 {
113         u_int32_t cnt, oldcnt;
114
115         KASSERT((se->sy_thrcnt & SY_THR_STATIC) == 0,
116             ("%s: not a static syscall", __func__));
117
118         do {
119                 oldcnt = se->sy_thrcnt;
120                 cnt = oldcnt - SY_THR_INCR;
121         } while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
122 }
123
124 int
125 kern_syscall_register(struct sysent *sysents, int *offset,
126     struct sysent *new_sysent, struct sysent *old_sysent, int flags)
127 {
128         int i;
129
130         if ((flags & ~SY_THR_STATIC) != 0)
131                 return (EINVAL);
132
133         if (*offset == NO_SYSCALL) {
134                 for (i = 1; i < SYS_MAXSYSCALL; ++i)
135                         if (sysents[i].sy_call == (sy_call_t *)lkmnosys)
136                                 break;
137                 if (i == SYS_MAXSYSCALL)
138                         return (ENFILE);
139                 *offset = i;
140         } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) {
141                 return (EINVAL);
142         } else if (sysents[*offset].sy_call != (sy_call_t *)lkmnosys &&
143             sysents[*offset].sy_call != (sy_call_t *)lkmressys) {
144                 KASSERT(sysents[*offset].sy_call != NULL,
145                     ("undefined syscall %d", *offset));
146                 return (EEXIST);
147         }
148
149         KASSERT(sysents[*offset].sy_thrcnt == SY_THR_ABSENT,
150             ("dynamic syscall is not protected"));
151         *old_sysent = sysents[*offset];
152         new_sysent->sy_thrcnt = SY_THR_ABSENT;
153         sysents[*offset] = *new_sysent;
154         atomic_store_rel_32(&sysents[*offset].sy_thrcnt, flags);
155         return (0);
156 }
157
158 int
159 kern_syscall_deregister(struct sysent *sysents, int offset,
160     const struct sysent *old_sysent)
161 {
162         struct sysent *se;
163
164         if (offset == 0)
165                 return (0); /* XXX? */
166
167         se = &sysents[offset];
168         if ((se->sy_thrcnt & SY_THR_STATIC) != 0)
169                 return (EINVAL);
170         syscall_thread_drain(se);
171         sysents[offset] = *old_sysent;
172         return (0);
173 }
174
175 int
176 syscall_module_handler(struct module *mod, int what, void *arg)
177 {
178
179         return (kern_syscall_module_handler(sysent, mod, what, arg));
180 }
181
182 int
183 kern_syscall_module_handler(struct sysent *sysents, struct module *mod,
184     int what, void *arg)
185 {
186         struct syscall_module_data *data = arg;
187         modspecific_t ms = { 0 };
188         int error;
189
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 }