X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=linux-user%2Fsyscall.c;h=932fe4658d469dbd365ca4295d8dc75ecf48abec;hb=a1516e92b6ed887ef27f3a33a27a9acd772a5de4;hp=9ed8daa0f8a1afdef34b418363dc1a4e9b67f869;hpb=6dbad63eef5947c6c8750e44f408138779b6d0bb;p=qemu diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9ed8daa..932fe46 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -20,11 +20,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -37,11 +39,17 @@ #include #include #include +#include +#include //#include +#include #define termios host_termios #define winsize host_winsize #define termio host_termio +#define sgttyb host_sgttyb /* same as target */ +#define tchars host_tchars /* same as target */ +#define ltchars host_ltchars /* same as target */ #include #include @@ -49,35 +57,28 @@ #include #include #include +#include +#include -#include "gemu.h" +#include "qemu.h" //#define DEBUG -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#define PAGE_MASK ~(PAGE_SIZE - 1) -#endif - -struct dirent { - long d_ino; - long d_off; - unsigned short d_reclen; - char d_name[256]; /* We must not include limits.h! */ -}; - -#include "syscall_defs.h" - -#ifdef TARGET_I386 -#include "cpu-i386.h" -#include "syscall-i386.h" -#endif +//#include +#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) +#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) #define __NR_sys_uname __NR_uname #define __NR_sys_getcwd1 __NR_getcwd #define __NR_sys_statfs __NR_statfs #define __NR_sys_fstatfs __NR_fstatfs #define __NR_sys_getdents __NR_getdents +#define __NR_sys_getdents64 __NR_getdents64 +#define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo + +#if defined(__alpha__) || defined (__ia64__) +#define __NR__llseek __NR_lseek +#endif #ifdef __NR_gettid _syscall0(int, gettid) @@ -89,10 +90,25 @@ static int gettid(void) { _syscall1(int,sys_uname,struct new_utsname *,buf) _syscall2(int,sys_getcwd1,char *,buf,size_t,size) _syscall3(int, sys_getdents, uint, fd, struct dirent *, dirp, uint, count); +_syscall3(int, sys_getdents64, uint, fd, struct dirent64 *, dirp, uint, count); _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh); _syscall2(int,sys_statfs,const char *,path,struct kernel_statfs *,buf) _syscall2(int,sys_fstatfs,int,fd,struct kernel_statfs *,buf) +_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo) +#ifdef __NR_exit_group +_syscall1(int,exit_group,int,error_code) +#endif + +extern int personality(int); +extern int flock(int, int); +extern int setfsuid(int); +extern int setfsgid(int); +extern int setresuid(uid_t, uid_t, uid_t); +extern int getresuid(uid_t *, uid_t *, uid_t *); +extern int setresgid(gid_t, gid_t, gid_t); +extern int getresgid(gid_t *, gid_t *, gid_t *); +extern int setgroups(int, gid_t *); static inline long get_errno(long ret) { @@ -127,7 +143,7 @@ static long do_brk(char *new_brk) if (new_brk < target_original_brk) return -ENOMEM; - brk_page = (char *)(((unsigned long)target_brk + PAGE_SIZE - 1) & PAGE_MASK); + brk_page = (char *)HOST_PAGE_ALIGN((unsigned long)target_brk); /* If the new brk is less than this, set it and we're done... */ if (new_brk < brk_page) { @@ -136,11 +152,10 @@ static long do_brk(char *new_brk) } /* We need to allocate more memory after the brk... */ - new_alloc_size = ((new_brk - brk_page + 1)+(PAGE_SIZE-1)) & PAGE_MASK; - mapped_addr = get_errno((long)mmap((caddr_t)brk_page, new_alloc_size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0)); - + new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); + mapped_addr = get_errno(target_mmap((unsigned long)brk_page, new_alloc_size, + PROT_READ|PROT_WRITE, + MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0)); if (is_error(mapped_addr)) { return mapped_addr; } else { @@ -152,7 +167,7 @@ static long do_brk(char *new_brk) static inline fd_set *target_to_host_fds(fd_set *fds, target_long *target_fds, int n) { -#if !defined(BSWP_NEEDED) && !defined(WORD_BIGENDIAN) +#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN) return (fd_set *)target_fds; #else int i, b; @@ -174,7 +189,7 @@ static inline fd_set *target_to_host_fds(fd_set *fds, static inline void host_to_target_fds(target_long *target_fds, fd_set *fds, int n) { -#if !defined(BSWP_NEEDED) && !defined(WORD_BIGENDIAN) +#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN) /* nothing to do */ #else int i, nw, j, k; @@ -195,18 +210,41 @@ static inline void host_to_target_fds(target_long *target_fds, #endif } -/* XXX: incorrect for some archs */ -static void host_to_target_old_sigset(target_ulong *old_sigset, - const sigset_t *sigset) +static inline void host_to_target_rusage(struct target_rusage *target_rusage, + const struct rusage *rusage) { - *old_sigset = tswap32(*(unsigned long *)sigset & 0xffffffff); + 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); } -static void target_to_host_old_sigset(sigset_t *sigset, - const target_ulong *old_sigset) +static inline void target_to_host_timeval(struct timeval *tv, + const struct target_timeval *target_tv) { - sigemptyset(sigset); - *(unsigned long *)sigset = tswapl(*old_sigset); + tv->tv_sec = tswapl(target_tv->tv_sec); + tv->tv_usec = tswapl(target_tv->tv_usec); +} + +static inline void host_to_target_timeval(struct target_timeval *target_tv, + const struct timeval *tv) +{ + target_tv->tv_sec = tswapl(tv->tv_sec); + target_tv->tv_usec = tswapl(tv->tv_usec); } @@ -224,8 +262,7 @@ static long do_select(long n, efds_ptr = target_to_host_fds(&efds, target_efds, n); if (target_tv) { - tv.tv_sec = tswapl(target_tv->tv_sec); - tv.tv_usec = tswapl(target_tv->tv_usec); + target_to_host_timeval(&tv, target_tv); tv_ptr = &tv; } else { tv_ptr = NULL; @@ -237,62 +274,273 @@ static long do_select(long n, host_to_target_fds(target_efds, efds_ptr, n); if (target_tv) { - target_tv->tv_sec = tswapl(tv.tv_sec); - target_tv->tv_usec = tswapl(tv.tv_usec); + host_to_target_timeval(target_tv, &tv); } } return ret; } -static long do_socketcall(int num, long *vptr) +static inline void target_to_host_sockaddr(struct sockaddr *addr, + struct target_sockaddr *target_addr, + socklen_t len) +{ + memcpy(addr, target_addr, len); + addr->sa_family = tswap16(target_addr->sa_family); +} + +static inline void host_to_target_sockaddr(struct target_sockaddr *target_addr, + struct sockaddr *addr, + socklen_t len) +{ + memcpy(target_addr, addr, len); + target_addr->sa_family = tswap16(addr->sa_family); +} + +static inline void target_to_host_cmsg(struct msghdr *msgh, + struct target_msghdr *target_msgh) +{ + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); + struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh); + socklen_t space = 0; + + while (cmsg && target_cmsg) { + void *data = CMSG_DATA(cmsg); + void *target_data = TARGET_CMSG_DATA(target_cmsg); + + int len = tswapl(target_cmsg->cmsg_len) + - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr)); + + space += CMSG_SPACE(len); + if (space > msgh->msg_controllen) { + space -= CMSG_SPACE(len); + gemu_log("Host cmsg overflow"); + break; + } + + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); + cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type); + cmsg->cmsg_len = CMSG_LEN(len); + + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); + memcpy(data, target_data, len); + } else { + int *fd = (int *)data; + int *target_fd = (int *)target_data; + int i, numfds = len / sizeof(int); + + for (i = 0; i < numfds; i++) + fd[i] = tswap32(target_fd[i]); + } + + cmsg = CMSG_NXTHDR(msgh, cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + } + + msgh->msg_controllen = space; +} + +static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, + struct msghdr *msgh) +{ + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); + struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh); + socklen_t space = 0; + + while (cmsg && target_cmsg) { + void *data = CMSG_DATA(cmsg); + void *target_data = TARGET_CMSG_DATA(target_cmsg); + + int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr)); + + space += TARGET_CMSG_SPACE(len); + if (space > tswapl(target_msgh->msg_controllen)) { + space -= TARGET_CMSG_SPACE(len); + gemu_log("Target cmsg overflow"); + break; + } + + 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)); + + if (cmsg->cmsg_level != 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 { + 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]); + } + + cmsg = CMSG_NXTHDR(msgh, cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + } + + msgh->msg_controllen = tswapl(space); +} + +static long do_setsockopt(int sockfd, int level, int optname, + void *optval, socklen_t optlen) +{ + if (level == SOL_TCP) { + /* TCP options all take an 'int' value. */ + int val; + + if (optlen < sizeof(uint32_t)) + return -EINVAL; + + val = tswap32(*(uint32_t *)optval); + return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); + } + + else if (level != SOL_SOCKET) { + gemu_log("Unsupported setsockopt level: %d\n", level); + return -ENOSYS; + } + + switch (optname) { + /* Options with 'int' argument. */ + case SO_DEBUG: + case SO_REUSEADDR: + case SO_TYPE: + case SO_ERROR: + case SO_DONTROUTE: + case SO_BROADCAST: + case SO_SNDBUF: + case SO_RCVBUF: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case SO_NO_CHECK: + case SO_PRIORITY: + case SO_BSDCOMPAT: + case SO_PASSCRED: + case SO_TIMESTAMP: + case SO_RCVLOWAT: + case SO_RCVTIMEO: + case SO_SNDTIMEO: + { + int val; + if (optlen < sizeof(uint32_t)) + return -EINVAL; + val = tswap32(*(uint32_t *)optval); + return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); + } + + default: + gemu_log("Unsupported setsockopt SOL_SOCKET option: %d\n", optname); + return -ENOSYS; + } +} + +static long do_getsockopt(int sockfd, int level, int optname, + void *optval, socklen_t *optlen) +{ + gemu_log("getsockopt not yet supported\n"); + return -ENOSYS; +} + +static long do_socketcall(int num, int32_t *vptr) { long ret; switch(num) { case SOCKOP_socket: - ret = get_errno(socket(vptr[0], vptr[1], vptr[2])); + { + int domain = tswap32(vptr[0]); + int type = tswap32(vptr[1]); + int protocol = tswap32(vptr[2]); + + ret = get_errno(socket(domain, type, protocol)); + } break; case SOCKOP_bind: - ret = get_errno(bind(vptr[0], (struct sockaddr *)vptr[1], vptr[2])); + { + int sockfd = tswap32(vptr[0]); + void *target_addr = (void *)tswap32(vptr[1]); + socklen_t addrlen = tswap32(vptr[2]); + void *addr = alloca(addrlen); + + target_to_host_sockaddr(addr, target_addr, addrlen); + ret = get_errno(bind(sockfd, addr, addrlen)); + } break; case SOCKOP_connect: - ret = get_errno(connect(vptr[0], (struct sockaddr *)vptr[1], vptr[2])); + { + int sockfd = tswap32(vptr[0]); + void *target_addr = (void *)tswap32(vptr[1]); + socklen_t addrlen = tswap32(vptr[2]); + void *addr = alloca(addrlen); + + target_to_host_sockaddr(addr, target_addr, addrlen); + ret = get_errno(connect(sockfd, addr, addrlen)); + } break; case SOCKOP_listen: - ret = get_errno(listen(vptr[0], vptr[1])); + { + int sockfd = tswap32(vptr[0]); + int backlog = tswap32(vptr[1]); + + ret = get_errno(listen(sockfd, backlog)); + } break; case SOCKOP_accept: { - socklen_t size; - size = tswap32(*(int32_t *)vptr[2]); - ret = get_errno(accept(vptr[0], (struct sockaddr *)vptr[1], &size)); - if (!is_error(ret)) - *(int32_t *)vptr[2] = size; + int sockfd = tswap32(vptr[0]); + void *target_addr = (void *)tswap32(vptr[1]); + uint32_t *target_addrlen = (void *)tswap32(vptr[2]); + socklen_t addrlen = tswap32(*target_addrlen); + void *addr = alloca(addrlen); + + ret = get_errno(accept(sockfd, addr, &addrlen)); + if (!is_error(ret)) { + host_to_target_sockaddr(target_addr, addr, addrlen); + *target_addrlen = tswap32(addrlen); + } } break; case SOCKOP_getsockname: { - socklen_t size; - size = tswap32(*(int32_t *)vptr[2]); - ret = get_errno(getsockname(vptr[0], (struct sockaddr *)vptr[1], &size)); - if (!is_error(ret)) - *(int32_t *)vptr[2] = size; + int sockfd = tswap32(vptr[0]); + void *target_addr = (void *)tswap32(vptr[1]); + uint32_t *target_addrlen = (void *)tswap32(vptr[2]); + socklen_t addrlen = tswap32(*target_addrlen); + void *addr = alloca(addrlen); + + ret = get_errno(getsockname(sockfd, addr, &addrlen)); + if (!is_error(ret)) { + host_to_target_sockaddr(target_addr, addr, addrlen); + *target_addrlen = tswap32(addrlen); + } } break; case SOCKOP_getpeername: { - socklen_t size; - size = tswap32(*(int32_t *)vptr[2]); - ret = get_errno(getpeername(vptr[0], (struct sockaddr *)vptr[1], &size)); - if (!is_error(ret)) - *(int32_t *)vptr[2] = size; + int sockfd = tswap32(vptr[0]); + void *target_addr = (void *)tswap32(vptr[1]); + uint32_t *target_addrlen = (void *)tswap32(vptr[2]); + socklen_t addrlen = tswap32(*target_addrlen); + void *addr = alloca(addrlen); + + ret = get_errno(getpeername(sockfd, addr, &addrlen)); + if (!is_error(ret)) { + host_to_target_sockaddr(target_addr, addr, addrlen); + *target_addrlen = tswap32(addrlen); + } } break; case SOCKOP_socketpair: { + int domain = tswap32(vptr[0]); + int type = tswap32(vptr[1]); + int protocol = tswap32(vptr[2]); + int32_t *target_tab = (void *)tswap32(vptr[3]); int tab[2]; - int32_t *target_tab = (int32_t *)vptr[3]; - ret = get_errno(socketpair(vptr[0], vptr[1], vptr[2], tab)); + + ret = get_errno(socketpair(domain, type, protocol, tab)); if (!is_error(ret)) { target_tab[0] = tswap32(tab[0]); target_tab[1] = tswap32(tab[1]); @@ -300,32 +548,129 @@ static long do_socketcall(int num, long *vptr) } break; case SOCKOP_send: - ret = get_errno(send(vptr[0], (void *)vptr[1], vptr[2], vptr[3])); + { + int sockfd = tswap32(vptr[0]); + void *msg = (void *)tswap32(vptr[1]); + size_t len = tswap32(vptr[2]); + int flags = tswap32(vptr[3]); + + ret = get_errno(send(sockfd, msg, len, flags)); + } break; case SOCKOP_recv: - ret = get_errno(recv(vptr[0], (void *)vptr[1], vptr[2], vptr[3])); + { + int sockfd = tswap32(vptr[0]); + void *msg = (void *)tswap32(vptr[1]); + size_t len = tswap32(vptr[2]); + int flags = tswap32(vptr[3]); + + ret = get_errno(recv(sockfd, msg, len, flags)); + } break; case SOCKOP_sendto: - ret = get_errno(sendto(vptr[0], (void *)vptr[1], vptr[2], vptr[3], - (struct sockaddr *)vptr[4], vptr[5])); + { + int sockfd = tswap32(vptr[0]); + void *msg = (void *)tswap32(vptr[1]); + size_t len = tswap32(vptr[2]); + int flags = tswap32(vptr[3]); + void *target_addr = (void *)tswap32(vptr[4]); + socklen_t addrlen = tswap32(vptr[5]); + void *addr = alloca(addrlen); + + target_to_host_sockaddr(addr, target_addr, addrlen); + ret = get_errno(sendto(sockfd, msg, len, flags, addr, addrlen)); + } break; case SOCKOP_recvfrom: { - socklen_t size; - size = tswap32(*(int32_t *)vptr[5]); - ret = get_errno(recvfrom(vptr[0], (void *)vptr[1], vptr[2], - vptr[3], (struct sockaddr *)vptr[4], &size)); - if (!is_error(ret)) - *(int32_t *)vptr[5] = size; + int sockfd = tswap32(vptr[0]); + void *msg = (void *)tswap32(vptr[1]); + size_t len = tswap32(vptr[2]); + int flags = tswap32(vptr[3]); + void *target_addr = (void *)tswap32(vptr[4]); + uint32_t *target_addrlen = (void *)tswap32(vptr[5]); + socklen_t addrlen = tswap32(*target_addrlen); + void *addr = alloca(addrlen); + + ret = get_errno(recvfrom(sockfd, msg, len, flags, addr, &addrlen)); + if (!is_error(ret)) { + host_to_target_sockaddr(target_addr, addr, addrlen); + *target_addrlen = tswap32(addrlen); + } } break; case SOCKOP_shutdown: - ret = get_errno(shutdown(vptr[0], vptr[1])); + { + int sockfd = tswap32(vptr[0]); + int how = tswap32(vptr[1]); + + ret = get_errno(shutdown(sockfd, how)); + } break; case SOCKOP_sendmsg: case SOCKOP_recvmsg: + { + int fd; + struct target_msghdr *msgp; + struct msghdr msg; + int flags, count, i; + struct iovec *vec; + struct target_iovec *target_vec; + + msgp = (void *)tswap32(vptr[1]); + msg.msg_name = (void *)tswapl(msgp->msg_name); + msg.msg_namelen = tswapl(msgp->msg_namelen); + msg.msg_controllen = 2 * tswapl(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 = (void *)tswapl(msgp->msg_iov); + for(i = 0;i < count; i++) { + vec[i].iov_base = (void *)tswapl(target_vec[i].iov_base); + vec[i].iov_len = tswapl(target_vec[i].iov_len); + } + msg.msg_iovlen = count; + msg.msg_iov = vec; + + fd = tswap32(vptr[0]); + flags = tswap32(vptr[2]); + if (num == SOCKOP_sendmsg) { + target_to_host_cmsg(&msg, msgp); + ret = get_errno(sendmsg(fd, &msg, flags)); + } else { + ret = get_errno(recvmsg(fd, &msg, flags)); + if (!is_error(ret)) + host_to_target_cmsg(msgp, &msg); + } + } + break; case SOCKOP_setsockopt: + { + int sockfd = tswap32(vptr[0]); + int level = tswap32(vptr[1]); + int optname = tswap32(vptr[2]); + void *optval = (void *)tswap32(vptr[3]); + socklen_t optlen = tswap32(vptr[4]); + + ret = do_setsockopt(sockfd, level, optname, optval, optlen); + } + break; case SOCKOP_getsockopt: + { + int sockfd = tswap32(vptr[0]); + int level = tswap32(vptr[1]); + int optname = tswap32(vptr[2]); + void *optval = (void *)tswap32(vptr[3]); + uint32_t *target_len = (void *)tswap32(vptr[4]); + socklen_t optlen = tswap32(*target_len); + + ret = do_getsockopt(sockfd, level, optname, optval, &optlen); + if (!is_error(ret)) + *target_len = tswap32(optlen); + } + break; default: gemu_log("Unsupported socketcall: %d\n", num); ret = -ENOSYS; @@ -352,11 +697,11 @@ enum { #undef STRUCT_SPECIAL typedef struct IOCTLEntry { - int target_cmd; - int host_cmd; + unsigned int target_cmd; + unsigned int host_cmd; const char *name; int access; - const argtype arg_type[3]; + const argtype arg_type[5]; } IOCTLEntry; #define IOC_R 0x0001 @@ -365,7 +710,7 @@ typedef struct IOCTLEntry { #define MAX_STRUCT_SIZE 4096 -const IOCTLEntry ioctl_entries[] = { +IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, types...) \ { TARGET_ ## cmd, cmd, #cmd, access, { types } }, #include "ioctls.h" @@ -390,7 +735,7 @@ static long do_ioctl(long fd, long cmd, long arg) ie++; } arg_type = ie->arg_type; -#ifdef DEBUG +#if defined(DEBUG) gemu_log("ioctl: cmd=0x%04lx (%s)\n", cmd, ie->name); #endif switch(arg_type[0]) { @@ -608,7 +953,19 @@ StructEntry struct_termios_def = { .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) }, }; -#ifdef TARGET_I386 +static bitmask_transtbl mmap_flags_tbl[] = { + { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED }, + { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE }, + { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED }, + { TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS, MAP_ANONYMOUS, MAP_ANONYMOUS }, + { TARGET_MAP_GROWSDOWN, TARGET_MAP_GROWSDOWN, MAP_GROWSDOWN, MAP_GROWSDOWN }, + { TARGET_MAP_DENYWRITE, TARGET_MAP_DENYWRITE, MAP_DENYWRITE, MAP_DENYWRITE }, + { TARGET_MAP_EXECUTABLE, TARGET_MAP_EXECUTABLE, MAP_EXECUTABLE, MAP_EXECUTABLE }, + { TARGET_MAP_LOCKED, TARGET_MAP_LOCKED, MAP_LOCKED, MAP_LOCKED }, + { 0, 0, 0, 0 } +}; + +#if defined(TARGET_I386) /* NOTE: there is really one LDT for all the threads */ uint8_t *ldt_table; @@ -697,7 +1054,7 @@ static int write_ldt(CPUX86State *env, 0x7000; if (!oldmode) entry_2 |= (useable << 20); - + /* Install the new entry ... */ install: lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3)); @@ -707,7 +1064,7 @@ install: } /* specific and weird i386 syscalls */ -int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount) +int do_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount) { int ret = -ENOSYS; @@ -724,15 +1081,154 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou } return ret; } + +#endif /* defined(TARGET_I386) */ + +/* 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; +} + +int do_fork(CPUState *env, unsigned int flags, unsigned long newsp) +{ + int ret; + TaskState *ts; + uint8_t *new_stack; + CPUState *new_env; + + if (flags & CLONE_VM) { + ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); + memset(ts, 0, sizeof(TaskState)); + new_stack = ts->stack; + ts->used = 1; + /* add in task state list */ + ts->next = first_task_state; + first_task_state = ts; + /* we create a new CPU instance. */ + new_env = cpu_init(); + memcpy(new_env, env, sizeof(CPUState)); +#if defined(TARGET_I386) + if (!newsp) + newsp = env->regs[R_ESP]; + new_env->regs[R_ESP] = newsp; + new_env->regs[R_EAX] = 0; +#elif defined(TARGET_ARM) + if (!newsp) + newsp = env->regs[13]; + new_env->regs[13] = newsp; + new_env->regs[0] = 0; +#else +#error unsupported target CPU +#endif + new_env->opaque = ts; +#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 + } else { + /* if no CLONE_VM, we consider it is a fork */ + if ((flags & ~CSIGNAL) != 0) + return -EINVAL; + ret = fork(); + } + return ret; +} + +static long do_fcntl(int fd, int cmd, unsigned long arg) +{ + struct flock fl; + struct target_flock *target_fl = (void *)arg; + long ret; + + switch(cmd) { + case TARGET_F_GETLK: + ret = fcntl(fd, cmd, &fl); + if (ret == 0) { + target_fl->l_type = tswap16(fl.l_type); + 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_pid = tswapl(fl.l_pid); + } + break; + + case TARGET_F_SETLK: + case TARGET_F_SETLKW: + fl.l_type = tswap16(target_fl->l_type); + 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_pid = tswapl(target_fl->l_pid); + ret = fcntl(fd, cmd, &fl); + break; + + case TARGET_F_GETLK64: + case TARGET_F_SETLK64: + case TARGET_F_SETLKW64: + ret = -1; + errno = EINVAL; + break; + + default: + ret = fcntl(fd, cmd, arg); + break; + } + return ret; +} + + +#define high2lowuid(x) (x) +#define high2lowgid(x) (x) +#define low2highuid(x) (x) +#define low2highgid(x) (x) void syscall_init(void) { + IOCTLEntry *ie; + const argtype *arg_type; + int size; + #define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); #define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def); #include "syscall_types.h" #undef STRUCT #undef STRUCT_SPECIAL + + /* 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; + while (ie->target_cmd != 0) { + if (((ie->target_cmd >> TARGET_IOC_SIZESHIFT) & TARGET_IOC_SIZEMASK) == + TARGET_IOC_SIZEMASK) { + arg_type = ie->arg_type; + if (arg_type[0] != TYPE_PTR) { + fprintf(stderr, "cannot patch size for ioctl 0x%x\n", + ie->target_cmd); + exit(1); + } + arg_type++; + size = thunk_type_size(arg_type, 0); + ie->target_cmd = (ie->target_cmd & + ~(TARGET_IOC_SIZEMASK << TARGET_IOC_SIZESHIFT)) | + (size << TARGET_IOC_SIZESHIFT); + } + /* automatic consistency check if same arch */ +#if defined(__i386__) && defined(TARGET_I386) + if (ie->target_cmd != ie->host_cmd) { + fprintf(stderr, "ERROR: ioctl: target=0x%x host=0x%x\n", + ie->target_cmd, ie->host_cmd); + } +#endif + ie++; + } } long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, @@ -750,17 +1246,19 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, #ifdef HAVE_GPROF _mcleanup(); #endif + /* XXX: should free thread stack and CPU env */ _exit(arg1); ret = 0; /* avoid warning */ break; case TARGET_NR_read: + page_unprotect_range((void *)arg2, arg3); ret = get_errno(read(arg1, (void *)arg2, arg3)); break; case TARGET_NR_write: ret = get_errno(write(arg1, (void *)arg2, arg3)); break; case TARGET_NR_open: - ret = get_errno(open((const char *)arg1, arg2, arg3)); + ret = get_errno(open(path((const char *)arg1), arg2, arg3)); break; case TARGET_NR_close: ret = get_errno(close(arg1)); @@ -769,7 +1267,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = do_brk((char *)arg1); break; case TARGET_NR_fork: - ret = get_errno(fork()); + ret = get_errno(do_fork(cpu_env, SIGCHLD, 0)); break; case TARGET_NR_waitpid: { @@ -789,7 +1287,32 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(unlink((const char *)arg1)); break; case TARGET_NR_execve: - ret = get_errno(execve((const char *)arg1, (void *)arg2, (void *)arg3)); + { + char **argp, **envp; + int argc, envc; + uint32_t *p; + char **q; + + argc = 0; + for (p = (void *)arg2; *p; p++) + argc++; + envc = 0; + for (p = (void *)arg3; *p; p++) + envc++; + + argp = alloca((argc + 1) * sizeof(void *)); + envp = alloca((envc + 1) * sizeof(void *)); + + for (p = (void *)arg2, q = argp; *p; p++, q++) + *q = (void *)tswap32(*p); + *q = NULL; + + for (p = (void *)arg3, q = envp; *p; p++, q++) + *q = (void *)tswap32(*p); + *q = NULL; + + ret = get_errno(execve((const char *)arg1, argp, envp)); + } break; case TARGET_NR_chdir: ret = get_errno(chdir((const char *)arg1)); @@ -828,7 +1351,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(umount((const char *)arg1)); break; case TARGET_NR_setuid: - ret = get_errno(setuid(arg1)); + ret = get_errno(setuid(low2highuid(arg1))); break; case TARGET_NR_getuid: ret = get_errno(getuid()); @@ -866,7 +1389,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_ftime: goto unimplemented; case TARGET_NR_sync: - ret = get_errno(sync()); + sync(); + ret = 0; break; case TARGET_NR_kill: ret = get_errno(kill(arg1, arg2)); @@ -894,11 +1418,22 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, } break; case TARGET_NR_times: - goto unimplemented; + { + struct target_tms *tmsp = (void *)arg1; + struct tms tms; + ret = get_errno(times(&tms)); + if (tmsp) { + tmsp->tms_utime = tswapl(tms.tms_utime); + tmsp->tms_stime = tswapl(tms.tms_stime); + tmsp->tms_cutime = tswapl(tms.tms_cutime); + tmsp->tms_cstime = tswapl(tms.tms_cstime); + } + } + break; case TARGET_NR_prof: goto unimplemented; case TARGET_NR_setgid: - ret = get_errno(setgid(arg1)); + ret = get_errno(setgid(low2highgid(arg1))); break; case TARGET_NR_getgid: ret = get_errno(getgid()); @@ -922,15 +1457,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = do_ioctl(arg1, arg2, arg3); break; case TARGET_NR_fcntl: - switch(arg2) { - case F_GETLK: - case F_SETLK: - case F_SETLKW: - goto unimplemented; - default: - ret = get_errno(fcntl(arg1, arg2, arg3)); - break; - } + ret = get_errno(do_fcntl(arg1, arg2, arg3)); break; case TARGET_NR_mpx: goto unimplemented; @@ -962,74 +1489,298 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(setsid()); break; case TARGET_NR_sigaction: -#if 0 { - int signum = arg1; - struct target_old_sigaction *tact = arg2, *toldact = arg3; - ret = get_errno(setsid()); - - + struct target_old_sigaction *old_act = (void *)arg2; + struct target_old_sigaction *old_oact = (void *)arg3; + struct target_sigaction act, oact, *pact; + if (old_act) { + act._sa_handler = old_act->_sa_handler; + target_siginitset(&act.sa_mask, old_act->sa_mask); + act.sa_flags = old_act->sa_flags; + act.sa_restorer = old_act->sa_restorer; + pact = &act; + } else { + pact = NULL; + } + ret = get_errno(do_sigaction(arg1, pact, &oact)); + if (!is_error(ret) && old_oact) { + old_oact->_sa_handler = oact._sa_handler; + old_oact->sa_mask = oact.sa_mask.sig[0]; + old_oact->sa_flags = oact.sa_flags; + old_oact->sa_restorer = oact.sa_restorer; + } } break; -#else - goto unimplemented; -#endif - case TARGET_NR_sgetmask: - goto unimplemented; - case TARGET_NR_ssetmask: - goto unimplemented; - case TARGET_NR_setreuid: - ret = get_errno(setreuid(arg1, arg2)); - break; - case TARGET_NR_setregid: - ret = get_errno(setregid(arg1, arg2)); + case TARGET_NR_rt_sigaction: + ret = get_errno(do_sigaction(arg1, (void *)arg2, (void *)arg3)); break; - case TARGET_NR_sigsuspend: - goto unimplemented; - case TARGET_NR_sigpending: - goto unimplemented; - case TARGET_NR_sethostname: - ret = get_errno(sethostname((const char *)arg1, arg2)); + case TARGET_NR_sgetmask: + { + sigset_t cur_set; + target_ulong target_set; + sigprocmask(0, NULL, &cur_set); + host_to_target_old_sigset(&target_set, &cur_set); + ret = target_set; + } break; - case TARGET_NR_setrlimit: - goto unimplemented; - case TARGET_NR_getrlimit: - goto unimplemented; - case TARGET_NR_getrusage: - goto unimplemented; - case TARGET_NR_gettimeofday: + case TARGET_NR_ssetmask: { - struct target_timeval *target_tv = (void *)arg1; - struct timeval tv; - ret = get_errno(gettimeofday(&tv, NULL)); - if (!is_error(ret)) { - target_tv->tv_sec = tswapl(tv.tv_sec); - target_tv->tv_usec = tswapl(tv.tv_usec); - } + sigset_t set, oset, cur_set; + target_ulong target_set = arg1; + sigprocmask(0, NULL, &cur_set); + target_to_host_old_sigset(&set, &target_set); + sigorset(&set, &set, &cur_set); + sigprocmask(SIG_SETMASK, &set, &oset); + host_to_target_old_sigset(&target_set, &oset); + ret = target_set; } break; - case TARGET_NR_settimeofday: + case TARGET_NR_sigprocmask: { - struct target_timeval *target_tv = (void *)arg1; - struct timeval tv; - tv.tv_sec = tswapl(target_tv->tv_sec); - tv.tv_usec = tswapl(target_tv->tv_usec); - ret = get_errno(settimeofday(&tv, NULL)); + int how = arg1; + sigset_t set, oldset, *set_ptr; + target_ulong *pset = (void *)arg2, *poldset = (void *)arg3; + + if (pset) { + switch(how) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -EINVAL; + goto fail; + } + target_to_host_old_sigset(&set, pset); + set_ptr = &set; + } else { + how = 0; + set_ptr = NULL; + } + ret = get_errno(sigprocmask(arg1, set_ptr, &oldset)); + if (!is_error(ret) && poldset) { + host_to_target_old_sigset(poldset, &oldset); + } + } + break; + case TARGET_NR_rt_sigprocmask: + { + int how = arg1; + sigset_t set, oldset, *set_ptr; + target_sigset_t *pset = (void *)arg2; + target_sigset_t *poldset = (void *)arg3; + + if (pset) { + switch(how) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -EINVAL; + goto fail; + } + target_to_host_sigset(&set, pset); + set_ptr = &set; + } else { + how = 0; + set_ptr = NULL; + } + ret = get_errno(sigprocmask(how, set_ptr, &oldset)); + if (!is_error(ret) && poldset) { + host_to_target_sigset(poldset, &oldset); + } + } + break; + case TARGET_NR_sigpending: + { + sigset_t set; + ret = get_errno(sigpending(&set)); + if (!is_error(ret)) { + host_to_target_old_sigset((target_ulong *)arg1, &set); + } + } + break; + case TARGET_NR_rt_sigpending: + { + sigset_t set; + ret = get_errno(sigpending(&set)); + if (!is_error(ret)) { + host_to_target_sigset((target_sigset_t *)arg1, &set); + } + } + break; + case TARGET_NR_sigsuspend: + { + sigset_t set; + target_to_host_old_sigset(&set, (target_ulong *)arg1); + ret = get_errno(sigsuspend(&set)); + } + break; + case TARGET_NR_rt_sigsuspend: + { + sigset_t set; + target_to_host_sigset(&set, (target_sigset_t *)arg1); + ret = get_errno(sigsuspend(&set)); + } + break; + case TARGET_NR_rt_sigtimedwait: + { + target_sigset_t *target_set = (void *)arg1; + target_siginfo_t *target_uinfo = (void *)arg2; + struct target_timespec *target_uts = (void *)arg3; + sigset_t set; + struct timespec uts, *puts; + siginfo_t uinfo; + + target_to_host_sigset(&set, target_set); + if (target_uts) { + puts = &uts; + puts->tv_sec = tswapl(target_uts->tv_sec); + puts->tv_nsec = tswapl(target_uts->tv_nsec); + } else { + puts = NULL; + } + ret = get_errno(sigtimedwait(&set, &uinfo, puts)); + if (!is_error(ret) && target_uinfo) { + host_to_target_siginfo(target_uinfo, &uinfo); + } + } + break; + case TARGET_NR_rt_sigqueueinfo: + { + siginfo_t uinfo; + target_to_host_siginfo(&uinfo, (target_siginfo_t *)arg3); + ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo)); + } + break; + case TARGET_NR_sigreturn: + /* NOTE: ret is eax, so not transcoding must be done */ + ret = do_sigreturn(cpu_env); + break; + case TARGET_NR_rt_sigreturn: + /* NOTE: ret is eax, so not transcoding must be done */ + ret = do_rt_sigreturn(cpu_env); + break; + case TARGET_NR_setreuid: + ret = get_errno(setreuid(arg1, arg2)); + break; + case TARGET_NR_setregid: + ret = get_errno(setregid(arg1, arg2)); + break; + case TARGET_NR_sethostname: + ret = get_errno(sethostname((const char *)arg1, arg2)); + break; + case TARGET_NR_setrlimit: + { + /* XXX: convert resource ? */ + int resource = arg1; + struct target_rlimit *target_rlim = (void *)arg2; + struct rlimit rlim; + rlim.rlim_cur = tswapl(target_rlim->rlim_cur); + rlim.rlim_max = tswapl(target_rlim->rlim_max); + ret = get_errno(setrlimit(resource, &rlim)); + } + break; + case TARGET_NR_getrlimit: + { + /* XXX: convert resource ? */ + int resource = arg1; + struct target_rlimit *target_rlim = (void *)arg2; + struct rlimit rlim; + + ret = get_errno(getrlimit(resource, &rlim)); + if (!is_error(ret)) { + target_rlim->rlim_cur = tswapl(rlim.rlim_cur); + target_rlim->rlim_max = tswapl(rlim.rlim_max); + } + } + break; + case TARGET_NR_getrusage: + { + struct rusage rusage; + struct target_rusage *target_rusage = (void *)arg2; + ret = get_errno(getrusage(arg1, &rusage)); + if (!is_error(ret)) { + host_to_target_rusage(target_rusage, &rusage); + } + } + break; + case TARGET_NR_gettimeofday: + { + struct target_timeval *target_tv = (void *)arg1; + struct timeval tv; + ret = get_errno(gettimeofday(&tv, NULL)); + if (!is_error(ret)) { + host_to_target_timeval(target_tv, &tv); + } + } + break; + case TARGET_NR_settimeofday: + { + struct target_timeval *target_tv = (void *)arg1; + struct timeval tv; + target_to_host_timeval(&tv, target_tv); + ret = get_errno(settimeofday(&tv, NULL)); + } + break; + case TARGET_NR_getgroups: + { + int gidsetsize = arg1; + uint16_t *target_grouplist = (void *)arg2; + gid_t *grouplist; + int i; + + grouplist = alloca(gidsetsize * sizeof(gid_t)); + ret = get_errno(getgroups(gidsetsize, grouplist)); + if (!is_error(ret)) { + for(i = 0;i < gidsetsize; i++) + target_grouplist[i] = tswap16(grouplist[i]); + } } break; - case TARGET_NR_getgroups: - goto unimplemented; case TARGET_NR_setgroups: - goto unimplemented; + { + int gidsetsize = arg1; + uint16_t *target_grouplist = (void *)arg2; + gid_t *grouplist; + int i; + + grouplist = alloca(gidsetsize * sizeof(gid_t)); + for(i = 0;i < gidsetsize; i++) + grouplist[i] = tswap16(target_grouplist[i]); + ret = get_errno(setgroups(gidsetsize, grouplist)); + } + break; case TARGET_NR_select: - goto unimplemented; + { + struct target_sel_arg_struct *sel = (void *)arg1; + sel->n = tswapl(sel->n); + sel->inp = tswapl(sel->inp); + sel->outp = tswapl(sel->outp); + sel->exp = tswapl(sel->exp); + sel->tvp = tswapl(sel->tvp); + ret = do_select(sel->n, (void *)sel->inp, (void *)sel->outp, + (void *)sel->exp, (void *)sel->tvp); + } + break; case TARGET_NR_symlink: ret = get_errno(symlink((const char *)arg1, (const char *)arg2)); break; case TARGET_NR_oldlstat: goto unimplemented; case TARGET_NR_readlink: - ret = get_errno(readlink((const char *)arg1, (char *)arg2, arg3)); + ret = get_errno(readlink(path((const char *)arg1), (char *)arg2, arg3)); break; case TARGET_NR_uselib: goto unimplemented; @@ -1040,8 +1791,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, goto unimplemented; case TARGET_NR_readdir: goto unimplemented; -#ifdef TARGET_I386 case TARGET_NR_mmap: +#if defined(TARGET_I386) || defined(TARGET_ARM) { uint32_t v1, v2, v3, v4, v5, v6, *vptr; vptr = (uint32_t *)arg1; @@ -1051,19 +1802,46 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, v4 = tswap32(vptr[3]); v5 = tswap32(vptr[4]); v6 = tswap32(vptr[5]); - ret = get_errno((long)mmap((void *)v1, v2, v3, v4, v5, v6)); + ret = get_errno(target_mmap(v1, v2, v3, + target_to_host_bitmask(v4, mmap_flags_tbl), + v5, v6)); } - break; -#endif -#ifdef TARGET_I386 - case TARGET_NR_mmap2: #else - case TARGET_NR_mmap: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6)); #endif - ret = get_errno((long)mmap((void *)arg1, arg2, arg3, arg4, arg5, arg6)); + break; + case TARGET_NR_mmap2: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6 << TARGET_PAGE_BITS)); break; case TARGET_NR_munmap: - ret = get_errno(munmap((void *)arg1, arg2)); + ret = get_errno(target_munmap(arg1, arg2)); + break; + case TARGET_NR_mprotect: + ret = get_errno(target_mprotect(arg1, arg2, arg3)); + break; + case TARGET_NR_mremap: + ret = get_errno(target_mremap(arg1, arg2, arg3, arg4, arg5)); + break; + case TARGET_NR_msync: + ret = get_errno(msync((void *)arg1, arg2, arg3)); + break; + case TARGET_NR_mlock: + ret = get_errno(mlock((void *)arg1, arg2)); + break; + case TARGET_NR_munlock: + ret = get_errno(munlock((void *)arg1, arg2)); + break; + case TARGET_NR_mlockall: + ret = get_errno(mlockall(arg1)); + break; + case TARGET_NR_munlockall: + ret = get_errno(munlockall()); break; case TARGET_NR_truncate: ret = get_errno(truncate((const char *)arg1, arg2)); @@ -1087,7 +1865,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, goto unimplemented; case TARGET_NR_statfs: stfs = (void *)arg2; - ret = get_errno(sys_statfs((const char *)arg1, stfs)); + ret = get_errno(sys_statfs(path((const char *)arg1), stfs)); convert_statfs: if (!is_error(ret)) { tswap32s(&stfs->f_type); @@ -1109,19 +1887,53 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_ioperm: goto unimplemented; case TARGET_NR_socketcall: - ret = do_socketcall(arg1, (long *)arg2); + ret = do_socketcall(arg1, (int32_t *)arg2); break; case TARGET_NR_syslog: goto unimplemented; case TARGET_NR_setitimer: - goto unimplemented; + { + struct target_itimerval *target_value = (void *)arg2; + struct target_itimerval *target_ovalue = (void *)arg3; + struct itimerval value, ovalue, *pvalue; + + if (target_value) { + pvalue = &value; + target_to_host_timeval(&pvalue->it_interval, + &target_value->it_interval); + target_to_host_timeval(&pvalue->it_value, + &target_value->it_value); + } else { + pvalue = NULL; + } + ret = get_errno(setitimer(arg1, pvalue, &ovalue)); + if (!is_error(ret) && target_ovalue) { + host_to_target_timeval(&target_ovalue->it_interval, + &ovalue.it_interval); + host_to_target_timeval(&target_ovalue->it_value, + &ovalue.it_value); + } + } + break; case TARGET_NR_getitimer: - goto unimplemented; + { + struct target_itimerval *target_value = (void *)arg2; + struct itimerval value; + + ret = get_errno(getitimer(arg1, &value)); + if (!is_error(ret) && target_value) { + host_to_target_timeval(&target_value->it_interval, + &value.it_interval); + host_to_target_timeval(&target_value->it_value, + &value.it_value); + } + } + break; case TARGET_NR_stat: - ret = get_errno(stat((const char *)arg1, &st)); + ret = get_errno(stat(path((const char *)arg1), &st)); goto do_stat; case TARGET_NR_lstat: - ret = get_errno(lstat((const char *)arg1, &st)); + ret = get_errno(lstat(path((const char *)arg1), &st)); goto do_stat; case TARGET_NR_fstat: { @@ -1139,9 +1951,9 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, target_st->st_size = tswapl(st.st_size); target_st->st_blksize = tswapl(st.st_blksize); target_st->st_blocks = tswapl(st.st_blocks); - target_st->st_atime = tswapl(st.st_atime); - target_st->st_mtime = tswapl(st.st_mtime); - target_st->st_ctime = tswapl(st.st_ctime); + target_st->target_st_atime = tswapl(st.st_atime); + target_st->target_st_mtime = tswapl(st.st_mtime); + target_st->target_st_ctime = tswapl(st.st_ctime); } } break; @@ -1154,8 +1966,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, break; case TARGET_NR_idle: goto unimplemented; - case TARGET_NR_vm86old: - goto unimplemented; case TARGET_NR_wait4: { int status; @@ -1171,24 +1981,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, if (status_ptr) *status_ptr = tswap32(status); if (target_rusage) { - 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); + host_to_target_rusage(target_rusage, &rusage); } } } @@ -1203,10 +1996,15 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_fsync: ret = get_errno(fsync(arg1)); break; - case TARGET_NR_sigreturn: - goto unimplemented; case TARGET_NR_clone: - goto unimplemented; + ret = get_errno(do_fork(cpu_env, arg1, arg2)); + break; +#ifdef __NR_exit_group + /* new thread calls */ + case TARGET_NR_exit_group: + ret = get_errno(exit_group(arg1)); + break; +#endif case TARGET_NR_setdomainname: ret = get_errno(setdomainname((const char *)arg1, arg2)); break; @@ -1216,47 +2014,16 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, break; #ifdef TARGET_I386 case TARGET_NR_modify_ldt: - ret = get_errno(gemu_modify_ldt(cpu_env, arg1, (void *)arg2, arg3)); + ret = get_errno(do_modify_ldt(cpu_env, arg1, (void *)arg2, arg3)); + break; + case TARGET_NR_vm86old: + goto unimplemented; + case TARGET_NR_vm86: + ret = do_vm86(cpu_env, arg1, (void *)arg2); break; #endif case TARGET_NR_adjtimex: goto unimplemented; - case TARGET_NR_mprotect: - ret = get_errno(mprotect((void *)arg1, arg2, arg3)); - break; - case TARGET_NR_sigprocmask: - { - int how = arg1; - sigset_t set, oldset, *set_ptr; - target_ulong *pset = (void *)arg2, *poldset = (void *)arg3; - - switch(how) { - case TARGET_SIG_BLOCK: - how = SIG_BLOCK; - break; - case TARGET_SIG_UNBLOCK: - how = SIG_UNBLOCK; - break; - case TARGET_SIG_SETMASK: - how = SIG_SETMASK; - break; - default: - ret = -EINVAL; - goto fail; - } - - if (pset) { - target_to_host_old_sigset(&set, pset); - set_ptr = &set; - } else { - set_ptr = NULL; - } - ret = get_errno(sigprocmask(arg1, set_ptr, &oldset)); - if (!is_error(ret) && poldset) { - host_to_target_old_sigset(poldset, &oldset); - } - } - break; case TARGET_NR_create_module: case TARGET_NR_init_module: case TARGET_NR_delete_module: @@ -1275,14 +2042,16 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_sysfs: goto unimplemented; case TARGET_NR_personality: - ret = get_errno(mprotect((void *)arg1, arg2, arg3)); + ret = get_errno(personality(arg1)); break; case TARGET_NR_afs_syscall: goto unimplemented; case TARGET_NR_setfsuid: - goto unimplemented; + ret = get_errno(setfsuid(arg1)); + break; case TARGET_NR_setfsgid: - goto unimplemented; + ret = get_errno(setfsgid(arg1)); + break; case TARGET_NR__llseek: { int64_t res; @@ -1293,10 +2062,51 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_getdents: #if TARGET_LONG_SIZE != 4 #error not supported -#endif +#elif TARGET_LONG_SIZE == 4 && HOST_LONG_SIZE == 8 + { + struct target_dirent *target_dirp = (void *)arg2; + struct dirent *dirp; + long count = arg3; + + dirp = malloc(count); + if (!dirp) + return -ENOMEM; + + ret = get_errno(sys_getdents(arg1, dirp, count)); + if (!is_error(ret)) { + struct dirent *de; + struct target_dirent *tde; + int len = ret; + int reclen, treclen; + int count1, tnamelen; + + count1 = 0; + de = dirp; + tde = target_dirp; + while (len > 0) { + reclen = de->d_reclen; + treclen = reclen - (2 * (sizeof(long) - sizeof(target_long))); + tde->d_reclen = tswap16(treclen); + tde->d_ino = tswapl(de->d_ino); + tde->d_off = tswapl(de->d_off); + tnamelen = treclen - (2 * sizeof(target_long) + 2); + if (tnamelen > 256) + tnamelen = 256; + strncpy(tde->d_name, de->d_name, tnamelen); + de = (struct dirent *)((char *)de + reclen); + len -= reclen; + tde = (struct dirent *)((char *)tde + treclen); + count1 += treclen; + } + ret = count1; + } + free(dirp); + } +#else { struct dirent *dirp = (void *)arg2; long count = arg3; + ret = get_errno(sys_getdents(arg1, dirp, count)); if (!is_error(ret)) { struct dirent *de; @@ -1304,10 +2114,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, int reclen; de = dirp; while (len > 0) { - reclen = tswap16(de->d_reclen); + reclen = de->d_reclen; if (reclen > len) break; - de->d_reclen = reclen; + de->d_reclen = tswap16(reclen); tswapls(&de->d_ino); tswapls(&de->d_off); de = (struct dirent *)((char *)de + reclen); @@ -1315,15 +2125,60 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, } } } +#endif + break; + case TARGET_NR_getdents64: + { + struct dirent64 *dirp = (void *)arg2; + long count = arg3; + ret = get_errno(sys_getdents64(arg1, dirp, count)); + if (!is_error(ret)) { + struct dirent64 *de; + int len = ret; + int reclen; + de = dirp; + while (len > 0) { + reclen = de->d_reclen; + if (reclen > len) + break; + de->d_reclen = tswap16(reclen); + tswap64s(&de->d_ino); + tswap64s(&de->d_off); + de = (struct dirent64 *)((char *)de + reclen); + len -= reclen; + } + } + } break; case TARGET_NR__newselect: ret = do_select(arg1, (void *)arg2, (void *)arg3, (void *)arg4, (void *)arg5); break; + case TARGET_NR_poll: + { + struct target_pollfd *target_pfd = (void *)arg1; + unsigned int nfds = arg2; + int timeout = arg3; + struct pollfd *pfd; + unsigned int i; + + 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)); + if (!is_error(ret)) { + for(i = 0; i < nfds; i++) { + target_pfd[i].revents = tswap16(pfd[i].revents); + } + } + } + break; case TARGET_NR_flock: - goto unimplemented; - case TARGET_NR_msync: - ret = get_errno(msync((void *)arg1, arg2, arg3)); + /* NOTE: the flock constant seems to be the same for every + Linux platform */ + ret = get_errno(flock(arg1, arg2)); break; case TARGET_NR_readv: { @@ -1359,56 +2214,118 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(getsid(arg1)); break; case TARGET_NR_fdatasync: - goto unimplemented; + ret = get_errno(fdatasync(arg1)); + break; case TARGET_NR__sysctl: goto unimplemented; - case TARGET_NR_mlock: - ret = get_errno(mlock((void *)arg1, arg2)); - break; - case TARGET_NR_munlock: - ret = get_errno(munlock((void *)arg1, arg2)); - break; - case TARGET_NR_mlockall: - ret = get_errno(mlockall(arg1)); - break; - case TARGET_NR_munlockall: - ret = get_errno(munlockall()); - break; case TARGET_NR_sched_setparam: - goto unimplemented; + { + struct sched_param *target_schp = (void *)arg2; + struct sched_param schp; + schp.sched_priority = tswap32(target_schp->sched_priority); + ret = get_errno(sched_setparam(arg1, &schp)); + } + break; case TARGET_NR_sched_getparam: - goto unimplemented; + { + struct sched_param *target_schp = (void *)arg2; + struct sched_param schp; + ret = get_errno(sched_getparam(arg1, &schp)); + if (!is_error(ret)) { + target_schp->sched_priority = tswap32(schp.sched_priority); + } + } + break; case TARGET_NR_sched_setscheduler: - goto unimplemented; + { + struct sched_param *target_schp = (void *)arg3; + struct sched_param schp; + schp.sched_priority = tswap32(target_schp->sched_priority); + ret = get_errno(sched_setscheduler(arg1, arg2, &schp)); + } + break; case TARGET_NR_sched_getscheduler: - goto unimplemented; + ret = get_errno(sched_getscheduler(arg1)); + break; case TARGET_NR_sched_yield: ret = get_errno(sched_yield()); break; case TARGET_NR_sched_get_priority_max: + ret = get_errno(sched_get_priority_max(arg1)); + break; case TARGET_NR_sched_get_priority_min: + ret = get_errno(sched_get_priority_min(arg1)); + break; case TARGET_NR_sched_rr_get_interval: + { + struct target_timespec *target_ts = (void *)arg2; + struct timespec ts; + ret = get_errno(sched_rr_get_interval(arg1, &ts)); + if (!is_error(ret)) { + target_ts->tv_sec = tswapl(ts.tv_sec); + target_ts->tv_nsec = tswapl(ts.tv_nsec); + } + } + break; case TARGET_NR_nanosleep: - case TARGET_NR_mremap: + { + struct target_timespec *target_req = (void *)arg1; + struct target_timespec *target_rem = (void *)arg2; + struct timespec req, rem; + req.tv_sec = tswapl(target_req->tv_sec); + req.tv_nsec = tswapl(target_req->tv_nsec); + ret = get_errno(nanosleep(&req, &rem)); + if (target_rem) { + target_rem->tv_sec = tswapl(rem.tv_sec); + target_rem->tv_nsec = tswapl(rem.tv_nsec); + } + } + break; case TARGET_NR_setresuid: + ret = get_errno(setresuid(low2highuid(arg1), + low2highuid(arg2), + low2highuid(arg3))); + break; case TARGET_NR_getresuid: - case TARGET_NR_vm86: - case TARGET_NR_query_module: - case TARGET_NR_poll: - case TARGET_NR_nfsservctl: + { + int ruid, euid, suid; + ret = get_errno(getresuid(&ruid, &euid, &suid)); + if (!is_error(ret)) { + *(uint16_t *)arg1 = tswap16(high2lowuid(ruid)); + *(uint16_t *)arg2 = tswap16(high2lowuid(euid)); + *(uint16_t *)arg3 = tswap16(high2lowuid(suid)); + } + } + break; case TARGET_NR_setresgid: + ret = get_errno(setresgid(low2highgid(arg1), + low2highgid(arg2), + low2highgid(arg3))); + break; case TARGET_NR_getresgid: + { + int rgid, egid, sgid; + ret = get_errno(getresgid(&rgid, &egid, &sgid)); + if (!is_error(ret)) { + *(uint16_t *)arg1 = high2lowgid(tswap16(rgid)); + *(uint16_t *)arg2 = high2lowgid(tswap16(egid)); + *(uint16_t *)arg3 = high2lowgid(tswap16(sgid)); + } + } + break; + case TARGET_NR_query_module: + goto unimplemented; + case TARGET_NR_nfsservctl: + goto unimplemented; case TARGET_NR_prctl: - case TARGET_NR_rt_sigreturn: - case TARGET_NR_rt_sigaction: - case TARGET_NR_rt_sigprocmask: - case TARGET_NR_rt_sigpending: - case TARGET_NR_rt_sigtimedwait: - case TARGET_NR_rt_sigqueueinfo: - case TARGET_NR_rt_sigsuspend: + goto unimplemented; case TARGET_NR_pread: + page_unprotect_range((void *)arg2, arg3); + ret = get_errno(pread(arg1, (void *)arg2, arg3, arg4)); + break; case TARGET_NR_pwrite: - goto unimplemented; + ret = get_errno(pwrite(arg1, (void *)arg2, arg3, arg4)); + break; case TARGET_NR_chown: ret = get_errno(chown((const char *)arg1, arg2, arg3)); break; @@ -1416,50 +2333,187 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(sys_getcwd1((char *)arg1, arg2)); break; case TARGET_NR_capget: + goto unimplemented; case TARGET_NR_capset: + goto unimplemented; case TARGET_NR_sigaltstack: + goto unimplemented; case TARGET_NR_sendfile: + goto unimplemented; case TARGET_NR_getpmsg: + goto unimplemented; case TARGET_NR_putpmsg: + goto unimplemented; case TARGET_NR_vfork: - ret = get_errno(vfork()); + ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0)); break; case TARGET_NR_ugetrlimit: + { + struct rlimit rlim; + ret = get_errno(getrlimit(arg1, &rlim)); + if (!is_error(ret)) { + struct target_rlimit *target_rlim = (void *)arg2; + target_rlim->rlim_cur = tswapl(rlim.rlim_cur); + target_rlim->rlim_max = tswapl(rlim.rlim_max); + } + break; + } case TARGET_NR_truncate64: + goto unimplemented; case TARGET_NR_ftruncate64: + goto unimplemented; case TARGET_NR_stat64: + ret = get_errno(stat(path((const char *)arg1), &st)); + goto do_stat64; case TARGET_NR_lstat64: + ret = get_errno(lstat(path((const char *)arg1), &st)); + goto do_stat64; case TARGET_NR_fstat64: + { + ret = get_errno(fstat(arg1, &st)); + do_stat64: + if (!is_error(ret)) { + struct target_stat64 *target_st = (void *)arg2; + memset(target_st, 0, sizeof(struct target_stat64)); + target_st->st_dev = tswap16(st.st_dev); + target_st->st_ino = tswap64(st.st_ino); +#ifdef TARGET_STAT64_HAS_BROKEN_ST_INO + target_st->__st_ino = tswapl(st.st_ino); +#endif + target_st->st_mode = tswap32(st.st_mode); + target_st->st_nlink = tswap32(st.st_nlink); + target_st->st_uid = tswapl(st.st_uid); + target_st->st_gid = tswapl(st.st_gid); + target_st->st_rdev = tswap16(st.st_rdev); + /* XXX: better use of kernel struct */ + target_st->st_size = tswap64(st.st_size); + target_st->st_blksize = tswapl(st.st_blksize); + target_st->st_blocks = tswapl(st.st_blocks); + target_st->target_st_atime = tswapl(st.st_atime); + target_st->target_st_mtime = tswapl(st.st_mtime); + target_st->target_st_ctime = tswapl(st.st_ctime); + } + } + break; + case TARGET_NR_lchown32: + ret = get_errno(lchown((const char *)arg1, arg2, arg3)); + break; case TARGET_NR_getuid32: + ret = get_errno(getuid()); + break; case TARGET_NR_getgid32: + ret = get_errno(getgid()); + break; case TARGET_NR_geteuid32: + ret = get_errno(geteuid()); + break; case TARGET_NR_getegid32: + ret = get_errno(getegid()); + break; case TARGET_NR_setreuid32: + ret = get_errno(setreuid(arg1, arg2)); + break; case TARGET_NR_setregid32: + ret = get_errno(setregid(arg1, arg2)); + break; case TARGET_NR_getgroups32: + goto unimplemented; case TARGET_NR_setgroups32: + goto unimplemented; case TARGET_NR_fchown32: + ret = get_errno(fchown(arg1, arg2, arg3)); + break; case TARGET_NR_setresuid32: + ret = get_errno(setresuid(arg1, arg2, arg3)); + break; case TARGET_NR_getresuid32: + { + int ruid, euid, suid; + ret = get_errno(getresuid(&ruid, &euid, &suid)); + if (!is_error(ret)) { + *(uint32_t *)arg1 = tswap32(ruid); + *(uint32_t *)arg2 = tswap32(euid); + *(uint32_t *)arg3 = tswap32(suid); + } + } + break; case TARGET_NR_setresgid32: + ret = get_errno(setresgid(arg1, arg2, arg3)); + break; case TARGET_NR_getresgid32: + { + int rgid, egid, sgid; + ret = get_errno(getresgid(&rgid, &egid, &sgid)); + if (!is_error(ret)) { + *(uint32_t *)arg1 = tswap32(rgid); + *(uint32_t *)arg2 = tswap32(egid); + *(uint32_t *)arg3 = tswap32(sgid); + } + } + break; case TARGET_NR_chown32: + ret = get_errno(chown((const char *)arg1, arg2, arg3)); + break; case TARGET_NR_setuid32: + ret = get_errno(setuid(arg1)); + break; case TARGET_NR_setgid32: + ret = get_errno(setgid(arg1)); + break; case TARGET_NR_setfsuid32: + ret = get_errno(setfsuid(arg1)); + break; case TARGET_NR_setfsgid32: + ret = get_errno(setfsgid(arg1)); + break; case TARGET_NR_pivot_root: + goto unimplemented; case TARGET_NR_mincore: + goto unimplemented; case TARGET_NR_madvise: - case TARGET_NR_getdents64: + goto unimplemented; +#if TARGET_LONG_BITS == 32 case TARGET_NR_fcntl64: + { + struct flock64 fl; + struct target_flock64 *target_fl = (void *)arg3; + + switch(arg2) { + case F_GETLK64: + ret = get_errno(fcntl(arg1, arg2, &fl)); + if (ret == 0) { + target_fl->l_type = tswap16(fl.l_type); + target_fl->l_whence = tswap16(fl.l_whence); + target_fl->l_start = tswap64(fl.l_start); + target_fl->l_len = tswap64(fl.l_len); + target_fl->l_pid = tswapl(fl.l_pid); + } + break; + + case F_SETLK64: + case F_SETLKW64: + fl.l_type = tswap16(target_fl->l_type); + fl.l_whence = tswap16(target_fl->l_whence); + fl.l_start = tswap64(target_fl->l_start); + fl.l_len = tswap64(target_fl->l_len); + fl.l_pid = tswapl(target_fl->l_pid); + ret = get_errno(fcntl(arg1, arg2, &fl)); + break; + default: + ret = get_errno(do_fcntl(arg1, arg2, arg3)); + break; + } + break; + } +#endif case TARGET_NR_security: goto unimplemented; case TARGET_NR_gettid: ret = get_errno(gettid()); break; case TARGET_NR_readahead: + goto unimplemented; case TARGET_NR_setxattr: case TARGET_NR_lsetxattr: case TARGET_NR_fsetxattr: @@ -1472,10 +2526,14 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_removexattr: case TARGET_NR_lremovexattr: case TARGET_NR_fremovexattr: - goto unimplemented; + goto unimplemented_nowarn; + case TARGET_NR_set_thread_area: + case TARGET_NR_get_thread_area: + goto unimplemented_nowarn; default: unimplemented: - gemu_log("Unsupported syscall: %d\n", num); + gemu_log("qemu: Unsupported syscall: %d\n", num); + unimplemented_nowarn: ret = -ENOSYS; break; }