1/* $NetBSD$ */ 2 3/*- 4 * Copyright (c) 2012 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD$"); 31 32#include <sys/param.h> 33#include <sys/proc.h> 34#include <sys/systm.h> 35#include <sys/module.h> 36#include <sys/atomic.h> 37#include <sys/syscallvar.h> 38 39#include "syscallemu.h" 40 41#if !defined(__HAVE_SYSCALL_INTERN) 42#error syscallemu requires __HAVE_SYSCALL_INTERN 43#endif 44 45static specificdata_key_t syscallemu_data_key; 46static unsigned int syscallemu_refcnt; 47 48static const struct syscall_package syscallemu_syscalls[] = { 49 { SYS_syscallemu, 0, (sy_call_t *)sys_syscallemu }, 50 { 0, 0, NULL }, 51}; 52 53struct syscallemu_data * 54syscallemu_getsce(struct proc *p) 55{ 56 return proc_getspecific(p, syscallemu_data_key); 57} 58 59void 60syscallemu_setsce(struct proc *p, struct syscallemu_data *sce) 61{ 62 proc_setspecific(p, syscallemu_data_key, sce); 63} 64 65/* 66 * specificdata destructor 67 */ 68static void 69syscallemu_dtor(void *priv) 70{ 71 struct syscallemu_data *sce = priv; 72 73 kmem_free(sce, sizeof(*sce)); 74 atomic_dec_uint(&syscallemu_refcnt); 75} 76 77/* 78 * Allocate private storage for the syscallemu parameters and stash it 79 * in process specificdata. This can only be called once per process. 80 * 81 * Returns EINVAL if the specified start address falls after the end. 82 * Returns EACCESS if syscallemu has already been configured for this process. 83 */ 84int 85sys_syscallemu(lwp_t *l, const struct sys_syscallemu_args *uap, 86 register_t *retval) 87{ 88 /* { 89 syscallarg(uintptr_t) user_start; 90 syscallarg(uintptr_t) user_end; 91 } */ 92 vaddr_t user_start = (vaddr_t)SCARG(uap, user_start); 93 vaddr_t user_end = (vaddr_t)SCARG(uap, user_end); 94 struct syscallemu_data *sce; 95 struct proc *p = l->l_proc; 96 97 if (syscallemu_getsce(p) != NULL) 98 return EACCES; 99 if (user_start >= user_end) 100 return EINVAL; 101 102 sce = kmem_alloc(sizeof(*sce), KM_SLEEP); 103 sce->sce_user_start = user_start; 104 sce->sce_user_end = user_end; 105 sce->sce_md_syscall = md_syscallemu(p); 106 KASSERT(sce->sce_md_syscall != NULL); 107 108 atomic_inc_uint(&syscallemu_refcnt); 109 syscallemu_setsce(p, sce); 110 111#ifdef DEBUG 112 printf("syscallemu: enabled for pid %d\n", p->p_pid); 113#endif 114 115 return 0; 116} 117 118/* 119 * Initialize the syscallemu module 120 */ 121static int 122syscallemu_init(void) 123{ 124 int error; 125 126 syscallemu_refcnt = 0; 127 128 /* XXX workaround for kern/45781 */ 129 if (emul_netbsd.e_sysent[SYS_syscallemu].sy_call == sys_nosys) { 130 printf("syscallemu: applying workaround for kern/45781\n"); 131 emul_netbsd.e_sysent[SYS_syscallemu].sy_call = sys_nomodule; 132 } 133 emul_netbsd.e_sysent[SYS_syscallemu].sy_narg = 134 sizeof(struct sys_syscallemu_args) / sizeof(register_t); 135 emul_netbsd.e_sysent[SYS_syscallemu].sy_argsize = 136 sizeof(struct sys_syscallemu_args); 137 138 error = proc_specific_key_create(&syscallemu_data_key, syscallemu_dtor); 139 if (error) { 140 printf("syscallemu: couldn't create proc specific key (%d)\n", 141 error); 142 return error; 143 } 144 145 error = syscall_establish(NULL, syscallemu_syscalls); 146 if (error) { 147 printf("syscallemu: couldn't establish syscalls\n"); 148 proc_specific_key_delete(syscallemu_data_key); 149 return ENXIO; 150 } 151 152 return 0; 153} 154 155/* 156 * Finalize the syscallemu module 157 */ 158static int 159syscallemu_fini(void) 160{ 161 if (syscallemu_refcnt > 0) 162 return EBUSY; 163 164 syscall_disestablish(NULL, syscallemu_syscalls); 165 proc_specific_key_delete(syscallemu_data_key); 166 return 0; 167} 168 169/* 170 * Module glue 171 */ 172MODULE(MODULE_CLASS_MISC, syscallemu, NULL); 173 174static int 175syscallemu_modcmd(modcmd_t cmd, void *arg) 176{ 177 switch (cmd) { 178 case MODULE_CMD_INIT: 179 return syscallemu_init(); 180 case MODULE_CMD_FINI: 181 return syscallemu_fini(); 182 case MODULE_CMD_AUTOUNLOAD: 183 return EBUSY; 184 default: 185 return ENOTTY; 186 } 187} 188