xref: /freebsd/sys/compat/linux/linux_misc.c (revision 48991a368427cadb9cdac39581d1676c29619c52)
1 /*-
2  * Copyright (c) 1994-1995 S�ren Schmidt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software withough specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  *  $Id: linux_misc.c,v 1.3 1995/11/06 12:52:24 davidg Exp $
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/sysproto.h>
34 #include <sys/kernel.h>
35 #include <sys/exec.h>
36 #include <sys/mman.h>
37 #include <sys/proc.h>
38 #include <sys/dirent.h>
39 #include <sys/file.h>
40 #include <sys/filedesc.h>
41 #include <sys/ioctl.h>
42 #include <sys/imgact_aout.h>
43 #include <sys/mount.h>
44 #include <sys/namei.h>
45 #include <sys/resource.h>
46 #include <sys/resourcevar.h>
47 #include <sys/stat.h>
48 #include <sys/sysctl.h>
49 #include <sys/times.h>
50 #include <sys/utsname.h>
51 #include <sys/vnode.h>
52 #include <sys/wait.h>
53 
54 #include <vm/vm_kern.h>
55 
56 #include <machine/cpu.h>
57 #include <machine/psl.h>
58 
59 #include <i386/linux/linux.h>
60 #include <i386/linux/sysproto.h>
61 
62 struct linux_alarm_args {
63     unsigned int secs;
64 };
65 
66 int
67 linux_alarm(struct proc *p, struct linux_alarm_args *args, int *retval)
68 {
69     struct itimerval it, old_it;
70     int s;
71 
72 #ifdef DEBUG
73     printf("Linux-emul(%d): alarm(%d)\n", p->p_pid, args->secs);
74 #endif
75     it.it_value.tv_sec = (long)args->secs;
76     it.it_value.tv_usec = 0;
77     it.it_interval.tv_sec = 0;
78     it.it_interval.tv_usec = 0;
79     s = splclock();
80     old_it = p->p_realtimer;
81     if (timerisset(&old_it.it_value))
82 	if (timercmp(&old_it.it_value, &time, <))
83 	    timerclear(&old_it.it_value);
84 	else
85 	    timevalsub(&old_it.it_value, &time);
86     splx(s);
87     if (itimerfix(&it.it_value) || itimerfix(&it.it_interval))
88 	return EINVAL;
89     s = splclock();
90     untimeout(realitexpire, (caddr_t)p);
91     if (timerisset(&it.it_value)) {
92 	timevaladd(&it.it_value, &time);
93 	timeout(realitexpire, (caddr_t)p, hzto(&it.it_value));
94     }
95     p->p_realtimer = it;
96     splx(s);
97     if (old_it.it_value.tv_usec)
98 	old_it.it_value.tv_sec++;
99     *retval = old_it.it_value.tv_sec;
100     return 0;
101 }
102 
103 struct linux_brk_args {
104     linux_caddr_t dsend;
105 };
106 
107 int
108 linux_brk(struct proc *p, struct linux_brk_args *args, int *retval)
109 {
110 #if 0
111     struct vmspace *vm = p->p_vmspace;
112     vm_offset_t new, old;
113     int error;
114 
115     if ((vm_offset_t)args->dsend < (vm_offset_t)vm->vm_daddr)
116 	return EINVAL;
117     if (((caddr_t)args->dsend - (caddr_t)vm->vm_daddr)
118 	> p->p_rlimit[RLIMIT_DATA].rlim_cur)
119 	return ENOMEM;
120 
121     old = round_page((vm_offset_t)vm->vm_daddr) + ctob(vm->vm_dsize);
122     new = round_page((vm_offset_t)args->dsend);
123     *retval = old;
124     if ((new-old) > 0) {
125 	if (swap_pager_full)
126 	    return ENOMEM;
127 	error = vm_map_find(&vm->vm_map, NULL, 0, &old, (new-old), FALSE);
128 	if (error)
129 	    return error;
130 	vm->vm_dsize += btoc((new-old));
131 	*retval = (int)(vm->vm_daddr + ctob(vm->vm_dsize));
132     }
133     return 0;
134 #else
135     struct vmspace *vm = p->p_vmspace;
136     vm_offset_t new, old;
137     struct obreak_args {
138 	vm_offset_t newsize;
139     } tmp;
140 
141 #ifdef DEBUG
142     printf("Linux-emul(%d): brk(%08x)\n", p->p_pid, args->dsend);
143 #endif
144     old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize);
145     new = (vm_offset_t)args->dsend;
146     tmp.newsize = new;
147     if (((caddr_t)new > vm->vm_daddr) && !obreak(p, &tmp, retval))
148 	retval[0] = (int)new;
149     else
150 	retval[0] = (int)old;
151 
152     return 0;
153 #endif
154 }
155 
156 struct linux_uselib_args {
157     char *library;
158 };
159 
160 int
161 linux_uselib(struct proc *p, struct linux_uselib_args *args, int *retval)
162 {
163     struct nameidata ni;
164     struct vnode *vp;
165     struct exec *a_out = 0;
166     struct vattr attr;
167     unsigned long vmaddr, virtual_offset, file_offset;
168     unsigned long buffer, bss_size;
169     char *ptr;
170     char path[MAXPATHLEN];
171     const char *prefix = "/compat/linux";
172     size_t sz, len;
173     int error;
174 
175 #ifdef DEBUG
176     printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, args->library);
177 #endif
178 
179     for (ptr = path; (*ptr = *prefix) != '\0'; ptr++, prefix++) ;
180     sz = MAXPATHLEN - (ptr - path);
181     if (error = copyinstr(args->library, ptr, sz, &len))
182 	return error;
183     if (*ptr != '/')
184 	return EINVAL;
185 
186 #ifdef DEBUG
187     printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, path);
188 #endif
189 
190     NDINIT(&ni, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, path, p);
191     if (error = namei(&ni))
192 	return error;
193 
194     vp = ni.ni_vp;
195     if (vp == NULL)
196 	    return ENOEXEC;
197 
198     if (vp->v_writecount) {
199 	    VOP_UNLOCK(vp);
200 	    return ETXTBSY;
201     }
202 
203     if (error = VOP_GETATTR(vp, &attr, p->p_ucred, p)) {
204 	    VOP_UNLOCK(vp);
205 	    return error;
206     }
207 
208     if ((vp->v_mount->mnt_flag & MNT_NOEXEC)
209 	|| ((attr.va_mode & 0111) == 0)
210 	|| (attr.va_type != VREG)) {
211 	    VOP_UNLOCK(vp);
212 	    return ENOEXEC;
213     }
214 
215     if (attr.va_size == 0) {
216 	    VOP_UNLOCK(vp);
217 	    return ENOEXEC;
218     }
219 
220     if (error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p)) {
221 	VOP_UNLOCK(vp);
222 	return error;
223     }
224 
225     if (error = VOP_OPEN(vp, FREAD, p->p_ucred, p)) {
226 	VOP_UNLOCK(vp);
227 	return error;
228     }
229 
230     VOP_UNLOCK(vp);	/* lock no longer needed */
231 
232     error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, 1024,
233 	    	    VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vp, 0);
234     if (error)
235 	return (error);
236 
237     /*
238      * Is it a Linux binary ?
239      */
240     if (((a_out->a_magic >> 16) & 0xff) != 0x64)
241 	return ENOEXEC;
242 
243     /*
244      * Set file/virtual offset based on a.out variant.
245      */
246     switch ((int)(a_out->a_magic & 0xffff)) {
247     case 0413:	/* ZMAGIC */
248 	virtual_offset = 0;
249 	file_offset = 1024;
250 	break;
251     case 0314:	/* QMAGIC */
252 	virtual_offset = 4096;
253 	file_offset = 0;
254 	break;
255     default:
256 	return ENOEXEC;
257     }
258 
259     vp->v_flag |= VTEXT;
260     bss_size = round_page(a_out->a_bss);
261     /*
262      * Check if file_offset page aligned,.
263      * Currently we cannot handle misalinged file offsets,
264      * and so we read in the entire image (what a waste).
265      */
266     if (file_offset & PGOFSET) {
267 #ifdef DEBUG
268 printf("uselib: Non page aligned binary %d\n", file_offset);
269 #endif
270 	/*
271 	 * Map text+data read/write/execute
272 	 */
273 	vmaddr = virtual_offset + round_page(a_out->a_entry);
274 	error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr,
275 		    	    round_page(a_out->a_text + a_out->a_data), FALSE);
276 	if (error)
277 	    return error;
278 
279 	error = vm_mmap(kernel_map, &buffer,
280 			round_page(a_out->a_text + a_out->a_data + file_offset),
281 		   	VM_PROT_READ, VM_PROT_READ, MAP_FILE,
282 			(caddr_t)vp, trunc_page(file_offset));
283 	if (error)
284 	    return error;
285 
286 	error = copyout((caddr_t)(buffer + file_offset), (caddr_t)vmaddr,
287 			a_out->a_text + a_out->a_data);
288 	if (error)
289 	    return error;
290 
291 	vm_map_remove(kernel_map, trunc_page(vmaddr),
292 		      round_page(a_out->a_text + a_out->a_data + file_offset));
293 
294 	error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr,
295 		   	       round_page(a_out->a_text + a_out->a_data),
296 		   	       VM_PROT_ALL, TRUE);
297 	if (error)
298 	    return error;
299     }
300     else {
301 #ifdef DEBUG
302 printf("uselib: Page aligned binary %d\n", file_offset);
303 #endif
304 	vmaddr = virtual_offset + round_page(a_out->a_entry);
305 	error = vm_mmap(&p->p_vmspace->vm_map, &vmaddr,
306 			a_out->a_text + a_out->a_data,
307 			VM_PROT_ALL, VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED,
308 			(caddr_t)vp, file_offset);
309 	if (error)
310 	    return (error);
311     }
312 #ifdef DEBUG
313 printf("mem=%08x = %08x %08x\n", vmaddr, ((int*)vmaddr)[0], ((int*)vmaddr)[1]);
314 #endif
315     if (bss_size != 0) {
316 	vmaddr = virtual_offset + round_page(a_out->a_entry) +
317 		 round_page(a_out->a_text + a_out->a_data);
318 	error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr,
319 			    bss_size, FALSE);
320 	if (error)
321 	    return error;
322 	error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr, bss_size,
323 		   	       VM_PROT_ALL, TRUE);
324 	if (error)
325 	    return error;
326     }
327     return 0;
328 }
329 
330 struct linux_select_args {
331     void *ptr;
332 };
333 
334 int
335 linux_select(struct proc *p, struct linux_select_args *args, int *retval)
336 {
337     struct {
338 	int nfds;
339 	fd_set *readfds;
340 	fd_set *writefds;
341 	fd_set *exceptfds;
342 	struct timeval *timeout;
343     } linux_args;
344     struct {
345 	unsigned int nd;
346 	fd_set *in;
347 	fd_set *ou;
348 	fd_set *ex;
349 	struct timeval *tv;
350     } bsd_args;
351     int error;
352 
353     if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args,
354 			sizeof(linux_args))))
355 	return error;
356 #ifdef DEBUG
357     printf("Linux-emul(%d): select(%d, %d, %d, %d, %d)\n",
358 	   p->p_pid, linux_args.nfds, linux_args.readfds,
359 	   linux_args.writefds, linux_args.exceptfds,
360 	   linux_args.timeout);
361 #endif
362     bsd_args.nd = linux_args.nfds;
363     bsd_args.in = linux_args.readfds;
364     bsd_args.ou = linux_args.writefds;
365     bsd_args.ex = linux_args.exceptfds;
366     bsd_args.tv = linux_args.timeout;
367     return select(p, &bsd_args, retval);
368 }
369 
370 struct linux_getpgid_args {
371     int pid;
372 };
373 
374 int
375 linux_getpgid(struct proc *p, struct linux_getpgid_args *args, int *retval)
376 {
377     struct proc *curproc;
378 
379 #ifdef DEBUG
380     printf("Linux-emul(%d): getpgid(%d)\n", p->p_pid, args->pid);
381 #endif
382     if (args->pid != p->p_pid) {
383 	if (!(curproc = pfind(args->pid)))
384 	    return ESRCH;
385     }
386     else
387 	curproc = p;
388     *retval = curproc->p_pgid;
389     return 0;
390 }
391 
392 int
393 linux_fork(struct proc *p, void *args, int *retval)
394 {
395     int error;
396 
397 #ifdef DEBUG
398     printf("Linux-emul(%d): fork()\n", p->p_pid);
399 #endif
400     if (error = fork(p, args, retval))
401 	return error;
402     if (retval[1] == 1)
403 	retval[0] = 0;
404     return 0;
405 }
406 
407 struct linux_mmap_args {
408     void *ptr;
409 };
410 
411 int
412 linux_mmap(struct proc *p, struct linux_mmap_args *args, int *retval)
413 {
414     struct {
415 	linux_caddr_t addr;
416 	int len;
417 	int prot;
418 	int flags;
419 	int fd;
420 	int pos;
421     } linux_args;
422     struct {
423 	caddr_t addr;
424 	size_t len;
425 	int prot;
426 	int flags;
427 	int fd;
428 	long pad;
429 	off_t pos;
430     } bsd_args;
431     int error;
432 
433     if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args,
434 			sizeof(linux_args))))
435 	return error;
436 #ifdef DEBUG
437     printf("Linux-emul(%d): mmap(%08x, %d, %d, %08x, %d, %d)\n",
438 	   p->p_pid, linux_args.addr, linux_args.len, linux_args.prot,
439 	   linux_args.flags, linux_args.fd, linux_args.pos);
440 #endif
441     bsd_args.flags = 0;
442     if (linux_args.flags & LINUX_MAP_SHARED)
443 	bsd_args.flags |= MAP_SHARED;
444     if (linux_args.flags & LINUX_MAP_PRIVATE)
445 	bsd_args.flags |= MAP_PRIVATE;
446     if (linux_args.flags & LINUX_MAP_FIXED)
447 	bsd_args.flags |= MAP_FIXED;
448     if (linux_args.flags & LINUX_MAP_ANON)
449 	bsd_args.flags |= MAP_ANON;
450     bsd_args.addr = linux_args.addr;
451     bsd_args.len = linux_args.len;
452     bsd_args.prot = linux_args.prot;
453     bsd_args.fd = linux_args.fd;
454     bsd_args.pos = linux_args.pos;
455     bsd_args.pad = 0;
456     return mmap(p, &bsd_args, retval);
457 }
458 
459 struct linux_pipe_args {
460     int *pipefds;
461 };
462 
463 int
464 linux_pipe(struct proc *p, struct linux_pipe_args *args, int *retval)
465 {
466     int error;
467 
468 #ifdef DEBUG
469     printf("Linux-emul(%d): pipe(*)\n", p->p_pid);
470 #endif
471     if (error = pipe(p, 0, retval))
472 	return error;
473     if (error = copyout(retval, args->pipefds, 2*sizeof(int)))
474 	return error;
475     *retval = 0;
476     return 0;
477 }
478 
479 struct linux_time_args {
480     linux_time_t *tm;
481 };
482 
483 int
484 linux_time(struct proc *p, struct linux_time_args *args, int *retval)
485 {
486     struct timeval tv;
487     linux_time_t tm;
488     int error;
489 
490 #ifdef DEBUG
491     printf("Linux-emul(%d): time(*)\n", p->p_pid);
492 #endif
493     microtime(&tv);
494     tm = tv.tv_sec;
495     if (error = copyout(&tm, args->tm, sizeof(linux_time_t)))
496 	return error;
497     *retval = tv.tv_sec;
498     return 0;
499 }
500 
501 struct linux_tms {
502     long    tms_utime;
503     long    tms_stime;
504     long    tms_cutime;
505     long    tms_cstime;
506 };
507 
508 struct linux_tms_args {
509     char *buf;
510 };
511 
512 int
513 linux_times(struct proc *p, struct linux_tms_args *args, int *retval)
514 {
515     struct timeval tv;
516     struct linux_tms tms;
517 
518 #ifdef DEBUG
519     printf("Linux-emul(%d): times(*)\n", p->p_pid);
520 #endif
521     tms.tms_utime = p->p_uticks;
522     tms.tms_stime = p->p_sticks;
523     tms.tms_cutime = p->p_stats->p_cru.ru_utime.tv_sec * hz +
524 	    ((p->p_stats->p_cru.ru_utime.tv_usec * hz)/1000000);
525     tms.tms_cstime = p->p_stats->p_cru.ru_stime.tv_sec * hz +
526 	    ((p->p_stats->p_cru.ru_stime.tv_usec * hz)/1000000);
527     microtime(&tv);
528     *retval = tv.tv_sec * hz + (tv.tv_usec * hz)/1000000;
529     return (copyout((caddr_t)&tms, (caddr_t)args->buf,
530 	    	    sizeof(struct linux_tms)));
531 }
532 
533 struct linux_newuname_t {
534     char sysname[65];
535     char nodename[65];
536     char release[65];
537     char version[65];
538     char machine[65];
539     char domainname[65];
540 };
541 
542 struct linux_newuname_args {
543     char *buf;
544 };
545 
546 int
547 linux_newuname(struct proc *p, struct linux_newuname_args *args, int *retval)
548 {
549     struct linux_newuname_t linux_newuname;
550 
551 #ifdef DEBUG
552     printf("Linux-emul(%d): newuname(*)\n", p->p_pid);
553 #endif
554     bzero(&linux_newuname, sizeof(struct linux_newuname_args));
555     strncpy(linux_newuname.sysname, ostype, 64);
556     strncpy(linux_newuname.nodename, hostname, 64);
557     strncpy(linux_newuname.release, osrelease, 64);
558     strncpy(linux_newuname.version, version, 64);
559     strncpy(linux_newuname.machine, machine, 64);
560     strncpy(linux_newuname.domainname, domainname, 64);
561     return (copyout((caddr_t)&linux_newuname, (caddr_t)args->buf,
562 	    	    sizeof(struct linux_newuname_t)));
563 }
564 
565 struct linux_utime_args {
566     char	*fname;
567     linux_time_t    *timeptr;
568 };
569 
570 int
571 linux_utime(struct proc *p, struct linux_utime_args *args, int *retval)
572 {
573     struct bsd_utimes_args {
574 	char	*fname;
575 	struct	timeval *tptr;
576     } bsdutimes;
577     struct timeval tv;
578 
579 #ifdef DEBUG
580     printf("Linux-emul(%d): utime(%s, *)\n", p->p_pid, args->fname);
581 #endif
582     tv.tv_sec = (long)args->timeptr;
583     tv.tv_usec = 0;
584     bsdutimes.tptr = &tv;
585     bsdutimes.fname = args->fname;
586     return utimes(p, &bsdutimes, retval);
587 }
588 
589 struct linux_waitpid_args {
590     int pid;
591     int *status;
592     int options;
593 };
594 
595 int
596 linux_waitpid(struct proc *p, struct linux_waitpid_args *args, int *retval)
597 {
598     struct wait4_args {
599 	int pid;
600 	int *status;
601 	int options;
602 	struct	rusage *rusage;
603 	int compat;
604     } tmp;
605     int error, tmpstat;
606 
607 #ifdef DEBUG
608     printf("Linux-emul(%d): waitpid(%d, *, %d)\n",
609 	   p->p_pid, args->pid, args->options);
610 #endif
611     tmp.pid = args->pid;
612     tmp.status = args->status;
613     tmp.options = args->options;
614     tmp.rusage = NULL;
615     tmp.compat = 0;
616 
617     if (error = wait4(p, &tmp, retval))
618 	return error;
619     if (error = copyin(args->status, &tmpstat, sizeof(int)))
620 	return error;
621     if (WIFSIGNALED(tmpstat))
622 	tmpstat = (tmpstat & 0xffffff80) |
623 		  bsd_to_linux_signal[WTERMSIG(tmpstat)];
624     else if (WIFSTOPPED(tmpstat))
625 	tmpstat = (tmpstat & 0xffff00ff) |
626 	      	  (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8);
627     return copyout(&tmpstat, args->status, sizeof(int));
628 }
629 
630 struct linux_wait4_args {
631     int pid;
632     int *status;
633     int options;
634     struct rusage *rusage;
635 };
636 
637 int
638 linux_wait4(struct proc *p, struct linux_wait4_args *args, int *retval)
639 {
640     struct wait4_args {
641 	int pid;
642 	int *status;
643 	int options;
644 	struct	rusage *rusage;
645 	int compat;
646     } tmp;
647     int error, tmpstat;
648 
649 #ifdef DEBUG
650     printf("Linux-emul(%d): wait4(%d, *, %d, *)\n",
651 	   p->p_pid, args->pid, args->options);
652 #endif
653     tmp.pid = args->pid;
654     tmp.status = args->status;
655     tmp.options = args->options;
656     tmp.rusage = args->rusage;
657     tmp.compat = 0;
658 
659     if (error = wait4(p, &tmp, retval))
660 	return error;
661     if (error = copyin(args->status, &tmpstat, sizeof(int)))
662 	return error;
663     if (WIFSIGNALED(tmpstat))
664 	tmpstat = (tmpstat & 0xffffff80) |
665 	      bsd_to_linux_signal[WTERMSIG(tmpstat)];
666     else if (WIFSTOPPED(tmpstat))
667 	tmpstat = (tmpstat & 0xffff00ff) |
668 	      (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8);
669     return copyout(&tmpstat, args->status, sizeof(int));
670 }
671