On Fri, May 22, 2026 at 5:46 PM Pierrick Bouvier <
[email protected]> wrote:

> On 5/18/2026 2:27 PM, Warner Losh wrote:
> > Add I/O multiplexing system call shims: select, pselect, and ppoll
> > with proper fd_set, timespec/timeval, and signal mask conversion.
> >
> > Signed-off-by: Stacey Son <[email protected]>
> > Signed-off-by: Mikaël Urankar <[email protected]>
> > Signed-off-by: Kyle Evans <[email protected]>
> > Signed-off-by: Warner Losh <[email protected]>
> > Assisted-by: Claude Opus 4.6 (1M context)
> > ---
> >  bsd-user/freebsd/os-time.h | 202
> +++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 202 insertions(+)
> >
> > diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
> > index 05fa043442..12c5ba02e8 100644
> > --- a/bsd-user/freebsd/os-time.h
> > +++ b/bsd-user/freebsd/os-time.h
> > @@ -427,5 +427,207 @@ static inline abi_long
> do_freebsd_ktimer_gettime(abi_long arg1, abi_long arg2)
> >      return ret;
> >  }
> >
> > +/* select(2) */
> > +static inline abi_long do_freebsd_select(CPUArchState *env, int n,
> > +        abi_ulong rfd_addr, abi_ulong wfd_addr, abi_ulong efd_addr,
> > +        abi_ulong target_tv_addr)
> > +{
> > +    fd_set rfds, wfds, efds;
> > +    fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
> > +    struct timeval tv, *tvp;
> > +    abi_long ret, error;
> > +
> > +    ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n);
> > +    if (ret != 0) {
> > +        return ret;
> > +    }
> > +    ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n);
> > +    if (ret != 0) {
> > +        return ret;
> > +    }
> > +    ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n);
> > +    if (ret != 0) {
> > +        return ret;
> > +    }
> > +
>
> We can factor this with a macro
>
> > +    if (target_tv_addr != 0) {
> > +        if (t2h_freebsd_timeval(&tv, target_tv_addr)) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        tvp = &tv;
> > +    } else {
> > +        tvp = NULL;
> > +    }
> > +
> > +    ret = get_errno(safe_select(n, rfds_ptr, wfds_ptr, efds_ptr, tvp));
> > +
> > +    if (!is_error(ret)) {
>
> We can simplify depth here by using an early return.
> if (is_error(ret)) {
>   return ret;
> }
>
> > +        if (rfd_addr != 0) {
> > +            error = copy_to_user_fdset(rfd_addr, &rfds, n);
> > +            if (error != 0) {
> > +                return error;
> > +            }
> > +        }
> > +        if (wfd_addr != 0) {
> > +            error = copy_to_user_fdset(wfd_addr, &wfds, n);
> > +            if (error != 0) {
> > +                return error;
> > +            }
> > +        }
> > +        if (efd_addr != 0) {
> > +            error = copy_to_user_fdset(efd_addr, &efds, n);
> > +            if (error != 0) {
> > +                return error;
> > +            }
> > +        }
>
> Same, reuse the macro defined above.
>
> > +        if (target_tv_addr != 0) {
> > +            error = h2t_freebsd_timeval(&tv, target_tv_addr);
> > +            if (is_error(error)) {
> > +                return error;
> > +            }
> > +        }
> > +    }
> > +    return ret;
> > +}
> > +
> > +/* pselect(2) */
> > +static inline abi_long do_freebsd_pselect(CPUArchState *env, int n,
> > +        abi_ulong rfd_addr, abi_ulong wfd_addr, abi_ulong efd_addr,
> > +        abi_ulong ts_addr, abi_ulong set_addr)
> > +{
> > +    CPUState *cpu = env_cpu(env);
> > +    TaskState *tstate = cpu->opaque;
> > +    fd_set rfds, wfds, efds;
> > +    fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
> > +    sigset_t *set_ptr;
> > +    struct timespec ts, *ts_ptr;
> > +    void *p;
> > +    abi_long ret, error;
> > +
> > +    ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n);
> > +    if (is_error(ret)) {
> > +        return ret;
> > +    }
> > +    ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n);
> > +    if (is_error(ret)) {
> > +        return ret;
> > +    }
> > +    ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n);
> > +    if (is_error(ret)) {
> > +        return ret;
> > +    }
> > +
>
> Same than above.
>

