linux_emul.c revision 293546
1/*- 2 * Copyright (c) 2006 Roman Divacky 3 * Copyright (c) 2013 Dmitry Chagin 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/10/sys/compat/linux/linux_emul.c 293546 2016-01-09 16:44:17Z dchagin $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/imgact.h> 36#include <sys/kernel.h> 37#include <sys/ktr.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mutex.h> 41#include <sys/sx.h> 42#include <sys/proc.h> 43#include <sys/syscallsubr.h> 44#include <sys/sysent.h> 45 46#include <compat/linux/linux_emul.h> 47#include <compat/linux/linux_misc.h> 48#include <compat/linux/linux_util.h> 49 50 51/* 52 * This returns reference to the thread emuldata entry (if found) 53 * 54 * Hold PROC_LOCK when referencing emuldata from other threads. 55 */ 56struct linux_emuldata * 57em_find(struct thread *td) 58{ 59 struct linux_emuldata *em; 60 61 em = td->td_emuldata; 62 63 return (em); 64} 65 66/* 67 * This returns reference to the proc pemuldata entry (if found) 68 * 69 * Hold PROC_LOCK when referencing proc pemuldata from other threads. 70 * Hold LINUX_PEM_LOCK wher referencing pemuldata members. 71 */ 72struct linux_pemuldata * 73pem_find(struct proc *p) 74{ 75 struct linux_pemuldata *pem; 76 77 pem = p->p_emuldata; 78 79 return (pem); 80} 81 82void 83linux_proc_init(struct thread *td, struct thread *newtd, int flags) 84{ 85 struct linux_emuldata *em; 86 struct linux_pemuldata *pem; 87 struct epoll_emuldata *emd; 88 89 if (newtd != NULL) { 90 /* non-exec call */ 91 em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO); 92 em->pdeath_signal = 0; 93 em->robust_futexes = NULL; 94 if (flags & LINUX_CLONE_THREAD) { 95 LINUX_CTR1(proc_init, "thread newtd(%d)", 96 newtd->td_tid); 97 98 em->em_tid = newtd->td_tid; 99 } else { 100 LINUX_CTR1(proc_init, "fork newtd(%d)", 101 newtd->td_proc->p_pid); 102 103 em->em_tid = newtd->td_proc->p_pid; 104 105 pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO); 106 sx_init(&pem->pem_sx, "lpemlk"); 107 newtd->td_proc->p_emuldata = pem; 108 } 109 newtd->td_emuldata = em; 110 } else { 111 /* exec */ 112 LINUX_CTR1(proc_init, "exec newtd(%d)", 113 td->td_proc->p_pid); 114 115 /* lookup the old one */ 116 em = em_find(td); 117 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 118 119 em->em_tid = td->td_proc->p_pid; 120 121 /* epoll should be destroyed in a case of exec. */ 122 pem = pem_find(td->td_proc); 123 KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n")); 124 125 if (pem->epoll != NULL) { 126 emd = pem->epoll; 127 pem->epoll = NULL; 128 free(emd, M_EPOLL); 129 } 130 } 131 132 em->child_clear_tid = NULL; 133 em->child_set_tid = NULL; 134} 135 136void 137linux_proc_exit(void *arg __unused, struct proc *p) 138{ 139 struct linux_pemuldata *pem; 140 struct epoll_emuldata *emd; 141 struct thread *td = curthread; 142 143 if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX)) 144 return; 145 146 pem = pem_find(p); 147 if (pem == NULL) 148 return; 149 (p->p_sysent->sv_thread_detach)(td); 150 151 p->p_emuldata = NULL; 152 153 if (pem->epoll != NULL) { 154 emd = pem->epoll; 155 pem->epoll = NULL; 156 free(emd, M_EPOLL); 157 } 158 159 sx_destroy(&pem->pem_sx); 160 free(pem, M_LINUX); 161} 162 163int 164linux_common_execve(struct thread *td, struct image_args *eargs) 165{ 166 struct linux_pemuldata *pem; 167 struct epoll_emuldata *emd; 168 struct linux_emuldata *em; 169 struct proc *p; 170 int error; 171 172 p = td->td_proc; 173 174 /* 175 * Unlike FreeBSD abort all other threads before 176 * proceeding exec. 177 */ 178 PROC_LOCK(p); 179 /* See exit1() comments. */ 180 thread_suspend_check(0); 181 while (p->p_flag & P_HADTHREADS) { 182 if (!thread_single(p, SINGLE_EXIT)) 183 break; 184 thread_suspend_check(0); 185 } 186 PROC_UNLOCK(p); 187 188 error = kern_execve(td, eargs, NULL); 189 if (error != 0) 190 return (error); 191 192 /* 193 * In a case of transition from Linux binary execing to 194 * FreeBSD binary we destroy linux emuldata thread & proc entries. 195 */ 196 if (SV_CURPROC_ABI() != SV_ABI_LINUX) { 197 PROC_LOCK(p); 198 em = em_find(td); 199 KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n")); 200 td->td_emuldata = NULL; 201 202 pem = pem_find(p); 203 KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n")); 204 p->p_emuldata = NULL; 205 PROC_UNLOCK(p); 206 207 if (pem->epoll != NULL) { 208 emd = pem->epoll; 209 pem->epoll = NULL; 210 free(emd, M_EPOLL); 211 } 212 213 free(em, M_TEMP); 214 free(pem, M_LINUX); 215 } 216 return (0); 217} 218 219void 220linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) 221{ 222 struct thread *td = curthread; 223 224 /* 225 * In a case of execing to linux binary we create linux 226 * emuldata thread entry. 227 */ 228 if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) == 229 SV_ABI_LINUX)) { 230 231 if (SV_PROC_ABI(p) == SV_ABI_LINUX) 232 linux_proc_init(td, NULL, 0); 233 else 234 linux_proc_init(td, td, 0); 235 } 236} 237 238void 239linux_thread_dtor(void *arg __unused, struct thread *td) 240{ 241 struct linux_emuldata *em; 242 243 em = em_find(td); 244 if (em == NULL) 245 return; 246 td->td_emuldata = NULL; 247 248 LINUX_CTR1(exit, "thread dtor(%d)", em->em_tid); 249 250 free(em, M_TEMP); 251} 252 253void 254linux_schedtail(struct thread *td) 255{ 256 struct linux_emuldata *em; 257 struct proc *p; 258 int error = 0; 259 int *child_set_tid; 260 261 p = td->td_proc; 262 263 em = em_find(td); 264 KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n")); 265 child_set_tid = em->child_set_tid; 266 267 if (child_set_tid != NULL) { 268 error = copyout(&em->em_tid, (int *)child_set_tid, 269 sizeof(em->em_tid)); 270 LINUX_CTR4(clone, "schedtail(%d) %p stored %d error %d", 271 td->td_tid, child_set_tid, em->em_tid, error); 272 } else 273 LINUX_CTR1(clone, "schedtail(%d)", em->em_tid); 274} 275