linux_emul.c revision 293560
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 293560 2016-01-09 17:05:00Z 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 struct proc *p; 89 90 if (newtd != NULL) { 91 p = newtd->td_proc; 92 93 /* non-exec call */ 94 em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO); 95 if (flags & LINUX_CLONE_THREAD) { 96 LINUX_CTR1(proc_init, "thread newtd(%d)", 97 newtd->td_tid); 98 99 em->em_tid = newtd->td_tid; 100 } else { 101 LINUX_CTR1(proc_init, "fork newtd(%d)", p->p_pid); 102 103 em->em_tid = p->p_pid; 104 105 pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO); 106 sx_init(&pem->pem_sx, "lpemlk"); 107 p->p_emuldata = pem; 108 } 109 newtd->td_emuldata = em; 110 } else { 111 p = td->td_proc; 112 113 /* exec */ 114 LINUX_CTR1(proc_init, "exec newtd(%d)", p->p_pid); 115 116 /* lookup the old one */ 117 em = em_find(td); 118 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 119 120 em->em_tid = p->p_pid; 121 em->flags = 0; 122 em->pdeath_signal = 0; 123 em->robust_futexes = NULL; 124 em->child_clear_tid = NULL; 125 em->child_set_tid = NULL; 126 127 /* epoll should be destroyed in a case of exec. */ 128 pem = pem_find(p); 129 KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n")); 130 131 if (pem->epoll != NULL) { 132 emd = pem->epoll; 133 pem->epoll = NULL; 134 free(emd, M_EPOLL); 135 } 136 } 137 138} 139 140void 141linux_proc_exit(void *arg __unused, struct proc *p) 142{ 143 struct linux_pemuldata *pem; 144 struct epoll_emuldata *emd; 145 struct thread *td = curthread; 146 147 if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX)) 148 return; 149 150 LINUX_CTR3(proc_exit, "thread(%d) proc(%d) p %p", 151 td->td_tid, p->p_pid, p); 152 153 pem = pem_find(p); 154 if (pem == NULL) 155 return; 156 (p->p_sysent->sv_thread_detach)(td); 157 158 p->p_emuldata = NULL; 159 160 if (pem->epoll != NULL) { 161 emd = pem->epoll; 162 pem->epoll = NULL; 163 free(emd, M_EPOLL); 164 } 165 166 sx_destroy(&pem->pem_sx); 167 free(pem, M_LINUX); 168} 169 170int 171linux_common_execve(struct thread *td, struct image_args *eargs) 172{ 173 struct linux_pemuldata *pem; 174 struct epoll_emuldata *emd; 175 struct linux_emuldata *em; 176 struct proc *p; 177 int error; 178 179 p = td->td_proc; 180 181 /* 182 * Unlike FreeBSD abort all other threads before 183 * proceeding exec. 184 */ 185 PROC_LOCK(p); 186 /* See exit1() comments. */ 187 thread_suspend_check(0); 188 while (p->p_flag & P_HADTHREADS) { 189 if (!thread_single(p, SINGLE_EXIT)) 190 break; 191 thread_suspend_check(0); 192 } 193 PROC_UNLOCK(p); 194 195 error = kern_execve(td, eargs, NULL); 196 if (error != 0) 197 return (error); 198 199 /* 200 * In a case of transition from Linux binary execing to 201 * FreeBSD binary we destroy linux emuldata thread & proc entries. 202 */ 203 if (SV_CURPROC_ABI() != SV_ABI_LINUX) { 204 PROC_LOCK(p); 205 em = em_find(td); 206 KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n")); 207 td->td_emuldata = NULL; 208 209 pem = pem_find(p); 210 KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n")); 211 p->p_emuldata = NULL; 212 PROC_UNLOCK(p); 213 214 if (pem->epoll != NULL) { 215 emd = pem->epoll; 216 pem->epoll = NULL; 217 free(emd, M_EPOLL); 218 } 219 220 free(em, M_TEMP); 221 free(pem, M_LINUX); 222 } 223 return (0); 224} 225 226void 227linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) 228{ 229 struct thread *td = curthread; 230 231 /* 232 * In a case of execing to linux binary we create linux 233 * emuldata thread entry. 234 */ 235 if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) == 236 SV_ABI_LINUX)) { 237 238 if (SV_PROC_ABI(p) == SV_ABI_LINUX) 239 linux_proc_init(td, NULL, 0); 240 else 241 linux_proc_init(td, td, 0); 242 } 243} 244 245void 246linux_thread_dtor(void *arg __unused, struct thread *td) 247{ 248 struct linux_emuldata *em; 249 250 em = em_find(td); 251 if (em == NULL) 252 return; 253 td->td_emuldata = NULL; 254 255 LINUX_CTR1(thread_dtor, "thread(%d)", em->em_tid); 256 257 free(em, M_TEMP); 258} 259 260void 261linux_schedtail(struct thread *td) 262{ 263 struct linux_emuldata *em; 264 struct proc *p; 265 int error = 0; 266 int *child_set_tid; 267 268 p = td->td_proc; 269 270 em = em_find(td); 271 KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n")); 272 child_set_tid = em->child_set_tid; 273 274 if (child_set_tid != NULL) { 275 error = copyout(&em->em_tid, child_set_tid, 276 sizeof(em->em_tid)); 277 LINUX_CTR4(schedtail, "thread(%d) %p stored %d error %d", 278 td->td_tid, child_set_tid, em->em_tid, error); 279 } else 280 LINUX_CTR1(schedtail, "thread(%d)", em->em_tid); 281} 282