I like these suggestions, but due to lack of good regression tests, I'm
going to defer them
since I know the code that's there works.
https://github.com/qemu-bsd-user/qemu-bsd-user/issues/66
memorializes the issue.

Warner


> > +    /* Unlike select(), pselect() uses struct timespec instead of
> timeval */
> > +    if (ts_addr) {
> > +        if (t2h_freebsd_timespec(&ts, ts_addr)) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        ts_ptr = &ts;
> > +    } else {
> > +        ts_ptr = NULL;
> > +    }
> > +
> > +    if (set_addr != 0) {
> > +        p = lock_user(VERIFY_READ, set_addr, sizeof(target_sigset_t),
> 1);
> > +        if (p == NULL) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        target_to_host_sigset(&tstate->sigsuspend_mask, p);
> > +        unlock_user(p, set_addr, 0);
> > +        set_ptr = &tstate->sigsuspend_mask;
> > +    } else {
> > +        set_ptr = NULL;
> > +    }
> > +
> > +    ret = get_errno(safe_pselect(n, rfds_ptr, wfds_ptr, efds_ptr,
> ts_ptr,
> > +        set_ptr));
> > +    if (ret != -TARGET_ERESTART)  {
> > +        tstate->in_sigsuspend = true;
> > +    }
> > +    if (!is_error(ret)) {
> > +        if (rfd_addr != 0) {
> > +            error = copy_to_user_fdset(rfd_addr, &rfds, n);
> > +            if (is_error(error)) {
> > +                return error;
> > +            }
> > +        }
> > +        if (wfd_addr != 0) {
> > +            error = copy_to_user_fdset(wfd_addr, &wfds, n);
> > +            if (is_error(error)) {
> > +                return error;
> > +            }
> > +        }
> > +        if (efd_addr != 0) {
> > +            error = copy_to_user_fdset(efd_addr, &efds, n);
> > +            if (is_error(error)) {
> > +                return error;
> > +            }
> > +        }
> > +    }
> > +    return ret;
> > +}
> > +
> > +/* ppoll(2) */
> > +static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long
> arg1,
> > +        abi_long arg2, abi_ulong arg3, abi_ulong arg4)
> > +{
> > +    CPUState *cpu = env_cpu(env);
> > +    TaskState *tstate = cpu->opaque;
> > +    abi_long ret;
> > +    nfds_t i, nfds = arg2;
> > +    struct pollfd *pfd;
> > +    struct target_pollfd *target_pfd;
> > +    struct timespec ts, *ts_ptr;
> > +    sigset_t *set_ptr;
> > +    void *p;
> > +
> > +    target_pfd = lock_user(VERIFY_WRITE, arg1,
> > +                           sizeof(struct target_pollfd) * nfds, 1);
> > +    if (!target_pfd) {
> > +        return -TARGET_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);
> > +    }
> > +
> > +    /* Unlike poll(), ppoll() uses struct timespec. */
> > +    if (arg3) {
> > +        if (t2h_freebsd_timespec(&ts, arg3)) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        ts_ptr = &ts;
> > +    } else {
> > +        ts_ptr = NULL;
> > +    }
> > +
> > +    if (arg4 != 0) {
> > +        p = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1);
> > +        if (p == NULL) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        target_to_host_sigset(&tstate->sigsuspend_mask, p);
> > +        unlock_user(p, arg4, 0);
> > +        set_ptr = &tstate->sigsuspend_mask;
> > +    } else {
> > +        set_ptr = NULL;
> > +    }
> > +
> > +    ret = get_errno(ppoll(pfd, nfds, ts_ptr, set_ptr));
> > +    if (ret != -TARGET_ERESTART) {
> > +        tstate->in_sigsuspend = true;
> > +    }
> > +    if (!is_error(ret)) {
> > +        for (i = 0; i < nfds; i++) {
> > +            target_pfd[i].revents = tswap16(pfd[i].revents);
> > +        }
> > +    }
> > +    unlock_user(target_pfd, arg1, sizeof(struct target_pollfd) * nfds);
> > +
> > +    return ret;
> > +}
> > +
> > +/* kqueue(2) */
> >
> >  #endif /* FREEBSD_OS_TIME_H */
> >
>
>

Reply via email to