linux_emul.c revision 293600
155714Skris/*-
255714Skris * Copyright (c) 2006 Roman Divacky
355714Skris * Copyright (c) 2013 Dmitry Chagin
455714Skris * All rights reserved.
555714Skris *
655714Skris * Redistribution and use in source and binary forms, with or without
755714Skris * modification, are permitted provided that the following conditions
855714Skris * are met:
9238405Sjkim * 1. Redistributions of source code must retain the above copyright
1055714Skris *    notice, this list of conditions and the following disclaimer
1155714Skris *    in this position and unchanged.
1259191Skris * 2. Redistributions in binary form must reproduce the above copyright
1355714Skris *    notice, this list of conditions and the following disclaimer in the
14238405Sjkim *    documentation and/or other materials provided with the distribution.
15160814Ssimon * 3. The name of the author may not be used to endorse or promote products
16238405Sjkim *    derived from this software without specific prior written permission
17160814Ssimon *
18160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19238405Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20160814Ssimon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21238405Sjkim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22160814Ssimon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23238405Sjkim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24194206Ssimon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25238405Sjkim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26238405Sjkim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27238405Sjkim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28238405Sjkim */
29238405Sjkim
30238405Sjkim#include <sys/cdefs.h>
31238405Sjkim__FBSDID("$FreeBSD: stable/10/sys/compat/linux/linux_emul.c 293600 2016-01-09 18:07:48Z dchagin $");
32238405Sjkim
33238405Sjkim#include <sys/param.h>
34238405Sjkim#include <sys/systm.h>
35238405Sjkim#include <sys/imgact.h>
36238405Sjkim#include <sys/kernel.h>
37238405Sjkim#include <sys/ktr.h>
38238405Sjkim#include <sys/lock.h>
39238405Sjkim#include <sys/malloc.h>
40238405Sjkim#include <sys/mutex.h>
41238405Sjkim#include <sys/sx.h>
42238405Sjkim#include <sys/proc.h>
43238405Sjkim#include <sys/syscallsubr.h>
44238405Sjkim#include <sys/sysent.h>
45238405Sjkim
46238405Sjkim#include <compat/linux/linux_emul.h>
47238405Sjkim#include <compat/linux/linux_misc.h>
48238405Sjkim#include <compat/linux/linux_util.h>
49194206Ssimon
50194206Ssimon
51142425Snectar/*
5255714Skris * This returns reference to the thread emuldata entry (if found)
53238405Sjkim *
54238405Sjkim * Hold PROC_LOCK when referencing emuldata from other threads.
55238405Sjkim */
56238405Sjkimstruct linux_emuldata *
57238405Sjkimem_find(struct thread *td)
58238405Sjkim{
5955714Skris	struct linux_emuldata *em;
6055714Skris
6155714Skris	em = td->td_emuldata;
62238405Sjkim
63238405Sjkim	return (em);
64142425Snectar}
6555714Skris
6655714Skris/*
6755714Skris * This returns reference to the proc pemuldata entry (if found)
6855714Skris *
6955714Skris * Hold PROC_LOCK when referencing proc pemuldata from other threads.
70160814Ssimon * Hold LINUX_PEM_LOCK wher referencing pemuldata members.
71160814Ssimon */
72109998Smarkmstruct linux_pemuldata *
7355714Skrispem_find(struct proc *p)
7455714Skris{
7555714Skris	struct linux_pemuldata *pem;
7655714Skris
7755714Skris	pem = p->p_emuldata;
7855714Skris
7955714Skris	return (pem);
80109998Smarkm}
81160814Ssimon
82194206Ssimonvoid
83160814Ssimonlinux_proc_init(struct thread *td, struct thread *newtd, int flags)
84160814Ssimon{
8555714Skris	struct linux_emuldata *em;
86238405Sjkim	struct linux_pemuldata *pem;
8755714Skris	struct epoll_emuldata *emd;
8855714Skris	struct proc *p;
8955714Skris
90160814Ssimon	if (newtd != NULL) {
9155714Skris		p = newtd->td_proc;
9255714Skris
9355714Skris		/* non-exec call */
9455714Skris		em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO);
9555714Skris		if (flags & LINUX_CLONE_THREAD) {
9655714Skris			LINUX_CTR1(proc_init, "thread newtd(%d)",
9755714Skris			    newtd->td_tid);
9855714Skris
9955714Skris			em->em_tid = newtd->td_tid;
10055714Skris		} else {
10155714Skris			LINUX_CTR1(proc_init, "fork newtd(%d)", p->p_pid);
10268651Skris
10368651Skris			em->em_tid = p->p_pid;
104109998Smarkm
105194206Ssimon			pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO);
10655714Skris			sx_init(&pem->pem_sx, "lpemlk");
10755714Skris			p->p_emuldata = pem;
10855714Skris		}
10955714Skris		newtd->td_emuldata = em;
110109998Smarkm	} else {
111238405Sjkim		p = td->td_proc;
112109998Smarkm
113160814Ssimon		/* exec */
114160814Ssimon		LINUX_CTR1(proc_init, "exec newtd(%d)", p->p_pid);
115111147Snectar
116111147Snectar		/* lookup the old one */
11755714Skris		em = em_find(td);
118160814Ssimon		KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
119194206Ssimon
12059191Skris		em->em_tid = p->p_pid;
12155714Skris		em->flags = 0;
12255714Skris		em->pdeath_signal = 0;
12355714Skris		em->robust_futexes = NULL;
12455714Skris		em->child_clear_tid = NULL;
12568651Skris		em->child_set_tid = NULL;
12655714Skris
12755714Skris		 /* epoll should be destroyed in a case of exec. */
12855714Skris		pem = pem_find(p);
12955714Skris		KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n"));
13055714Skris
13155714Skris		if (pem->epoll != NULL) {
13255714Skris			emd = pem->epoll;
13355714Skris			pem->epoll = NULL;
13455714Skris			free(emd, M_EPOLL);
13555714Skris		}
13655714Skris	}
13755714Skris
13855714Skris}
139109998Smarkm
14055714Skrisvoid
14155714Skrislinux_proc_exit(void *arg __unused, struct proc *p)
14255714Skris{
14355714Skris	struct linux_pemuldata *pem;
144160814Ssimon	struct epoll_emuldata *emd;
145160814Ssimon	struct thread *td = curthread;
14655714Skris
14755714Skris	if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX))
14855714Skris		return;
14955714Skris
15055714Skris	LINUX_CTR3(proc_exit, "thread(%d) proc(%d) p %p",
151109998Smarkm	    td->td_tid, p->p_pid, p);
15255714Skris
15355714Skris	pem = pem_find(p);
15455714Skris	if (pem == NULL)
15555714Skris		return;
15655714Skris	(p->p_sysent->sv_thread_detach)(td);
15755714Skris
158160814Ssimon	p->p_emuldata = NULL;
159160814Ssimon
16055714Skris	if (pem->epoll != NULL) {
16155714Skris		emd = pem->epoll;
16255714Skris		pem->epoll = NULL;
16355714Skris		free(emd, M_EPOLL);
16455714Skris	}
16555714Skris
16655714Skris	sx_destroy(&pem->pem_sx);
16755714Skris	free(pem, M_LINUX);
16855714Skris}
16955714Skris
170160814Ssimonint
17155714Skrislinux_common_execve(struct thread *td, struct image_args *eargs)
17255714Skris{
17355714Skris	struct linux_pemuldata *pem;
174238405Sjkim	struct epoll_emuldata *emd;
175238405Sjkim	struct vmspace *oldvmspace;
176238405Sjkim	struct linux_emuldata *em;
177238405Sjkim	struct proc *p;
178238405Sjkim	int error;
179238405Sjkim
180160814Ssimon	p = td->td_proc;
18155714Skris
182160814Ssimon	error = pre_execve(td, &oldvmspace);
183160814Ssimon	if (error != 0)
18455714Skris		return (error);
18555714Skris
18655714Skris	error = kern_execve(td, eargs, NULL);
18755714Skris	post_execve(td, error, oldvmspace);
18855714Skris	if (error != 0)
18955714Skris		return (error);
19055714Skris
19155714Skris	/*
19255714Skris	 * In a case of transition from Linux binary execing to
19355714Skris	 * FreeBSD binary we destroy linux emuldata thread & proc entries.
19455714Skris	 */
19555714Skris	if (SV_CURPROC_ABI() != SV_ABI_LINUX) {
19655714Skris		PROC_LOCK(p);
19755714Skris		em = em_find(td);
19855714Skris		KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n"));
19955714Skris		td->td_emuldata = NULL;
20055714Skris
20155714Skris		pem = pem_find(p);
20255714Skris		KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n"));
20355714Skris		p->p_emuldata = NULL;
20455714Skris		PROC_UNLOCK(p);
20555714Skris
20655714Skris		if (pem->epoll != NULL) {
20755714Skris			emd = pem->epoll;
20855714Skris			pem->epoll = NULL;
20955714Skris			free(emd, M_EPOLL);
21055714Skris		}
21155714Skris
21255714Skris		free(em, M_TEMP);
21355714Skris		free(pem, M_LINUX);
21455714Skris	}
21555714Skris	return (0);
216109998Smarkm}
217109998Smarkm
218109998Smarkmvoid
219109998Smarkmlinux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
220109998Smarkm{
221160814Ssimon	struct thread *td = curthread;
222194206Ssimon
223160814Ssimon	/*
224160814Ssimon	 * In a case of execing to linux binary we create linux
225194206Ssimon	 * emuldata thread entry.
226160814Ssimon	 */
227160814Ssimon	if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) ==
22855714Skris	    SV_ABI_LINUX)) {
22955714Skris
23055714Skris		if (SV_PROC_ABI(p) == SV_ABI_LINUX)
23155714Skris			linux_proc_init(td, NULL, 0);
23255714Skris		else
23355714Skris			linux_proc_init(td, td, 0);
23455714Skris	}
23555714Skris}
236238405Sjkim
237238405Sjkimvoid
23855714Skrislinux_thread_dtor(void *arg __unused, struct thread *td)
23955714Skris{
24055714Skris	struct linux_emuldata *em;
24155714Skris
24255714Skris	em = em_find(td);
24355714Skris	if (em == NULL)
244160814Ssimon		return;
245160814Ssimon	td->td_emuldata = NULL;
246109998Smarkm
247109998Smarkm	LINUX_CTR1(thread_dtor, "thread(%d)", em->em_tid);
248162911Ssimon
249194206Ssimon	free(em, M_TEMP);
250109998Smarkm}
251109998Smarkm
252109998Smarkmvoid
253109998Smarkmlinux_schedtail(struct thread *td)
254109998Smarkm{
255109998Smarkm	struct linux_emuldata *em;
256109998Smarkm	struct proc *p;
257109998Smarkm	int error = 0;
258111147Snectar	int *child_set_tid;
259109998Smarkm
260160814Ssimon	p = td->td_proc;
261109998Smarkm
262109998Smarkm	em = em_find(td);
263109998Smarkm	KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n"));
264109998Smarkm	child_set_tid = em->child_set_tid;
265109998Smarkm
266238405Sjkim	if (child_set_tid != NULL) {
267109998Smarkm		error = copyout(&em->em_tid, child_set_tid,
268109998Smarkm		    sizeof(em->em_tid));
269109998Smarkm		LINUX_CTR4(schedtail, "thread(%d) %p stored %d error %d",
270194206Ssimon		    td->td_tid, child_set_tid, em->em_tid, error);
271238405Sjkim	} else
272194206Ssimon		LINUX_CTR1(schedtail, "thread(%d)", em->em_tid);
273109998Smarkm}
274109998Smarkm