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 f1f8da90ba..51f84c31a1 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 */ -- 2.52.0
