14c3df794SDoug Rabson /*- 24c3df794SDoug Rabson * Copyright (c) 1999 Assar Westerlund 34c3df794SDoug Rabson * All rights reserved. 44c3df794SDoug Rabson * 54c3df794SDoug Rabson * Redistribution and use in source and binary forms, with or without 64c3df794SDoug Rabson * modification, are permitted provided that the following conditions 74c3df794SDoug Rabson * are met: 84c3df794SDoug Rabson * 1. Redistributions of source code must retain the above copyright 94c3df794SDoug Rabson * notice, this list of conditions and the following disclaimer. 104c3df794SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 114c3df794SDoug Rabson * notice, this list of conditions and the following disclaimer in the 124c3df794SDoug Rabson * documentation and/or other materials provided with the distribution. 134c3df794SDoug Rabson * 144c3df794SDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 154c3df794SDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 164c3df794SDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 174c3df794SDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 184c3df794SDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 194c3df794SDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 204c3df794SDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214c3df794SDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 224c3df794SDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 234c3df794SDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 244c3df794SDoug Rabson * SUCH DAMAGE. 254c3df794SDoug Rabson */ 264c3df794SDoug Rabson 27677b542eSDavid E. O'Brien #include <sys/cdefs.h> 28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 29677b542eSDavid E. O'Brien 304c3df794SDoug Rabson #include <sys/param.h> 31153ac44cSKonstantin Belousov #include <sys/kernel.h> 329b3851e9SAndrew R. Reiter #include <sys/lock.h> 334c3df794SDoug Rabson #include <sys/module.h> 34*4ea6a9a2SMateusz Guzik #include <sys/mutex.h> 35*4ea6a9a2SMateusz Guzik #include <sys/proc.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 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 57b40ce416SJulian Elischer lkmressys(struct thread *td, struct nosys_args *args) 5878525ce3SAlfred Perlstein { 59da672ec2SJohn Baldwin 60b40ce416SJulian Elischer return (nosys(td, args)); 6178525ce3SAlfred Perlstein } 6278525ce3SAlfred Perlstein 63153ac44cSKonstantin Belousov static void 64153ac44cSKonstantin Belousov syscall_thread_drain(struct sysent *se) 65153ac44cSKonstantin Belousov { 66153ac44cSKonstantin Belousov u_int32_t cnt, oldcnt; 67153ac44cSKonstantin Belousov 68153ac44cSKonstantin Belousov do { 69153ac44cSKonstantin Belousov oldcnt = se->sy_thrcnt; 70153ac44cSKonstantin Belousov KASSERT((oldcnt & SY_THR_STATIC) == 0, 71153ac44cSKonstantin Belousov ("drain on static syscall")); 72153ac44cSKonstantin Belousov cnt = oldcnt | SY_THR_DRAINING; 73153ac44cSKonstantin Belousov } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0); 74153ac44cSKonstantin Belousov while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING, 75153ac44cSKonstantin Belousov SY_THR_ABSENT) == 0) 76153ac44cSKonstantin Belousov pause("scdrn", hz/2); 77153ac44cSKonstantin Belousov } 78153ac44cSKonstantin Belousov 79153ac44cSKonstantin Belousov int 80153ac44cSKonstantin Belousov syscall_thread_enter(struct thread *td, struct sysent *se) 81153ac44cSKonstantin Belousov { 82153ac44cSKonstantin Belousov u_int32_t cnt, oldcnt; 83153ac44cSKonstantin Belousov 84153ac44cSKonstantin Belousov do { 85153ac44cSKonstantin Belousov oldcnt = se->sy_thrcnt; 86153ac44cSKonstantin Belousov if ((oldcnt & SY_THR_STATIC) != 0) 87153ac44cSKonstantin Belousov return (0); 88153ac44cSKonstantin Belousov if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0) 89153ac44cSKonstantin Belousov return (ENOSYS); 90153ac44cSKonstantin Belousov cnt = oldcnt + SY_THR_INCR; 91153ac44cSKonstantin Belousov } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0); 92153ac44cSKonstantin Belousov return (0); 93153ac44cSKonstantin Belousov } 94153ac44cSKonstantin Belousov 95153ac44cSKonstantin Belousov void 96153ac44cSKonstantin Belousov syscall_thread_exit(struct thread *td, struct sysent *se) 97153ac44cSKonstantin Belousov { 98153ac44cSKonstantin Belousov u_int32_t cnt, oldcnt; 99153ac44cSKonstantin Belousov 100153ac44cSKonstantin Belousov do { 101153ac44cSKonstantin Belousov oldcnt = se->sy_thrcnt; 102153ac44cSKonstantin Belousov if ((oldcnt & SY_THR_STATIC) != 0) 103153ac44cSKonstantin Belousov return; 104153ac44cSKonstantin Belousov cnt = oldcnt - SY_THR_INCR; 105153ac44cSKonstantin Belousov } while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0); 106153ac44cSKonstantin Belousov } 107153ac44cSKonstantin Belousov 10878525ce3SAlfred Perlstein int 1094c3df794SDoug Rabson syscall_register(int *offset, struct sysent *new_sysent, 110e015b1abSMateusz Guzik struct sysent *old_sysent, int flags) 1114c3df794SDoug Rabson { 1124c3df794SDoug Rabson int i; 1134c3df794SDoug Rabson 114e015b1abSMateusz Guzik if ((flags & ~SY_THR_STATIC) != 0) 115e015b1abSMateusz Guzik return (EINVAL); 116e015b1abSMateusz Guzik 117da672ec2SJohn Baldwin if (*offset == NO_SYSCALL) { 1184c3df794SDoug Rabson for (i = 1; i < SYS_MAXSYSCALL; ++i) 1194c3df794SDoug Rabson if (sysent[i].sy_call == (sy_call_t *)lkmnosys) 1204c3df794SDoug Rabson break; 1214c3df794SDoug Rabson if (i == SYS_MAXSYSCALL) 122da672ec2SJohn Baldwin return (ENFILE); 1234c3df794SDoug Rabson *offset = i; 1244c3df794SDoug Rabson } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) 125da672ec2SJohn Baldwin return (EINVAL); 12678525ce3SAlfred Perlstein else if (sysent[*offset].sy_call != (sy_call_t *)lkmnosys && 12778525ce3SAlfred Perlstein sysent[*offset].sy_call != (sy_call_t *)lkmressys) 128da672ec2SJohn Baldwin return (EEXIST); 1294c3df794SDoug Rabson 130153ac44cSKonstantin Belousov KASSERT(sysent[*offset].sy_thrcnt == SY_THR_ABSENT, 131153ac44cSKonstantin Belousov ("dynamic syscall is not protected")); 1324c3df794SDoug Rabson *old_sysent = sysent[*offset]; 133153ac44cSKonstantin Belousov new_sysent->sy_thrcnt = SY_THR_ABSENT; 1344c3df794SDoug Rabson sysent[*offset] = *new_sysent; 135e015b1abSMateusz Guzik atomic_store_rel_32(&sysent[*offset].sy_thrcnt, flags); 136da672ec2SJohn Baldwin return (0); 1374c3df794SDoug Rabson } 1384c3df794SDoug Rabson 1394c3df794SDoug Rabson int 1404c3df794SDoug Rabson syscall_deregister(int *offset, struct sysent *old_sysent) 1414c3df794SDoug Rabson { 142e015b1abSMateusz Guzik struct sysent *se; 143da672ec2SJohn Baldwin 144e015b1abSMateusz Guzik if (*offset == 0) 145e015b1abSMateusz Guzik return (0); /* XXX? */ 146e015b1abSMateusz Guzik 147e015b1abSMateusz Guzik se = &sysent[*offset]; 148e015b1abSMateusz Guzik if ((se->sy_thrcnt & SY_THR_STATIC) != 0) 149e015b1abSMateusz Guzik return (EINVAL); 150e015b1abSMateusz Guzik syscall_thread_drain(se); 1514c3df794SDoug Rabson sysent[*offset] = *old_sysent; 152da672ec2SJohn Baldwin return (0); 1534c3df794SDoug Rabson } 1544c3df794SDoug Rabson 1554c3df794SDoug Rabson int 1564c3df794SDoug Rabson syscall_module_handler(struct module *mod, int what, void *arg) 1574c3df794SDoug Rabson { 158da672ec2SJohn Baldwin struct syscall_module_data *data = arg; 159a35261efSDoug Rabson modspecific_t ms; 1604c3df794SDoug Rabson int error; 1614c3df794SDoug Rabson 1624c3df794SDoug Rabson switch (what) { 1634c3df794SDoug Rabson case MOD_LOAD: 1644c3df794SDoug Rabson error = syscall_register(data->offset, data->new_sysent, 165cdcf2428SMateusz Guzik &data->old_sysent, data->flags); 16603e161fdSJohn Baldwin if (error) { 16703e161fdSJohn Baldwin /* Leave a mark so we know to safely unload below. */ 16803e161fdSJohn Baldwin data->offset = NULL; 169da672ec2SJohn Baldwin return (error); 17003e161fdSJohn Baldwin } 171a35261efSDoug Rabson ms.intval = *data->offset; 1729b3851e9SAndrew R. Reiter MOD_XLOCK; 173a35261efSDoug Rabson module_setspecific(mod, &ms); 1749b3851e9SAndrew R. Reiter MOD_XUNLOCK; 175c049aba8SDoug Rabson if (data->chainevh) 176c049aba8SDoug Rabson error = data->chainevh(mod, what, data->chainarg); 177da672ec2SJohn Baldwin return (error); 1784c3df794SDoug Rabson case MOD_UNLOAD: 17903e161fdSJohn Baldwin /* 18003e161fdSJohn Baldwin * MOD_LOAD failed, so just return without calling the 18103e161fdSJohn Baldwin * chained handler since we didn't pass along the MOD_LOAD 18203e161fdSJohn Baldwin * event. 18303e161fdSJohn Baldwin */ 18403e161fdSJohn Baldwin if (data->offset == NULL) 18503e161fdSJohn Baldwin return (0); 186c049aba8SDoug Rabson if (data->chainevh) { 187c049aba8SDoug Rabson error = data->chainevh(mod, what, data->chainarg); 1884c3df794SDoug Rabson if (error) 1894c3df794SDoug Rabson return error; 1904c3df794SDoug Rabson } 191c049aba8SDoug Rabson error = syscall_deregister(data->offset, &data->old_sysent); 192da672ec2SJohn Baldwin return (error); 1933e019deaSPoul-Henning Kamp default: 1945e5fd037SXin LI if (data->chainevh) 1955e5fd037SXin LI return (data->chainevh(mod, what, data->chainarg)); 1965e5fd037SXin LI return (EOPNOTSUPP); 197c049aba8SDoug Rabson } 198c049aba8SDoug Rabson 19900e3c12eSXin LI /* NOTREACHED */ 2004c3df794SDoug Rabson } 2010687ba3eSKonstantin Belousov 2020687ba3eSKonstantin Belousov int 203e015b1abSMateusz Guzik syscall_helper_register(struct syscall_helper_data *sd, int flags) 2040687ba3eSKonstantin Belousov { 2050687ba3eSKonstantin Belousov struct syscall_helper_data *sd1; 2060687ba3eSKonstantin Belousov int error; 2070687ba3eSKonstantin Belousov 2080687ba3eSKonstantin Belousov for (sd1 = sd; sd1->syscall_no != NO_SYSCALL; sd1++) { 2090687ba3eSKonstantin Belousov error = syscall_register(&sd1->syscall_no, &sd1->new_sysent, 210e015b1abSMateusz Guzik &sd1->old_sysent, flags); 2110687ba3eSKonstantin Belousov if (error != 0) { 2120687ba3eSKonstantin Belousov syscall_helper_unregister(sd); 2130687ba3eSKonstantin Belousov return (error); 2140687ba3eSKonstantin Belousov } 2150687ba3eSKonstantin Belousov sd1->registered = 1; 2160687ba3eSKonstantin Belousov } 2170687ba3eSKonstantin Belousov return (0); 2180687ba3eSKonstantin Belousov } 2190687ba3eSKonstantin Belousov 2200687ba3eSKonstantin Belousov int 2210687ba3eSKonstantin Belousov syscall_helper_unregister(struct syscall_helper_data *sd) 2220687ba3eSKonstantin Belousov { 2230687ba3eSKonstantin Belousov struct syscall_helper_data *sd1; 2240687ba3eSKonstantin Belousov 2250687ba3eSKonstantin Belousov for (sd1 = sd; sd1->registered != 0; sd1++) { 2260687ba3eSKonstantin Belousov syscall_deregister(&sd1->syscall_no, &sd1->old_sysent); 2270687ba3eSKonstantin Belousov sd1->registered = 0; 2280687ba3eSKonstantin Belousov } 2290687ba3eSKonstantin Belousov return (0); 2300687ba3eSKonstantin Belousov } 231