From e36800c91a74b656b4b4c74483863950cf9ec202 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 2 Oct 2015 14:48:09 +0200 Subject: linux-user: add signalfd/signalfd4 syscalls This patch introduces a system very similar to the one used in the kernel to attach specific functions to a given file descriptor. In this case, we attach a specific "host_to_target()" translator to the fd returned by signalfd() to be able to byte-swap the signalfd_siginfo structure provided by read(). This patch allows to execute the example program given by man signalfd(2): #include #include #include #include #include #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { sigset_t mask; int sfd; struct signalfd_siginfo fdsi; ssize_t s; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); /* Block signals so that they aren't handled according to their default dispositions */ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) handle_error("sigprocmask"); sfd = signalfd(-1, &mask, 0); if (sfd == -1) handle_error("signalfd"); for (;;) { s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); if (s != sizeof(struct signalfd_siginfo)) handle_error("read"); if (fdsi.ssi_signo == SIGINT) { printf("Got SIGINT\n"); } else if (fdsi.ssi_signo == SIGQUIT) { printf("Got SIGQUIT\n"); exit(EXIT_SUCCESS); } else { printf("Read unexpected signal\n"); } } } $ ./signalfd_demo ^CGot SIGINT ^CGot SIGINT ^\Got SIGQUIT Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6c64ba63db..8fa8e0ce14 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -60,6 +60,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include #include #include +#include //#include #include #include @@ -294,6 +295,54 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { 0, 0, 0, 0 } }; +typedef abi_long (*TargetFdFunc)(void *, size_t); +typedef struct TargetFdTrans { + TargetFdFunc host_to_target; + TargetFdFunc target_to_host; +} TargetFdTrans; + +static TargetFdTrans **target_fd_trans; + +static unsigned int target_fd_max; + +static TargetFdFunc fd_trans_host_to_target(int fd) +{ + if (fd < target_fd_max && target_fd_trans[fd]) { + return target_fd_trans[fd]->host_to_target; + } + return NULL; +} + +static void fd_trans_register(int fd, TargetFdTrans *trans) +{ + unsigned int oldmax; + + if (fd >= target_fd_max) { + oldmax = target_fd_max; + target_fd_max = ((fd >> 6) + 1) << 6; /* by slice of 64 entries */ + target_fd_trans = g_realloc(target_fd_trans, + target_fd_max * sizeof(TargetFdTrans)); + memset((void *)(target_fd_trans + oldmax), 0, + (target_fd_max - oldmax) * sizeof(TargetFdTrans *)); + } + target_fd_trans[fd] = trans; +} + +static void fd_trans_unregister(int fd) +{ + if (fd >= 0 && fd < target_fd_max) { + target_fd_trans[fd] = NULL; + } +} + +static void fd_trans_dup(int oldfd, int newfd) +{ + fd_trans_unregister(newfd); + if (oldfd < target_fd_max && target_fd_trans[oldfd]) { + fd_trans_register(newfd, target_fd_trans[oldfd]); + } +} + static int sys_getcwd1(char *buf, size_t size) { if (getcwd(buf, size) == NULL) { @@ -5340,6 +5389,92 @@ static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle, } #endif +#if defined(TARGET_NR_signalfd) || defined(TARGET_NR_signalfd4) + +/* signalfd siginfo conversion */ + +static void +host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo, + const struct signalfd_siginfo *info) +{ + int sig = host_to_target_signal(info->ssi_signo); + + /* linux/signalfd.h defines a ssi_addr_lsb + * not defined in sys/signalfd.h but used by some kernels + */ + +#ifdef BUS_MCEERR_AO + if (tinfo->ssi_signo == SIGBUS && + (tinfo->ssi_code == BUS_MCEERR_AR || + tinfo->ssi_code == BUS_MCEERR_AO)) { + uint16_t *ssi_addr_lsb = (uint16_t *)(&info->ssi_addr + 1); + uint16_t *tssi_addr_lsb = (uint16_t *)(&tinfo->ssi_addr + 1); + *tssi_addr_lsb = tswap16(*ssi_addr_lsb); + } +#endif + + tinfo->ssi_signo = tswap32(sig); + tinfo->ssi_errno = tswap32(tinfo->ssi_errno); + tinfo->ssi_code = tswap32(info->ssi_code); + tinfo->ssi_pid = tswap32(info->ssi_pid); + tinfo->ssi_uid = tswap32(info->ssi_uid); + tinfo->ssi_fd = tswap32(info->ssi_fd); + tinfo->ssi_tid = tswap32(info->ssi_tid); + tinfo->ssi_band = tswap32(info->ssi_band); + tinfo->ssi_overrun = tswap32(info->ssi_overrun); + tinfo->ssi_trapno = tswap32(info->ssi_trapno); + tinfo->ssi_status = tswap32(info->ssi_status); + tinfo->ssi_int = tswap32(info->ssi_int); + tinfo->ssi_ptr = tswap64(info->ssi_ptr); + tinfo->ssi_utime = tswap64(info->ssi_utime); + tinfo->ssi_stime = tswap64(info->ssi_stime); + tinfo->ssi_addr = tswap64(info->ssi_addr); +} + +static abi_long host_to_target_signalfd(void *buf, size_t len) +{ + int i; + + for (i = 0; i < len; i += sizeof(struct signalfd_siginfo)) { + host_to_target_signalfd_siginfo(buf + i, buf + i); + } + + return len; +} + +static TargetFdTrans target_signalfd_trans = { + .host_to_target = host_to_target_signalfd, +}; + +static abi_long do_signalfd4(int fd, abi_long mask, int flags) +{ + int host_flags; + target_sigset_t *target_mask; + sigset_t host_mask; + abi_long ret; + + if (flags & ~(TARGET_O_NONBLOCK | TARGET_O_CLOEXEC)) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_READ, target_mask, mask, 1)) { + return -TARGET_EFAULT; + } + + target_to_host_sigset(&host_mask, target_mask); + + host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl); + + ret = get_errno(signalfd(fd, &host_mask, host_flags)); + if (ret >= 0) { + fd_trans_register(ret, &target_signalfd_trans); + } + + unlock_user_struct(target_mask, mask, 0); + + return ret; +} +#endif + /* Map host to target signal numbers for the wait family of syscalls. Assume all other status bits are the same. */ int host_to_target_waitstatus(int status) @@ -5724,6 +5859,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; ret = get_errno(read(arg1, p, arg3)); + if (ret >= 0 && + fd_trans_host_to_target(arg1)) { + ret = fd_trans_host_to_target(arg1)(p, ret); + } unlock_user(p, arg2, ret); } break; @@ -5740,6 +5879,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_openat(cpu_env, AT_FDCWD, p, target_to_host_bitmask(arg2, fcntl_flags_tbl), arg3)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; #endif @@ -5749,6 +5889,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_openat(cpu_env, arg1, p, target_to_host_bitmask(arg3, fcntl_flags_tbl), arg4)); + fd_trans_unregister(ret); unlock_user(p, arg2, 0); break; #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) @@ -5759,9 +5900,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) case TARGET_NR_open_by_handle_at: ret = do_open_by_handle_at(arg1, arg2, arg3); + fd_trans_unregister(ret); break; #endif case TARGET_NR_close: + fd_trans_unregister(arg1); ret = get_errno(close(arg1)); break; case TARGET_NR_brk: @@ -5803,6 +5946,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(creat(p, arg2)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; #endif @@ -6250,6 +6394,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif case TARGET_NR_dup: ret = get_errno(dup(arg1)); + if (ret >= 0) { + fd_trans_dup(arg1, ret); + } break; #ifdef TARGET_NR_pipe case TARGET_NR_pipe: @@ -6347,11 +6494,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_dup2 case TARGET_NR_dup2: ret = get_errno(dup2(arg1, arg2)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; #endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: ret = get_errno(dup3(arg1, arg2, arg3)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; #endif #ifdef TARGET_NR_getppid /* not on alpha */ @@ -7347,6 +7500,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_socket case TARGET_NR_socket: ret = do_socket(arg1, arg2, arg3); + fd_trans_unregister(ret); break; #endif #ifdef TARGET_NR_socketpair @@ -9600,6 +9754,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_eventfd) case TARGET_NR_eventfd: ret = get_errno(eventfd(arg1, 0)); + fd_trans_unregister(ret); break; #endif #if defined(TARGET_NR_eventfd2) @@ -9613,6 +9768,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_flags |= O_CLOEXEC; } ret = get_errno(eventfd(arg1, host_flags)); + fd_trans_unregister(ret); break; } #endif @@ -9655,6 +9811,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif #endif +#if defined(TARGET_NR_signalfd4) + case TARGET_NR_signalfd4: + ret = do_signalfd4(arg1, arg2, arg4); + break; +#endif +#if defined(TARGET_NR_signalfd) + case TARGET_NR_signalfd: + ret = do_signalfd4(arg1, arg2, 0); + break; +#endif #if defined(CONFIG_EPOLL) #if defined(TARGET_NR_epoll_create) case TARGET_NR_epoll_create: @@ -9926,6 +10092,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_getoverrun(htimer)); } + fd_trans_unregister(ret); break; } #endif -- cgit v1.2.1 From 928bed6a057cedd6110e634865e021a24029785a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 3 Oct 2015 17:14:06 +0200 Subject: linux-user: correctly align target_epoll_event According to comments in /usr/include/linux/eventpoll.h, poll_event is packed only on x86_64. And to be sure fields are correctly aligned in epoll_data, use abi_XXX types for all of them. Moreover, fd type is wrong: fd is int, not ulong. This has been tested with a ppc guest on an x86_64 host: without this patch, systemd crashes (core). CC: Alexander Graf CC: Peter Maydell Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall_defs.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index f996acf945..2fd4afffb8 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2514,20 +2514,23 @@ struct target_mq_attr { #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) #ifdef CONFIG_EPOLL +#if defined(TARGET_X86_64) +#define TARGET_EPOLL_PACKED QEMU_PACKED +#else +#define TARGET_EPOLL_PACKED +#endif + typedef union target_epoll_data { abi_ulong ptr; - abi_ulong fd; - uint32_t u32; - uint64_t u64; + abi_int fd; + abi_uint u32; + abi_ullong u64; } target_epoll_data_t; struct target_epoll_event { - uint32_t events; -#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_MIPS64) - uint32_t __pad; -#endif + abi_uint events; target_epoll_data_t data; -} QEMU_PACKED; +} TARGET_EPOLL_PACKED; #endif struct target_rlimit64 { uint64_t rlim_cur; -- cgit v1.2.1 From 3e24bb3f1277ee25743f0a3274306080df80da2c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 6 Oct 2015 01:20:48 +0200 Subject: linux-user: in poll(), if nfds is 0, pfd can be NULL This problem appears with yum in Fedora 20 / PPC64 container. test case: #include #include int main(void) { int ret; ret = poll(NULL, 0, 1000); printf("%d\n", ret); } target test environment: Fedora 20 / PPC64 host test environment: Ubuntu 14.0.2 / x86_64 original test result: -1 13451 poll(0,0,1000,274886297496,268566664,268566648) = -1 errno=14 (Bad address) patched test result: 0 13536 poll(0,0,1000,274886297496,268566664,268566648) = 0 Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 8fa8e0ce14..c2169664d5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8046,14 +8046,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct pollfd *pfd; unsigned int i; - target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); - if (!target_pfd) - goto efault; + pfd = NULL; + target_pfd = NULL; + if (nfds) { + 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); + 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); + } } # ifdef TARGET_NR_ppoll -- cgit v1.2.1 From 0e173b24b523b432854689717e09de5c95c158f8 Mon Sep 17 00:00:00 2001 From: Harmandeep Kaur Date: Tue, 6 Oct 2015 21:47:12 +0530 Subject: linux-user/syscall.c: malloc()/calloc() to g_malloc()/g_try_malloc()/g_new0() Convert malloc()/ calloc() calls to g_malloc()/ g_try_malloc()/ g_new0() All heap memory allocation should go through glib so that we can take advantage of a single memory allocator and its debugging/tracing features. Reviewed-by: Stefan Hajnoczi Signed-off-by: Harmandeep Kaur Signed-off-by: Riku Voipio --- linux-user/syscall.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c2169664d5..06a59b4417 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1608,7 +1608,7 @@ set_timeout: } fprog.len = tswap16(tfprog->len); - filter = malloc(fprog.len * sizeof(*filter)); + filter = g_try_new(struct sock_filter, fprog.len); if (filter == NULL) { unlock_user_struct(tfilter, tfprog->filter, 1); unlock_user_struct(tfprog, optval_addr, 1); @@ -1624,7 +1624,7 @@ set_timeout: ret = get_errno(setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog))); - free(filter); + g_free(filter); unlock_user_struct(tfilter, tfprog->filter, 1); unlock_user_struct(tfprog, optval_addr, 1); @@ -1935,7 +1935,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, return NULL; } - vec = calloc(count, sizeof(struct iovec)); + vec = g_try_new0(struct iovec, count); if (vec == NULL) { errno = ENOMEM; return NULL; @@ -1999,7 +1999,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, } unlock_user(target_vec, target_addr, 0); fail2: - free(vec); + g_free(vec); errno = err; return NULL; } @@ -2024,7 +2024,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, unlock_user(target_vec, target_addr, 0); } - free(vec); + g_free(vec); } static inline int target_to_host_sock_type(int *type) @@ -2726,14 +2726,14 @@ static inline abi_long target_to_host_semarray(int semid, unsigned short **host_ nsems = semid_ds.sem_nsems; - *host_array = malloc(nsems*sizeof(unsigned short)); + *host_array = g_try_new(unsigned short, nsems); if (!*host_array) { return -TARGET_ENOMEM; } array = lock_user(VERIFY_READ, target_addr, nsems*sizeof(unsigned short), 1); if (!array) { - free(*host_array); + g_free(*host_array); return -TARGET_EFAULT; } @@ -2770,7 +2770,7 @@ static inline abi_long host_to_target_semarray(int semid, abi_ulong target_addr, for(i=0; imtype = (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); + g_free(host_mb); unlock_user_struct(target_mb, msgp, 0); return ret; @@ -3465,7 +3465,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, /* We can't fit all the extents into the fixed size buffer. * Allocate one that is large enough and use it instead. */ - fm = malloc(outbufsz); + fm = g_try_malloc(outbufsz); if (!fm) { return -TARGET_ENOMEM; } @@ -3500,7 +3500,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, } } if (free_fm) { - free(fm); + g_free(fm); } return ret; } @@ -7876,8 +7876,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct linux_dirent *dirp; abi_long count = arg3; - dirp = malloc(count); - if (!dirp) { + dirp = g_try_malloc(count); + if (!dirp) { ret = -TARGET_ENOMEM; goto fail; } @@ -7913,7 +7913,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = count1; unlock_user(target_dirp, arg2, ret); } - free(dirp); + g_free(dirp); } #else { -- cgit v1.2.1 From ff626f2d9e43c74659e8f4c284c62bb223a3bf56 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 28 Oct 2015 21:40:42 +0100 Subject: linux-user: SOCK_PACKET uses network endian to encode protocol in socket() in PACKET(7) : packet_socket = socket(AF_PACKET, int socket_type, int protocol); [...] protocol is the IEEE 802.3 protocol number in network order. See the include file for a list of allowed protocols. When protocol is set to htons(ETH_P_ALL) then all protocols are received. All incoming packets of that protocol type will be passed to the packet socket before they are passed to the protocols implemented in the kernel. [...] Compatibility In Linux 2.0, the only way to get a packet socket was by calling socket(AF_INET, SOCK_PACKET, protocol). We need to tswap16() the protocol because on big-endian, the ABI is waiting for, for instance for ETH_P_ALL, 0x0003 (big endian == network order), whereas on little-endian it is waiting for 0x0300. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 06a59b4417..965d7db7df 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2089,6 +2089,12 @@ static abi_long do_socket(int domain, int type, int protocol) if (domain == PF_NETLINK) return -TARGET_EAFNOSUPPORT; + + if (domain == AF_PACKET || + (domain == AF_INET && type == SOCK_PACKET)) { + protocol = tswap16(protocol); + } + ret = get_errno(socket(domain, type, protocol)); if (ret >= 0) { ret = sock_flags_fixup(ret, target_type); -- cgit v1.2.1 From 5d4d36658578bd5672576e95bec2b5e7a86dd3db Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 28 Oct 2015 21:40:43 +0100 Subject: linux-user: rename TargetFdFunc to TargetFdDataFunc, and structure fields accordingly Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 965d7db7df..25b846b9a2 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -295,20 +295,20 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { 0, 0, 0, 0 } }; -typedef abi_long (*TargetFdFunc)(void *, size_t); +typedef abi_long (*TargetFdDataFunc)(void *, size_t); typedef struct TargetFdTrans { - TargetFdFunc host_to_target; - TargetFdFunc target_to_host; + TargetFdDataFunc host_to_target_data; + TargetFdDataFunc target_to_host_data; } TargetFdTrans; static TargetFdTrans **target_fd_trans; static unsigned int target_fd_max; -static TargetFdFunc fd_trans_host_to_target(int fd) +static TargetFdDataFunc fd_trans_host_to_target_data(int fd) { if (fd < target_fd_max && target_fd_trans[fd]) { - return target_fd_trans[fd]->host_to_target; + return target_fd_trans[fd]->host_to_target_data; } return NULL; } @@ -5437,7 +5437,7 @@ host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo, tinfo->ssi_addr = tswap64(info->ssi_addr); } -static abi_long host_to_target_signalfd(void *buf, size_t len) +static abi_long host_to_target_data_signalfd(void *buf, size_t len) { int i; @@ -5449,7 +5449,7 @@ static abi_long host_to_target_signalfd(void *buf, size_t len) } static TargetFdTrans target_signalfd_trans = { - .host_to_target = host_to_target_signalfd, + .host_to_target_data = host_to_target_data_signalfd, }; static abi_long do_signalfd4(int fd, abi_long mask, int flags) @@ -5866,8 +5866,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, goto efault; ret = get_errno(read(arg1, p, arg3)); if (ret >= 0 && - fd_trans_host_to_target(arg1)) { - ret = fd_trans_host_to_target(arg1)(p, ret); + fd_trans_host_to_target_data(arg1)) { + ret = fd_trans_host_to_target_data(arg1)(p, ret); } unlock_user(p, arg2, ret); } -- cgit v1.2.1 From 7b36f78274e701ee17db3171ec7e9f732a60f031 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 28 Oct 2015 21:40:44 +0100 Subject: linux-user: add a function hook to translate sockaddr Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 25b846b9a2..3484132e34 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -296,9 +296,11 @@ static bitmask_transtbl fcntl_flags_tbl[] = { }; typedef abi_long (*TargetFdDataFunc)(void *, size_t); +typedef abi_long (*TargetFdAddrFunc)(void *, abi_ulong, socklen_t); typedef struct TargetFdTrans { TargetFdDataFunc host_to_target_data; TargetFdDataFunc target_to_host_data; + TargetFdAddrFunc target_to_host_addr; } TargetFdTrans; static TargetFdTrans **target_fd_trans; @@ -313,6 +315,14 @@ static TargetFdDataFunc fd_trans_host_to_target_data(int fd) return NULL; } +static TargetFdAddrFunc fd_trans_target_to_host_addr(int fd) +{ + if (fd < target_fd_max && target_fd_trans[fd]) { + return target_fd_trans[fd]->target_to_host_addr; + } + return NULL; +} + static void fd_trans_register(int fd, TargetFdTrans *trans) { unsigned int oldmax; @@ -1162,7 +1172,7 @@ static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, return 0; } -static inline abi_long target_to_host_sockaddr(struct sockaddr *addr, +static inline abi_long target_to_host_sockaddr(int fd, struct sockaddr *addr, abi_ulong target_addr, socklen_t len) { @@ -1170,6 +1180,10 @@ static inline abi_long target_to_host_sockaddr(struct sockaddr *addr, sa_family_t sa_family; struct target_sockaddr *target_saddr; + if (fd_trans_target_to_host_addr(fd)) { + return fd_trans_target_to_host_addr(fd)(addr, target_addr, len); + } + target_saddr = lock_user(VERIFY_READ, target_addr, len, 1); if (!target_saddr) return -TARGET_EFAULT; @@ -2115,7 +2129,7 @@ static abi_long do_bind(int sockfd, abi_ulong target_addr, addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(sockfd, addr, target_addr, addrlen); if (ret) return ret; @@ -2135,7 +2149,7 @@ static abi_long do_connect(int sockfd, abi_ulong target_addr, addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(sockfd, addr, target_addr, addrlen); if (ret) return ret; @@ -2155,8 +2169,9 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, if (msgp->msg_name) { msg.msg_namelen = tswap32(msgp->msg_namelen); msg.msg_name = alloca(msg.msg_namelen+1); - ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name), - msg.msg_namelen); + ret = target_to_host_sockaddr(fd, msg.msg_name, + tswapal(msgp->msg_name), + msg.msg_namelen); if (ret) { goto out2; } @@ -2418,7 +2433,7 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags, return -TARGET_EFAULT; if (target_addr) { addr = alloca(addrlen+1); - ret = target_to_host_sockaddr(addr, target_addr, addrlen); + ret = target_to_host_sockaddr(fd, addr, target_addr, addrlen); if (ret) { unlock_user(host_msg, msg, 0); return ret; -- cgit v1.2.1 From 0cf227229bfd288a67fd9d4005ee01ffdb492c70 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 28 Oct 2015 21:40:45 +0100 Subject: linux-user: manage bind with a socket of SOCK_PACKET type. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is obsolete, but if we want to use dhcp with an old distro (like debian etch), we need it. Some users (like dhclient) use SOCK_PACKET with AF_PACKET and the kernel allows that. packet(7) In Linux 2.0, the only way to get a packet socket was by calling socket(AF_INET, SOCK_PACKET, protocol). This is still supported but strongly deprecated. The main difference between the two methods is that SOCK_PACKET uses the old struct sockaddr_pkt to specify an inter‐ face, which doesn't provide physical layer independence. struct sockaddr_pkt { unsigned short spkt_family; unsigned char spkt_device[14]; unsigned short spkt_protocol; }; spkt_family contains the device type, spkt_protocol is the IEEE 802.3 protocol type as defined in and spkt_device is the device name as a null-terminated string, for example, eth0. Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3484132e34..94d64fa30d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2090,6 +2090,30 @@ static int sock_flags_fixup(int fd, int target_type) return fd; } +static abi_long packet_target_to_host_sockaddr(void *host_addr, + abi_ulong target_addr, + socklen_t len) +{ + struct sockaddr *addr = host_addr; + struct target_sockaddr *target_saddr; + + target_saddr = lock_user(VERIFY_READ, target_addr, len, 1); + if (!target_saddr) { + return -TARGET_EFAULT; + } + + memcpy(addr, target_saddr, len); + addr->sa_family = tswap16(target_saddr->sa_family); + /* spkt_protocol is big-endian */ + + unlock_user(target_saddr, target_addr, 0); + return 0; +} + +static TargetFdTrans target_packet_trans = { + .target_to_host_addr = packet_target_to_host_sockaddr, +}; + /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { @@ -2112,6 +2136,12 @@ static abi_long do_socket(int domain, int type, int protocol) ret = get_errno(socket(domain, type, protocol)); if (ret >= 0) { ret = sock_flags_fixup(ret, target_type); + if (type == SOCK_PACKET) { + /* Manage an obsolete case : + * if socket type is SOCK_PACKET, bind by name + */ + fd_trans_register(ret, &target_packet_trans); + } } return ret; } -- cgit v1.2.1 From 861d72cd28b5793fc367c46b7821a5372b66e3f4 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 28 Oct 2015 21:40:46 +0100 Subject: linux-user: check fd is >= 0 in fd_trans_host_to_target_data/fd_trans_host_to_target_addr Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 94d64fa30d..ff20ea7d74 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -309,7 +309,7 @@ static unsigned int target_fd_max; static TargetFdDataFunc fd_trans_host_to_target_data(int fd) { - if (fd < target_fd_max && target_fd_trans[fd]) { + if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { return target_fd_trans[fd]->host_to_target_data; } return NULL; @@ -317,7 +317,7 @@ static TargetFdDataFunc fd_trans_host_to_target_data(int fd) static TargetFdAddrFunc fd_trans_target_to_host_addr(int fd) { - if (fd < target_fd_max && target_fd_trans[fd]) { + if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { return target_fd_trans[fd]->target_to_host_addr; } return NULL; -- cgit v1.2.1 From 2a0fa68fb9761e2eb3dae4034131948d33018dc9 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 23 Nov 2015 11:38:26 +0100 Subject: linux-user,sh4: fix signal retcode address To return from a signal, setup_frame() puts an instruction to be executed in the stack. This sequence calls the syscall sigreturn(). The address of the instruction must be set in the PR register to be executed. This patch fixes this: the current code sets the register to the address of the instruction in the host address space (which can be 64bit whereas PR is only 32bit), but the virtual CPU can't access this address space, so we put in PR the address of the instruction in the guest address space. This patch also removes an useless variable (ret) in the modified functions. Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/signal.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 919aa836fa..d4d83f247a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3210,7 +3210,6 @@ static void setup_frame(int sig, struct target_sigaction *ka, struct target_sigframe *frame; abi_ulong frame_addr; int i; - int err = 0; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_frame(regs, frame_addr); @@ -3229,15 +3228,14 @@ static void setup_frame(int sig, struct target_sigaction *ka, regs->pr = (unsigned long) ka->sa_restorer; } else { /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_sigframe, retcode); __put_user(MOVW(2), &frame->retcode[0]); __put_user(TRAP_NOARG, &frame->retcode[1]); __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) frame->retcode; + regs->pr = (unsigned long) retcode_addr; } - if (err) - goto give_sigsegv; - /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; regs->gregs[4] = sig; /* Arg for signal handler */ @@ -3260,7 +3258,6 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, struct target_rt_sigframe *frame; abi_ulong frame_addr; int i; - int err = 0; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_rt_frame(regs, frame_addr); @@ -3290,15 +3287,14 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, regs->pr = (unsigned long) ka->sa_restorer; } else { /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_rt_sigframe, retcode); __put_user(MOVW(2), &frame->retcode[0]); __put_user(TRAP_NOARG, &frame->retcode[1]); __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) frame->retcode; + regs->pr = (unsigned long) retcode_addr; } - if (err) - goto give_sigsegv; - /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; regs->gregs[4] = sig; /* Arg for signal handler */ -- cgit v1.2.1 From e6deac9cf99e013a3e253aff2332836c86d8a52c Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 30 Dec 2015 09:10:54 +0800 Subject: linux-user/mmap.c: Always zero MAP_ANONYMOUS memory in mmap_frag() When mapping MAP_ANONYMOUS memory fragments, still need notice about to set it zero, or it will cause issues. Signed-off-by: Chen Gang Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/mmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 7b459d5100..c6c478e552 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -186,10 +186,12 @@ static int mmap_frag(abi_ulong real_start, if (prot_new != (prot1 | PROT_WRITE)) mprotect(host_start, qemu_host_page_size, prot_new); } else { - /* just update the protection */ if (prot_new != prot1) { mprotect(host_start, qemu_host_page_size, prot_new); } + if (prot_new & PROT_WRITE) { + memset(g2h(start), 0, end - start); + } } return 0; } -- cgit v1.2.1 From 27e112f9fd0dcac6c3448f0c71db1b62f0589ffd Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 29 Dec 2015 12:43:39 +0300 Subject: unicore32: convert get_sp_from_cpustate from macro to inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other architectures define get_sp_from_cpustate as an inline function, only unicore32 uses a #define. With this, some usages are impossible, for example, enabling sigaltstack in linux-user/syscall.c results in linux-user/syscall.c: In function ‘do_syscall’: linux-user/syscall.c:8299:39: error: dereferencing ‘void *’ pointer [-Werror] get_sp_from_cpustate(arg1, arg2, get_sp_from_cpustate((CPUArchState *)cpu_env)); ^ linux-user/syscall.c:8299:39: error: request for member ‘regs’ in something not a structure or union Signed-off-by: Michael Tokarev Signed-off-by: Riku Voipio --- linux-user/unicore32/target_signal.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux-user/unicore32/target_signal.h b/linux-user/unicore32/target_signal.h index 8b255c4550..7c442381ab 100644 --- a/linux-user/unicore32/target_signal.h +++ b/linux-user/unicore32/target_signal.h @@ -21,6 +21,10 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 -#define get_sp_from_cpustate(cpustate) (cpustate->regs[29]) +static inline abi_ulong get_sp_from_cpustate(CPUUniCore32State *state) +{ + return state->regs[29]; +} + #endif /* TARGET_SIGNAL_H */ -- cgit v1.2.1 From c0d35736323e5b638aac45bfc25f74fa7b6e10f1 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 29 Dec 2015 12:51:13 +0300 Subject: linux-user: enable sigaltstack for all architectures There is no reason to limit sigaltstack syscall to just a few architectures and pretend it is not implemented for others. If some architecture is not ready for this, that architecture should be fixed instead. This fixes LP#1516408. Signed-off-by: Michael Tokarev Reviewed-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ff20ea7d74..d1eb3eb6a5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8503,14 +8503,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } 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) || 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: -- cgit v1.2.1 From fad6c58a3d2b7d4c20754b2b906927905db13392 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 11 Jan 2016 13:58:50 +0800 Subject: linux-user/syscall.c: Use SOL_SOCKET instead of level for setsockopt() In this case, level is TARGET_SOL_SOCKET, but we need SOL_SOCKET for setsockopt(). Signed-off-by: Chen Gang Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d1eb3eb6a5..11b72e1680 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1659,7 +1659,8 @@ set_timeout: addr_ifname = alloca(IFNAMSIZ); memcpy(addr_ifname, dev_ifname, optlen); addr_ifname[optlen] = 0; - ret = get_errno(setsockopt(sockfd, level, optname, addr_ifname, optlen)); + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + addr_ifname, optlen)); unlock_user (dev_ifname, optval_addr, 0); return ret; } -- cgit v1.2.1 From e73eecbdc278970f6d829951293efc2167b6a4c5 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Thu, 24 Dec 2015 19:59:58 +0100 Subject: linux-user: Update m68k syscall definitions to match Linux 4.4. Signed-off-by: John Paul Adrian Glaubitz Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/m68k/syscall_nr.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/linux-user/m68k/syscall_nr.h b/linux-user/m68k/syscall_nr.h index 25f8521ec3..a2daba034a 100644 --- a/linux-user/m68k/syscall_nr.h +++ b/linux-user/m68k/syscall_nr.h @@ -349,3 +349,30 @@ #define TARGET_NR_process_vm_writev 346 #define TARGET_NR_kcmp 347 #define TARGET_NR_finit_module 348 +#define TARGET_NR_sched_setattr 349 +#define TARGET_NR_sched_getattr 350 +#define TARGET_NR_renameat2 351 +#define TARGET_NR_getrandom 352 +#define TARGET_NR_memfd_create 353 +#define TARGET_NR_bpf 354 +#define TARGET_NR_execveat 355 +#define TARGET_NR_socket 356 +#define TARGET_NR_socketpair 357 +#define TARGET_NR_bind 358 +#define TARGET_NR_connect 359 +#define TARGET_NR_listen 360 +#define TARGET_NR_accept4 361 +#define TARGET_NR_getsockopt 362 +#define TARGET_NR_setsockopt 363 +#define TARGET_NR_getsockname 364 +#define TARGET_NR_getpeername 365 +#define TARGET_NR_sendto 366 +#define TARGET_NR_sendmsg 367 +#define TARGET_NR_recvfrom 368 +#define TARGET_NR_recvmsg 369 +#define TARGET_NR_shutdown 370 +#define TARGET_NR_recvmmsg 371 +#define TARGET_NR_sendmmsg 372 +#define TARGET_NR_userfaultfd 373 +#define TARGET_NR_membarrier 374 +#define TARGET_NR_mlock2 375 -- cgit v1.2.1 From 5a53dc5042d9250f2011e54cd065ed360e34d2ac Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Thu, 24 Dec 2015 19:59:59 +0100 Subject: linux-user: Add SOCKOP_sendmmsg and SOCKOP_recvmmsg socket call, wire them up. Adds the definitions for the socket calls SOCKOP_sendmmsg and SOCKOP_recvmmsg and wires them up with the rest of the code. The necessary function do_sendrecvmmsg() is already present in linux-user/syscall.c. After adding these two definitions and wiring them up, I no longer receive an error message about the unimplemented socket calls when running "apt-get update" on Debian unstable running on qemu with glibc_2.21 on m68k. Signed-off-by: John Paul Adrian Glaubitz Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 8 ++++++-- linux-user/syscall_defs.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 11b72e1680..0cbace45fd 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2272,7 +2272,6 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, return ret; } -#ifdef TARGET_NR_sendmmsg /* We don't rely on the C library to have sendmmsg/recvmmsg support, * so it might not have this *mmsg-specific flag either. */ @@ -2319,7 +2318,6 @@ static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec, } return ret; } -#endif /* If we don't have a system accept4() then just call accept. * The callsites to do_accept4() will ensure that they don't @@ -2542,6 +2540,8 @@ static abi_long do_socketcall(int num, abi_ulong vptr) [SOCKOP_shutdown] = 2, /* sockfd, how */ [SOCKOP_sendmsg] = 3, /* sockfd, msg, flags */ [SOCKOP_recvmsg] = 3, /* sockfd, msg, flags */ + [SOCKOP_sendmmsg] = 4, /* sockfd, msgvec, vlen, flags */ + [SOCKOP_recvmmsg] = 4, /* sockfd, msgvec, vlen, flags */ [SOCKOP_setsockopt] = 5, /* sockfd, level, optname, optval, optlen */ [SOCKOP_getsockopt] = 5, /* sockfd, level, optname, optval, optlen */ }; @@ -2592,6 +2592,10 @@ static abi_long do_socketcall(int num, abi_ulong vptr) return do_sendrecvmsg(a[0], a[1], a[2], 1); case SOCKOP_recvmsg: /* sockfd, msg, flags */ return do_sendrecvmsg(a[0], a[1], a[2], 0); + case SOCKOP_sendmmsg: /* sockfd, msgvec, vlen, flags */ + return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1); + case SOCKOP_recvmmsg: /* sockfd, msgvec, vlen, flags */ + return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 0); case SOCKOP_setsockopt: /* sockfd, level, optname, optval, optlen */ return do_setsockopt(a[0], a[1], a[2], a[3], a[4]); case SOCKOP_getsockopt: /* sockfd, level, optname, optval, optlen */ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2fd4afffb8..9e2b3c200a 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -28,6 +28,8 @@ #define SOCKOP_sendmsg 16 #define SOCKOP_recvmsg 17 #define SOCKOP_accept4 18 +#define SOCKOP_recvmmsg 19 +#define SOCKOP_sendmmsg 20 #define IPCOP_semop 1 #define IPCOP_semget 2 -- cgit v1.2.1 From 530c003252e07f1ea9df7f8a9adb1082d3a2eb08 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 24 Dec 2015 09:07:33 +0800 Subject: linux-user/mmap.c: Use end instead of real_end in target_mmap The fragment must effectively be mapped only to "end" not to "real_end" (which is a host page aligned address, and thus this is not a fragment). It is consistent with what it is done in the case of one single page. Signed-off-by: Chen Gang Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index c6c478e552..445e8c6785 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -538,7 +538,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, /* handle the end of the mapping */ if (end < real_end) { ret = mmap_frag(real_end - qemu_host_page_size, - real_end - qemu_host_page_size, real_end, + real_end - qemu_host_page_size, end, prot, flags, fd, offset + real_end - qemu_host_page_size - start); if (ret == -1) -- cgit v1.2.1