142433Sdfr/*-
242433Sdfr * Copyright (c) 1999 Assar Westerlund
342433Sdfr * All rights reserved.
442433Sdfr *
542433Sdfr * Redistribution and use in source and binary forms, with or without
642433Sdfr * modification, are permitted provided that the following conditions
742433Sdfr * are met:
842433Sdfr * 1. Redistributions of source code must retain the above copyright
942433Sdfr *    notice, this list of conditions and the following disclaimer.
1042433Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1142433Sdfr *    notice, this list of conditions and the following disclaimer in the
1242433Sdfr *    documentation and/or other materials provided with the distribution.
1342433Sdfr *
1442433Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1542433Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1642433Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1742433Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1842433Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1942433Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2042433Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2142433Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2242433Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2342433Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2442433Sdfr * SUCH DAMAGE.
2542433Sdfr */
2642433Sdfr
27116182Sobrien#include <sys/cdefs.h>
28116182Sobrien__FBSDID("$FreeBSD$");
29116182Sobrien
3042433Sdfr#include <sys/param.h>
31209579Skib#include <sys/kernel.h>
3292547Sarr#include <sys/lock.h>
33183156Sjhb#include <sys/module.h>
3492547Sarr#include <sys/sx.h>
35183156Sjhb#include <sys/syscall.h>
36183156Sjhb#include <sys/sysent.h>
37183156Sjhb#include <sys/sysproto.h>
38209579Skib#include <sys/systm.h>
39209579Skib#include <machine/atomic.h>
4042433Sdfr
4142756Speter/*
42183156Sjhb * Acts like "nosys" but can be identified in sysent for dynamic call
43183156Sjhb * number assignment for a limited number of calls.
44183156Sjhb *
4542756Speter * Place holder for system call slots reserved for loadable modules.
46183156Sjhb */
4742433Sdfrint
4883366Sjulianlkmnosys(struct thread *td, struct nosys_args *args)
4942756Speter{
50183156Sjhb
51183156Sjhb	return (nosys(td, args));
5242756Speter}
5342756Speter
5442756Speterint
5583366Sjulianlkmressys(struct thread *td, struct nosys_args *args)
5669449Salfred{
57183156Sjhb
58183156Sjhb	return (nosys(td, args));
5969449Salfred}
6069449Salfred
61209579Skibstatic void
62209579Skibsyscall_thread_drain(struct sysent *se)
63209579Skib{
64209579Skib	u_int32_t cnt, oldcnt;
65209579Skib
66209579Skib	do {
67209579Skib		oldcnt = se->sy_thrcnt;
68209579Skib		KASSERT((oldcnt & SY_THR_STATIC) == 0,
69209579Skib		    ("drain on static syscall"));
70209579Skib		cnt = oldcnt | SY_THR_DRAINING;
71209579Skib	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
72209579Skib	while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
73209579Skib	    SY_THR_ABSENT) == 0)
74209579Skib		pause("scdrn", hz/2);
75209579Skib}
76209579Skib
7769449Salfredint
78209579Skibsyscall_thread_enter(struct thread *td, struct sysent *se)
79209579Skib{
80209579Skib	u_int32_t cnt, oldcnt;
81209579Skib
82209579Skib	do {
83209579Skib		oldcnt = se->sy_thrcnt;
84209579Skib		if ((oldcnt & SY_THR_STATIC) != 0)
85209579Skib			return (0);
86209579Skib		if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0)
87209579Skib			return (ENOSYS);
88209579Skib		cnt = oldcnt + SY_THR_INCR;
89209579Skib	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
90209579Skib	return (0);
91209579Skib}
92209579Skib
93209579Skibvoid
94209579Skibsyscall_thread_exit(struct thread *td, struct sysent *se)
95209579Skib{
96209579Skib	u_int32_t cnt, oldcnt;
97209579Skib
98209579Skib	do {
99209579Skib		oldcnt = se->sy_thrcnt;
100209579Skib		if ((oldcnt & SY_THR_STATIC) != 0)
101209579Skib			return;
102209579Skib		cnt = oldcnt - SY_THR_INCR;
103209579Skib	} while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
104209579Skib}
105209579Skib
106209579Skibint
10742433Sdfrsyscall_register(int *offset, struct sysent *new_sysent,
108183156Sjhb    struct sysent *old_sysent)
10942433Sdfr{
110183156Sjhb	int i;
11142433Sdfr
112183156Sjhb	if (*offset == NO_SYSCALL) {
113183156Sjhb		for (i = 1; i < SYS_MAXSYSCALL; ++i)
114183156Sjhb			if (sysent[i].sy_call == (sy_call_t *)lkmnosys)
115183156Sjhb				break;
116183156Sjhb		if (i == SYS_MAXSYSCALL)
117183156Sjhb			return (ENFILE);
118183156Sjhb		*offset = i;
119183156Sjhb	} else if (*offset < 0 || *offset >= SYS_MAXSYSCALL)
120183156Sjhb		return (EINVAL);
121183156Sjhb	else if (sysent[*offset].sy_call != (sy_call_t *)lkmnosys &&
122183156Sjhb	    sysent[*offset].sy_call != (sy_call_t *)lkmressys)
123183156Sjhb		return (EEXIST);
12442433Sdfr
125209579Skib	KASSERT(sysent[*offset].sy_thrcnt == SY_THR_ABSENT,
126209579Skib	    ("dynamic syscall is not protected"));
127183156Sjhb	*old_sysent = sysent[*offset];
128209579Skib	new_sysent->sy_thrcnt = SY_THR_ABSENT;
129183156Sjhb	sysent[*offset] = *new_sysent;
130209579Skib	atomic_store_rel_32(&sysent[*offset].sy_thrcnt, 0);
131183156Sjhb	return (0);
13242433Sdfr}
13342433Sdfr
13442433Sdfrint
13542433Sdfrsyscall_deregister(int *offset, struct sysent *old_sysent)
13642433Sdfr{
137183156Sjhb
138209579Skib	if (*offset) {
139209579Skib		syscall_thread_drain(&sysent[*offset]);
140183156Sjhb		sysent[*offset] = *old_sysent;
141209579Skib	}
142183156Sjhb	return (0);
14342433Sdfr}
14442433Sdfr
14542433Sdfrint
14642433Sdfrsyscall_module_handler(struct module *mod, int what, void *arg)
14742433Sdfr{
148183156Sjhb	struct syscall_module_data *data = arg;
149183156Sjhb	modspecific_t ms;
150183156Sjhb	int error;
15142433Sdfr
152183156Sjhb	switch (what) {
153183156Sjhb	case MOD_LOAD:
154183156Sjhb		error = syscall_register(data->offset, data->new_sysent,
155183156Sjhb		    &data->old_sysent);
156183156Sjhb		if (error) {
157183156Sjhb			/* Leave a mark so we know to safely unload below. */
158183156Sjhb			data->offset = NULL;
159183156Sjhb			return (error);
160183156Sjhb		}
161183156Sjhb		ms.intval = *data->offset;
162183156Sjhb		MOD_XLOCK;
163183156Sjhb		module_setspecific(mod, &ms);
164183156Sjhb		MOD_XUNLOCK;
165183156Sjhb		if (data->chainevh)
166183156Sjhb			error = data->chainevh(mod, what, data->chainarg);
167183156Sjhb		return (error);
168183156Sjhb	case MOD_UNLOAD:
169183156Sjhb		/*
170183156Sjhb		 * MOD_LOAD failed, so just return without calling the
171183156Sjhb		 * chained handler since we didn't pass along the MOD_LOAD
172183156Sjhb		 * event.
173183156Sjhb		 */
174183156Sjhb		if (data->offset == NULL)
175183156Sjhb			return (0);
176183156Sjhb		if (data->chainevh) {
177183156Sjhb			error = data->chainevh(mod, what, data->chainarg);
178183156Sjhb			if (error)
179183156Sjhb				return error;
180183156Sjhb		}
181183156Sjhb		error = syscall_deregister(data->offset, &data->old_sysent);
182183156Sjhb		return (error);
183183156Sjhb	default:
184214181Sdelphij		if (data->chainevh)
185214181Sdelphij			return (data->chainevh(mod, what, data->chainarg));
186214181Sdelphij		return (EOPNOTSUPP);
187183156Sjhb	}
18848269Sdfr
189214125Sdelphij	/* NOTREACHED */
19042433Sdfr}
191205321Skib
192205321Skibint
193205321Skibsyscall_helper_register(struct syscall_helper_data *sd)
194205321Skib{
195205321Skib	struct syscall_helper_data *sd1;
196205321Skib	int error;
197205321Skib
198205321Skib	for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) {
199205321Skib		error = syscall_register(&sd1->syscall_no, &sd1->new_sysent,
200205321Skib		    &sd1->old_sysent);
201205321Skib		if (error != 0) {
202205321Skib			syscall_helper_unregister(sd);
203205321Skib			return (error);
204205321Skib		}
205205321Skib		sd1->registered = 1;
206205321Skib	}
207205321Skib	return (0);
208205321Skib}
209205321Skib
210205321Skibint
211205321Skibsyscall_helper_unregister(struct syscall_helper_data *sd)
212205321Skib{
213205321Skib	struct syscall_helper_data *sd1;
214205321Skib
215205321Skib	for (sd1 = sd; sd1->registered != 0; sd1++) {
216205321Skib		syscall_deregister(&sd1->syscall_no, &sd1->old_sysent);
217205321Skib		sd1->registered = 0;
218205321Skib	}
219205321Skib	return (0);
220205321Skib}
221