| Message ID | 20260518-misc-2026q2-v2-19-6c16fe448301@bsdimp.com |
|---|---|
| State | New |
| Headers | show |
| Series | bsd-user: Upstream most of the remaining system calls | expand |
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 <sson@FreeBSD.org> > Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com> > Signed-off-by: Kyle Evans <kevans@FreeBSD.org> > Signed-off-by: Warner Losh <imp@bsdimp.com> > 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. > + /* 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 */ >
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; + } + + 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)) { + 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; + } + } + 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; + } + + /* 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 */