X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=linux-user%2Fsyscall.c;h=f986548c2dc62154a862ec90e33eaafa12d5e78b;hb=a72d9d04108cb29525c6c4983726cc6f38be4405;hp=499c4d7d62efb3d0cddb3f8175b01da63d7ca95e;hpb=2f6748ffe6d08b016ccf73c85f329d7c822fed20;p=external%2Fqemu.git diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 499c4d7..f986548 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -59,13 +63,24 @@ int __clone2(int (*fn)(void *), void *child_stack_base, //#include #include #include -#include +#include +#include +#include "qemu-common.h" #ifdef TARGET_GPROF #include #endif #ifdef CONFIG_EVENTFD #include #endif +#ifdef CONFIG_EPOLL +#include +#endif +#ifdef CONFIG_ATTR +#include "qemu/xattr.h" +#endif +#ifdef CONFIG_SENDFILE +#include +#endif #define termios host_termios #define winsize host_winsize @@ -88,19 +103,16 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #endif #include #include +#include +#include +#include #include "linux_loop.h" #include "cpu-uname.h" #include "qemu.h" -#include "qemu-common.h" -#if defined(CONFIG_USE_NPTL) #define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) -#else -/* XXX: Hardcode the above values. */ -#define CLONE_NPTL_FLAGS2 0 -#endif //#define DEBUG @@ -165,35 +177,21 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #define __NR_sys_uname __NR_uname -#define __NR_sys_faccessat __NR_faccessat -#define __NR_sys_fchmodat __NR_fchmodat -#define __NR_sys_fchownat __NR_fchownat -#define __NR_sys_fstatat64 __NR_fstatat64 -#define __NR_sys_futimesat __NR_futimesat #define __NR_sys_getcwd1 __NR_getcwd #define __NR_sys_getdents __NR_getdents #define __NR_sys_getdents64 __NR_getdents64 #define __NR_sys_getpriority __NR_getpriority -#define __NR_sys_linkat __NR_linkat -#define __NR_sys_mkdirat __NR_mkdirat -#define __NR_sys_mknodat __NR_mknodat -#define __NR_sys_newfstatat __NR_newfstatat -#define __NR_sys_openat __NR_openat -#define __NR_sys_readlinkat __NR_readlinkat -#define __NR_sys_renameat __NR_renameat #define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo -#define __NR_sys_symlinkat __NR_symlinkat #define __NR_sys_syslog __NR_syslog #define __NR_sys_tgkill __NR_tgkill #define __NR_sys_tkill __NR_tkill -#define __NR_sys_unlinkat __NR_unlinkat -#define __NR_sys_utimensat __NR_utimensat #define __NR_sys_futex __NR_futex #define __NR_sys_inotify_init __NR_inotify_init #define __NR_sys_inotify_add_watch __NR_inotify_add_watch #define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch -#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) +#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) || \ + defined(__s390x__) #define __NR__llseek __NR_lseek #endif @@ -206,11 +204,13 @@ static int gettid(void) { return -ENOSYS; } #endif +#ifdef __NR_getdents _syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); -#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64) +#endif +#if !defined(__NR_getdents) || \ + (defined(TARGET_NR_getdents64) && defined(__NR_getdents64)) _syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count); #endif -_syscall2(int, sys_getpriority, int, which, int, who); #if defined(TARGET_NR__llseek) && defined(__NR_llseek) _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh); @@ -229,12 +229,18 @@ _syscall1(int,exit_group,int,error_code) #if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) _syscall1(int,set_tid_address,int *,tidptr) #endif -#if defined(CONFIG_USE_NPTL) #if defined(TARGET_NR_futex) && defined(__NR_futex) _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, const struct timespec *,timeout,int *,uaddr2,int,val3) #endif -#endif +#define __NR_sys_sched_getaffinity __NR_sched_getaffinity +_syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); +#define __NR_sys_sched_setaffinity __NR_sched_setaffinity +_syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); +_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, + void *, arg); static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -245,14 +251,27 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_TRUNC, TARGET_O_TRUNC, O_TRUNC, O_TRUNC, }, { TARGET_O_APPEND, TARGET_O_APPEND, O_APPEND, O_APPEND, }, { TARGET_O_NONBLOCK, TARGET_O_NONBLOCK, O_NONBLOCK, O_NONBLOCK, }, + { TARGET_O_SYNC, TARGET_O_DSYNC, O_SYNC, O_DSYNC, }, { TARGET_O_SYNC, TARGET_O_SYNC, O_SYNC, O_SYNC, }, { TARGET_FASYNC, TARGET_FASYNC, FASYNC, FASYNC, }, { TARGET_O_DIRECTORY, TARGET_O_DIRECTORY, O_DIRECTORY, O_DIRECTORY, }, { TARGET_O_NOFOLLOW, TARGET_O_NOFOLLOW, O_NOFOLLOW, O_NOFOLLOW, }, - { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, }, #if defined(O_DIRECT) { TARGET_O_DIRECT, TARGET_O_DIRECT, O_DIRECT, O_DIRECT, }, #endif +#if defined(O_NOATIME) + { TARGET_O_NOATIME, TARGET_O_NOATIME, O_NOATIME, O_NOATIME }, +#endif +#if defined(O_CLOEXEC) + { TARGET_O_CLOEXEC, TARGET_O_CLOEXEC, O_CLOEXEC, O_CLOEXEC }, +#endif +#if defined(O_PATH) + { TARGET_O_PATH, TARGET_O_PATH, O_PATH, O_PATH }, +#endif + /* Don't terminate the list prematurely on 64-bit host+guest. */ +#if TARGET_O_LARGEFILE != 0 || O_LARGEFILE != 0 + { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, }, +#endif { 0, 0, 0, 0 } }; @@ -276,7 +295,7 @@ static int sys_uname(struct new_utsname *buf) * struct linux kernel uses). */ - bzero(buf, sizeof (*buf)); + memset(buf, 0, sizeof(*buf)); COPY_UTSNAME_FIELD(buf->sysname, uts_buf.sysname); COPY_UTSNAME_FIELD(buf->nodename, uts_buf.nodename); COPY_UTSNAME_FIELD(buf->release, uts_buf.release); @@ -299,183 +318,21 @@ static int sys_getcwd1(char *buf, size_t size) return strlen(buf)+1; } -#ifdef CONFIG_ATFILE -/* - * Host system seems to have atfile syscall stubs available. We - * now enable them one by one as specified by target syscall_nr.h. - */ - -#ifdef TARGET_NR_faccessat -static int sys_faccessat(int dirfd, const char *pathname, int mode) -{ - return (faccessat(dirfd, pathname, mode, 0)); -} -#endif -#ifdef TARGET_NR_fchmodat -static int sys_fchmodat(int dirfd, const char *pathname, mode_t mode) -{ - return (fchmodat(dirfd, pathname, mode, 0)); -} -#endif -#if defined(TARGET_NR_fchownat) && defined(USE_UID16) -static int sys_fchownat(int dirfd, const char *pathname, uid_t owner, - gid_t group, int flags) -{ - return (fchownat(dirfd, pathname, owner, group, flags)); -} -#endif -#ifdef __NR_fstatat64 -static int sys_fstatat64(int dirfd, const char *pathname, struct stat *buf, - int flags) -{ - return (fstatat(dirfd, pathname, buf, flags)); -} -#endif -#ifdef __NR_newfstatat -static int sys_newfstatat(int dirfd, const char *pathname, struct stat *buf, - int flags) -{ - return (fstatat(dirfd, pathname, buf, flags)); -} -#endif -#ifdef TARGET_NR_futimesat -static int sys_futimesat(int dirfd, const char *pathname, - const struct timeval times[2]) -{ - return (futimesat(dirfd, pathname, times)); -} -#endif -#ifdef TARGET_NR_linkat -static int sys_linkat(int olddirfd, const char *oldpath, - int newdirfd, const char *newpath, int flags) -{ - return (linkat(olddirfd, oldpath, newdirfd, newpath, flags)); -} -#endif -#ifdef TARGET_NR_mkdirat -static int sys_mkdirat(int dirfd, const char *pathname, mode_t mode) -{ - return (mkdirat(dirfd, pathname, mode)); -} -#endif -#ifdef TARGET_NR_mknodat -static int sys_mknodat(int dirfd, const char *pathname, mode_t mode, - dev_t dev) -{ - return (mknodat(dirfd, pathname, mode, dev)); -} -#endif #ifdef TARGET_NR_openat -static int sys_openat(int dirfd, const char *pathname, int flags, ...) +static int sys_openat(int dirfd, const char *pathname, int flags, mode_t mode) { /* * open(2) has extra parameter 'mode' when called with * flag O_CREAT. */ if ((flags & O_CREAT) != 0) { - va_list ap; - mode_t mode; - - /* - * Get the 'mode' parameter and translate it to - * host bits. - */ - va_start(ap, flags); - mode = va_arg(ap, mode_t); - mode = target_to_host_bitmask(mode, fcntl_flags_tbl); - va_end(ap); - return (openat(dirfd, pathname, flags, mode)); } return (openat(dirfd, pathname, flags)); } #endif -#ifdef TARGET_NR_readlinkat -static int sys_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) -{ - return (readlinkat(dirfd, pathname, buf, bufsiz)); -} -#endif -#ifdef TARGET_NR_renameat -static int sys_renameat(int olddirfd, const char *oldpath, - int newdirfd, const char *newpath) -{ - return (renameat(olddirfd, oldpath, newdirfd, newpath)); -} -#endif -#ifdef TARGET_NR_symlinkat -static int sys_symlinkat(const char *oldpath, int newdirfd, const char *newpath) -{ - return (symlinkat(oldpath, newdirfd, newpath)); -} -#endif -#ifdef TARGET_NR_unlinkat -static int sys_unlinkat(int dirfd, const char *pathname, int flags) -{ - return (unlinkat(dirfd, pathname, flags)); -} -#endif -#else /* !CONFIG_ATFILE */ - -/* - * Try direct syscalls instead - */ -#if defined(TARGET_NR_faccessat) && defined(__NR_faccessat) -_syscall3(int,sys_faccessat,int,dirfd,const char *,pathname,int,mode) -#endif -#if defined(TARGET_NR_fchmodat) && defined(__NR_fchmodat) -_syscall3(int,sys_fchmodat,int,dirfd,const char *,pathname, mode_t,mode) -#endif -#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat) && defined(USE_UID16) -_syscall5(int,sys_fchownat,int,dirfd,const char *,pathname, - uid_t,owner,gid_t,group,int,flags) -#endif -#if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) && \ - defined(__NR_fstatat64) -_syscall4(int,sys_fstatat64,int,dirfd,const char *,pathname, - struct stat *,buf,int,flags) -#endif -#if defined(TARGET_NR_futimesat) && defined(__NR_futimesat) -_syscall3(int,sys_futimesat,int,dirfd,const char *,pathname, - const struct timeval *,times) -#endif -#if (defined(TARGET_NR_newfstatat) || defined(TARGET_NR_fstatat64) ) && \ - defined(__NR_newfstatat) -_syscall4(int,sys_newfstatat,int,dirfd,const char *,pathname, - struct stat *,buf,int,flags) -#endif -#if defined(TARGET_NR_linkat) && defined(__NR_linkat) -_syscall5(int,sys_linkat,int,olddirfd,const char *,oldpath, - int,newdirfd,const char *,newpath,int,flags) -#endif -#if defined(TARGET_NR_mkdirat) && defined(__NR_mkdirat) -_syscall3(int,sys_mkdirat,int,dirfd,const char *,pathname,mode_t,mode) -#endif -#if defined(TARGET_NR_mknodat) && defined(__NR_mknodat) -_syscall4(int,sys_mknodat,int,dirfd,const char *,pathname, - mode_t,mode,dev_t,dev) -#endif -#if defined(TARGET_NR_openat) && defined(__NR_openat) -_syscall4(int,sys_openat,int,dirfd,const char *,pathname,int,flags,mode_t,mode) -#endif -#if defined(TARGET_NR_readlinkat) && defined(__NR_readlinkat) -_syscall4(int,sys_readlinkat,int,dirfd,const char *,pathname, - char *,buf,size_t,bufsize) -#endif -#if defined(TARGET_NR_renameat) && defined(__NR_renameat) -_syscall4(int,sys_renameat,int,olddirfd,const char *,oldpath, - int,newdirfd,const char *,newpath) -#endif -#if defined(TARGET_NR_symlinkat) && defined(__NR_symlinkat) -_syscall3(int,sys_symlinkat,const char *,oldpath, - int,newdirfd,const char *,newpath) -#endif -#if defined(TARGET_NR_unlinkat) && defined(__NR_unlinkat) -_syscall3(int,sys_unlinkat,int,dirfd,const char *,pathname,int,flags) -#endif - -#endif /* CONFIG_ATFILE */ +#ifdef TARGET_NR_utimensat #ifdef CONFIG_UTIMENSAT static int sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags) @@ -485,12 +342,19 @@ static int sys_utimensat(int dirfd, const char *pathname, else return utimensat(dirfd, pathname, times, flags); } -#else -#if defined(TARGET_NR_utimensat) && defined(__NR_utimensat) +#elif defined(__NR_utimensat) +#define __NR_sys_utimensat __NR_utimensat _syscall4(int,sys_utimensat,int,dirfd,const char *,pathname, const struct timespec *,tsp,int,flags) +#else +static int sys_utimensat(int dirfd, const char *pathname, + const struct timespec times[2], int flags) +{ + errno = ENOSYS; + return -1; +} #endif -#endif /* CONFIG_UTIMENSAT */ +#endif /* TARGET_NR_utimensat */ #ifdef CONFIG_INOTIFY #include @@ -529,12 +393,55 @@ static int sys_inotify_init1(int flags) #undef TARGET_NR_inotify_rm_watch #endif /* CONFIG_INOTIFY */ +#if defined(TARGET_NR_ppoll) +#ifndef __NR_ppoll +# define __NR_ppoll -1 +#endif +#define __NR_sys_ppoll __NR_ppoll +_syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds, + struct timespec *, timeout, const __sigset_t *, sigmask, + size_t, sigsetsize) +#endif + +#if defined(TARGET_NR_pselect6) +#ifndef __NR_pselect6 +# define __NR_pselect6 -1 +#endif +#define __NR_sys_pselect6 __NR_pselect6 +_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, + fd_set *, exceptfds, struct timespec *, timeout, void *, sig); +#endif + +#if defined(TARGET_NR_prlimit64) +#ifndef __NR_prlimit64 +# define __NR_prlimit64 -1 +#endif +#define __NR_sys_prlimit64 __NR_prlimit64 +/* The glibc rlimit structure may not be that used by the underlying syscall */ +struct host_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; +_syscall4(int, sys_prlimit64, pid_t, pid, int, resource, + const struct host_rlimit64 *, new_limit, + struct host_rlimit64 *, old_limit) +#endif -extern int personality(int); -extern int flock(int, int); -extern int setfsuid(int); -extern int setfsgid(int); -extern int setgroups(int, gid_t *); +/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ +#ifdef TARGET_ARM +static inline int regpairs_aligned(void *cpu_env) { + return ((((CPUARMState *)cpu_env)->eabi) == 1) ; +} +#elif defined(TARGET_MIPS) +static inline int regpairs_aligned(void *cpu_env) { return 1; } +#elif defined(TARGET_PPC) && !defined(TARGET_PPC64) +/* SysV AVI for PPC32 expects 64bit parameters to be passed on odd/even pairs + * of registers which translates to the same as ARM/MIPS, because we start with + * r3 as arg1 */ +static inline int regpairs_aligned(void *cpu_env) { return 1; } +#else +static inline int regpairs_aligned(void *cpu_env) { return 0; } +#endif #define ERRNO_TABLE_SIZE 1200 @@ -684,54 +591,100 @@ static inline int is_error(abi_long ret) char *target_strerror(int err) { + if ((err >= ERRNO_TABLE_SIZE) || (err < 0)) { + return NULL; + } return strerror(target_to_host_errno(err)); } static abi_ulong target_brk; static abi_ulong target_original_brk; +static abi_ulong brk_page; void target_set_brk(abi_ulong new_brk) { target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); + brk_page = HOST_PAGE_ALIGN(target_brk); } +//#define DEBUGF_BRK(message, args...) do { fprintf(stderr, (message), ## args); } while (0) +#define DEBUGF_BRK(message, args...) + /* do_brk() must return target values and target errnos. */ abi_long do_brk(abi_ulong new_brk) { - abi_ulong brk_page; abi_long mapped_addr; int new_alloc_size; - if (!new_brk) + DEBUGF_BRK("do_brk(" TARGET_ABI_FMT_lx ") -> ", new_brk); + + if (!new_brk) { + DEBUGF_BRK(TARGET_ABI_FMT_lx " (!new_brk)\n", target_brk); return target_brk; - if (new_brk < target_original_brk) + } + if (new_brk < target_original_brk) { + DEBUGF_BRK(TARGET_ABI_FMT_lx " (new_brk < target_original_brk)\n", + target_brk); return target_brk; + } - brk_page = HOST_PAGE_ALIGN(target_brk); - - /* If the new brk is less than this, set it and we're done... */ - if (new_brk < brk_page) { + /* If the new brk is less than the highest page reserved to the + * target heap allocation, set it and we're almost done... */ + if (new_brk <= brk_page) { + /* Heap contents are initialized to zero, as for anonymous + * mapped pages. */ + if (new_brk > target_brk) { + memset(g2h(target_brk), 0, new_brk - target_brk); + } target_brk = new_brk; + DEBUGF_BRK(TARGET_ABI_FMT_lx " (new_brk <= brk_page)\n", target_brk); return target_brk; } - /* We need to allocate more memory after the brk... */ - new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); + /* We need to allocate more memory after the brk... Note that + * we don't use MAP_FIXED because that will map over the top of + * any existing mapping (like the one with the host libc or qemu + * itself); instead we treat "mapped but at wrong address" as + * a failure and unmap again. + */ + new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page); mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, PROT_READ|PROT_WRITE, - MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0)); + MAP_ANON|MAP_PRIVATE, 0, 0)); + + if (mapped_addr == brk_page) { + /* Heap contents are initialized to zero, as for anonymous + * mapped pages. Technically the new pages are already + * initialized to zero since they *are* anonymous mapped + * pages, however we have to take care with the contents that + * come from the remaining part of the previous page: it may + * contains garbage data due to a previous heap usage (grown + * then shrunken). */ + memset(g2h(target_brk), 0, brk_page - target_brk); + + target_brk = new_brk; + brk_page = HOST_PAGE_ALIGN(target_brk); + DEBUGF_BRK(TARGET_ABI_FMT_lx " (mapped_addr == brk_page)\n", + target_brk); + return target_brk; + } else if (mapped_addr != -1) { + /* Mapped but at wrong address, meaning there wasn't actually + * enough space for this brk. + */ + target_munmap(mapped_addr, new_alloc_size); + mapped_addr = -1; + DEBUGF_BRK(TARGET_ABI_FMT_lx " (mapped_addr != -1)\n", target_brk); + } + else { + DEBUGF_BRK(TARGET_ABI_FMT_lx " (otherwise)\n", target_brk); + } #if defined(TARGET_ALPHA) /* We (partially) emulate OSF/1 on Alpha, which requires we return a proper errno, not an unchanged brk value. */ - if (is_error(mapped_addr)) { - return -TARGET_ENOMEM; - } + return -TARGET_ENOMEM; #endif - - if (!is_error(mapped_addr)) { - target_brk = new_brk; - } + /* For everything else, return the previous break. */ return target_brk; } @@ -767,6 +720,20 @@ static inline abi_long copy_from_user_fdset(fd_set *fds, return 0; } +static inline abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr, + abi_ulong target_fds_addr, + int n) +{ + if (target_fds_addr) { + if (copy_from_user_fdset(fds, target_fds_addr, n)) + return -TARGET_EFAULT; + *fds_ptr = fds; + } else { + *fds_ptr = NULL; + } + return 0; +} + static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr, const fd_set *fds, int n) @@ -786,7 +753,7 @@ static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr, for (i = 0; i < nw; i++) { v = 0; for (j = 0; j < TARGET_ABI_BITS; j++) { - v |= ((FD_ISSET(k, fds) != 0) << j); + v |= ((abi_ulong)(FD_ISSET(k, fds) != 0) << j); k++; } __put_user(v, &target_fds[i]); @@ -819,43 +786,95 @@ static inline abi_long host_to_target_rusage(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_rusage, target_addr, 0)) return -TARGET_EFAULT; - target_rusage->ru_utime.tv_sec = tswapl(rusage->ru_utime.tv_sec); - target_rusage->ru_utime.tv_usec = tswapl(rusage->ru_utime.tv_usec); - target_rusage->ru_stime.tv_sec = tswapl(rusage->ru_stime.tv_sec); - target_rusage->ru_stime.tv_usec = tswapl(rusage->ru_stime.tv_usec); - target_rusage->ru_maxrss = tswapl(rusage->ru_maxrss); - target_rusage->ru_ixrss = tswapl(rusage->ru_ixrss); - target_rusage->ru_idrss = tswapl(rusage->ru_idrss); - target_rusage->ru_isrss = tswapl(rusage->ru_isrss); - target_rusage->ru_minflt = tswapl(rusage->ru_minflt); - target_rusage->ru_majflt = tswapl(rusage->ru_majflt); - target_rusage->ru_nswap = tswapl(rusage->ru_nswap); - target_rusage->ru_inblock = tswapl(rusage->ru_inblock); - target_rusage->ru_oublock = tswapl(rusage->ru_oublock); - target_rusage->ru_msgsnd = tswapl(rusage->ru_msgsnd); - target_rusage->ru_msgrcv = tswapl(rusage->ru_msgrcv); - target_rusage->ru_nsignals = tswapl(rusage->ru_nsignals); - target_rusage->ru_nvcsw = tswapl(rusage->ru_nvcsw); - target_rusage->ru_nivcsw = tswapl(rusage->ru_nivcsw); + target_rusage->ru_utime.tv_sec = tswapal(rusage->ru_utime.tv_sec); + target_rusage->ru_utime.tv_usec = tswapal(rusage->ru_utime.tv_usec); + target_rusage->ru_stime.tv_sec = tswapal(rusage->ru_stime.tv_sec); + target_rusage->ru_stime.tv_usec = tswapal(rusage->ru_stime.tv_usec); + target_rusage->ru_maxrss = tswapal(rusage->ru_maxrss); + target_rusage->ru_ixrss = tswapal(rusage->ru_ixrss); + target_rusage->ru_idrss = tswapal(rusage->ru_idrss); + target_rusage->ru_isrss = tswapal(rusage->ru_isrss); + target_rusage->ru_minflt = tswapal(rusage->ru_minflt); + target_rusage->ru_majflt = tswapal(rusage->ru_majflt); + target_rusage->ru_nswap = tswapal(rusage->ru_nswap); + target_rusage->ru_inblock = tswapal(rusage->ru_inblock); + target_rusage->ru_oublock = tswapal(rusage->ru_oublock); + target_rusage->ru_msgsnd = tswapal(rusage->ru_msgsnd); + target_rusage->ru_msgrcv = tswapal(rusage->ru_msgrcv); + target_rusage->ru_nsignals = tswapal(rusage->ru_nsignals); + target_rusage->ru_nvcsw = tswapal(rusage->ru_nvcsw); + target_rusage->ru_nivcsw = tswapal(rusage->ru_nivcsw); unlock_user_struct(target_rusage, target_addr, 1); return 0; } -static inline rlim_t target_to_host_rlim(target_ulong target_rlim) +static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) { - if (target_rlim == TARGET_RLIM_INFINITY) + abi_ulong target_rlim_swap; + rlim_t result; + + target_rlim_swap = tswapal(target_rlim); + if (target_rlim_swap == TARGET_RLIM_INFINITY) return RLIM_INFINITY; - else - return tswapl(target_rlim); + + result = target_rlim_swap; + if (target_rlim_swap != (rlim_t)result) + return RLIM_INFINITY; + + return result; } -static inline target_ulong host_to_target_rlim(rlim_t rlim) +static inline abi_ulong host_to_target_rlim(rlim_t rlim) { - if (rlim == RLIM_INFINITY || rlim != (target_long)rlim) - return TARGET_RLIM_INFINITY; + abi_ulong target_rlim_swap; + abi_ulong result; + + if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim) + target_rlim_swap = TARGET_RLIM_INFINITY; else - return tswapl(rlim); + target_rlim_swap = rlim; + result = tswapal(target_rlim_swap); + + return result; +} + +static inline int target_to_host_resource(int code) +{ + switch (code) { + case TARGET_RLIMIT_AS: + return RLIMIT_AS; + case TARGET_RLIMIT_CORE: + return RLIMIT_CORE; + case TARGET_RLIMIT_CPU: + return RLIMIT_CPU; + case TARGET_RLIMIT_DATA: + return RLIMIT_DATA; + case TARGET_RLIMIT_FSIZE: + return RLIMIT_FSIZE; + case TARGET_RLIMIT_LOCKS: + return RLIMIT_LOCKS; + case TARGET_RLIMIT_MEMLOCK: + return RLIMIT_MEMLOCK; + case TARGET_RLIMIT_MSGQUEUE: + return RLIMIT_MSGQUEUE; + case TARGET_RLIMIT_NICE: + return RLIMIT_NICE; + case TARGET_RLIMIT_NOFILE: + return RLIMIT_NOFILE; + case TARGET_RLIMIT_NPROC: + return RLIMIT_NPROC; + case TARGET_RLIMIT_RSS: + return RLIMIT_RSS; + case TARGET_RLIMIT_RTPRIO: + return RLIMIT_RTPRIO; + case TARGET_RLIMIT_SIGPENDING: + return RLIMIT_SIGPENDING; + case TARGET_RLIMIT_STACK: + return RLIMIT_STACK; + default: + return code; + } } static inline abi_long copy_from_user_timeval(struct timeval *tv, @@ -932,6 +951,7 @@ static inline abi_long copy_to_user_mq_attr(abi_ulong target_mq_attr_addr, } #endif +#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) /* do_select() must return target values and target errnos. */ static abi_long do_select(int n, abi_ulong rfd_addr, abi_ulong wfd_addr, @@ -942,26 +962,17 @@ static abi_long do_select(int n, struct timeval tv, *tv_ptr; abi_long ret; - if (rfd_addr) { - if (copy_from_user_fdset(&rfds, rfd_addr, n)) - return -TARGET_EFAULT; - rfds_ptr = &rfds; - } else { - rfds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); + if (ret) { + return ret; } - if (wfd_addr) { - if (copy_from_user_fdset(&wfds, wfd_addr, n)) - return -TARGET_EFAULT; - wfds_ptr = &wfds; - } else { - wfds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); + if (ret) { + return ret; } - if (efd_addr) { - if (copy_from_user_fdset(&efds, efd_addr, n)) - return -TARGET_EFAULT; - efds_ptr = &efds; - } else { - efds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); + if (ret) { + return ret; } if (target_tv_addr) { @@ -988,6 +999,7 @@ static abi_long do_select(int n, return ret; } +#endif static abi_long do_pipe2(int host_pipe[], int flags) { @@ -1020,6 +1032,9 @@ static abi_long do_pipe(void *cpu_env, abi_ulong pipedes, #elif defined(TARGET_SH4) ((CPUSH4State*)cpu_env)->gregs[1] = host_pipe[1]; return host_pipe[0]; +#elif defined(TARGET_SPARC) + ((CPUSPARCState*)cpu_env)->regwptr[1] = host_pipe[1]; + return host_pipe[0]; #endif } @@ -1041,7 +1056,7 @@ static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, mreqn->imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; mreqn->imr_address.s_addr = target_smreqn->imr_address.s_addr; if (len == sizeof(struct target_ip_mreqn)) - mreqn->imr_ifindex = tswapl(target_smreqn->imr_ifindex); + mreqn->imr_ifindex = tswapal(target_smreqn->imr_ifindex); unlock_user(target_smreqn, target_addr, 0); return 0; @@ -1103,7 +1118,6 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr, return 0; } -/* ??? Should this also swap msgh->name? */ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, struct target_msghdr *target_msgh) { @@ -1113,10 +1127,10 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, struct target_cmsghdr *target_cmsg; socklen_t space = 0; - msg_controllen = tswapl(target_msgh->msg_controllen); + msg_controllen = tswapal(target_msgh->msg_controllen); if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; - target_cmsg_addr = tswapl(target_msgh->msg_control); + target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); if (!target_cmsg) return -TARGET_EFAULT; @@ -1125,7 +1139,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, void *data = CMSG_DATA(cmsg); void *target_data = TARGET_CMSG_DATA(target_cmsg); - int len = tswapl(target_cmsg->cmsg_len) + int len = tswapal(target_cmsg->cmsg_len) - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr)); space += CMSG_SPACE(len); @@ -1160,7 +1174,6 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, return 0; } -/* ??? Should this also swap msgh->name? */ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct msghdr *msgh) { @@ -1170,10 +1183,10 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct target_cmsghdr *target_cmsg; socklen_t space = 0; - msg_controllen = tswapl(target_msgh->msg_controllen); + msg_controllen = tswapal(target_msgh->msg_controllen); if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; - target_cmsg_addr = tswapl(target_msgh->msg_control); + target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); if (!target_cmsg) return -TARGET_EFAULT; @@ -1193,18 +1206,30 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); - target_cmsg->cmsg_len = tswapl(TARGET_CMSG_LEN(len)); + target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len)); - if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { - gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); - memcpy(target_data, data, len); - } else { + if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + (cmsg->cmsg_type == SCM_RIGHTS)) { int *fd = (int *)data; int *target_fd = (int *)target_data; int i, numfds = len / sizeof(int); for (i = 0; i < numfds; i++) target_fd[i] = tswap32(fd[i]); + } else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + (cmsg->cmsg_type == SO_TIMESTAMP) && + (len == sizeof(struct timeval))) { + /* copy struct timeval to target */ + struct timeval *tv = (struct timeval *)data; + struct target_timeval *target_tv = + (struct target_timeval *)target_data; + + target_tv->tv_sec = tswapal(tv->tv_sec); + target_tv->tv_usec = tswapal(tv->tv_usec); + } else { + gemu_log("Unsupported ancillary data: %d/%d\n", + cmsg->cmsg_level, cmsg->cmsg_type); + memcpy(target_data, data, len); } cmsg = CMSG_NXTHDR(msgh, cmsg); @@ -1212,7 +1237,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, } unlock_user(target_cmsg, target_cmsg_addr, space); the_end: - target_msgh->msg_controllen = tswapl(space); + target_msgh->msg_controllen = tswapal(space); return 0; } @@ -1289,8 +1314,49 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, goto unimplemented; } break; + case SOL_RAW: + switch (optname) { + case ICMP_FILTER: + /* struct icmp_filter takes an u32 value */ + if (optlen < sizeof(uint32_t)) { + return -TARGET_EINVAL; + } + + if (get_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + ret = get_errno(setsockopt(sockfd, level, optname, + &val, sizeof(val))); + break; + + default: + goto unimplemented; + } + break; case TARGET_SOL_SOCKET: switch (optname) { + case TARGET_SO_RCVTIMEO: + { + struct timeval tv; + + optname = SO_RCVTIMEO; + +set_timeout: + if (optlen != sizeof(struct target_timeval)) { + return -TARGET_EINVAL; + } + + if (copy_from_user_timeval(&tv, optval_addr)) { + return -TARGET_EFAULT; + } + + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + &tv, sizeof(tv))); + return ret; + } + case TARGET_SO_SNDTIMEO: + optname = SO_SNDTIMEO; + goto set_timeout; /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -1342,12 +1408,6 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case TARGET_SO_RCVLOWAT: optname = SO_RCVLOWAT; break; - case TARGET_SO_RCVTIMEO: - optname = SO_RCVTIMEO; - break; - case TARGET_SO_SNDTIMEO: - optname = SO_SNDTIMEO; - break; break; default: goto unimplemented; @@ -1361,7 +1421,7 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, break; default: unimplemented: - gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname); + gemu_log("Unsupported setsockopt level=%d optname=%d\n", level, optname); ret = -TARGET_ENOPROTOOPT; } return ret; @@ -1383,9 +1443,41 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, case TARGET_SO_LINGER: case TARGET_SO_RCVTIMEO: case TARGET_SO_SNDTIMEO: - case TARGET_SO_PEERCRED: case TARGET_SO_PEERNAME: goto unimplemented; + case TARGET_SO_PEERCRED: { + struct ucred cr; + socklen_t crlen; + struct target_ucred *tcr; + + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + + crlen = sizeof(cr); + ret = get_errno(getsockopt(sockfd, level, SO_PEERCRED, + &cr, &crlen)); + if (ret < 0) { + return ret; + } + if (len > crlen) { + len = crlen; + } + if (!lock_user_struct(VERIFY_WRITE, tcr, optval_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(cr.pid, &tcr->pid); + __put_user(cr.uid, &tcr->uid); + __put_user(cr.gid, &tcr->gid); + unlock_user_struct(tcr, optval_addr, 1); + if (put_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + break; + } /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -1448,7 +1540,7 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -1485,7 +1577,7 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -1517,82 +1609,128 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return ret; } -/* FIXME - * lock_iovec()/unlock_iovec() have a return code of 0 for success where - * other lock functions have a return code of 0 for failure. - */ -static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr, - int count, int copy) +static struct iovec *lock_iovec(int type, abi_ulong target_addr, + int count, int copy) { struct target_iovec *target_vec; - abi_ulong base; + struct iovec *vec; + abi_ulong total_len, max_len; int i; - target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); - if (!target_vec) - return -TARGET_EFAULT; - for(i = 0;i < count; i++) { - base = tswapl(target_vec[i].iov_base); - vec[i].iov_len = tswapl(target_vec[i].iov_len); - if (vec[i].iov_len != 0) { - vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy); - /* Don't check lock_user return value. We must call writev even - if a element has invalid base address. */ + if (count == 0) { + errno = 0; + return NULL; + } + if (count < 0 || count > IOV_MAX) { + errno = EINVAL; + return NULL; + } + + vec = calloc(count, sizeof(struct iovec)); + if (vec == NULL) { + errno = ENOMEM; + return NULL; + } + + target_vec = lock_user(VERIFY_READ, target_addr, + count * sizeof(struct target_iovec), 1); + if (target_vec == NULL) { + errno = EFAULT; + goto fail2; + } + + /* ??? If host page size > target page size, this will result in a + value larger than what we can actually support. */ + max_len = 0x7fffffff & TARGET_PAGE_MASK; + total_len = 0; + + for (i = 0; i < count; i++) { + abi_ulong base = tswapal(target_vec[i].iov_base); + abi_long len = tswapal(target_vec[i].iov_len); + + if (len < 0) { + errno = EINVAL; + goto fail; + } else if (len == 0) { + /* Zero length pointer is ignored. */ + vec[i].iov_base = 0; } else { - /* zero length pointer is ignored */ - vec[i].iov_base = NULL; + vec[i].iov_base = lock_user(type, base, len, copy); + if (!vec[i].iov_base) { + errno = EFAULT; + goto fail; + } + if (len > max_len - total_len) { + len = max_len - total_len; + } } + vec[i].iov_len = len; + total_len += len; } - unlock_user (target_vec, target_addr, 0); - return 0; + + unlock_user(target_vec, target_addr, 0); + return vec; + + fail: + free(vec); + fail2: + unlock_user(target_vec, target_addr, 0); + return NULL; } -static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr, - int count, int copy) +static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, + int count, int copy) { struct target_iovec *target_vec; - abi_ulong base; int i; - target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); - if (!target_vec) - return -TARGET_EFAULT; - for(i = 0;i < count; i++) { - if (target_vec[i].iov_base) { - base = tswapl(target_vec[i].iov_base); + target_vec = lock_user(VERIFY_READ, target_addr, + count * sizeof(struct target_iovec), 1); + if (target_vec) { + for (i = 0; i < count; i++) { + abi_ulong base = tswapal(target_vec[i].iov_base); + abi_long len = tswapal(target_vec[i].iov_base); + if (len < 0) { + break; + } unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0); } + unlock_user(target_vec, target_addr, 0); } - unlock_user (target_vec, target_addr, 0); - return 0; + free(vec); } -/* do_socket() Must return target values and target errnos. */ -static abi_long do_socket(int domain, int type, int protocol) +static inline void target_to_host_sock_type(int *type) { -#if defined(TARGET_MIPS) - switch(type) { + int host_type = 0; + int target_type = *type; + + switch (target_type & TARGET_SOCK_TYPE_MASK) { case TARGET_SOCK_DGRAM: - type = SOCK_DGRAM; + host_type = SOCK_DGRAM; break; case TARGET_SOCK_STREAM: - type = SOCK_STREAM; - break; - case TARGET_SOCK_RAW: - type = SOCK_RAW; - break; - case TARGET_SOCK_RDM: - type = SOCK_RDM; + host_type = SOCK_STREAM; break; - case TARGET_SOCK_SEQPACKET: - type = SOCK_SEQPACKET; - break; - case TARGET_SOCK_PACKET: - type = SOCK_PACKET; + default: + host_type = target_type & TARGET_SOCK_TYPE_MASK; break; } -#endif + if (target_type & TARGET_SOCK_CLOEXEC) { + host_type |= SOCK_CLOEXEC; + } + if (target_type & TARGET_SOCK_NONBLOCK) { + host_type |= SOCK_NONBLOCK; + } + *type = host_type; +} + +/* do_socket() Must return target values and target errnos. */ +static abi_long do_socket(int domain, int type, int protocol) +{ + target_to_host_sock_type(&type); + if (domain == PF_NETLINK) return -EAFNOSUPPORT; /* do not NETLINK socket connections possible */ return get_errno(socket(domain, type, protocol)); @@ -1658,24 +1796,27 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, if (msgp->msg_name) { msg.msg_namelen = tswap32(msgp->msg_namelen); msg.msg_name = alloca(msg.msg_namelen); - ret = target_to_host_sockaddr(msg.msg_name, tswapl(msgp->msg_name), + ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name), msg.msg_namelen); if (ret) { - unlock_user_struct(msgp, target_msg, send ? 0 : 1); - return ret; + goto out2; } } else { msg.msg_name = NULL; msg.msg_namelen = 0; } - msg.msg_controllen = 2 * tswapl(msgp->msg_controllen); + msg.msg_controllen = 2 * tswapal(msgp->msg_controllen); msg.msg_control = alloca(msg.msg_controllen); msg.msg_flags = tswap32(msgp->msg_flags); - count = tswapl(msgp->msg_iovlen); - vec = alloca(count * sizeof(struct iovec)); - target_vec = tswapl(msgp->msg_iov); - lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, vec, target_vec, count, send); + count = tswapal(msgp->msg_iovlen); + target_vec = tswapal(msgp->msg_iov); + vec = lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, + target_vec, count, send); + if (vec == NULL) { + ret = -host_to_target_errno(errno); + goto out2; + } msg.msg_iovlen = count; msg.msg_iov = vec; @@ -1688,25 +1829,52 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, if (!is_error(ret)) { len = ret; ret = host_to_target_cmsg(msgp, &msg); - if (!is_error(ret)) + if (!is_error(ret)) { + msgp->msg_namelen = tswap32(msg.msg_namelen); + if (msg.msg_name != NULL) { + ret = host_to_target_sockaddr(tswapal(msgp->msg_name), + msg.msg_name, msg.msg_namelen); + if (ret) { + goto out; + } + } + ret = len; + } } } + +out: unlock_iovec(vec, target_vec, count, !send); +out2: unlock_user_struct(msgp, target_msg, send ? 0 : 1); return ret; } -/* do_accept() Must return target values and target errnos. */ -static abi_long do_accept(int fd, abi_ulong target_addr, - abi_ulong target_addrlen_addr) +/* If we don't have a system accept4() then just call accept. + * The callsites to do_accept4() will ensure that they don't + * pass a non-zero flags argument in this config. + */ +#ifndef CONFIG_ACCEPT4 +static inline int accept4(int sockfd, struct sockaddr *addr, + socklen_t *addrlen, int flags) +{ + assert(flags == 0); + return accept(sockfd, addr, addrlen); +} +#endif + +/* do_accept4() Must return target values and target errnos. */ +static abi_long do_accept4(int fd, abi_ulong target_addr, + abi_ulong target_addrlen_addr, int flags) { socklen_t addrlen; void *addr; abi_long ret; - if (target_addr == 0) - return get_errno(accept(fd, NULL, NULL)); + if (target_addr == 0) { + return get_errno(accept4(fd, NULL, NULL, flags)); + } /* linux returns EINVAL if addrlen pointer is invalid */ if (get_user_u32(addrlen, target_addrlen_addr)) @@ -1721,7 +1889,7 @@ static abi_long do_accept(int fd, abi_ulong target_addr, addr = alloca(addrlen); - ret = get_errno(accept(fd, addr, &addrlen)); + ret = get_errno(accept4(fd, addr, &addrlen, flags)); if (!is_error(ret)) { host_to_target_sockaddr(target_addr, addr, addrlen); if (put_user_u32(addrlen, target_addrlen_addr)) @@ -1795,6 +1963,8 @@ static abi_long do_socketpair(int domain, int type, int protocol, int tab[2]; abi_long ret; + target_to_host_sock_type(&type); + ret = get_errno(socketpair(domain, type, protocol, tab)); if (!is_error(ret)) { if (put_user_s32(tab[0], target_tab_addr) @@ -1860,7 +2030,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags, ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen)); } else { addr = NULL; /* To keep compiler quiet. */ - ret = get_errno(recv(fd, host_msg, len, flags)); + ret = get_errno(qemu_recv(fd, host_msg, len, flags)); } if (!is_error(ret)) { if (target_addr) { @@ -1947,7 +2117,7 @@ static abi_long do_socketcall(int num, abi_ulong vptr) || get_user_ual(target_addrlen, vptr + 2 * n)) return -TARGET_EFAULT; - ret = do_accept(sockfd, target_addr, target_addrlen); + ret = do_accept4(sockfd, target_addr, target_addrlen, 0); } break; case SOCKOP_getsockname: @@ -2177,12 +2347,12 @@ static inline abi_long target_to_host_ipc_perm(struct ipc_perm *host_ip, if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - host_ip->__key = tswapl(target_ip->__key); - host_ip->uid = tswapl(target_ip->uid); - host_ip->gid = tswapl(target_ip->gid); - host_ip->cuid = tswapl(target_ip->cuid); - host_ip->cgid = tswapl(target_ip->cgid); - host_ip->mode = tswapl(target_ip->mode); + host_ip->__key = tswapal(target_ip->__key); + host_ip->uid = tswapal(target_ip->uid); + host_ip->gid = tswapal(target_ip->gid); + host_ip->cuid = tswapal(target_ip->cuid); + host_ip->cgid = tswapal(target_ip->cgid); + host_ip->mode = tswap16(target_ip->mode); unlock_user_struct(target_sd, target_addr, 0); return 0; } @@ -2196,12 +2366,12 @@ static inline abi_long host_to_target_ipc_perm(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - target_ip->__key = tswapl(host_ip->__key); - target_ip->uid = tswapl(host_ip->uid); - target_ip->gid = tswapl(host_ip->gid); - target_ip->cuid = tswapl(host_ip->cuid); - target_ip->cgid = tswapl(host_ip->cgid); - target_ip->mode = tswapl(host_ip->mode); + target_ip->__key = tswapal(host_ip->__key); + target_ip->uid = tswapal(host_ip->uid); + target_ip->gid = tswapal(host_ip->gid); + target_ip->cuid = tswapal(host_ip->cuid); + target_ip->cgid = tswapal(host_ip->cgid); + target_ip->mode = tswap16(host_ip->mode); unlock_user_struct(target_sd, target_addr, 1); return 0; } @@ -2215,9 +2385,9 @@ static inline abi_long target_to_host_semid_ds(struct semid_ds *host_sd, return -TARGET_EFAULT; if (target_to_host_ipc_perm(&(host_sd->sem_perm),target_addr)) return -TARGET_EFAULT; - host_sd->sem_nsems = tswapl(target_sd->sem_nsems); - host_sd->sem_otime = tswapl(target_sd->sem_otime); - host_sd->sem_ctime = tswapl(target_sd->sem_ctime); + host_sd->sem_nsems = tswapal(target_sd->sem_nsems); + host_sd->sem_otime = tswapal(target_sd->sem_otime); + host_sd->sem_ctime = tswapal(target_sd->sem_ctime); unlock_user_struct(target_sd, target_addr, 0); return 0; } @@ -2230,10 +2400,10 @@ static inline abi_long host_to_target_semid_ds(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) return -TARGET_EFAULT; if (host_to_target_ipc_perm(target_addr,&(host_sd->sem_perm))) - return -TARGET_EFAULT;; - target_sd->sem_nsems = tswapl(host_sd->sem_nsems); - target_sd->sem_otime = tswapl(host_sd->sem_otime); - target_sd->sem_ctime = tswapl(host_sd->sem_ctime); + return -TARGET_EFAULT; + target_sd->sem_nsems = tswapal(host_sd->sem_nsems); + target_sd->sem_otime = tswapal(host_sd->sem_otime); + target_sd->sem_ctime = tswapal(host_sd->sem_ctime); unlock_user_struct(target_sd, target_addr, 1); return 0; } @@ -2361,9 +2531,9 @@ static inline abi_long do_semctl(int semid, int semnum, int cmd, switch( cmd ) { case GETVAL: case SETVAL: - arg.val = tswapl(target_su.val); + arg.val = tswap32(target_su.val); ret = get_errno(semctl(semid, semnum, cmd, arg)); - target_su.val = tswapl(arg.val); + target_su.val = tswap32(arg.val); break; case GETALL: case SETALL: @@ -2443,7 +2613,7 @@ static inline abi_long do_semop(int semid, abi_long ptr, unsigned nsops) if (target_to_host_sembuf(sops, ptr, nsops)) return -TARGET_EFAULT; - return semop(semid, sops, nsops); + return get_errno(semop(semid, sops, nsops)); } struct target_msqid_ds @@ -2479,14 +2649,14 @@ static inline abi_long target_to_host_msqid_ds(struct msqid_ds *host_md, return -TARGET_EFAULT; if (target_to_host_ipc_perm(&(host_md->msg_perm),target_addr)) return -TARGET_EFAULT; - host_md->msg_stime = tswapl(target_md->msg_stime); - host_md->msg_rtime = tswapl(target_md->msg_rtime); - host_md->msg_ctime = tswapl(target_md->msg_ctime); - host_md->__msg_cbytes = tswapl(target_md->__msg_cbytes); - host_md->msg_qnum = tswapl(target_md->msg_qnum); - host_md->msg_qbytes = tswapl(target_md->msg_qbytes); - host_md->msg_lspid = tswapl(target_md->msg_lspid); - host_md->msg_lrpid = tswapl(target_md->msg_lrpid); + host_md->msg_stime = tswapal(target_md->msg_stime); + host_md->msg_rtime = tswapal(target_md->msg_rtime); + host_md->msg_ctime = tswapal(target_md->msg_ctime); + host_md->__msg_cbytes = tswapal(target_md->__msg_cbytes); + host_md->msg_qnum = tswapal(target_md->msg_qnum); + host_md->msg_qbytes = tswapal(target_md->msg_qbytes); + host_md->msg_lspid = tswapal(target_md->msg_lspid); + host_md->msg_lrpid = tswapal(target_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 0); return 0; } @@ -2500,14 +2670,14 @@ static inline abi_long host_to_target_msqid_ds(abi_ulong target_addr, return -TARGET_EFAULT; if (host_to_target_ipc_perm(target_addr,&(host_md->msg_perm))) return -TARGET_EFAULT; - target_md->msg_stime = tswapl(host_md->msg_stime); - target_md->msg_rtime = tswapl(host_md->msg_rtime); - target_md->msg_ctime = tswapl(host_md->msg_ctime); - target_md->__msg_cbytes = tswapl(host_md->__msg_cbytes); - target_md->msg_qnum = tswapl(host_md->msg_qnum); - target_md->msg_qbytes = tswapl(host_md->msg_qbytes); - target_md->msg_lspid = tswapl(host_md->msg_lspid); - target_md->msg_lrpid = tswapl(host_md->msg_lrpid); + target_md->msg_stime = tswapal(host_md->msg_stime); + target_md->msg_rtime = tswapal(host_md->msg_rtime); + target_md->msg_ctime = tswapal(host_md->msg_ctime); + target_md->__msg_cbytes = tswapal(host_md->__msg_cbytes); + target_md->msg_qnum = tswapal(host_md->msg_qnum); + target_md->msg_qbytes = tswapal(host_md->msg_qbytes); + target_md->msg_lspid = tswapal(host_md->msg_lspid); + target_md->msg_lrpid = tswapal(host_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 1); return 0; } @@ -2588,7 +2758,7 @@ static inline abi_long do_msgsnd(int msqid, abi_long msgp, if (!lock_user_struct(VERIFY_READ, target_mb, msgp, 0)) return -TARGET_EFAULT; host_mb = malloc(msgsz+sizeof(long)); - host_mb->mtype = (abi_long) tswapl(target_mb->mtype); + host_mb->mtype = (abi_long) tswapal(target_mb->mtype); memcpy(host_mb->mtext, target_mb->mtext, msgsz); ret = get_errno(msgsnd(msqid, host_mb, msgsz, msgflg)); free(host_mb); @@ -2609,8 +2779,8 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) return -TARGET_EFAULT; - host_mb = malloc(msgsz+sizeof(long)); - ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapl(msgtyp), msgflg)); + host_mb = g_malloc(msgsz+sizeof(long)); + ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg)); if (ret > 0) { abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong); @@ -2623,12 +2793,12 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, unlock_user(target_mtext, target_mtext_addr, ret); } - target_mb->mtype = tswapl(host_mb->mtype); - free(host_mb); + target_mb->mtype = tswapal(host_mb->mtype); end: if (target_mb) unlock_user_struct(target_mb, msgp, 1); + g_free(host_mb); return ret; } @@ -2902,7 +3072,7 @@ static abi_long do_ipc(unsigned int call, int first, break; } - ret = do_msgrcv(first, tmp->msgp, second, tmp->msgtyp, third); + ret = do_msgrcv(first, tswapal(tmp->msgp), second, tswapal(tmp->msgtyp), third); unlock_user_struct(tmp, ptr, 0); break; @@ -2952,7 +3122,6 @@ static abi_long do_ipc(unsigned int call, int first, #endif /* kernel structure types definitions */ -#define IFNAMSIZ 16 #define STRUCT(name, ...) STRUCT_ ## name, #define STRUCT_SPECIAL(name) STRUCT_ ## name, @@ -3077,6 +3246,386 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, } #endif +static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg) +{ + const argtype *arg_type = ie->arg_type; + int target_size; + void *argptr; + int ret; + struct ifconf *host_ifconf; + uint32_t outbufsz; + const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) }; + int target_ifreq_size; + int nb_ifreq; + int free_buf = 0; + int i; + int target_ifc_len; + abi_long target_ifc_buf; + int host_ifc_len; + char *host_ifc_buf; + + assert(arg_type[0] == TYPE_PTR); + assert(ie->access == IOC_RW); + + arg_type++; + target_size = thunk_type_size(arg_type, 0); + + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) + return -TARGET_EFAULT; + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + + host_ifconf = (struct ifconf *)(unsigned long)buf_temp; + target_ifc_len = host_ifconf->ifc_len; + target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf; + + target_ifreq_size = thunk_type_size(ifreq_arg_type, 0); + nb_ifreq = target_ifc_len / target_ifreq_size; + host_ifc_len = nb_ifreq * sizeof(struct ifreq); + + outbufsz = sizeof(*host_ifconf) + host_ifc_len; + if (outbufsz > MAX_STRUCT_SIZE) { + /* We can't fit all the extents into the fixed size buffer. + * Allocate one that is large enough and use it instead. + */ + host_ifconf = malloc(outbufsz); + if (!host_ifconf) { + return -TARGET_ENOMEM; + } + memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf)); + free_buf = 1; + } + host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf); + + host_ifconf->ifc_len = host_ifc_len; + host_ifconf->ifc_buf = host_ifc_buf; + + ret = get_errno(ioctl(fd, ie->host_cmd, host_ifconf)); + if (!is_error(ret)) { + /* convert host ifc_len to target ifc_len */ + + nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq); + target_ifc_len = nb_ifreq * target_ifreq_size; + host_ifconf->ifc_len = target_ifc_len; + + /* restore target ifc_buf */ + + host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf; + + /* copy struct ifconf to target user */ + + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); + if (!argptr) + return -TARGET_EFAULT; + thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET); + unlock_user(argptr, arg, target_size); + + /* copy ifreq[] to target user */ + + argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0); + for (i = 0; i < nb_ifreq ; i++) { + thunk_convert(argptr + i * target_ifreq_size, + host_ifc_buf + i * sizeof(struct ifreq), + ifreq_arg_type, THUNK_TARGET); + } + unlock_user(argptr, target_ifc_buf, target_ifc_len); + } + + if (free_buf) { + free(host_ifconf); + } + + return ret; +} + +static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, + abi_long cmd, abi_long arg) +{ + void *argptr; + struct dm_ioctl *host_dm; + abi_long guest_data; + uint32_t guest_data_size; + int target_size; + const argtype *arg_type = ie->arg_type; + abi_long ret; + void *big_buf = NULL; + char *host_data; + + arg_type++; + target_size = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) { + ret = -TARGET_EFAULT; + goto out; + } + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + + /* buf_temp is too small, so fetch things into a bigger buffer */ + big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2); + memcpy(big_buf, buf_temp, target_size); + buf_temp = big_buf; + host_dm = big_buf; + + guest_data = arg + host_dm->data_start; + if ((guest_data - arg) < 0) { + ret = -EINVAL; + goto out; + } + guest_data_size = host_dm->data_size - host_dm->data_start; + host_data = (char*)host_dm + host_dm->data_start; + + argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1); + switch (ie->host_cmd) { + case DM_REMOVE_ALL: + case DM_LIST_DEVICES: + case DM_DEV_CREATE: + case DM_DEV_REMOVE: + case DM_DEV_SUSPEND: + case DM_DEV_STATUS: + case DM_DEV_WAIT: + case DM_TABLE_STATUS: + case DM_TABLE_CLEAR: + case DM_TABLE_DEPS: + case DM_LIST_VERSIONS: + /* no input data */ + break; + case DM_DEV_RENAME: + case DM_DEV_SET_GEOMETRY: + /* data contains only strings */ + memcpy(host_data, argptr, guest_data_size); + break; + case DM_TARGET_MSG: + memcpy(host_data, argptr, guest_data_size); + *(uint64_t*)host_data = tswap64(*(uint64_t*)argptr); + break; + case DM_TABLE_LOAD: + { + void *gspec = argptr; + void *cur_data = host_data; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(arg_type, 0); + int i; + + for (i = 0; i < host_dm->target_count; i++) { + struct dm_target_spec *spec = cur_data; + uint32_t next; + int slen; + + thunk_convert(spec, gspec, arg_type, THUNK_HOST); + slen = strlen((char*)gspec + spec_size) + 1; + next = spec->next; + spec->next = sizeof(*spec) + slen; + strcpy((char*)&spec[1], gspec + spec_size); + gspec += next; + cur_data += spec->next; + } + break; + } + default: + ret = -TARGET_EINVAL; + goto out; + } + unlock_user(argptr, guest_data, 0); + + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + if (!is_error(ret)) { + guest_data = arg + host_dm->data_start; + guest_data_size = host_dm->data_size - host_dm->data_start; + argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0); + switch (ie->host_cmd) { + case DM_REMOVE_ALL: + case DM_DEV_CREATE: + case DM_DEV_REMOVE: + case DM_DEV_RENAME: + case DM_DEV_SUSPEND: + case DM_DEV_STATUS: + case DM_TABLE_LOAD: + case DM_TABLE_CLEAR: + case DM_TARGET_MSG: + case DM_DEV_SET_GEOMETRY: + /* no return data */ + break; + case DM_LIST_DEVICES: + { + struct dm_name_list *nl = (void*)host_dm + host_dm->data_start; + uint32_t remaining_data = guest_data_size; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) }; + int nl_size = 12; /* can't use thunk_size due to alignment */ + + while (1) { + uint32_t next = nl->next; + if (next) { + nl->next = nl_size + (strlen(nl->name) + 1); + } + if (remaining_data < nl->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, nl, arg_type, THUNK_TARGET); + strcpy(cur_data + nl_size, nl->name); + cur_data += nl->next; + remaining_data -= nl->next; + if (!next) { + break; + } + nl = (void*)nl + next; + } + break; + } + case DM_DEV_WAIT: + case DM_TABLE_STATUS: + { + struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(arg_type, 0); + int i; + + for (i = 0; i < host_dm->target_count; i++) { + uint32_t next = spec->next; + int slen = strlen((char*)&spec[1]) + 1; + spec->next = (cur_data - argptr) + spec_size + slen; + if (guest_data_size < spec->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, spec, arg_type, THUNK_TARGET); + strcpy(cur_data + spec_size, (char*)&spec[1]); + cur_data = argptr + spec->next; + spec = (void*)host_dm + host_dm->data_start + next; + } + break; + } + case DM_TABLE_DEPS: + { + void *hdata = (void*)host_dm + host_dm->data_start; + int count = *(uint32_t*)hdata; + uint64_t *hdev = hdata + 8; + uint64_t *gdev = argptr + 8; + int i; + + *(uint32_t*)argptr = tswap32(count); + for (i = 0; i < count; i++) { + *gdev = tswap64(*hdev); + gdev++; + hdev++; + } + break; + } + case DM_LIST_VERSIONS: + { + struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start; + uint32_t remaining_data = guest_data_size; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) }; + int vers_size = thunk_type_size(arg_type, 0); + + while (1) { + uint32_t next = vers->next; + if (next) { + vers->next = vers_size + (strlen(vers->name) + 1); + } + if (remaining_data < vers->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, vers, arg_type, THUNK_TARGET); + strcpy(cur_data + vers_size, vers->name); + cur_data += vers->next; + remaining_data -= vers->next; + if (!next) { + break; + } + vers = (void*)vers + next; + } + break; + } + default: + ret = -TARGET_EINVAL; + goto out; + } + unlock_user(argptr, guest_data, guest_data_size); + + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); + if (!argptr) { + ret = -TARGET_EFAULT; + goto out; + } + thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET); + unlock_user(argptr, arg, target_size); + } +out: + g_free(big_buf); + return ret; +} + +static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg) +{ + const argtype *arg_type = ie->arg_type; + const StructEntry *se; + const argtype *field_types; + const int *dst_offsets, *src_offsets; + int target_size; + void *argptr; + abi_ulong *target_rt_dev_ptr; + unsigned long *host_rt_dev_ptr; + abi_long ret; + int i; + + assert(ie->access == IOC_W); + assert(*arg_type == TYPE_PTR); + arg_type++; + assert(*arg_type == TYPE_STRUCT); + target_size = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) { + return -TARGET_EFAULT; + } + arg_type++; + assert(*arg_type == (int)STRUCT_rtentry); + se = struct_entries + *arg_type++; + assert(se->convert[0] == NULL); + /* convert struct here to be able to catch rt_dev string */ + field_types = se->field_types; + dst_offsets = se->field_offsets[THUNK_HOST]; + src_offsets = se->field_offsets[THUNK_TARGET]; + for (i = 0; i < se->nb_fields; i++) { + if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) { + assert(*field_types == TYPE_PTRVOID); + target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]); + host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]); + if (*target_rt_dev_ptr != 0) { + *host_rt_dev_ptr = (unsigned long)lock_user_string( + tswapal(*target_rt_dev_ptr)); + if (!*host_rt_dev_ptr) { + unlock_user(argptr, arg, 0); + return -TARGET_EFAULT; + } + } else { + *host_rt_dev_ptr = 0; + } + field_types++; + continue; + } + field_types = thunk_convert(buf_temp + dst_offsets[i], + argptr + src_offsets[i], + field_types, THUNK_HOST); + } + unlock_user(argptr, arg, 0); + + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + if (*host_rt_dev_ptr != 0) { + unlock_user((void *)*host_rt_dev_ptr, + *target_rt_dev_ptr, 0); + } + return ret; +} + static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, @@ -3401,7 +3950,7 @@ static abi_long write_ldt(CPUX86State *env, if (!lock_user_struct(VERIFY_READ, target_ldt_info, ptr, 1)) return -TARGET_EFAULT; ldt_info.entry_number = tswap32(target_ldt_info->entry_number); - ldt_info.base_addr = tswapl(target_ldt_info->base_addr); + ldt_info.base_addr = tswapal(target_ldt_info->base_addr); ldt_info.limit = tswap32(target_ldt_info->limit); ldt_info.flags = tswap32(target_ldt_info->flags); unlock_user_struct(target_ldt_info, ptr, 0); @@ -3502,7 +4051,7 @@ static abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr, } #if defined(TARGET_I386) && defined(TARGET_ABI32) -static abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) +abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) { uint64_t *gdt_table = g2h(env->gdt.base); struct target_modify_ldt_ldt_s ldt_info; @@ -3516,7 +4065,7 @@ static abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) if (!target_ldt_info) return -TARGET_EFAULT; ldt_info.entry_number = tswap32(target_ldt_info->entry_number); - ldt_info.base_addr = tswapl(target_ldt_info->base_addr); + ldt_info.base_addr = tswapal(target_ldt_info->base_addr); ldt_info.limit = tswap32(target_ldt_info->limit); ldt_info.flags = tswap32(target_ldt_info->flags); if (ldt_info.entry_number == -1) { @@ -3627,7 +4176,7 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) base_addr = (entry_1 >> 16) | (entry_2 & 0xff000000) | ((entry_2 & 0xff) << 16); - target_ldt_info->base_addr = tswapl(base_addr); + target_ldt_info->base_addr = tswapal(base_addr); target_ldt_info->limit = tswap32(limit); target_ldt_info->flags = tswap32(flags); unlock_user_struct(target_ldt_info, ptr, 1); @@ -3636,12 +4185,12 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) #endif /* TARGET_I386 && TARGET_ABI32 */ #ifndef TARGET_ABI32 -static abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) +abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) { - abi_long ret; + abi_long ret = 0; abi_ulong val; int idx; - + switch(code) { case TARGET_ARCH_SET_GS: case TARGET_ARCH_SET_FS: @@ -3660,25 +4209,24 @@ static abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) idx = R_FS; val = env->segs[idx].base; if (put_user(val, addr, abi_ulong)) - return -TARGET_EFAULT; + ret = -TARGET_EFAULT; break; default: ret = -TARGET_EINVAL; break; } - return 0; + return ret; } #endif #endif /* defined(TARGET_I386) */ -#if defined(CONFIG_USE_NPTL) +#define NEW_STACK_SIZE 0x40000 -#define NEW_STACK_SIZE PTHREAD_STACK_MIN static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER; typedef struct { - CPUState *env; + CPUArchState *env; pthread_mutex_t mutex; pthread_cond_t cond; pthread_t thread; @@ -3691,14 +4239,16 @@ typedef struct { static void *clone_func(void *arg) { new_thread_info *info = arg; - CPUState *env; + CPUArchState *env; + CPUState *cpu; TaskState *ts; env = info->env; - thread_env = env; - ts = (TaskState *)thread_env->opaque; + cpu = ENV_GET_CPU(env); + thread_cpu = cpu; + ts = (TaskState *)env->opaque; info->tid = gettid(); - env->host_tid = info->tid; + cpu->host_tid = info->tid; task_settid(ts); if (info->child_tidptr) put_user_u32(info->tid, info->child_tidptr); @@ -3717,35 +4267,18 @@ static void *clone_func(void *arg) /* never exits */ return NULL; } -#else -/* this stack is the equivalent of the kernel stack associated with a - thread/process */ -#define NEW_STACK_SIZE 8192 - -static int clone_func(void *arg) -{ - CPUState *env = arg; - cpu_loop(env); - /* never exits */ - return 0; -} -#endif /* do_fork() Must return host values and target errnos (unlike most do_*() functions). */ -static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, +static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, abi_ulong parent_tidptr, target_ulong newtls, abi_ulong child_tidptr) { int ret; TaskState *ts; - CPUState *new_env; -#if defined(CONFIG_USE_NPTL) + CPUArchState *new_env; unsigned int nptl_flags; sigset_t sigmask; -#else - uint8_t *new_stack; -#endif /* Emulate vfork() with fork() */ if (flags & CLONE_VFORK) @@ -3753,23 +4286,18 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, if (flags & CLONE_VM) { TaskState *parent_ts = (TaskState *)env->opaque; -#if defined(CONFIG_USE_NPTL) new_thread_info info; pthread_attr_t attr; -#endif - ts = qemu_mallocz(sizeof(TaskState)); + + ts = g_malloc0(sizeof(TaskState)); init_task_state(ts); /* we create a new CPU instance. */ new_env = cpu_copy(env); -#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) - cpu_reset(new_env); -#endif /* Init regs that differ from the parent. */ cpu_clone_regs(new_env, newsp); new_env->opaque = ts; ts->bprm = parent_ts->bprm; ts->info = parent_ts->info; -#if defined(CONFIG_USE_NPTL) nptl_flags = flags; flags &= ~CLONE_NPTL_FLAGS2; @@ -3819,17 +4347,6 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, pthread_cond_destroy(&info.cond); pthread_mutex_destroy(&info.mutex); pthread_mutex_unlock(&clone_lock); -#else - if (flags & CLONE_NPTL_FLAGS2) - return -EINVAL; - /* This is probably going to die very quickly, but do it anyway. */ - new_stack = qemu_mallocz (NEW_STACK_SIZE); -#ifdef __ia64__ - ret = __clone2(clone_func, new_stack, NEW_STACK_SIZE, flags, new_env); -#else - ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); -#endif -#endif } else { /* if no CLONE_VM, we consider it is a fork */ if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) @@ -3840,7 +4357,6 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, /* Child Process. */ cpu_clone_regs(env, newsp); fork_end(1); -#if defined(CONFIG_USE_NPTL) /* There is a race condition here. The parent process could theoretically read the TID in the child process before the child tid is set. This would require using either ptrace @@ -3856,7 +4372,6 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, cpu_set_tls (env, newtls); if (flags & CLONE_CHILD_CLEARTID) ts->child_tidptr = child_tidptr; -#endif } else { fork_end(0); } @@ -3912,6 +4427,16 @@ static int target_to_host_fcntl_cmd(int cmd) return -TARGET_EINVAL; } +#define TRANSTBL_CONVERT(a) { -1, TARGET_##a, -1, a } +static const bitmask_transtbl flock_tbl[] = { + TRANSTBL_CONVERT(F_RDLCK), + TRANSTBL_CONVERT(F_WRLCK), + TRANSTBL_CONVERT(F_UNLCK), + TRANSTBL_CONVERT(F_EXLCK), + TRANSTBL_CONVERT(F_SHLCK), + { 0, 0, 0, 0 } +}; + static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) { struct flock fl; @@ -3928,20 +4453,22 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_GETLK: if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1)) return -TARGET_EFAULT; - fl.l_type = tswap16(target_fl->l_type); + fl.l_type = + target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl); fl.l_whence = tswap16(target_fl->l_whence); - fl.l_start = tswapl(target_fl->l_start); - fl.l_len = tswapl(target_fl->l_len); + fl.l_start = tswapal(target_fl->l_start); + fl.l_len = tswapal(target_fl->l_len); fl.l_pid = tswap32(target_fl->l_pid); unlock_user_struct(target_fl, arg, 0); ret = get_errno(fcntl(fd, host_cmd, &fl)); if (ret == 0) { if (!lock_user_struct(VERIFY_WRITE, target_fl, arg, 0)) return -TARGET_EFAULT; - target_fl->l_type = tswap16(fl.l_type); + target_fl->l_type = + host_to_target_bitmask(tswap16(fl.l_type), flock_tbl); target_fl->l_whence = tswap16(fl.l_whence); - target_fl->l_start = tswapl(fl.l_start); - target_fl->l_len = tswapl(fl.l_len); + target_fl->l_start = tswapal(fl.l_start); + target_fl->l_len = tswapal(fl.l_len); target_fl->l_pid = tswap32(fl.l_pid); unlock_user_struct(target_fl, arg, 1); } @@ -3951,10 +4478,11 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_SETLKW: if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1)) return -TARGET_EFAULT; - fl.l_type = tswap16(target_fl->l_type); + fl.l_type = + target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl); fl.l_whence = tswap16(target_fl->l_whence); - fl.l_start = tswapl(target_fl->l_start); - fl.l_len = tswapl(target_fl->l_len); + fl.l_start = tswapal(target_fl->l_start); + fl.l_len = tswapal(target_fl->l_len); fl.l_pid = tswap32(target_fl->l_pid); unlock_user_struct(target_fl, arg, 0); ret = get_errno(fcntl(fd, host_cmd, &fl)); @@ -3963,20 +4491,22 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_GETLK64: if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1)) return -TARGET_EFAULT; - fl64.l_type = tswap16(target_fl64->l_type) >> 1; + fl64.l_type = + target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1; fl64.l_whence = tswap16(target_fl64->l_whence); - fl64.l_start = tswapl(target_fl64->l_start); - fl64.l_len = tswapl(target_fl64->l_len); + fl64.l_start = tswap64(target_fl64->l_start); + fl64.l_len = tswap64(target_fl64->l_len); fl64.l_pid = tswap32(target_fl64->l_pid); unlock_user_struct(target_fl64, arg, 0); ret = get_errno(fcntl(fd, host_cmd, &fl64)); if (ret == 0) { if (!lock_user_struct(VERIFY_WRITE, target_fl64, arg, 0)) return -TARGET_EFAULT; - target_fl64->l_type = tswap16(fl64.l_type) >> 1; + target_fl64->l_type = + host_to_target_bitmask(tswap16(fl64.l_type), flock_tbl) >> 1; target_fl64->l_whence = tswap16(fl64.l_whence); - target_fl64->l_start = tswapl(fl64.l_start); - target_fl64->l_len = tswapl(fl64.l_len); + target_fl64->l_start = tswap64(fl64.l_start); + target_fl64->l_len = tswap64(fl64.l_len); target_fl64->l_pid = tswap32(fl64.l_pid); unlock_user_struct(target_fl64, arg, 1); } @@ -3985,10 +4515,11 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_SETLKW64: if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1)) return -TARGET_EFAULT; - fl64.l_type = tswap16(target_fl64->l_type) >> 1; + fl64.l_type = + target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1; fl64.l_whence = tswap16(target_fl64->l_whence); - fl64.l_start = tswapl(target_fl64->l_start); - fl64.l_len = tswapl(target_fl64->l_len); + fl64.l_start = tswap64(target_fl64->l_start); + fl64.l_len = tswap64(target_fl64->l_len); fl64.l_pid = tswap32(target_fl64->l_pid); unlock_user_struct(target_fl64, arg, 0); ret = get_errno(fcntl(fd, host_cmd, &fl64)); @@ -4054,7 +4585,31 @@ static inline int low2highgid(int gid) else return gid; } - +static inline int tswapid(int id) +{ + return tswap16(id); +} +#else /* !USE_UID16 */ +static inline int high2lowuid(int uid) +{ + return uid; +} +static inline int high2lowgid(int gid) +{ + return gid; +} +static inline int low2highuid(int uid) +{ + return uid; +} +static inline int low2highgid(int gid) +{ + return gid; +} +static inline int tswapid(int id) +{ + return tswap32(id); +} #endif /* USE_UID16 */ void syscall_init(void) @@ -4070,6 +4625,12 @@ void syscall_init(void) #undef STRUCT #undef STRUCT_SPECIAL + /* Build target_to_host_errno_table[] table from + * host_to_target_errno_table[]. */ + for (i = 0; i < ERRNO_TABLE_SIZE; i++) { + target_to_host_errno_table[host_to_target_errno_table[i]] = i; + } + /* we patch the ioctl size if necessary. We rely on the fact that no ioctl has all the bits at '1' in the size field */ ie = ioctl_entries; @@ -4089,11 +4650,6 @@ void syscall_init(void) (size << TARGET_IOC_SIZESHIFT); } - /* Build target_to_host_errno_table[] table from - * host_to_target_errno_table[]. */ - for (i=0; i < ERRNO_TABLE_SIZE; i++) - target_to_host_errno_table[host_to_target_errno_table[i]] = i; - /* automatic consistency check if same arch */ #if (defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32)) || \ (defined(__x86_64__) && defined(TARGET_X86_64)) @@ -4128,13 +4684,10 @@ static inline abi_long target_truncate64(void *cpu_env, const char *arg1, abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(truncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -4145,13 +4698,10 @@ static inline abi_long target_ftruncate64(void *cpu_env, abi_long arg1, abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -4163,8 +4713,8 @@ static inline abi_long target_to_host_timespec(struct timespec *host_ts, if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1)) return -TARGET_EFAULT; - host_ts->tv_sec = tswapl(target_ts->tv_sec); - host_ts->tv_nsec = tswapl(target_ts->tv_nsec); + host_ts->tv_sec = tswapal(target_ts->tv_sec); + host_ts->tv_nsec = tswapal(target_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 0); return 0; } @@ -4176,8 +4726,8 @@ static inline abi_long host_to_target_timespec(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_ts, target_addr, 0)) return -TARGET_EFAULT; - target_ts->tv_sec = tswapl(host_ts->tv_sec); - target_ts->tv_nsec = tswapl(host_ts->tv_nsec); + target_ts->tv_sec = tswapal(host_ts->tv_sec); + target_ts->tv_nsec = tswapal(host_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 1); return 0; } @@ -4247,7 +4797,6 @@ static inline abi_long host_to_target_stat64(void *cpu_env, } #endif -#if defined(CONFIG_USE_NPTL) /* ??? Using host futex calls even when target atomic operations are not really atomic probably breaks things. However implementing futexes locally would make futexes shared between multiple processes @@ -4268,6 +4817,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, #endif switch (base_op) { case FUTEX_WAIT: + case FUTEX_WAIT_BITSET: if (timeout) { pts = &ts; target_to_host_timespec(pts, timeout); @@ -4275,7 +4825,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, pts = NULL; } return get_errno(sys_futex(g2h(uaddr), op, tswap32(val), - pts, NULL, 0)); + pts, NULL, val3)); case FUTEX_WAKE: return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_FD: @@ -4298,11 +4848,10 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, return -TARGET_ENOSYS; } } -#endif /* Map host to target signal numbers for the wait family of syscalls. Assume all other status bits are the same. */ -static int host_to_target_waitstatus(int status) +int host_to_target_waitstatus(int status) { if (WIFSIGNALED(status)) { return host_to_target_signal(WTERMSIG(status)) | (status & ~0x7f); @@ -4345,93 +4894,285 @@ int get_osversion(void) return osversion; } -/* do_syscall() should always have a single exit point at the end so - that actions, such as logging of syscall results, can be performed. - All errnos that do_syscall() returns must be -TARGET_. */ -abi_long do_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6) + +static int open_self_maps(void *cpu_env, int fd) { - abi_long ret; - struct stat st; - struct statfs stfs; - void *p; +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; +#endif + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t read; -#ifdef DEBUG - gemu_log("syscall %d", num); + fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) { + return -EACCES; + } + + while ((read = getline(&line, &len, fp)) != -1) { + int fields, dev_maj, dev_min, inode; + uint64_t min, max, offset; + char flag_r, flag_w, flag_x, flag_p; + char path[512] = ""; + fields = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64" %x:%x %d" + " %512s", &min, &max, &flag_r, &flag_w, &flag_x, + &flag_p, &offset, &dev_maj, &dev_min, &inode, path); + + if ((fields < 10) || (fields > 11)) { + continue; + } + if (!strncmp(path, "[stack]", 7)) { + continue; + } + if (h2g_valid(min) && h2g_valid(max)) { + dprintf(fd, TARGET_ABI_FMT_lx "-" TARGET_ABI_FMT_lx + " %c%c%c%c %08" PRIx64 " %02x:%02x %d %s%s\n", + h2g(min), h2g(max), flag_r, flag_w, + flag_x, flag_p, offset, dev_maj, dev_min, inode, + path[0] ? " " : "", path); + } + } + + free(line); + fclose(fp); + +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) + dprintf(fd, "%08llx-%08llx rw-p %08llx 00:00 0 [stack]\n", + (unsigned long long)ts->info->stack_limit, + (unsigned long long)(ts->info->start_stack + + (TARGET_PAGE_SIZE - 1)) & TARGET_PAGE_MASK, + (unsigned long long)0); #endif - if(do_strace) - print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - switch(num) { - case TARGET_NR_exit: -#ifdef CONFIG_USE_NPTL - /* In old applications this may be used to implement _exit(2). - However in threaded applictions it is used for thread termination, - and _exit_group is used for application termination. - Do thread termination if we have more then one thread. */ - /* FIXME: This probably breaks if a signal arrives. We should probably - be disabling signals. */ - if (first_cpu->next_cpu) { - TaskState *ts; - CPUState **lastp; - CPUState *p; - - cpu_list_lock(); - lastp = &first_cpu; - p = first_cpu; - while (p && p != (CPUState *)cpu_env) { - lastp = &p->next_cpu; - p = p->next_cpu; - } - /* If we didn't find the CPU for this thread then something is - horribly wrong. */ - if (!p) - abort(); - /* Remove the CPU from the list. */ - *lastp = p->next_cpu; - cpu_list_unlock(); - ts = ((CPUState *)cpu_env)->opaque; - if (ts->child_tidptr) { - put_user_u32(0, ts->child_tidptr); - sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, - NULL, NULL, 0); - } - thread_env = NULL; - qemu_free(cpu_env); - qemu_free(ts); - pthread_exit(NULL); + return 0; +} + +static int open_self_stat(void *cpu_env, int fd) +{ + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; + abi_ulong start_stack = ts->info->start_stack; + int i; + + for (i = 0; i < 44; i++) { + char buf[128]; + int len; + uint64_t val = 0; + + if (i == 0) { + /* pid */ + val = getpid(); + snprintf(buf, sizeof(buf), "%"PRId64 " ", val); + } else if (i == 1) { + /* app name */ + snprintf(buf, sizeof(buf), "(%s) ", ts->bprm->argv[0]); + } else if (i == 27) { + /* stack bottom */ + val = start_stack; + snprintf(buf, sizeof(buf), "%"PRId64 " ", val); + } else { + /* for the rest, there is MasterCard */ + snprintf(buf, sizeof(buf), "0%c", i == 43 ? '\n' : ' '); } -#endif -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); - _exit(arg1); - ret = 0; /* avoid warning */ - break; - case TARGET_NR_read: - if (arg3 == 0) - ret = 0; - else { - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); - } - break; - case TARGET_NR_write: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(write(arg1, p, arg3)); - unlock_user(p, arg2, 0); - break; - case TARGET_NR_open: + + len = strlen(buf); + if (write(fd, buf, len) != len) { + return -1; + } + } + + return 0; +} + +static int open_self_auxv(void *cpu_env, int fd) +{ + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; + abi_ulong auxv = ts->info->saved_auxv; + abi_ulong len = ts->info->auxv_len; + char *ptr; + + /* + * Auxiliary vector is stored in target process stack. + * read in whole auxv vector and copy it to file + */ + ptr = lock_user(VERIFY_READ, auxv, len, 0); + if (ptr != NULL) { + while (len > 0) { + ssize_t r; + r = write(fd, ptr, len); + if (r <= 0) { + break; + } + len -= r; + ptr += r; + } + lseek(fd, 0, SEEK_SET); + unlock_user(ptr, auxv, len); + } + + return 0; +} + +static int is_proc_myself(const char *filename, const char *entry) +{ + if (!strncmp(filename, "/proc/", strlen("/proc/"))) { + filename += strlen("/proc/"); + if (!strncmp(filename, "self/", strlen("self/"))) { + filename += strlen("self/"); + } else if (*filename >= '1' && *filename <= '9') { + char myself[80]; + snprintf(myself, sizeof(myself), "%d/", getpid()); + if (!strncmp(filename, myself, strlen(myself))) { + filename += strlen(myself); + } else { + return 0; + } + } else { + return 0; + } + if (!strcmp(filename, entry)) { + return 1; + } + } + return 0; +} + +static int do_open(void *cpu_env, const char *pathname, int flags, mode_t mode) +{ + struct fake_open { + const char *filename; + int (*fill)(void *cpu_env, int fd); + }; + const struct fake_open *fake_open; + static const struct fake_open fakes[] = { + { "maps", open_self_maps }, + { "stat", open_self_stat }, + { "auxv", open_self_auxv }, + { NULL, NULL } + }; + + for (fake_open = fakes; fake_open->filename; fake_open++) { + if (is_proc_myself(pathname, fake_open->filename)) { + break; + } + } + + if (fake_open->filename) { + const char *tmpdir; + char filename[PATH_MAX]; + int fd, r; + + /* create temporary file to map stat to */ + tmpdir = getenv("TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + snprintf(filename, sizeof(filename), "%s/qemu-open.XXXXXX", tmpdir); + fd = mkstemp(filename); + if (fd < 0) { + return fd; + } + unlink(filename); + + if ((r = fake_open->fill(cpu_env, fd))) { + close(fd); + return r; + } + lseek(fd, 0, SEEK_SET); + + return fd; + } + + return get_errno(open(path(pathname), flags, mode)); +} + +/* do_syscall() should always have a single exit point at the end so + that actions, such as logging of syscall results, can be performed. + All errnos that do_syscall() returns must be -TARGET_. */ +abi_long do_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + CPUState *cpu = ENV_GET_CPU(cpu_env); + abi_long ret; + struct stat st; + struct statfs stfs; + void *p; + +#ifdef DEBUG + gemu_log("syscall %d", num); +#endif + if(do_strace) + print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + + switch(num) { + case TARGET_NR_exit: + /* In old applications this may be used to implement _exit(2). + However in threaded applictions it is used for thread termination, + and _exit_group is used for application termination. + Do thread termination if we have more then one thread. */ + /* FIXME: This probably breaks if a signal arrives. We should probably + be disabling signals. */ + if (first_cpu->next_cpu) { + TaskState *ts; + CPUState **lastp; + CPUState *p; + + cpu_list_lock(); + lastp = &first_cpu; + p = first_cpu; + while (p && p != cpu) { + lastp = &p->next_cpu; + p = p->next_cpu; + } + /* If we didn't find the CPU for this thread then something is + horribly wrong. */ + if (!p) { + abort(); + } + /* Remove the CPU from the list. */ + *lastp = p->next_cpu; + cpu_list_unlock(); + ts = ((CPUArchState *)cpu_env)->opaque; + if (ts->child_tidptr) { + put_user_u32(0, ts->child_tidptr); + sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, + NULL, NULL, 0); + } + thread_cpu = NULL; + object_unref(OBJECT(ENV_GET_CPU(cpu_env))); + g_free(ts); + pthread_exit(NULL); + } +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(cpu_env, arg1); + _exit(arg1); + ret = 0; /* avoid warning */ + break; + case TARGET_NR_read: + if (arg3 == 0) + ret = 0; + else { + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + } + break; + case TARGET_NR_write: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + break; + case TARGET_NR_open: if (!(p = lock_user_string(arg1))) goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); + ret = get_errno(do_open(cpu_env, p, + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); unlock_user(p, arg1, 0); break; #if defined(TARGET_NR_openat) && defined(__NR_openat) @@ -4459,7 +5200,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { int status; ret = get_errno(waitpid(arg1, &status, arg3)); - if (!is_error(ret) && arg2 + if (!is_error(ret) && arg2 && ret && put_user_s32(host_to_target_waitstatus(status), arg2)) goto efault; } @@ -4501,7 +5242,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#if defined(TARGET_NR_linkat) && defined(__NR_linkat) +#if defined(TARGET_NR_linkat) case TARGET_NR_linkat: { void * p2 = NULL; @@ -4512,7 +5253,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!p || !p2) ret = -TARGET_EFAULT; else - ret = get_errno(sys_linkat(arg1, p, arg3, p2, arg5)); + ret = get_errno(linkat(arg1, p, arg3, p2, arg5)); unlock_user(p, arg2, 0); unlock_user(p2, arg4, 0); } @@ -4524,11 +5265,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(unlink(p)); unlock_user(p, arg1, 0); break; -#if defined(TARGET_NR_unlinkat) && defined(__NR_unlinkat) +#if defined(TARGET_NR_unlinkat) case TARGET_NR_unlinkat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_unlinkat(arg1, p, arg3)); + ret = get_errno(unlinkat(arg1, p, arg3)); unlock_user(p, arg2, 0); break; #endif @@ -4541,6 +5282,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_ulong guest_envp; abi_ulong addr; char **q; + int total_size = 0; argc = 0; guest_argp = arg2; @@ -4572,6 +5314,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; if (!(*q = lock_user_string(addr))) goto execve_efault; + total_size += strlen(*q) + 1; } *q = NULL; @@ -4583,9 +5326,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; if (!(*q = lock_user_string(addr))) goto execve_efault; + total_size += strlen(*q) + 1; } *q = NULL; + /* This case will not be caught by the host's execve() if its + page size is bigger than the target's. */ + if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) { + ret = -TARGET_E2BIG; + goto execve_end; + } if (!(p = lock_user_string(arg1))) goto execve_efault; ret = get_errno(execve(p, argp, envp)); @@ -4637,11 +5387,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(mknod(p, arg2, arg3)); unlock_user(p, arg1, 0); break; -#if defined(TARGET_NR_mknodat) && defined(__NR_mknodat) +#if defined(TARGET_NR_mknodat) case TARGET_NR_mknodat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_mknodat(arg1, p, arg3, arg4)); + ret = get_errno(mknodat(arg1, p, arg3, arg4)); unlock_user(p, arg2, 0); break; #endif @@ -4740,8 +5490,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg2) { if (!lock_user_struct(VERIFY_READ, target_tbuf, arg2, 1)) goto efault; - tbuf.actime = tswapl(target_tbuf->actime); - tbuf.modtime = tswapl(target_tbuf->modtime); + tbuf.actime = tswapal(target_tbuf->actime); + tbuf.modtime = tswapal(target_tbuf->modtime); unlock_user_struct(target_tbuf, arg2, 0); host_tbuf = &tbuf; } else { @@ -4772,7 +5522,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#if defined(TARGET_NR_futimesat) && defined(__NR_futimesat) +#if defined(TARGET_NR_futimesat) case TARGET_NR_futimesat: { struct timeval *tvp, tv[2]; @@ -4787,7 +5537,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_futimesat(arg1, path(p), tvp)); + ret = get_errno(futimesat(arg1, path(p), tvp)); unlock_user(p, arg2, 0); } break; @@ -4810,7 +5560,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_faccessat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_faccessat(arg1, p, arg3)); + ret = get_errno(faccessat(arg1, p, arg3, 0)); unlock_user(p, arg2, 0); break; #endif @@ -4843,7 +5593,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#if defined(TARGET_NR_renameat) && defined(__NR_renameat) +#if defined(TARGET_NR_renameat) case TARGET_NR_renameat: { void *p2; @@ -4852,7 +5602,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!p || !p2) ret = -TARGET_EFAULT; else - ret = get_errno(sys_renameat(arg1, p, arg3, p2)); + ret = get_errno(renameat(arg1, p, arg3, p2)); unlock_user(p2, arg4, 0); unlock_user(p, arg2, 0); } @@ -4864,11 +5614,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(mkdir(p, arg2)); unlock_user(p, arg1, 0); break; -#if defined(TARGET_NR_mkdirat) && defined(__NR_mkdirat) +#if defined(TARGET_NR_mkdirat) case TARGET_NR_mkdirat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_mkdirat(arg1, p, arg3)); + ret = get_errno(mkdirat(arg1, p, arg3)); unlock_user(p, arg2, 0); break; #endif @@ -4886,7 +5636,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #ifdef TARGET_NR_pipe2 case TARGET_NR_pipe2: - ret = do_pipe(cpu_env, arg1, arg2, 1); + ret = do_pipe(cpu_env, arg1, + target_to_host_bitmask(arg2, fcntl_flags_tbl), 1); break; #endif case TARGET_NR_times: @@ -4898,10 +5649,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, tmsp = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_tms), 0); if (!tmsp) goto efault; - tmsp->tms_utime = tswapl(host_to_target_clock_t(tms.tms_utime)); - tmsp->tms_stime = tswapl(host_to_target_clock_t(tms.tms_stime)); - tmsp->tms_cutime = tswapl(host_to_target_clock_t(tms.tms_cutime)); - tmsp->tms_cstime = tswapl(host_to_target_clock_t(tms.tms_cstime)); + tmsp->tms_utime = tswapal(host_to_target_clock_t(tms.tms_utime)); + tmsp->tms_stime = tswapal(host_to_target_clock_t(tms.tms_stime)); + tmsp->tms_cutime = tswapal(host_to_target_clock_t(tms.tms_cutime)); + tmsp->tms_cstime = tswapal(host_to_target_clock_t(tms.tms_cstime)); } if (!is_error(ret)) ret = host_to_target_clock_t(ret); @@ -5171,11 +5922,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_to_host_old_sigset(&set, &mask); ret = get_errno(sigprocmask(how, &set, &oldset)); - if (!is_error(ret)) { host_to_target_old_sigset(&mask, &oldset); ret = mask; - ((CPUAlphaState *)cpu_env)->[IR_V0] = 0; /* force no error */ + ((CPUAlphaState *)cpu_env)->ir[IR_V0] = 0; /* force no error */ } #else sigset_t set, oldset, *set_ptr; @@ -5360,7 +6110,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_setrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) @@ -5373,7 +6123,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_getrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; @@ -5414,8 +6164,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(settimeofday(&tv, NULL)); } break; -#ifdef TARGET_NR_select +#if defined(TARGET_NR_select) case TARGET_NR_select: +#if defined(TARGET_S390X) || defined(TARGET_ALPHA) + ret = do_select(arg1, arg2, arg3, arg4, arg5); +#else { struct target_sel_arg_struct *sel; abi_ulong inp, outp, exp, tvp; @@ -5423,19 +6176,120 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!lock_user_struct(VERIFY_READ, sel, arg1, 1)) goto efault; - nsel = tswapl(sel->n); - inp = tswapl(sel->inp); - outp = tswapl(sel->outp); - exp = tswapl(sel->exp); - tvp = tswapl(sel->tvp); + nsel = tswapal(sel->n); + inp = tswapal(sel->inp); + outp = tswapal(sel->outp); + exp = tswapal(sel->exp); + tvp = tswapal(sel->tvp); unlock_user_struct(sel, arg1, 0); ret = do_select(nsel, inp, outp, exp, tvp); } +#endif break; #endif #ifdef TARGET_NR_pselect6 case TARGET_NR_pselect6: - goto unimplemented_nowarn; + { + abi_long rfd_addr, wfd_addr, efd_addr, n, ts_addr; + fd_set rfds, wfds, efds; + fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; + struct timespec ts, *ts_ptr; + + /* + * The 6th arg is actually two args smashed together, + * so we cannot use the C library. + */ + sigset_t set; + struct { + sigset_t *set; + size_t size; + } sig, *sig_ptr; + + abi_ulong arg_sigset, arg_sigsize, *arg7; + target_sigset_t *target_sigset; + + n = arg1; + rfd_addr = arg2; + wfd_addr = arg3; + efd_addr = arg4; + ts_addr = arg5; + + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); + if (ret) { + goto fail; + } + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); + if (ret) { + goto fail; + } + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); + if (ret) { + goto fail; + } + + /* + * This takes a timespec, and not a timeval, so we cannot + * use the do_select() helper ... + */ + if (ts_addr) { + if (target_to_host_timespec(&ts, ts_addr)) { + goto efault; + } + ts_ptr = &ts; + } else { + ts_ptr = NULL; + } + + /* Extract the two packed args for the sigset */ + if (arg6) { + sig_ptr = &sig; + sig.size = _NSIG / 8; + + arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1); + if (!arg7) { + goto efault; + } + arg_sigset = tswapal(arg7[0]); + arg_sigsize = tswapal(arg7[1]); + unlock_user(arg7, arg6, 0); + + if (arg_sigset) { + sig.set = &set; + if (arg_sigsize != sizeof(*target_sigset)) { + /* Like the kernel, we enforce correct size sigsets */ + ret = -TARGET_EINVAL; + goto fail; + } + target_sigset = lock_user(VERIFY_READ, arg_sigset, + sizeof(*target_sigset), 1); + if (!target_sigset) { + goto efault; + } + target_to_host_sigset(&set, target_sigset); + unlock_user(target_sigset, arg_sigset, 0); + } else { + sig.set = NULL; + } + } else { + sig_ptr = NULL; + } + + ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, sig_ptr)); + + if (!is_error(ret)) { + if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) + goto efault; + if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n)) + goto efault; + if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) + goto efault; + + if (ts_addr && host_to_target_timespec(ts_addr, &ts)) + goto efault; + } + } + break; #endif case TARGET_NR_symlink: { @@ -5450,7 +6304,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#if defined(TARGET_NR_symlinkat) && defined(__NR_symlinkat) +#if defined(TARGET_NR_symlinkat) case TARGET_NR_symlinkat: { void *p2; @@ -5459,7 +6313,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!p || !p2) ret = -TARGET_EFAULT; else - ret = get_errno(sys_symlinkat(p, arg2, p2)); + ret = get_errno(symlinkat(p, arg2, p2)); unlock_user(p2, arg3, 0); unlock_user(p, arg1, 0); } @@ -5471,35 +6325,39 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif case TARGET_NR_readlink: { - void *p2, *temp; + void *p2; p = lock_user_string(arg1); p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0); - if (!p || !p2) + if (!p || !p2) { ret = -TARGET_EFAULT; - else { - if (strncmp((const char *)p, "/proc/self/exe", 14) == 0) { - char real[PATH_MAX]; - temp = realpath(exec_path,real); - ret = (temp==NULL) ? get_errno(-1) : strlen(real) ; - snprintf((char *)p2, arg3, "%s", real); - } - else - ret = get_errno(readlink(path(p), p2, arg3)); + } else if (is_proc_myself((const char *)p, "exe")) { + char real[PATH_MAX], *temp; + temp = realpath(exec_path, real); + ret = temp == NULL ? get_errno(-1) : strlen(real) ; + snprintf((char *)p2, arg3, "%s", real); + } else { + ret = get_errno(readlink(path(p), p2, arg3)); } unlock_user(p2, arg2, ret); unlock_user(p, arg1, 0); } break; -#if defined(TARGET_NR_readlinkat) && defined(__NR_readlinkat) +#if defined(TARGET_NR_readlinkat) case TARGET_NR_readlinkat: { void *p2; p = lock_user_string(arg2); p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0); - if (!p || !p2) - ret = -TARGET_EFAULT; - else - ret = get_errno(sys_readlinkat(arg1, path(p), p2, arg4)); + if (!p || !p2) { + ret = -TARGET_EFAULT; + } else if (is_proc_myself((const char *)p, "exe")) { + char real[PATH_MAX], *temp; + temp = realpath(exec_path, real); + ret = temp == NULL ? get_errno(-1) : strlen(real) ; + snprintf((char *)p2, arg4, "%s", real); + } else { + ret = get_errno(readlinkat(arg1, path(p), p2, arg4)); + } unlock_user(p2, arg3, ret); unlock_user(p, arg2, 0); } @@ -5518,25 +6376,38 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif case TARGET_NR_reboot: - goto unimplemented; + if (arg3 == LINUX_REBOOT_CMD_RESTART2) { + /* arg4 must be ignored in all other cases */ + p = lock_user_string(arg4); + if (!p) { + goto efault; + } + ret = get_errno(reboot(arg1, arg2, arg3, p)); + unlock_user(p, arg4, 0); + } else { + ret = get_errno(reboot(arg1, arg2, arg3, NULL)); + } + break; #ifdef TARGET_NR_readdir case TARGET_NR_readdir: goto unimplemented; #endif #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || \ + defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) \ + || defined(TARGET_S390X) { abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; if (!(v = lock_user(VERIFY_READ, arg1, 6 * sizeof(abi_ulong), 1))) goto efault; - v1 = tswapl(v[0]); - v2 = tswapl(v[1]); - v3 = tswapl(v[2]); - v4 = tswapl(v[3]); - v5 = tswapl(v[4]); - v6 = tswapl(v[5]); + v1 = tswapal(v[0]); + v2 = tswapal(v[1]); + v3 = tswapal(v[2]); + v4 = tswapal(v[3]); + v5 = tswapal(v[4]); + v6 = tswapal(v[5]); unlock_user(v, arg1, 0); ret = get_errno(target_mmap(v1, v2, v3, target_to_host_bitmask(v4, mmap_flags_tbl), @@ -5566,7 +6437,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_mprotect: { - TaskState *ts = ((CPUState *)cpu_env)->opaque; + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; /* Special hack to detect libc making the stack executable. */ if ((arg3 & PROT_GROWSDOWN) && arg1 >= ts->info->stack_limit @@ -5621,19 +6492,30 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_fchmod: ret = get_errno(fchmod(arg1, arg2)); break; -#if defined(TARGET_NR_fchmodat) && defined(__NR_fchmodat) +#if defined(TARGET_NR_fchmodat) case TARGET_NR_fchmodat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_fchmodat(arg1, p, arg3)); + ret = get_errno(fchmodat(arg1, p, arg3, 0)); unlock_user(p, arg2, 0); break; #endif case TARGET_NR_getpriority: - /* libc does special remapping of the return value of - * sys_getpriority() so it's just easiest to call - * sys_getpriority() directly rather than through libc. */ - ret = get_errno(sys_getpriority(arg1, arg2)); + /* Note that negative values are valid for getpriority, so we must + differentiate based on errno settings. */ + errno = 0; + ret = getpriority(arg1, arg2); + if (ret == -1 && errno != 0) { + ret = -host_to_target_errno(errno); + break; + } +#ifdef TARGET_ALPHA + /* Return value is the unbiased priority. Signal no error. */ + ((CPUAlphaState *)cpu_env)->ir[IR_V0] = 0; +#else + /* Return value is a biased priority to avoid negative numbers. */ + ret = 20 - ret; +#endif break; case TARGET_NR_setpriority: ret = get_errno(setpriority(arg1, arg2, arg3)); @@ -5663,6 +6545,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); __put_user(stfs.f_namelen, &target_stfs->f_namelen); + __put_user(stfs.f_frsize, &target_stfs->f_frsize); + memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg2, 1); } break; @@ -5691,6 +6575,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); __put_user(stfs.f_namelen, &target_stfs->f_namelen); + __put_user(stfs.f_frsize, &target_stfs->f_frsize); + memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg3, 1); } break; @@ -5709,7 +6595,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_accept case TARGET_NR_accept: - ret = do_accept(arg1, arg2, arg3); + ret = do_accept4(arg1, arg2, arg3, 0); + break; +#endif +#ifdef TARGET_NR_accept4 + case TARGET_NR_accept4: +#ifdef CONFIG_ACCEPT4 + ret = do_accept4(arg1, arg2, arg3, arg4); +#else + goto unimplemented; +#endif break; #endif #ifdef TARGET_NR_bind @@ -5893,8 +6788,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_syscall case TARGET_NR_syscall: - ret = do_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); - break; + ret = do_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, arg5, + arg6, arg7, arg8, 0); + break; #endif case TARGET_NR_wait4: { @@ -5908,7 +6804,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, rusage_ptr = NULL; ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr)); if (!is_error(ret)) { - if (status_ptr) { + if (status_ptr && ret) { status = host_to_target_waitstatus(status); if (put_user_s32(status, status_ptr)) goto efault; @@ -5965,7 +6861,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_semop case TARGET_NR_semop: - ret = get_errno(do_semop(arg1, arg2, arg3)); + ret = do_semop(arg1, arg2, arg3); break; #endif #ifdef TARGET_NR_semctl @@ -6017,12 +6913,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(fsync(arg1)); break; case TARGET_NR_clone: -#if defined(TARGET_SH4) || defined(TARGET_ALPHA) - ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4)); -#elif defined(TARGET_CRIS) - ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg4, arg5)); -#else + /* Linux manages to have three different orderings for its + * arguments to clone(); the BACKWARDS and BACKWARDS2 defines + * match the kernel's CONFIG_CLONE_* settings. + * Microblaze is further special in that it uses a sixth + * implicit argument to clone for the TLS pointer. + */ +#if defined(TARGET_MICROBLAZE) + ret = get_errno(do_fork(cpu_env, arg1, arg2, arg4, arg6, arg5)); +#elif defined(TARGET_CLONE_BACKWARDS) ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5)); +#elif defined(TARGET_CLONE_BACKWARDS2) + ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg5, arg4)); +#else + ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4)); #endif break; #ifdef __NR_exit_group @@ -6109,20 +7013,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR__llseek /* Not on alpha */ case TARGET_NR__llseek: { + int64_t res; #if !defined(__NR_llseek) - ret = get_errno(lseek(arg1, ((uint64_t )arg2 << 32) | arg3, arg5)); - if (put_user_s64(ret, arg4)) - goto efault; + res = lseek(arg1, ((uint64_t)arg2 << 32) | arg3, arg5); + if (res == -1) { + ret = get_errno(res); + } else { + ret = 0; + } #else - int64_t res; ret = get_errno(_llseek(arg1, arg2, arg3, &res, arg5)); - if (put_user_s64(res, arg4)) - goto efault; #endif + if ((ret == 0) && put_user_s64(res, arg4)) { + goto efault; + } } break; #endif case TARGET_NR_getdents: +#ifdef __NR_getdents #if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 { struct target_dirent *target_dirp; @@ -6150,15 +7059,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, tde = target_dirp; while (len > 0) { reclen = de->d_reclen; - treclen = reclen - (2 * (sizeof(long) - sizeof(abi_long))); + tnamelen = reclen - offsetof(struct linux_dirent, d_name); + assert(tnamelen >= 0); + treclen = tnamelen + offsetof(struct target_dirent, d_name); + assert(count1 + treclen <= count); tde->d_reclen = tswap16(treclen); - tde->d_ino = tswapl(de->d_ino); - tde->d_off = tswapl(de->d_off); - tnamelen = treclen - (2 * sizeof(abi_long) + 2); - if (tnamelen > 256) - tnamelen = 256; - /* XXX: may not be correct */ - pstrcpy(tde->d_name, tnamelen, de->d_name); + tde->d_ino = tswapal(de->d_ino); + tde->d_off = tswapal(de->d_off); + memcpy(tde->d_name, de->d_name, tnamelen); de = (struct linux_dirent *)((char *)de + reclen); len -= reclen; tde = (struct target_dirent *)((char *)tde + treclen); @@ -6196,6 +7104,61 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(dirp, arg2, ret); } #endif +#else + /* Implement getdents in terms of getdents64 */ + { + struct linux_dirent64 *dirp; + abi_long count = arg3; + + dirp = lock_user(VERIFY_WRITE, arg2, count, 0); + if (!dirp) { + goto efault; + } + ret = get_errno(sys_getdents64(arg1, dirp, count)); + if (!is_error(ret)) { + /* Convert the dirent64 structs to target dirent. We do this + * in-place, since we can guarantee that a target_dirent is no + * larger than a dirent64; however this means we have to be + * careful to read everything before writing in the new format. + */ + struct linux_dirent64 *de; + struct target_dirent *tde; + int len = ret; + int tlen = 0; + + de = dirp; + tde = (struct target_dirent *)dirp; + while (len > 0) { + int namelen, treclen; + int reclen = de->d_reclen; + uint64_t ino = de->d_ino; + int64_t off = de->d_off; + uint8_t type = de->d_type; + + namelen = strlen(de->d_name); + treclen = offsetof(struct target_dirent, d_name) + + namelen + 2; + treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long)); + + memmove(tde->d_name, de->d_name, namelen + 1); + tde->d_ino = tswapal(ino); + tde->d_off = tswapal(off); + tde->d_reclen = tswap16(treclen); + /* The target_dirent type is in what was formerly a padding + * byte at the end of the structure: + */ + *(((char *)tde) + treclen - 1) = type; + + de = (struct linux_dirent64 *)((char *)de + reclen); + tde = (struct target_dirent *)((char *)tde + treclen); + len -= reclen; + tlen += treclen; + } + ret = tlen; + } + unlock_user(dirp, arg2, ret); + } +#endif break; #if defined(TARGET_NR_getdents64) && defined(__NR_getdents64) case TARGET_NR_getdents64: @@ -6225,13 +7188,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif /* TARGET_NR_getdents64 */ -#ifdef TARGET_NR__newselect +#if defined(TARGET_NR__newselect) case TARGET_NR__newselect: ret = do_select(arg1, arg2, arg3, arg4, arg5); break; #endif -#ifdef TARGET_NR_poll +#if defined(TARGET_NR_poll) || defined(TARGET_NR_ppoll) +# ifdef TARGET_NR_poll case TARGET_NR_poll: +# endif +# ifdef TARGET_NR_ppoll + case TARGET_NR_ppoll: +# endif { struct target_pollfd *target_pfd; unsigned int nfds = arg2; @@ -6242,20 +7210,57 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); if (!target_pfd) goto efault; + pfd = alloca(sizeof(struct pollfd) * nfds); for(i = 0; i < nfds; i++) { pfd[i].fd = tswap32(target_pfd[i].fd); pfd[i].events = tswap16(target_pfd[i].events); } - ret = get_errno(poll(pfd, nfds, timeout)); + +# ifdef TARGET_NR_ppoll + if (num == TARGET_NR_ppoll) { + struct timespec _timeout_ts, *timeout_ts = &_timeout_ts; + target_sigset_t *target_set; + sigset_t _set, *set = &_set; + + if (arg3) { + if (target_to_host_timespec(timeout_ts, arg3)) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + } else { + timeout_ts = NULL; + } + + if (arg4) { + target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1); + if (!target_set) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + target_to_host_sigset(set, target_set); + } else { + set = NULL; + } + + ret = get_errno(sys_ppoll(pfd, nfds, timeout_ts, set, _NSIG/8)); + + if (!is_error(ret) && arg3) { + host_to_target_timespec(arg3, timeout_ts); + } + if (arg4) { + unlock_user(target_set, arg4, 0); + } + } else +# endif + ret = get_errno(poll(pfd, nfds, timeout)); + if (!is_error(ret)) { for(i = 0; i < nfds; i++) { target_pfd[i].revents = tswap16(pfd[i].revents); } - ret += nfds * (sizeof(struct target_pollfd) - - sizeof(struct pollfd)); } - unlock_user(target_pfd, arg1, ret); + unlock_user(target_pfd, arg1, sizeof(struct target_pollfd) * nfds); } break; #endif @@ -6266,26 +7271,24 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_readv: { - int count = arg3; - struct iovec *vec; - - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0) - goto efault; - ret = get_errno(readv(arg1, vec, count)); - unlock_iovec(vec, arg2, count, 1); + struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0); + if (vec != NULL) { + ret = get_errno(readv(arg1, vec, arg3)); + unlock_iovec(vec, arg2, arg3, 1); + } else { + ret = -host_to_target_errno(errno); + } } break; case TARGET_NR_writev: { - int count = arg3; - struct iovec *vec; - - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) - goto efault; - ret = get_errno(writev(arg1, vec, count)); - unlock_iovec(vec, arg2, count, 0); + struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); + if (vec != NULL) { + ret = get_errno(writev(arg1, vec, arg3)); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } } break; case TARGET_NR_getsid: @@ -6301,6 +7304,56 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, return value. */ ret = -TARGET_ENOTDIR; break; + case TARGET_NR_sched_getaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_getaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask)); + + if (!is_error(ret)) { + if (copy_to_user(arg3, mask, ret)) { + goto efault; + } + } + } + break; + case TARGET_NR_sched_setaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_setaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + if (!lock_user_struct(VERIFY_READ, p, arg3, 1)) { + goto efault; + } + memcpy(mask, p, arg2); + unlock_user_struct(p, arg2, 0); + + ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); + } + break; case TARGET_NR_sched_setparam: { struct sched_param *target_schp; @@ -6377,61 +7430,72 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, goto unimplemented; #endif case TARGET_NR_prctl: - switch (arg1) - { - case PR_GET_PDEATHSIG: - { - int deathsig; - ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); - if (!is_error(ret) && arg2 - && put_user_ual(deathsig, arg2)) - goto efault; - } - break; - default: - ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); - break; + switch (arg1) { + case PR_GET_PDEATHSIG: + { + int deathsig; + ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); + if (!is_error(ret) && arg2 + && put_user_ual(deathsig, arg2)) { + goto efault; } - break; -#ifdef TARGET_NR_arch_prctl - case TARGET_NR_arch_prctl: -#if defined(TARGET_I386) && !defined(TARGET_ABI32) - ret = do_arch_prctl(cpu_env, arg1, arg2); - break; -#else - goto unimplemented; -#endif -#endif -#ifdef TARGET_NR_pread - case TARGET_NR_pread: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - arg4 = arg5; + break; + } +#ifdef PR_GET_NAME + case PR_GET_NAME: + { + void *name = lock_user(VERIFY_WRITE, arg2, 16, 1); + if (!name) { + goto efault; + } + ret = get_errno(prctl(arg1, (unsigned long)name, + arg3, arg4, arg5)); + unlock_user(name, arg2, 16); + break; + } + case PR_SET_NAME: + { + void *name = lock_user(VERIFY_READ, arg2, 16, 1); + if (!name) { + goto efault; + } + ret = get_errno(prctl(arg1, (unsigned long)name, + arg3, arg4, arg5)); + unlock_user(name, arg2, 0); + break; + } #endif - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(pread(arg1, p, arg3, arg4)); - unlock_user(p, arg2, ret); + default: + /* Most prctl options have no pointer arguments */ + ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); + break; + } break; - case TARGET_NR_pwrite: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - arg4 = arg5; -#endif - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(pwrite(arg1, p, arg3, arg4)); - unlock_user(p, arg2, 0); +#ifdef TARGET_NR_arch_prctl + case TARGET_NR_arch_prctl: +#if defined(TARGET_I386) && !defined(TARGET_ABI32) + ret = do_arch_prctl(cpu_env, arg1, arg2); break; +#else + goto unimplemented; +#endif #endif #ifdef TARGET_NR_pread64 case TARGET_NR_pread64: + if (regpairs_aligned(cpu_env)) { + arg4 = arg5; + arg5 = arg6; + } if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; ret = get_errno(pread64(arg1, p, arg3, target_offset64(arg4, arg5))); unlock_user(p, arg2, ret); break; case TARGET_NR_pwrite64: + if (regpairs_aligned(cpu_env)) { + arg4 = arg5; + arg5 = arg6; + } if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) goto efault; ret = get_errno(pwrite64(arg1, p, arg3, target_offset64(arg4, arg5))); @@ -6451,14 +7515,64 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_sigaltstack: #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ - defined(TARGET_M68K) - ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUState *)cpu_env)); + defined(TARGET_M68K) || defined(TARGET_S390X) || defined(TARGET_OPENRISC) + ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUArchState *)cpu_env)); break; #else goto unimplemented; #endif + +#ifdef CONFIG_SENDFILE case TARGET_NR_sendfile: + { + off_t *offp = NULL; + off_t off; + if (arg3) { + ret = get_user_sal(off, arg3); + if (is_error(ret)) { + break; + } + offp = &off; + } + ret = get_errno(sendfile(arg1, arg2, offp, arg4)); + if (!is_error(ret) && arg3) { + abi_long ret2 = put_user_sal(off, arg3); + if (is_error(ret2)) { + ret = ret2; + } + } + break; + } +#ifdef TARGET_NR_sendfile64 + case TARGET_NR_sendfile64: + { + off_t *offp = NULL; + off_t off; + if (arg3) { + ret = get_user_s64(off, arg3); + if (is_error(ret)) { + break; + } + offp = &off; + } + ret = get_errno(sendfile(arg1, arg2, offp, arg4)); + if (!is_error(ret) && arg3) { + abi_long ret2 = put_user_s64(off, arg3); + if (is_error(ret2)) { + ret = ret2; + } + } + break; + } +#endif +#else + case TARGET_NR_sendfile: +#ifdef TARGET_NR_sendfile64 + case TARGET_NR_sendfile64: +#endif goto unimplemented; +#endif + #ifdef TARGET_NR_getpmsg case TARGET_NR_getpmsg: goto unimplemented; @@ -6477,7 +7591,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_ugetrlimit: { struct rlimit rlim; - ret = get_errno(getrlimit(arg1, &rlim)); + int resource = target_to_host_resource(arg1); + ret = get_errno(getrlimit(resource, &rlim)); if (!is_error(ret)) { struct target_rlimit *target_rlim; if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) @@ -6529,8 +7644,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_stat64(cpu_env, arg2, &st); break; #endif -#if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) && \ - (defined(__NR_fstatat64) || defined(__NR_newfstatat)) +#if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) #ifdef TARGET_NR_fstatat64 case TARGET_NR_fstatat64: #endif @@ -6539,34 +7653,37 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif if (!(p = lock_user_string(arg2))) goto efault; -#ifdef __NR_fstatat64 - ret = get_errno(sys_fstatat64(arg1, path(p), &st, arg4)); -#else - ret = get_errno(sys_newfstatat(arg1, path(p), &st, arg4)); -#endif + ret = get_errno(fstatat(arg1, path(p), &st, arg4)); if (!is_error(ret)) ret = host_to_target_stat64(cpu_env, arg3, &st); break; #endif -#ifdef USE_UID16 case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#ifdef TARGET_NR_getuid case TARGET_NR_getuid: ret = get_errno(high2lowuid(getuid())); break; +#endif +#ifdef TARGET_NR_getgid case TARGET_NR_getgid: ret = get_errno(high2lowgid(getgid())); break; +#endif +#ifdef TARGET_NR_geteuid case TARGET_NR_geteuid: ret = get_errno(high2lowuid(geteuid())); break; +#endif +#ifdef TARGET_NR_getegid case TARGET_NR_getegid: ret = get_errno(high2lowgid(getegid())); break; +#endif case TARGET_NR_setreuid: ret = get_errno(setreuid(low2highuid(arg1), low2highuid(arg2))); break; @@ -6576,7 +7693,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_getgroups: { int gidsetsize = arg1; - uint16_t *target_grouplist; + target_id *target_grouplist; gid_t *grouplist; int i; @@ -6585,42 +7702,45 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (gidsetsize == 0) break; if (!is_error(ret)) { - target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 2, 0); + target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0); if (!target_grouplist) goto efault; for(i = 0;i < ret; i++) - target_grouplist[i] = tswap16(grouplist[i]); - unlock_user(target_grouplist, arg2, gidsetsize * 2); + target_grouplist[i] = tswapid(high2lowgid(grouplist[i])); + unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id)); } } break; case TARGET_NR_setgroups: { int gidsetsize = arg1; - uint16_t *target_grouplist; - gid_t *grouplist; + target_id *target_grouplist; + gid_t *grouplist = NULL; int i; - - grouplist = alloca(gidsetsize * sizeof(gid_t)); - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1); - if (!target_grouplist) { - ret = -TARGET_EFAULT; - goto fail; + if (gidsetsize) { + grouplist = alloca(gidsetsize * sizeof(gid_t)); + target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1); + if (!target_grouplist) { + ret = -TARGET_EFAULT; + goto fail; + } + for (i = 0; i < gidsetsize; i++) { + grouplist[i] = low2highgid(tswapid(target_grouplist[i])); + } + unlock_user(target_grouplist, arg2, 0); } - for(i = 0;i < gidsetsize; i++) - grouplist[i] = tswap16(target_grouplist[i]); - unlock_user(target_grouplist, arg2, 0); ret = get_errno(setgroups(gidsetsize, grouplist)); } break; case TARGET_NR_fchown: ret = get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); break; -#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat) +#if defined(TARGET_NR_fchownat) case TARGET_NR_fchownat: if (!(p = lock_user_string(arg2))) goto efault; - ret = get_errno(sys_fchownat(arg1, p, low2highuid(arg3), low2highgid(arg4), arg5)); + ret = get_errno(fchownat(arg1, p, low2highuid(arg3), + low2highgid(arg4), arg5)); unlock_user(p, arg2, 0); break; #endif @@ -6684,7 +7804,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_setfsgid: ret = get_errno(setfsgid(arg1)); break; -#endif /* USE_UID16 */ #ifdef TARGET_NR_lchown32 case TARGET_NR_lchown32: @@ -6766,13 +7885,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -TARGET_EOPNOTSUPP; switch (arg1) { case TARGET_SSI_IEEE_FP_CONTROL: - case TARGET_SSI_IEEE_RAISE_EXCEPTION: { uint64_t swcr, fpcr, orig_fpcr; - if (get_user_u64 (swcr, arg2)) + if (get_user_u64 (swcr, arg2)) { goto efault; - orig_fpcr = cpu_alpha_load_fpcr (cpu_env); + } + orig_fpcr = cpu_alpha_load_fpcr(cpu_env); fpcr = orig_fpcr & FPCR_DYN_MASK; /* Copied from linux ieee_swcr_to_fpcr. */ @@ -6786,16 +7905,57 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, fpcr |= (swcr & SWCR_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0); fpcr |= (~swcr & SWCR_TRAP_ENABLE_DNO) << 41; - cpu_alpha_store_fpcr (cpu_env, fpcr); + cpu_alpha_store_fpcr(cpu_env, fpcr); + ret = 0; + } + break; + + case TARGET_SSI_IEEE_RAISE_EXCEPTION: + { + uint64_t exc, fpcr, orig_fpcr; + int si_code; + + if (get_user_u64(exc, arg2)) { + goto efault; + } + + orig_fpcr = cpu_alpha_load_fpcr(cpu_env); + + /* We only add to the exception status here. */ + fpcr = orig_fpcr | ((exc & SWCR_STATUS_MASK) << 35); + + cpu_alpha_store_fpcr(cpu_env, fpcr); ret = 0; - if (arg1 == TARGET_SSI_IEEE_RAISE_EXCEPTION) { - /* Old exceptions are not signaled. */ - fpcr &= ~(orig_fpcr & FPCR_STATUS_MASK); + /* Old exceptions are not signaled. */ + fpcr &= ~(orig_fpcr & FPCR_STATUS_MASK); - /* If any exceptions set by this call, and are unmasked, - send a signal. */ - /* ??? FIXME */ + /* If any exceptions set by this call, + and are unmasked, send a signal. */ + si_code = 0; + if ((fpcr & (FPCR_INE | FPCR_INED)) == FPCR_INE) { + si_code = TARGET_FPE_FLTRES; + } + if ((fpcr & (FPCR_UNF | FPCR_UNFD)) == FPCR_UNF) { + si_code = TARGET_FPE_FLTUND; + } + if ((fpcr & (FPCR_OVF | FPCR_OVFD)) == FPCR_OVF) { + si_code = TARGET_FPE_FLTOVF; + } + if ((fpcr & (FPCR_DZE | FPCR_DZED)) == FPCR_DZE) { + si_code = TARGET_FPE_FLTDIV; + } + if ((fpcr & (FPCR_INV | FPCR_INVD)) == FPCR_INV) { + si_code = TARGET_FPE_FLTINV; + } + if (si_code != 0) { + target_siginfo_t info; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = si_code; + info._sifields._sigfault._addr + = ((CPUArchState *)cpu_env)->pc; + queue_signal((CPUArchState *)cpu_env, info.si_signo, &info); } } break; @@ -6814,7 +7974,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_osf_sigprocmask: { abi_ulong mask; - int how = arg1; + int how; sigset_t set, oldset; switch(arg1) { @@ -6833,7 +7993,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } mask = arg2; target_to_host_old_sigset(&set, &mask); - sigprocmask(arg1, &set, &oldset); + sigprocmask(how, &set, &oldset); host_to_target_old_sigset(&mask, &oldset); ret = mask; } @@ -7036,7 +8196,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_madvise case TARGET_NR_madvise: /* A straight passthrough may not be safe because qemu sometimes - turns private flie-backed mappings into anonymous mappings. + turns private file-backed mappings into anonymous mappings. This will break MADV_DONTNEED. This is a hint, so ignoring and returning success is ok. */ ret = get_errno(0); @@ -7053,8 +8213,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif cmd = target_to_host_fcntl_cmd(arg2); - if (cmd == -TARGET_EINVAL) - return cmd; + if (cmd == -TARGET_EINVAL) { + ret = cmd; + break; + } switch(arg2) { case TARGET_F_GETLK64: @@ -7161,36 +8323,185 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; arg4 = arg5; } -#endif ret = get_errno(readahead(arg1, ((off64_t)arg3 << 32) | arg2, arg4)); #else ret = get_errno(readahead(arg1, arg2, arg3)); #endif break; #endif +#ifdef CONFIG_ATTR #ifdef TARGET_NR_setxattr + case TARGET_NR_listxattr: + case TARGET_NR_llistxattr: + { + void *p, *b = 0; + if (arg2) { + b = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (!b) { + ret = -TARGET_EFAULT; + break; + } + } + p = lock_user_string(arg1); + if (p) { + if (num == TARGET_NR_listxattr) { + ret = get_errno(listxattr(p, b, arg3)); + } else { + ret = get_errno(llistxattr(p, b, arg3)); + } + } else { + ret = -TARGET_EFAULT; + } + unlock_user(p, arg1, 0); + unlock_user(b, arg2, arg3); + break; + } + case TARGET_NR_flistxattr: + { + void *b = 0; + if (arg2) { + b = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (!b) { + ret = -TARGET_EFAULT; + break; + } + } + ret = get_errno(flistxattr(arg1, b, arg3)); + unlock_user(b, arg2, arg3); + break; + } case TARGET_NR_setxattr: case TARGET_NR_lsetxattr: + { + void *p, *n, *v = 0; + if (arg3) { + v = lock_user(VERIFY_READ, arg3, arg4, 1); + if (!v) { + ret = -TARGET_EFAULT; + break; + } + } + p = lock_user_string(arg1); + n = lock_user_string(arg2); + if (p && n) { + if (num == TARGET_NR_setxattr) { + ret = get_errno(setxattr(p, n, v, arg4, arg5)); + } else { + ret = get_errno(lsetxattr(p, n, v, arg4, arg5)); + } + } else { + ret = -TARGET_EFAULT; + } + unlock_user(p, arg1, 0); + unlock_user(n, arg2, 0); + unlock_user(v, arg3, 0); + } + break; case TARGET_NR_fsetxattr: + { + void *n, *v = 0; + if (arg3) { + v = lock_user(VERIFY_READ, arg3, arg4, 1); + if (!v) { + ret = -TARGET_EFAULT; + break; + } + } + n = lock_user_string(arg2); + if (n) { + ret = get_errno(fsetxattr(arg1, n, v, arg4, arg5)); + } else { + ret = -TARGET_EFAULT; + } + unlock_user(n, arg2, 0); + unlock_user(v, arg3, 0); + } + break; case TARGET_NR_getxattr: case TARGET_NR_lgetxattr: + { + void *p, *n, *v = 0; + if (arg3) { + v = lock_user(VERIFY_WRITE, arg3, arg4, 0); + if (!v) { + ret = -TARGET_EFAULT; + break; + } + } + p = lock_user_string(arg1); + n = lock_user_string(arg2); + if (p && n) { + if (num == TARGET_NR_getxattr) { + ret = get_errno(getxattr(p, n, v, arg4)); + } else { + ret = get_errno(lgetxattr(p, n, v, arg4)); + } + } else { + ret = -TARGET_EFAULT; + } + unlock_user(p, arg1, 0); + unlock_user(n, arg2, 0); + unlock_user(v, arg3, arg4); + } + break; case TARGET_NR_fgetxattr: - case TARGET_NR_listxattr: - case TARGET_NR_llistxattr: - case TARGET_NR_flistxattr: + { + void *n, *v = 0; + if (arg3) { + v = lock_user(VERIFY_WRITE, arg3, arg4, 0); + if (!v) { + ret = -TARGET_EFAULT; + break; + } + } + n = lock_user_string(arg2); + if (n) { + ret = get_errno(fgetxattr(arg1, n, v, arg4)); + } else { + ret = -TARGET_EFAULT; + } + unlock_user(n, arg2, 0); + unlock_user(v, arg3, arg4); + } + break; case TARGET_NR_removexattr: case TARGET_NR_lremovexattr: + { + void *p, *n; + p = lock_user_string(arg1); + n = lock_user_string(arg2); + if (p && n) { + if (num == TARGET_NR_removexattr) { + ret = get_errno(removexattr(p, n)); + } else { + ret = get_errno(lremovexattr(p, n)); + } + } else { + ret = -TARGET_EFAULT; + } + unlock_user(p, arg1, 0); + unlock_user(n, arg2, 0); + } + break; case TARGET_NR_fremovexattr: - ret = -TARGET_EOPNOTSUPP; + { + void *n; + n = lock_user_string(arg2); + if (n) { + ret = get_errno(fremovexattr(arg1, n)); + } else { + ret = -TARGET_EFAULT; + } + unlock_user(n, arg2, 0); + } break; #endif +#endif /* CONFIG_ATTR */ #ifdef TARGET_NR_set_thread_area case TARGET_NR_set_thread_area: #if defined(TARGET_MIPS) @@ -7208,6 +8519,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #elif defined(TARGET_I386) && defined(TARGET_ABI32) ret = do_set_thread_area(cpu_env, arg1); break; +#elif defined(TARGET_M68K) + { + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; + ts->tp_value = arg1; + ret = 0; + break; + } #else goto unimplemented_nowarn; #endif @@ -7216,6 +8534,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_get_thread_area: #if defined(TARGET_I386) && defined(TARGET_ABI32) ret = do_get_thread_area(cpu_env, arg1); + break; +#elif defined(TARGET_M68K) + { + TaskState *ts = ((CPUArchState *)cpu_env)->opaque; + ret = ts->tp_value; + break; + } #else goto unimplemented_nowarn; #endif @@ -7280,10 +8605,23 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_set_robust_list case TARGET_NR_set_robust_list: - goto unimplemented_nowarn; + case TARGET_NR_get_robust_list: + /* The ABI for supporting robust futexes has userspace pass + * the kernel a pointer to a linked list which is updated by + * userspace after the syscall; the list is walked by the kernel + * when the thread exits. Since the linked list in QEMU guest + * memory isn't a valid linked list for the host and we have + * no way to reliably intercept the thread-death event, we can't + * support these. Silently return ENOSYS so that guest userspace + * falls back to a non-robust futex implementation (which should + * be OK except in the corner case of the guest crashing while + * holding a mutex that is shared with another process via + * shared memory). + */ + goto unimplemented_nowarn; #endif -#if defined(TARGET_NR_utimensat) && defined(__NR_utimensat) +#if defined(TARGET_NR_utimensat) case TARGET_NR_utimensat: { struct timespec *tsp, ts[2]; @@ -7307,11 +8645,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif -#if defined(CONFIG_USE_NPTL) case TARGET_NR_futex: ret = do_futex(arg1, arg2, arg3, arg4, arg5, arg6); break; -#endif #if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) case TARGET_NR_inotify_init: ret = get_errno(sys_inotify_init()); @@ -7440,14 +8776,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_vmsplice case TARGET_NR_vmsplice: { - int count = arg3; - struct iovec *vec; - - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) - goto efault; - ret = get_errno(vmsplice(arg1, vec, count, arg4)); - unlock_iovec(vec, arg2, count, 0); + struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); + if (vec != NULL) { + ret = get_errno(vmsplice(arg1, vec, arg3, arg4)); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } } break; #endif @@ -7460,21 +8795,40 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #if defined(TARGET_NR_eventfd2) case TARGET_NR_eventfd2: - ret = get_errno(eventfd(arg1, arg2)); + { + int host_flags = arg2 & (~(TARGET_O_NONBLOCK | TARGET_O_CLOEXEC)); + if (arg2 & TARGET_O_NONBLOCK) { + host_flags |= O_NONBLOCK; + } + if (arg2 & TARGET_O_CLOEXEC) { + host_flags |= O_CLOEXEC; + } + ret = get_errno(eventfd(arg1, host_flags)); break; + } #endif #endif /* CONFIG_EVENTFD */ #if defined(CONFIG_FALLOCATE) && defined(TARGET_NR_fallocate) case TARGET_NR_fallocate: +#if TARGET_ABI_BITS == 32 + ret = get_errno(fallocate(arg1, arg2, target_offset64(arg3, arg4), + target_offset64(arg5, arg6))); +#else ret = get_errno(fallocate(arg1, arg2, arg3, arg4)); +#endif break; #endif #if defined(CONFIG_SYNC_FILE_RANGE) #if defined(TARGET_NR_sync_file_range) case TARGET_NR_sync_file_range: #if TARGET_ABI_BITS == 32 +#if defined(TARGET_MIPS) + ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), + target_offset64(arg5, arg6), arg7)); +#else ret = get_errno(sync_file_range(arg1, target_offset64(arg2, arg3), target_offset64(arg4, arg5), arg6)); +#endif /* !TARGET_MIPS */ #else ret = get_errno(sync_file_range(arg1, arg2, arg3, arg4)); #endif @@ -7492,6 +8846,151 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif #endif +#if defined(CONFIG_EPOLL) +#if defined(TARGET_NR_epoll_create) + case TARGET_NR_epoll_create: + ret = get_errno(epoll_create(arg1)); + break; +#endif +#if defined(TARGET_NR_epoll_create1) && defined(CONFIG_EPOLL_CREATE1) + case TARGET_NR_epoll_create1: + ret = get_errno(epoll_create1(arg1)); + break; +#endif +#if defined(TARGET_NR_epoll_ctl) + case TARGET_NR_epoll_ctl: + { + struct epoll_event ep; + struct epoll_event *epp = 0; + if (arg4) { + struct target_epoll_event *target_ep; + if (!lock_user_struct(VERIFY_READ, target_ep, arg4, 1)) { + goto efault; + } + ep.events = tswap32(target_ep->events); + /* The epoll_data_t union is just opaque data to the kernel, + * so we transfer all 64 bits across and need not worry what + * actual data type it is. + */ + ep.data.u64 = tswap64(target_ep->data.u64); + unlock_user_struct(target_ep, arg4, 0); + epp = &ep; + } + ret = get_errno(epoll_ctl(arg1, arg2, arg3, epp)); + break; + } +#endif + +#if defined(TARGET_NR_epoll_pwait) && defined(CONFIG_EPOLL_PWAIT) +#define IMPLEMENT_EPOLL_PWAIT +#endif +#if defined(TARGET_NR_epoll_wait) || defined(IMPLEMENT_EPOLL_PWAIT) +#if defined(TARGET_NR_epoll_wait) + case TARGET_NR_epoll_wait: +#endif +#if defined(IMPLEMENT_EPOLL_PWAIT) + case TARGET_NR_epoll_pwait: +#endif + { + struct target_epoll_event *target_ep; + struct epoll_event *ep; + int epfd = arg1; + int maxevents = arg3; + int timeout = arg4; + + target_ep = lock_user(VERIFY_WRITE, arg2, + maxevents * sizeof(struct target_epoll_event), 1); + if (!target_ep) { + goto efault; + } + + ep = alloca(maxevents * sizeof(struct epoll_event)); + + switch (num) { +#if defined(IMPLEMENT_EPOLL_PWAIT) + case TARGET_NR_epoll_pwait: + { + target_sigset_t *target_set; + sigset_t _set, *set = &_set; + + if (arg5) { + target_set = lock_user(VERIFY_READ, arg5, + sizeof(target_sigset_t), 1); + if (!target_set) { + unlock_user(target_ep, arg2, 0); + goto efault; + } + target_to_host_sigset(set, target_set); + unlock_user(target_set, arg5, 0); + } else { + set = NULL; + } + + ret = get_errno(epoll_pwait(epfd, ep, maxevents, timeout, set)); + break; + } +#endif +#if defined(TARGET_NR_epoll_wait) + case TARGET_NR_epoll_wait: + ret = get_errno(epoll_wait(epfd, ep, maxevents, timeout)); + break; +#endif + default: + ret = -TARGET_ENOSYS; + } + if (!is_error(ret)) { + int i; + for (i = 0; i < ret; i++) { + target_ep[i].events = tswap32(ep[i].events); + target_ep[i].data.u64 = tswap64(ep[i].data.u64); + } + } + unlock_user(target_ep, arg2, ret * sizeof(struct target_epoll_event)); + break; + } +#endif +#endif +#ifdef TARGET_NR_prlimit64 + case TARGET_NR_prlimit64: + { + /* args: pid, resource number, ptr to new rlimit, ptr to old rlimit */ + struct target_rlimit64 *target_rnew, *target_rold; + struct host_rlimit64 rnew, rold, *rnewp = 0; + if (arg3) { + if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { + goto efault; + } + rnew.rlim_cur = tswap64(target_rnew->rlim_cur); + rnew.rlim_max = tswap64(target_rnew->rlim_max); + unlock_user_struct(target_rnew, arg3, 0); + rnewp = &rnew; + } + + ret = get_errno(sys_prlimit64(arg1, arg2, rnewp, arg4 ? &rold : 0)); + if (!is_error(ret) && arg4) { + if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { + goto efault; + } + target_rold->rlim_cur = tswap64(rold.rlim_cur); + target_rold->rlim_max = tswap64(rold.rlim_max); + unlock_user_struct(target_rold, arg4, 1); + } + break; + } +#endif +#ifdef TARGET_NR_gethostname + case TARGET_NR_gethostname: + { + char *name = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (name) { + ret = get_errno(gethostname(name, arg2)); + unlock_user(name, arg1, arg2); + } else { + ret = -TARGET_EFAULT; + } + break; + } +#endif default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num);