xref: /freebsd/sys/compat/linux/linux_emul.c (revision e16fe1c7304dad853feab78221dfdadce3053e7e)
1ad2056f2SAlexander Leidinger /*-
2ad2056f2SAlexander Leidinger  * Copyright (c) 2006 Roman Divacky
381338031SDmitry Chagin  * Copyright (c) 2013 Dmitry Chagin
4ad2056f2SAlexander Leidinger  * All rights reserved.
5ad2056f2SAlexander Leidinger  *
6ad2056f2SAlexander Leidinger  * Redistribution and use in source and binary forms, with or without
7ad2056f2SAlexander Leidinger  * modification, are permitted provided that the following conditions
8ad2056f2SAlexander Leidinger  * are met:
9ad2056f2SAlexander Leidinger  * 1. Redistributions of source code must retain the above copyright
10ad2056f2SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer
11ad2056f2SAlexander Leidinger  *    in this position and unchanged.
12ad2056f2SAlexander Leidinger  * 2. Redistributions in binary form must reproduce the above copyright
13ad2056f2SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer in the
14ad2056f2SAlexander Leidinger  *    documentation and/or other materials provided with the distribution.
15ad2056f2SAlexander Leidinger  * 3. The name of the author may not be used to endorse or promote products
16ad2056f2SAlexander Leidinger  *    derived from this software without specific prior written permission
17ad2056f2SAlexander Leidinger  *
18ad2056f2SAlexander Leidinger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19ad2056f2SAlexander Leidinger  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20ad2056f2SAlexander Leidinger  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21ad2056f2SAlexander Leidinger  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22ad2056f2SAlexander Leidinger  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23ad2056f2SAlexander Leidinger  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24ad2056f2SAlexander Leidinger  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25ad2056f2SAlexander Leidinger  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26ad2056f2SAlexander Leidinger  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27ad2056f2SAlexander Leidinger  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28ad2056f2SAlexander Leidinger  */
29ad2056f2SAlexander Leidinger 
30ad2056f2SAlexander Leidinger #include <sys/cdefs.h>
31ad2056f2SAlexander Leidinger __FBSDID("$FreeBSD$");
32ad2056f2SAlexander Leidinger 
33ad2056f2SAlexander Leidinger #include <sys/param.h>
34ad2056f2SAlexander Leidinger #include <sys/systm.h>
35ad2056f2SAlexander Leidinger #include <sys/imgact.h>
36b4bb5154SKonstantin Belousov #include <sys/kernel.h>
3781338031SDmitry Chagin #include <sys/ktr.h>
38ad2056f2SAlexander Leidinger #include <sys/lock.h>
39ad2056f2SAlexander Leidinger #include <sys/malloc.h>
40ad2056f2SAlexander Leidinger #include <sys/mutex.h>
41ad2056f2SAlexander Leidinger #include <sys/sx.h>
42ad2056f2SAlexander Leidinger #include <sys/proc.h>
43ad2056f2SAlexander Leidinger #include <sys/syscallsubr.h>
4495353459SDimitry Andric #include <sys/sysent.h>
45ad2056f2SAlexander Leidinger 
464732e446SRoman Divacky #include <compat/linux/linux_emul.h>
47d825ce0aSJohn Baldwin #include <compat/linux/linux_misc.h>
4881338031SDmitry Chagin #include <compat/linux/linux_util.h>
494732e446SRoman Divacky 
5019e252baSAlexander Leidinger 
5181338031SDmitry Chagin /*
52bc273677SDmitry Chagin  * This returns reference to the thread emuldata entry (if found)
5381338031SDmitry Chagin  *
5481338031SDmitry Chagin  * Hold PROC_LOCK when referencing emuldata from other threads.
5581338031SDmitry Chagin  */
56ad2056f2SAlexander Leidinger struct linux_emuldata *
5781338031SDmitry Chagin em_find(struct thread *td)
58ad2056f2SAlexander Leidinger {
59ad2056f2SAlexander Leidinger 	struct linux_emuldata *em;
60ad2056f2SAlexander Leidinger 
6181338031SDmitry Chagin 	em = td->td_emuldata;
62ad2056f2SAlexander Leidinger 
63ad2056f2SAlexander Leidinger 	return (em);
64ad2056f2SAlexander Leidinger }
65ad2056f2SAlexander Leidinger 
66bc273677SDmitry Chagin /*
67bc273677SDmitry Chagin  * This returns reference to the proc pemuldata entry (if found)
68bc273677SDmitry Chagin  *
69bc273677SDmitry Chagin  * Hold PROC_LOCK when referencing proc pemuldata from other threads.
70bc273677SDmitry Chagin  * Hold LINUX_PEM_LOCK wher referencing pemuldata members.
71bc273677SDmitry Chagin  */
72bc273677SDmitry Chagin struct linux_pemuldata *
73bc273677SDmitry Chagin pem_find(struct proc *p)
74bc273677SDmitry Chagin {
75bc273677SDmitry Chagin 	struct linux_pemuldata *pem;
76bc273677SDmitry Chagin 
77bc273677SDmitry Chagin 	pem = p->p_emuldata;
78bc273677SDmitry Chagin 
79bc273677SDmitry Chagin 	return (pem);
80bc273677SDmitry Chagin }
81bc273677SDmitry Chagin 
8281338031SDmitry Chagin void
8381338031SDmitry Chagin linux_proc_init(struct thread *td, struct thread *newtd, int flags)
84ad2056f2SAlexander Leidinger {
8581338031SDmitry Chagin 	struct linux_emuldata *em;
86bc273677SDmitry Chagin 	struct linux_pemuldata *pem;
87*e16fe1c7SDmitry Chagin 	struct epoll_emuldata *emd;
8819e252baSAlexander Leidinger 
8981338031SDmitry Chagin 	if (newtd != NULL) {
9081338031SDmitry Chagin 		/* non-exec call */
9181338031SDmitry Chagin 		em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO);
92955d762aSAlexander Leidinger 		em->pdeath_signal = 0;
934732e446SRoman Divacky 		em->robust_futexes = NULL;
94a4e3bad7SJung-uk Kim 		if (flags & LINUX_CLONE_THREAD) {
95*e16fe1c7SDmitry Chagin 			LINUX_CTR1(proc_init, "thread newtd(%d)",
96*e16fe1c7SDmitry Chagin 			    newtd->td_tid);
97*e16fe1c7SDmitry Chagin 
9881338031SDmitry Chagin 			em->em_tid = newtd->td_tid;
9981338031SDmitry Chagin 		} else {
100*e16fe1c7SDmitry Chagin 			LINUX_CTR1(proc_init, "fork newtd(%d)",
101*e16fe1c7SDmitry Chagin 			    newtd->td_proc->p_pid);
10219e252baSAlexander Leidinger 
10381338031SDmitry Chagin 			em->em_tid = newtd->td_proc->p_pid;
104bc273677SDmitry Chagin 
105e0d3ea8cSDmitry Chagin 			pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO);
106bc273677SDmitry Chagin 			sx_init(&pem->pem_sx, "lpemlk");
107bc273677SDmitry Chagin 			newtd->td_proc->p_emuldata = pem;
108ad2056f2SAlexander Leidinger 		}
10981338031SDmitry Chagin 		newtd->td_emuldata = em;
110ad2056f2SAlexander Leidinger 	} else {
11119e252baSAlexander Leidinger 		/* exec */
112*e16fe1c7SDmitry Chagin 		LINUX_CTR1(proc_init, "exec newtd(%d)",
113*e16fe1c7SDmitry Chagin 		    td->td_proc->p_pid);
11419e252baSAlexander Leidinger 
115ad2056f2SAlexander Leidinger 		/* lookup the old one */
11681338031SDmitry Chagin 		em = em_find(td);
117ad2056f2SAlexander Leidinger 		KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
11881338031SDmitry Chagin 
11981338031SDmitry Chagin 		em->em_tid = td->td_proc->p_pid;
120*e16fe1c7SDmitry Chagin 
121*e16fe1c7SDmitry Chagin 		 /* epoll should be destroyed in a case of exec. */
122*e16fe1c7SDmitry Chagin 		pem = pem_find(td->td_proc);
123*e16fe1c7SDmitry Chagin 		KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n"));
124*e16fe1c7SDmitry Chagin 
125*e16fe1c7SDmitry Chagin 		if (pem->epoll != NULL) {
126*e16fe1c7SDmitry Chagin 			emd = pem->epoll;
127*e16fe1c7SDmitry Chagin 			pem->epoll = NULL;
128*e16fe1c7SDmitry Chagin 			free(emd, M_EPOLL);
129*e16fe1c7SDmitry Chagin 		}
130ad2056f2SAlexander Leidinger 	}
131ad2056f2SAlexander Leidinger 
132ad2056f2SAlexander Leidinger 	em->child_clear_tid = NULL;
133ad2056f2SAlexander Leidinger 	em->child_set_tid = NULL;
134ad2056f2SAlexander Leidinger }
135ad2056f2SAlexander Leidinger 
136ad2056f2SAlexander Leidinger void
137ad2056f2SAlexander Leidinger linux_proc_exit(void *arg __unused, struct proc *p)
138ad2056f2SAlexander Leidinger {
139bc273677SDmitry Chagin 	struct linux_pemuldata *pem;
140*e16fe1c7SDmitry Chagin 	struct epoll_emuldata *emd;
14181338031SDmitry Chagin 	struct thread *td = curthread;
142ad2056f2SAlexander Leidinger 
143bc273677SDmitry Chagin 	if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX))
144bc273677SDmitry Chagin 		return;
145bc273677SDmitry Chagin 
146bc273677SDmitry Chagin 	pem = pem_find(p);
147bc273677SDmitry Chagin 	if (pem == NULL)
148bc273677SDmitry Chagin 		return;
14981338031SDmitry Chagin 	(p->p_sysent->sv_thread_detach)(td);
150bc273677SDmitry Chagin 
151bc273677SDmitry Chagin 	p->p_emuldata = NULL;
152bc273677SDmitry Chagin 
153*e16fe1c7SDmitry Chagin 	if (pem->epoll != NULL) {
154*e16fe1c7SDmitry Chagin 		emd = pem->epoll;
155*e16fe1c7SDmitry Chagin 		pem->epoll = NULL;
156*e16fe1c7SDmitry Chagin 		free(emd, M_EPOLL);
157*e16fe1c7SDmitry Chagin 	}
158*e16fe1c7SDmitry Chagin 
159bc273677SDmitry Chagin 	sx_destroy(&pem->pem_sx);
160e0d3ea8cSDmitry Chagin 	free(pem, M_LINUX);
161e8b8b834SAlexander Leidinger }
162ad2056f2SAlexander Leidinger 
16381338031SDmitry Chagin int
16481338031SDmitry Chagin linux_common_execve(struct thread *td, struct image_args *eargs)
16581338031SDmitry Chagin {
166bc273677SDmitry Chagin 	struct linux_pemuldata *pem;
167*e16fe1c7SDmitry Chagin 	struct epoll_emuldata *emd;
16881338031SDmitry Chagin 	struct linux_emuldata *em;
16981338031SDmitry Chagin 	struct proc *p;
17081338031SDmitry Chagin 	int error;
171ad2056f2SAlexander Leidinger 
17281338031SDmitry Chagin 	p = td->td_proc;
173ad2056f2SAlexander Leidinger 
17481338031SDmitry Chagin 	/*
17581338031SDmitry Chagin 	 * Unlike FreeBSD abort all other threads before
17681338031SDmitry Chagin 	 * proceeding exec.
17781338031SDmitry Chagin 	 */
17881338031SDmitry Chagin 	PROC_LOCK(p);
17981338031SDmitry Chagin 	/* See exit1() comments. */
18081338031SDmitry Chagin 	thread_suspend_check(0);
18181338031SDmitry Chagin 	while (p->p_flag & P_HADTHREADS) {
18281338031SDmitry Chagin 		if (!thread_single(p, SINGLE_EXIT))
18381338031SDmitry Chagin 			break;
18481338031SDmitry Chagin 		thread_suspend_check(0);
18581338031SDmitry Chagin 	}
18681338031SDmitry Chagin 	PROC_UNLOCK(p);
18781338031SDmitry Chagin 
18881338031SDmitry Chagin 	error = kern_execve(td, eargs, NULL);
18981338031SDmitry Chagin 	if (error != 0)
19081338031SDmitry Chagin 		return (error);
19181338031SDmitry Chagin 
19281338031SDmitry Chagin 	/*
19381338031SDmitry Chagin 	 * In a case of transition from Linux binary execing to
194bc273677SDmitry Chagin 	 * FreeBSD binary we destroy linux emuldata thread & proc entries.
19581338031SDmitry Chagin 	 */
19681338031SDmitry Chagin 	if (SV_CURPROC_ABI() != SV_ABI_LINUX) {
19781338031SDmitry Chagin 		PROC_LOCK(p);
19881338031SDmitry Chagin 		em = em_find(td);
199bc273677SDmitry Chagin 		KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n"));
20081338031SDmitry Chagin 		td->td_emuldata = NULL;
201bc273677SDmitry Chagin 
202bc273677SDmitry Chagin 		pem = pem_find(p);
203bc273677SDmitry Chagin 		KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n"));
204bc273677SDmitry Chagin 		p->p_emuldata = NULL;
20581338031SDmitry Chagin 		PROC_UNLOCK(p);
20681338031SDmitry Chagin 
207*e16fe1c7SDmitry Chagin 		if (pem->epoll != NULL) {
208*e16fe1c7SDmitry Chagin 			emd = pem->epoll;
209*e16fe1c7SDmitry Chagin 			pem->epoll = NULL;
210*e16fe1c7SDmitry Chagin 			free(emd, M_EPOLL);
211*e16fe1c7SDmitry Chagin 		}
212*e16fe1c7SDmitry Chagin 
21381338031SDmitry Chagin 		free(em, M_TEMP);
214e0d3ea8cSDmitry Chagin 		free(pem, M_LINUX);
21581338031SDmitry Chagin 	}
21681338031SDmitry Chagin 	return (0);
21781338031SDmitry Chagin }
21881338031SDmitry Chagin 
21981338031SDmitry Chagin void
22081338031SDmitry Chagin linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
22181338031SDmitry Chagin {
22281338031SDmitry Chagin 	struct thread *td = curthread;
22381338031SDmitry Chagin 
22481338031SDmitry Chagin 	/*
22581338031SDmitry Chagin 	 * In a case of execing to linux binary we create linux
22681338031SDmitry Chagin 	 * emuldata thread entry.
22781338031SDmitry Chagin 	 */
22881338031SDmitry Chagin 	if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) ==
22981338031SDmitry Chagin 	    SV_ABI_LINUX)) {
230*e16fe1c7SDmitry Chagin 
23181338031SDmitry Chagin 		if (SV_PROC_ABI(p) == SV_ABI_LINUX)
23281338031SDmitry Chagin 			linux_proc_init(td, NULL, 0);
23381338031SDmitry Chagin 		else
23481338031SDmitry Chagin 			linux_proc_init(td, td, 0);
23581338031SDmitry Chagin 	}
23681338031SDmitry Chagin }
23781338031SDmitry Chagin 
23881338031SDmitry Chagin void
23981338031SDmitry Chagin linux_thread_dtor(void *arg __unused, struct thread *td)
240ad2056f2SAlexander Leidinger {
241ad2056f2SAlexander Leidinger 	struct linux_emuldata *em;
242ad2056f2SAlexander Leidinger 
24381338031SDmitry Chagin 	em = em_find(td);
24481338031SDmitry Chagin 	if (em == NULL)
24581338031SDmitry Chagin 		return;
24681338031SDmitry Chagin 	td->td_emuldata = NULL;
247ad2056f2SAlexander Leidinger 
24881338031SDmitry Chagin 	LINUX_CTR1(exit, "thread dtor(%d)", em->em_tid);
249ad2056f2SAlexander Leidinger 
25081338031SDmitry Chagin 	free(em, M_TEMP);
251ad2056f2SAlexander Leidinger }
252ad2056f2SAlexander Leidinger 
253ad2056f2SAlexander Leidinger void
254e5d81ef1SDmitry Chagin linux_schedtail(struct thread *td)
255ad2056f2SAlexander Leidinger {
256ad2056f2SAlexander Leidinger 	struct linux_emuldata *em;
257e5d81ef1SDmitry Chagin 	struct proc *p;
258ad2056f2SAlexander Leidinger 	int error = 0;
259ad2056f2SAlexander Leidinger 	int *child_set_tid;
260ad2056f2SAlexander Leidinger 
261e5d81ef1SDmitry Chagin 	p = td->td_proc;
262e5d81ef1SDmitry Chagin 
26381338031SDmitry Chagin 	em = em_find(td);
264bc273677SDmitry Chagin 	KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n"));
265ad2056f2SAlexander Leidinger 	child_set_tid = em->child_set_tid;
266ad2056f2SAlexander Leidinger 
26719e252baSAlexander Leidinger 	if (child_set_tid != NULL) {
26881338031SDmitry Chagin 		error = copyout(&em->em_tid, (int *)child_set_tid,
26981338031SDmitry Chagin 		    sizeof(em->em_tid));
27081338031SDmitry Chagin 		LINUX_CTR4(clone, "schedtail(%d) %p stored %d error %d",
27181338031SDmitry Chagin 		    td->td_tid, child_set_tid, em->em_tid, error);
27281338031SDmitry Chagin 	} else
27381338031SDmitry Chagin 		LINUX_CTR1(clone, "schedtail(%d)", em->em_tid);
274bb63fddeSAlexander Leidinger }
275