kern_syscalls.c revision 209579
1231990Smp/*-
259243Sobrien * Copyright (c) 1999 Assar Westerlund
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * Redistribution and use in source and binary forms, with or without
659243Sobrien * modification, are permitted provided that the following conditions
759243Sobrien * are met:
859243Sobrien * 1. Redistributions of source code must retain the above copyright
959243Sobrien *    notice, this list of conditions and the following disclaimer.
1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1159243Sobrien *    notice, this list of conditions and the following disclaimer in the
1259243Sobrien *    documentation and/or other materials provided with the distribution.
1359243Sobrien *
1459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17100616Smp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459243Sobrien * SUCH DAMAGE.
2559243Sobrien */
2659243Sobrien
2759243Sobrien#include <sys/cdefs.h>
2859243Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_syscalls.c 209579 2010-06-28 18:06:46Z kib $");
2959243Sobrien
3059243Sobrien#include <sys/param.h>
3159243Sobrien#include <sys/kernel.h>
3259243Sobrien#include <sys/lock.h>
3359243Sobrien#include <sys/module.h>
3459243Sobrien#include <sys/sx.h>
35231990Smp#include <sys/syscall.h>
3659243Sobrien#include <sys/sysent.h>
37167465Smp#include <sys/sysproto.h>
38167465Smp#include <sys/systm.h>
39167465Smp#include <machine/atomic.h>
40167465Smp
4159243Sobrien/*
4259243Sobrien * Acts like "nosys" but can be identified in sysent for dynamic call
4359243Sobrien * number assignment for a limited number of calls.
4459243Sobrien *
4559243Sobrien * Place holder for system call slots reserved for loadable modules.
4659243Sobrien */
47167465Smpint
4859243Sobrienlkmnosys(struct thread *td, struct nosys_args *args)
4959243Sobrien{
5059243Sobrien
5159243Sobrien	return (nosys(td, args));
52145479Smp}
5359243Sobrien
5459243Sobrienint
5559243Sobrienlkmressys(struct thread *td, struct nosys_args *args)
5659243Sobrien{
5759243Sobrien
58167465Smp	return (nosys(td, args));
5959243Sobrien}
60167465Smp
6159243Sobrienstatic void
6259243Sobriensyscall_thread_drain(struct sysent *se)
63231990Smp{
64167465Smp	u_int32_t cnt, oldcnt;
65167465Smp
66167465Smp	do {
67167465Smp		oldcnt = se->sy_thrcnt;
68167465Smp		KASSERT((oldcnt & SY_THR_STATIC) == 0,
69167465Smp		    ("drain on static syscall"));
70167465Smp		cnt = oldcnt | SY_THR_DRAINING;
71167465Smp	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
72167465Smp	while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
73167465Smp	    SY_THR_ABSENT) == 0)
74231990Smp		pause("scdrn", hz/2);
75167465Smp}
7659243Sobrien
77167465Smpint
7859243Sobriensyscall_thread_enter(struct thread *td, struct sysent *se)
79167465Smp{
80167465Smp	u_int32_t cnt, oldcnt;
8159243Sobrien
8259243Sobrien	do {
83145479Smp		oldcnt = se->sy_thrcnt;
84167465Smp		if ((oldcnt & SY_THR_STATIC) != 0)
85167465Smp			return (0);
86167465Smp		if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0)
87145479Smp			return (ENOSYS);
8859243Sobrien		cnt = oldcnt + SY_THR_INCR;
8959243Sobrien	} while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
9059243Sobrien	return (0);
91167465Smp}
9259243Sobrien
9359243Sobrienvoid
9459243Sobriensyscall_thread_exit(struct thread *td, struct sysent *se)
9559243Sobrien{
9659243Sobrien	u_int32_t cnt, oldcnt;
9759243Sobrien
9859243Sobrien	do {
9959243Sobrien		oldcnt = se->sy_thrcnt;
10059243Sobrien		if ((oldcnt & SY_THR_STATIC) != 0)
101167465Smp			return;
10259243Sobrien		cnt = oldcnt - SY_THR_INCR;
10359243Sobrien	} while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
10459243Sobrien}
10559243Sobrien
10659243Sobrienint
10759243Sobriensyscall_register(int *offset, struct sysent *new_sysent,
10859243Sobrien    struct sysent *old_sysent)
10959243Sobrien{
11059243Sobrien	int i;
111167465Smp
112167465Smp	if (*offset == NO_SYSCALL) {
11359243Sobrien		for (i = 1; i < SYS_MAXSYSCALL; ++i)
114167465Smp			if (sysent[i].sy_call == (sy_call_t *)lkmnosys)
115167465Smp				break;
11659243Sobrien		if (i == SYS_MAXSYSCALL)
117167465Smp			return (ENFILE);
11859243Sobrien		*offset = i;
119167465Smp	} else if (*offset < 0 || *offset >= SYS_MAXSYSCALL)
12059243Sobrien		return (EINVAL);
121167465Smp	else if (sysent[*offset].sy_call != (sy_call_t *)lkmnosys &&
12259243Sobrien	    sysent[*offset].sy_call != (sy_call_t *)lkmressys)
12359243Sobrien		return (EEXIST);
12459243Sobrien
125167465Smp	KASSERT(sysent[*offset].sy_thrcnt == SY_THR_ABSENT,
12659243Sobrien	    ("dynamic syscall is not protected"));
127145479Smp	*old_sysent = sysent[*offset];
12859243Sobrien	new_sysent->sy_thrcnt = SY_THR_ABSENT;
12959243Sobrien	sysent[*offset] = *new_sysent;
13059243Sobrien	atomic_store_rel_32(&sysent[*offset].sy_thrcnt, 0);
13159243Sobrien	return (0);
13259243Sobrien}
13359243Sobrien
13459243Sobrienint
135167465Smpsyscall_deregister(int *offset, struct sysent *old_sysent)
13659243Sobrien{
137145479Smp
13859243Sobrien	if (*offset) {
13959243Sobrien		syscall_thread_drain(&sysent[*offset]);
14059243Sobrien		sysent[*offset] = *old_sysent;
14159243Sobrien	}
14259243Sobrien	return (0);
14359243Sobrien}
14459243Sobrien
145167465Smpint
14659243Sobriensyscall_module_handler(struct module *mod, int what, void *arg)
14759243Sobrien{
14859243Sobrien	struct syscall_module_data *data = arg;
14959243Sobrien	modspecific_t ms;
15059243Sobrien	int error;
15159243Sobrien
15259243Sobrien	switch (what) {
153167465Smp	case MOD_LOAD:
15459243Sobrien		error = syscall_register(data->offset, data->new_sysent,
155145479Smp		    &data->old_sysent);
15659243Sobrien		if (error) {
15759243Sobrien			/* Leave a mark so we know to safely unload below. */
15859243Sobrien			data->offset = NULL;
15959243Sobrien			return (error);
160167465Smp		}
161167465Smp		ms.intval = *data->offset;
16259243Sobrien		MOD_XLOCK;
16359243Sobrien		module_setspecific(mod, &ms);
164167465Smp		MOD_XUNLOCK;
165167465Smp		if (data->chainevh)
166167465Smp			error = data->chainevh(mod, what, data->chainarg);
167167465Smp		return (error);
168167465Smp	case MOD_UNLOAD:
169167465Smp		/*
170167465Smp		 * MOD_LOAD failed, so just return without calling the
171167465Smp		 * chained handler since we didn't pass along the MOD_LOAD
172167465Smp		 * event.
173167465Smp		 */
174167465Smp		if (data->offset == NULL)
175167465Smp			return (0);
176167465Smp		if (data->chainevh) {
177167465Smp			error = data->chainevh(mod, what, data->chainarg);
178167465Smp			if (error)
179167465Smp				return error;
18059243Sobrien		}
181167465Smp		error = syscall_deregister(data->offset, &data->old_sysent);
18259243Sobrien		return (error);
183167465Smp	default:
18459243Sobrien		return EOPNOTSUPP;
185167465Smp	}
186167465Smp
187167465Smp	if (data->chainevh)
188167465Smp		return (data->chainevh(mod, what, data->chainarg));
189167465Smp	else
19059243Sobrien		return (0);
19159243Sobrien}
19259243Sobrien
19359243Sobrienint
19459243Sobriensyscall_helper_register(struct syscall_helper_data *sd)
195145479Smp{
19659243Sobrien	struct syscall_helper_data *sd1;
197167465Smp	int error;
19859243Sobrien
19959243Sobrien	for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) {
200145479Smp		error = syscall_register(&sd1->syscall_no, &sd1->new_sysent,
201145479Smp		    &sd1->old_sysent);
20259243Sobrien		if (error != 0) {
20359243Sobrien			syscall_helper_unregister(sd);
20459243Sobrien			return (error);
205167465Smp		}
20659243Sobrien		sd1->registered = 1;
20759243Sobrien	}
20859243Sobrien	return (0);
20959243Sobrien}
210145479Smp
21159243Sobrienint
21259243Sobriensyscall_helper_unregister(struct syscall_helper_data *sd)
213167465Smp{
21459243Sobrien	struct syscall_helper_data *sd1;
21559243Sobrien
216145479Smp	for (sd1 = sd; sd1->registered != 0; sd1++) {
21759243Sobrien		syscall_deregister(&sd1->syscall_no, &sd1->old_sysent);
21859243Sobrien		sd1->registered = 0;
21959243Sobrien	}
22059243Sobrien	return (0);
22159243Sobrien}
222145479Smp