diff mbox

linux-user: add pselect6 syscall support

Message ID 1305677682-21158-1-git-send-email-vapier@gentoo.org
State New
Headers show

Commit Message

Mike Frysinger May 18, 2011, 12:14 a.m. UTC
Some architectures (like Blackfin) only implement pselect6 (and skip
select/newselect).  So add support for it using existing newselect code.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 linux-user/syscall.c |  145 +++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 126 insertions(+), 19 deletions(-)

Comments

Riku Voipio June 3, 2011, 6:10 p.m. UTC | #1
On 05/18/2011 03:14 AM, Mike Frysinger wrote:
> Some architectures (like Blackfin) only implement pselect6 (and skip
> select/newselect).  So add support for it using existing newselect code.

There is a blackfin qemu? Anyways, with this patch pselect01 ltp 
testcase starts failing. Looks like (at least on arm), arg6 is set and 
valid pointer, but arg7[0] is 0, which quite doesn't work as lock_user 
read target...

Checking if arg7==0 and setting sig_ptr to null in that case makes the
testcase to work, but is that correct?

Riku

>
> Signed-off-by: Mike Frysinger<vapier@gentoo.org>
> ---
>   linux-user/syscall.c |  145 +++++++++++++++++++++++++++++++++++++++++++-------
>   1 files changed, 126 insertions(+), 19 deletions(-)
>
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 6e7d88e..b35c437 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -550,6 +550,15 @@ _syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds,
>             size_t, sigsetsize)
>   #endif
>
> +#if defined(TARGET_NR_pselect6)
> +#ifndef __NR_pselect6
> +# define __NR_pselect6 -1
> +#endif
> +#define __NR_sys_pselect6 __NR_pselect6
> +_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds,
> +          fd_set *, exceptfds, struct timespec *, timeout, void *, sig);
> +#endif
> +
>   extern int personality(int);
>   extern int flock(int, int);
>   extern int setfsuid(int);
> @@ -787,6 +796,20 @@ static inline abi_long copy_from_user_fdset(fd_set *fds,
>       return 0;
>   }
>
> +static inline abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr,
> +                                                 abi_ulong target_fds_addr,
> +                                                 int n)
> +{
> +    if (target_fds_addr) {
> +        if (copy_from_user_fdset(fds, target_fds_addr, n))
> +            return -TARGET_EFAULT;
> +        *fds_ptr = fds;
> +    } else {
> +        *fds_ptr = NULL;
> +    }
> +    return 0;
> +}
> +
>   static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr,
>                                             const fd_set *fds,
>                                             int n)
> @@ -952,6 +975,7 @@ static inline abi_long copy_to_user_mq_attr(abi_ulong target_mq_attr_addr,
>   }
>   #endif
>
> +#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect)
>   /* do_select() must return target values and target errnos. */
>   static abi_long do_select(int n,
>                             abi_ulong rfd_addr, abi_ulong wfd_addr,
> @@ -962,26 +986,17 @@ static abi_long do_select(int n,
>       struct timeval tv, *tv_ptr;
>       abi_long ret;
>
> -    if (rfd_addr) {
> -        if (copy_from_user_fdset(&rfds, rfd_addr, n))
> -            return -TARGET_EFAULT;
> -        rfds_ptr =&rfds;
> -    } else {
> -        rfds_ptr = NULL;
> +    ret = copy_from_user_fdset_ptr(&rfds,&rfds_ptr, rfd_addr, n);
> +    if (ret) {
> +        return ret;
>       }
> -    if (wfd_addr) {
> -        if (copy_from_user_fdset(&wfds, wfd_addr, n))
> -            return -TARGET_EFAULT;
> -        wfds_ptr =&wfds;
> -    } else {
> -        wfds_ptr = NULL;
> +    ret = copy_from_user_fdset_ptr(&wfds,&wfds_ptr, wfd_addr, n);
> +    if (ret) {
> +        return ret;
>       }
> -    if (efd_addr) {
> -        if (copy_from_user_fdset(&efds, efd_addr, n))
> -            return -TARGET_EFAULT;
> -        efds_ptr =&efds;
> -    } else {
> -        efds_ptr = NULL;
> +    ret = copy_from_user_fdset_ptr(&efds,&efds_ptr, efd_addr, n);
> +    if (ret) {
> +        return ret;
>       }
>
>       if (target_tv_addr) {
> @@ -1008,6 +1023,7 @@ static abi_long do_select(int n,
>
>       return ret;
>   }
> +#endif
>
>   static abi_long do_pipe2(int host_pipe[], int flags)
>   {
> @@ -5569,7 +5585,98 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
>   #endif
>   #ifdef TARGET_NR_pselect6
>       case TARGET_NR_pselect6:
> -	    goto unimplemented_nowarn;
> +        {
> +            abi_long rfd_addr, wfd_addr, efd_addr, n, ts_addr;
> +            fd_set rfds, wfds, efds;
> +            fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
> +            struct timespec ts, *ts_ptr;
> +
> +            /*
> +             * The 6th arg is actually two args smashed together,
> +             * so we cannot use the C library.
> +             */
> +            sigset_t set;
> +            struct {
> +                sigset_t *set;
> +                size_t size;
> +            } sig, *sig_ptr;
> +
> +            abi_ulong arg_sigset, arg_sigsize, *arg7;
> +            target_sigset_t *target_sigset;
> +
> +            n = arg1;
> +            rfd_addr = arg2;
> +            wfd_addr = arg3;
> +            efd_addr = arg4;
> +            ts_addr = arg5;
> +
> +            ret = copy_from_user_fdset_ptr(&rfds,&rfds_ptr, rfd_addr, n);
> +            if (ret) {
> +                goto fail;
> +            }
> +            ret = copy_from_user_fdset_ptr(&wfds,&wfds_ptr, wfd_addr, n);
> +            if (ret) {
> +                goto fail;
> +            }
> +            ret = copy_from_user_fdset_ptr(&efds,&efds_ptr, efd_addr, n);
> +            if (ret) {
> +                goto fail;
> +            }
> +
> +            /*
> +             * This takes a timespec, and not a timeval, so we cannot
> +             * the do_select() helper ...
> +             */
> +            if (ts_addr) {
> +                if (target_to_host_timespec(&ts, ts_addr)) {
> +                    goto efault;
> +                }
> +                ts_ptr =&ts;
> +            } else {
> +                ts_ptr = NULL;
> +            }
> +
> +            /* Extract the two packed args for the sigset */
> +            if (arg6) {
> +                sig_ptr =&sig;
> +                sig.set =&set;
> +                sig.size = _NSIG / 8;
> +
> +                arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1);
> +                if (!arg7) {
> +                    goto efault;
> +                }
> +                arg_sigset = tswapl(arg7[0]);
> +                arg_sigsize = tswapl(arg7[1]);
> +                unlock_user(arg7, arg6, 0);
> +
> +                target_sigset = lock_user(VERIFY_READ, arg_sigset,
> +                                          sizeof(*target_sigset), 1);
> +                if (!target_sigset) {
> +                    goto efault;
> +                }
> +                target_to_host_sigset(&set, target_sigset);
> +                unlock_user(target_sigset, arg_sigset, 0);
> +            } else {
> +                sig_ptr = NULL;
> +            }
> +
> +            ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr,
> +                                         ts_ptr, sig_ptr));
> +
> +            if (!is_error(ret)) {
> +                if (rfd_addr&&  copy_to_user_fdset(rfd_addr,&rfds, n))
> +                    goto efault;
> +                if (wfd_addr&&  copy_to_user_fdset(wfd_addr,&wfds, n))
> +                    goto efault;
> +                if (efd_addr&&  copy_to_user_fdset(efd_addr,&efds, n))
> +                    goto efault;
> +
> +                if (ts_addr&&  host_to_target_timespec(ts_addr,&ts))
> +                    goto efault;
> +            }
> +        }
> +        break;
>   #endif
>       case TARGET_NR_symlink:
>           {
Mike Frysinger June 3, 2011, 8:55 p.m. UTC | #2
On Fri, Jun 3, 2011 at 14:10, riku voipio wrote:
> On 05/18/2011 03:14 AM, Mike Frysinger wrote:
>> Some architectures (like Blackfin) only implement pselect6 (and skip
>> select/newselect).  So add support for it using existing newselect code.
>
> There is a blackfin qemu?

i posted it to the list for feedback, but i havent followed up since

> Anyways, with this patch pselect01 ltp testcase
> starts failing. Looks like (at least on arm), arg6 is set and valid pointer,
> but arg7[0] is 0, which quite doesn't work as lock_user read target...
>
> Checking if arg7==0 and setting sig_ptr to null in that case makes the
> testcase to work, but is that correct?

looking at the kernel code, it accepts a 6th arg which contains a NULL
pointer (and then just ignores it), so i'll do the same
-mike
Andreas Färber June 4, 2011, 11:03 a.m. UTC | #3
Am 03.06.2011 um 20:10 schrieb Riku Voipio:

> On 05/18/2011 03:14 AM, Mike Frysinger wrote:
>> Some architectures (like Blackfin) only implement pselect6 (and skip
>> select/newselect).  So add support for it using existing newselect  
>> code.
>
> There is a blackfin qemu?

http://blackfin.uclinux.org/git/?p=users/vapier/qemu.git;a=summary

Mike, you should add a feature page for your target: http://wiki.qemu.org/Contribute/StartHere

Andreas
diff mbox

Patch

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 6e7d88e..b35c437 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -550,6 +550,15 @@  _syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds,
           size_t, sigsetsize)
 #endif
 
+#if defined(TARGET_NR_pselect6)
+#ifndef __NR_pselect6
+# define __NR_pselect6 -1
+#endif
+#define __NR_sys_pselect6 __NR_pselect6
+_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds,
+          fd_set *, exceptfds, struct timespec *, timeout, void *, sig);
+#endif
+
 extern int personality(int);
 extern int flock(int, int);
 extern int setfsuid(int);
@@ -787,6 +796,20 @@  static inline abi_long copy_from_user_fdset(fd_set *fds,
     return 0;
 }
 
+static inline abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr,
+                                                 abi_ulong target_fds_addr,
+                                                 int n)
+{
+    if (target_fds_addr) {
+        if (copy_from_user_fdset(fds, target_fds_addr, n))
+            return -TARGET_EFAULT;
+        *fds_ptr = fds;
+    } else {
+        *fds_ptr = NULL;
+    }
+    return 0;
+}
+
 static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr,
                                           const fd_set *fds,
                                           int n)
@@ -952,6 +975,7 @@  static inline abi_long copy_to_user_mq_attr(abi_ulong target_mq_attr_addr,
 }
 #endif
 
+#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect)
 /* do_select() must return target values and target errnos. */
 static abi_long do_select(int n,
                           abi_ulong rfd_addr, abi_ulong wfd_addr,
@@ -962,26 +986,17 @@  static abi_long do_select(int n,
     struct timeval tv, *tv_ptr;
     abi_long ret;
 
-    if (rfd_addr) {
-        if (copy_from_user_fdset(&rfds, rfd_addr, n))
-            return -TARGET_EFAULT;
-        rfds_ptr = &rfds;
-    } else {
-        rfds_ptr = NULL;
+    ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n);
+    if (ret) {
+        return ret;
     }
-    if (wfd_addr) {
-        if (copy_from_user_fdset(&wfds, wfd_addr, n))
-            return -TARGET_EFAULT;
-        wfds_ptr = &wfds;
-    } else {
-        wfds_ptr = NULL;
+    ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n);
+    if (ret) {
+        return ret;
     }
-    if (efd_addr) {
-        if (copy_from_user_fdset(&efds, efd_addr, n))
-            return -TARGET_EFAULT;
-        efds_ptr = &efds;
-    } else {
-        efds_ptr = NULL;
+    ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n);
+    if (ret) {
+        return ret;
     }
 
     if (target_tv_addr) {
@@ -1008,6 +1023,7 @@  static abi_long do_select(int n,
 
     return ret;
 }
+#endif
 
 static abi_long do_pipe2(int host_pipe[], int flags)
 {
@@ -5569,7 +5585,98 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #endif
 #ifdef TARGET_NR_pselect6
     case TARGET_NR_pselect6:
-	    goto unimplemented_nowarn;
+        {
+            abi_long rfd_addr, wfd_addr, efd_addr, n, ts_addr;
+            fd_set rfds, wfds, efds;
+            fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
+            struct timespec ts, *ts_ptr;
+
+            /*
+             * The 6th arg is actually two args smashed together,
+             * so we cannot use the C library.
+             */
+            sigset_t set;
+            struct {
+                sigset_t *set;
+                size_t size;
+            } sig, *sig_ptr;
+
+            abi_ulong arg_sigset, arg_sigsize, *arg7;
+            target_sigset_t *target_sigset;
+
+            n = arg1;
+            rfd_addr = arg2;
+            wfd_addr = arg3;
+            efd_addr = arg4;
+            ts_addr = arg5;
+
+            ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n);
+            if (ret) {
+                goto fail;
+            }
+            ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n);
+            if (ret) {
+                goto fail;
+            }
+            ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n);
+            if (ret) {
+                goto fail;
+            }
+
+            /*
+             * This takes a timespec, and not a timeval, so we cannot
+             * the do_select() helper ...
+             */
+            if (ts_addr) {
+                if (target_to_host_timespec(&ts, ts_addr)) {
+                    goto efault;
+                }
+                ts_ptr = &ts;
+            } else {
+                ts_ptr = NULL;
+            }
+
+            /* Extract the two packed args for the sigset */
+            if (arg6) {
+                sig_ptr = &sig;
+                sig.set = &set;
+                sig.size = _NSIG / 8;
+
+                arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1);
+                if (!arg7) {
+                    goto efault;
+                }
+                arg_sigset = tswapl(arg7[0]);
+                arg_sigsize = tswapl(arg7[1]);
+                unlock_user(arg7, arg6, 0);
+
+                target_sigset = lock_user(VERIFY_READ, arg_sigset,
+                                          sizeof(*target_sigset), 1);
+                if (!target_sigset) {
+                    goto efault;
+                }
+                target_to_host_sigset(&set, target_sigset);
+                unlock_user(target_sigset, arg_sigset, 0);
+            } else {
+                sig_ptr = NULL;
+            }
+
+            ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr,
+                                         ts_ptr, sig_ptr));
+
+            if (!is_error(ret)) {
+                if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n))
+                    goto efault;
+                if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n))
+                    goto efault;
+                if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n))
+                    goto efault;
+
+                if (ts_addr && host_to_target_timespec(ts_addr, &ts))
+                    goto efault;
+            }
+        }
+        break;
 #endif
     case TARGET_NR_symlink:
         {