xref: /freebsd/sys/compat/linux/linux_misc.c (revision 0699090b6a4116e3cc55b20d33548f9866c5ff1d)
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.4 1995/06/08 13:50:52 sos Exp $
29  */
30 
31 #include <i386/linux/linux.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/exec.h>
35 #include <sys/mman.h>
36 #include <sys/proc.h>
37 #include <sys/dirent.h>
38 #include <sys/file.h>
39 #include <sys/filedesc.h>
40 #include <sys/ioctl.h>
41 #include <sys/imgact_aout.h>
42 #include <sys/mount.h>
43 #include <sys/namei.h>
44 #include <sys/resource.h>
45 #include <sys/resourcevar.h>
46 #include <sys/stat.h>
47 #include <sys/time.h>
48 #include <sys/times.h>
49 #include <sys/utsname.h>
50 #include <sys/vnode.h>
51 #include <sys/wait.h>
52 
53 #include <machine/cpu.h>
54 #include <machine/psl.h>
55 #include <machine/reg.h>
56 
57 #include <vm/vm.h>
58 #include <vm/vm_kern.h>
59 
60 
61 struct linux_alarm_args {
62     unsigned int secs;
63 };
64 
65 int
66 linux_alarm(struct proc *p, struct linux_alarm_args *args, int *retval)
67 {
68     extern struct timeval time;
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     extern int swap_pager_full;
115 
116     if ((vm_offset_t)args->dsend < (vm_offset_t)vm->vm_daddr)
117 	return EINVAL;
118     if (((caddr_t)args->dsend - (caddr_t)vm->vm_daddr)
119 	> p->p_rlimit[RLIMIT_DATA].rlim_cur)
120 	return ENOMEM;
121 
122     old = round_page((vm_offset_t)vm->vm_daddr) + ctob(vm->vm_dsize);
123     new = round_page((vm_offset_t)args->dsend);
124     *retval = old;
125     if ((new-old) > 0) {
126 	if (swap_pager_full)
127 	    return ENOMEM;
128 	error = vm_map_find(&vm->vm_map, NULL, 0, &old, (new-old), FALSE);
129 	if (error)
130 	    return error;
131 	vm->vm_dsize += btoc((new-old));
132 	*retval = (int)(vm->vm_daddr + ctob(vm->vm_dsize));
133     }
134     return 0;
135 #else
136     struct vmspace *vm = p->p_vmspace;
137     vm_offset_t new, old;
138     struct obreak_args {
139 	vm_offset_t newsize;
140     } tmp;
141 
142 #ifdef DEBUG
143     printf("Linux-emul(%d): brk(%08x)\n", p->p_pid, args->dsend);
144 #endif
145     old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize);
146     new = (vm_offset_t)args->dsend;
147     tmp.newsize = new;
148     if (((caddr_t)new > vm->vm_daddr) && !obreak(p, &tmp, retval))
149 	retval[0] = (int)new;
150     else
151 	retval[0] = (int)old;
152 
153     return 0;
154 #endif
155 }
156 
157 struct linux_uselib_args {
158     char *library;
159 };
160 
161 int
162 linux_uselib(struct proc *p, struct linux_uselib_args *args, int *retval)
163 {
164     struct nameidata ni;
165     struct vnode *vnodep;
166     struct exec *a_out = 0;
167     struct vattr attr;
168     unsigned long vmaddr, virtual_offset, file_offset;
169     unsigned long buffer, bss_size;
170     char *ptr;
171     char path[MAXPATHLEN];
172     const char *prefix = "/compat/linux";
173     size_t sz, len;
174     int error;
175 
176 #ifdef DEBUG
177     printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, args->library);
178 #endif
179 
180     for (ptr = path; (*ptr = *prefix) != '\0'; ptr++, prefix++) ;
181     sz = MAXPATHLEN - (ptr - path);
182     if (error = copyinstr(args->library, ptr, sz, &len))
183 	return error;
184     if (*ptr != '/')
185 	return EINVAL;
186 
187 #ifdef DEBUG
188     printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, path);
189 #endif
190 
191     NDINIT(&ni, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p);
192     if (error = namei(&ni))
193 	return error;
194 
195     vnodep = ni.ni_vp;
196     if (vnodep == NULL)
197 	    return ENOEXEC;
198 
199     if (vnodep->v_writecount)
200 	    return ETXTBSY;
201 
202     if (error = VOP_GETATTR(vnodep, &attr, p->p_ucred, p))
203 	return error;
204 
205     if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC)
206 	|| ((attr.va_mode & 0111) == 0)
207 	|| (attr.va_type != VREG))
208 	    return ENOEXEC;
209 
210     if (attr.va_size == 0)
211 	    return ENOEXEC;
212 
213     if (error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p))
214 	return error;
215 
216     if (error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p))
217 	return error;
218 
219     error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, 1024,
220 	    	    VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vnodep, 0);
221     if (error)
222 	return (error);
223 
224     /*
225      * Is it a Linux binary ?
226      */
227     if (((a_out->a_magic >> 16) & 0xff) != 0x64)
228 	return -1;
229 
230     /*
231      * Set file/virtual offset based on a.out variant.
232      */
233     switch ((int)(a_out->a_magic & 0xffff)) {
234     case 0413:	/* ZMAGIC */
235 	virtual_offset = 0;
236 	file_offset = 1024;
237 	break;
238     case 0314:	/* QMAGIC */
239 	virtual_offset = 4096;
240 	file_offset = 0;
241 	break;
242     default:
243 	return (-1);
244     }
245 
246     vnodep->v_flag |= VTEXT;
247     bss_size = round_page(a_out->a_bss);
248     /*
249      * Check if file_offset page aligned,.
250      * Currently we cannot handle misalinged file offsets,
251      * and so we read in the entire image (what a waste).
252      */
253     if (file_offset & PGOFSET) {
254 #ifdef DEBUG
255 printf("uselib: Non page aligned binary %d\n", file_offset);
256 #endif
257 	/*
258 	 * Map text+data read/write/execute
259 	 */
260 	vmaddr = virtual_offset + round_page(a_out->a_entry);
261 	error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr,
262 		    	    round_page(a_out->a_text + a_out->a_data), FALSE);
263 	if (error)
264 	    return error;
265 
266 	error = vm_mmap(kernel_map, &buffer,
267 			round_page(a_out->a_text + a_out->a_data + file_offset),
268 		   	VM_PROT_READ, VM_PROT_READ, MAP_FILE,
269 			(caddr_t)vnodep, trunc_page(file_offset));
270 	if (error)
271 	    return error;
272 
273 	error = copyout((caddr_t)(buffer + file_offset), (caddr_t)vmaddr,
274 			a_out->a_text + a_out->a_data);
275 	if (error)
276 	    return error;
277 
278 	vm_map_remove(kernel_map, trunc_page(vmaddr),
279 		      round_page(a_out->a_text + a_out->a_data + file_offset));
280 
281 	error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr,
282 		   	       round_page(a_out->a_text + a_out->a_data),
283 		   	       VM_PROT_ALL, TRUE);
284 	if (error)
285 	    return error;
286     }
287     else {
288 #ifdef DEBUG
289 printf("uselib: Page aligned binary %d\n", file_offset);
290 #endif
291 	vmaddr = virtual_offset + round_page(a_out->a_entry);
292 	error = vm_mmap(&p->p_vmspace->vm_map, &vmaddr,
293 			a_out->a_text + a_out->a_data,
294 			VM_PROT_ALL, VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED,
295 			(caddr_t)vnodep, file_offset);
296 	if (error)
297 	    return (error);
298     }
299 #ifdef DEBUG
300 printf("mem=%08x = %08x %08x\n", vmaddr, ((int*)vmaddr)[0], ((int*)vmaddr)[1]);
301 #endif
302     if (bss_size != 0) {
303 	vmaddr = virtual_offset + round_page(a_out->a_entry) +
304 		 round_page(a_out->a_text + a_out->a_data);
305 	error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr,
306 			    bss_size, FALSE);
307 	if (error)
308 	    return error;
309 	error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr, bss_size,
310 		   	       VM_PROT_ALL, TRUE);
311 	if (error)
312 	    return error;
313     }
314     return 0;
315 }
316 
317 struct linux_select_args {
318     void *ptr;
319 };
320 
321 int
322 linux_select(struct proc *p, struct linux_select_args *args, int *retval)
323 {
324     struct {
325 	int nfds;
326 	fd_set *readfds;
327 	fd_set *writefds;
328 	fd_set *exceptfds;
329 	struct timeval *timeout;
330     } linux_args;
331     struct {
332 	unsigned int nd;
333 	fd_set *in;
334 	fd_set *ou;
335 	fd_set *ex;
336 	struct timeval *tv;
337     } bsd_args;
338     int error;
339 
340     if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args,
341 			sizeof(linux_args))))
342 	return error;
343 #ifdef DEBUG
344     printf("Linux-emul(%d): select(%d, %d, %d, %d, %d)\n",
345 	   p->p_pid, linux_args.nfds, linux_args.readfds,
346 	   linux_args.writefds, linux_args.exceptfds,
347 	   linux_args.timeout);
348 #endif
349     bsd_args.nd = linux_args.nfds;
350     bsd_args.in = linux_args.readfds;
351     bsd_args.ou = linux_args.writefds;
352     bsd_args.ex = linux_args.exceptfds;
353     bsd_args.tv = linux_args.timeout;
354     return select(p, &bsd_args, retval);
355 }
356 
357 struct linux_getpgid_args {
358     int pid;
359 };
360 
361 int
362 linux_getpgid(struct proc *p, struct linux_getpgid_args *args, int *retval)
363 {
364     struct proc *curproc;
365 
366 #ifdef DEBUG
367     printf("Linux-emul(%d): getpgid(%d)\n", p->p_pid, args->pid);
368 #endif
369     if (args->pid != p->p_pid) {
370 	if (!(curproc = pfind(args->pid)))
371 	    return ESRCH;
372     }
373     else
374 	curproc = p;
375     *retval = curproc->p_pgid;
376     return 0;
377 }
378 
379 int
380 linux_fork(struct proc *p, void *args, int *retval)
381 {
382     int error;
383 
384 #ifdef DEBUG
385     printf("Linux-emul(%d): fork()\n", p->p_pid);
386 #endif
387     if (error = fork(p, args, retval))
388 	return error;
389     if (retval[1] == 1)
390 	retval[0] = 0;
391     return 0;
392 }
393 
394 struct linux_mmap_args {
395     void *ptr;
396 };
397 
398 int
399 linux_mmap(struct proc *p, struct linux_mmap_args *args, int *retval)
400 {
401     struct {
402 	linux_caddr_t addr;
403 	int len;
404 	int prot;
405 	int flags;
406 	int fd;
407 	int pos;
408     } linux_args;
409     struct {
410 	caddr_t addr;
411 	size_t len;
412 	int prot;
413 	int flags;
414 	int fd;
415 	long pad;
416 	off_t pos;
417     } bsd_args;
418     int error;
419 
420     if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args,
421 			sizeof(linux_args))))
422 	return error;
423 #ifdef DEBUG
424     printf("Linux-emul(%d): mmap(%08x, %d, %d, %08x, %d, %d)\n",
425 	   p->p_pid, linux_args.addr, linux_args.len, linux_args.prot,
426 	   linux_args.flags, linux_args.fd, linux_args.pos);
427 #endif
428     bsd_args.flags = 0;
429     if (linux_args.flags & LINUX_MAP_SHARED)
430 	bsd_args.flags |= MAP_SHARED;
431     if (linux_args.flags & LINUX_MAP_PRIVATE)
432 	bsd_args.flags |= MAP_PRIVATE;
433     if (linux_args.flags & LINUX_MAP_FIXED)
434 	bsd_args.flags |= MAP_FIXED;
435     if (linux_args.flags & LINUX_MAP_ANON)
436 	bsd_args.flags |= MAP_ANON;
437     bsd_args.addr = linux_args.addr;
438     bsd_args.len = linux_args.len;
439     bsd_args.prot = linux_args.prot;
440     bsd_args.fd = linux_args.fd;
441     bsd_args.pos = linux_args.pos;
442     bsd_args.pad = 0;
443     return mmap(p, &bsd_args, retval);
444 }
445 
446 struct linux_pipe_args {
447     int *pipefds;
448 };
449 
450 int
451 linux_pipe(struct proc *p, struct linux_pipe_args *args, int *retval)
452 {
453     int error;
454 
455 #ifdef DEBUG
456     printf("Linux-emul(%d): pipe(*)\n", p->p_pid);
457 #endif
458     if (error = pipe(p, 0, retval))
459 	return error;
460     if (error = copyout(retval, args->pipefds, 2*sizeof(int)))
461 	return error;
462     *retval = 0;
463     return 0;
464 }
465 
466 struct linux_time_args {
467     linux_time_t *tm;
468 };
469 
470 int
471 linux_time(struct proc *p, struct linux_time_args *args, int *retval)
472 {
473     struct timeval tv;
474     linux_time_t tm;
475     int error;
476 
477 #ifdef DEBUG
478     printf("Linux-emul(%d): time(*)\n", p->p_pid);
479 #endif
480     microtime(&tv);
481     tm = tv.tv_sec;
482     if (error = copyout(&tm, args->tm, sizeof(linux_time_t)))
483 	return error;
484     *retval = tv.tv_sec;
485     return 0;
486 }
487 
488 struct linux_tms {
489     long    tms_utime;
490     long    tms_stime;
491     long    tms_cutime;
492     long    tms_cstime;
493 };
494 
495 struct linux_tms_args {
496     char *buf;
497 };
498 
499 int
500 linux_times(struct proc *p, struct linux_tms_args *args, int *retval)
501 {
502     extern int hz;
503     struct timeval tv;
504     struct linux_tms tms;
505 
506 #ifdef DEBUG
507     printf("Linux-emul(%d): times(*)\n", p->p_pid);
508 #endif
509     tms.tms_utime = p->p_uticks;
510     tms.tms_stime = p->p_sticks;
511     tms.tms_cutime = p->p_stats->p_cru.ru_utime.tv_sec * hz +
512 	    ((p->p_stats->p_cru.ru_utime.tv_usec * hz)/1000000);
513     tms.tms_cstime = p->p_stats->p_cru.ru_stime.tv_sec * hz +
514 	    ((p->p_stats->p_cru.ru_stime.tv_usec * hz)/1000000);
515     microtime(&tv);
516     *retval = tv.tv_sec * hz + (tv.tv_usec * hz)/1000000;
517     return (copyout((caddr_t)&tms, (caddr_t)args->buf,
518 	    	    sizeof(struct linux_tms)));
519 }
520 
521 struct linux_newuname_t {
522     char sysname[65];
523     char nodename[65];
524     char release[65];
525     char version[65];
526     char machine[65];
527     char domainname[65];
528 };
529 
530 struct linux_newuname_args {
531     char *buf;
532 };
533 
534 int
535 linux_newuname(struct proc *p, struct linux_newuname_args *args, int *retval)
536 {
537     struct linux_newuname_t linux_newuname;
538     extern char ostype[], osrelease[], machine[];
539     extern char hostname[], domainname[];
540 
541 #ifdef DEBUG
542     printf("Linux-emul(%d): newuname(*)\n", p->p_pid);
543 #endif
544     bzero(&linux_newuname, sizeof(struct linux_newuname_args));
545     strncpy(linux_newuname.sysname, ostype, 64);
546     strncpy(linux_newuname.nodename, hostname, 64);
547     strncpy(linux_newuname.release, osrelease, 64);
548     strncpy(linux_newuname.version, version, 64);
549     strncpy(linux_newuname.machine, machine, 64);
550     strncpy(linux_newuname.domainname, domainname, 64);
551     return (copyout((caddr_t)&linux_newuname, (caddr_t)args->buf,
552 	    	    sizeof(struct linux_newuname_t)));
553 }
554 
555 struct linux_utime_args {
556     char	*fname;
557     linux_time_t    *timeptr;
558 };
559 
560 int
561 linux_utime(struct proc *p, struct linux_utime_args *args, int *retval)
562 {
563     struct bsd_utimes_args {
564 	char	*fname;
565 	struct	timeval *tptr;
566     } bsdutimes;
567     struct timeval tv;
568 
569 #ifdef DEBUG
570     printf("Linux-emul(%d): utime(%s, *)\n", p->p_pid, args->fname);
571 #endif
572     tv.tv_sec = (long)args->timeptr;
573     tv.tv_usec = 0;
574     bsdutimes.tptr = &tv;
575     bsdutimes.fname = args->fname;
576     return utimes(p, &bsdutimes, retval);
577 }
578 
579 struct linux_waitpid_args {
580     int pid;
581     int *status;
582     int options;
583 };
584 
585 int
586 linux_waitpid(struct proc *p, struct linux_waitpid_args *args, int *retval)
587 {
588     struct wait4_args {
589 	int pid;
590 	int *status;
591 	int options;
592 	struct	rusage *rusage;
593 	int compat;
594     } tmp;
595     int error, tmpstat;
596 
597 #ifdef DEBUG
598     printf("Linux-emul(%d): waitpid(%d, *, %d)\n",
599 	   p->p_pid, args->pid, args->options);
600 #endif
601     tmp.pid = args->pid;
602     tmp.status = args->status;
603     tmp.options = args->options;
604     tmp.rusage = NULL;
605     tmp.compat = 0;
606 
607     if (error = wait4(p, &tmp, retval))
608 	return error;
609     if (error = copyin(args->status, &tmpstat, sizeof(int)))
610 	return error;
611     if (WIFSIGNALED(tmpstat))
612 	tmpstat = (tmpstat & 0xffffff80) |
613 		  bsd_to_linux_signal[WTERMSIG(tmpstat)];
614     else if (WIFSTOPPED(tmpstat))
615 	tmpstat = (tmpstat & 0xffff00ff) |
616 	      	  (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8);
617     return copyout(&tmpstat, args->status, sizeof(int));
618 }
619 
620 struct linux_wait4_args {
621     int pid;
622     int *status;
623     int options;
624     struct rusage *rusage;
625 };
626 
627 int
628 linux_wait4(struct proc *p, struct linux_wait4_args *args, int *retval)
629 {
630     struct wait4_args {
631 	int pid;
632 	int *status;
633 	int options;
634 	struct	rusage *rusage;
635 	int compat;
636     } tmp;
637     int error, tmpstat;
638 
639 #ifdef DEBUG
640     printf("Linux-emul(%d): wait4(%d, *, %d, *)\n",
641 	   p->p_pid, args->pid, args->options);
642 #endif
643     tmp.pid = args->pid;
644     tmp.status = args->status;
645     tmp.options = args->options;
646     tmp.rusage = args->rusage;
647     tmp.compat = 0;
648 
649     if (error = wait4(p, &tmp, retval))
650 	return error;
651     if (error = copyin(args->status, &tmpstat, sizeof(int)))
652 	return error;
653     if (WIFSIGNALED(tmpstat))
654 	tmpstat = (tmpstat & 0xffffff80) |
655 	      bsd_to_linux_signal[WTERMSIG(tmpstat)];
656     else if (WIFSTOPPED(tmpstat))
657 	tmpstat = (tmpstat & 0xffff00ff) |
658 	      (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8);
659     return copyout(&tmpstat, args->status, sizeof(int));
660 }
661