]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linux/linux_util.c
Import device-tree files from Linux 6.2
[FreeBSD/FreeBSD.git] / sys / compat / linux / linux_util.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1994 Christos Zoulas
5  * Copyright (c) 1995 Frank van der Linden
6  * Copyright (c) 1995 Scott Bartram
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  *      from: svr4_util.c,v 1.5 1995/01/22 23:44:50 christos Exp
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/bus.h>
40 #include <sys/conf.h>
41 #include <sys/fcntl.h>
42 #include <sys/jail.h>
43 #include <sys/malloc.h>
44 #include <sys/namei.h>
45 #include <sys/proc.h>
46 #include <sys/stat.h>
47 #include <sys/syscallsubr.h>
48 #include <sys/vnode.h>
49
50 #include <machine/stdarg.h>
51
52 #include <compat/linux/linux_dtrace.h>
53 #include <compat/linux/linux_mib.h>
54 #include <compat/linux/linux_util.h>
55
56 MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures");
57 MALLOC_DEFINE(M_EPOLL, "lepoll", "Linux events structures");
58
59 FEATURE(linuxulator_v4l, "V4L ioctl wrapper support in the linuxulator");
60 FEATURE(linuxulator_v4l2, "V4L2 ioctl wrapper support in the linuxulator");
61
62 /**
63  * Special DTrace provider for the linuxulator.
64  *
65  * In this file we define the provider for the entire linuxulator. All
66  * modules (= files of the linuxulator) use it.
67  *
68  * We define a different name depending on the emulated bitsize, see
69  * ../../<ARCH>/linux{,32}/linux.h, e.g.:
70  *      native bitsize          = linuxulator
71  *      amd64, 32bit emulation  = linuxulator32
72  */
73 LIN_SDT_PROVIDER_DEFINE(linuxulator);
74 LIN_SDT_PROVIDER_DEFINE(linuxulator32);
75
76 char linux_emul_path[MAXPATHLEN] = "/compat/linux";
77
78 SYSCTL_STRING(_compat_linux, OID_AUTO, emul_path, CTLFLAG_RWTUN,
79     linux_emul_path, sizeof(linux_emul_path),
80     "Linux runtime environment path");
81
82 int
83 linux_pwd_onexec(struct thread *td)
84 {
85         struct nameidata nd;
86         struct pwd *pwd;
87         int error;
88
89         NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path);
90         error = namei(&nd);
91         if (error != 0) {
92                 /*
93                  * Do not bother if we are in chroot or jail.
94                  */
95                 pwd = pwd_hold(td);
96                 if (pwd->pwd_rdir != rootvnode) {
97                         pwd_drop(pwd);
98                         return (0);
99                 }
100                 pwd_drop(pwd);
101                 return (error);
102         }
103         NDFREE_PNBUF(&nd);
104         pwd_altroot(td, nd.ni_vp);
105         vrele(nd.ni_vp);
106         return (0);
107 }
108
109 void
110 linux_pwd_onexec_native(struct thread *td)
111 {
112
113         pwd_altroot(td, NULL);
114 }
115
116 void
117 linux_msg(const struct thread *td, const char *fmt, ...)
118 {
119         va_list ap;
120         struct proc *p;
121
122         if (linux_debug == 0)
123                 return;
124
125         p = td->td_proc;
126         printf("linux: jid %d pid %d (%s): ", p->p_ucred->cr_prison->pr_id,
127             (int)p->p_pid, p->p_comm);
128         va_start(ap, fmt);
129         vprintf(fmt, ap);
130         va_end(ap);
131         printf("\n");
132 }
133
134 struct device_element
135 {
136         TAILQ_ENTRY(device_element) list;
137         struct linux_device_handler entry;
138 };
139
140 static TAILQ_HEAD(, device_element) devices =
141         TAILQ_HEAD_INITIALIZER(devices);
142
143 static struct linux_device_handler null_handler =
144         { "mem", "mem", "null", "null", 1, 3, 1};
145
146 DATA_SET(linux_device_handler_set, null_handler);
147
148 char *
149 linux_driver_get_name_dev(device_t dev)
150 {
151         struct device_element *de;
152         const char *device_name = device_get_name(dev);
153
154         if (device_name == NULL)
155                 return (NULL);
156         TAILQ_FOREACH(de, &devices, list) {
157                 if (strcmp(device_name, de->entry.bsd_driver_name) == 0)
158                         return (de->entry.linux_driver_name);
159         }
160
161         return (NULL);
162 }
163
164 int
165 linux_driver_get_major_minor(const char *node, int *major, int *minor)
166 {
167         struct device_element *de;
168         unsigned long devno;
169         size_t sz;
170
171         if (node == NULL || major == NULL || minor == NULL)
172                 return (1);
173
174         sz = sizeof("pts/") - 1;
175         if (strncmp(node, "pts/", sz) == 0 && node[sz] != '\0') {
176                 /*
177                  * Linux checks major and minors of the slave device
178                  * to make sure it's a pty device, so let's make him
179                  * believe it is.
180                  */
181                 devno = strtoul(node + sz, NULL, 10);
182                 *major = 136 + (devno / 256);
183                 *minor = devno % 256;
184                 return (0);
185         }
186
187         sz = sizeof("dri/card") - 1;
188         if (strncmp(node, "dri/card", sz) == 0 && node[sz] != '\0') {
189                 devno = strtoul(node + sz, NULL, 10);
190                 *major = 226 + (devno / 256);
191                 *minor = devno % 256;
192                 return (0);
193         }
194         sz = sizeof("dri/controlD") - 1;
195         if (strncmp(node, "dri/controlD", sz) == 0 && node[sz] != '\0') {
196                 devno = strtoul(node + sz, NULL, 10);
197                 *major = 226 + (devno / 256);
198                 *minor = devno % 256;
199                 return (0);
200         }
201         sz = sizeof("dri/renderD") - 1;
202         if (strncmp(node, "dri/renderD", sz) == 0 && node[sz] != '\0') {
203                 devno = strtoul(node + sz, NULL, 10);
204                 *major = 226 + (devno / 256);
205                 *minor = devno % 256;
206                 return (0);
207         }
208         sz = sizeof("drm/") - 1;
209         if (strncmp(node, "drm/", sz) == 0 && node[sz] != '\0') {
210                 devno = strtoul(node + sz, NULL, 10);
211                 *major = 226 + (devno / 256);
212                 *minor = devno % 256;
213                 return (0);
214         }
215
216         TAILQ_FOREACH(de, &devices, list) {
217                 if (strcmp(node, de->entry.bsd_device_name) == 0) {
218                         *major = de->entry.linux_major;
219                         *minor = de->entry.linux_minor;
220                         return (0);
221                 }
222         }
223
224         return (1);
225 }
226
227 int
228 linux_vn_get_major_minor(const struct vnode *vp, int *major, int *minor)
229 {
230         int error;
231
232         if (vp->v_type != VCHR)
233                 return (ENOTBLK);
234         dev_lock();
235         if (vp->v_rdev == NULL) {
236                 dev_unlock();
237                 return (ENXIO);
238         }
239         error = linux_driver_get_major_minor(devtoname(vp->v_rdev),
240             major, minor);
241         dev_unlock();
242         return (error);
243 }
244
245 void
246 translate_vnhook_major_minor(struct vnode *vp, struct stat *sb)
247 {
248         int major, minor;
249
250         if (vn_isdisk(vp)) {
251                 sb->st_mode &= ~S_IFMT;
252                 sb->st_mode |= S_IFBLK;
253         }
254
255         /*
256          * Return the same st_dev for every devfs instance.  The reason
257          * for this is to work around an idiosyncrasy of glibc getttynam()
258          * implementation: it checks whether st_dev returned for fd 0
259          * is the same as st_dev returned for the target of /proc/self/fd/0
260          * symlink, and with linux chroots having their own devfs instance,
261          * the check will fail if you chroot into it.
262          */
263         if (rootdevmp != NULL && vp->v_mount->mnt_vfc == rootdevmp->mnt_vfc)
264                 sb->st_dev = rootdevmp->mnt_stat.f_fsid.val[0];
265
266         if (linux_vn_get_major_minor(vp, &major, &minor) == 0)
267                 sb->st_rdev = makedev(major, minor);
268 }
269
270 char *
271 linux_get_char_devices(void)
272 {
273         struct device_element *de;
274         char *temp, *string, *last;
275         char formated[256];
276         int current_size = 0, string_size = 1024;
277
278         string = malloc(string_size, M_LINUX, M_WAITOK);
279         string[0] = '\000';
280         last = "";
281         TAILQ_FOREACH(de, &devices, list) {
282                 if (!de->entry.linux_char_device)
283                         continue;
284                 temp = string;
285                 if (strcmp(last, de->entry.bsd_driver_name) != 0) {
286                         last = de->entry.bsd_driver_name;
287
288                         snprintf(formated, sizeof(formated), "%3d %s\n",
289                                  de->entry.linux_major,
290                                  de->entry.linux_device_name);
291                         if (strlen(formated) + current_size
292                             >= string_size) {
293                                 string_size *= 2;
294                                 string = malloc(string_size,
295                                     M_LINUX, M_WAITOK);
296                                 bcopy(temp, string, current_size);
297                                 free(temp, M_LINUX);
298                         }
299                         strcat(string, formated);
300                         current_size = strlen(string);
301                 }
302         }
303
304         return (string);
305 }
306
307 void
308 linux_free_get_char_devices(char *string)
309 {
310
311         free(string, M_LINUX);
312 }
313
314 static int linux_major_starting = 200;
315
316 int
317 linux_device_register_handler(struct linux_device_handler *d)
318 {
319         struct device_element *de;
320
321         if (d == NULL)
322                 return (EINVAL);
323
324         de = malloc(sizeof(*de), M_LINUX, M_WAITOK);
325         if (d->linux_major < 0) {
326                 d->linux_major = linux_major_starting++;
327         }
328         bcopy(d, &de->entry, sizeof(*d));
329
330         /* Add the element to the list, sorted on span. */
331         TAILQ_INSERT_TAIL(&devices, de, list);
332
333         return (0);
334 }
335
336 int
337 linux_device_unregister_handler(struct linux_device_handler *d)
338 {
339         struct device_element *de;
340
341         if (d == NULL)
342                 return (EINVAL);
343
344         TAILQ_FOREACH(de, &devices, list) {
345                 if (bcmp(d, &de->entry, sizeof(*d)) == 0) {
346                         TAILQ_REMOVE(&devices, de, list);
347                         free(de, M_LINUX);
348
349                         return (0);
350                 }
351         }
352
353         return (EINVAL);
354 }