xref: /freebsd/sys/kern/kern_syscalls.c (revision a1bd83fede8fef926d0013eca1402fd4dc18e6f3)
14c3df794SDoug Rabson /*-
28a36da99SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
29677b542eSDavid E. O'Brien #include <sys/cdefs.h>
30677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
31677b542eSDavid E. O'Brien 
324c3df794SDoug Rabson #include <sys/param.h>
33153ac44cSKonstantin Belousov #include <sys/kernel.h>
349b3851e9SAndrew R. Reiter #include <sys/lock.h>
354c3df794SDoug Rabson #include <sys/module.h>
364ea6a9a2SMateusz Guzik #include <sys/mutex.h>
374ea6a9a2SMateusz Guzik #include <sys/proc.h>
38f6f6d240SMateusz Guzik #include <sys/resourcevar.h>
39da672ec2SJohn Baldwin #include <sys/sx.h>
40da672ec2SJohn Baldwin #include <sys/syscall.h>
41da672ec2SJohn Baldwin #include <sys/sysent.h>
42da672ec2SJohn Baldwin #include <sys/sysproto.h>
43153ac44cSKonstantin Belousov #include <sys/systm.h>
44153ac44cSKonstantin Belousov #include <machine/atomic.h>
454c3df794SDoug Rabson 
4646db4836SPeter Wemm /*
4746db4836SPeter Wemm  * Acts like "nosys" but can be identified in sysent for dynamic call
4846db4836SPeter Wemm  * number assignment for a limited number of calls.
4946db4836SPeter Wemm  *
5046db4836SPeter Wemm  * Place holder for system call slots reserved for loadable modules.
5146db4836SPeter Wemm  */
5246db4836SPeter Wemm int
53b40ce416SJulian Elischer lkmnosys(struct thread *td, struct nosys_args *args)
5446db4836SPeter Wemm {
55da672ec2SJohn Baldwin 
56b40ce416SJulian Elischer 	return (nosys(td, args));
5746db4836SPeter Wemm }
5846db4836SPeter Wemm 
594c3df794SDoug Rabson int
60b40ce416SJulian Elischer lkmressys(struct thread *td, struct nosys_args *args)
6178525ce3SAlfred Perlstein {
62da672ec2SJohn Baldwin 
63b40ce416SJulian Elischer 	return (nosys(td, args));
6478525ce3SAlfred Perlstein }
6578525ce3SAlfred Perlstein 
66153ac44cSKonstantin Belousov static void
67153ac44cSKonstantin Belousov syscall_thread_drain(struct sysent *se)
68153ac44cSKonstantin Belousov {
69153ac44cSKonstantin Belousov 	u_int32_t cnt, oldcnt;
70153ac44cSKonstantin Belousov 
71153ac44cSKonstantin Belousov 	do {
72153ac44cSKonstantin Belousov 		oldcnt = se->sy_thrcnt;
73153ac44cSKonstantin Belousov 		KASSERT((oldcnt & SY_THR_STATIC) == 0,
74153ac44cSKonstantin Belousov 		    ("drain on static syscall"));
75153ac44cSKonstantin Belousov 		cnt = oldcnt | SY_THR_DRAINING;
76153ac44cSKonstantin Belousov 	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
77153ac44cSKonstantin Belousov 	while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
78153ac44cSKonstantin Belousov 	    SY_THR_ABSENT) == 0)
79153ac44cSKonstantin Belousov 		pause("scdrn", hz/2);
80153ac44cSKonstantin Belousov }
81153ac44cSKonstantin Belousov 
82153ac44cSKonstantin Belousov int
83*a1bd83feSEdward Tomasz Napierala syscall_thread_enter(struct thread *td, struct sysent *se)
84153ac44cSKonstantin Belousov {
85153ac44cSKonstantin Belousov 	u_int32_t cnt, oldcnt;
86153ac44cSKonstantin Belousov 
87*a1bd83feSEdward Tomasz Napierala 	KASSERT((se->sy_thrcnt & SY_THR_STATIC) == 0,
88*a1bd83feSEdward Tomasz Napierala 	    ("%s: not a static syscall", __func__));
89*a1bd83feSEdward Tomasz Napierala 
90153ac44cSKonstantin Belousov 	do {
91153ac44cSKonstantin Belousov 		oldcnt = se->sy_thrcnt;
92153ac44cSKonstantin Belousov 		if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0)
93153ac44cSKonstantin Belousov 			return (ENOSYS);
94153ac44cSKonstantin Belousov 		cnt = oldcnt + SY_THR_INCR;
95153ac44cSKonstantin Belousov 	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
96153ac44cSKonstantin Belousov 	return (0);
97153ac44cSKonstantin Belousov }
98153ac44cSKonstantin Belousov 
99153ac44cSKonstantin Belousov void
100*a1bd83feSEdward Tomasz Napierala syscall_thread_exit(struct thread *td, struct sysent *se)
101153ac44cSKonstantin Belousov {
102153ac44cSKonstantin Belousov 	u_int32_t cnt, oldcnt;
103153ac44cSKonstantin Belousov 
104*a1bd83feSEdward Tomasz Napierala 	KASSERT((se->sy_thrcnt & SY_THR_STATIC) == 0,
105*a1bd83feSEdward Tomasz Napierala 	    ("%s: not a static syscall", __func__));
106*a1bd83feSEdward Tomasz Napierala 
107153ac44cSKonstantin Belousov 	do {
108153ac44cSKonstantin Belousov 		oldcnt = se->sy_thrcnt;
109153ac44cSKonstantin Belousov 		cnt = oldcnt - SY_THR_INCR;
110153ac44cSKonstantin Belousov 	} while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
111153ac44cSKonstantin Belousov }
112153ac44cSKonstantin Belousov 
11378525ce3SAlfred Perlstein int
114b81e88d2SBrooks Davis kern_syscall_register(struct sysent *sysents, int *offset,
115b81e88d2SBrooks Davis     struct sysent *new_sysent, struct sysent *old_sysent, int flags)
1164c3df794SDoug Rabson {
1174c3df794SDoug Rabson 	int i;
1184c3df794SDoug Rabson 
119e015b1abSMateusz Guzik 	if ((flags & ~SY_THR_STATIC) != 0)
120e015b1abSMateusz Guzik 		return (EINVAL);
121e015b1abSMateusz Guzik 
122da672ec2SJohn Baldwin 	if (*offset == NO_SYSCALL) {
1234c3df794SDoug Rabson 		for (i = 1; i < SYS_MAXSYSCALL; ++i)
124b81e88d2SBrooks Davis 			if (sysents[i].sy_call == (sy_call_t *)lkmnosys)
1254c3df794SDoug Rabson 				break;
1264c3df794SDoug Rabson 		if (i == SYS_MAXSYSCALL)
127da672ec2SJohn Baldwin 			return (ENFILE);
1284c3df794SDoug Rabson 		*offset = i;
12964ebbdd5SAndriy Gapon 	} else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) {
130da672ec2SJohn Baldwin 		return (EINVAL);
13164ebbdd5SAndriy Gapon 	} else if (sysents[*offset].sy_call != (sy_call_t *)lkmnosys &&
13264ebbdd5SAndriy Gapon 	    sysents[*offset].sy_call != (sy_call_t *)lkmressys) {
13364ebbdd5SAndriy Gapon 		KASSERT(sysents[*offset].sy_call != NULL,
13464ebbdd5SAndriy Gapon 		    ("undefined syscall %d", *offset));
135da672ec2SJohn Baldwin 		return (EEXIST);
13664ebbdd5SAndriy Gapon 	}
1374c3df794SDoug Rabson 
138b81e88d2SBrooks Davis 	KASSERT(sysents[*offset].sy_thrcnt == SY_THR_ABSENT,
139153ac44cSKonstantin Belousov 	    ("dynamic syscall is not protected"));
140b81e88d2SBrooks Davis 	*old_sysent = sysents[*offset];
141153ac44cSKonstantin Belousov 	new_sysent->sy_thrcnt = SY_THR_ABSENT;
142b81e88d2SBrooks Davis 	sysents[*offset] = *new_sysent;
143b81e88d2SBrooks Davis 	atomic_store_rel_32(&sysents[*offset].sy_thrcnt, flags);
144da672ec2SJohn Baldwin 	return (0);
1454c3df794SDoug Rabson }
1464c3df794SDoug Rabson 
1474c3df794SDoug Rabson int
148b81e88d2SBrooks Davis kern_syscall_deregister(struct sysent *sysents, int offset,
149b81e88d2SBrooks Davis     const struct sysent *old_sysent)
1504c3df794SDoug Rabson {
151e015b1abSMateusz Guzik 	struct sysent *se;
152da672ec2SJohn Baldwin 
153b81e88d2SBrooks Davis 	if (offset == 0)
154e015b1abSMateusz Guzik 		return (0); /* XXX? */
155e015b1abSMateusz Guzik 
156b81e88d2SBrooks Davis 	se = &sysents[offset];
157e015b1abSMateusz Guzik 	if ((se->sy_thrcnt & SY_THR_STATIC) != 0)
158e015b1abSMateusz Guzik 		return (EINVAL);
159e015b1abSMateusz Guzik 	syscall_thread_drain(se);
160a2609714SAndriy Gapon 	sysents[offset] = *old_sysent;
161da672ec2SJohn Baldwin 	return (0);
1624c3df794SDoug Rabson }
1634c3df794SDoug Rabson 
1644c3df794SDoug Rabson int
1654c3df794SDoug Rabson syscall_module_handler(struct module *mod, int what, void *arg)
1664c3df794SDoug Rabson {
167b81e88d2SBrooks Davis 
168b81e88d2SBrooks Davis 	return (kern_syscall_module_handler(sysent, mod, what, arg));
169b81e88d2SBrooks Davis }
170b81e88d2SBrooks Davis 
171b81e88d2SBrooks Davis int
172b81e88d2SBrooks Davis kern_syscall_module_handler(struct sysent *sysents, struct module *mod,
173b81e88d2SBrooks Davis     int what, void *arg)
174b81e88d2SBrooks Davis {
175da672ec2SJohn Baldwin 	struct syscall_module_data *data = arg;
176a35261efSDoug Rabson 	modspecific_t ms;
1774c3df794SDoug Rabson 	int error;
1784c3df794SDoug Rabson 
1794c3df794SDoug Rabson 	switch (what) {
1804c3df794SDoug Rabson 	case MOD_LOAD:
181b81e88d2SBrooks Davis 		error = kern_syscall_register(sysents, data->offset,
182b81e88d2SBrooks Davis 		    data->new_sysent, &data->old_sysent, data->flags);
18303e161fdSJohn Baldwin 		if (error) {
18403e161fdSJohn Baldwin 			/* Leave a mark so we know to safely unload below. */
18503e161fdSJohn Baldwin 			data->offset = NULL;
186da672ec2SJohn Baldwin 			return (error);
18703e161fdSJohn Baldwin 		}
188a35261efSDoug Rabson 		ms.intval = *data->offset;
1899b3851e9SAndrew R. Reiter 		MOD_XLOCK;
190a35261efSDoug Rabson 		module_setspecific(mod, &ms);
1919b3851e9SAndrew R. Reiter 		MOD_XUNLOCK;
192c049aba8SDoug Rabson 		if (data->chainevh)
193c049aba8SDoug Rabson 			error = data->chainevh(mod, what, data->chainarg);
194da672ec2SJohn Baldwin 		return (error);
1954c3df794SDoug Rabson 	case MOD_UNLOAD:
19603e161fdSJohn Baldwin 		/*
19703e161fdSJohn Baldwin 		 * MOD_LOAD failed, so just return without calling the
19803e161fdSJohn Baldwin 		 * chained handler since we didn't pass along the MOD_LOAD
19903e161fdSJohn Baldwin 		 * event.
20003e161fdSJohn Baldwin 		 */
20103e161fdSJohn Baldwin 		if (data->offset == NULL)
20203e161fdSJohn Baldwin 			return (0);
203c049aba8SDoug Rabson 		if (data->chainevh) {
204c049aba8SDoug Rabson 			error = data->chainevh(mod, what, data->chainarg);
2054c3df794SDoug Rabson 			if (error)
2064c3df794SDoug Rabson 				return error;
2074c3df794SDoug Rabson 		}
208b81e88d2SBrooks Davis 		error = kern_syscall_deregister(sysents, *data->offset,
209b81e88d2SBrooks Davis 		    &data->old_sysent);
210da672ec2SJohn Baldwin 		return (error);
2113e019deaSPoul-Henning Kamp 	default:
2125e5fd037SXin LI 		if (data->chainevh)
2135e5fd037SXin LI 			return (data->chainevh(mod, what, data->chainarg));
2145e5fd037SXin LI 		return (EOPNOTSUPP);
215c049aba8SDoug Rabson 	}
216c049aba8SDoug Rabson 
21700e3c12eSXin LI 	/* NOTREACHED */
2184c3df794SDoug Rabson }
2190687ba3eSKonstantin Belousov 
2200687ba3eSKonstantin Belousov int
221e015b1abSMateusz Guzik syscall_helper_register(struct syscall_helper_data *sd, int flags)
2220687ba3eSKonstantin Belousov {
223b81e88d2SBrooks Davis 
224b81e88d2SBrooks Davis 	return (kern_syscall_helper_register(sysent, sd, flags));
225b81e88d2SBrooks Davis }
226b81e88d2SBrooks Davis 
227b81e88d2SBrooks Davis int
228b81e88d2SBrooks Davis kern_syscall_helper_register(struct sysent *sysents,
229b81e88d2SBrooks Davis     struct syscall_helper_data *sd, int flags)
230b81e88d2SBrooks Davis {
2310687ba3eSKonstantin Belousov 	struct syscall_helper_data *sd1;
2320687ba3eSKonstantin Belousov 	int error;
2330687ba3eSKonstantin Belousov 
2340687ba3eSKonstantin Belousov 	for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) {
235b81e88d2SBrooks Davis 		error = kern_syscall_register(sysents, &sd1->syscall_no,
236b81e88d2SBrooks Davis 		    &sd1->new_sysent, &sd1->old_sysent, flags);
2370687ba3eSKonstantin Belousov 		if (error != 0) {
238b81e88d2SBrooks Davis 			kern_syscall_helper_unregister(sysents, sd);
2390687ba3eSKonstantin Belousov 			return (error);
2400687ba3eSKonstantin Belousov 		}
2410687ba3eSKonstantin Belousov 		sd1->registered = 1;
2420687ba3eSKonstantin Belousov 	}
2430687ba3eSKonstantin Belousov 	return (0);
2440687ba3eSKonstantin Belousov }
2450687ba3eSKonstantin Belousov 
2460687ba3eSKonstantin Belousov int
2470687ba3eSKonstantin Belousov syscall_helper_unregister(struct syscall_helper_data *sd)
2480687ba3eSKonstantin Belousov {
249b81e88d2SBrooks Davis 
250b81e88d2SBrooks Davis 	return (kern_syscall_helper_unregister(sysent, sd));
251b81e88d2SBrooks Davis }
252b81e88d2SBrooks Davis 
253b81e88d2SBrooks Davis int
254b81e88d2SBrooks Davis kern_syscall_helper_unregister(struct sysent *sysents,
255b81e88d2SBrooks Davis     struct syscall_helper_data *sd)
256b81e88d2SBrooks Davis {
2570687ba3eSKonstantin Belousov 	struct syscall_helper_data *sd1;
2580687ba3eSKonstantin Belousov 
2590687ba3eSKonstantin Belousov 	for (sd1 = sd; sd1->registered != 0; sd1++) {
260b81e88d2SBrooks Davis 		kern_syscall_deregister(sysents, sd1->syscall_no,
261b81e88d2SBrooks Davis 		    &sd1->old_sysent);
2620687ba3eSKonstantin Belousov 		sd1->registered = 0;
2630687ba3eSKonstantin Belousov 	}
2640687ba3eSKonstantin Belousov 	return (0);
2650687ba3eSKonstantin Belousov }
266