1139743Simp/*-
214331Speter * Copyright (c) 1994 Christos Zoulas
314331Speter * Copyright (c) 1995 Frank van der Linden
414331Speter * Copyright (c) 1995 Scott Bartram
514331Speter * All rights reserved.
614331Speter *
714331Speter * Redistribution and use in source and binary forms, with or without
814331Speter * modification, are permitted provided that the following conditions
914331Speter * are met:
1014331Speter * 1. Redistributions of source code must retain the above copyright
1114331Speter *    notice, this list of conditions and the following disclaimer.
1214331Speter * 2. Redistributions in binary form must reproduce the above copyright
1314331Speter *    notice, this list of conditions and the following disclaimer in the
1414331Speter *    documentation and/or other materials provided with the distribution.
1514331Speter * 3. The name of the author may not be used to endorse or promote products
1614331Speter *    derived from this software without specific prior written permission
1714331Speter *
1814331Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1914331Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2014331Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2114331Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2214331Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2314331Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2414331Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2514331Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2614331Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2714331Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2814331Speter *
2914331Speter *	from: svr4_util.c,v 1.5 1995/01/22 23:44:50 christos Exp
3014331Speter */
3114331Speter
32116173Sobrien#include <sys/cdefs.h>
33116173Sobrien__FBSDID("$FreeBSD$");
34116173Sobrien
35161310Snetchild#include "opt_compat.h"
36246290Sdchagin#include "opt_kdtrace.h"
37161310Snetchild
3814331Speter#include <sys/param.h>
39158311Sambrisko#include <sys/bus.h>
40177785Skib#include <sys/fcntl.h>
41130959Sbde#include <sys/lock.h>
42130959Sbde#include <sys/malloc.h>
43246290Sdchagin#include <sys/kernel.h>
44158311Sambrisko#include <sys/linker_set.h>
45130959Sbde#include <sys/mutex.h>
4614331Speter#include <sys/namei.h>
4714331Speter#include <sys/proc.h>
48246290Sdchagin#include <sys/sdt.h>
49141472Sjhb#include <sys/syscallsubr.h>
50130959Sbde#include <sys/systm.h>
5114331Speter#include <sys/vnode.h>
5214331Speter
53108541Salfred#include <machine/stdarg.h>
54108541Salfred
5564913Smarcel#include <compat/linux/linux_util.h>
56161310Snetchild#ifdef COMPAT_LINUX32
57161310Snetchild#include <machine/../linux32/linux.h>
58161310Snetchild#else
59161310Snetchild#include <machine/../linux/linux.h>
60161310Snetchild#endif
6114331Speter
62246290Sdchagin#include <compat/linux/linux_dtrace.h>
63246290Sdchagin
6414331Speterconst char      linux_emul_path[] = "/compat/linux";
6514331Speter
66246290Sdchagin/* DTrace init */
67246290SdchaginLIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE);
68246290Sdchagin
69246290Sdchagin/**
70246290Sdchagin * DTrace probes in this module.
71246290Sdchagin */
72246290SdchaginLIN_SDT_PROBE_DEFINE5(util, linux_emul_convpath, entry, "const char *",
73246290Sdchagin    "enum uio_seg", "char **", "int", "int");
74246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_emul_convpath, return, "int");
75246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_msg, entry, "const char *");
76246290SdchaginLIN_SDT_PROBE_DEFINE0(util, linux_msg, return);
77246290SdchaginLIN_SDT_PROBE_DEFINE2(util, linux_driver_get_name_dev, entry, "device_t",
78246290Sdchagin    "const char *");
79246290SdchaginLIN_SDT_PROBE_DEFINE0(util, linux_driver_get_name_dev, nullcall);
80246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_driver_get_name_dev, return, "char *");
81246290SdchaginLIN_SDT_PROBE_DEFINE3(util, linux_driver_get_major_minor, entry, "char *",
82246290Sdchagin    "int *", "int *");
83246290SdchaginLIN_SDT_PROBE_DEFINE0(util, linux_driver_get_major_minor, nullcall);
84246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_driver_get_major_minor, notfound, "char *");
85246290SdchaginLIN_SDT_PROBE_DEFINE3(util, linux_driver_get_major_minor, return, "int",
86246290Sdchagin    "int", "int");
87246290SdchaginLIN_SDT_PROBE_DEFINE0(util, linux_get_char_devices, entry);
88246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_get_char_devices, return, "char *");
89246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_free_get_char_devices, entry, "char *");
90246290SdchaginLIN_SDT_PROBE_DEFINE0(util, linux_free_get_char_devices, return);
91246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_device_register_handler, entry,
92246290Sdchagin    "struct linux_device_handler *");
93246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_device_register_handler, return, "int");
94246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_device_unregister_handler, entry,
95246290Sdchagin    "struct linux_device_handler *");
96246290SdchaginLIN_SDT_PROBE_DEFINE1(util, linux_device_unregister_handler, return, "int");
97246290Sdchagin
9814331Speter/*
99166944Snetchild * Search an alternate path before passing pathname arguments on to
100166944Snetchild * system calls. Useful for keeping a separate 'emulation tree'.
10114331Speter *
102166944Snetchild * If cflag is set, we check if an attempt can be made to create the
103166944Snetchild * named file, i.e. we check if the directory it should be in exists.
10414331Speter */
10514331Speterint
106246290Sdchaginlinux_emul_convpath(struct thread *td, const char *path, enum uio_seg pathseg,
107246290Sdchagin    char **pbuf, int cflag, int dfd)
108102803Siedowse{
109246290Sdchagin	int retval;
11014331Speter
111246290Sdchagin	LIN_SDT_PROBE5(util, linux_emul_convpath, entry, path, pathseg, pbuf,
112246290Sdchagin	    cflag, dfd);
113246290Sdchagin
114246290Sdchagin	retval = kern_alternate_path(td, linux_emul_path, path, pathseg, pbuf,
115246290Sdchagin	    cflag, dfd);
116246290Sdchagin
117246290Sdchagin	LIN_SDT_PROBE1(util, linux_emul_convpath, return, retval);
118246290Sdchagin	return (retval);
11914331Speter}
120108541Salfred
121108541Salfredvoid
122108541Salfredlinux_msg(const struct thread *td, const char *fmt, ...)
123108541Salfred{
124108541Salfred	va_list ap;
125108541Salfred	struct proc *p;
126108541Salfred
127246290Sdchagin	LIN_SDT_PROBE1(util, linux_msg, entry, fmt);
128246290Sdchagin
129108541Salfred	p = td->td_proc;
130108541Salfred	printf("linux: pid %d (%s): ", (int)p->p_pid, p->p_comm);
131108541Salfred	va_start(ap, fmt);
132108541Salfred	vprintf(fmt, ap);
133108541Salfred	va_end(ap);
134108541Salfred	printf("\n");
135246290Sdchagin
136246290Sdchagin	LIN_SDT_PROBE0(util, linux_msg, return);
137108541Salfred}
138158311Sambrisko
139158311Sambriskostruct device_element
140158311Sambrisko{
141158311Sambrisko	TAILQ_ENTRY(device_element) list;
142158311Sambrisko	struct linux_device_handler entry;
143158311Sambrisko};
144158311Sambrisko
145158311Sambriskostatic TAILQ_HEAD(, device_element) devices =
146158311Sambrisko	TAILQ_HEAD_INITIALIZER(devices);
147158311Sambrisko
148158311Sambriskostatic struct linux_device_handler null_handler =
149158311Sambrisko	{ "mem", "mem", "null", "null", 1, 3, 1};
150158311Sambrisko
151158311SambriskoDATA_SET(linux_device_handler_set, null_handler);
152158311Sambrisko
153158311Sambriskochar *
154158311Sambriskolinux_driver_get_name_dev(device_t dev)
155158311Sambrisko{
156158311Sambrisko	struct device_element *de;
157158311Sambrisko	const char *device_name = device_get_name(dev);
158158311Sambrisko
159246290Sdchagin	LIN_SDT_PROBE2(util, linux_driver_get_name_dev, entry, dev,
160246290Sdchagin	    device_name);
161246290Sdchagin
162246290Sdchagin	if (device_name == NULL) {
163246290Sdchagin		LIN_SDT_PROBE0(util, linux_driver_get_name_dev, nullcall);
164246290Sdchagin		LIN_SDT_PROBE1(util, linux_driver_get_name_dev, return, NULL);
165158311Sambrisko		return NULL;
166246290Sdchagin	}
167158311Sambrisko	TAILQ_FOREACH(de, &devices, list) {
168246290Sdchagin		if (strcmp(device_name, de->entry.bsd_driver_name) == 0) {
169246290Sdchagin			LIN_SDT_PROBE1(util, linux_driver_get_name_dev, return,
170246290Sdchagin			    de->entry.linux_driver_name);
171158311Sambrisko			return (de->entry.linux_driver_name);
172246290Sdchagin		}
173158311Sambrisko	}
174158311Sambrisko
175246290Sdchagin	LIN_SDT_PROBE1(util, linux_driver_get_name_dev, return, NULL);
176158311Sambrisko	return NULL;
177158311Sambrisko}
178158311Sambrisko
179158311Sambriskoint
180232405Sedlinux_driver_get_major_minor(const char *node, int *major, int *minor)
181158311Sambrisko{
182158311Sambrisko	struct device_element *de;
183158311Sambrisko
184246290Sdchagin	LIN_SDT_PROBE3(util, linux_driver_get_major_minor, entry, node, major,
185246290Sdchagin	    minor);
186246290Sdchagin
187246290Sdchagin	if (node == NULL || major == NULL || minor == NULL) {
188246290Sdchagin		LIN_SDT_PROBE0(util, linux_driver_get_major_minor, nullcall);
189246290Sdchagin		LIN_SDT_PROBE3(util, linux_driver_get_major_minor, return, 1,
190246290Sdchagin		   0, 0);
191158311Sambrisko		return 1;
192246290Sdchagin	}
193179486Sed
194179486Sed	if (strlen(node) > strlen("pts/") &&
195179486Sed	    strncmp(node, "pts/", strlen("pts/")) == 0) {
196179486Sed		unsigned long devno;
197179486Sed
198179486Sed		/*
199179486Sed		 * Linux checks major and minors of the slave device
200179486Sed		 * to make sure it's a pty device, so let's make him
201179486Sed		 * believe it is.
202179486Sed		 */
203179486Sed		devno = strtoul(node + strlen("pts/"), NULL, 10);
204179486Sed		*major = 136 + (devno / 256);
205179486Sed		*minor = devno % 256;
206246290Sdchagin
207246290Sdchagin		LIN_SDT_PROBE3(util, linux_driver_get_major_minor, return, 0,
208246290Sdchagin		    *major, *minor);
209179486Sed		return 0;
210179486Sed	}
211179486Sed
212158311Sambrisko	TAILQ_FOREACH(de, &devices, list) {
213158311Sambrisko		if (strcmp(node, de->entry.bsd_device_name) == 0) {
214158311Sambrisko			*major = de->entry.linux_major;
215158311Sambrisko			*minor = de->entry.linux_minor;
216246290Sdchagin
217246290Sdchagin			LIN_SDT_PROBE3(util, linux_driver_get_major_minor,
218246290Sdchagin			    return, 0, *major, *minor);
219158311Sambrisko			return 0;
220158311Sambrisko		}
221158311Sambrisko	}
222158311Sambrisko
223246290Sdchagin	LIN_SDT_PROBE1(util, linux_driver_get_major_minor, notfound, node);
224246290Sdchagin	LIN_SDT_PROBE3(util, linux_driver_get_major_minor, return, 1, 0, 0);
225158311Sambrisko	return 1;
226158311Sambrisko}
227158311Sambrisko
228158311Sambriskochar *
229158311Sambriskolinux_get_char_devices()
230158311Sambrisko{
231158311Sambrisko	struct device_element *de;
232158311Sambrisko	char *temp, *string, *last;
233158311Sambrisko	char formated[256];
234158311Sambrisko	int current_size = 0, string_size = 1024;
235158311Sambrisko
236246290Sdchagin	LIN_SDT_PROBE0(util, linux_get_char_devices, entry);
237246290Sdchagin
238184205Sdes	string = malloc(string_size, M_LINUX, M_WAITOK);
239158311Sambrisko	string[0] = '\000';
240158311Sambrisko	last = "";
241158311Sambrisko	TAILQ_FOREACH(de, &devices, list) {
242158311Sambrisko		if (!de->entry.linux_char_device)
243158311Sambrisko			continue;
244158311Sambrisko		temp = string;
245158311Sambrisko		if (strcmp(last, de->entry.bsd_driver_name) != 0) {
246158311Sambrisko			last = de->entry.bsd_driver_name;
247158311Sambrisko
248158311Sambrisko			snprintf(formated, sizeof(formated), "%3d %s\n",
249158311Sambrisko				 de->entry.linux_major,
250158311Sambrisko				 de->entry.linux_device_name);
251158311Sambrisko			if (strlen(formated) + current_size
252158311Sambrisko			    >= string_size) {
253158311Sambrisko				string_size *= 2;
254184205Sdes				string = malloc(string_size,
255158311Sambrisko				    M_LINUX, M_WAITOK);
256158311Sambrisko				bcopy(temp, string, current_size);
257184205Sdes				free(temp, M_LINUX);
258158311Sambrisko			}
259158311Sambrisko			strcat(string, formated);
260158311Sambrisko			current_size = strlen(string);
261158311Sambrisko		}
262158311Sambrisko	}
263158311Sambrisko
264246290Sdchagin	LIN_SDT_PROBE1(util, linux_get_char_devices, return, string);
265158311Sambrisko	return string;
266158311Sambrisko}
267158311Sambrisko
268158311Sambriskovoid
269158311Sambriskolinux_free_get_char_devices(char *string)
270158311Sambrisko{
271246290Sdchagin
272246290Sdchagin	LIN_SDT_PROBE1(util, linux_get_char_devices, entry, string);
273246290Sdchagin
274184205Sdes	free(string, M_LINUX);
275246290Sdchagin
276246290Sdchagin	LIN_SDT_PROBE0(util, linux_get_char_devices, return);
277158311Sambrisko}
278158311Sambrisko
279158311Sambriskostatic int linux_major_starting = 200;
280158311Sambrisko
281158311Sambriskoint
282158311Sambriskolinux_device_register_handler(struct linux_device_handler *d)
283158311Sambrisko{
284158311Sambrisko	struct device_element *de;
285158311Sambrisko
286246290Sdchagin	LIN_SDT_PROBE1(util, linux_device_register_handler, entry, d);
287246290Sdchagin
288246290Sdchagin	if (d == NULL) {
289246290Sdchagin		LIN_SDT_PROBE1(util, linux_device_register_handler, return,
290246290Sdchagin		    EINVAL);
291158311Sambrisko		return (EINVAL);
292246290Sdchagin	}
293158311Sambrisko
294246290Sdchagin	de = malloc(sizeof(*de), M_LINUX, M_WAITOK);
295158311Sambrisko	if (d->linux_major < 0) {
296158311Sambrisko		d->linux_major = linux_major_starting++;
297158311Sambrisko	}
298158311Sambrisko	bcopy(d, &de->entry, sizeof(*d));
299158311Sambrisko
300158311Sambrisko	/* Add the element to the list, sorted on span. */
301158311Sambrisko	TAILQ_INSERT_TAIL(&devices, de, list);
302158311Sambrisko
303246290Sdchagin	LIN_SDT_PROBE1(util, linux_device_register_handler, return, 0);
304158311Sambrisko	return (0);
305158311Sambrisko}
306158311Sambrisko
307158311Sambriskoint
308158311Sambriskolinux_device_unregister_handler(struct linux_device_handler *d)
309158311Sambrisko{
310158311Sambrisko	struct device_element *de;
311158311Sambrisko
312246290Sdchagin	LIN_SDT_PROBE1(util, linux_device_unregister_handler, entry, d);
313246290Sdchagin
314246290Sdchagin	if (d == NULL) {
315246290Sdchagin		LIN_SDT_PROBE1(util, linux_device_unregister_handler, return,
316246290Sdchagin		    EINVAL);
317158311Sambrisko		return (EINVAL);
318246290Sdchagin	}
319158311Sambrisko
320158311Sambrisko	TAILQ_FOREACH(de, &devices, list) {
321158311Sambrisko		if (bcmp(d, &de->entry, sizeof(*d)) == 0) {
322158311Sambrisko			TAILQ_REMOVE(&devices, de, list);
323184205Sdes			free(de, M_LINUX);
324246290Sdchagin
325246290Sdchagin			LIN_SDT_PROBE1(util, linux_device_unregister_handler,
326246290Sdchagin			    return, 0);
327158311Sambrisko			return (0);
328158311Sambrisko		}
329158311Sambrisko	}
330158311Sambrisko
331246290Sdchagin	LIN_SDT_PROBE1(util, linux_device_unregister_handler, return, EINVAL);
332158311Sambrisko	return (EINVAL);
333158311Sambrisko}
334