diff mbox series

[v2,20/37] bsd-user: Add kqueue and kevent functions to os-time.h

Message ID 20260518-misc-2026q2-v2-20-6c16fe448301@bsdimp.com
State New
Headers show
Series bsd-user: Upstream most of the remaining system calls | expand

Commit Message

Warner Losh May 18, 2026, 9:27 p.m. UTC
Add event notification system call shims: kqueue, freebsd11_kevent
(legacy 32-bit data field), and kevent (with 64-bit ext fields).

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
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 | 173 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 173 insertions(+)

Comments

Pierrick Bouvier May 22, 2026, 11:49 p.m. UTC | #1
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add event notification system call shims: kqueue, freebsd11_kevent
> (legacy 32-bit data field), and kevent (with 64-bit ext fields).
> 
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
> Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
> 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 | 173 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 173 insertions(+)
> 
> diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
> index 12c5ba02e8..078355392d 100644
> --- a/bsd-user/freebsd/os-time.h
> +++ b/bsd-user/freebsd/os-time.h
> @@ -629,5 +629,178 @@ static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long arg1,
>  }
>  
>  /* kqueue(2) */
> +static inline abi_long do_freebsd_kqueue(void)
> +{
> +
> +    return get_errno(kqueue());
> +}
> +
> +/* kevent(2) */
> +/* XXX Maybe some day, consolidate these two... */
> +static inline abi_long do_freebsd_freebsd11_kevent(abi_long arg1,
> +    abi_ulong arg2, abi_long arg3, abi_ulong arg4, abi_long arg5, abi_long arg6)
> +{
> +    abi_long ret;
> +    struct kevent *changelist = NULL, *eventlist = NULL;
> +    struct target_freebsd11_kevent *target_changelist, *target_eventlist;
> +    struct timespec ts;
> +    int i;
> +
> +    if (arg3 != 0) {
> +        target_changelist = lock_user(VERIFY_READ, arg2,
> +                                      sizeof(*target_changelist) * arg3, 1);
> +        if (target_changelist == NULL) {
> +            return -TARGET_EFAULT;
> +        }
> +
> +        changelist = alloca(sizeof(struct kevent) * arg3);
> +        memset(changelist, '\0', sizeof(struct kevent) * arg3);
> +        for (i = 0; i < arg3; i++) {
> +            __get_user(changelist[i].ident, &target_changelist[i].ident);
> +            __get_user(changelist[i].filter, &target_changelist[i].filter);
> +            __get_user(changelist[i].flags, &target_changelist[i].flags);
> +            __get_user(changelist[i].fflags, &target_changelist[i].fflags);
> +            __get_user(changelist[i].data, &target_changelist[i].data);
> +            /* __get_user(changelist[i].udata, &target_changelist[i].udata); */
> +#if TARGET_ABI_BITS == 32
> +            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
> +            tswap32s((uint32_t *)&changelist[i].udata);
> +#else
> +            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
> +            tswap64s((uint64_t *)&changelist[i].udata);
> +#endif
> +        }
> +        unlock_user(target_changelist, arg2, sizeof(*target_changelist) * arg3);
> +    }
> +
> +    if (arg5 != 0) {
> +        eventlist = alloca(sizeof(struct kevent) * arg5);
> +    }
> +    if (arg6 != 0) {
> +        if (t2h_freebsd_timespec(&ts, arg6)) {
> +            return -TARGET_EFAULT;
> +        }
> +    }
> +    ret = get_errno(safe_kevent(arg1, changelist, arg3, eventlist, arg5,
> +                                arg6 != 0 ? &ts : NULL));
> +
> +    if (arg5 == 0) {
> +        return ret;
> +    }
> +
> +    if (!is_error(ret)) {
> +        target_eventlist = lock_user(VERIFY_WRITE, arg4,
> +                                     sizeof(*target_eventlist) * arg5, 0);
> +        if (target_eventlist == NULL) {
> +            return -TARGET_EFAULT;
> +        }
> +        for (i = 0; i < ret; i++) {
> +            __put_user(eventlist[i].ident, &target_eventlist[i].ident);
> +            __put_user(eventlist[i].filter, &target_eventlist[i].filter);
> +            __put_user(eventlist[i].flags, &target_eventlist[i].flags);
> +            __put_user(eventlist[i].fflags, &target_eventlist[i].fflags);
> +            __put_user(eventlist[i].data, &target_eventlist[i].data);
> +            /* __put_user(eventlist[i].udata, &target_eventlist[i].udata);*/
> +#if TARGET_ABI_BITS == 32
> +            tswap32s((uint32_t *)&eventlist[i].udata);
> +            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
> +#else
> +            tswap64s((uint64_t *)&eventlist[i].udata);
> +            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
> +#endif
> +        }
> +        unlock_user(target_eventlist, arg4,
> +                    sizeof(*target_eventlist) * ret);
> +    }
> +    return ret;
> +}
> +
> +/* kevent(2) */
> +static inline abi_long do_freebsd_kevent(abi_long arg1, abi_ulong arg2,
> +        abi_long arg3, abi_ulong arg4, abi_long arg5, abi_long arg6)
> +{
> +    abi_long ret;
> +    struct kevent *changelist = NULL, *eventlist = NULL;
> +    struct target_freebsd_kevent *target_changelist, *target_eventlist;
> +    struct timespec ts;
> +    int i;
> +
> +    if (arg3 != 0) {
> +        target_changelist = lock_user(VERIFY_READ, arg2,
> +                sizeof(struct target_freebsd_kevent) * arg3, 1);
> +        if (target_changelist == NULL) {
> +            return -TARGET_EFAULT;
> +        }
> +
> +        changelist = alloca(sizeof(struct kevent) * arg3);
> +        for (i = 0; i < arg3; i++) {
> +            __get_user(changelist[i].ident, &target_changelist[i].ident);
> +            __get_user(changelist[i].filter, &target_changelist[i].filter);
> +            __get_user(changelist[i].flags, &target_changelist[i].flags);
> +            __get_user(changelist[i].fflags, &target_changelist[i].fflags);
> +            __get_user(changelist[i].data, &target_changelist[i].data);
> +            /* __get_user(changelist[i].udata, &target_changelist[i].udata); */
> +#if TARGET_ABI_BITS == 32
> +            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
> +            tswap32s((uint32_t *)&changelist[i].udata);
> +#else
> +            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
> +            tswap64s((uint64_t *)&changelist[i].udata);
> +#endif
> +            __get_user(changelist[i].ext[0], &target_changelist[i].ext[0]);
> +            __get_user(changelist[i].ext[1], &target_changelist[i].ext[1]);
> +            __get_user(changelist[i].ext[2], &target_changelist[i].ext[2]);
> +            __get_user(changelist[i].ext[3], &target_changelist[i].ext[3]);
> +        }
> +        unlock_user(target_changelist, arg2, 0);
> +    }
> +
> +    if (arg5 != 0) {
> +        eventlist = alloca(sizeof(struct kevent) * arg5);
> +    }
> +    if (arg6 != 0) {
> +        if (t2h_freebsd_timespec(&ts, arg6)) {
> +            return -TARGET_EFAULT;
> +        }
> +    }
> +    ret = get_errno(safe_kevent(arg1, changelist, arg3, eventlist, arg5,
> +                                arg6 != 0 ? &ts : NULL));
> +
> +    if (arg5 == 0) {
> +        return ret;
> +    }
> +
> +    if (!is_error(ret)) {
> +        target_eventlist = lock_user(VERIFY_WRITE, arg4,
> +            sizeof(struct target_freebsd_kevent) * arg5, 0);
> +        if (target_eventlist == NULL) {
> +            return -TARGET_EFAULT;
> +        }
> +        for (i = 0; i < ret; i++) {
> +            __put_user(eventlist[i].ident, &target_eventlist[i].ident);
> +            __put_user(eventlist[i].filter, &target_eventlist[i].filter);
> +            __put_user(eventlist[i].flags, &target_eventlist[i].flags);
> +            __put_user(eventlist[i].fflags, &target_eventlist[i].fflags);
> +            __put_user(eventlist[i].data, &target_eventlist[i].data);
> +            /* __put_user(eventlist[i].udata, &target_eventlist[i].udata);*/
> +#if TARGET_ABI_BITS == 32
> +            tswap32s((uint32_t *)&eventlist[i].udata);
> +            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
> +#else
> +            tswap64s((uint64_t *)&eventlist[i].udata);
> +            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
> +#endif
> +            __put_user(eventlist[i].ext[0], &target_eventlist[i].ext[0]);
> +            __put_user(eventlist[i].ext[1], &target_eventlist[i].ext[1]);
> +            __put_user(eventlist[i].ext[2], &target_eventlist[i].ext[2]);
> +            __put_user(eventlist[i].ext[3], &target_eventlist[i].ext[3]);
> +        }
> +        unlock_user(target_eventlist, arg4,
> +                sizeof(struct target_freebsd_kevent) * ret);
> +    }
> +    return ret;
> +}
> +
> +/* sigtimedwait(2) */
>  
>  #endif /* FREEBSD_OS_TIME_H */
> 

