xref: /freebsd/sys/kern/kern_syscalls.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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