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