]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/compat/linux/linux_emul.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / compat / linux / linux_emul.c
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$");
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  */
56 struct linux_emuldata *
57 em_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  */
72 struct linux_pemuldata *
73 pem_find(struct proc *p)
74 {
75         struct linux_pemuldata *pem;
76
77         pem = p->p_emuldata;
78
79         return (pem);
80 }
81
82 void
83 linux_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
140 void 
141 linux_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
170 int 
171 linux_common_execve(struct thread *td, struct image_args *eargs)
172 {
173         struct linux_pemuldata *pem;
174         struct epoll_emuldata *emd;
175         struct vmspace *oldvmspace;
176         struct linux_emuldata *em;
177         struct proc *p;
178         int error;
179
180         p = td->td_proc;
181
182         error = pre_execve(td, &oldvmspace);
183         if (error != 0)
184                 return (error);
185
186         error = kern_execve(td, eargs, NULL);
187         post_execve(td, error, oldvmspace);
188         if (error != 0)
189                 return (error);
190
191         /*
192          * In a case of transition from Linux binary execing to
193          * FreeBSD binary we destroy linux emuldata thread & proc entries.
194          */
195         if (SV_CURPROC_ABI() != SV_ABI_LINUX) {
196                 PROC_LOCK(p);
197                 em = em_find(td);
198                 KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n"));
199                 td->td_emuldata = NULL;
200
201                 pem = pem_find(p);
202                 KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n"));
203                 p->p_emuldata = NULL;
204                 PROC_UNLOCK(p);
205
206                 if (pem->epoll != NULL) {
207                         emd = pem->epoll;
208                         pem->epoll = NULL;
209                         free(emd, M_EPOLL);
210                 }
211
212                 free(em, M_TEMP);
213                 free(pem, M_LINUX);
214         }
215         return (0);
216 }
217
218 void 
219 linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
220 {
221         struct thread *td = curthread;
222         struct thread *othertd;
223
224         /*
225          * In a case of execing from linux binary properly detach
226          * other threads from the user space.
227          */
228         if (__predict_false(SV_PROC_ABI(p) == SV_ABI_LINUX)) {
229                 FOREACH_THREAD_IN_PROC(p, othertd) {
230                         if (td != othertd)
231                                 (p->p_sysent->sv_thread_detach)(othertd);
232                 }
233         }
234
235         /*
236          * In a case of execing to linux binary we create linux
237          * emuldata thread entry.
238          */
239         if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) ==
240             SV_ABI_LINUX)) {
241
242                 if (SV_PROC_ABI(p) == SV_ABI_LINUX)
243                         linux_proc_init(td, NULL, 0);
244                 else
245                         linux_proc_init(td, td, 0);
246         }
247 }
248
249 void
250 linux_thread_dtor(void *arg __unused, struct thread *td)
251 {
252         struct linux_emuldata *em;
253
254         em = em_find(td);
255         if (em == NULL)
256                 return;
257         td->td_emuldata = NULL;
258
259         LINUX_CTR1(thread_dtor, "thread(%d)", em->em_tid);
260
261         free(em, M_TEMP);
262 }
263
264 void
265 linux_schedtail(struct thread *td)
266 {
267         struct linux_emuldata *em;
268         struct proc *p;
269         int error = 0;
270         int *child_set_tid;
271
272         p = td->td_proc;
273
274         em = em_find(td);
275         KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n"));
276         child_set_tid = em->child_set_tid;
277
278         if (child_set_tid != NULL) {
279                 error = copyout(&em->em_tid, child_set_tid,
280                     sizeof(em->em_tid));
281                 LINUX_CTR4(schedtail, "thread(%d) %p stored %d error %d",
282                     td->td_tid, child_set_tid, em->em_tid, error);
283         } else
284                 LINUX_CTR1(schedtail, "thread(%d)", em->em_tid);
285 }