diff --git a/sys/amd64/linux/linux_dummy_machdep.c b/sys/amd64/linux/linux_dummy_machdep.c index 4e4d0f9c0d8b..3ce407af291c 100644 --- a/sys/amd64/linux/linux_dummy_machdep.c +++ b/sys/amd64/linux/linux_dummy_machdep.c @@ -1,73 +1,72 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Dmitry Chagin * * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); /* * Before adding new stubs to this file, please check if a stub can be added to * the machine-independent code in sys/compat/linux/linux_dummy.c (or * sys/x86/linux/linux_dummy_x86.c). */ UNIMPLEMENTED(get_thread_area); UNIMPLEMENTED(set_thread_area); UNIMPLEMENTED(uselib); DUMMY(modify_ldt); DUMMY(ioperm); DUMMY(io_setup); DUMMY(io_destroy); DUMMY(io_getevents); DUMMY(io_submit); DUMMY(io_cancel); DUMMY(mq_open); DUMMY(mq_unlink); DUMMY(mq_timedsend); DUMMY(mq_timedreceive); DUMMY(mq_notify); DUMMY(mq_getsetattr); DUMMY(readahead); DUMMY(restart_syscall); -DUMMY(semtimedop); /* Linux 3.15: */ DUMMY(kexec_file_load); diff --git a/sys/amd64/linux32/linux32_dummy_machdep.c b/sys/amd64/linux32/linux32_dummy_machdep.c index a5c61e252f9d..f3bd8717524a 100644 --- a/sys/amd64/linux32/linux32_dummy_machdep.c +++ b/sys/amd64/linux32/linux32_dummy_machdep.c @@ -1,74 +1,73 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); UNIMPLEMENTED(break); UNIMPLEMENTED(ftime); UNIMPLEMENTED(gtty); UNIMPLEMENTED(stty); UNIMPLEMENTED(lock); UNIMPLEMENTED(mpx); UNIMPLEMENTED(prof); UNIMPLEMENTED(profil); UNIMPLEMENTED(ulimit); DUMMY(stime); DUMMY(olduname); DUMMY(uname); DUMMY(bdflush); DUMMY(ptrace); DUMMY(mq_open); DUMMY(mq_unlink); DUMMY(mq_timedsend); DUMMY(mq_timedreceive); DUMMY(mq_notify); DUMMY(mq_getsetattr); /* Linux 4.11: */ DUMMY(arch_prctl); /* Linux 5.0: */ DUMMY(clock_adjtime64); DUMMY(io_pgetevents_time64); DUMMY(mq_timedsend_time64); DUMMY(mq_timedreceive_time64); -DUMMY(semtimedop_time64); diff --git a/sys/amd64/linux32/linux32_machdep.c b/sys/amd64/linux32/linux32_machdep.c index baa567339689..e1d7742d0a21 100644 --- a/sys/amd64/linux32/linux32_machdep.c +++ b/sys/amd64/linux32/linux32_machdep.c @@ -1,744 +1,753 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2004 Tim J. Robbins * Copyright (c) 2002 Doug Rabson * Copyright (c) 2000 Marcel Moolenaar * 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 without 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void bsd_to_linux_rusage(struct rusage *ru, struct l_rusage *lru); struct l_old_select_argv { l_int nfds; l_uintptr_t readfds; l_uintptr_t writefds; l_uintptr_t exceptfds; l_uintptr_t timeout; } __packed; static void bsd_to_linux_rusage(struct rusage *ru, struct l_rusage *lru) { lru->ru_utime.tv_sec = ru->ru_utime.tv_sec; lru->ru_utime.tv_usec = ru->ru_utime.tv_usec; lru->ru_stime.tv_sec = ru->ru_stime.tv_sec; lru->ru_stime.tv_usec = ru->ru_stime.tv_usec; lru->ru_maxrss = ru->ru_maxrss; lru->ru_ixrss = ru->ru_ixrss; lru->ru_idrss = ru->ru_idrss; lru->ru_isrss = ru->ru_isrss; lru->ru_minflt = ru->ru_minflt; lru->ru_majflt = ru->ru_majflt; lru->ru_nswap = ru->ru_nswap; lru->ru_inblock = ru->ru_inblock; lru->ru_oublock = ru->ru_oublock; lru->ru_msgsnd = ru->ru_msgsnd; lru->ru_msgrcv = ru->ru_msgrcv; lru->ru_nsignals = ru->ru_nsignals; lru->ru_nvcsw = ru->ru_nvcsw; lru->ru_nivcsw = ru->ru_nivcsw; } int linux_copyout_rusage(struct rusage *ru, void *uaddr) { struct l_rusage lru; bsd_to_linux_rusage(ru, &lru); return (copyout(&lru, uaddr, sizeof(struct l_rusage))); } int linux_execve(struct thread *td, struct linux_execve_args *args) { struct image_args eargs; char *path; int error; LCONVPATHEXIST(args->path, &path); error = freebsd32_exec_copyin_args(&eargs, path, UIO_SYSSPACE, args->argp, args->envp); free(path, M_TEMP); if (error == 0) error = linux_common_execve(td, &eargs); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } CTASSERT(sizeof(struct l_iovec32) == 8); int linux32_copyinuio(struct l_iovec32 *iovp, l_ulong iovcnt, struct uio **uiop) { struct l_iovec32 iov32; struct iovec *iov; struct uio *uio; uint32_t iovlen; int error, i; *uiop = NULL; if (iovcnt > UIO_MAXIOV) return (EINVAL); iovlen = iovcnt * sizeof(struct iovec); uio = malloc(iovlen + sizeof(*uio), M_IOV, M_WAITOK); iov = (struct iovec *)(uio + 1); for (i = 0; i < iovcnt; i++) { error = copyin(&iovp[i], &iov32, sizeof(struct l_iovec32)); if (error) { free(uio, M_IOV); return (error); } iov[i].iov_base = PTRIN(iov32.iov_base); iov[i].iov_len = iov32.iov_len; } uio->uio_iov = iov; uio->uio_iovcnt = iovcnt; uio->uio_segflg = UIO_USERSPACE; uio->uio_offset = -1; uio->uio_resid = 0; for (i = 0; i < iovcnt; i++) { if (iov->iov_len > INT_MAX - uio->uio_resid) { free(uio, M_IOV); return (EINVAL); } uio->uio_resid += iov->iov_len; iov++; } *uiop = uio; return (0); } int linux32_copyiniov(struct l_iovec32 *iovp32, l_ulong iovcnt, struct iovec **iovp, int error) { struct l_iovec32 iov32; struct iovec *iov; uint32_t iovlen; int i; *iovp = NULL; if (iovcnt > UIO_MAXIOV) return (error); iovlen = iovcnt * sizeof(struct iovec); iov = malloc(iovlen, M_IOV, M_WAITOK); for (i = 0; i < iovcnt; i++) { error = copyin(&iovp32[i], &iov32, sizeof(struct l_iovec32)); if (error) { free(iov, M_IOV); return (error); } iov[i].iov_base = PTRIN(iov32.iov_base); iov[i].iov_len = iov32.iov_len; } *iovp = iov; return(0); } int linux_readv(struct thread *td, struct linux_readv_args *uap) { struct uio *auio; int error; error = linux32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_readv(td, uap->fd, auio); free(auio, M_IOV); return (error); } int linux_writev(struct thread *td, struct linux_writev_args *uap) { struct uio *auio; int error; error = linux32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_writev(td, uap->fd, auio); free(auio, M_IOV); return (error); } struct l_ipc_kludge { l_uintptr_t msgp; l_long msgtyp; } __packed; int linux_ipc(struct thread *td, struct linux_ipc_args *args) { switch (args->what & 0xFFFF) { case LINUX_SEMOP: { return (kern_semop(td, args->arg1, PTRIN(args->ptr), args->arg2, NULL)); } case LINUX_SEMGET: { struct linux_semget_args a; a.key = args->arg1; a.nsems = args->arg2; a.semflg = args->arg3; return (linux_semget(td, &a)); } case LINUX_SEMCTL: { struct linux_semctl_args a; int error; a.semid = args->arg1; a.semnum = args->arg2; a.cmd = args->arg3; error = copyin(PTRIN(args->ptr), &a.arg, sizeof(a.arg)); if (error) return (error); return (linux_semctl(td, &a)); } + case LINUX_SEMTIMEDOP: { + struct linux_semtimedop_args a; + + a.semid = args->arg1; + a.tsops = PTRIN(args->ptr); + a.nsops = args->arg2; + a.timeout = PTRIN(args->arg5); + return (linux_semtimedop(td, &a)); + } case LINUX_MSGSND: { struct linux_msgsnd_args a; a.msqid = args->arg1; a.msgp = PTRIN(args->ptr); a.msgsz = args->arg2; a.msgflg = args->arg3; return (linux_msgsnd(td, &a)); } case LINUX_MSGRCV: { struct linux_msgrcv_args a; a.msqid = args->arg1; a.msgsz = args->arg2; a.msgflg = args->arg3; if ((args->what >> 16) == 0) { struct l_ipc_kludge tmp; int error; if (args->ptr == 0) return (EINVAL); error = copyin(PTRIN(args->ptr), &tmp, sizeof(tmp)); if (error) return (error); a.msgp = PTRIN(tmp.msgp); a.msgtyp = tmp.msgtyp; } else { a.msgp = PTRIN(args->ptr); a.msgtyp = args->arg5; } return (linux_msgrcv(td, &a)); } case LINUX_MSGGET: { struct linux_msgget_args a; a.key = args->arg1; a.msgflg = args->arg2; return (linux_msgget(td, &a)); } case LINUX_MSGCTL: { struct linux_msgctl_args a; a.msqid = args->arg1; a.cmd = args->arg2; a.buf = PTRIN(args->ptr); return (linux_msgctl(td, &a)); } case LINUX_SHMAT: { struct linux_shmat_args a; l_uintptr_t addr; int error; a.shmid = args->arg1; a.shmaddr = PTRIN(args->ptr); a.shmflg = args->arg2; error = linux_shmat(td, &a); if (error != 0) return (error); addr = td->td_retval[0]; error = copyout(&addr, PTRIN(args->arg3), sizeof(addr)); td->td_retval[0] = 0; return (error); } case LINUX_SHMDT: { struct linux_shmdt_args a; a.shmaddr = PTRIN(args->ptr); return (linux_shmdt(td, &a)); } case LINUX_SHMGET: { struct linux_shmget_args a; a.key = args->arg1; a.size = args->arg2; a.shmflg = args->arg3; return (linux_shmget(td, &a)); } case LINUX_SHMCTL: { struct linux_shmctl_args a; a.shmid = args->arg1; a.cmd = args->arg2; a.buf = PTRIN(args->ptr); return (linux_shmctl(td, &a)); } default: break; } return (EINVAL); } int linux_old_select(struct thread *td, struct linux_old_select_args *args) { struct l_old_select_argv linux_args; struct linux_select_args newsel; int error; error = copyin(args->ptr, &linux_args, sizeof(linux_args)); if (error) return (error); newsel.nfds = linux_args.nfds; newsel.readfds = PTRIN(linux_args.readfds); newsel.writefds = PTRIN(linux_args.writefds); newsel.exceptfds = PTRIN(linux_args.exceptfds); newsel.timeout = PTRIN(linux_args.timeout); return (linux_select(td, &newsel)); } int linux_set_cloned_tls(struct thread *td, void *desc) { struct l_user_desc info; struct pcb *pcb; int error; error = copyin(desc, &info, sizeof(struct l_user_desc)); if (error) { linux_msg(td, "set_cloned_tls copyin info failed!"); } else { /* We might copy out the entry_number as GUGS32_SEL. */ info.entry_number = GUGS32_SEL; error = copyout(&info, desc, sizeof(struct l_user_desc)); if (error) linux_msg(td, "set_cloned_tls copyout info failed!"); pcb = td->td_pcb; update_pcb_bases(pcb); pcb->pcb_gsbase = (register_t)info.base_addr; td->td_frame->tf_gs = GSEL(GUGS32_SEL, SEL_UPL); } return (error); } int linux_set_upcall(struct thread *td, register_t stack) { if (stack) td->td_frame->tf_rsp = stack; /* * The newly created Linux thread returns * to the user space by the same path that a parent do. */ td->td_frame->tf_rax = 0; return (0); } int linux_mmap2(struct thread *td, struct linux_mmap2_args *args) { return (linux_mmap_common(td, PTROUT(args->addr), args->len, args->prot, args->flags, args->fd, (uint64_t)(uint32_t)args->pgoff * PAGE_SIZE)); } int linux_mmap(struct thread *td, struct linux_mmap_args *args) { int error; struct l_mmap_argv linux_args; error = copyin(args->ptr, &linux_args, sizeof(linux_args)); if (error) return (error); return (linux_mmap_common(td, linux_args.addr, linux_args.len, linux_args.prot, linux_args.flags, linux_args.fd, (uint32_t)linux_args.pgoff)); } int linux_mprotect(struct thread *td, struct linux_mprotect_args *uap) { return (linux_mprotect_common(td, PTROUT(uap->addr), uap->len, uap->prot)); } int linux_madvise(struct thread *td, struct linux_madvise_args *uap) { return (linux_madvise_common(td, PTROUT(uap->addr), uap->len, uap->behav)); } int linux_iopl(struct thread *td, struct linux_iopl_args *args) { int error; if (args->level < 0 || args->level > 3) return (EINVAL); if ((error = priv_check(td, PRIV_IO)) != 0) return (error); if ((error = securelevel_gt(td->td_ucred, 0)) != 0) return (error); td->td_frame->tf_rflags = (td->td_frame->tf_rflags & ~PSL_IOPL) | (args->level * (PSL_IOPL / 3)); return (0); } int linux_sigaction(struct thread *td, struct linux_sigaction_args *args) { l_osigaction_t osa; l_sigaction_t act, oact; int error; if (args->nsa != NULL) { error = copyin(args->nsa, &osa, sizeof(l_osigaction_t)); if (error) return (error); act.lsa_handler = osa.lsa_handler; act.lsa_flags = osa.lsa_flags; act.lsa_restorer = osa.lsa_restorer; LINUX_SIGEMPTYSET(act.lsa_mask); act.lsa_mask.__mask = osa.lsa_mask; } error = linux_do_sigaction(td, args->sig, args->nsa ? &act : NULL, args->osa ? &oact : NULL); if (args->osa != NULL && !error) { osa.lsa_handler = oact.lsa_handler; osa.lsa_flags = oact.lsa_flags; osa.lsa_restorer = oact.lsa_restorer; osa.lsa_mask = oact.lsa_mask.__mask; error = copyout(&osa, args->osa, sizeof(l_osigaction_t)); } return (error); } /* * Linux has two extra args, restart and oldmask. We don't use these, * but it seems that "restart" is actually a context pointer that * enables the signal to happen with a different register set. */ int linux_sigsuspend(struct thread *td, struct linux_sigsuspend_args *args) { sigset_t sigmask; l_sigset_t mask; LINUX_SIGEMPTYSET(mask); mask.__mask = args->mask; linux_to_bsd_sigset(&mask, &sigmask); return (kern_sigsuspend(td, sigmask)); } int linux_pause(struct thread *td, struct linux_pause_args *args) { struct proc *p = td->td_proc; sigset_t sigmask; PROC_LOCK(p); sigmask = td->td_sigmask; PROC_UNLOCK(p); return (kern_sigsuspend(td, sigmask)); } int linux_gettimeofday(struct thread *td, struct linux_gettimeofday_args *uap) { struct timeval atv; l_timeval atv32; struct timezone rtz; int error = 0; if (uap->tp) { microtime(&atv); atv32.tv_sec = atv.tv_sec; atv32.tv_usec = atv.tv_usec; error = copyout(&atv32, uap->tp, sizeof(atv32)); } if (error == 0 && uap->tzp != NULL) { rtz.tz_minuteswest = 0; rtz.tz_dsttime = 0; error = copyout(&rtz, uap->tzp, sizeof(rtz)); } return (error); } int linux_settimeofday(struct thread *td, struct linux_settimeofday_args *uap) { l_timeval atv32; struct timeval atv, *tvp; struct timezone atz, *tzp; int error; if (uap->tp) { error = copyin(uap->tp, &atv32, sizeof(atv32)); if (error) return (error); atv.tv_sec = atv32.tv_sec; atv.tv_usec = atv32.tv_usec; tvp = &atv; } else tvp = NULL; if (uap->tzp) { error = copyin(uap->tzp, &atz, sizeof(atz)); if (error) return (error); tzp = &atz; } else tzp = NULL; return (kern_settimeofday(td, tvp, tzp)); } int linux_getrusage(struct thread *td, struct linux_getrusage_args *uap) { struct rusage s; int error; error = kern_getrusage(td, uap->who, &s); if (error != 0) return (error); if (uap->rusage != NULL) error = linux_copyout_rusage(&s, uap->rusage); return (error); } int linux_set_thread_area(struct thread *td, struct linux_set_thread_area_args *args) { struct l_user_desc info; struct pcb *pcb; int error; error = copyin(args->desc, &info, sizeof(struct l_user_desc)); if (error) return (error); /* * Semantics of Linux version: every thread in the system has array * of three TLS descriptors. 1st is GLIBC TLS, 2nd is WINE, 3rd unknown. * This syscall loads one of the selected TLS decriptors with a value * and also loads GDT descriptors 6, 7 and 8 with the content of * the per-thread descriptors. * * Semantics of FreeBSD version: I think we can ignore that Linux has * three per-thread descriptors and use just the first one. * The tls_array[] is used only in [gs]et_thread_area() syscalls and * for loading the GDT descriptors. We use just one GDT descriptor * for TLS, so we will load just one. * * XXX: This doesn't work when a user space process tries to use more * than one TLS segment. Comment in the Linux source says wine might * do this. */ /* * GLIBC reads current %gs and call set_thread_area() with it. * We should let GUDATA_SEL and GUGS32_SEL proceed as well because * we use these segments. */ switch (info.entry_number) { case GUGS32_SEL: case GUDATA_SEL: case 6: case -1: info.entry_number = GUGS32_SEL; break; default: return (EINVAL); } /* * We have to copy out the GDT entry we use. * * XXX: What if a user space program does not check the return value * and tries to use 6, 7 or 8? */ error = copyout(&info, args->desc, sizeof(struct l_user_desc)); if (error) return (error); pcb = td->td_pcb; update_pcb_bases(pcb); pcb->pcb_gsbase = (register_t)info.base_addr; update_gdt_gsbase(td, info.base_addr); return (0); } void bsd_to_linux_regset32(const struct reg32 *b_reg, struct linux_pt_regset32 *l_regset) { l_regset->ebx = b_reg->r_ebx; l_regset->ecx = b_reg->r_ecx; l_regset->edx = b_reg->r_edx; l_regset->esi = b_reg->r_esi; l_regset->edi = b_reg->r_edi; l_regset->ebp = b_reg->r_ebp; l_regset->eax = b_reg->r_eax; l_regset->ds = b_reg->r_ds; l_regset->es = b_reg->r_es; l_regset->fs = b_reg->r_fs; l_regset->gs = b_reg->r_gs; l_regset->orig_eax = b_reg->r_eax; l_regset->eip = b_reg->r_eip; l_regset->cs = b_reg->r_cs; l_regset->eflags = b_reg->r_eflags; l_regset->esp = b_reg->r_esp; l_regset->ss = b_reg->r_ss; } int futex_xchgl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_xchgl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_xchgl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_xchgl_smap : futex_xchgl_nosmap); } int futex_addl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_addl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_addl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_addl_smap : futex_addl_nosmap); } int futex_orl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_orl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_orl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_orl_smap : futex_orl_nosmap); } int futex_andl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_andl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_andl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_andl_smap : futex_andl_nosmap); } int futex_xorl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_xorl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_xorl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_xorl_smap : futex_xorl_nosmap); } diff --git a/sys/arm64/linux/linux_dummy_machdep.c b/sys/arm64/linux/linux_dummy_machdep.c index 0b722c632709..22ad39a84eeb 100644 --- a/sys/arm64/linux/linux_dummy_machdep.c +++ b/sys/arm64/linux/linux_dummy_machdep.c @@ -1,63 +1,62 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Dmitry Chagin * * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); /* * Before adding new stubs to this file, please check if a stub can be added to * the machine-independent code in sys/compat/linux/linux_dummy.c. */ UNIMPLEMENTED(get_thread_area); UNIMPLEMENTED(set_thread_area); UNIMPLEMENTED(uselib); DUMMY(mq_open); DUMMY(mq_unlink); DUMMY(mq_timedsend); DUMMY(mq_timedreceive); DUMMY(mq_notify); DUMMY(mq_getsetattr); -DUMMY(semtimedop); DUMMY(kexec_file_load); diff --git a/sys/compat/linux/linux_ipc.c b/sys/compat/linux/linux_ipc.c index 3e810c303a2c..51bf18dcd097 100644 --- a/sys/compat/linux/linux_ipc.c +++ b/sys/compat/linux/linux_ipc.c @@ -1,913 +1,958 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "opt_compat.h" #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include +#include #include /* * old, pre 2.4 kernel */ struct l_ipc_perm { l_key_t key; l_uid16_t uid; l_gid16_t gid; l_uid16_t cuid; l_gid16_t cgid; l_ushort mode; l_ushort seq; }; struct l_seminfo { l_int semmap; l_int semmni; l_int semmns; l_int semmnu; l_int semmsl; l_int semopm; l_int semume; l_int semusz; l_int semvmx; l_int semaem; }; struct l_shminfo { l_int shmmax; l_int shmmin; l_int shmmni; l_int shmseg; l_int shmall; }; struct l_shm_info { l_int used_ids; l_ulong shm_tot; /* total allocated shm */ l_ulong shm_rss; /* total resident shm */ l_ulong shm_swp; /* total swapped shm */ l_ulong swap_attempts; l_ulong swap_successes; }; struct l_msginfo { l_int msgpool; l_int msgmap; l_int msgmax; l_int msgmnb; l_int msgmni; l_int msgssz; l_int msgtql; l_ushort msgseg; }; static void bsd_to_linux_shminfo( struct shminfo *bpp, struct l_shminfo64 *lpp) { lpp->shmmax = bpp->shmmax; lpp->shmmin = bpp->shmmin; lpp->shmmni = bpp->shmmni; lpp->shmseg = bpp->shmseg; lpp->shmall = bpp->shmall; } static void bsd_to_linux_shm_info( struct shm_info *bpp, struct l_shm_info *lpp) { lpp->used_ids = bpp->used_ids; lpp->shm_tot = bpp->shm_tot; lpp->shm_rss = bpp->shm_rss; lpp->shm_swp = bpp->shm_swp; lpp->swap_attempts = bpp->swap_attempts; lpp->swap_successes = bpp->swap_successes; } static void linux_to_bsd_ipc_perm(struct l_ipc64_perm *lpp, struct ipc_perm *bpp) { bpp->key = lpp->key; bpp->uid = lpp->uid; bpp->gid = lpp->gid; bpp->cuid = lpp->cuid; bpp->cgid = lpp->cgid; bpp->mode = lpp->mode; bpp->seq = lpp->seq; } static void bsd_to_linux_ipc_perm(struct ipc_perm *bpp, struct l_ipc64_perm *lpp) { lpp->key = bpp->key; lpp->uid = bpp->uid; lpp->gid = bpp->gid; lpp->cuid = bpp->cuid; lpp->cgid = bpp->cgid; lpp->mode = bpp->mode & (S_IRWXU|S_IRWXG|S_IRWXO); lpp->seq = bpp->seq; } struct l_msqid_ds { struct l_ipc_perm msg_perm; l_uintptr_t msg_first; /* first message on queue,unused */ l_uintptr_t msg_last; /* last message in queue,unused */ l_time_t msg_stime; /* last msgsnd time */ l_time_t msg_rtime; /* last msgrcv time */ l_time_t msg_ctime; /* last change time */ l_ulong msg_lcbytes; /* Reuse junk fields for 32 bit */ l_ulong msg_lqbytes; /* ditto */ l_ushort msg_cbytes; /* current number of bytes on queue */ l_ushort msg_qnum; /* number of messages in queue */ l_ushort msg_qbytes; /* max number of bytes on queue */ l_pid_t msg_lspid; /* pid of last msgsnd */ l_pid_t msg_lrpid; /* last receive pid */ }; struct l_semid_ds { struct l_ipc_perm sem_perm; l_time_t sem_otime; l_time_t sem_ctime; l_uintptr_t sem_base; l_uintptr_t sem_pending; l_uintptr_t sem_pending_last; l_uintptr_t undo; l_ushort sem_nsems; }; struct l_shmid_ds { struct l_ipc_perm shm_perm; l_int shm_segsz; l_time_t shm_atime; l_time_t shm_dtime; l_time_t shm_ctime; l_ushort shm_cpid; l_ushort shm_lpid; l_short shm_nattch; l_ushort private1; l_uintptr_t private2; l_uintptr_t private3; }; static void linux_to_bsd_semid_ds(struct l_semid64_ds *lsp, struct semid_ds *bsp) { linux_to_bsd_ipc_perm(&lsp->sem_perm, &bsp->sem_perm); bsp->sem_otime = lsp->sem_otime; bsp->sem_ctime = lsp->sem_ctime; bsp->sem_nsems = lsp->sem_nsems; } static void bsd_to_linux_semid_ds(struct semid_ds *bsp, struct l_semid64_ds *lsp) { bsd_to_linux_ipc_perm(&bsp->sem_perm, &lsp->sem_perm); lsp->sem_otime = bsp->sem_otime; lsp->sem_ctime = bsp->sem_ctime; lsp->sem_nsems = bsp->sem_nsems; } static void linux_to_bsd_shmid_ds(struct l_shmid64_ds *lsp, struct shmid_ds *bsp) { linux_to_bsd_ipc_perm(&lsp->shm_perm, &bsp->shm_perm); bsp->shm_segsz = lsp->shm_segsz; bsp->shm_lpid = lsp->shm_lpid; bsp->shm_cpid = lsp->shm_cpid; bsp->shm_nattch = lsp->shm_nattch; bsp->shm_atime = lsp->shm_atime; bsp->shm_dtime = lsp->shm_dtime; bsp->shm_ctime = lsp->shm_ctime; } static void bsd_to_linux_shmid_ds(struct shmid_ds *bsp, struct l_shmid64_ds *lsp) { bsd_to_linux_ipc_perm(&bsp->shm_perm, &lsp->shm_perm); lsp->shm_segsz = bsp->shm_segsz; lsp->shm_lpid = bsp->shm_lpid; lsp->shm_cpid = bsp->shm_cpid; lsp->shm_nattch = bsp->shm_nattch; lsp->shm_atime = bsp->shm_atime; lsp->shm_dtime = bsp->shm_dtime; lsp->shm_ctime = bsp->shm_ctime; } static void linux_to_bsd_msqid_ds(struct l_msqid64_ds *lsp, struct msqid_ds *bsp) { linux_to_bsd_ipc_perm(&lsp->msg_perm, &bsp->msg_perm); bsp->msg_cbytes = lsp->msg_cbytes; bsp->msg_qnum = lsp->msg_qnum; bsp->msg_qbytes = lsp->msg_qbytes; bsp->msg_lspid = lsp->msg_lspid; bsp->msg_lrpid = lsp->msg_lrpid; bsp->msg_stime = lsp->msg_stime; bsp->msg_rtime = lsp->msg_rtime; bsp->msg_ctime = lsp->msg_ctime; } static void bsd_to_linux_msqid_ds(struct msqid_ds *bsp, struct l_msqid64_ds *lsp) { bsd_to_linux_ipc_perm(&bsp->msg_perm, &lsp->msg_perm); lsp->msg_cbytes = bsp->msg_cbytes; lsp->msg_qnum = bsp->msg_qnum; lsp->msg_qbytes = bsp->msg_qbytes; lsp->msg_lspid = bsp->msg_lspid; lsp->msg_lrpid = bsp->msg_lrpid; lsp->msg_stime = bsp->msg_stime; lsp->msg_rtime = bsp->msg_rtime; lsp->msg_ctime = bsp->msg_ctime; } static int linux_ipc64_perm_to_ipc_perm(struct l_ipc64_perm *in, struct l_ipc_perm *out) { out->key = in->key; out->uid = in->uid; out->gid = in->gid; out->cuid = in->cuid; out->cgid = in->cgid; out->mode = in->mode; out->seq = in->seq; /* Linux does not check overflow */ if (out->uid != in->uid || out->gid != in->gid || out->cuid != in->cuid || out->cgid != in->cgid || out->mode != in->mode) return (EOVERFLOW); else return (0); } static int linux_msqid_pullup(l_int ver, struct l_msqid64_ds *linux_msqid64, caddr_t uaddr) { struct l_msqid_ds linux_msqid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyin(uaddr, linux_msqid64, sizeof(*linux_msqid64))); else { error = copyin(uaddr, &linux_msqid, sizeof(linux_msqid)); if (error != 0) return (error); bzero(linux_msqid64, sizeof(*linux_msqid64)); linux_msqid64->msg_perm.uid = linux_msqid.msg_perm.uid; linux_msqid64->msg_perm.gid = linux_msqid.msg_perm.gid; linux_msqid64->msg_perm.mode = linux_msqid.msg_perm.mode; if (linux_msqid.msg_qbytes == 0) linux_msqid64->msg_qbytes = linux_msqid.msg_lqbytes; else linux_msqid64->msg_qbytes = linux_msqid.msg_qbytes; return (0); } } static int linux_msqid_pushdown(l_int ver, struct l_msqid64_ds *linux_msqid64, caddr_t uaddr) { struct l_msqid_ds linux_msqid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyout(linux_msqid64, uaddr, sizeof(*linux_msqid64))); else { bzero(&linux_msqid, sizeof(linux_msqid)); error = linux_ipc64_perm_to_ipc_perm(&linux_msqid64->msg_perm, &linux_msqid.msg_perm); if (error != 0) return (error); linux_msqid.msg_stime = linux_msqid64->msg_stime; linux_msqid.msg_rtime = linux_msqid64->msg_rtime; linux_msqid.msg_ctime = linux_msqid64->msg_ctime; if (linux_msqid64->msg_cbytes > USHRT_MAX) linux_msqid.msg_cbytes = USHRT_MAX; else linux_msqid.msg_cbytes = linux_msqid64->msg_cbytes; linux_msqid.msg_lcbytes = linux_msqid64->msg_cbytes; if (linux_msqid64->msg_qnum > USHRT_MAX) linux_msqid.msg_qnum = USHRT_MAX; else linux_msqid.msg_qnum = linux_msqid64->msg_qnum; if (linux_msqid64->msg_qbytes > USHRT_MAX) linux_msqid.msg_qbytes = USHRT_MAX; else linux_msqid.msg_qbytes = linux_msqid64->msg_qbytes; linux_msqid.msg_lqbytes = linux_msqid64->msg_qbytes; linux_msqid.msg_lspid = linux_msqid64->msg_lspid; linux_msqid.msg_lrpid = linux_msqid64->msg_lrpid; /* Linux does not check overflow */ if (linux_msqid.msg_stime != linux_msqid64->msg_stime || linux_msqid.msg_rtime != linux_msqid64->msg_rtime || linux_msqid.msg_ctime != linux_msqid64->msg_ctime) return (EOVERFLOW); return (copyout(&linux_msqid, uaddr, sizeof(linux_msqid))); } } static int linux_semid_pullup(l_int ver, struct l_semid64_ds *linux_semid64, caddr_t uaddr) { struct l_semid_ds linux_semid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyin(uaddr, linux_semid64, sizeof(*linux_semid64))); else { error = copyin(uaddr, &linux_semid, sizeof(linux_semid)); if (error != 0) return (error); bzero(linux_semid64, sizeof(*linux_semid64)); linux_semid64->sem_perm.uid = linux_semid.sem_perm.uid; linux_semid64->sem_perm.gid = linux_semid.sem_perm.gid; linux_semid64->sem_perm.mode = linux_semid.sem_perm.mode; return (0); } } static int linux_semid_pushdown(l_int ver, struct l_semid64_ds *linux_semid64, caddr_t uaddr) { struct l_semid_ds linux_semid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyout(linux_semid64, uaddr, sizeof(*linux_semid64))); else { bzero(&linux_semid, sizeof(linux_semid)); error = linux_ipc64_perm_to_ipc_perm(&linux_semid64->sem_perm, &linux_semid.sem_perm); if (error != 0) return (error); linux_semid.sem_otime = linux_semid64->sem_otime; linux_semid.sem_ctime = linux_semid64->sem_ctime; linux_semid.sem_nsems = linux_semid64->sem_nsems; /* Linux does not check overflow */ if (linux_semid.sem_otime != linux_semid64->sem_otime || linux_semid.sem_ctime != linux_semid64->sem_ctime || linux_semid.sem_nsems != linux_semid64->sem_nsems) return (EOVERFLOW); return (copyout(&linux_semid, uaddr, sizeof(linux_semid))); } } static int linux_shmid_pullup(l_int ver, struct l_shmid64_ds *linux_shmid64, caddr_t uaddr) { struct l_shmid_ds linux_shmid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyin(uaddr, linux_shmid64, sizeof(*linux_shmid64))); else { error = copyin(uaddr, &linux_shmid, sizeof(linux_shmid)); if (error != 0) return (error); bzero(linux_shmid64, sizeof(*linux_shmid64)); linux_shmid64->shm_perm.uid = linux_shmid.shm_perm.uid; linux_shmid64->shm_perm.gid = linux_shmid.shm_perm.gid; linux_shmid64->shm_perm.mode = linux_shmid.shm_perm.mode; return (0); } } static int linux_shmid_pushdown(l_int ver, struct l_shmid64_ds *linux_shmid64, caddr_t uaddr) { struct l_shmid_ds linux_shmid; int error; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyout(linux_shmid64, uaddr, sizeof(*linux_shmid64))); else { bzero(&linux_shmid, sizeof(linux_shmid)); error = linux_ipc64_perm_to_ipc_perm(&linux_shmid64->shm_perm, &linux_shmid.shm_perm); if (error != 0) return (error); linux_shmid.shm_segsz = linux_shmid64->shm_segsz; linux_shmid.shm_atime = linux_shmid64->shm_atime; linux_shmid.shm_dtime = linux_shmid64->shm_dtime; linux_shmid.shm_ctime = linux_shmid64->shm_ctime; linux_shmid.shm_cpid = linux_shmid64->shm_cpid; linux_shmid.shm_lpid = linux_shmid64->shm_lpid; linux_shmid.shm_nattch = linux_shmid64->shm_nattch; /* Linux does not check overflow */ if (linux_shmid.shm_segsz != linux_shmid64->shm_segsz || linux_shmid.shm_atime != linux_shmid64->shm_atime || linux_shmid.shm_dtime != linux_shmid64->shm_dtime || linux_shmid.shm_ctime != linux_shmid64->shm_ctime || linux_shmid.shm_cpid != linux_shmid64->shm_cpid || linux_shmid.shm_lpid != linux_shmid64->shm_lpid || linux_shmid.shm_nattch != linux_shmid64->shm_nattch) return (EOVERFLOW); return (copyout(&linux_shmid, uaddr, sizeof(linux_shmid))); } } static int linux_shminfo_pushdown(l_int ver, struct l_shminfo64 *linux_shminfo64, caddr_t uaddr) { struct l_shminfo linux_shminfo; if (ver == LINUX_IPC_64 || SV_CURPROC_FLAG(SV_LP64)) return (copyout(linux_shminfo64, uaddr, sizeof(*linux_shminfo64))); else { bzero(&linux_shminfo, sizeof(linux_shminfo)); linux_shminfo.shmmax = linux_shminfo64->shmmax; linux_shminfo.shmmin = linux_shminfo64->shmmin; linux_shminfo.shmmni = linux_shminfo64->shmmni; linux_shminfo.shmseg = linux_shminfo64->shmseg; linux_shminfo.shmall = linux_shminfo64->shmall; return (copyout(&linux_shminfo, uaddr, sizeof(linux_shminfo))); } } +#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) +int +linux_semtimedop_time64(struct thread *td, struct linux_semtimedop_time64_args *args) +{ + struct timespec ts, *tsa; + struct l_timespec64 lts; + int error; + + if (args->timeout) { + if ((error = copyin(args->timeout, <s, sizeof(lts)))) + return (error); + error = linux_to_native_timespec64(&ts, <s); + if (error != 0) + return (error); + tsa = &ts; + } else + tsa = NULL; + + return (kern_semop(td, args->semid, PTRIN(args->tsops), + args->nsops, tsa)); +} +#endif /* __i386__) || (__amd64__ && COMPAT_LINUX32) */ + +int +linux_semtimedop(struct thread *td, struct linux_semtimedop_args *args) +{ + struct timespec ts, *tsa; + struct l_timespec lts; + int error; + + if (args->timeout) { + if ((error = copyin(args->timeout, <s, sizeof(lts)))) + return (error); + error = linux_to_native_timespec(&ts, <s); + if (error != 0) + return (error); + tsa = &ts; + } else + tsa = NULL; + + return (kern_semop(td, args->semid, PTRIN(args->tsops), + args->nsops, tsa)); +} + int linux_semget(struct thread *td, struct linux_semget_args *args) { struct semget_args /* { key_t key; int nsems; int semflg; } */ bsd_args; if (args->nsems < 0) return (EINVAL); bsd_args.key = args->key; bsd_args.nsems = args->nsems; bsd_args.semflg = args->semflg; return (sys_semget(td, &bsd_args)); } int linux_semctl(struct thread *td, struct linux_semctl_args *args) { struct l_semid64_ds linux_semid64; struct l_seminfo linux_seminfo; struct semid_ds semid; union semun semun; register_t rval; int cmd, error; memset(&linux_seminfo, 0, sizeof(linux_seminfo)); memset(&linux_semid64, 0, sizeof(linux_semid64)); switch (args->cmd & ~LINUX_IPC_64) { case LINUX_IPC_RMID: cmd = IPC_RMID; break; case LINUX_GETNCNT: cmd = GETNCNT; break; case LINUX_GETPID: cmd = GETPID; break; case LINUX_GETVAL: cmd = GETVAL; break; case LINUX_GETZCNT: cmd = GETZCNT; break; case LINUX_SETVAL: cmd = SETVAL; semun.val = args->arg.val; break; case LINUX_IPC_SET: cmd = IPC_SET; error = linux_semid_pullup(args->cmd & LINUX_IPC_64, &linux_semid64, PTRIN(args->arg.buf)); if (error != 0) return (error); linux_to_bsd_semid_ds(&linux_semid64, &semid); semun.buf = &semid; return (kern_semctl(td, args->semid, args->semnum, cmd, &semun, td->td_retval)); case LINUX_IPC_STAT: cmd = IPC_STAT; semun.buf = &semid; error = kern_semctl(td, args->semid, args->semnum, cmd, &semun, &rval); if (error != 0) return (error); bsd_to_linux_semid_ds(&semid, &linux_semid64); return (linux_semid_pushdown(args->cmd & LINUX_IPC_64, &linux_semid64, PTRIN(args->arg.buf))); case LINUX_SEM_STAT: cmd = SEM_STAT; semun.buf = &semid; error = kern_semctl(td, args->semid, args->semnum, cmd, &semun, &rval); if (error != 0) return (error); bsd_to_linux_semid_ds(&semid, &linux_semid64); error = linux_semid_pushdown(args->cmd & LINUX_IPC_64, &linux_semid64, PTRIN(args->arg.buf)); if (error == 0) td->td_retval[0] = rval; return (error); case LINUX_IPC_INFO: case LINUX_SEM_INFO: bcopy(&seminfo, &linux_seminfo.semmni, sizeof(linux_seminfo) - sizeof(linux_seminfo.semmap) ); /* * Linux does not use the semmap field but populates it with * the defined value from SEMMAP, which really is redefined to * SEMMNS, which they define as SEMMNI * SEMMSL. Try to * simulate this returning our dynamic semmns value. */ linux_seminfo.semmap = linux_seminfo.semmns; /* XXX BSD equivalent? #define used_semids 10 #define used_sems 10 linux_seminfo.semusz = used_semids; linux_seminfo.semaem = used_sems; */ error = copyout(&linux_seminfo, PTRIN(args->arg.buf), sizeof(linux_seminfo)); if (error != 0) return (error); /* * TODO: Linux return the last assigned id, not the semmni. */ td->td_retval[0] = seminfo.semmni; return (0); case LINUX_GETALL: cmd = GETALL; semun.array = PTRIN(args->arg.array); break; case LINUX_SETALL: cmd = SETALL; semun.array = PTRIN(args->arg.array); break; default: linux_msg(td, "ipc type %d is not implemented", args->cmd & ~LINUX_IPC_64); return (EINVAL); } return (kern_semctl(td, args->semid, args->semnum, cmd, &semun, td->td_retval)); } int linux_msgsnd(struct thread *td, struct linux_msgsnd_args *args) { const void *msgp; long mtype; l_long lmtype; int error; if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax) return (EINVAL); msgp = PTRIN(args->msgp); if ((error = copyin(msgp, &lmtype, sizeof(lmtype))) != 0) return (error); mtype = (long)lmtype; return (kern_msgsnd(td, args->msqid, (const char *)msgp + sizeof(lmtype), args->msgsz, args->msgflg, mtype)); } int linux_msgrcv(struct thread *td, struct linux_msgrcv_args *args) { void *msgp; long mtype; l_long lmtype; int error; if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax) return (EINVAL); msgp = PTRIN(args->msgp); if ((error = kern_msgrcv(td, args->msqid, (char *)msgp + sizeof(lmtype), args->msgsz, args->msgtyp, args->msgflg, &mtype)) != 0) return (error); lmtype = (l_long)mtype; return (copyout(&lmtype, msgp, sizeof(lmtype))); } int linux_msgget(struct thread *td, struct linux_msgget_args *args) { struct msgget_args /* { key_t key; int msgflg; } */ bsd_args; bsd_args.key = args->key; bsd_args.msgflg = args->msgflg; return (sys_msgget(td, &bsd_args)); } int linux_msgctl(struct thread *td, struct linux_msgctl_args *args) { int error, bsd_cmd; struct l_msqid64_ds linux_msqid64; struct msqid_ds bsd_msqid; memset(&linux_msqid64, 0, sizeof(linux_msqid64)); bsd_cmd = args->cmd & ~LINUX_IPC_64; switch (bsd_cmd) { case LINUX_IPC_INFO: case LINUX_MSG_INFO: { struct l_msginfo linux_msginfo; memset(&linux_msginfo, 0, sizeof(linux_msginfo)); /* * XXX MSG_INFO uses the same data structure but returns different * dynamic counters in msgpool, msgmap, and msgtql fields. */ linux_msginfo.msgpool = (long)msginfo.msgmni * (long)msginfo.msgmnb / 1024L; /* XXX MSG_INFO. */ linux_msginfo.msgmap = msginfo.msgmnb; /* XXX MSG_INFO. */ linux_msginfo.msgmax = msginfo.msgmax; linux_msginfo.msgmnb = msginfo.msgmnb; linux_msginfo.msgmni = msginfo.msgmni; linux_msginfo.msgssz = msginfo.msgssz; linux_msginfo.msgtql = msginfo.msgtql; /* XXX MSG_INFO. */ linux_msginfo.msgseg = msginfo.msgseg; error = copyout(&linux_msginfo, PTRIN(args->buf), sizeof(linux_msginfo)); if (error == 0) td->td_retval[0] = msginfo.msgmni; /* XXX */ return (error); } /* * TODO: implement this * case LINUX_MSG_STAT: */ case LINUX_IPC_STAT: /* NOTHING */ break; case LINUX_IPC_SET: error = linux_msqid_pullup(args->cmd & LINUX_IPC_64, &linux_msqid64, PTRIN(args->buf)); if (error != 0) return (error); linux_to_bsd_msqid_ds(&linux_msqid64, &bsd_msqid); break; case LINUX_IPC_RMID: /* NOTHING */ break; default: return (EINVAL); break; } error = kern_msgctl(td, args->msqid, bsd_cmd, &bsd_msqid); if (error != 0) { if (bsd_cmd == LINUX_IPC_RMID && error == EACCES) return (EPERM); if (bsd_cmd != LINUX_IPC_RMID || error != EINVAL) return (error); } if (bsd_cmd == LINUX_IPC_STAT) { bsd_to_linux_msqid_ds(&bsd_msqid, &linux_msqid64); return (linux_msqid_pushdown(args->cmd & LINUX_IPC_64, &linux_msqid64, PTRIN(args->buf))); } return (0); } int linux_shmat(struct thread *td, struct linux_shmat_args *args) { struct shmat_args /* { int shmid; void *shmaddr; int shmflg; } */ bsd_args; bsd_args.shmid = args->shmid; bsd_args.shmaddr = PTRIN(args->shmaddr); bsd_args.shmflg = args->shmflg; return (sys_shmat(td, &bsd_args)); } int linux_shmdt(struct thread *td, struct linux_shmdt_args *args) { struct shmdt_args /* { void *shmaddr; } */ bsd_args; bsd_args.shmaddr = PTRIN(args->shmaddr); return (sys_shmdt(td, &bsd_args)); } int linux_shmget(struct thread *td, struct linux_shmget_args *args) { struct shmget_args /* { key_t key; int size; int shmflg; } */ bsd_args; bsd_args.key = args->key; bsd_args.size = args->size; bsd_args.shmflg = args->shmflg; return (sys_shmget(td, &bsd_args)); } int linux_shmctl(struct thread *td, struct linux_shmctl_args *args) { struct l_shmid64_ds linux_shmid64; struct l_shminfo64 linux_shminfo64; struct l_shm_info linux_shm_info; struct shmid_ds bsd_shmid; int error; memset(&linux_shm_info, 0, sizeof(linux_shm_info)); memset(&linux_shmid64, 0, sizeof(linux_shmid64)); memset(&linux_shminfo64, 0, sizeof(linux_shminfo64)); switch (args->cmd & ~LINUX_IPC_64) { case LINUX_IPC_INFO: { struct shminfo bsd_shminfo; /* Perform shmctl wanting removed segments lookup */ error = kern_shmctl(td, args->shmid, IPC_INFO, (void *)&bsd_shminfo, NULL); if (error != 0) return (error); bsd_to_linux_shminfo(&bsd_shminfo, &linux_shminfo64); return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64, &linux_shminfo64, PTRIN(args->buf))); } case LINUX_SHM_INFO: { struct shm_info bsd_shm_info; /* Perform shmctl wanting removed segments lookup */ error = kern_shmctl(td, args->shmid, SHM_INFO, (void *)&bsd_shm_info, NULL); if (error != 0) return (error); bsd_to_linux_shm_info(&bsd_shm_info, &linux_shm_info); return (copyout(&linux_shm_info, PTRIN(args->buf), sizeof(struct l_shm_info))); } case LINUX_IPC_STAT: /* Perform shmctl wanting removed segments lookup */ error = kern_shmctl(td, args->shmid, IPC_STAT, (void *)&bsd_shmid, NULL); if (error != 0) return (error); bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid64); return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, &linux_shmid64, PTRIN(args->buf))); case LINUX_SHM_STAT: /* Perform shmctl wanting removed segments lookup */ error = kern_shmctl(td, args->shmid, IPC_STAT, (void *)&bsd_shmid, NULL); if (error != 0) return (error); bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid64); return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, &linux_shmid64, PTRIN(args->buf))); case LINUX_IPC_SET: error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, &linux_shmid64, PTRIN(args->buf)); if (error != 0) return (error); linux_to_bsd_shmid_ds(&linux_shmid64, &bsd_shmid); /* Perform shmctl wanting removed segments lookup */ return (kern_shmctl(td, args->shmid, IPC_SET, (void *)&bsd_shmid, NULL)); case LINUX_IPC_RMID: { void *buf; if (args->buf == 0) buf = NULL; else { error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, &linux_shmid64, PTRIN(args->buf)); if (error != 0) return (error); linux_to_bsd_shmid_ds(&linux_shmid64, &bsd_shmid); buf = (void *)&bsd_shmid; } return (kern_shmctl(td, args->shmid, IPC_RMID, buf, NULL)); } case LINUX_SHM_LOCK: /* FALLTHROUGH */ case LINUX_SHM_UNLOCK: /* FALLTHROUGH */ default: linux_msg(td, "ipc type %d not implemented", args->cmd & ~LINUX_IPC_64); return (EINVAL); } } MODULE_DEPEND(linux, sysvmsg, 1, 1, 1); MODULE_DEPEND(linux, sysvsem, 1, 1, 1); MODULE_DEPEND(linux, sysvshm, 1, 1, 1); diff --git a/sys/compat/linux/linux_ipc.h b/sys/compat/linux/linux_ipc.h index beef18a09507..a148814b8273 100644 --- a/sys/compat/linux/linux_ipc.h +++ b/sys/compat/linux/linux_ipc.h @@ -1,85 +1,98 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Marcel Moolenaar * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD$ */ #ifndef _LINUX_IPC_H_ #define _LINUX_IPC_H_ /* * SystemV IPC defines */ #define LINUX_SEMOP 1 #define LINUX_SEMGET 2 #define LINUX_SEMCTL 3 +#define LINUX_SEMTIMEDOP 4 #define LINUX_MSGSND 11 #define LINUX_MSGRCV 12 #define LINUX_MSGGET 13 #define LINUX_MSGCTL 14 #define LINUX_SHMAT 21 #define LINUX_SHMDT 22 #define LINUX_SHMGET 23 #define LINUX_SHMCTL 24 #define LINUX_IPC_RMID 0 #define LINUX_IPC_SET 1 #define LINUX_IPC_STAT 2 #define LINUX_IPC_INFO 3 #define LINUX_MSG_INFO 12 #define LINUX_SHM_LOCK 11 #define LINUX_SHM_UNLOCK 12 #define LINUX_SHM_STAT 13 #define LINUX_SHM_INFO 14 #define LINUX_SHM_RDONLY 0x1000 #define LINUX_SHM_RND 0x2000 #define LINUX_SHM_REMAP 0x4000 /* semctl commands */ #define LINUX_GETPID 11 #define LINUX_GETVAL 12 #define LINUX_GETALL 13 #define LINUX_GETNCNT 14 #define LINUX_GETZCNT 15 #define LINUX_SETVAL 16 #define LINUX_SETALL 17 #define LINUX_SEM_STAT 18 #define LINUX_SEM_INFO 19 /* * Version flags for semctl, msgctl, and shmctl commands * These are passed as bitflags or-ed with the actual command */ #define LINUX_IPC_OLD 0 /* Old version (no 32-bit UID support on many architectures) */ #define LINUX_IPC_64 0x0100 /* New version (support 32-bit UIDs, bigger message sizes, etc. */ +#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) +struct linux_semtimedop_args +{ + l_int semid; + struct sembuf *tsops; + l_uint nsops; + struct l_timespec *timeout; +}; + +int linux_semtimedop(struct thread *, struct linux_semtimedop_args *); +#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ + #endif /* _LINUX_IPC_H_ */ diff --git a/sys/i386/linux/linux_dummy_machdep.c b/sys/i386/linux/linux_dummy_machdep.c index dd89444537d0..0e41a4b3fc56 100644 --- a/sys/i386/linux/linux_dummy_machdep.c +++ b/sys/i386/linux/linux_dummy_machdep.c @@ -1,76 +1,75 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); /* * Before adding new stubs to this file, please check if a stub can be added to * the machine-independent code in sys/compat/linux/linux_dummy.c (or * sys/x86/linux/linux_dummy_x86.c). */ UNIMPLEMENTED(break); UNIMPLEMENTED(ftime); UNIMPLEMENTED(gtty); UNIMPLEMENTED(stty); UNIMPLEMENTED(lock); UNIMPLEMENTED(mpx); UNIMPLEMENTED(prof); UNIMPLEMENTED(profil); UNIMPLEMENTED(ulimit); DUMMY(bdflush); DUMMY(fstat); DUMMY(olduname); DUMMY(stime); DUMMY(uname); DUMMY(vm86); DUMMY(vm86old); /* Linux 4.11: */ DUMMY(arch_prctl); /* Linux 5.0: */ DUMMY(clock_adjtime64); DUMMY(io_pgetevents_time64); DUMMY(mq_timedsend_time64); DUMMY(mq_timedreceive_time64); -DUMMY(semtimedop_time64); diff --git a/sys/i386/linux/linux_machdep.c b/sys/i386/linux/linux_machdep.c index 09d4eb42c772..1e665eb9f505 100644 --- a/sys/i386/linux/linux_machdep.c +++ b/sys/i386/linux/linux_machdep.c @@ -1,693 +1,702 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Marcel Moolenaar * 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. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* needed for pcb definition in linux_set_thread_area */ #include "opt_posix.h" struct l_descriptor { l_uint entry_number; l_ulong base_addr; l_uint limit; l_uint seg_32bit:1; l_uint contents:2; l_uint read_exec_only:1; l_uint limit_in_pages:1; l_uint seg_not_present:1; l_uint useable:1; }; struct l_old_select_argv { l_int nfds; l_fd_set *readfds; l_fd_set *writefds; l_fd_set *exceptfds; struct l_timeval *timeout; }; int linux_execve(struct thread *td, struct linux_execve_args *args) { struct image_args eargs; char *newpath; int error; if (!LUSECONVPATH(td)) { error = exec_copyin_args(&eargs, args->path, UIO_USERSPACE, args->argp, args->envp); } else { LCONVPATHEXIST(args->path, &newpath); error = exec_copyin_args(&eargs, newpath, UIO_SYSSPACE, args->argp, args->envp); LFREEPATH(newpath); } if (error == 0) error = linux_common_execve(td, &eargs); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } struct l_ipc_kludge { struct l_msgbuf *msgp; l_long msgtyp; }; int linux_ipc(struct thread *td, struct linux_ipc_args *args) { switch (args->what & 0xFFFF) { case LINUX_SEMOP: { return (kern_semop(td, args->arg1, PTRIN(args->ptr), args->arg2, NULL)); } case LINUX_SEMGET: { struct linux_semget_args a; a.key = args->arg1; a.nsems = args->arg2; a.semflg = args->arg3; return (linux_semget(td, &a)); } case LINUX_SEMCTL: { struct linux_semctl_args a; int error; a.semid = args->arg1; a.semnum = args->arg2; a.cmd = args->arg3; error = copyin(PTRIN(args->ptr), &a.arg, sizeof(a.arg)); if (error) return (error); return (linux_semctl(td, &a)); } + case LINUX_SEMTIMEDOP: { + struct linux_semtimedop_args a; + + a.semid = args->arg1; + a.tsops = PTRIN(args->ptr); + a.nsops = args->arg2; + a.timeout = PTRIN(args->arg5); + return (linux_semtimedop(td, &a)); + } case LINUX_MSGSND: { struct linux_msgsnd_args a; a.msqid = args->arg1; a.msgp = PTRIN(args->ptr); a.msgsz = args->arg2; a.msgflg = args->arg3; return (linux_msgsnd(td, &a)); } case LINUX_MSGRCV: { struct linux_msgrcv_args a; a.msqid = args->arg1; a.msgsz = args->arg2; a.msgflg = args->arg3; if ((args->what >> 16) == 0) { struct l_ipc_kludge tmp; int error; if (args->ptr == 0) return (EINVAL); error = copyin(PTRIN(args->ptr), &tmp, sizeof(tmp)); if (error) return (error); a.msgp = PTRIN(tmp.msgp); a.msgtyp = tmp.msgtyp; } else { a.msgp = PTRIN(args->ptr); a.msgtyp = args->arg5; } return (linux_msgrcv(td, &a)); } case LINUX_MSGGET: { struct linux_msgget_args a; a.key = args->arg1; a.msgflg = args->arg2; return (linux_msgget(td, &a)); } case LINUX_MSGCTL: { struct linux_msgctl_args a; a.msqid = args->arg1; a.cmd = args->arg2; a.buf = PTRIN(args->ptr); return (linux_msgctl(td, &a)); } case LINUX_SHMAT: { struct linux_shmat_args a; l_uintptr_t addr; int error; a.shmid = args->arg1; a.shmaddr = PTRIN(args->ptr); a.shmflg = args->arg2; error = linux_shmat(td, &a); if (error != 0) return (error); addr = td->td_retval[0]; error = copyout(&addr, PTRIN(args->arg3), sizeof(addr)); td->td_retval[0] = 0; return (error); } case LINUX_SHMDT: { struct linux_shmdt_args a; a.shmaddr = PTRIN(args->ptr); return (linux_shmdt(td, &a)); } case LINUX_SHMGET: { struct linux_shmget_args a; a.key = args->arg1; a.size = args->arg2; a.shmflg = args->arg3; return (linux_shmget(td, &a)); } case LINUX_SHMCTL: { struct linux_shmctl_args a; a.shmid = args->arg1; a.cmd = args->arg2; a.buf = PTRIN(args->ptr); return (linux_shmctl(td, &a)); } default: break; } return (EINVAL); } int linux_old_select(struct thread *td, struct linux_old_select_args *args) { struct l_old_select_argv linux_args; struct linux_select_args newsel; int error; error = copyin(args->ptr, &linux_args, sizeof(linux_args)); if (error) return (error); newsel.nfds = linux_args.nfds; newsel.readfds = linux_args.readfds; newsel.writefds = linux_args.writefds; newsel.exceptfds = linux_args.exceptfds; newsel.timeout = linux_args.timeout; return (linux_select(td, &newsel)); } int linux_set_cloned_tls(struct thread *td, void *desc) { struct segment_descriptor sd; struct l_user_desc info; int idx, error; int a[2]; error = copyin(desc, &info, sizeof(struct l_user_desc)); if (error) { linux_msg(td, "set_cloned_tls copyin failed!"); } else { idx = info.entry_number; /* * looks like we're getting the idx we returned * in the set_thread_area() syscall */ if (idx != 6 && idx != 3) { linux_msg(td, "set_cloned_tls resetting idx!"); idx = 3; } /* this doesnt happen in practice */ if (idx == 6) { /* we might copy out the entry_number as 3 */ info.entry_number = 3; error = copyout(&info, desc, sizeof(struct l_user_desc)); if (error) linux_msg(td, "set_cloned_tls copyout failed!"); } a[0] = LINUX_LDT_entry_a(&info); a[1] = LINUX_LDT_entry_b(&info); memcpy(&sd, &a, sizeof(a)); /* set %gs */ td->td_pcb->pcb_gsd = sd; td->td_pcb->pcb_gs = GSEL(GUGS_SEL, SEL_UPL); } return (error); } int linux_set_upcall(struct thread *td, register_t stack) { if (stack) td->td_frame->tf_esp = stack; /* * The newly created Linux thread returns * to the user space by the same path that a parent do. */ td->td_frame->tf_eax = 0; return (0); } int linux_mmap2(struct thread *td, struct linux_mmap2_args *args) { return (linux_mmap_common(td, args->addr, args->len, args->prot, args->flags, args->fd, (uint64_t)(uint32_t)args->pgoff * PAGE_SIZE)); } int linux_mmap(struct thread *td, struct linux_mmap_args *args) { int error; struct l_mmap_argv linux_args; error = copyin(args->ptr, &linux_args, sizeof(linux_args)); if (error) return (error); return (linux_mmap_common(td, linux_args.addr, linux_args.len, linux_args.prot, linux_args.flags, linux_args.fd, (uint32_t)linux_args.pgoff)); } int linux_mprotect(struct thread *td, struct linux_mprotect_args *uap) { return (linux_mprotect_common(td, PTROUT(uap->addr), uap->len, uap->prot)); } int linux_madvise(struct thread *td, struct linux_madvise_args *uap) { return (linux_madvise_common(td, PTROUT(uap->addr), uap->len, uap->behav)); } int linux_ioperm(struct thread *td, struct linux_ioperm_args *args) { int error; struct i386_ioperm_args iia; iia.start = args->start; iia.length = args->length; iia.enable = args->enable; error = i386_set_ioperm(td, &iia); return (error); } int linux_iopl(struct thread *td, struct linux_iopl_args *args) { int error; if (args->level < 0 || args->level > 3) return (EINVAL); if ((error = priv_check(td, PRIV_IO)) != 0) return (error); if ((error = securelevel_gt(td->td_ucred, 0)) != 0) return (error); td->td_frame->tf_eflags = (td->td_frame->tf_eflags & ~PSL_IOPL) | (args->level * (PSL_IOPL / 3)); return (0); } int linux_modify_ldt(struct thread *td, struct linux_modify_ldt_args *uap) { int error; struct i386_ldt_args ldt; struct l_descriptor ld; union descriptor desc; int size, written; switch (uap->func) { case 0x00: /* read_ldt */ ldt.start = 0; ldt.descs = uap->ptr; ldt.num = uap->bytecount / sizeof(union descriptor); error = i386_get_ldt(td, &ldt); td->td_retval[0] *= sizeof(union descriptor); break; case 0x02: /* read_default_ldt = 0 */ size = 5*sizeof(struct l_desc_struct); if (size > uap->bytecount) size = uap->bytecount; for (written = error = 0; written < size && error == 0; written++) error = subyte((char *)uap->ptr + written, 0); td->td_retval[0] = written; break; case 0x01: /* write_ldt */ case 0x11: /* write_ldt */ if (uap->bytecount != sizeof(ld)) return (EINVAL); error = copyin(uap->ptr, &ld, sizeof(ld)); if (error) return (error); ldt.start = ld.entry_number; ldt.descs = &desc; ldt.num = 1; desc.sd.sd_lolimit = (ld.limit & 0x0000ffff); desc.sd.sd_hilimit = (ld.limit & 0x000f0000) >> 16; desc.sd.sd_lobase = (ld.base_addr & 0x00ffffff); desc.sd.sd_hibase = (ld.base_addr & 0xff000000) >> 24; desc.sd.sd_type = SDT_MEMRO | ((ld.read_exec_only ^ 1) << 1) | (ld.contents << 2); desc.sd.sd_dpl = 3; desc.sd.sd_p = (ld.seg_not_present ^ 1); desc.sd.sd_xx = 0; desc.sd.sd_def32 = ld.seg_32bit; desc.sd.sd_gran = ld.limit_in_pages; error = i386_set_ldt(td, &ldt, &desc); break; default: error = ENOSYS; break; } if (error == EOPNOTSUPP) { linux_msg(td, "modify_ldt needs kernel option USER_LDT"); error = ENOSYS; } return (error); } int linux_sigaction(struct thread *td, struct linux_sigaction_args *args) { l_osigaction_t osa; l_sigaction_t act, oact; int error; if (args->nsa != NULL) { error = copyin(args->nsa, &osa, sizeof(l_osigaction_t)); if (error) return (error); act.lsa_handler = osa.lsa_handler; act.lsa_flags = osa.lsa_flags; act.lsa_restorer = osa.lsa_restorer; LINUX_SIGEMPTYSET(act.lsa_mask); act.lsa_mask.__mask = osa.lsa_mask; } error = linux_do_sigaction(td, args->sig, args->nsa ? &act : NULL, args->osa ? &oact : NULL); if (args->osa != NULL && !error) { osa.lsa_handler = oact.lsa_handler; osa.lsa_flags = oact.lsa_flags; osa.lsa_restorer = oact.lsa_restorer; osa.lsa_mask = oact.lsa_mask.__mask; error = copyout(&osa, args->osa, sizeof(l_osigaction_t)); } return (error); } /* * Linux has two extra args, restart and oldmask. We dont use these, * but it seems that "restart" is actually a context pointer that * enables the signal to happen with a different register set. */ int linux_sigsuspend(struct thread *td, struct linux_sigsuspend_args *args) { sigset_t sigmask; l_sigset_t mask; LINUX_SIGEMPTYSET(mask); mask.__mask = args->mask; linux_to_bsd_sigset(&mask, &sigmask); return (kern_sigsuspend(td, sigmask)); } int linux_pause(struct thread *td, struct linux_pause_args *args) { struct proc *p = td->td_proc; sigset_t sigmask; PROC_LOCK(p); sigmask = td->td_sigmask; PROC_UNLOCK(p); return (kern_sigsuspend(td, sigmask)); } int linux_set_thread_area(struct thread *td, struct linux_set_thread_area_args *args) { struct l_user_desc info; int error; int idx; int a[2]; struct segment_descriptor sd; error = copyin(args->desc, &info, sizeof(struct l_user_desc)); if (error) return (error); idx = info.entry_number; /* * Semantics of Linux version: every thread in the system has array of * 3 tls descriptors. 1st is GLIBC TLS, 2nd is WINE, 3rd unknown. This * syscall loads one of the selected tls decriptors with a value and * also loads GDT descriptors 6, 7 and 8 with the content of the * per-thread descriptors. * * Semantics of FreeBSD version: I think we can ignore that Linux has 3 * per-thread descriptors and use just the 1st one. The tls_array[] * is used only in set/get-thread_area() syscalls and for loading the * GDT descriptors. In FreeBSD we use just one GDT descriptor for TLS * so we will load just one. * * XXX: this doesn't work when a user space process tries to use more * than 1 TLS segment. Comment in the Linux sources says wine might do * this. */ /* * we support just GLIBC TLS now * we should let 3 proceed as well because we use this segment so * if code does two subsequent calls it should succeed */ if (idx != 6 && idx != -1 && idx != 3) return (EINVAL); /* * we have to copy out the GDT entry we use * FreeBSD uses GDT entry #3 for storing %gs so load that * * XXX: what if a user space program doesn't check this value and tries * to use 6, 7 or 8? */ idx = info.entry_number = 3; error = copyout(&info, args->desc, sizeof(struct l_user_desc)); if (error) return (error); if (LINUX_LDT_empty(&info)) { a[0] = 0; a[1] = 0; } else { a[0] = LINUX_LDT_entry_a(&info); a[1] = LINUX_LDT_entry_b(&info); } memcpy(&sd, &a, sizeof(a)); /* this is taken from i386 version of cpu_set_user_tls() */ critical_enter(); /* set %gs */ td->td_pcb->pcb_gsd = sd; PCPU_GET(fsgs_gdt)[1] = sd; load_gs(GSEL(GUGS_SEL, SEL_UPL)); critical_exit(); return (0); } int linux_get_thread_area(struct thread *td, struct linux_get_thread_area_args *args) { struct l_user_desc info; int error; int idx; struct l_desc_struct desc; struct segment_descriptor sd; error = copyin(args->desc, &info, sizeof(struct l_user_desc)); if (error) return (error); idx = info.entry_number; /* XXX: I am not sure if we want 3 to be allowed too. */ if (idx != 6 && idx != 3) return (EINVAL); idx = 3; memset(&info, 0, sizeof(info)); sd = PCPU_GET(fsgs_gdt)[1]; memcpy(&desc, &sd, sizeof(desc)); info.entry_number = idx; info.base_addr = LINUX_GET_BASE(&desc); info.limit = LINUX_GET_LIMIT(&desc); info.seg_32bit = LINUX_GET_32BIT(&desc); info.contents = LINUX_GET_CONTENTS(&desc); info.read_exec_only = !LINUX_GET_WRITABLE(&desc); info.limit_in_pages = LINUX_GET_LIMIT_PAGES(&desc); info.seg_not_present = !LINUX_GET_PRESENT(&desc); info.useable = LINUX_GET_USEABLE(&desc); error = copyout(&info, args->desc, sizeof(struct l_user_desc)); if (error) return (EFAULT); return (0); } /* XXX: this wont work with module - convert it */ int linux_mq_open(struct thread *td, struct linux_mq_open_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_open(td, (struct kmq_open_args *)args)); #else return (ENOSYS); #endif } int linux_mq_unlink(struct thread *td, struct linux_mq_unlink_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_unlink(td, (struct kmq_unlink_args *)args)); #else return (ENOSYS); #endif } int linux_mq_timedsend(struct thread *td, struct linux_mq_timedsend_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_timedsend(td, (struct kmq_timedsend_args *)args)); #else return (ENOSYS); #endif } int linux_mq_timedreceive(struct thread *td, struct linux_mq_timedreceive_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_timedreceive(td, (struct kmq_timedreceive_args *)args)); #else return (ENOSYS); #endif } int linux_mq_notify(struct thread *td, struct linux_mq_notify_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_notify(td, (struct kmq_notify_args *)args)); #else return (ENOSYS); #endif } int linux_mq_getsetattr(struct thread *td, struct linux_mq_getsetattr_args *args) { #ifdef P1003_1B_MQUEUE return (sys_kmq_setattr(td, (struct kmq_setattr_args *)args)); #else return (ENOSYS); #endif }