Merge branch 'master' into kq_devices
[dragonfly.git] / sys / emulation / linux / linux_emuldata.c
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "opt_compat.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/imgact.h>
40 #include <sys/imgact_aout.h>
41 #include <sys/imgact_elf.h>
42 #include <sys/kern_syscall.h>
43 #include <sys/lock.h>
44 #include <sys/mplock2.h>
45 #include <sys/malloc.h>
46 #include <sys/ptrace.h>
47 #include <sys/proc.h>
48 #include <sys/signalvar.h>
49 #include <sys/sysent.h>
50 #include <sys/sysproto.h>
51
52 #include <vm/vm.h>
53 #include <vm/vm_param.h>
54 #include <vm/vm_page.h>
55 #include <vm/vm_extern.h>
56 #include <sys/exec.h>
57 #include <sys/kernel.h>
58 #include <sys/module.h>
59 #include <machine/cpu.h>
60
61 #include "i386/linux.h"
62 #include "i386/linux_proto.h"
63 #include "linux_signal.h"
64 #include "linux_util.h"
65 #include "linux_emuldata.h"
66
67
68 struct lock emul_lock;
69
70 struct linux_emuldata *
71 emuldata_get(struct proc *p)
72 {
73         struct linux_emuldata *em;
74
75         EMUL_LOCK();
76
77         em = p->p_emuldata;
78
79         EMUL_UNLOCK();
80         return (em);
81 }
82
83 void
84 emuldata_set_robust(struct proc *p, struct linux_robust_list_head *robust_ftx)
85 {
86         struct linux_emuldata *em;
87
88         EMUL_LOCK();
89
90         em = emuldata_get(p);
91         KKASSERT(em != NULL);
92
93         em->robust_futexes = robust_ftx;
94         EMUL_UNLOCK();
95 }
96
97 int
98 emuldata_init(struct proc *p, struct proc *pchild, int flags)
99 {
100         struct linux_emuldata_shared *s;
101         struct linux_emuldata *em, *ep;
102         int error = 0;
103
104         EMUL_LOCK();
105
106         em = emuldata_get(p);
107
108         if (pchild == NULL) {
109                 ep = NULL;
110                 /* This is the execv* case, where a process gets overwritten */
111                 KKASSERT(em != NULL);
112                 KKASSERT(em->s != NULL);
113                 if (atomic_fetchadd_int(&em->s->refs, -1) == 1) {
114                         kfree(em->s, M_LINUX); 
115                         em->s = NULL;
116                 }
117                 if (em->s)
118                         KKASSERT(em->s->refs >= 0);
119                 
120                 em->parent_tidptr = NULL;
121                 em->child_tidptr = NULL;
122                 em->clone_flags = 0;
123                 em->clear_tid = NULL;
124                 em->set_tls = NULL;
125                 em->proc = p;
126         } else {
127                 ep = em;
128                 em = kmalloc(sizeof(*em), M_LINUX, M_WAITOK | M_ZERO);
129         }
130
131         if (flags & LINUX_CLONE_THREAD) {
132                 /*
133                  * If CLONE_THREAD is set, the child is placed in the same
134                  * thread group as the calling process.
135                  */
136                 KKASSERT(ep != NULL);
137                 em->s = ep->s;
138                 s = em->s;
139         } else {
140                 /* new thread group */
141                 s = kmalloc(sizeof(*s), M_LINUX, M_WAITOK | M_ZERO);
142                 LIST_INIT(&s->threads);
143                 if (pchild)
144                         s->group_pid = pchild->p_pid;
145                 else
146                         s->group_pid = p->p_pid;
147         }
148
149         if (ep != NULL) {
150                 em->parent_tidptr = ep->parent_tidptr;
151                 em->child_tidptr = ep->child_tidptr;
152 #if 0
153                 em->clone_flags = ep->clone_flags;
154 #endif
155         }
156
157         em->clone_flags = flags;
158
159         atomic_add_int(&s->refs, 1);
160         KKASSERT(s->refs >= 0);
161         em->s = s;
162         LIST_INSERT_HEAD(&s->threads, em, threads);
163
164         
165         if (pchild != NULL) {
166                 em->proc = pchild;
167                 pchild->p_emuldata = em;
168         }
169
170         EMUL_UNLOCK();
171         return (error);
172 }
173
174 /* emuldata_exit is modelled after NetBSD's */
175 void
176 emuldata_exit(void *unused, struct proc *p)
177 {
178         struct linux_sys_futex_args cup;
179         struct linux_emuldata *em;
180         int error = 0;
181
182         if (__predict_true(p->p_sysent != &elf_linux_sysvec))
183                 return;
184
185         release_futexes(p);
186         EMUL_LOCK();
187
188         em = emuldata_get(p);
189         if (em == NULL) {
190                 EMUL_UNLOCK();
191                 return;
192         }
193
194         LIST_REMOVE(em, threads);
195         p->p_emuldata = NULL;
196
197         /*
198          * Members of the thread groups others than the leader should
199          * exit quietely: no zombie stage, no signal. We do that by
200          * reparenting to init. init will collect us and nobody will
201          * notice what happened.
202          */
203         if ((em->s->group_pid != p->p_pid) &&
204             (em->clone_flags & LINUX_CLONE_THREAD)) {
205                 p->p_sigparent = SIGCHLD;
206
207                 wakeup((caddr_t) initproc); /* kern_exit seems to do this */
208                 proc_reparent(p, initproc); /* XXX: might be dangerous */
209         }
210
211         if ((em->s->group_pid == p->p_pid) &&
212             (em->s->flags & LINUX_LES_INEXITGROUP)) {
213                 p->p_xstat = em->s->xstat;
214         }
215
216         if (atomic_fetchadd_int(&em->s->refs, -1) == 1) {
217                 kfree(em->s, M_LINUX);
218                 em->s = NULL;
219         }
220         if (em->s)
221                 KKASSERT(em->s->refs >= 0);
222
223         EMUL_UNLOCK();
224
225         if (em->clear_tid != NULL) {
226                 int tid = 0;
227                 copyout(&tid, em->clear_tid, sizeof(tid));
228                 cup.uaddr = em->clear_tid;
229                 cup.op = LINUX_FUTEX_WAKE;
230                 cup.val = 0x7fffffff;   /* Awake everyone */
231                 cup.timeout = NULL;
232                 cup.uaddr2 = NULL;
233                 cup.val3 = 0;
234                 error = sys_linux_sys_futex(&cup);
235                 if (error)
236                         kprintf("emuldata_exit futex stuff failed miserably\n");
237         }
238
239         kfree(em, M_LINUX);
240 }
241
242 void
243 linux_proc_transition(void *unused, struct image_params *imgp)
244 {
245         struct proc *p;
246
247         p = imgp->proc;
248         if (__predict_false(imgp->proc->p_sysent == &elf_linux_sysvec &&
249             imgp->proc->p_emuldata == NULL)) {
250 #ifdef LINUX_DEBUG
251                 kprintf("timidly hello from proc_transition\n");
252 #endif
253                 emuldata_init(p, p, 0);
254         }
255 }
256
257 static void
258 linux_proc_userret(void)
259 {
260         struct proc *p = curproc;
261         struct linux_emuldata *em;
262
263         em = emuldata_get(p);
264         KKASSERT(em != NULL);
265
266         if (em->clone_flags & LINUX_CLONE_CHILD_SETTID) {
267                 copyout(&p->p_pid, (int *)em->child_tidptr,
268                     sizeof(p->p_pid));
269         }
270
271         return;
272 }
273
274 void
275 linux_proc_fork(struct proc *p, struct proc *parent, void *child_tidptr)
276 {
277         struct linux_emuldata *em;
278
279         em = emuldata_get(p);
280         KKASSERT(em != NULL);
281
282         if (child_tidptr != NULL)
283                 em->child_tidptr = child_tidptr;
284
285         /* LINUX_CLONE_CHILD_CLEARTID: clear TID in child's memory on exit() */
286         if (em->clone_flags & LINUX_CLONE_CHILD_CLEARTID)
287                 em->clear_tid = em->child_tidptr;
288
289         if (em->clone_flags & LINUX_CLONE_CHILD_SETTID)
290                 p->p_userret = linux_proc_userret;
291
292         return;
293 }
294
295 int
296 sys_linux_set_tid_address(struct linux_set_tid_address_args *args)
297 {
298         struct linux_emuldata *em;
299
300         EMUL_LOCK();
301
302         em = emuldata_get(curproc);
303         KKASSERT(em != NULL);
304
305         em->clear_tid = args->tidptr;
306         args->sysmsg_iresult = curproc->p_pid;
307
308         EMUL_UNLOCK();
309         return 0;
310 }