diff options
author | jhb <jhb@FreeBSD.org> | 2008-09-25 20:50:21 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2008-09-25 20:50:21 +0000 |
commit | 97facf9f0e918833593afbc7a32dde456fe2ea68 (patch) | |
tree | 149c53adc50c9efe458eabd4bd1cadf0ab2b2b23 | |
parent | f1ce06c8fb1bf46519e719f39617b53afb0c3ab8 (diff) | |
download | FreeBSD-src-97facf9f0e918833593afbc7a32dde456fe2ea68.zip FreeBSD-src-97facf9f0e918833593afbc7a32dde456fe2ea68.tar.gz |
Add support for installing 32-bit system calls from kernel modules. This
includes syscall32_{de,}register() routines as well as a module handler
and wrapper macros similar to the support for native syscalls in
<sys/sysent.h>.
MFC after: 1 month
-rw-r--r-- | sys/compat/freebsd32/freebsd32_misc.c | 80 | ||||
-rw-r--r-- | sys/compat/freebsd32/freebsd32_util.h | 30 | ||||
-rw-r--r-- | sys/compat/ia32/ia32_sysvec.c | 2 |
3 files changed, 110 insertions, 2 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 917a672..4c89d74 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -2624,3 +2624,83 @@ freebsd32_xxx(struct thread *td, struct freebsd32_xxx_args *uap) return (error); } #endif + +int +syscall32_register(int *offset, struct sysent *new_sysent, + struct sysent *old_sysent) +{ + if (*offset == NO_SYSCALL) { + int i; + + for (i = 1; i < SYS_MAXSYSCALL; ++i) + if (freebsd32_sysent[i].sy_call == + (sy_call_t *)lkmnosys) + break; + if (i == SYS_MAXSYSCALL) + return (ENFILE); + *offset = i; + } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL) + return (EINVAL); + else if (freebsd32_sysent[*offset].sy_call != (sy_call_t *)lkmnosys && + freebsd32_sysent[*offset].sy_call != (sy_call_t *)lkmressys) + return (EEXIST); + + *old_sysent = freebsd32_sysent[*offset]; + freebsd32_sysent[*offset] = *new_sysent; + return 0; +} + +int +syscall32_deregister(int *offset, struct sysent *old_sysent) +{ + + if (*offset) + freebsd32_sysent[*offset] = *old_sysent; + return 0; +} + +int +syscall32_module_handler(struct module *mod, int what, void *arg) +{ + struct syscall_module_data *data = (struct syscall_module_data*)arg; + modspecific_t ms; + int error; + + switch (what) { + case MOD_LOAD: + error = syscall32_register(data->offset, data->new_sysent, + &data->old_sysent); + if (error) { + /* Leave a mark so we know to safely unload below. */ + data->offset = NULL; + return error; + } + ms.intval = *data->offset; + MOD_XLOCK; + module_setspecific(mod, &ms); + MOD_XUNLOCK; + if (data->chainevh) + error = data->chainevh(mod, what, data->chainarg); + return (error); + case MOD_UNLOAD: + /* + * MOD_LOAD failed, so just return without calling the + * chained handler since we didn't pass along the MOD_LOAD + * event. + */ + if (data->offset == NULL) + return (0); + if (data->chainevh) { + error = data->chainevh(mod, what, data->chainarg); + if (error) + return (error); + } + error = syscall_deregister(data->offset, &data->old_sysent); + return (error); + default: + error = EOPNOTSUPP; + if (data->chainevh) + error = data->chainevh(mod, what, data->chainarg); + return (error); + } +} diff --git a/sys/compat/freebsd32/freebsd32_util.h b/sys/compat/freebsd32/freebsd32_util.h index 5a4477f..6536b2c 100644 --- a/sys/compat/freebsd32/freebsd32_util.h +++ b/sys/compat/freebsd32/freebsd32_util.h @@ -53,4 +53,34 @@ struct freebsd32_ps_strings { #define FREEBSD32_PS_STRINGS \ (FREEBSD32_USRSTACK - sizeof(struct freebsd32_ps_strings)) +extern struct sysent freebsd32_sysent[]; + +#define SYSCALL32_MODULE(name, offset, new_sysent, evh, arg) \ +static struct syscall_module_data name##_syscall32_mod = { \ + evh, arg, offset, new_sysent, { 0, NULL } \ +}; \ + \ +static moduledata_t name##32_mod = { \ + #name, \ + syscall32_module_handler, \ + &name##_syscall32_mod \ +}; \ +DECLARE_MODULE(name##32, name##32_mod, SI_SUB_SYSCALLS, SI_ORDER_MIDDLE) + +#define SYSCALL32_MODULE_HELPER(syscallname) \ +static int syscallname##_syscall32 = FREEBSD32_SYS_##syscallname; \ +static struct sysent syscallname##_sysent32 = { \ + (sizeof(struct syscallname ## _args ) \ + / sizeof(register_t)), \ + (sy_call_t *)& syscallname \ +}; \ +SYSCALL32_MODULE(syscallname, \ + & syscallname##_syscall32, & syscallname##_sysent32,\ + NULL, NULL); + +int syscall32_register(int *offset, struct sysent *new_sysent, + struct sysent *old_sysent); +int syscall32_deregister(int *offset, struct sysent *old_sysent); +int syscall32_module_handler(struct module *mod, int what, void *arg); + #endif /* !_COMPAT_FREEBSD32_FREEBSD32_UTIL_H_ */ diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index cf87b7c..ef74ba0 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -96,8 +96,6 @@ CTASSERT(sizeof(struct ia32_sigframe4) == 408); static register_t *ia32_copyout_strings(struct image_params *imgp); static void ia32_fixlimit(struct rlimit *rl, int which); -extern struct sysent freebsd32_sysent[]; - SYSCTL_NODE(_compat, OID_AUTO, ia32, CTLFLAG_RW, 0, "ia32 mode"); static u_long ia32_maxdsiz = IA32_MAXDSIZ; |