If I understand correctly, the difference between the two is only in
get/put user for eventlist[i].ext[*].
It seems worth to factorize the two and add a parameter to switch
between the two.
diff mbox series

Patch

diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
index 12c5ba02e8..078355392d 100644
--- a/bsd-user/freebsd/os-time.h
+++ b/bsd-user/freebsd/os-time.h
@@ -629,5 +629,178 @@  static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long arg1,
 }
 
 /* kqueue(2) */
+static inline abi_long do_freebsd_kqueue(void)
+{
+
+    return get_errno(kqueue());
+}
+
+/* kevent(2) */
+/* XXX Maybe some day, consolidate these two... */
+static inline abi_long do_freebsd_freebsd11_kevent(abi_long arg1,
+    abi_ulong arg2, abi_long arg3, abi_ulong arg4, abi_long arg5, abi_long arg6)
+{
+    abi_long ret;
+    struct kevent *changelist = NULL, *eventlist = NULL;
+    struct target_freebsd11_kevent *target_changelist, *target_eventlist;
+    struct timespec ts;
+    int i;
+
+    if (arg3 != 0) {
+        target_changelist = lock_user(VERIFY_READ, arg2,
+                                      sizeof(*target_changelist) * arg3, 1);
+        if (target_changelist == NULL) {
+            return -TARGET_EFAULT;
+        }
+
+        changelist = alloca(sizeof(struct kevent) * arg3);
+        memset(changelist, '\0', sizeof(struct kevent) * arg3);
+        for (i = 0; i < arg3; i++) {
+            __get_user(changelist[i].ident, &target_changelist[i].ident);
+            __get_user(changelist[i].filter, &target_changelist[i].filter);
+            __get_user(changelist[i].flags, &target_changelist[i].flags);
+            __get_user(changelist[i].fflags, &target_changelist[i].fflags);
+            __get_user(changelist[i].data, &target_changelist[i].data);
+            /* __get_user(changelist[i].udata, &target_changelist[i].udata); */
+#if TARGET_ABI_BITS == 32
+            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
+            tswap32s((uint32_t *)&changelist[i].udata);
+#else
+            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
+            tswap64s((uint64_t *)&changelist[i].udata);
+#endif
+        }
+        unlock_user(target_changelist, arg2, sizeof(*target_changelist) * arg3);
+    }
+
+    if (arg5 != 0) {
+        eventlist = alloca(sizeof(struct kevent) * arg5);
+    }
+    if (arg6 != 0) {
+        if (t2h_freebsd_timespec(&ts, arg6)) {
+            return -TARGET_EFAULT;
+        }
+    }
+    ret = get_errno(safe_kevent(arg1, changelist, arg3, eventlist, arg5,
+                                arg6 != 0 ? &ts : NULL));
+
+    if (arg5 == 0) {
+        return ret;
+    }
+
+    if (!is_error(ret)) {
+        target_eventlist = lock_user(VERIFY_WRITE, arg4,
+                                     sizeof(*target_eventlist) * arg5, 0);
+        if (target_eventlist == NULL) {
+            return -TARGET_EFAULT;
+        }
+        for (i = 0; i < ret; i++) {
+            __put_user(eventlist[i].ident, &target_eventlist[i].ident);
+            __put_user(eventlist[i].filter, &target_eventlist[i].filter);
+            __put_user(eventlist[i].flags, &target_eventlist[i].flags);
+            __put_user(eventlist[i].fflags, &target_eventlist[i].fflags);
+            __put_user(eventlist[i].data, &target_eventlist[i].data);
+            /* __put_user(eventlist[i].udata, &target_eventlist[i].udata);*/
+#if TARGET_ABI_BITS == 32
+            tswap32s((uint32_t *)&eventlist[i].udata);
+            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
+#else
+            tswap64s((uint64_t *)&eventlist[i].udata);
+            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
+#endif
+        }
+        unlock_user(target_eventlist, arg4,
+                    sizeof(*target_eventlist) * ret);
+    }
+    return ret;
+}
+
+/* kevent(2) */
+static inline abi_long do_freebsd_kevent(abi_long arg1, abi_ulong arg2,
+        abi_long arg3, abi_ulong arg4, abi_long arg5, abi_long arg6)
+{
+    abi_long ret;
+    struct kevent *changelist = NULL, *eventlist = NULL;
+    struct target_freebsd_kevent *target_changelist, *target_eventlist;
+    struct timespec ts;
+    int i;
+
+    if (arg3 != 0) {
+        target_changelist = lock_user(VERIFY_READ, arg2,
+                sizeof(struct target_freebsd_kevent) * arg3, 1);
+        if (target_changelist == NULL) {
+            return -TARGET_EFAULT;
+        }
+
+        changelist = alloca(sizeof(struct kevent) * arg3);
+        for (i = 0; i < arg3; i++) {
+            __get_user(changelist[i].ident, &target_changelist[i].ident);
+            __get_user(changelist[i].filter, &target_changelist[i].filter);
+            __get_user(changelist[i].flags, &target_changelist[i].flags);
+            __get_user(changelist[i].fflags, &target_changelist[i].fflags);
+            __get_user(changelist[i].data, &target_changelist[i].data);
+            /* __get_user(changelist[i].udata, &target_changelist[i].udata); */
+#if TARGET_ABI_BITS == 32
+            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
+            tswap32s((uint32_t *)&changelist[i].udata);
+#else
+            changelist[i].udata = (void *)(uintptr_t)target_changelist[i].udata;
+            tswap64s((uint64_t *)&changelist[i].udata);
+#endif
+            __get_user(changelist[i].ext[0], &target_changelist[i].ext[0]);
+            __get_user(changelist[i].ext[1], &target_changelist[i].ext[1]);
+            __get_user(changelist[i].ext[2], &target_changelist[i].ext[2]);
+            __get_user(changelist[i].ext[3], &target_changelist[i].ext[3]);
+        }
+        unlock_user(target_changelist, arg2, 0);
+    }
+
+    if (arg5 != 0) {
+        eventlist = alloca(sizeof(struct kevent) * arg5);
+    }
+    if (arg6 != 0) {
+        if (t2h_freebsd_timespec(&ts, arg6)) {
+            return -TARGET_EFAULT;
+        }
+    }
+    ret = get_errno(safe_kevent(arg1, changelist, arg3, eventlist, arg5,
+                                arg6 != 0 ? &ts : NULL));
+
+    if (arg5 == 0) {
+        return ret;
+    }
+
+    if (!is_error(ret)) {
+        target_eventlist = lock_user(VERIFY_WRITE, arg4,
+            sizeof(struct target_freebsd_kevent) * arg5, 0);
+        if (target_eventlist == NULL) {
+            return -TARGET_EFAULT;
+        }
+        for (i = 0; i < ret; i++) {
+            __put_user(eventlist[i].ident, &target_eventlist[i].ident);
+            __put_user(eventlist[i].filter, &target_eventlist[i].filter);
+            __put_user(eventlist[i].flags, &target_eventlist[i].flags);
+            __put_user(eventlist[i].fflags, &target_eventlist[i].fflags);
+            __put_user(eventlist[i].data, &target_eventlist[i].data);
+            /* __put_user(eventlist[i].udata, &target_eventlist[i].udata);*/
+#if TARGET_ABI_BITS == 32
+            tswap32s((uint32_t *)&eventlist[i].udata);
+            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
+#else
+            tswap64s((uint64_t *)&eventlist[i].udata);
+            target_eventlist[i].udata = (uintptr_t)eventlist[i].udata;
+#endif
+            __put_user(eventlist[i].ext[0], &target_eventlist[i].ext[0]);
+            __put_user(eventlist[i].ext[1], &target_eventlist[i].ext[1]);
+            __put_user(eventlist[i].ext[2], &target_eventlist[i].ext[2]);
+            __put_user(eventlist[i].ext[3], &target_eventlist[i].ext[3]);
+        }
+        unlock_user(target_eventlist, arg4,
+                sizeof(struct target_freebsd_kevent) * ret);
+    }
+    return ret;
+}
+
+/* sigtimedwait(2) */
 
 #endif /* FREEBSD_OS_TIME_H */