diff options
Diffstat (limited to 'sys/i386/linux/linux_misc.c')
-rw-r--r-- | sys/i386/linux/linux_misc.c | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/sys/i386/linux/linux_misc.c b/sys/i386/linux/linux_misc.c new file mode 100644 index 0000000..bf40786 --- /dev/null +++ b/sys/i386/linux/linux_misc.c @@ -0,0 +1,660 @@ +/*- + * Copyright (c) 1994-1995 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: linux_misc.c,v 1.4 1995/06/08 13:50:52 sos Exp $ + */ + +#include <i386/linux/linux.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/exec.h> +#include <sys/mman.h> +#include <sys/proc.h> +#include <sys/dirent.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/ioctl.h> +#include <sys/imgact_aout.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/resource.h> +#include <sys/resourcevar.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/times.h> +#include <sys/utsname.h> +#include <sys/vnode.h> +#include <sys/wait.h> + +#include <machine/cpu.h> +#include <machine/psl.h> +#include <machine/reg.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> + + +struct linux_alarm_args { + unsigned int secs; +}; + +int +linux_alarm(struct proc *p, struct linux_alarm_args *args, int *retval) +{ + extern struct timeval time; + struct itimerval it, old_it; + int s; + +#ifdef DEBUG + printf("Linux-emul(%d): alarm(%d)\n", p->p_pid, args->secs); +#endif + it.it_value.tv_sec = (long)args->secs; + it.it_value.tv_usec = 0; + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + s = splclock(); + old_it = p->p_realtimer; + if (timerisset(&old_it.it_value)) + if (timercmp(&old_it.it_value, &time, <)) + timerclear(&old_it.it_value); + else + timevalsub(&old_it.it_value, &time); + splx(s); + if (itimerfix(&it.it_value) || itimerfix(&it.it_interval)) + return EINVAL; + s = splclock(); + untimeout(realitexpire, (caddr_t)p); + if (timerisset(&it.it_value)) { + timevaladd(&it.it_value, &time); + timeout(realitexpire, (caddr_t)p, hzto(&it.it_value)); + } + p->p_realtimer = it; + splx(s); + if (old_it.it_value.tv_usec) + old_it.it_value.tv_sec++; + *retval = old_it.it_value.tv_sec; + return 0; +} + +struct linux_brk_args { + linux_caddr_t dsend; +}; + +int +linux_brk(struct proc *p, struct linux_brk_args *args, int *retval) +{ +#if 0 + struct vmspace *vm = p->p_vmspace; + vm_offset_t new, old; + int error; + extern int swap_pager_full; + + if ((vm_offset_t)args->dsend < (vm_offset_t)vm->vm_daddr) + return EINVAL; + if (((caddr_t)args->dsend - (caddr_t)vm->vm_daddr) + > p->p_rlimit[RLIMIT_DATA].rlim_cur) + return ENOMEM; + + old = round_page((vm_offset_t)vm->vm_daddr) + ctob(vm->vm_dsize); + new = round_page((vm_offset_t)args->dsend); + *retval = old; + if ((new-old) > 0) { + if (swap_pager_full) + return ENOMEM; + error = vm_map_find(&vm->vm_map, NULL, 0, &old, (new-old), FALSE); + if (error) + return error; + vm->vm_dsize += btoc((new-old)); + *retval = (int)(vm->vm_daddr + ctob(vm->vm_dsize)); + } + return 0; +#else + struct vmspace *vm = p->p_vmspace; + vm_offset_t new, old; + struct obreak_args { + vm_offset_t newsize; + } tmp; + +#ifdef DEBUG + printf("Linux-emul(%d): brk(%08x)\n", p->p_pid, args->dsend); +#endif + old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize); + new = (vm_offset_t)args->dsend; + tmp.newsize = new; + if (((caddr_t)new > vm->vm_daddr) && !obreak(p, &tmp, retval)) + retval[0] = (int)new; + else + retval[0] = (int)old; + + return 0; +#endif +} + +struct linux_uselib_args { + char *library; +}; + +int +linux_uselib(struct proc *p, struct linux_uselib_args *args, int *retval) +{ + struct nameidata ni; + struct vnode *vnodep; + struct exec *a_out = 0; + struct vattr attr; + unsigned long vmaddr, virtual_offset, file_offset; + unsigned long buffer, bss_size; + char *ptr; + char path[MAXPATHLEN]; + const char *prefix = "/compat/linux"; + size_t sz, len; + int error; + +#ifdef DEBUG + printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, args->library); +#endif + + for (ptr = path; (*ptr = *prefix) != '\0'; ptr++, prefix++) ; + sz = MAXPATHLEN - (ptr - path); + if (error = copyinstr(args->library, ptr, sz, &len)) + return error; + if (*ptr != '/') + return EINVAL; + +#ifdef DEBUG + printf("Linux-emul(%d): uselib(%s)\n", p->p_pid, path); +#endif + + NDINIT(&ni, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); + if (error = namei(&ni)) + return error; + + vnodep = ni.ni_vp; + if (vnodep == NULL) + return ENOEXEC; + + if (vnodep->v_writecount) + return ETXTBSY; + + if (error = VOP_GETATTR(vnodep, &attr, p->p_ucred, p)) + return error; + + if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC) + || ((attr.va_mode & 0111) == 0) + || (attr.va_type != VREG)) + return ENOEXEC; + + if (attr.va_size == 0) + return ENOEXEC; + + if (error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p)) + return error; + + if (error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p)) + return error; + + error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, 1024, + VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vnodep, 0); + if (error) + return (error); + + /* + * Is it a Linux binary ? + */ + if (((a_out->a_magic >> 16) & 0xff) != 0x64) + return -1; + + /* + * Set file/virtual offset based on a.out variant. + */ + switch ((int)(a_out->a_magic & 0xffff)) { + case 0413: /* ZMAGIC */ + virtual_offset = 0; + file_offset = 1024; + break; + case 0314: /* QMAGIC */ + virtual_offset = 4096; + file_offset = 0; + break; + default: + return (-1); + } + + vnodep->v_flag |= VTEXT; + bss_size = round_page(a_out->a_bss); + /* + * Check if file_offset page aligned,. + * Currently we cannot handle misalinged file offsets, + * and so we read in the entire image (what a waste). + */ + if (file_offset & PGOFSET) { +#ifdef DEBUG +printf("uselib: Non page aligned binary %d\n", file_offset); +#endif + /* + * Map text+data read/write/execute + */ + vmaddr = virtual_offset + round_page(a_out->a_entry); + error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr, + round_page(a_out->a_text + a_out->a_data), FALSE); + if (error) + return error; + + error = vm_mmap(kernel_map, &buffer, + round_page(a_out->a_text + a_out->a_data + file_offset), + VM_PROT_READ, VM_PROT_READ, MAP_FILE, + (caddr_t)vnodep, trunc_page(file_offset)); + if (error) + return error; + + error = copyout((caddr_t)(buffer + file_offset), (caddr_t)vmaddr, + a_out->a_text + a_out->a_data); + if (error) + return error; + + vm_map_remove(kernel_map, trunc_page(vmaddr), + round_page(a_out->a_text + a_out->a_data + file_offset)); + + error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr, + round_page(a_out->a_text + a_out->a_data), + VM_PROT_ALL, TRUE); + if (error) + return error; + } + else { +#ifdef DEBUG +printf("uselib: Page aligned binary %d\n", file_offset); +#endif + vmaddr = virtual_offset + round_page(a_out->a_entry); + error = vm_mmap(&p->p_vmspace->vm_map, &vmaddr, + a_out->a_text + a_out->a_data, + VM_PROT_ALL, VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED, + (caddr_t)vnodep, file_offset); + if (error) + return (error); + } +#ifdef DEBUG +printf("mem=%08x = %08x %08x\n", vmaddr, ((int*)vmaddr)[0], ((int*)vmaddr)[1]); +#endif + if (bss_size != 0) { + vmaddr = virtual_offset + round_page(a_out->a_entry) + + round_page(a_out->a_text + a_out->a_data); + error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &vmaddr, + bss_size, FALSE); + if (error) + return error; + error = vm_map_protect(&p->p_vmspace->vm_map, vmaddr, bss_size, + VM_PROT_ALL, TRUE); + if (error) + return error; + } + return 0; +} + +struct linux_select_args { + void *ptr; +}; + +int +linux_select(struct proc *p, struct linux_select_args *args, int *retval) +{ + struct { + int nfds; + fd_set *readfds; + fd_set *writefds; + fd_set *exceptfds; + struct timeval *timeout; + } linux_args; + struct { + unsigned int nd; + fd_set *in; + fd_set *ou; + fd_set *ex; + struct timeval *tv; + } bsd_args; + int error; + + if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args, + sizeof(linux_args)))) + return error; +#ifdef DEBUG + printf("Linux-emul(%d): select(%d, %d, %d, %d, %d)\n", + p->p_pid, linux_args.nfds, linux_args.readfds, + linux_args.writefds, linux_args.exceptfds, + linux_args.timeout); +#endif + bsd_args.nd = linux_args.nfds; + bsd_args.in = linux_args.readfds; + bsd_args.ou = linux_args.writefds; + bsd_args.ex = linux_args.exceptfds; + bsd_args.tv = linux_args.timeout; + return select(p, &bsd_args, retval); +} + +struct linux_getpgid_args { + int pid; +}; + +int +linux_getpgid(struct proc *p, struct linux_getpgid_args *args, int *retval) +{ + struct proc *curproc; + +#ifdef DEBUG + printf("Linux-emul(%d): getpgid(%d)\n", p->p_pid, args->pid); +#endif + if (args->pid != p->p_pid) { + if (!(curproc = pfind(args->pid))) + return ESRCH; + } + else + curproc = p; + *retval = curproc->p_pgid; + return 0; +} + +int +linux_fork(struct proc *p, void *args, int *retval) +{ + int error; + +#ifdef DEBUG + printf("Linux-emul(%d): fork()\n", p->p_pid); +#endif + if (error = fork(p, args, retval)) + return error; + if (retval[1] == 1) + retval[0] = 0; + return 0; +} + +struct linux_mmap_args { + void *ptr; +}; + +int +linux_mmap(struct proc *p, struct linux_mmap_args *args, int *retval) +{ + struct { + linux_caddr_t addr; + int len; + int prot; + int flags; + int fd; + int pos; + } linux_args; + struct { + caddr_t addr; + size_t len; + int prot; + int flags; + int fd; + long pad; + off_t pos; + } bsd_args; + int error; + + if ((error = copyin((caddr_t)args->ptr, (caddr_t)&linux_args, + sizeof(linux_args)))) + return error; +#ifdef DEBUG + printf("Linux-emul(%d): mmap(%08x, %d, %d, %08x, %d, %d)\n", + p->p_pid, linux_args.addr, linux_args.len, linux_args.prot, + linux_args.flags, linux_args.fd, linux_args.pos); +#endif + bsd_args.flags = 0; + if (linux_args.flags & LINUX_MAP_SHARED) + bsd_args.flags |= MAP_SHARED; + if (linux_args.flags & LINUX_MAP_PRIVATE) + bsd_args.flags |= MAP_PRIVATE; + if (linux_args.flags & LINUX_MAP_FIXED) + bsd_args.flags |= MAP_FIXED; + if (linux_args.flags & LINUX_MAP_ANON) + bsd_args.flags |= MAP_ANON; + bsd_args.addr = linux_args.addr; + bsd_args.len = linux_args.len; + bsd_args.prot = linux_args.prot; + bsd_args.fd = linux_args.fd; + bsd_args.pos = linux_args.pos; + bsd_args.pad = 0; + return mmap(p, &bsd_args, retval); +} + +struct linux_pipe_args { + int *pipefds; +}; + +int +linux_pipe(struct proc *p, struct linux_pipe_args *args, int *retval) +{ + int error; + +#ifdef DEBUG + printf("Linux-emul(%d): pipe(*)\n", p->p_pid); +#endif + if (error = pipe(p, 0, retval)) + return error; + if (error = copyout(retval, args->pipefds, 2*sizeof(int))) + return error; + *retval = 0; + return 0; +} + +struct linux_time_args { + linux_time_t *tm; +}; + +int +linux_time(struct proc *p, struct linux_time_args *args, int *retval) +{ + struct timeval tv; + linux_time_t tm; + int error; + +#ifdef DEBUG + printf("Linux-emul(%d): time(*)\n", p->p_pid); +#endif + microtime(&tv); + tm = tv.tv_sec; + if (error = copyout(&tm, args->tm, sizeof(linux_time_t))) + return error; + *retval = tv.tv_sec; + return 0; +} + +struct linux_tms { + long tms_utime; + long tms_stime; + long tms_cutime; + long tms_cstime; +}; + +struct linux_tms_args { + char *buf; +}; + +int +linux_times(struct proc *p, struct linux_tms_args *args, int *retval) +{ + extern int hz; + struct timeval tv; + struct linux_tms tms; + +#ifdef DEBUG + printf("Linux-emul(%d): times(*)\n", p->p_pid); +#endif + tms.tms_utime = p->p_uticks; + tms.tms_stime = p->p_sticks; + tms.tms_cutime = p->p_stats->p_cru.ru_utime.tv_sec * hz + + ((p->p_stats->p_cru.ru_utime.tv_usec * hz)/1000000); + tms.tms_cstime = p->p_stats->p_cru.ru_stime.tv_sec * hz + + ((p->p_stats->p_cru.ru_stime.tv_usec * hz)/1000000); + microtime(&tv); + *retval = tv.tv_sec * hz + (tv.tv_usec * hz)/1000000; + return (copyout((caddr_t)&tms, (caddr_t)args->buf, + sizeof(struct linux_tms))); +} + +struct linux_newuname_t { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + char domainname[65]; +}; + +struct linux_newuname_args { + char *buf; +}; + +int +linux_newuname(struct proc *p, struct linux_newuname_args *args, int *retval) +{ + struct linux_newuname_t linux_newuname; + extern char ostype[], osrelease[], machine[]; + extern char hostname[], domainname[]; + +#ifdef DEBUG + printf("Linux-emul(%d): newuname(*)\n", p->p_pid); +#endif + bzero(&linux_newuname, sizeof(struct linux_newuname_args)); + strncpy(linux_newuname.sysname, ostype, 64); + strncpy(linux_newuname.nodename, hostname, 64); + strncpy(linux_newuname.release, osrelease, 64); + strncpy(linux_newuname.version, version, 64); + strncpy(linux_newuname.machine, machine, 64); + strncpy(linux_newuname.domainname, domainname, 64); + return (copyout((caddr_t)&linux_newuname, (caddr_t)args->buf, + sizeof(struct linux_newuname_t))); +} + +struct linux_utime_args { + char *fname; + linux_time_t *timeptr; +}; + +int +linux_utime(struct proc *p, struct linux_utime_args *args, int *retval) +{ + struct bsd_utimes_args { + char *fname; + struct timeval *tptr; + } bsdutimes; + struct timeval tv; + +#ifdef DEBUG + printf("Linux-emul(%d): utime(%s, *)\n", p->p_pid, args->fname); +#endif + tv.tv_sec = (long)args->timeptr; + tv.tv_usec = 0; + bsdutimes.tptr = &tv; + bsdutimes.fname = args->fname; + return utimes(p, &bsdutimes, retval); +} + +struct linux_waitpid_args { + int pid; + int *status; + int options; +}; + +int +linux_waitpid(struct proc *p, struct linux_waitpid_args *args, int *retval) +{ + struct wait4_args { + int pid; + int *status; + int options; + struct rusage *rusage; + int compat; + } tmp; + int error, tmpstat; + +#ifdef DEBUG + printf("Linux-emul(%d): waitpid(%d, *, %d)\n", + p->p_pid, args->pid, args->options); +#endif + tmp.pid = args->pid; + tmp.status = args->status; + tmp.options = args->options; + tmp.rusage = NULL; + tmp.compat = 0; + + if (error = wait4(p, &tmp, retval)) + return error; + if (error = copyin(args->status, &tmpstat, sizeof(int))) + return error; + if (WIFSIGNALED(tmpstat)) + tmpstat = (tmpstat & 0xffffff80) | + bsd_to_linux_signal[WTERMSIG(tmpstat)]; + else if (WIFSTOPPED(tmpstat)) + tmpstat = (tmpstat & 0xffff00ff) | + (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8); + return copyout(&tmpstat, args->status, sizeof(int)); +} + +struct linux_wait4_args { + int pid; + int *status; + int options; + struct rusage *rusage; +}; + +int +linux_wait4(struct proc *p, struct linux_wait4_args *args, int *retval) +{ + struct wait4_args { + int pid; + int *status; + int options; + struct rusage *rusage; + int compat; + } tmp; + int error, tmpstat; + +#ifdef DEBUG + printf("Linux-emul(%d): wait4(%d, *, %d, *)\n", + p->p_pid, args->pid, args->options); +#endif + tmp.pid = args->pid; + tmp.status = args->status; + tmp.options = args->options; + tmp.rusage = args->rusage; + tmp.compat = 0; + + if (error = wait4(p, &tmp, retval)) + return error; + if (error = copyin(args->status, &tmpstat, sizeof(int))) + return error; + if (WIFSIGNALED(tmpstat)) + tmpstat = (tmpstat & 0xffffff80) | + bsd_to_linux_signal[WTERMSIG(tmpstat)]; + else if (WIFSTOPPED(tmpstat)) + tmpstat = (tmpstat & 0xffff00ff) | + (bsd_to_linux_signal[WSTOPSIG(tmpstat)]<<8); + return copyout(&tmpstat, args->status, sizeof(int)); +} |