14c3df794SDoug Rabson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni *
44c3df794SDoug Rabson * Copyright (c) 1999 Assar Westerlund
54c3df794SDoug Rabson * All rights reserved.
64c3df794SDoug Rabson *
74c3df794SDoug Rabson * Redistribution and use in source and binary forms, with or without
84c3df794SDoug Rabson * modification, are permitted provided that the following conditions
94c3df794SDoug Rabson * are met:
104c3df794SDoug Rabson * 1. Redistributions of source code must retain the above copyright
114c3df794SDoug Rabson * notice, this list of conditions and the following disclaimer.
124c3df794SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright
134c3df794SDoug Rabson * notice, this list of conditions and the following disclaimer in the
144c3df794SDoug Rabson * documentation and/or other materials provided with the distribution.
154c3df794SDoug Rabson *
164c3df794SDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
174c3df794SDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184c3df794SDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194c3df794SDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204c3df794SDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214c3df794SDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224c3df794SDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234c3df794SDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244c3df794SDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254c3df794SDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264c3df794SDoug Rabson * SUCH DAMAGE.
274c3df794SDoug Rabson */
284c3df794SDoug Rabson
294c3df794SDoug Rabson #include <sys/param.h>
30153ac44cSKonstantin Belousov #include <sys/kernel.h>
319b3851e9SAndrew R. Reiter #include <sys/lock.h>
324c3df794SDoug Rabson #include <sys/module.h>
334ea6a9a2SMateusz Guzik #include <sys/mutex.h>
344ea6a9a2SMateusz Guzik #include <sys/proc.h>
35f6f6d240SMateusz Guzik #include <sys/resourcevar.h>
36da672ec2SJohn Baldwin #include <sys/sx.h>
37da672ec2SJohn Baldwin #include <sys/syscall.h>
38da672ec2SJohn Baldwin #include <sys/sysent.h>
39da672ec2SJohn Baldwin #include <sys/sysproto.h>
40153ac44cSKonstantin Belousov #include <sys/systm.h>
41153ac44cSKonstantin Belousov #include <machine/atomic.h>
424c3df794SDoug Rabson
4346db4836SPeter Wemm /*
4446db4836SPeter Wemm * Acts like "nosys" but can be identified in sysent for dynamic call
4546db4836SPeter Wemm * number assignment for a limited number of calls.
4646db4836SPeter Wemm *
4746db4836SPeter Wemm * Place holder for system call slots reserved for loadable modules.
4846db4836SPeter Wemm */
4946db4836SPeter Wemm int
lkmnosys(struct thread * td,struct nosys_args * args)50b40ce416SJulian Elischer lkmnosys(struct thread *td, struct nosys_args *args)
5146db4836SPeter Wemm {
52da672ec2SJohn Baldwin
53b40ce416SJulian Elischer return (nosys(td, args));
5446db4836SPeter Wemm }
5546db4836SPeter Wemm
564c3df794SDoug Rabson int
lkmressys(struct thread * td,struct nosys_args * args)57b40ce416SJulian Elischer lkmressys(struct thread *td, struct nosys_args *args)
5878525ce3SAlfred Perlstein {
59da672ec2SJohn Baldwin
60b40ce416SJulian Elischer return (nosys(td, args));
6178525ce3SAlfred Perlstein }
6278525ce3SAlfred Perlstein
63*39024a89SKonstantin Belousov struct sysent nosys_sysent = {
64*39024a89SKonstantin Belousov .sy_call = (sy_call_t *)nosys,
65*39024a89SKonstantin Belousov .sy_systrace_args_func = NULL,
66*39024a89SKonstantin Belousov .sy_narg = 0,
67*39024a89SKonstantin Belousov .sy_flags = SYF_CAPENABLED,
68*39024a89SKonstantin Belousov .sy_auevent = AUE_NULL,
69*39024a89SKonstantin Belousov .sy_entry = 0, /* DTRACE_IDNONE */
70*39024a89SKonstantin Belousov .sy_return = 0,
71*39024a89SKonstantin Belousov .sy_thrcnt = SY_THR_STATIC,
72*39024a89SKonstantin Belousov };
73*39024a89SKonstantin Belousov
74153ac44cSKonstantin Belousov static void
syscall_thread_drain(struct sysent * se)75153ac44cSKonstantin Belousov syscall_thread_drain(struct sysent *se)
76153ac44cSKonstantin Belousov {
772cee5861SJohn Baldwin uint32_t cnt, oldcnt;
78153ac44cSKonstantin Belousov
79153ac44cSKonstantin Belousov do {
80153ac44cSKonstantin Belousov oldcnt = se->sy_thrcnt;
81153ac44cSKonstantin Belousov KASSERT((oldcnt & SY_THR_STATIC) == 0,
82153ac44cSKonstantin Belousov ("drain on static syscall"));
83153ac44cSKonstantin Belousov cnt = oldcnt | SY_THR_DRAINING;
84153ac44cSKonstantin Belousov } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
85153ac44cSKonstantin Belousov while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
86153ac44cSKonstantin Belousov SY_THR_ABSENT) == 0)
87153ac44cSKonstantin Belousov pause("scdrn", hz/2);
88153ac44cSKonstantin Belousov }
89153ac44cSKonstantin Belousov
90153ac44cSKonstantin Belousov int
syscall_thread_enter(struct thread * td,struct sysent ** se)91*39024a89SKonstantin Belousov syscall_thread_enter(struct thread *td, struct sysent **se)
92153ac44cSKonstantin Belousov {
932cee5861SJohn Baldwin uint32_t cnt, oldcnt;
94153ac44cSKonstantin Belousov
95*39024a89SKonstantin Belousov KASSERT(((*se)->sy_thrcnt & SY_THR_STATIC) == 0,
96a1bd83feSEdward Tomasz Napierala ("%s: not a static syscall", __func__));
97a1bd83feSEdward Tomasz Napierala
98153ac44cSKonstantin Belousov do {
99*39024a89SKonstantin Belousov oldcnt = (*se)->sy_thrcnt;
100*39024a89SKonstantin Belousov if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0) {
101*39024a89SKonstantin Belousov *se = &nosys_sysent;
102*39024a89SKonstantin Belousov return (0);
103*39024a89SKonstantin Belousov }
104153ac44cSKonstantin Belousov cnt = oldcnt + SY_THR_INCR;
105*39024a89SKonstantin Belousov } while (atomic_cmpset_acq_32(&(*se)->sy_thrcnt, oldcnt, cnt) == 0);
106153ac44cSKonstantin Belousov return (0);
107153ac44cSKonstantin Belousov }
108153ac44cSKonstantin Belousov
109153ac44cSKonstantin Belousov void
syscall_thread_exit(struct thread * td,struct sysent * se)110a1bd83feSEdward Tomasz Napierala syscall_thread_exit(struct thread *td, struct sysent *se)
111153ac44cSKonstantin Belousov {
1122cee5861SJohn Baldwin uint32_t cnt, oldcnt;
113153ac44cSKonstantin Belousov
114a1bd83feSEdward Tomasz Napierala KASSERT((se->sy_thrcnt & SY_THR_STATIC) == 0,
115a1bd83feSEdward Tomasz Napierala ("%s: not a static syscall", __func__));
116a1bd83feSEdward Tomasz Napierala
117153ac44cSKonstantin Belousov do {
118153ac44cSKonstantin Belousov oldcnt = se->sy_thrcnt;
119153ac44cSKonstantin Belousov cnt = oldcnt - SY_THR_INCR;
120153ac44cSKonstantin Belousov } while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
121153ac44cSKonstantin Belousov }
122153ac44cSKonstantin Belousov
12378525ce3SAlfred Perlstein int
kern_syscall_register(struct sysent * sysents,int * offset,struct sysent * new_sysent,struct sysent * old_sysent,int flags)124b81e88d2SBrooks Davis kern_syscall_register(struct sysent *sysents, int *offset,
125b81e88d2SBrooks Davis struct sysent *new_sysent, struct sysent *old_sysent, int flags)
1264c3df794SDoug Rabson {
1274c3df794SDoug Rabson int i;
1284c3df794SDoug Rabson
129e015b1abSMateusz Guzik if ((flags & ~SY_THR_STATIC) != 0)
130e015b1abSMateusz Guzik return (EINVAL);
131e015b1abSMateusz Guzik
132da672ec2SJohn Baldwin if (*offset == NO_SYSCALL) {
1334c3df794SDoug Rabson for (i = 1; i < SYS_MAXSYSCALL; ++i)
134b81e88d2SBrooks Davis if (sysents[i].sy_call == (sy_call_t *)lkmnosys)
1354c3df794SDoug Rabson break;
1364c3df794SDoug Rabson if (i == SYS_MAXSYSCALL)
137da672ec2SJohn Baldwin return (ENFILE);
1384c3df794SDoug Rabson *offset = i;
13964ebbdd5SAndriy Gapon } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) {
140da672ec2SJohn Baldwin return (EINVAL);
14164ebbdd5SAndriy Gapon } else if (sysents[*offset].sy_call != (sy_call_t *)lkmnosys &&
14264ebbdd5SAndriy Gapon sysents[*offset].sy_call != (sy_call_t *)lkmressys) {
14364ebbdd5SAndriy Gapon KASSERT(sysents[*offset].sy_call != NULL,
14464ebbdd5SAndriy Gapon ("undefined syscall %d", *offset));
145da672ec2SJohn Baldwin return (EEXIST);
14664ebbdd5SAndriy Gapon }
1474c3df794SDoug Rabson
148b81e88d2SBrooks Davis KASSERT(sysents[*offset].sy_thrcnt == SY_THR_ABSENT,
149153ac44cSKonstantin Belousov ("dynamic syscall is not protected"));
150b81e88d2SBrooks Davis *old_sysent = sysents[*offset];
151153ac44cSKonstantin Belousov new_sysent->sy_thrcnt = SY_THR_ABSENT;
152b81e88d2SBrooks Davis sysents[*offset] = *new_sysent;
153b81e88d2SBrooks Davis atomic_store_rel_32(&sysents[*offset].sy_thrcnt, flags);
154da672ec2SJohn Baldwin return (0);
1554c3df794SDoug Rabson }
1564c3df794SDoug Rabson
1574c3df794SDoug Rabson int
kern_syscall_deregister(struct sysent * sysents,int offset,const struct sysent * old_sysent)158b81e88d2SBrooks Davis kern_syscall_deregister(struct sysent *sysents, int offset,
159b81e88d2SBrooks Davis const struct sysent *old_sysent)
1604c3df794SDoug Rabson {
161e015b1abSMateusz Guzik struct sysent *se;
162da672ec2SJohn Baldwin
163b81e88d2SBrooks Davis if (offset == 0)
164e015b1abSMateusz Guzik return (0); /* XXX? */
165e015b1abSMateusz Guzik
166b81e88d2SBrooks Davis se = &sysents[offset];
167e015b1abSMateusz Guzik if ((se->sy_thrcnt & SY_THR_STATIC) != 0)
168e015b1abSMateusz Guzik return (EINVAL);
169e015b1abSMateusz Guzik syscall_thread_drain(se);
170a2609714SAndriy Gapon sysents[offset] = *old_sysent;
171da672ec2SJohn Baldwin return (0);
1724c3df794SDoug Rabson }
1734c3df794SDoug Rabson
1744c3df794SDoug Rabson int
syscall_module_handler(struct module * mod,int what,void * arg)1754c3df794SDoug Rabson syscall_module_handler(struct module *mod, int what, void *arg)
1764c3df794SDoug Rabson {
177b81e88d2SBrooks Davis
178b81e88d2SBrooks Davis return (kern_syscall_module_handler(sysent, mod, what, arg));
179b81e88d2SBrooks Davis }
180b81e88d2SBrooks Davis
181b81e88d2SBrooks Davis int
kern_syscall_module_handler(struct sysent * sysents,struct module * mod,int what,void * arg)182b81e88d2SBrooks Davis kern_syscall_module_handler(struct sysent *sysents, struct module *mod,
183b81e88d2SBrooks Davis int what, void *arg)
184b81e88d2SBrooks Davis {
185da672ec2SJohn Baldwin struct syscall_module_data *data = arg;
186005aa174SKa Ho Ng modspecific_t ms;
1874c3df794SDoug Rabson int error;
1884c3df794SDoug Rabson
189005aa174SKa Ho Ng bzero(&ms, sizeof(ms));
1904c3df794SDoug Rabson switch (what) {
1914c3df794SDoug Rabson case MOD_LOAD:
192b81e88d2SBrooks Davis error = kern_syscall_register(sysents, data->offset,
193b81e88d2SBrooks Davis data->new_sysent, &data->old_sysent, data->flags);
19403e161fdSJohn Baldwin if (error) {
19503e161fdSJohn Baldwin /* Leave a mark so we know to safely unload below. */
19603e161fdSJohn Baldwin data->offset = NULL;
197da672ec2SJohn Baldwin return (error);
19803e161fdSJohn Baldwin }
199a35261efSDoug Rabson ms.intval = *data->offset;
2009b3851e9SAndrew R. Reiter MOD_XLOCK;
201a35261efSDoug Rabson module_setspecific(mod, &ms);
2029b3851e9SAndrew R. Reiter MOD_XUNLOCK;
203c049aba8SDoug Rabson if (data->chainevh)
204c049aba8SDoug Rabson error = data->chainevh(mod, what, data->chainarg);
205da672ec2SJohn Baldwin return (error);
2064c3df794SDoug Rabson case MOD_UNLOAD:
20703e161fdSJohn Baldwin /*
20803e161fdSJohn Baldwin * MOD_LOAD failed, so just return without calling the
20903e161fdSJohn Baldwin * chained handler since we didn't pass along the MOD_LOAD
21003e161fdSJohn Baldwin * event.
21103e161fdSJohn Baldwin */
21203e161fdSJohn Baldwin if (data->offset == NULL)
21303e161fdSJohn Baldwin return (0);
214c049aba8SDoug Rabson if (data->chainevh) {
215c049aba8SDoug Rabson error = data->chainevh(mod, what, data->chainarg);
2164c3df794SDoug Rabson if (error)
2174c3df794SDoug Rabson return error;
2184c3df794SDoug Rabson }
219b81e88d2SBrooks Davis error = kern_syscall_deregister(sysents, *data->offset,
220b81e88d2SBrooks Davis &data->old_sysent);
221da672ec2SJohn Baldwin return (error);
2223e019deaSPoul-Henning Kamp default:
2235e5fd037SXin LI if (data->chainevh)
2245e5fd037SXin LI return (data->chainevh(mod, what, data->chainarg));
2255e5fd037SXin LI return (EOPNOTSUPP);
226c049aba8SDoug Rabson }
227c049aba8SDoug Rabson
22800e3c12eSXin LI /* NOTREACHED */
2294c3df794SDoug Rabson }
2300687ba3eSKonstantin Belousov
2310687ba3eSKonstantin Belousov int
syscall_helper_register(struct syscall_helper_data * sd,int flags)232e015b1abSMateusz Guzik syscall_helper_register(struct syscall_helper_data *sd, int flags)
2330687ba3eSKonstantin Belousov {
234b81e88d2SBrooks Davis
235b81e88d2SBrooks Davis return (kern_syscall_helper_register(sysent, sd, flags));
236b81e88d2SBrooks Davis }
237b81e88d2SBrooks Davis
238b81e88d2SBrooks Davis int
kern_syscall_helper_register(struct sysent * sysents,struct syscall_helper_data * sd,int flags)239b81e88d2SBrooks Davis kern_syscall_helper_register(struct sysent *sysents,
240b81e88d2SBrooks Davis struct syscall_helper_data *sd, int flags)
241b81e88d2SBrooks Davis {
2420687ba3eSKonstantin Belousov struct syscall_helper_data *sd1;
2430687ba3eSKonstantin Belousov int error;
2440687ba3eSKonstantin Belousov
2450687ba3eSKonstantin Belousov for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) {
246b81e88d2SBrooks Davis error = kern_syscall_register(sysents, &sd1->syscall_no,
247b81e88d2SBrooks Davis &sd1->new_sysent, &sd1->old_sysent, flags);
2480687ba3eSKonstantin Belousov if (error != 0) {
249b81e88d2SBrooks Davis kern_syscall_helper_unregister(sysents, sd);
2500687ba3eSKonstantin Belousov return (error);
2510687ba3eSKonstantin Belousov }
2520687ba3eSKonstantin Belousov sd1->registered = 1;
2530687ba3eSKonstantin Belousov }
2540687ba3eSKonstantin Belousov return (0);
2550687ba3eSKonstantin Belousov }
2560687ba3eSKonstantin Belousov
2570687ba3eSKonstantin Belousov int
syscall_helper_unregister(struct syscall_helper_data * sd)2580687ba3eSKonstantin Belousov syscall_helper_unregister(struct syscall_helper_data *sd)
2590687ba3eSKonstantin Belousov {
260b81e88d2SBrooks Davis
261b81e88d2SBrooks Davis return (kern_syscall_helper_unregister(sysent, sd));
262b81e88d2SBrooks Davis }
263b81e88d2SBrooks Davis
264b81e88d2SBrooks Davis int
kern_syscall_helper_unregister(struct sysent * sysents,struct syscall_helper_data * sd)265b81e88d2SBrooks Davis kern_syscall_helper_unregister(struct sysent *sysents,
266b81e88d2SBrooks Davis struct syscall_helper_data *sd)
267b81e88d2SBrooks Davis {
2680687ba3eSKonstantin Belousov struct syscall_helper_data *sd1;
2690687ba3eSKonstantin Belousov
2700687ba3eSKonstantin Belousov for (sd1 = sd; sd1->registered != 0; sd1++) {
271b81e88d2SBrooks Davis kern_syscall_deregister(sysents, sd1->syscall_no,
272b81e88d2SBrooks Davis &sd1->old_sysent);
2730687ba3eSKonstantin Belousov sd1->registered = 0;
2740687ba3eSKonstantin Belousov }
2750687ba3eSKonstantin Belousov return (0);
2760687ba3eSKonstantin Belousov }
277