* [PATCH v2 01/37] bsd-user: catchup to locking / mapping routines in bsd-misc.
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-19 15:00 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user Warner Losh
` (36 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh
These were added to bsd-misc.c in a prior series. The declarations
logically belong here, but aren't strictly needed with the current
structure of the code due to include contamination.
Signed-off-by: Warner Losh <imp@bsdimp.com>
---
bsd-user/qemu-bsd.h | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/bsd-user/qemu-bsd.h b/bsd-user/qemu-bsd.h
index 756425d18e..f7c8338213 100644
--- a/bsd-user/qemu-bsd.h
+++ b/bsd-user/qemu-bsd.h
@@ -32,6 +32,24 @@ int host_to_target_waitstatus(int status);
void h2g_rusage(const struct rusage *rusage,
struct target_freebsd_rusage *target_rusage);
+/* bsd-misc.c */
+abi_long host_to_target_uuid(abi_ulong target_addr, struct uuid *host_uuid);
+
+abi_long target_to_host_semarray(int semid, unsigned short **host_array,
+ abi_ulong target_addr);
+abi_long host_to_target_semarray(int semid, abi_ulong target_addr,
+ unsigned short **host_array);
+
+abi_long target_to_host_semid_ds(struct semid_ds *host_sd,
+ abi_ulong target_addr);
+abi_long host_to_target_semid_ds(abi_ulong target_addr,
+ struct semid_ds *host_sd);
+
+abi_long target_to_host_msqid_ds(struct msqid_ds *host_md,
+ abi_ulong target_addr);
+abi_long host_to_target_msqid_ds(abi_ulong target_addr,
+ struct msqid_ds *host_md);
+
/* bsd-mem.c */
void target_to_host_ipc_perm__locked(struct ipc_perm *host_ip,
struct target_ipc_perm *target_ip);
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
2026-05-18 21:27 ` [PATCH v2 01/37] bsd-user: catchup to locking / mapping routines in bsd-misc Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-19 14:59 ` Pierrick Bouvier
2026-05-19 15:05 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 03/37] bsd-user: Add bsd-signal.h with sigaction through sigreturn Warner Losh
` (35 subsequent siblings)
37 siblings, 2 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Replace 'void *cpu_env' parameters with 'CPUArchState *env' across all
bsd-user source files. This improves type safety and aligns with the
convention used in the rest of the codebase.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/aarch64/target.h | 2 +-
bsd-user/arm/target.h | 2 +-
bsd-user/bsd-file.h | 28 +++++++++++++--------------
bsd-user/bsd-mem.h | 4 ++--
bsd-user/bsd-proc.h | 6 +++---
bsd-user/freebsd/os-proc.c | 4 ++--
bsd-user/freebsd/os-proc.h | 26 ++++++++++++-------------
bsd-user/freebsd/os-sys.c | 4 ++--
bsd-user/freebsd/os-syscall.c | 44 +++++++++++++++++++++----------------------
bsd-user/i386/target.h | 2 +-
bsd-user/qemu.h | 6 +++---
bsd-user/riscv/target.h | 2 +-
bsd-user/x86_64/target.h | 2 +-
13 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/bsd-user/aarch64/target.h b/bsd-user/aarch64/target.h
index 702aeb7fc5..37b9817fe1 100644
--- a/bsd-user/aarch64/target.h
+++ b/bsd-user/aarch64/target.h
@@ -12,7 +12,7 @@
/*
* aaarch64 ABI does not 'lump' the registers for 64-bit args.
*/
-static inline bool regpairs_aligned(void *cpu_env)
+static inline bool regpairs_aligned(CPUArchState *env)
{
return false;
}
diff --git a/bsd-user/arm/target.h b/bsd-user/arm/target.h
index 7c423ec575..cf131f29fb 100644
--- a/bsd-user/arm/target.h
+++ b/bsd-user/arm/target.h
@@ -12,7 +12,7 @@
/*
* arm EABI 'lumps' the registers for 64-bit args.
*/
-static inline bool regpairs_aligned(void *cpu_env)
+static inline bool regpairs_aligned(CPUArchState *env)
{
return true;
}
diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h
index dca1518cb1..dec59bd80b 100644
--- a/bsd-user/bsd-file.h
+++ b/bsd-user/bsd-file.h
@@ -72,7 +72,7 @@ static abi_long do_bsd_read(abi_long arg1, abi_long arg2, abi_long arg3)
}
/* pread(2) */
-static abi_long do_bsd_pread(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_pread(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
{
abi_long ret;
@@ -82,7 +82,7 @@ static abi_long do_bsd_pread(void *cpu_env, abi_long arg1,
if (p == NULL) {
return -TARGET_EFAULT;
}
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg4 = arg5;
arg5 = arg6;
}
@@ -109,14 +109,14 @@ static abi_long do_bsd_readv(abi_long arg1, abi_long arg2, abi_long arg3)
}
/* preadv(2) */
-static abi_long do_bsd_preadv(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_preadv(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
{
abi_long ret;
struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 1);
if (vec != NULL) {
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg4 = arg5;
arg5 = arg6;
}
@@ -151,7 +151,7 @@ static abi_long do_bsd_write(abi_long arg1, abi_long arg2, abi_long arg3)
}
/* pwrite(2) */
-static abi_long do_bsd_pwrite(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_pwrite(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
{
abi_long ret;
@@ -161,7 +161,7 @@ static abi_long do_bsd_pwrite(void *cpu_env, abi_long arg1,
if (p == NULL) {
return -TARGET_EFAULT;
}
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg4 = arg5;
arg5 = arg6;
}
@@ -188,14 +188,14 @@ static abi_long do_bsd_writev(abi_long arg1, abi_long arg2, abi_long arg3)
}
/* pwritev(2) */
-static abi_long do_bsd_pwritev(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_pwritev(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
{
abi_long ret;
struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
if (vec != NULL) {
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg4 = arg5;
arg5 = arg6;
}
@@ -484,14 +484,14 @@ static abi_long do_bsd_dup2(abi_long arg1, abi_long arg2)
}
/* truncate(2) */
-static abi_long do_bsd_truncate(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_truncate(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4)
{
abi_long ret;
void *p;
LOCK_PATH(p, arg1);
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg2 = arg3;
arg3 = arg4;
}
@@ -502,10 +502,10 @@ static abi_long do_bsd_truncate(void *cpu_env, abi_long arg1,
}
/* ftruncate(2) */
-static abi_long do_bsd_ftruncate(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_ftruncate(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4)
{
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg2 = arg3;
arg3 = arg4;
}
@@ -735,7 +735,7 @@ static abi_long do_bsd_freebsd11_mknodat(abi_long arg1, abi_long arg2,
}
/* post-ino64 mknodat(2) */
-static abi_long do_bsd_mknodat(void *cpu_env, abi_long arg1,
+static abi_long do_bsd_mknodat(CPUArchState *env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5,
abi_long arg6)
{
@@ -744,7 +744,7 @@ static abi_long do_bsd_mknodat(void *cpu_env, abi_long arg1,
LOCK_PATH(p, arg2);
/* 32-bit arch's use two 32 registers for 64 bit return value */
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg5, arg6)));
} else {
ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg4, arg5)));
diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h
index 9d7b60d3c3..3086143d40 100644
--- a/bsd-user/bsd-mem.h
+++ b/bsd-user/bsd-mem.h
@@ -51,11 +51,11 @@ extern abi_ulong target_brk;
extern abi_ulong initial_target_brk;
/* mmap(2) */
-static inline abi_long do_bsd_mmap(void *cpu_env, abi_long arg1, abi_long arg2,
+static inline abi_long do_bsd_mmap(CPUArchState *env, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8)
{
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
arg6 = arg7;
arg7 = arg8;
}
diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
index b1d4446ff1..62052c70b9 100644
--- a/bsd-user/bsd-proc.h
+++ b/bsd-user/bsd-proc.h
@@ -6,8 +6,8 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
-#ifndef BSD_PROC_H_
-#define BSD_PROC_H_
+#ifndef BSD_PROC_H
+#define BSD_PROC_H
#include <sys/resource.h>
@@ -19,7 +19,7 @@ extern int _getlogin(char*, int);
int bsd_get_ncpu(void);
/* exit(2) */
-static inline abi_long do_bsd_exit(void *cpu_env, abi_long arg1)
+static inline abi_long do_bsd_exit(CPUArchState *env, abi_long arg1)
{
gdb_exit(arg1);
qemu_plugin_user_exit();
diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c
index 0309036178..0a0cea5bb2 100644
--- a/bsd-user/freebsd/os-proc.c
+++ b/bsd-user/freebsd/os-proc.c
@@ -238,7 +238,7 @@ h2t_procctl_reaper_pidinfo(struct procctl_reaper_pidinfo *host_pi,
}
abi_long
-do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2, abi_ulong arg3,
+do_freebsd_procctl(CPUArchState *env, int idtype, abi_ulong arg2, abi_ulong arg3,
abi_ulong arg4, abi_ulong arg5, abi_ulong arg6)
{
abi_long error = 0, target_rp_pids;
@@ -257,7 +257,7 @@ do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2, abi_ulong arg3,
#if TARGET_ABI_BITS == 32
/* See if we need to align the register pairs. */
- if (regpairs_aligned(cpu_env)) {
+ if (regpairs_aligned(env)) {
id = (id_t)target_arg64(arg3, arg4);
target_cmd = (int)arg5;
target_arg = arg6;
diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
index 72ccf23e17..fef6c03319 100644
--- a/bsd-user/freebsd/os-proc.h
+++ b/bsd-user/freebsd/os-proc.h
@@ -67,7 +67,7 @@ static inline abi_long do_freebsd_wait4(abi_long arg1, abi_ulong target_status,
}
/* wait6(2) */
-static inline abi_long do_freebsd_wait6(void *cpu_env, abi_long idtype,
+static inline abi_long do_freebsd_wait6(CPUArchState *env, abi_long idtype,
abi_long id1, abi_long id2,
abi_ulong target_status, abi_long options, abi_ulong target_wrusage,
abi_ulong target_infop, abi_ulong pad1)
@@ -78,7 +78,7 @@ static inline abi_long do_freebsd_wait6(void *cpu_env, abi_long idtype,
siginfo_t info;
void *p;
- if (regpairs_aligned(cpu_env) != 0) {
+ if (regpairs_aligned(env) != 0) {
/* printf("shifting args\n"); */
/* 64-bit id is aligned, so shift all the arguments over by one */
id1 = id2;
@@ -172,7 +172,7 @@ static inline abi_long do_freebsd___setugid(abi_long arg1)
}
/* fork(2) */
-static inline abi_long do_freebsd_fork(void *cpu_env)
+static inline abi_long do_freebsd_fork(CPUArchState *env)
{
abi_long ret;
abi_ulong child_flag;
@@ -182,7 +182,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
if (ret == 0) {
/* child */
child_flag = 1;
- target_cpu_clone_regs(cpu_env, 0);
+ target_cpu_clone_regs(env, 0);
} else {
/* parent */
child_flag = 0;
@@ -192,7 +192,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
* The fork system call sets a child flag in the second return
* value: 0 for parent process, 1 for child process.
*/
- set_second_rval(cpu_env, child_flag);
+ set_second_rval(env, child_flag);
fork_end(ret);
@@ -200,13 +200,13 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
}
/* vfork(2) */
-static inline abi_long do_freebsd_vfork(void *cpu_env)
+static inline abi_long do_freebsd_vfork(CPUArchState *env)
{
- return do_freebsd_fork(cpu_env);
+ return do_freebsd_fork(env);
}
/* rfork(2) */
-static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
+static inline abi_long do_freebsd_rfork(CPUArchState *env, abi_long flags)
{
abi_long ret;
abi_ulong child_flag;
@@ -227,7 +227,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
if (ret == 0) {
/* child */
child_flag = 1;
- target_cpu_clone_regs(cpu_env, 0);
+ target_cpu_clone_regs(env, 0);
} else {
/* parent */
child_flag = 0;
@@ -237,7 +237,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
* The fork system call sets a child flag in the second return
* value: 0 for parent process, 1 for child process.
*/
- set_second_rval(cpu_env, child_flag);
+ set_second_rval(env, child_flag);
fork_end(ret);
return ret;
@@ -245,7 +245,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
}
/* pdfork(2) */
-static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
+static inline abi_long do_freebsd_pdfork(CPUArchState *env, abi_ulong target_fdp,
abi_long flags)
{
abi_long ret;
@@ -257,7 +257,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
if (ret == 0) {
/* child */
child_flag = 1;
- target_cpu_clone_regs(cpu_env, 0);
+ target_cpu_clone_regs(env, 0);
} else {
/* parent */
child_flag = 0;
@@ -270,7 +270,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
* The fork system call sets a child flag in the second return
* value: 0 for parent process, 1 for child process.
*/
- set_second_rval(cpu_env, child_flag);
+ set_second_rval(env, child_flag);
fork_end(ret);
return ret;
diff --git a/bsd-user/freebsd/os-sys.c b/bsd-user/freebsd/os-sys.c
index df31706558..d6f7c92420 100644
--- a/bsd-user/freebsd/os-sys.c
+++ b/bsd-user/freebsd/os-sys.c
@@ -593,7 +593,7 @@ out:
}
/* sysarch() is architecture dependent. */
-abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2)
+abi_long do_freebsd_sysarch(CPUArchState *env, abi_long arg1, abi_long arg2)
{
- return do_freebsd_arch_sysarch(cpu_env, arg1, arg2);
+ return do_freebsd_arch_sysarch(env, arg1, arg2);
}
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 90ef1bd916..d402c64726 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -215,7 +215,7 @@ void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
/*
* All errnos that freebsd_syscall() returns must be -TARGET_<errcode>.
*/
-static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
+static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8)
@@ -227,19 +227,19 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
* process system calls
*/
case TARGET_FREEBSD_NR_fork: /* fork(2) */
- ret = do_freebsd_fork(cpu_env);
+ ret = do_freebsd_fork(env);
break;
case TARGET_FREEBSD_NR_vfork: /* vfork(2) */
- ret = do_freebsd_vfork(cpu_env);
+ ret = do_freebsd_vfork(env);
break;
case TARGET_FREEBSD_NR_rfork: /* rfork(2) */
- ret = do_freebsd_rfork(cpu_env, arg1);
+ ret = do_freebsd_rfork(env, arg1);
break;
case TARGET_FREEBSD_NR_pdfork: /* pdfork(2) */
- ret = do_freebsd_pdfork(cpu_env, arg1, arg2);
+ ret = do_freebsd_pdfork(env, arg1, arg2);
break;
case TARGET_FREEBSD_NR_execve: /* execve(2) */
@@ -255,12 +255,12 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_wait6: /* wait6(2) */
- ret = do_freebsd_wait6(cpu_env, arg1, arg2, arg3,
+ ret = do_freebsd_wait6(env, arg1, arg2, arg3,
arg4, arg5, arg6, arg7, arg8);
break;
case TARGET_FREEBSD_NR_exit: /* exit(2) */
- ret = do_bsd_exit(cpu_env, arg1);
+ ret = do_bsd_exit(env, arg1);
break;
case TARGET_FREEBSD_NR_getgroups: /* getgroups(2) */
@@ -424,7 +424,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_procctl: /* procctl(2) */
- ret = do_freebsd_procctl(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_freebsd_procctl(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
/*
@@ -435,7 +435,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_pread: /* pread(2) */
- ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_bsd_pread(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_readv: /* readv(2) */
@@ -443,7 +443,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_preadv: /* preadv(2) */
- ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_bsd_preadv(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_write: /* write(2) */
@@ -451,7 +451,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */
- ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_bsd_pwrite(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_writev: /* writev(2) */
@@ -459,7 +459,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */
- ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_bsd_pwritev(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_open: /* open(2) */
@@ -559,11 +559,11 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_truncate: /* truncate(2) */
- ret = do_bsd_truncate(cpu_env, arg1, arg2, arg3, arg4);
+ ret = do_bsd_truncate(env, arg1, arg2, arg3, arg4);
break;
case TARGET_FREEBSD_NR_ftruncate: /* ftruncate(2) */
- ret = do_bsd_ftruncate(cpu_env, arg1, arg2, arg3, arg4);
+ ret = do_bsd_ftruncate(env, arg1, arg2, arg3, arg4);
break;
case TARGET_FREEBSD_NR_acct: /* acct(2) */
@@ -595,7 +595,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_readlink: /* readlink(2) */
- ret = do_bsd_readlink(cpu_env, arg1, arg2, arg3);
+ ret = do_bsd_readlink(env, arg1, arg2, arg3);
break;
case TARGET_FREEBSD_NR_readlinkat: /* readlinkat(2) */
@@ -627,7 +627,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_mknodat: /* mknodat(2) */
- ret = do_bsd_mknodat(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_bsd_mknodat(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_chown: /* chown(2) */
@@ -807,7 +807,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
* Memory management system calls.
*/
case TARGET_FREEBSD_NR_mmap: /* mmap(2) */
- ret = do_bsd_mmap(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ret = do_bsd_mmap(env, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
arg8);
break;
@@ -953,15 +953,15 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
* sys{ctl, arch, call}
*/
case TARGET_FREEBSD_NR___sysctl: /* sysctl(3) */
- ret = do_freebsd_sysctl(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_freebsd_sysctl(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR___sysctlbyname: /* sysctlbyname(2) */
- ret = do_freebsd_sysctlbyname(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = do_freebsd_sysctlbyname(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_FREEBSD_NR_sysarch: /* sysarch(2) */
- ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
+ ret = do_freebsd_sysarch(env, arg1, arg2);
break;
default:
@@ -979,7 +979,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
* as a wrapper around freebsd_syscall() so that actually happens. Since
* that is a singleton, modern compilers will inline it anyway...
*/
-abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
+abi_long do_freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8)
@@ -990,7 +990,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
}
- ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6,
+ ret = freebsd_syscall(env, num, arg1, arg2, arg3, arg4, arg5, arg6,
arg7, arg8);
if (do_strace) {
print_freebsd_syscall_ret(num, ret);
diff --git a/bsd-user/i386/target.h b/bsd-user/i386/target.h
index ddd3b8ec08..9cd158fc11 100644
--- a/bsd-user/i386/target.h
+++ b/bsd-user/i386/target.h
@@ -12,7 +12,7 @@
/*
* i386 doesn't 'lump' the registers for 64-bit args.
*/
-static inline bool regpairs_aligned(void *cpu_env)
+static inline bool regpairs_aligned(CPUArchState *env)
{
return false;
}
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index b0b2c249fb..60d1adf560 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -176,7 +176,7 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src,
void target_set_brk(abi_ulong new_brk);
abi_long do_brk(abi_ulong new_brk);
void syscall_init(void);
-abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
+abi_long do_freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8);
@@ -238,7 +238,7 @@ int host_to_target_errno(int err);
/* os-proc.c */
abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
abi_ulong guest_envp, int do_fexec);
-abi_long do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2,
+abi_long do_freebsd_procctl(CPUArchState *env, int idtype, abi_ulong arg2,
abi_ulong arg3, abi_ulong arg4, abi_ulong arg5, abi_ulong arg6);
/* os-sys.c */
@@ -247,7 +247,7 @@ abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen,
abi_long do_freebsd_sysctlbyname(CPUArchState *env, abi_ulong namep,
int32_t namelen, abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp,
abi_ulong newlen);
-abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2);
+abi_long do_freebsd_sysarch(CPUArchState *env, abi_long arg1, abi_long arg2);
/* user access */
diff --git a/bsd-user/riscv/target.h b/bsd-user/riscv/target.h
index 036ddd185e..dc3e0be795 100644
--- a/bsd-user/riscv/target.h
+++ b/bsd-user/riscv/target.h
@@ -12,7 +12,7 @@
/*
* riscv64 ABI does not 'lump' the registers for 64-bit args.
*/
-static inline bool regpairs_aligned(void *cpu_env)
+static inline bool regpairs_aligned(CPUArchState *env)
{
return false;
}
diff --git a/bsd-user/x86_64/target.h b/bsd-user/x86_64/target.h
index 0cf0e2a14a..fccfc593bd 100644
--- a/bsd-user/x86_64/target.h
+++ b/bsd-user/x86_64/target.h
@@ -12,7 +12,7 @@
/*
* x86 doesn't 'lump' the registers for 64-bit args, all args are 64 bits.
*/
-static inline bool regpairs_aligned(void *cpu_env)
+static inline bool regpairs_aligned(CPUArchState *env)
{
return false;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user
2026-05-18 21:27 ` [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user Warner Losh
@ 2026-05-19 14:59 ` Pierrick Bouvier
2026-05-19 15:05 ` Pierrick Bouvier
1 sibling, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-19 14:59 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 4:27 PM, Warner Losh wrote:
> Replace 'void *cpu_env' parameters with 'CPUArchState *env' across all
> bsd-user source files. This improves type safety and aligns with the
> convention used in the rest of the codebase.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/aarch64/target.h | 2 +-
> bsd-user/arm/target.h | 2 +-
> bsd-user/bsd-file.h | 28 +++++++++++++--------------
> bsd-user/bsd-mem.h | 4 ++--
> bsd-user/bsd-proc.h | 6 +++---
> bsd-user/freebsd/os-proc.c | 4 ++--
> bsd-user/freebsd/os-proc.h | 26 ++++++++++++-------------
> bsd-user/freebsd/os-sys.c | 4 ++--
> bsd-user/freebsd/os-syscall.c | 44 +++++++++++++++++++++----------------------
> bsd-user/i386/target.h | 2 +-
> bsd-user/qemu.h | 6 +++---
> bsd-user/riscv/target.h | 2 +-
> bsd-user/x86_64/target.h | 2 +-
> 13 files changed, 66 insertions(+), 66 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user
2026-05-18 21:27 ` [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user Warner Losh
2026-05-19 14:59 ` Pierrick Bouvier
@ 2026-05-19 15:05 ` Pierrick Bouvier
2026-05-19 17:32 ` Warner Losh
1 sibling, 1 reply; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-19 15:05 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 4:27 PM, Warner Losh wrote:
> Replace 'void *cpu_env' parameters with 'CPUArchState *env' across all
> bsd-user source files. This improves type safety and aligns with the
> convention used in the rest of the codebase.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/aarch64/target.h | 2 +-
> bsd-user/arm/target.h | 2 +-
> bsd-user/bsd-file.h | 28 +++++++++++++--------------
> bsd-user/bsd-mem.h | 4 ++--
> bsd-user/bsd-proc.h | 6 +++---
> bsd-user/freebsd/os-proc.c | 4 ++--
> bsd-user/freebsd/os-proc.h | 26 ++++++++++++-------------
> bsd-user/freebsd/os-sys.c | 4 ++--
> bsd-user/freebsd/os-syscall.c | 44 +++++++++++++++++++++----------------------
> bsd-user/i386/target.h | 2 +-
> bsd-user/qemu.h | 6 +++---
> bsd-user/riscv/target.h | 2 +-
> bsd-user/x86_64/target.h | 2 +-
> 13 files changed, 66 insertions(+), 66 deletions(-)
>
...
> diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
> index b1d4446ff1..62052c70b9 100644
> --- a/bsd-user/bsd-proc.h
> +++ b/bsd-user/bsd-proc.h
> @@ -6,8 +6,8 @@
> * SPDX-License-Identifier: GPL-2.0-or-later
> */
>
> -#ifndef BSD_PROC_H_
> -#define BSD_PROC_H_
> +#ifndef BSD_PROC_H
> +#define BSD_PROC_H
>
Last patch of the series does the opposite change in comment associated
to endif. We should pick one and stick to it consistently.
Regards,
Pierrick
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user
2026-05-19 15:05 ` Pierrick Bouvier
@ 2026-05-19 17:32 ` Warner Losh
0 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-19 17:32 UTC (permalink / raw)
To: Pierrick Bouvier; +Cc: qemu-devel, Kyle Evans, Stacey Son
[-- Attachment #1: Type: text/plain, Size: 1938 bytes --]
On Tue, May 19, 2026 at 9:05 AM Pierrick Bouvier <
pierrick.bouvier@oss.qualcomm.com> wrote:
> On 5/18/2026 4:27 PM, Warner Losh wrote:
> > Replace 'void *cpu_env' parameters with 'CPUArchState *env' across all
> > bsd-user source files. This improves type safety and aligns with the
> > convention used in the rest of the codebase.
> >
> > Signed-off-by: Stacey Son <sson@FreeBSD.org>
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > Assisted-by: Claude Opus 4.6 (1M context)
> > ---
> > bsd-user/aarch64/target.h | 2 +-
> > bsd-user/arm/target.h | 2 +-
> > bsd-user/bsd-file.h | 28 +++++++++++++--------------
> > bsd-user/bsd-mem.h | 4 ++--
> > bsd-user/bsd-proc.h | 6 +++---
> > bsd-user/freebsd/os-proc.c | 4 ++--
> > bsd-user/freebsd/os-proc.h | 26 ++++++++++++-------------
> > bsd-user/freebsd/os-sys.c | 4 ++--
> > bsd-user/freebsd/os-syscall.c | 44
> +++++++++++++++++++++----------------------
> > bsd-user/i386/target.h | 2 +-
> > bsd-user/qemu.h | 6 +++---
> > bsd-user/riscv/target.h | 2 +-
> > bsd-user/x86_64/target.h | 2 +-
> > 13 files changed, 66 insertions(+), 66 deletions(-)
> >
>
> ...
>
> > diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
> > index b1d4446ff1..62052c70b9 100644
> > --- a/bsd-user/bsd-proc.h
> > +++ b/bsd-user/bsd-proc.h
> > @@ -6,8 +6,8 @@
> > * SPDX-License-Identifier: GPL-2.0-or-later
> > */
> >
> > -#ifndef BSD_PROC_H_
> > -#define BSD_PROC_H_
> > +#ifndef BSD_PROC_H
> > +#define BSD_PROC_H
> >
>
> Last patch of the series does the opposite change in comment associated
> to endif. We should pick one and stick to it consistently.
>
Ugg. OK. Good eye, I'll see if there's some good standard here. I hit a lot
of conflicts a while
back when the tree was normalized, so I'll make sure I just do that.
Warner
[-- Attachment #2: Type: text/html, Size: 2696 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 03/37] bsd-user: Add bsd-signal.h with sigaction through sigreturn
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
2026-05-18 21:27 ` [PATCH v2 01/37] bsd-user: catchup to locking / mapping routines in bsd-misc Warner Losh
2026-05-18 21:27 ` [PATCH v2 02/37] bsd-user: Rename cpu_env to env throughout bsd-user Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:39 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 04/37] bsd-user: Add signal shims sigwait through kill and os-signal.h Warner Losh
` (34 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans,
Jessica Clarke, Mikaël Urankar
Add the first set of signal-related system call shims: sigaction,
sigprocmask, sigpending, sigsuspend, and sigreturn. Also add the
do_sigprocmask and target_to_host_sigevent helper functions to signal.c
and their declarations to signal-common.h.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/bsd-signal.h | 151 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-syscall.c | 1 +
bsd-user/signal-common.h | 4 ++
bsd-user/signal.c | 67 +++++++++++++++++++
4 files changed, 223 insertions(+)
diff --git a/bsd-user/bsd-signal.h b/bsd-user/bsd-signal.h
new file mode 100644
index 0000000000..49e58d7436
--- /dev/null
+++ b/bsd-user/bsd-signal.h
@@ -0,0 +1,151 @@
+/*
+ * signal related system call shims
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef BSD_SIGNAL_H
+#define BSD_SIGNAL_H
+
+#include <signal.h>
+
+/* sigaction(2) */
+static inline abi_long do_bsd_sigaction(abi_long arg1, abi_long arg2,
+ abi_long arg3)
+{
+ abi_long ret;
+ struct target_sigaction *old_act, act, oact, *pact;
+
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) {
+ return -TARGET_EFAULT;
+ }
+ act._sa_handler = old_act->_sa_handler;
+ act.sa_flags = old_act->sa_flags;
+ memcpy(&act.sa_mask, &old_act->sa_mask, sizeof(target_sigset_t));
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ } else {
+ pact = NULL;
+ }
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+ if (!is_error(ret) && arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0)) {
+ return -TARGET_EFAULT;
+ }
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_flags = oact.sa_flags;
+ memcpy(&old_act->sa_mask, &oact.sa_mask, sizeof(target_sigset_t));
+ unlock_user_struct(old_act, arg3, 1);
+ }
+ return ret;
+}
+
+
+/* sigprocmask(2) */
+static inline abi_long do_bsd_sigprocmask(abi_long arg1, abi_ulong arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ sigset_t set, oldset, *set_ptr;
+ int how;
+
+ ret = 0;
+ if (arg2) {
+ p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg2, 0);
+ set_ptr = &set;
+ switch (arg1) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ return -TARGET_EINVAL;
+ }
+ } else {
+ how = 0;
+ set_ptr = NULL;
+ }
+ ret = do_sigprocmask(how, set_ptr, &oldset);
+ if (!is_error(ret) && arg3) {
+ p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ host_to_target_sigset(p, &oldset);
+ unlock_user(p, arg3, sizeof(target_sigset_t));
+ }
+ return ret;
+}
+
+/* sigpending(2) */
+static inline abi_long do_bsd_sigpending(abi_long arg1)
+{
+ abi_long ret;
+ void *p;
+ sigset_t set;
+
+ ret = get_errno(sigpending(&set));
+ if (!is_error(ret)) {
+ p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ host_to_target_sigset(p, &set);
+ unlock_user(p, arg1, sizeof(target_sigset_t));
+ }
+ return ret;
+}
+
+/* sigsuspend(2) */
+static inline abi_long do_bsd_sigsuspend(CPUArchState *env, abi_long arg1,
+ abi_long arg2)
+{
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts = cpu->opaque;
+ void *p;
+ abi_long ret;
+
+ p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_sigset(&ts->sigsuspend_mask, p);
+ unlock_user(p, arg1, 0);
+
+ ret = get_errno(sigsuspend(&ts->sigsuspend_mask));
+ /* XXX Trivially true until safe_syscall */
+ if (ret != -TARGET_ERESTART) {
+ ts->in_sigsuspend = true;
+ }
+
+ return ret;
+}
+
+/* sigreturn(2) */
+static inline abi_long do_bsd_sigreturn(CPUArchState *env, abi_long arg1)
+{
+ if (block_signals()) {
+ return -TARGET_ERESTART;
+ }
+ return do_sigreturn(env, arg1);
+}
+
+/* sigvec(2) - not defined */
+/* sigblock(2) - not defined */
+/* sigsetmask(2) - not defined */
+/* sigstack(2) - not defined */
+
+#endif /* BSD_SIGNAL_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index d402c64726..7fefdb569f 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -38,6 +38,7 @@
#include "bsd-mem.h"
#include "bsd-proc.h"
#include "bsd-misc.h"
+#include "bsd-signal.h"
/* BSD dependent syscall shims */
#include "os-stat.h"
diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h
index 4e634e04a3..9071757873 100644
--- a/bsd-user/signal-common.h
+++ b/bsd-user/signal-common.h
@@ -32,6 +32,7 @@ int block_signals(void); /* Returns non zero if signal pending */
long do_rt_sigreturn(CPUArchState *env);
int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact);
+int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
long do_sigreturn(CPUArchState *env, abi_ulong addr);
void force_sig_fault(int sig, int code, abi_ulong addr);
@@ -42,6 +43,9 @@ void process_pending_signals(CPUArchState *env);
void queue_signal(CPUArchState *env, int sig, int si_type,
target_siginfo_t *info);
void signal_init(void);
+abi_long target_to_host_sigevent(struct sigevent *host_sevp,
+ abi_ulong target_addr);
+int target_to_host_signal(int sig);
void target_to_host_sigset(sigset_t *d, const target_sigset_t *s);
/*
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index 3e5e41e1b1..3f2f823051 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -346,6 +346,33 @@ int block_signals(void)
return qatomic_xchg(&ts->signal_pending, 1);
}
+abi_long target_to_host_sigevent(struct sigevent *host_sevp,
+ abi_ulong target_addr)
+{
+ struct target_sigevent *target_sevp;
+
+ if (!lock_user_struct(VERIFY_READ, target_sevp, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+
+ /*
+ * This union is awkward on 64 bit systems because it has a 32 bit
+ * integer and a pointer in it; we follow the conversion approach
+ * used for handling sigval types in signal.c so the guest should get
+ * the correct value back even if we did a 64 bit byteswap and it's
+ * using the 32 bit integer.
+ */
+ host_sevp->sigev_value.sival_ptr =
+ (void *)(uintptr_t)target_sevp->sigev_value.sival_ptr;
+ host_sevp->sigev_signo =
+ target_to_host_signal(tswap32(target_sevp->sigev_signo));
+ host_sevp->sigev_notify = tswap32(target_sevp->sigev_notify);
+ host_sevp->_sigev_un._threadid = tswap32(target_sevp->_sigev_un._threadid);
+
+ unlock_user_struct(target_sevp, target_addr, 0);
+ return 0;
+}
+
/* Returns 1 if given signal should dump core if not handled. */
static int core_dump_signal(int sig)
{
@@ -725,6 +752,46 @@ int do_sigaction(int sig, const struct target_sigaction *act,
return ret;
}
+int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
+{
+ TaskState *ts = get_task_state(thread_cpu);
+
+ if (oldset) {
+ *oldset = ts->signal_mask;
+ }
+
+ if (set) {
+ int i;
+
+ if (block_signals()) {
+ return -TARGET_ERESTART;
+ }
+
+ switch (how) {
+ case SIG_BLOCK:
+ sigorset(&ts->signal_mask, &ts->signal_mask, set);
+ break;
+ case SIG_UNBLOCK:
+ for (i = 1; i <= NSIG; ++i) {
+ if (sigismember(set, i)) {
+ sigdelset(&ts->signal_mask, i);
+ }
+ }
+ break;
+ case SIG_SETMASK:
+ ts->signal_mask = *set;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Silently ignore attempts to change blocking status of KILL or STOP */
+ sigdelset(&ts->signal_mask, SIGKILL);
+ sigdelset(&ts->signal_mask, SIGSTOP);
+ }
+ return 0;
+}
+
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
CPUArchState *env, size_t frame_size)
{
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 03/37] bsd-user: Add bsd-signal.h with sigaction through sigreturn
2026-05-18 21:27 ` [PATCH v2 03/37] bsd-user: Add bsd-signal.h with sigaction through sigreturn Warner Losh
@ 2026-05-22 22:39 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:39 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Jessica Clarke, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the first set of signal-related system call shims: sigaction,
> sigprocmask, sigpending, sigsuspend, and sigreturn. Also add the
> do_sigprocmask and target_to_host_sigevent helper functions to signal.c
> and their declarations to signal-common.h.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
> Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
> Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/bsd-signal.h | 151 ++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-syscall.c | 1 +
> bsd-user/signal-common.h | 4 ++
> bsd-user/signal.c | 67 +++++++++++++++++++
> 4 files changed, 223 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 04/37] bsd-user: Add signal shims sigwait through kill and os-signal.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (2 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 03/37] bsd-user: Add bsd-signal.h with sigaction through sigreturn Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:42 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 05/37] bsd-user: Add signal system call dispatch Warner Losh
` (33 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Complete bsd-signal.h with sigwait, sigwaitinfo, sigqueue,
sigaltstack, kill, and killpg. Add FreeBSD-specific os-signal.h
with pdkill.
Signed-off-by: Stacey Son <sson@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/bsd-signal.h | 80 ++++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-signal.h | 20 +++++++++++
2 files changed, 100 insertions(+)
diff --git a/bsd-user/bsd-signal.h b/bsd-user/bsd-signal.h
index 49e58d7436..09c5b30913 100644
--- a/bsd-user/bsd-signal.h
+++ b/bsd-user/bsd-signal.h
@@ -148,4 +148,84 @@ static inline abi_long do_bsd_sigreturn(CPUArchState *env, abi_long arg1)
/* sigsetmask(2) - not defined */
/* sigstack(2) - not defined */
+/* sigwait(2) */
+static inline abi_long do_bsd_sigwait(abi_ulong arg1, abi_ulong arg2,
+ abi_long arg3)
+{
+ abi_long ret;
+ void *p;
+ sigset_t set;
+ int sig;
+
+ p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sigwait(&set, &sig));
+ if (!is_error(ret) && arg2) {
+ ret = put_user_s32(sig, arg2);
+ }
+ return ret;
+}
+
+/* sigwaitinfo(2) */
+static inline abi_long do_bsd_sigwaitinfo(abi_ulong arg1, abi_ulong arg2)
+{
+ abi_long ret;
+ void *p;
+ sigset_t set;
+ siginfo_t uinfo;
+
+ p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sigwaitinfo(&set, &uinfo));
+ if (!is_error(ret) && arg2) {
+ p = lock_user(VERIFY_WRITE, arg2, sizeof(target_siginfo_t), 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ host_to_target_siginfo(p, &uinfo);
+ unlock_user(p, arg2, sizeof(target_siginfo_t));
+ }
+ return ret;
+}
+
+/* sigqueue(2) */
+static inline abi_long do_bsd_sigqueue(abi_long arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ union sigval value;
+ target_sigval_t *tvalue = (target_sigval_t *)&arg3;
+ abi_ulong sival_ptr;
+
+ __get_user(sival_ptr, &tvalue->sival_ptr);
+ value.sival_ptr = (void *)(uintptr_t)sival_ptr;
+ return get_errno(sigqueue(arg1, target_to_host_signal(arg2), value));
+}
+
+/* sigaltstck(2) */
+static inline abi_long do_bsd_sigaltstack(CPUArchState *env, abi_ulong arg1,
+ abi_ulong arg2)
+{
+ return do_sigaltstack(arg1, arg2, get_sp_from_cpustate(env));
+}
+
+/* kill(2) */
+static inline abi_long do_bsd_kill(abi_long pid, abi_long sig)
+{
+ return get_errno(kill(pid, target_to_host_signal(sig)));
+}
+
+/* killpg(2) */
+static inline abi_long do_bsd_killpg(abi_long pg, abi_long sig)
+{
+ return get_errno(killpg(pg, target_to_host_signal(sig)));
+}
+
#endif /* BSD_SIGNAL_H */
diff --git a/bsd-user/freebsd/os-signal.h b/bsd-user/freebsd/os-signal.h
new file mode 100644
index 0000000000..fe102c33e4
--- /dev/null
+++ b/bsd-user/freebsd/os-signal.h
@@ -0,0 +1,20 @@
+/*
+ * FreeBSD signal system call shims
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef FREEBSD_OS_SIGNAL_H
+#define FREEBSD_OS_SIGNAL_H
+
+#include <sys/procdesc.h>
+
+/* pdkill(2) */
+static inline abi_long do_freebsd_pdkill(abi_long arg1, abi_long arg2)
+{
+
+ return get_errno(pdkill(arg1, target_to_host_signal(arg2)));
+}
+
+#endif /* FREEBSD_OS_SIGNAL_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 04/37] bsd-user: Add signal shims sigwait through kill and os-signal.h
2026-05-18 21:27 ` [PATCH v2 04/37] bsd-user: Add signal shims sigwait through kill and os-signal.h Warner Losh
@ 2026-05-22 22:42 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:42 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Complete bsd-signal.h with sigwait, sigwaitinfo, sigqueue,
> sigaltstack, kill, and killpg. Add FreeBSD-specific os-signal.h
> with pdkill.
>
> Signed-off-by: Stacey Son <sson@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/bsd-signal.h | 80 ++++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-signal.h | 20 +++++++++++
> 2 files changed, 100 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 05/37] bsd-user: Add signal system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (3 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 04/37] bsd-user: Add signal shims sigwait through kill and os-signal.h Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:45 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 06/37] bsd-user: Add poll, lseek, pipe, and swap system call shims Warner Losh
` (32 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Wire up the signal-related system calls in the FreeBSD syscall
dispatcher: sigaction, sigprocmask, sigpending, sigsuspend, sigreturn,
sigwait, sigwaitinfo, sigqueue, sigaltstack, kill, and pdkill.
Signed-off-by: Stacey Son <sson@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-syscall.c | 48 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 7fefdb569f..06148fe3ce 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -43,6 +43,7 @@
/* BSD dependent syscall shims */
#include "os-stat.h"
#include "os-proc.h"
+#include "os-signal.h"
#include "os-misc.h"
/* I/O */
@@ -950,6 +951,53 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_getdtablesize();
break;
+ /*
+ * signal system calls
+ */
+ case TARGET_FREEBSD_NR_sigaction: /* sigaction(2) */
+ ret = do_bsd_sigaction(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_sigprocmask: /* sigprocmask(2) */
+ ret = do_bsd_sigprocmask(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_sigpending: /* sigpending(2) */
+ ret = do_bsd_sigpending(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_sigsuspend: /* sigsuspend(2) */
+ ret = do_bsd_sigsuspend(env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_sigreturn: /* sigreturn(2) */
+ ret = do_bsd_sigreturn(env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_sigwait: /* sigwait(2) */
+ ret = do_bsd_sigwait(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_sigwaitinfo: /* sigwaitinfo(2) */
+ ret = do_bsd_sigwaitinfo(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_sigqueue: /* sigqueue(2) */
+ ret = do_bsd_sigqueue(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_sigaltstack: /* sigaltstack(2) */
+ ret = do_bsd_sigaltstack(env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_kill: /* kill(2) */
+ ret = do_bsd_kill(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_pdkill: /* pdkill(2) */
+ ret = do_freebsd_pdkill(arg1, arg2);
+ break;
+
/*
* sys{ctl, arch, call}
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 05/37] bsd-user: Add signal system call dispatch
2026-05-18 21:27 ` [PATCH v2 05/37] bsd-user: Add signal system call dispatch Warner Losh
@ 2026-05-22 22:45 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:45 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Wire up the signal-related system calls in the FreeBSD syscall
> dispatcher: sigaction, sigprocmask, sigpending, sigsuspend, sigreturn,
> sigwait, sigwaitinfo, sigqueue, sigaltstack, kill, and pdkill.
>
> Signed-off-by: Stacey Son <sson@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-syscall.c | 48 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 06/37] bsd-user: Add poll, lseek, pipe, and swap system call shims
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (4 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 05/37] bsd-user: Add signal system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:51 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more Warner Losh
` (31 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add do_bsd_poll, do_bsd_lseek, do_bsd_pipe, do_bsd_swapon,
do_freebsd13_swapoff, and do_bsd_swapoff to bsd-file.h. Also add
target_pollfd structure to syscall_defs.h and include poll.h in
os-syscall.c.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/bsd-file.h | 136 +++++++++++++
bsd-user/freebsd/os-syscall.c | 1 +
bsd-user/syscall_defs.h | 458 ++++++++++++++++++++++++++++++++++++------
3 files changed, 534 insertions(+), 61 deletions(-)
diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h
index dec59bd80b..c5636aff6f 100644
--- a/bsd-user/bsd-file.h
+++ b/bsd-user/bsd-file.h
@@ -55,6 +55,10 @@ ssize_t safe_pwrite(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t safe_writev(int fd, const struct iovec *iov, int iovcnt);
ssize_t safe_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
+int safe_ppoll(struct pollfd *fds, nfds_t nfds,
+ const struct timespec *restrict timeout,
+ const sigset_t *restrict newsigmask);
+
/* read(2) */
static abi_long do_bsd_read(abi_long arg1, abi_long arg2, abi_long arg3)
{
@@ -925,4 +929,136 @@ static abi_long do_bsd_undelete(abi_long arg1)
return ret;
}
+/* poll(2) */
+static abi_long do_bsd_poll(abi_long arg1, abi_long arg2, abi_long arg3)
+{
+ abi_long ret;
+ nfds_t i, nfds = arg2;
+ int timeout = arg3;
+ struct pollfd *pfd;
+ struct target_pollfd *target_pfd;
+ struct timespec ts, *pts = NULL;
+
+ 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);
+ }
+
+ if (timeout != INFTIM) {
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ pts = &ts;
+ }
+
+ ret = get_errno(safe_ppoll(pfd, nfds, pts, NULL));
+
+ 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;
+}
+
+/* lseek(2) */
+static abi_long do_bsd_lseek(CPUArchState *env, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ abi_long ret;
+#if TARGET_ABI_BITS == 32
+ int64_t res;
+
+ /* 32-bit arch's use two 32 registers for 64 bit return value */
+ if (regpairs_aligned(env) != 0) {
+ res = lseek(arg1, target_arg64(arg3, arg4), arg5);
+ } else {
+ res = lseek(arg1, target_arg64(arg2, arg3), arg4);
+ }
+ if (res == -1) {
+ ret = get_errno(res);
+ set_second_rval(env, 0xFFFFFFFF);
+ } else {
+#ifdef TARGET_BIG_ENDIAN
+ ret = ((res >> 32) & 0xFFFFFFFF);
+ set_second_rval(env, res & 0xFFFFFFFF);
+#else
+ ret = res & 0xFFFFFFFF;
+ set_second_rval(env, (res >> 32) & 0xFFFFFFFF);
+#endif
+ }
+#else
+ ret = get_errno(lseek(arg1, arg2, arg3));
+#endif
+ return ret;
+}
+
+/* pipe(2) */
+static abi_long do_bsd_pipe(CPUArchState *env, abi_ulong pipedes)
+{
+ abi_long ret;
+ int host_pipe[2];
+ int host_ret = pipe(host_pipe);
+
+ if (host_ret != -1) {
+ set_second_rval(env, host_pipe[1]);
+ ret = host_pipe[0];
+ } else {
+ ret = get_errno(host_ret);
+ }
+ return ret;
+}
+
+/* swapon(2) */
+static abi_long do_bsd_swapon(abi_long arg1)
+{
+ abi_long ret;
+ void *p;
+
+ LOCK_PATH(p, arg1);
+ ret = get_errno(swapon(path(p)));
+ UNLOCK_PATH(p, arg1);
+
+ return ret;
+}
+
+#ifdef TARGET_FREEBSD_NR_freebsd13_swapoff
+/* swapoff(2) */
+static abi_long do_freebsd13_swapoff(abi_long arg1)
+{
+ abi_long ret;
+ void *p;
+
+ LOCK_PATH(p, arg1);
+ ret = get_errno(swapoff(path(p), 0));
+ UNLOCK_PATH(p, arg1);
+
+ return ret;
+}
+#endif
+
+/* swapoff(2) */
+static abi_long do_bsd_swapoff(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ void *p;
+
+ LOCK_PATH(p, arg1);
+#ifdef TARGET_FREEBSD_NR_freebsd13_swapoff
+ ret = get_errno(swapoff(path(p), arg2));
+#else
+ ret = get_errno(swapoff(path(p)));
+#endif
+ UNLOCK_PATH(p, arg1);
+
+ return ret;
+}
+
#endif /* BSD_FILE_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 06148fe3ce..fc6273a780 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -26,6 +26,7 @@
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <utime.h>
+#include <poll.h>
#include "include/gdbstub/syscalls.h"
diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h
index 0612be8bbb..833c7509e3 100644
--- a/bsd-user/syscall_defs.h
+++ b/bsd-user/syscall_defs.h
@@ -42,26 +42,6 @@ struct target_ipc_perm {
#define TARGET_IPC_SET 1 /* set options */
#define TARGET_IPC_STAT 2 /* get options */
-/*
- * sys/shm.h
- */
-struct target_shmid_ds {
- struct target_ipc_perm shm_perm; /* peration permission structure */
- abi_ulong shm_segsz; /* size of segment in bytes */
- int32_t shm_lpid; /* process ID of last shared memory op */
- int32_t shm_cpid; /* process ID of creator */
- int32_t shm_nattch; /* number of current attaches */
- target_time_t shm_atime; /* time of last shmat() */
- target_time_t shm_dtime; /* time of last shmdt() */
- target_time_t shm_ctime; /* time of last change by shmctl() */
-};
-
-#define N_BSD_SHM_REGIONS 32
-struct bsd_shm_regions {
- abi_long start;
- abi_long size;
-};
-
/*
* sys/sem.h
*/
@@ -93,6 +73,26 @@ struct target_semid_ds {
target_time_t sem_ctime; /* times measured in secs */
};
+/*
+ * sys/shm.h
+ */
+struct target_shmid_ds {
+ struct target_ipc_perm shm_perm; /* peration permission structure */
+ abi_ulong shm_segsz; /* size of segment in bytes */
+ int32_t shm_lpid; /* process ID of last shared memory op */
+ int32_t shm_cpid; /* process ID of creator */
+ int32_t shm_nattch; /* number of current attaches */
+ target_time_t shm_atime; /* time of last shmat() */
+ target_time_t shm_dtime; /* time of last shmdt() */
+ target_time_t shm_ctime; /* time of last change by shmctl() */
+};
+
+#define N_BSD_SHM_REGIONS 32
+struct bsd_shm_regions {
+ abi_long start;
+ abi_long size;
+};
+
/*
* sys/msg.h
*/
@@ -118,23 +118,26 @@ struct target_msgbuf {
char mtext[1]; /* body of message */
};
+/*
+ * sched.h
+ */
+struct target_sched_param {
+ int32_t sched_priority;
+};
+
/*
* sys/mman.h
*/
#define TARGET_MADV_DONTNEED 4 /* dont need these pages */
-#define TARGET_FREEBSD_MAP_RESERVED0080 0x0080 /* previously misimplemented */
- /* MAP_INHERIT */
-#define TARGET_FREEBSD_MAP_RESERVED0100 0x0100 /* previously unimplemented */
- /* MAP_NOEXTEND */
-#define TARGET_FREEBSD_MAP_STACK 0x0400 /* region grows down, like a */
- /* stack */
-#define TARGET_FREEBSD_MAP_NOSYNC 0x0800 /* page to but do not sync */
- /* underlying file */
+#define MAP_TYPE 0xf
+/* XXX These are needed by mmap.c until it is updated to match blitz */
+#define TARGET_FREEBSD_MAP_RESERVED0080 0x0080
+#define TARGET_FREEBSD_MAP_RESERVED0100 0x0100
+#define TARGET_FREEBSD_MAP_STACK 0x0400
+#define TARGET_FREEBSD_MAP_NOSYNC 0x0800
#define TARGET_FREEBSD_MAP_FLAGMASK 0x1ff7
-
-/* XXX */
#define TARGET_BSD_MAP_FLAGMASK 0x3ff7
/*
@@ -142,8 +145,6 @@ struct target_msgbuf {
* sys/timex.h
*/
-typedef abi_long target_freebsd_suseconds_t;
-
/* compare to sys/timespec.h */
struct target_freebsd_timespec {
target_time_t tv_sec; /* seconds */
@@ -165,12 +166,75 @@ struct target_freebsd__umtx_time {
struct target_freebsd_timeval {
target_time_t tv_sec; /* seconds */
- target_freebsd_suseconds_t tv_usec;/* and microseconds */
+ target_suseconds_t tv_usec;/* and microseconds */
#if !defined(TARGET_I386) && TARGET_ABI_BITS == 32
abi_long _pad;
#endif
};
+/* compare to sys/timex.h */
+struct target_freebsd_ntptimeval {
+ struct target_freebsd_timespec time;
+ abi_long maxerror;
+ abi_long esterror;
+ abi_long tai;
+ int32_t time_state;
+};
+
+struct target_freebsd_itimerspec {
+ struct target_freebsd_timespec it_interval;
+ struct target_freebsd_timespec it_value;
+};
+
+struct target_freebsd_timex {
+ uint32_t modes;
+ abi_long offset;
+ abi_long freq;
+ abi_long maxerror;
+ abi_long esterror;
+ int32_t status;
+ abi_long constant;
+ abi_long precision;
+ abi_long tolerance;
+
+ abi_long ppsfreq;
+ abi_long jitter;
+ int32_t shift;
+ abi_long stabil;
+ abi_long jitcnt;
+ abi_long calcnt;
+ abi_long errcnt;
+ abi_long stbcnt;
+};
+
+/* Maxiumum of 32 active POSIX timers allowed at any one time. */
+extern int g_posix_timers[32];
+
+#define TIMER_MAGIC 0x0caf0000
+#define TIMER_MAGIC_MASK 0xffff0000
+
+/*
+ * sys/event.h
+ */
+struct target_freebsd11_kevent {
+ abi_ulong ident;
+ int16_t filter;
+ uint16_t flags;
+ uint32_t fflags;
+ abi_long data;
+ abi_ulong udata;
+};
+
+struct target_freebsd_kevent {
+ abi_ulong ident;
+ int16_t filter;
+ uint16_t flags;
+ uint32_t fflags;
+ int64_t data;
+ abi_ulong udata;
+ uint64_t ext[4];
+};
+
/*
* sys/resource.h
*/
@@ -219,6 +283,154 @@ struct target_freebsd__wrusage {
struct target_freebsd_rusage wru_children;
};
+/*
+ * sys/socket.h
+ */
+
+/*
+ * Types
+ */
+#define TARGET_SOCK_STREAM 1 /* stream socket */
+#define TARGET_SOCK_DGRAM 2 /* datagram socket */
+#define TARGET_SOCK_RAW 3 /* raw-protocol interface */
+#define TARGET_SOCK_RDM 4 /* reliably-delivered message */
+#define TARGET_SOCK_SEQPACKET 5 /* sequenced packet stream */
+
+
+/*
+ * Option flags per-socket.
+ */
+
+#define TARGET_SO_DEBUG 0x0001 /* turn on debugging info recording */
+#define TARGET_SO_ACCEPTCONN 0x0002 /* socket has had listen() */
+#define TARGET_SO_REUSEADDR 0x0004 /* allow local address reuse */
+#define TARGET_SO_KEEPALIVE 0x0008 /* keep connections alive */
+#define TARGET_SO_DONTROUTE 0x0010 /* just use interface addresses */
+#define TARGET_SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
+#define TARGET_SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
+#define TARGET_SO_LINGER 0x0080 /* linger on close if data present */
+#define TARGET_SO_OOBINLINE 0x0100 /* leave received OOB data in line */
+#define TARGET_SO_REUSEPORT 0x0200 /* allow local address & port reuse */
+#define TARGET_SO_TIMESTAMP 0x0400 /* timestamp received dgram traffic */
+#define TARGET_SO_NOSIGPIPE 0x0800 /* no SIGPIPE from EPIPE */
+#define TARGET_SO_ACCEPTFILTER 0x1000 /* there is an accept filter */
+#define TARGET_SO_BINTIME 0x2000 /* timestamp received dgram traffic */
+#define TARGET_SO_NO_OFFLOAD 0x4000 /* socket cannot be offloaded */
+#define TARGET_SO_NO_DDP 0x8000 /* disable direct data placement */
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define TARGET_SO_SNDBUF 0x1001 /* send buffer size */
+#define TARGET_SO_RCVBUF 0x1002 /* receive buffer size */
+#define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */
+#define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */
+#define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */
+#define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */
+#define TARGET_SO_ERROR 0x1007 /* get error status and clear */
+#define TARGET_SO_TYPE 0x1008 /* get socket type */
+#define TARGET_SO_LABEL 0x1009 /* socket's MAC label */
+#define TARGET_SO_PEERLABEL 0x1010 /* socket's peer's MAC label */
+#define TARGET_SO_LISTENQLIMIT 0x1011 /* socket's backlog limit */
+#define TARGET_SO_LISTENQLEN 0x1012 /* socket's complete queue length */
+#define TARGET_SO_LISTENINCQLEN 0x1013 /* socket's incomplete queue length */
+#define TARGET_SO_SETFIB 0x1014 /* use this FIB to route */
+#define TARGET_SO_USER_COOKIE 0x1015 /* user cookie (dummynet etc.) */
+#define TARGET_SO_PROTOCOL 0x1016 /* get socket protocol (Linux name) */
+
+/* alias for SO_PROTOCOL (SunOS name) */
+#define TARGET_SO_PROTOTYPE TARGET_SO_PROTOCOL
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define TARGET_SOL_SOCKET 0xffff /* options for socket level */
+
+#ifndef CMSG_ALIGN
+#define CMSG_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#endif
+
+/*
+ * sys/socket.h
+ */
+struct target_msghdr {
+ abi_long msg_name; /* Socket name */
+ int32_t msg_namelen; /* Length of name */
+ abi_long msg_iov; /* Data blocks */
+ int32_t msg_iovlen; /* Number of blocks */
+ abi_long msg_control; /* Per protocol magic (eg BSD fd passing) */
+ int32_t msg_controllen; /* Length of cmsg list */
+ int32_t msg_flags; /* flags on received message */
+};
+
+struct target_sockaddr {
+ uint8_t sa_len;
+ uint8_t sa_family;
+ uint8_t sa_data[14];
+} QEMU_PACKED;
+
+struct target_in_addr {
+ uint32_t s_addr; /* big endian */
+};
+
+struct target_cmsghdr {
+ uint32_t cmsg_len;
+ int32_t cmsg_level;
+ int32_t cmsg_type;
+};
+
+/*
+ * mips32 is the exception to the general rule of long-alignment; it
+ * unconditionally uses 64-bit alignment instead.
+ */
+#if defined(TARGET_MIPS) && TARGET_ABI_BITS == 32
+#define TARGET_ALIGNBYTES (sizeof(abi_llong) - 1)
+#else
+#define TARGET_ALIGNBYTES (sizeof(abi_long) - 1)
+#endif
+
+#define TARGET_CMSG_NXTHDR(mhdr, cmsg, cmsg_start) \
+ __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start)
+#define TARGET_CMSG_ALIGN(len) (((len) + TARGET_ALIGNBYTES) \
+ & (size_t) ~TARGET_ALIGNBYTES)
+#define TARGET_CMSG_DATA(cmsg) \
+ ((unsigned char *)(cmsg) + TARGET_CMSG_ALIGN(sizeof(struct target_cmsghdr)))
+#define TARGET_CMSG_SPACE(len) \
+ (TARGET_CMSG_ALIGN(sizeof(struct target_cmsghdr)) + TARGET_CMSG_ALIGN(len))
+#define TARGET_CMSG_LEN(len) \
+ (TARGET_CMSG_ALIGN(sizeof(struct target_cmsghdr)) + (len))
+
+static inline struct target_cmsghdr *
+__target_cmsg_nxthdr(struct target_msghdr *__mhdr,
+ struct target_cmsghdr *__cmsg,
+ struct target_cmsghdr *__cmsg_start)
+{
+ struct target_cmsghdr *__ptr;
+
+ __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg +
+ TARGET_CMSG_ALIGN(tswap32(__cmsg->cmsg_len)));
+ if ((unsigned long)((char *)(__ptr + 1) - (char *)__cmsg_start) >
+ tswap32(__mhdr->msg_controllen)) {
+ /* No more entries. */
+ return (struct target_cmsghdr *)0;
+ }
+ return __ptr;
+}
+
+/*
+ * netinet/in.h
+ */
+struct target_ip_mreq {
+ struct target_in_addr imr_multiaddr;
+ struct target_in_addr imr_interface;
+};
+
+struct target_ip_mreqn {
+ struct target_in_addr imr_multiaddr;
+ struct target_in_addr imr_address;
+ int32_t imr_ifindex;
+};
+
/*
* sys/stat.h
*/
@@ -434,6 +646,152 @@ struct target_freebsd_flock {
/* user: vfork(2) semantics, clear signals */
#define TARGET_RFSPAWN (1U << 31)
+/* sys/specialfd.h */
+enum target_specialfd_type {
+ TARGET_SPECIALFD_EVENT = 1,
+};
+
+struct target_specialfd_eventfd {
+ unsigned int initval;
+ int flags;
+};
+
+/*
+ * FreeBSD thread and user mutex support.
+ */
+
+/* sys/thr.h */
+#define TARGET_THR_SUSPENDED 0x0001
+#define TARGET_THR_SYSTEM_SCOPE 0x0002
+
+struct target_freebsd_thr_param {
+ abi_ulong start_func; /* thread entry function. */
+ abi_ulong arg; /* argument for entry function. */
+ abi_ulong stack_base; /* stack base address. */
+ abi_ulong stack_size; /* stack size. */
+ abi_ulong tls_base; /* tls base address. */
+ abi_ulong tls_size; /* tls size. */
+ abi_ulong child_tid; /* address to store new TID. */
+ abi_ulong parent_tid; /* parent access the new TID here. */
+ int32_t flags; /* thread flags. */
+ abi_ulong rtp; /* Real-time scheduling priority. */
+ abi_ulong spare[3]; /* spares. */
+};
+
+/* sys/rtprio.h */
+struct target_freebsd_rtprio {
+ uint16_t type;
+ uint16_t prio;
+};
+
+typedef struct {
+ CPUArchState *env;
+ long parent_tid;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ pthread_t thread;
+ sigset_t sigmask;
+ struct target_freebsd_thr_param param;
+} new_freebsd_thread_info_t;
+
+/* sys/utmx.h */
+/* op code for _umtx_op */
+#define TARGET_UMTX_OP_LOCK 0
+#define TARGET_UMTX_OP_UNLOCK 1
+#define TARGET_UMTX_OP_WAIT 2
+#define TARGET_UMTX_OP_WAKE 3
+#define TARGET_UMTX_OP_MUTEX_TRYLOCK 4
+#define TARGET_UMTX_OP_MUTEX_LOCK 5
+#define TARGET_UMTX_OP_MUTEX_UNLOCK 6
+#define TARGET_UMTX_OP_SET_CEILING 7
+#define TARGET_UMTX_OP_CV_WAIT 8
+#define TARGET_UMTX_OP_CV_SIGNAL 9
+#define TARGET_UMTX_OP_CV_BROADCAST 10
+#define TARGET_UMTX_OP_WAIT_UINT 11
+#define TARGET_UMTX_OP_RW_RDLOCK 12
+#define TARGET_UMTX_OP_RW_WRLOCK 13
+#define TARGET_UMTX_OP_RW_UNLOCK 14
+#define TARGET_UMTX_OP_WAIT_UINT_PRIVATE 15
+#define TARGET_UMTX_OP_WAKE_PRIVATE 16
+#define TARGET_UMTX_OP_MUTEX_WAIT 17
+#define TARGET_UMTX_OP_MUTEX_WAKE 18
+#define TARGET_UMTX_OP_SEM_WAIT 19
+#define TARGET_UMTX_OP_SEM_WAKE 20
+#define TARGET_UMTX_OP_NWAKE_PRIVATE 21
+#define TARGET_UMTX_OP_MUTEX_WAKE2 22
+#define TARGET_UMTX_OP_SEM2_WAIT 23
+#define TARGET_UMTX_OP_SEM2_WAKE 24
+#define TARGET_UMTX_OP_SHM 25
+#define TARGET_UMTX_OP_ROBUST_LISTS 26
+
+/* flags for UMTX_OP_CV_WAIT */
+#define TARGET_CVWAIT_CHECK_UNPARKING 0x01
+#define TARGET_CVWAIT_ABSTIME 0x02
+#define TARGET_CVWAIT_CLOCKID 0x04
+
+#define TARGET_UMTX_UNOWNED 0x0
+#define TARGET_UMUTEX_UNOWNED 0x0
+#define TARGET_UMTX_CONTESTED (abi_ulong)(-1)
+#define TARGET_UMUTEX_CONTESTED 0x80000000U
+
+/* flags for umutex */
+#define TARGET_UMUTEX_ERROR_CHECK 0x0002 /* Error-checking mutex */
+#define TARGET_UMUTEX_PRIO_INHERIT 0x0004 /* Priority inherited mutex */
+#define TARGET_UMUTEX_PRIO_PROTECT 0x0008 /* Priority protect mutex */
+
+#define TARGET_UMUTEX_TRY 1
+#define TARGET_UMUTEX_WAIT 2
+
+/* urwlock flags */
+#define TARGET_URWLOCK_PREFER_READER 0x0002
+#define TARGET_URWLOCK_WRITE_OWNER 0x80000000U
+#define TARGET_URWLOCK_WRITE_WAITERS 0x40000000U
+#define TARGET_URWLOCK_READ_WAITERS 0x20000000U
+#define TARGET_URWLOCK_MAX_READERS 0x1fffffffU
+#define TARGET_URWLOCK_READER_COUNT(c) ((c) & TARGET_URWLOCK_MAX_READERS)
+
+/*
+ * sys/acl.h
+ */
+#define TARGET_FREEBSD_ACL_MAX_ENTRIES 254
+
+/* vaild acl_type_t arguments */
+#define TARGET_FREEBSD_ACL_TYPE_ACCESS_OLD 0x00000000
+#define TARGET_FREEBSD_ACL_TYPE_DEFAULT_OLD 0x00000001
+#define TARGET_FREEBSD_ACL_TYPE_ACCESS 0x00000002
+#define TARGET_FREEBSD_ACL_TYPE_DEFAULT 0x00000003
+#define TARGET_FREEBSD_ACL_TYPE_NFS4 0x00000004
+
+struct target_freebsd_acl_entry {
+ uint32_t ae_tag;
+ uint32_t ae_id;
+ uint32_t ae_perm;
+ uint16_t ae_entry_type;
+ uint16_t ae_flags;
+};
+
+struct target_freebsd_acl {
+ uint32_t acl_maxcnt;
+ uint32_t acl_cnt;
+ int32_t acl_spare[4];
+ struct target_freebsd_acl_entry acl_entry[TARGET_FREEBSD_ACL_MAX_ENTRIES];
+};
+
+/*
+ * sys/uuid.h
+ */
+
+#define TARGET_UUID_NODE_LEN 6
+
+struct target_uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[TARGET_UUID_NODE_LEN];
+};
+
/*
* from sys/procctl.h
*/
@@ -475,21 +833,6 @@ struct target_procctl_reaper_kill {
uint32_t rk_pad0[15];
};
-/*
- * sys/uuid.h
- */
-#define TARGET_UUID_NODE_LEN 6
-
-struct target_uuid {
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint8_t clock_seq_hi_and_reserved;
- uint8_t clock_seq_low;
- uint8_t node[TARGET_UUID_NODE_LEN];
-};
-
-
#define safe_syscall0(type, name) \
type safe_##name(void) \
{ \
@@ -537,22 +880,15 @@ type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4, arg5, arg6); \
}
-/*
- * sys/socket.h
- */
-struct target_sockaddr {
- uint8_t sa_len;
- uint8_t sa_family;
- uint8_t sa_data[14];
-} QEMU_PACKED;
-
-struct target_in_addr {
- uint32_t s_addr; /* big endian */
-};
-
#define safe_ioctl(...) safe_syscall(SYS_ioctl, __VA_ARGS__)
#define safe_fcntl(...) safe_syscall(SYS_fcntl, __VA_ARGS__)
+struct target_pollfd {
+ int32_t fd; /* file descriptor */
+ int16_t events; /* requested events */
+ int16_t revents; /* returned events */
+};
+
/* So far all target and host bitmasks are the same */
#undef target_to_host_bitmask
#define target_to_host_bitmask(x, tbl) (x)
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 06/37] bsd-user: Add poll, lseek, pipe, and swap system call shims
2026-05-18 21:27 ` [PATCH v2 06/37] bsd-user: Add poll, lseek, pipe, and swap system call shims Warner Losh
@ 2026-05-22 22:51 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:51 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add do_bsd_poll, do_bsd_lseek, do_bsd_pipe, do_bsd_swapon,
> do_freebsd13_swapoff, and do_bsd_swapoff to bsd-file.h. Also add
> target_pollfd structure to syscall_defs.h and include poll.h in
> os-syscall.c.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/bsd-file.h | 136 +++++++++++++
> bsd-user/freebsd/os-syscall.c | 1 +
> bsd-user/syscall_defs.h | 458 ++++++++++++++++++++++++++++++++++++------
> 3 files changed, 534 insertions(+), 61 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (5 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 06/37] bsd-user: Add poll, lseek, pipe, and swap system call shims Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:54 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 08/37] bsd-user: Add file operation system call dispatch Warner Losh
` (30 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add FreeBSD-specific file operation shims: pipe2, chflagsat,
close_range, copy_file_range, and __specialfd. Also add the
safe_copy_file_range and safe_ppoll syscall wrappers and the
target_specialfd_eventfd type definitions.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-file.h | 129 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-syscall.c | 7 +++
2 files changed, 136 insertions(+)
diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h
new file mode 100644
index 0000000000..c1da52789a
--- /dev/null
+++ b/bsd-user/freebsd/os-file.h
@@ -0,0 +1,129 @@
+/*
+ * FreeBSD file related system call shims and definitions
+ *
+ * Copyright (c) 2014 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef FREEBSD_OS_FILE_H
+#define FREEBSD_OS_FILE_H
+
+#include <sys/specialfd.h>
+
+/*
+ * Asynchronous I/O.
+ */
+
+/* pipe2(2) */
+static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int flags)
+{
+ int host_pipe[2];
+ int host_ret = pipe2(host_pipe, flags);
+ /* XXXss - flags should be translated from target to host. */
+
+ if (host_ret == -1) {
+ return get_errno(host_ret);
+ }
+
+ /*
+ * pipe2() returns it's second FD by copying it back to userspace and not in
+ * a second register like pipe(2): set_second_rval(env, host_pipe[1]);
+ *
+ * Copy the FD's back to userspace:
+ */
+ if (put_user_s32(host_pipe[0], pipedes) ||
+ put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) {
+ close(host_pipe[0]);
+ close(host_pipe[1]);
+ return -TARGET_EFAULT;
+ }
+ return 0;
+}
+
+/* chflagsat(2) */
+static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path,
+ abi_ulong flags, int atflags)
+{
+ abi_long ret;
+ void *p;
+
+ LOCK_PATH(p, path);
+ ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)? */
+ UNLOCK_PATH(p, path);
+
+ return ret;
+}
+
+/* close_range(2) */
+static inline abi_long do_freebsd_close_range(unsigned int lowfd,
+ unsigned int highfd, int flags)
+{
+
+ return get_errno(close_range(lowfd, highfd, flags));
+}
+
+ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int);
+
+/* copy_file_range(2) */
+static inline abi_long do_freebsd_copy_file_range(int infd,
+ abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len,
+ unsigned int flags)
+{
+ off_t inoff, outoff, *inp, *outp;
+ abi_long ret;
+
+ inp = outp = NULL;
+ if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) {
+ return -TARGET_EFAULT;
+ } else if (inofftp != 0) {
+ inoff = tswap64(*(off_t *)g2h_untagged(inofftp));
+ inp = &inoff;
+ }
+ if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp, sizeof(off_t))) {
+ return -TARGET_EFAULT;
+ } else if (outofftp != 0) {
+ outoff = tswap64(*(off_t *)g2h_untagged(outofftp));
+ outp = &outoff;
+ }
+
+ ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len,
+ flags));
+
+ if (!is_error(ret)) {
+ if (inofftp != 0) {
+ *(off_t *)g2h_untagged(inofftp) = tswap64(inoff);
+ }
+ if (outofftp != 0) {
+ *(off_t *)g2h_untagged(outofftp) = tswap64(outoff);
+ }
+ }
+ return ret;
+}
+
+static inline abi_long do_freebsd___specialfd(int type, abi_ulong req,
+ size_t len)
+{
+ abi_long ret;
+
+ ret = -TARGET_EINVAL;
+ switch (type) {
+ case TARGET_SPECIALFD_EVENT: {
+ struct specialfd_eventfd evfd;
+ struct target_specialfd_eventfd *target_eventfd;
+
+ if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) {
+ return -TARGET_EFAULT;
+ }
+
+ evfd.initval = tswap32(target_eventfd->initval);
+ evfd.flags = tswap32(target_eventfd->flags);
+ ret = get_errno(syscall(SYS___specialfd, type, &evfd, sizeof(evfd)));
+ unlock_user_struct(target_eventfd, req, 0);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#endif /* FREEBSD_OS_FILE_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index fc6273a780..8a25be4ac7 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -45,6 +45,7 @@
#include "os-stat.h"
#include "os-proc.h"
#include "os-signal.h"
+#include "os-file.h"
#include "os-misc.h"
/* I/O */
@@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt);
safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt,
off_t, offset);
+safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds,
+ const struct timespec *, restrict_timeout, const sigset_t *,
+ restrict_newsigmask);
+safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd,
+ off_t *, outoffp, size_t, len, unsigned int, flags);
+
/* used in os-proc */
safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
struct rusage *, rusage);
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more
2026-05-18 21:27 ` [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more Warner Losh
@ 2026-05-22 22:54 ` Pierrick Bouvier
2026-05-25 21:32 ` Warner Losh
0 siblings, 1 reply; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:54 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add FreeBSD-specific file operation shims: pipe2, chflagsat,
> close_range, copy_file_range, and __specialfd. Also add the
> safe_copy_file_range and safe_ppoll syscall wrappers and the
> target_specialfd_eventfd type definitions.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/freebsd/os-file.h | 129 ++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-syscall.c | 7 +++
> 2 files changed, 136 insertions(+)
>
> diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h
> new file mode 100644
> index 0000000000..c1da52789a
> --- /dev/null
> +++ b/bsd-user/freebsd/os-file.h
> @@ -0,0 +1,129 @@
> +/*
> + * FreeBSD file related system call shims and definitions
> + *
> + * Copyright (c) 2014 Stacey D. Son
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#ifndef FREEBSD_OS_FILE_H
> +#define FREEBSD_OS_FILE_H
> +
> +#include <sys/specialfd.h>
> +
> +/*
> + * Asynchronous I/O.
> + */
> +
> +/* pipe2(2) */
> +static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int flags)
> +{
> + int host_pipe[2];
> + int host_ret = pipe2(host_pipe, flags);
> + /* XXXss - flags should be translated from target to host. */
> +
> + if (host_ret == -1) {
> + return get_errno(host_ret);
> + }
> +
> + /*
> + * pipe2() returns it's second FD by copying it back to userspace and not in
> + * a second register like pipe(2): set_second_rval(env, host_pipe[1]);
> + *
> + * Copy the FD's back to userspace:
> + */
> + if (put_user_s32(host_pipe[0], pipedes) ||
> + put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) {
> + close(host_pipe[0]);
> + close(host_pipe[1]);
> + return -TARGET_EFAULT;
> + }
> + return 0;
> +}
> +
> +/* chflagsat(2) */
> +static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path,
> + abi_ulong flags, int atflags)
> +{
> + abi_long ret;
> + void *p;
> +
> + LOCK_PATH(p, path);
> + ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)? */
> + UNLOCK_PATH(p, path);
> +
> + return ret;
> +}
> +
> +/* close_range(2) */
> +static inline abi_long do_freebsd_close_range(unsigned int lowfd,
> + unsigned int highfd, int flags)
> +{
> +
> + return get_errno(close_range(lowfd, highfd, flags));
> +}
> +
> +ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int);
> +
> +/* copy_file_range(2) */
> +static inline abi_long do_freebsd_copy_file_range(int infd,
> + abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len,
> + unsigned int flags)
> +{
> + off_t inoff, outoff, *inp, *outp;
> + abi_long ret;
> +
> + inp = outp = NULL;
> + if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) {
> + return -TARGET_EFAULT;
> + } else if (inofftp != 0) {
> + inoff = tswap64(*(off_t *)g2h_untagged(inofftp));
> + inp = &inoff;
> + }
> + if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp, sizeof(off_t))) {
> + return -TARGET_EFAULT;
> + } else if (outofftp != 0) {
> + outoff = tswap64(*(off_t *)g2h_untagged(outofftp));
> + outp = &outoff;
> + }
> +
Very small nit, but this pattern is a bit weird to read:
Would be better with:
if (inofftp != 0) {
if (!access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) {
return -TARGET_EFAULT;
}
inoff = tswap64(*(off_t *)g2h_untagged(inofftp));
inp = &inoff;
}
> + ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len,
> + flags));
> +
> + if (!is_error(ret)) {
> + if (inofftp != 0) {
> + *(off_t *)g2h_untagged(inofftp) = tswap64(inoff);
> + }
> + if (outofftp != 0) {
> + *(off_t *)g2h_untagged(outofftp) = tswap64(outoff);
> + }
> + }
> + return ret;
> +}
> +
> +static inline abi_long do_freebsd___specialfd(int type, abi_ulong req,
> + size_t len)
> +{
> + abi_long ret;
> +
> + ret = -TARGET_EINVAL;
> + switch (type) {
> + case TARGET_SPECIALFD_EVENT: {
> + struct specialfd_eventfd evfd;
> + struct target_specialfd_eventfd *target_eventfd;
> +
> + if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) {
> + return -TARGET_EFAULT;
> + }
> +
> + evfd.initval = tswap32(target_eventfd->initval);
> + evfd.flags = tswap32(target_eventfd->flags);
> + ret = get_errno(syscall(SYS___specialfd, type, &evfd, sizeof(evfd)));
> + unlock_user_struct(target_eventfd, req, 0);
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +#endif /* FREEBSD_OS_FILE_H */
> diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
> index fc6273a780..8a25be4ac7 100644
> --- a/bsd-user/freebsd/os-syscall.c
> +++ b/bsd-user/freebsd/os-syscall.c
> @@ -45,6 +45,7 @@
> #include "os-stat.h"
> #include "os-proc.h"
> #include "os-signal.h"
> +#include "os-file.h"
> #include "os-misc.h"
>
> /* I/O */
> @@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt);
> safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt,
> off_t, offset);
>
> +safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds,
> + const struct timespec *, restrict_timeout, const sigset_t *,
> + restrict_newsigmask);
> +safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd,
> + off_t *, outoffp, size_t, len, unsigned int, flags);
> +
> /* used in os-proc */
> safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
> struct rusage *, rusage);
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more
2026-05-22 22:54 ` Pierrick Bouvier
@ 2026-05-25 21:32 ` Warner Losh
0 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-25 21:32 UTC (permalink / raw)
To: Pierrick Bouvier; +Cc: qemu-devel, Kyle Evans, Stacey Son
[-- Attachment #1: Type: text/plain, Size: 6701 bytes --]
On Fri, May 22, 2026 at 4:54 PM Pierrick Bouvier <
pierrick.bouvier@oss.qualcomm.com> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote:
> > Add FreeBSD-specific file operation shims: pipe2, chflagsat,
> > close_range, copy_file_range, and __specialfd. Also add the
> > safe_copy_file_range and safe_ppoll syscall wrappers and the
> > target_specialfd_eventfd type definitions.
> >
> > Signed-off-by: Stacey Son <sson@FreeBSD.org>
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > Assisted-by: Claude Opus 4.6 (1M context)
> > ---
> > bsd-user/freebsd/os-file.h | 129
> ++++++++++++++++++++++++++++++++++++++++++
> > bsd-user/freebsd/os-syscall.c | 7 +++
> > 2 files changed, 136 insertions(+)
> >
> > diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h
> > new file mode 100644
> > index 0000000000..c1da52789a
> > --- /dev/null
> > +++ b/bsd-user/freebsd/os-file.h
> > @@ -0,0 +1,129 @@
> > +/*
> > + * FreeBSD file related system call shims and definitions
> > + *
> > + * Copyright (c) 2014 Stacey D. Son
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +#ifndef FREEBSD_OS_FILE_H
> > +#define FREEBSD_OS_FILE_H
> > +
> > +#include <sys/specialfd.h>
> > +
> > +/*
> > + * Asynchronous I/O.
> > + */
> > +
> > +/* pipe2(2) */
> > +static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int
> flags)
> > +{
> > + int host_pipe[2];
> > + int host_ret = pipe2(host_pipe, flags);
> > + /* XXXss - flags should be translated from target to host. */
> > +
> > + if (host_ret == -1) {
> > + return get_errno(host_ret);
> > + }
> > +
> > + /*
> > + * pipe2() returns it's second FD by copying it back to userspace
> and not in
> > + * a second register like pipe(2): set_second_rval(env,
> host_pipe[1]);
> > + *
> > + * Copy the FD's back to userspace:
> > + */
> > + if (put_user_s32(host_pipe[0], pipedes) ||
> > + put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) {
> > + close(host_pipe[0]);
> > + close(host_pipe[1]);
> > + return -TARGET_EFAULT;
> > + }
> > + return 0;
> > +}
> > +
> > +/* chflagsat(2) */
> > +static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path,
> > + abi_ulong flags, int atflags)
> > +{
> > + abi_long ret;
> > + void *p;
> > +
> > + LOCK_PATH(p, path);
> > + ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)?
> */
> > + UNLOCK_PATH(p, path);
> > +
> > + return ret;
> > +}
> > +
> > +/* close_range(2) */
> > +static inline abi_long do_freebsd_close_range(unsigned int lowfd,
> > + unsigned int highfd, int flags)
> > +{
> > +
> > + return get_errno(close_range(lowfd, highfd, flags));
> > +}
> > +
> > +ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t,
> unsigned int);
> > +
> > +/* copy_file_range(2) */
> > +static inline abi_long do_freebsd_copy_file_range(int infd,
> > + abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len,
> > + unsigned int flags)
> > +{
> > + off_t inoff, outoff, *inp, *outp;
> > + abi_long ret;
> > +
> > + inp = outp = NULL;
> > + if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp,
> sizeof(off_t))) {
> > + return -TARGET_EFAULT;
> > + } else if (inofftp != 0) {
> > + inoff = tswap64(*(off_t *)g2h_untagged(inofftp));
> > + inp = &inoff;
> > + }
> > + if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp,
> sizeof(off_t))) {
> > + return -TARGET_EFAULT;
> > + } else if (outofftp != 0) {
> > + outoff = tswap64(*(off_t *)g2h_untagged(outofftp));
> > + outp = &outoff;
> > + }
> > +
>
> Very small nit, but this pattern is a bit weird to read:
>
> Would be better with:
>
> if (inofftp != 0) {
> if (!access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) {
> return -TARGET_EFAULT;
> }
> inoff = tswap64(*(off_t *)g2h_untagged(inofftp));
> inp = &inoff;
> }
>
Yes. That's a better pattern. I've redone this and outoffp and had claude
double check.
You can check in the next rev :)
> > + ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len,
> > + flags));
> > +
> > + if (!is_error(ret)) {
> > + if (inofftp != 0) {
> > + *(off_t *)g2h_untagged(inofftp) = tswap64(inoff);
> > + }
> > + if (outofftp != 0) {
> > + *(off_t *)g2h_untagged(outofftp) = tswap64(outoff);
> > + }
> > + }
> > + return ret;
> > +}
> > +
> > +static inline abi_long do_freebsd___specialfd(int type, abi_ulong req,
> > + size_t len)
> > +{
> > + abi_long ret;
> > +
> > + ret = -TARGET_EINVAL;
> > + switch (type) {
> > + case TARGET_SPECIALFD_EVENT: {
> > + struct specialfd_eventfd evfd;
> > + struct target_specialfd_eventfd *target_eventfd;
> > +
> > + if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) {
> > + return -TARGET_EFAULT;
> > + }
> > +
> > + evfd.initval = tswap32(target_eventfd->initval);
> > + evfd.flags = tswap32(target_eventfd->flags);
> > + ret = get_errno(syscall(SYS___specialfd, type, &evfd,
> sizeof(evfd)));
> > + unlock_user_struct(target_eventfd, req, 0);
> > + break;
> > + }
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +#endif /* FREEBSD_OS_FILE_H */
> > diff --git a/bsd-user/freebsd/os-syscall.c
> b/bsd-user/freebsd/os-syscall.c
> > index fc6273a780..8a25be4ac7 100644
> > --- a/bsd-user/freebsd/os-syscall.c
> > +++ b/bsd-user/freebsd/os-syscall.c
> > @@ -45,6 +45,7 @@
> > #include "os-stat.h"
> > #include "os-proc.h"
> > #include "os-signal.h"
> > +#include "os-file.h"
> > #include "os-misc.h"
> >
> > /* I/O */
> > @@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct
> iovec *, iov, int, iovcnt);
> > safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov,
> int, iovcnt,
> > off_t, offset);
> >
> > +safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds,
> > + const struct timespec *, restrict_timeout, const sigset_t *,
> > + restrict_newsigmask);
> > +safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp,
> int, outfd,
> > + off_t *, outoffp, size_t, len, unsigned int, flags);
> > +
> > /* used in os-proc */
> > safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
> > struct rusage *, rusage);
> >
>
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
>
Thanks!
Warner
[-- Attachment #2: Type: text/html, Size: 8625 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 08/37] bsd-user: Add file operation system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (6 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 07/37] bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 22:57 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 09/37] bsd-user: Add bsd-socket.h with bind through getsockname Warner Losh
` (29 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Wire up additional file-related system calls: poll, lseek, pipe,
pipe2, swapon, swapoff, chflagsat, close_range, __realpathat,
copy_file_range, and __specialfd. Also fix __realpathat to use
__sys___realpathat on FreeBSD >= 15.0.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-stat.h | 8 +------
bsd-user/freebsd/os-syscall.c | 51 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/bsd-user/freebsd/os-stat.h b/bsd-user/freebsd/os-stat.h
index ea95aa6344..4e5fc30de4 100644
--- a/bsd-user/freebsd/os-stat.h
+++ b/bsd-user/freebsd/os-stat.h
@@ -622,11 +622,6 @@ static inline abi_long do_freebsd_fcntl(abi_long arg1, abi_long arg2,
return ret;
}
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300080
-extern int __realpathat(int fd, const char *path, char *buf, size_t size,
- int flags);
-/* https://svnweb.freebsd.org/base?view=revision&revision=358172 */
-/* no man page */
static inline abi_long do_freebsd_realpathat(abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
@@ -640,12 +635,11 @@ static inline abi_long do_freebsd_realpathat(abi_long arg1, abi_long arg2,
return -TARGET_EFAULT;
}
- ret = get_errno(__realpathat(arg1, p, b, arg4, arg5));
+ ret = get_errno(syscall(SYS___realpathat, arg1, p, b, arg4, arg5));
UNLOCK_PATH(p, arg2);
unlock_user(b, arg3, ret);
return ret;
}
-#endif
#endif /* BSD_USER_FREEBSD_OS_STAT_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 8a25be4ac7..228daed4c4 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -700,6 +700,57 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_undelete(arg1);
break;
+ case TARGET_FREEBSD_NR_poll: /* poll(2) */
+ ret = do_bsd_poll(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_lseek: /* lseek(2) */
+ ret = do_bsd_lseek(env, arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd10_pipe: /* pipe(2) */
+ ret = do_bsd_pipe(env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_pipe2: /* pipe2(2) */
+ ret = do_bsd_pipe2(env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_swapon: /* swapon(2) */
+ ret = do_bsd_swapon(arg1);
+ break;
+
+#if TARGET_FREEBSD_NR_freebsd13_swapoff
+ case TARGET_FREEBSD_NR_freebsd13_swapoff: /* freebsd13_swapoff(2) */
+ ret = do_freebsd13_swapoff(arg1);
+ break;
+#endif
+
+ case TARGET_FREEBSD_NR_swapoff: /* swapoff(2) */
+ ret = do_bsd_swapoff(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_chflagsat: /* chflagsat(2) */
+ ret = do_bsd_chflagsat(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_close_range: /* close_range(2) */
+ ret = do_freebsd_close_range(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___realpathat:
+ /* __realpathat(2) (XXX no realpathat()) */
+ ret = do_freebsd_realpathat(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_copy_file_range:
+ ret = do_freebsd_copy_file_range(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR___specialfd:
+ ret = do_freebsd___specialfd(arg1, arg2, arg3);
+ break;
+
/*
* ioctl(2)
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 08/37] bsd-user: Add file operation system call dispatch
2026-05-18 21:27 ` [PATCH v2 08/37] bsd-user: Add file operation system call dispatch Warner Losh
@ 2026-05-22 22:57 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 22:57 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Wire up additional file-related system calls: poll, lseek, pipe,
> pipe2, swapon, swapoff, chflagsat, close_range, __realpathat,
> copy_file_range, and __specialfd. Also fix __realpathat to use
> __sys___realpathat on FreeBSD >= 15.0.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/freebsd/os-stat.h | 8 +------
> bsd-user/freebsd/os-syscall.c | 51 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 52 insertions(+), 7 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 09/37] bsd-user: Add bsd-socket.h with bind through getsockname
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (7 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 08/37] bsd-user: Add file operation system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:04 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 10/37] bsd-user: Add socket shims socketpair through shutdown Warner Losh
` (28 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Add the first set of socket system call shims: bind, connect,
accept, getpeername, and getsockname. Also add safe_syscall wrappers
for sendto, recvfrom, select, pselect, recvmsg, and sendmsg.
Signed-off-by: Stacey Son <sson@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/bsd-socket.h | 167 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-syscall.c | 15 ++++
2 files changed, 182 insertions(+)
diff --git a/bsd-user/bsd-socket.h b/bsd-user/bsd-socket.h
new file mode 100644
index 0000000000..c786d6717a
--- /dev/null
+++ b/bsd-user/bsd-socket.h
@@ -0,0 +1,167 @@
+/*
+ * socket related system call shims
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef BSD_SOCKET_H
+#define BSD_SOCKET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "qemu-bsd.h"
+
+ssize_t safe_recvfrom(int s, void *buf, size_t len, int flags,
+ struct sockaddr *restrict from, socklen_t *restrict fromlen);
+ssize_t safe_sendto(int s, const void *buf, size_t len, int flags,
+ const struct sockaddr *to, socklen_t tolen);
+int safe_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *timeout);
+int safe_pselect(int nfds, fd_set *restrict readfds,
+ fd_set *restrict writefds, fd_set *restrict exceptfds,
+ const struct timespec *restrict timeout,
+ const sigset_t *restrict newsigmask);
+
+/* bind(2) */
+static inline abi_long do_bsd_bind(int sockfd, abi_ulong target_addr,
+ socklen_t addrlen)
+{
+ abi_long ret;
+ void *addr;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ addr = alloca(addrlen + 1);
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ return get_errno(bind(sockfd, addr, addrlen));
+}
+
+/* connect(2) */
+static inline abi_long do_bsd_connect(int sockfd, abi_ulong target_addr,
+ socklen_t addrlen)
+{
+ abi_long ret;
+ void *addr;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ addr = alloca(addrlen + 1);
+
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ return get_errno(connect(sockfd, addr, addrlen));
+}
+
+/* accept(2) */
+static inline abi_long do_bsd_accept(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (target_addr == 0) {
+ return get_errno(accept(fd, NULL, NULL));
+ }
+ /* return EINVAL if addrlen pointer is invalid */
+ if (get_user_u32(addrlen, target_addrlen_addr)) {
+ return -TARGET_EINVAL;
+ }
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
+ return -TARGET_EINVAL;
+ }
+ addr = alloca(addrlen);
+
+ ret = get_errno(accept(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ if (is_error(host_to_target_sockaddr(target_addr, addr, addrlen))) {
+ close(ret);
+ ret = -TARGET_EFAULT;
+ } else if (put_user_u32(addrlen, target_addrlen_addr)) {
+ close(ret);
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* getpeername(2) */
+static inline abi_long do_bsd_getpeername(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (get_user_u32(addrlen, target_addrlen_addr)) {
+ return -TARGET_EFAULT;
+ }
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
+ return -TARGET_EFAULT;
+ }
+ addr = alloca(addrlen);
+ ret = get_errno(getpeername(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ ret = host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (is_error(ret)) {
+ ret = -TARGET_EFAULT;
+ } else if (put_user_u32(addrlen, target_addrlen_addr)) {
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* getsockname(2) */
+static inline abi_long do_bsd_getsockname(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (get_user_u32(addrlen, target_addrlen_addr)) {
+ return -TARGET_EFAULT;
+ }
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
+ return -TARGET_EFAULT;
+ }
+ addr = alloca(addrlen);
+
+ ret = get_errno(getsockname(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ ret = host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (is_error(ret)) {
+ ret = -TARGET_EFAULT;
+ } else if (put_user_u32(addrlen, target_addrlen_addr)) {
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+#endif /* BSD_SOCKET_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 228daed4c4..737de36514 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -40,6 +40,7 @@
#include "bsd-proc.h"
#include "bsd-misc.h"
#include "bsd-signal.h"
+#include "bsd-socket.h"
/* BSD dependent syscall shims */
#include "os-stat.h"
@@ -73,6 +74,20 @@ safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds,
safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd,
off_t *, outoffp, size_t, len, unsigned int, flags);
+/* used in bsd-socket */
+safe_syscall5(int, select, int, nfds, fd_set *, readfs, fd_set *, writefds,
+ fd_set *, exceptfds, struct timeval *, timeout);
+safe_syscall6(int, pselect, int, nfds, fd_set *restrict, readfs,
+ fd_set *restrict, writefds, fd_set *restrict, exceptfds,
+ const struct timespec *restrict, timeout, const sigset_t *restrict,
+ newsigmask);
+safe_syscall6(ssize_t, recvfrom, int, fd, void *, buf, size_t, len, int, flags,
+ struct sockaddr *restrict, from, socklen_t *restrict, fromlen);
+safe_syscall6(ssize_t, sendto, int, fd, const void *, buf, size_t, len, int,
+ flags, const struct sockaddr *, to, socklen_t, tolen);
+safe_syscall3(ssize_t, recvmsg, int, s, struct msghdr *, msg, int, flags);
+safe_syscall3(ssize_t, sendmsg, int, s, const struct msghdr *, msg, int, flags);
+
/* used in os-proc */
safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
struct rusage *, rusage);
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 09/37] bsd-user: Add bsd-socket.h with bind through getsockname
2026-05-18 21:27 ` [PATCH v2 09/37] bsd-user: Add bsd-socket.h with bind through getsockname Warner Losh
@ 2026-05-22 23:04 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:04 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the first set of socket system call shims: bind, connect,
> accept, getpeername, and getsockname. Also add safe_syscall wrappers
> for sendto, recvfrom, select, pselect, recvmsg, and sendmsg.
>
> Signed-off-by: Stacey Son <sson@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/bsd-socket.h | 167 ++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-syscall.c | 15 ++++
> 2 files changed, 182 insertions(+)
>
> diff --git a/bsd-user/bsd-socket.h b/bsd-user/bsd-socket.h
> new file mode 100644
> index 0000000000..c786d6717a
> --- /dev/null
> +++ b/bsd-user/bsd-socket.h
> @@ -0,0 +1,167 @@
> +/*
> + * socket related system call shims
> + *
> + * Copyright (c) 2013 Stacey D. Son
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#ifndef BSD_SOCKET_H
> +#define BSD_SOCKET_H
> +
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <netinet/in.h>
> +
> +#include "qemu-bsd.h"
> +
> +ssize_t safe_recvfrom(int s, void *buf, size_t len, int flags,
> + struct sockaddr *restrict from, socklen_t *restrict fromlen);
> +ssize_t safe_sendto(int s, const void *buf, size_t len, int flags,
> + const struct sockaddr *to, socklen_t tolen);
> +int safe_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds,
> + struct timeval *timeout);
> +int safe_pselect(int nfds, fd_set *restrict readfds,
> + fd_set *restrict writefds, fd_set *restrict exceptfds,
> + const struct timespec *restrict timeout,
> + const sigset_t *restrict newsigmask);
> +
> +/* bind(2) */
> +static inline abi_long do_bsd_bind(int sockfd, abi_ulong target_addr,
> + socklen_t addrlen)
> +{
> + abi_long ret;
> + void *addr;
> +
> + if ((int)addrlen < 0) {
> + return -TARGET_EINVAL;
> + }
> +
> + addr = alloca(addrlen + 1);
> + ret = target_to_host_sockaddr(addr, target_addr, addrlen);
> + if (is_error(ret)) {
> + return ret;
> + }
> +
> + return get_errno(bind(sockfd, addr, addrlen));
> +}
> +
> +/* connect(2) */
> +static inline abi_long do_bsd_connect(int sockfd, abi_ulong target_addr,
> + socklen_t addrlen)
> +{
> + abi_long ret;
> + void *addr;
> +
> + if ((int)addrlen < 0) {
> + return -TARGET_EINVAL;
> + }
> + addr = alloca(addrlen + 1);
> +
> + ret = target_to_host_sockaddr(addr, target_addr, addrlen);
> +
> + if (is_error(ret)) {
> + return ret;
> + }
> +
> + return get_errno(connect(sockfd, addr, addrlen));
> +}
> +
> +/* accept(2) */
> +static inline abi_long do_bsd_accept(int fd, abi_ulong target_addr,
> + abi_ulong target_addrlen_addr)
> +{
> + socklen_t addrlen;
> + void *addr;
> + abi_long ret;
> +
> + if (target_addr == 0) {
> + return get_errno(accept(fd, NULL, NULL));
> + }
> + /* return EINVAL if addrlen pointer is invalid */
> + if (get_user_u32(addrlen, target_addrlen_addr)) {
> + return -TARGET_EINVAL;
> + }
> + if ((int)addrlen < 0) {
> + return -TARGET_EINVAL;
> + }
> + if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
> + return -TARGET_EINVAL;
> + }
> + addr = alloca(addrlen);
> +
> + ret = get_errno(accept(fd, addr, &addrlen));
> + if (!is_error(ret)) {
> + if (is_error(host_to_target_sockaddr(target_addr, addr, addrlen))) {
> + close(ret);
> + ret = -TARGET_EFAULT;
> + } else if (put_user_u32(addrlen, target_addrlen_addr)) {
> + close(ret);
> + ret = -TARGET_EFAULT;
> + }
> + }
> + return ret;
> +}
> +
> +/* getpeername(2) */
> +static inline abi_long do_bsd_getpeername(int fd, abi_ulong target_addr,
> + abi_ulong target_addrlen_addr)
> +{
> + socklen_t addrlen;
> + void *addr;
> + abi_long ret;
> +
> + if (get_user_u32(addrlen, target_addrlen_addr)) {
> + return -TARGET_EFAULT;
> + }
> + if ((int)addrlen < 0) {
> + return -TARGET_EINVAL;
> + }
> + if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
> + return -TARGET_EFAULT;
> + }
> + addr = alloca(addrlen);
> + ret = get_errno(getpeername(fd, addr, &addrlen));
> + if (!is_error(ret)) {
> + ret = host_to_target_sockaddr(target_addr, addr, addrlen);
> + if (is_error(ret)) {
> + ret = -TARGET_EFAULT;
> + } else if (put_user_u32(addrlen, target_addrlen_addr)) {
> + ret = -TARGET_EFAULT;
> + }
> + }
> + return ret;
> +}
> +
> +/* getsockname(2) */
> +static inline abi_long do_bsd_getsockname(int fd, abi_ulong target_addr,
> + abi_ulong target_addrlen_addr)
> +{
> + socklen_t addrlen;
> + void *addr;
> + abi_long ret;
> +
> + if (get_user_u32(addrlen, target_addrlen_addr)) {
> + return -TARGET_EFAULT;
> + }
> + if ((int)addrlen < 0) {
> + return -TARGET_EINVAL;
> + }
> + if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
> + return -TARGET_EFAULT;
> + }
> + addr = alloca(addrlen);
> +
> + ret = get_errno(getsockname(fd, addr, &addrlen));
> + if (!is_error(ret)) {
> + ret = host_to_target_sockaddr(target_addr, addr, addrlen);
> + if (is_error(ret)) {
> + ret = -TARGET_EFAULT;
> + } else if (put_user_u32(addrlen, target_addrlen_addr)) {
> + ret = -TARGET_EFAULT;
> + }
> + }
> + return ret;
> +}
> +
> +#endif /* BSD_SOCKET_H */
> diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
> index 228daed4c4..737de36514 100644
> --- a/bsd-user/freebsd/os-syscall.c
> +++ b/bsd-user/freebsd/os-syscall.c
> @@ -40,6 +40,7 @@
> #include "bsd-proc.h"
> #include "bsd-misc.h"
> #include "bsd-signal.h"
> +#include "bsd-socket.h"
>
> /* BSD dependent syscall shims */
> #include "os-stat.h"
> @@ -73,6 +74,20 @@ safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds,
> safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd,
> off_t *, outoffp, size_t, len, unsigned int, flags);
>
> +/* used in bsd-socket */
> +safe_syscall5(int, select, int, nfds, fd_set *, readfs, fd_set *, writefds,
> + fd_set *, exceptfds, struct timeval *, timeout);
> +safe_syscall6(int, pselect, int, nfds, fd_set *restrict, readfs,
> + fd_set *restrict, writefds, fd_set *restrict, exceptfds,
> + const struct timespec *restrict, timeout, const sigset_t *restrict,
> + newsigmask);
> +safe_syscall6(ssize_t, recvfrom, int, fd, void *, buf, size_t, len, int, flags,
> + struct sockaddr *restrict, from, socklen_t *restrict, fromlen);
> +safe_syscall6(ssize_t, sendto, int, fd, const void *, buf, size_t, len, int,
> + flags, const struct sockaddr *, to, socklen_t, tolen);
> +safe_syscall3(ssize_t, recvmsg, int, s, struct msghdr *, msg, int, flags);
> +safe_syscall3(ssize_t, sendmsg, int, s, const struct msghdr *, msg, int, flags);
> +
> /* used in os-proc */
> safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
> struct rusage *, rusage);
>
I'm really not fond of seeing alloca, especially used with a random
parameter for the size. Seems like linux-user uses it to implement those
functions though.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 10/37] bsd-user: Add socket shims socketpair through shutdown
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (8 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 09/37] bsd-user: Add bsd-socket.h with bind through getsockname Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:08 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 11/37] bsd-user: Add os-socket.h with sendrecvmsg and message structures Warner Losh
` (27 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Complete bsd-socket.h with socketpair, sendto, recvfrom, socket,
and shutdown system call shims.
Signed-off-by: Stacey Son <sson@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/bsd-socket.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/bsd-user/bsd-socket.h b/bsd-user/bsd-socket.h
index c786d6717a..c87ba195dc 100644
--- a/bsd-user/bsd-socket.h
+++ b/bsd-user/bsd-socket.h
@@ -164,4 +164,120 @@ static inline abi_long do_bsd_getsockname(int fd, abi_ulong target_addr,
return ret;
}
+/* socketpair(2) */
+static inline abi_long do_bsd_socketpair(int domain, int type, int protocol,
+ abi_ulong target_tab_addr)
+{
+ int tab[2];
+ abi_long ret;
+
+ if (!access_ok(VERIFY_WRITE, target_tab_addr, sizeof(tab[0]) * 2)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(socketpair(domain, type, protocol, tab));
+ if (!is_error(ret)) {
+ if (put_user_s32(tab[0], target_tab_addr) ||
+ put_user_s32(tab[1], target_tab_addr + sizeof(tab[0]))) {
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* sendto(2) */
+static inline abi_long do_bsd_sendto(int fd, abi_ulong msg, size_t len,
+ int flags, abi_ulong target_addr,
+ socklen_t addrlen)
+{
+ struct sockaddr *saddr;
+ void *host_msg;
+ abi_long ret;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (len != 0) {
+ host_msg = lock_user(VERIFY_READ, msg, len, 1);
+ if (!host_msg) {
+ return -TARGET_EFAULT;
+ }
+ } else {
+ host_msg = NULL;
+ }
+ if (target_addr) {
+ saddr = alloca(addrlen + 1);
+ ret = target_to_host_sockaddr(saddr, target_addr, addrlen);
+ if (is_error(ret)) {
+ unlock_user(host_msg, msg, 0);
+ return ret;
+ }
+ ret = get_errno(safe_sendto(fd, host_msg, len, flags, saddr, addrlen));
+ } else {
+ ret = get_errno(send(fd, host_msg, len, flags));
+ }
+ unlock_user(host_msg, msg, 0);
+ return ret;
+}
+
+/* recvfrom(2) */
+static inline abi_long do_bsd_recvfrom(int fd, abi_ulong msg, size_t len,
+ int flags, abi_ulong target_addr,
+ abi_ulong target_addrlen)
+{
+ socklen_t addrlen;
+ struct sockaddr *saddr;
+ void *host_msg;
+ abi_long ret;
+
+ host_msg = lock_user(VERIFY_WRITE, msg, len, 0);
+ if (!host_msg) {
+ return -TARGET_EFAULT;
+ }
+ if (target_addr) {
+ if (get_user_u32(addrlen, target_addrlen)) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ if ((int)addrlen < 0) {
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ saddr = alloca(addrlen + 1);
+ ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, saddr,
+ &addrlen));
+ } else {
+ saddr = NULL; /* To keep compiler quiet. */
+ ret = get_errno(recv(fd, host_msg, len, flags));
+ }
+ if (!is_error(ret)) {
+ if (target_addr) {
+ if (is_error(host_to_target_sockaddr(target_addr, saddr, addrlen))) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ } else if (put_user_u32(addrlen, target_addrlen)) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ }
+ unlock_user(host_msg, msg, len);
+ } else {
+fail:
+ unlock_user(host_msg, msg, 0);
+ }
+ return ret;
+}
+
+/* socket(2) */
+static inline abi_long do_bsd_socket(abi_long domain, abi_long type,
+ abi_long protocol)
+{
+ return get_errno(socket(domain, type, protocol));
+}
+
+/* shutdown(2) */
+static inline abi_long do_bsd_shutdown(abi_long s, abi_long how)
+{
+ return get_errno(shutdown(s, how));
+}
+
#endif /* BSD_SOCKET_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 10/37] bsd-user: Add socket shims socketpair through shutdown
2026-05-18 21:27 ` [PATCH v2 10/37] bsd-user: Add socket shims socketpair through shutdown Warner Losh
@ 2026-05-22 23:08 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:08 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Complete bsd-socket.h with socketpair, sendto, recvfrom, socket,
> and shutdown system call shims.
>
> Signed-off-by: Stacey Son <sson@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/bsd-socket.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 116 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 11/37] bsd-user: Add os-socket.h with sendrecvmsg and message structures
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (9 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 10/37] bsd-user: Add socket shims socketpair through shutdown Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:10 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 12/37] bsd-user: Add do_bsd_setsockopt and socket option definitions Warner Losh
` (26 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
Add the first part of FreeBSD socket support: do_sendrecvmsg_locked
and do_sendrecvmsg functions. Also add target_msghdr, target_cmsghdr,
CMSG macros, target_ip_mreq, and target_ip_mreqn structures to
syscall_defs.h.
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-socket.h | 142 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-syscall.c | 1 +
2 files changed, 143 insertions(+)
diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
new file mode 100644
index 0000000000..279157dd05
--- /dev/null
+++ b/bsd-user/freebsd/os-socket.h
@@ -0,0 +1,142 @@
+/*
+ * FreeBSD socket related system call shims
+ *
+ * Copyright (c) 2013-2014 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef BSD_USER_FREEBSD_OS_SOCKET_H
+#define BSD_USER_FREEBSD_OS_SOCKET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "qemu-os.h"
+
+ssize_t safe_recvmsg(int s, struct msghdr *msg, int flags);
+ssize_t safe_sendmsg(int s, const struct msghdr *msg, int flags);
+
+/* do_sendrecvmsg_locked() Must return target values and target errnos. */
+static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
+ int flags, int send)
+{
+ abi_long ret, len;
+ struct msghdr msg;
+ abi_ulong count;
+ struct iovec *vec;
+ abi_ulong target_vec;
+
+ if (msgp->msg_name) {
+ msg.msg_namelen = tswap32(msgp->msg_namelen);
+ msg.msg_name = alloca(msg.msg_namelen + 1);
+ ret = target_to_host_sockaddr(msg.msg_name,
+ tswapal(msgp->msg_name),
+ msg.msg_namelen);
+ if (ret == -TARGET_EFAULT) {
+ /*
+ * For connected sockets msg_name and msg_namelen must be ignored,
+ * so returning EFAULT immediately is wrong. Instead, pass a bad
+ * msg_name to the host kernel, and let it decide whether to return
+ * EFAULT or not.
+ */
+ msg.msg_name = (void *)-1;
+ } else if (ret) {
+ goto out2;
+ }
+ } else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
+ msg.msg_controllen = 2 * tswap32(msgp->msg_controllen);
+ if (msgp->msg_control) {
+ msg.msg_control = alloca(msg.msg_controllen);
+ memset(msg.msg_control, 0, msg.msg_controllen);
+ } else {
+ msg.msg_control = NULL;
+ }
+
+ msg.msg_flags = tswap32(msgp->msg_flags);
+
+ count = tswap32(msgp->msg_iovlen);
+ target_vec = tswapal(msgp->msg_iov);
+
+ if (count > IOV_MAX) {
+ /*
+ * sendrcvmsg returns a different errno for this condition than
+ * readv/writev, so we must catch it here before lock_iovec() does.
+ */
+ ret = -TARGET_EMSGSIZE;
+ goto out2;
+ }
+
+ vec = lock_iovec(send ? VERIFY_READ : VERIFY_WRITE,
+ target_vec, count, send);
+ if (vec == NULL) {
+ ret = -host_to_target_errno(errno);
+ goto out2;
+ }
+ msg.msg_iovlen = count;
+ msg.msg_iov = vec;
+
+ if (send) {
+ if (msg.msg_control != NULL) {
+ ret = t2h_freebsd_cmsg(&msg, msgp);
+ } else {
+ ret = 0;
+ }
+ if (ret == 0) {
+ ret = get_errno(safe_sendmsg(fd, &msg, flags));
+ }
+ } else {
+ ret = get_errno(safe_recvmsg(fd, &msg, flags));
+ if (!is_error(ret)) {
+ len = ret;
+ if (msg.msg_control != NULL) {
+ ret = h2t_freebsd_cmsg(msgp, &msg);
+ } else {
+ ret = 0;
+ }
+ if (!is_error(ret)) {
+ msgp->msg_namelen = tswap32(msg.msg_namelen);
+ msgp->msg_flags = tswap32(msg.msg_flags);
+ if (msg.msg_name != NULL && msg.msg_name != (void *)-1) {
+ ret = host_to_target_sockaddr(tswapal(msgp->msg_name),
+ msg.msg_name, msg.msg_namelen);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ ret = len;
+ }
+ }
+ }
+
+out:
+ unlock_iovec(vec, target_vec, count, !send);
+out2:
+ return ret;
+}
+
+
+static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
+ int flags, int send)
+{
+ abi_long ret;
+ struct target_msghdr *msgp;
+
+ if (!lock_user_struct(send ? VERIFY_READ : VERIFY_WRITE,
+ msgp,
+ target_msg,
+ send ? 1 : 0)) {
+ return -TARGET_EFAULT;
+ }
+ ret = do_sendrecvmsg_locked(fd, msgp, flags, send);
+ unlock_user_struct(msgp, target_msg, send ? 0 : 1);
+ return ret;
+}
+
+
+#endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 737de36514..46fee46336 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -47,6 +47,7 @@
#include "os-proc.h"
#include "os-signal.h"
#include "os-file.h"
+#include "os-socket.h"
#include "os-misc.h"
/* I/O */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 11/37] bsd-user: Add os-socket.h with sendrecvmsg and message structures
2026-05-18 21:27 ` [PATCH v2 11/37] bsd-user: Add os-socket.h with sendrecvmsg and message structures Warner Losh
@ 2026-05-22 23:10 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:10 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the first part of FreeBSD socket support: do_sendrecvmsg_locked
> and do_sendrecvmsg functions. Also add target_msghdr, target_cmsghdr,
> CMSG macros, target_ip_mreq, and target_ip_mreqn structures to
> syscall_defs.h.
>
> 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-socket.h | 142 ++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-syscall.c | 1 +
> 2 files changed, 143 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 12/37] bsd-user: Add do_bsd_setsockopt and socket option definitions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (10 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 11/37] bsd-user: Add os-socket.h with sendrecvmsg and message structures Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:28 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 13/37] bsd-user: Add do_bsd_getsockopt Warner Losh
` (25 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Jessica Clarke, Kyle Evans
Add the setsockopt(2) implementation handling IPPROTO_TCP, IPPROTO_IP,
IPPROTO_IPV6, and SOL_SOCKET levels. Also add the TARGET_SO_* socket
option definitions and TARGET_SOL_SOCKET to syscall_defs.h.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.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-socket.h | 261 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
index 279157dd05..897a1f4d2f 100644
--- a/bsd-user/freebsd/os-socket.h
+++ b/bsd-user/freebsd/os-socket.h
@@ -138,5 +138,266 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
return ret;
}
+/* setsockopt(2) */
+static inline abi_long do_bsd_setsockopt(int sockfd, int level, int optname,
+ abi_ulong optval_addr, socklen_t optlen)
+{
+ abi_long ret;
+ int val;
+ struct ip_mreqn *ip_mreq;
+ void *p;
+
+ switch (level) {
+ case IPPROTO_TCP:
+ /* TCP options all take an 'int' value. */
+ if (optlen < sizeof(uint32_t)) {
+ return -TARGET_EINVAL;
+ }
+ if (get_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
+ break;
+
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_OPTIONS:
+ p = lock_user(VERIFY_READ, optval_addr, optlen, 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, p, optlen));
+ unlock_user(p, optval_addr, 0);
+ break;
+ case IP_HDRINCL:/* int; header is included with data */
+ case IP_TOS: /* int; IP type of service and preced. */
+ case IP_TTL: /* int; IP time to live */
+ case IP_RECVOPTS: /* bool; receive all IP opts w/dgram */
+ case IP_RECVRETOPTS: /* bool; receive IP opts for response */
+ case IP_RECVDSTADDR: /* bool; receive IP dst addr w/dgram */
+ case IP_MULTICAST_IF:/* u_char; set/get IP multicast i/f */
+ case IP_MULTICAST_TTL:/* u_char; set/get IP multicast ttl */
+ case IP_MULTICAST_LOOP:/*u_char;set/get IP multicast loopback */
+ case IP_PORTRANGE: /* int; range to choose for unspec port */
+ case IP_RECVIF: /* bool; receive reception if w/dgram */
+ case IP_IPSEC_POLICY: /* int; set/get security policy */
+ case IP_RECVTTL: /* bool; receive reception TTL w/dgram */
+ val = 0;
+ if (optlen >= sizeof(uint32_t)) {
+ if (get_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ } else if (optlen >= 1) {
+ if (get_user_u8(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, &val,
+ sizeof(val)));
+ break;
+
+ case IP_ADD_MEMBERSHIP: /*ip_mreq; add an IP group membership */
+ case IP_DROP_MEMBERSHIP:/*ip_mreq; drop an IP group membership*/
+ if (optlen < sizeof(struct target_ip_mreq) ||
+ optlen > sizeof(struct target_ip_mreqn)) {
+ return -TARGET_EINVAL;
+ }
+ ip_mreq = (struct ip_mreqn *) alloca(optlen);
+ ret = target_to_host_ip_mreq(ip_mreq, optval_addr, optlen);
+ if (is_error(ret)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq,
+ optlen));
+ break;
+
+ default:
+ goto unimplemented;
+ }
+ break;
+
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_UNICAST_HOPS: /* int; IP6 hops */
+ case IPV6_MULTICAST_IF: /* u_int; set/get IP6 multicast i/f */
+ case IPV6_MULTICAST_HOPS: /* int; set/get IP6 multicast hops */
+ case IPV6_MULTICAST_LOOP: /* u_int; set/get IP6 multicast loopback */
+ case IPV6_PORTRANGE: /* int; range to choose for unspec port */
+ case IPV6_CHECKSUM: /* int; checksum offset for raw socket */
+ case IPV6_V6ONLY: /* bool; make AF_INET6 sockets v6 only */
+ case IPV6_RECVPKTINFO: /* bool; recv if, dst addr */
+ case IPV6_RECVHOPLIMIT: /* bool; recv hop limit */
+ case IPV6_RECVRTHDR: /* bool; recv routing header */
+ case IPV6_RECVHOPOPTS: /* bool; recv hop-by-hop option */
+ case IPV6_RECVDSTOPTS: /* bool; recv dst option after rthdr */
+ case IPV6_USE_MIN_MTU: /* bool; send packets at the minimum MTU */
+ case IPV6_RECVPATHMTU: /* bool; notify an according MTU */
+ case IPV6_HOPLIMIT: /* int; send hop limit */
+ case IPV6_RECVTCLASS: /* bool; recv traffic class values */
+ case IPV6_AUTOFLOWLABEL: /* bool; attach flowlabel automagically */
+ case IPV6_TCLASS: /* int; send traffic class value */
+ case IPV6_DONTFRAG: /* bool; disable IPv6 fragmentation */
+ case IPV6_PREFER_TEMPADDR: /* int; prefer temporary addresses */
+ case IPV6_BINDANY: /* bool: allow bind to any address */
+#ifdef IPV6_BINDMULTI
+ case IPV6_BINDMULTI: /* bool; allow multibind to same addr/port*/
+#endif /* IPV6_BINDMULTI */
+#ifdef IPV6_RSS_LISTEN_BUCKET
+ case IPV6_RSS_LISTEN_BUCKET: /* int; set RSS listen bucket */
+#endif /* IPV6_RSS_LISTEN_BUCKET */
+#ifdef IPV6_FLOWID
+ case IPV6_FLOWID: /* int; flowid of given socket */
+#endif /* IPV6_FLOWID */
+#ifdef IPV6_FLOWTYPE
+ case IPV6_FLOWTYPE: /* int; flowtype of given socket */
+#endif /* IPV6_FLOWTYPE */
+#ifdef IPV6_RSSBUCKETID
+ case IPV6_RSSBUCKETID: /* int; RSS bucket ID of given socket */
+#endif /* IPV6_RSSBUCKETID */
+ val = 0;
+ if (optlen >= sizeof(uint32_t)) {
+ if (get_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ } else if (optlen >= 1) {
+ if (get_user_u8(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, &val,
+ sizeof(val)));
+ break;
+
+ case IPV6_JOIN_GROUP: /* ipv6_mreq; join a group membership */
+ case IPV6_LEAVE_GROUP: /* ipv6_mreq; leave a group membership */
+ case ICMP6_FILTER: /* icmp6_filter; icmp6 filter */
+ case IPV6_IPSEC_POLICY: /* struct; get/set security policy */
+ case IPV6_FW_ADD: /* add a firewall rule to chain */
+ case IPV6_FW_DEL: /* delete a firewall rule from chain */
+ case IPV6_FW_FLUSH: /* flush firewall rule chain */
+ case IPV6_FW_ZERO: /* clear single/all firewall counter(s) */
+ case IPV6_FW_GET: /* get entire firewall rule chain */
+ case IPV6_RTHDRDSTOPTS: /* ip6_dest; send dst option before rthdr */
+ case IPV6_PATHMTU: /* mtuinfo; get the current path MTU */
+ case IPV6_PKTINFO: /* in6_pktinfo; send if, src addr */
+ case IPV6_NEXTHOP: /* sockaddr; next hop addr */
+ case IPV6_HOPOPTS: /* ip6_hbh; send hop-by-hop option */
+ case IPV6_DSTOPTS: /* ip6_dest; send dst option befor rthdr */
+ case IPV6_RTHDR: /* ip6_rthdr; send routing header */
+ case IPV6_MSFILTER: /* struct __msfilterreq; */
+ default:
+ goto unimplemented;
+ }
+ break;
+
+ case TARGET_SOL_SOCKET:
+ switch (optname) {
+ /* Options with 'int' argument. */
+ case TARGET_SO_DEBUG:
+ optname = SO_DEBUG;
+ break;
+
+ case TARGET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ break;
+
+ case TARGET_SO_REUSEPORT:
+ optname = SO_REUSEPORT;
+ break;
+
+ case TARGET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ break;
+
+ case TARGET_SO_DONTROUTE:
+ optname = SO_DONTROUTE;
+ break;
+
+ case TARGET_SO_LINGER:
+ optname = SO_LINGER;
+ break;
+
+ case TARGET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ break;
+
+ case TARGET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ break;
+
+ case TARGET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ break;
+
+ case TARGET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ break;
+
+ case TARGET_SO_SNDLOWAT:
+ optname = SO_RCVLOWAT;
+ break;
+
+ case TARGET_SO_RCVLOWAT:
+ optname = SO_RCVLOWAT;
+ break;
+
+ case TARGET_SO_SNDTIMEO:
+ optname = SO_SNDTIMEO;
+ break;
+
+ case TARGET_SO_RCVTIMEO:
+ optname = SO_RCVTIMEO;
+ break;
+
+ case TARGET_SO_ACCEPTFILTER:
+ goto unimplemented;
+
+ case TARGET_SO_NOSIGPIPE:
+ optname = SO_NOSIGPIPE;
+ break;
+
+ case TARGET_SO_TIMESTAMP:
+ optname = SO_TIMESTAMP;
+ break;
+
+ case TARGET_SO_BINTIME:
+ optname = SO_BINTIME;
+ break;
+
+ case TARGET_SO_ERROR:
+ optname = SO_ERROR;
+ break;
+
+ case TARGET_SO_SETFIB:
+ optname = SO_SETFIB;
+ break;
+
+#ifdef SO_USER_COOKIE
+ case TARGET_SO_USER_COOKIE:
+ optname = SO_USER_COOKIE;
+ break;
+#endif
+ default:
+ goto unimplemented;
+ }
+ if (optlen < sizeof(uint32_t)) {
+ return -TARGET_EINVAL;
+ }
+ if (get_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val,
+ sizeof(val)));
+ break;
+ default:
+unimplemented:
+ gemu_log("Unsupported setsockopt level=%d optname=%d\n",
+ level, optname);
+ ret = -TARGET_ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
#endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 12/37] bsd-user: Add do_bsd_setsockopt and socket option definitions
2026-05-18 21:27 ` [PATCH v2 12/37] bsd-user: Add do_bsd_setsockopt and socket option definitions Warner Losh
@ 2026-05-22 23:28 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:28 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Mikaël Urankar, Jessica Clarke
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the setsockopt(2) implementation handling IPPROTO_TCP, IPPROTO_IP,
> IPPROTO_IPV6, and SOL_SOCKET levels. Also add the TARGET_SO_* socket
> option definitions and TARGET_SOL_SOCKET to syscall_defs.h.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
> Signed-off-by: Jessica Clarke <jrtc27@jrtc27.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-socket.h | 261 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 261 insertions(+)
>
> diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
> index 279157dd05..897a1f4d2f 100644
> --- a/bsd-user/freebsd/os-socket.h
> +++ b/bsd-user/freebsd/os-socket.h
> @@ -138,5 +138,266 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
> return ret;
> }
>
> +/* setsockopt(2) */
> +static inline abi_long do_bsd_setsockopt(int sockfd, int level, int optname,
> + abi_ulong optval_addr, socklen_t optlen)
> +{
> + abi_long ret;
> + int val;
> + struct ip_mreqn *ip_mreq;
> + void *p;
> +
> + switch (level) {
> + case IPPROTO_TCP:
> + /* TCP options all take an 'int' value. */
> + if (optlen < sizeof(uint32_t)) {
> + return -TARGET_EINVAL;
> + }
> + if (get_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
> + break;
> +
> + case IPPROTO_IP:
> + switch (optname) {
> + case IP_OPTIONS:
> + p = lock_user(VERIFY_READ, optval_addr, optlen, 1);
> + if (p == NULL) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(setsockopt(sockfd, level, optname, p, optlen));
> + unlock_user(p, optval_addr, 0);
> + break;
> + case IP_HDRINCL:/* int; header is included with data */
> + case IP_TOS: /* int; IP type of service and preced. */
> + case IP_TTL: /* int; IP time to live */
> + case IP_RECVOPTS: /* bool; receive all IP opts w/dgram */
> + case IP_RECVRETOPTS: /* bool; receive IP opts for response */
> + case IP_RECVDSTADDR: /* bool; receive IP dst addr w/dgram */
> + case IP_MULTICAST_IF:/* u_char; set/get IP multicast i/f */
> + case IP_MULTICAST_TTL:/* u_char; set/get IP multicast ttl */
> + case IP_MULTICAST_LOOP:/*u_char;set/get IP multicast loopback */
> + case IP_PORTRANGE: /* int; range to choose for unspec port */
> + case IP_RECVIF: /* bool; receive reception if w/dgram */
> + case IP_IPSEC_POLICY: /* int; set/get security policy */
> + case IP_RECVTTL: /* bool; receive reception TTL w/dgram */
> + val = 0;
> + if (optlen >= sizeof(uint32_t)) {
> + if (get_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + } else if (optlen >= 1) {
> + if (get_user_u8(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + ret = get_errno(setsockopt(sockfd, level, optname, &val,
> + sizeof(val)));
> + break;
> +
> + case IP_ADD_MEMBERSHIP: /*ip_mreq; add an IP group membership */
> + case IP_DROP_MEMBERSHIP:/*ip_mreq; drop an IP group membership*/
> + if (optlen < sizeof(struct target_ip_mreq) ||
> + optlen > sizeof(struct target_ip_mreqn)) {
> + return -TARGET_EINVAL;
> + }
> + ip_mreq = (struct ip_mreqn *) alloca(optlen);
> + ret = target_to_host_ip_mreq(ip_mreq, optval_addr, optlen);
> + if (is_error(ret)) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq,
> + optlen));
> + break;
> +
> + default:
> + goto unimplemented;
> + }
> + break;
> +
> + case IPPROTO_IPV6:
> + switch (optname) {
> + case IPV6_UNICAST_HOPS: /* int; IP6 hops */
> + case IPV6_MULTICAST_IF: /* u_int; set/get IP6 multicast i/f */
> + case IPV6_MULTICAST_HOPS: /* int; set/get IP6 multicast hops */
> + case IPV6_MULTICAST_LOOP: /* u_int; set/get IP6 multicast loopback */
> + case IPV6_PORTRANGE: /* int; range to choose for unspec port */
> + case IPV6_CHECKSUM: /* int; checksum offset for raw socket */
> + case IPV6_V6ONLY: /* bool; make AF_INET6 sockets v6 only */
> + case IPV6_RECVPKTINFO: /* bool; recv if, dst addr */
> + case IPV6_RECVHOPLIMIT: /* bool; recv hop limit */
> + case IPV6_RECVRTHDR: /* bool; recv routing header */
> + case IPV6_RECVHOPOPTS: /* bool; recv hop-by-hop option */
> + case IPV6_RECVDSTOPTS: /* bool; recv dst option after rthdr */
> + case IPV6_USE_MIN_MTU: /* bool; send packets at the minimum MTU */
> + case IPV6_RECVPATHMTU: /* bool; notify an according MTU */
> + case IPV6_HOPLIMIT: /* int; send hop limit */
> + case IPV6_RECVTCLASS: /* bool; recv traffic class values */
> + case IPV6_AUTOFLOWLABEL: /* bool; attach flowlabel automagically */
> + case IPV6_TCLASS: /* int; send traffic class value */
> + case IPV6_DONTFRAG: /* bool; disable IPv6 fragmentation */
> + case IPV6_PREFER_TEMPADDR: /* int; prefer temporary addresses */
> + case IPV6_BINDANY: /* bool: allow bind to any address */
> +#ifdef IPV6_BINDMULTI
> + case IPV6_BINDMULTI: /* bool; allow multibind to same addr/port*/
> +#endif /* IPV6_BINDMULTI */
> +#ifdef IPV6_RSS_LISTEN_BUCKET
> + case IPV6_RSS_LISTEN_BUCKET: /* int; set RSS listen bucket */
> +#endif /* IPV6_RSS_LISTEN_BUCKET */
> +#ifdef IPV6_FLOWID
> + case IPV6_FLOWID: /* int; flowid of given socket */
> +#endif /* IPV6_FLOWID */
> +#ifdef IPV6_FLOWTYPE
> + case IPV6_FLOWTYPE: /* int; flowtype of given socket */
> +#endif /* IPV6_FLOWTYPE */
> +#ifdef IPV6_RSSBUCKETID
> + case IPV6_RSSBUCKETID: /* int; RSS bucket ID of given socket */
> +#endif /* IPV6_RSSBUCKETID */
> + val = 0;
> + if (optlen >= sizeof(uint32_t)) {
> + if (get_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + } else if (optlen >= 1) {
> + if (get_user_u8(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + ret = get_errno(setsockopt(sockfd, level, optname, &val,
> + sizeof(val)));
> + break;
> +
> + case IPV6_JOIN_GROUP: /* ipv6_mreq; join a group membership */
> + case IPV6_LEAVE_GROUP: /* ipv6_mreq; leave a group membership */
> + case ICMP6_FILTER: /* icmp6_filter; icmp6 filter */
> + case IPV6_IPSEC_POLICY: /* struct; get/set security policy */
> + case IPV6_FW_ADD: /* add a firewall rule to chain */
> + case IPV6_FW_DEL: /* delete a firewall rule from chain */
> + case IPV6_FW_FLUSH: /* flush firewall rule chain */
> + case IPV6_FW_ZERO: /* clear single/all firewall counter(s) */
> + case IPV6_FW_GET: /* get entire firewall rule chain */
> + case IPV6_RTHDRDSTOPTS: /* ip6_dest; send dst option before rthdr */
> + case IPV6_PATHMTU: /* mtuinfo; get the current path MTU */
> + case IPV6_PKTINFO: /* in6_pktinfo; send if, src addr */
> + case IPV6_NEXTHOP: /* sockaddr; next hop addr */
> + case IPV6_HOPOPTS: /* ip6_hbh; send hop-by-hop option */
> + case IPV6_DSTOPTS: /* ip6_dest; send dst option befor rthdr */
> + case IPV6_RTHDR: /* ip6_rthdr; send routing header */
> + case IPV6_MSFILTER: /* struct __msfilterreq; */
> + default:
> + goto unimplemented;
> + }
> + break;
> +
> + case TARGET_SOL_SOCKET:
> + switch (optname) {
> + /* Options with 'int' argument. */
> + case TARGET_SO_DEBUG:
> + optname = SO_DEBUG;
> + break;
> +
> + case TARGET_SO_REUSEADDR:
> + optname = SO_REUSEADDR;
> + break;
> +
> + case TARGET_SO_REUSEPORT:
> + optname = SO_REUSEPORT;
> + break;
> +
> + case TARGET_SO_KEEPALIVE:
> + optname = SO_KEEPALIVE;
> + break;
> +
> + case TARGET_SO_DONTROUTE:
> + optname = SO_DONTROUTE;
> + break;
> +
> + case TARGET_SO_LINGER:
> + optname = SO_LINGER;
> + break;
> +
> + case TARGET_SO_BROADCAST:
> + optname = SO_BROADCAST;
> + break;
> +
> + case TARGET_SO_OOBINLINE:
> + optname = SO_OOBINLINE;
> + break;
> +
> + case TARGET_SO_SNDBUF:
> + optname = SO_SNDBUF;
> + break;
> +
> + case TARGET_SO_RCVBUF:
> + optname = SO_RCVBUF;
> + break;
> +
> + case TARGET_SO_SNDLOWAT:
> + optname = SO_RCVLOWAT;
> + break;
> +
> + case TARGET_SO_RCVLOWAT:
> + optname = SO_RCVLOWAT;
> + break;
> +
> + case TARGET_SO_SNDTIMEO:
> + optname = SO_SNDTIMEO;
> + break;
> +
> + case TARGET_SO_RCVTIMEO:
> + optname = SO_RCVTIMEO;
> + break;
> +
> + case TARGET_SO_ACCEPTFILTER:
> + goto unimplemented;
> +
> + case TARGET_SO_NOSIGPIPE:
> + optname = SO_NOSIGPIPE;
> + break;
> +
> + case TARGET_SO_TIMESTAMP:
> + optname = SO_TIMESTAMP;
> + break;
> +
> + case TARGET_SO_BINTIME:
> + optname = SO_BINTIME;
> + break;
> +
> + case TARGET_SO_ERROR:
> + optname = SO_ERROR;
> + break;
> +
> + case TARGET_SO_SETFIB:
> + optname = SO_SETFIB;
> + break;
> +
> +#ifdef SO_USER_COOKIE
> + case TARGET_SO_USER_COOKIE:
> + optname = SO_USER_COOKIE;
> + break;
> +#endif
> + default:
> + goto unimplemented;
> + }
> + if (optlen < sizeof(uint32_t)) {
> + return -TARGET_EINVAL;
> + }
> + if (get_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val,
> + sizeof(val)));
> + break;
> + default:
> +unimplemented:
> + gemu_log("Unsupported setsockopt level=%d optname=%d\n",
> + level, optname);
> + ret = -TARGET_ENOPROTOOPT;
> + }
> +
> + return ret;
> +}
> +
>
> #endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
>
See comment on next patch to solve duplicate for translating sockopt.
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 13/37] bsd-user: Add do_bsd_getsockopt
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (11 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 12/37] bsd-user: Add do_bsd_setsockopt and socket option definitions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:28 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 14/37] bsd-user: Add FreeBSD socket helpers and sockaddr conversion Warner Losh
` (24 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
Add the getsockopt(2) implementation handling SOL_SOCKET, IPPROTO_TCP,
IPPROTO_IP, and IPPROTO_IPV6 levels with proper target-to-host option
name translation.
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-socket.h | 284 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 284 insertions(+)
diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
index 897a1f4d2f..f8521780c1 100644
--- a/bsd-user/freebsd/os-socket.h
+++ b/bsd-user/freebsd/os-socket.h
@@ -399,5 +399,289 @@ unimplemented:
return ret;
}
+/* getsockopt(2) */
+static inline abi_long do_bsd_getsockopt(int sockfd, int level, int optname,
+ abi_ulong optval_addr, abi_ulong optlen)
+{
+ abi_long ret;
+ int len, val;
+ socklen_t lv;
+ void *p;
+
+ switch (level) {
+ case TARGET_SOL_SOCKET:
+ level = SOL_SOCKET;
+ switch (optname) {
+
+ /* These don't just return a single integer */
+ case TARGET_SO_LINGER:
+ case TARGET_SO_RCVTIMEO:
+ case TARGET_SO_SNDTIMEO:
+ case TARGET_SO_ACCEPTFILTER:
+ goto unimplemented;
+
+ /* Options with 'int' argument. */
+ case TARGET_SO_DEBUG:
+ optname = SO_DEBUG;
+ goto int_case;
+
+ case TARGET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ goto int_case;
+
+ case TARGET_SO_REUSEPORT:
+ optname = SO_REUSEPORT;
+ goto int_case;
+
+ case TARGET_SO_TYPE:
+ optname = SO_TYPE;
+ goto int_case;
+
+ case TARGET_SO_ERROR:
+ optname = SO_ERROR;
+ goto int_case;
+
+ case TARGET_SO_DONTROUTE:
+ optname = SO_DONTROUTE;
+ goto int_case;
+
+ case TARGET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ goto int_case;
+
+ case TARGET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ goto int_case;
+
+ case TARGET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ goto int_case;
+
+ case TARGET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ goto int_case;
+
+ case TARGET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ goto int_case;
+
+ case TARGET_SO_TIMESTAMP:
+ optname = SO_TIMESTAMP;
+ goto int_case;
+
+ case TARGET_SO_RCVLOWAT:
+ optname = SO_RCVLOWAT;
+ goto int_case;
+
+ case TARGET_SO_LISTENINCQLEN:
+ optname = SO_LISTENINCQLEN;
+ goto int_case;
+
+ default:
+int_case:
+ if (get_user_u32(len, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ if (len < 0) {
+ return -TARGET_EINVAL;
+ }
+ lv = sizeof(lv);
+ ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
+ if (ret < 0) {
+ return ret;
+ }
+ if (len > lv) {
+ len = lv;
+ }
+ if (len == 4) {
+ if (put_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ } else {
+ if (put_user_u8(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ if (put_user_u32(len, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ break;
+
+ }
+ break;
+
+ case IPPROTO_TCP:
+ /* TCP options all take an 'int' value. */
+ goto int_case;
+
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_OPTIONS:
+ if (get_user_u32(len, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ lv = (socklen_t)len;
+ p = lock_user(VERIFY_WRITE, optval_addr, len, 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(getsockopt(sockfd, level, optname, p, &lv));
+ unlock_user(p, optval_addr, len);
+ if (put_user_u32(lv, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ break;
+ case IP_HDRINCL:
+ case IP_TOS:
+ case IP_TTL:
+ case IP_RECVOPTS:
+ case IP_RECVRETOPTS:
+ case IP_RECVDSTADDR:
+
+ case IP_RETOPTS:
+#if defined(IP_RECVTOS)
+ case IP_RECVTOS:
+#endif
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_PORTRANGE:
+ case IP_IPSEC_POLICY:
+ case IP_ONESBCAST:
+ case IP_BINDANY:
+ if (get_user_u32(len, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ if (len < 0) {
+ return -TARGET_EINVAL;
+ }
+ lv = sizeof(lv);
+ ret = get_errno(getsockopt(sockfd, level, optname,
+ &val, &lv));
+ if (ret < 0) {
+ return ret;
+ }
+ if (len < sizeof(int) && len > 0 && val >= 0 &&
+ val < 255) {
+ len = 1;
+ if (put_user_u32(len, optlen) ||
+ put_user_u8(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ } else {
+ if (len > sizeof(int)) {
+ len = sizeof(int);
+ }
+ if (put_user_u32(len, optlen) ||
+ put_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ break;
+
+ default:
+ goto unimplemented;
+ }
+ break;
+
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_UNICAST_HOPS: /* int; IP6 hops */
+ case IPV6_MULTICAST_IF: /* u_int; set/get IP6 multicast i/f */
+ case IPV6_MULTICAST_HOPS: /* int; set/get IP6 multicast hops */
+ case IPV6_MULTICAST_LOOP: /* u_int; set/get IP6 multicast loopback */
+ case IPV6_PORTRANGE: /* int; range to choose for unspec port */
+ case IPV6_CHECKSUM: /* int; checksum offset for raw socket */
+ case IPV6_V6ONLY: /* bool; make AF_INET6 sockets v6 only */
+ case IPV6_RECVPKTINFO: /* bool; recv if, dst addr */
+ case IPV6_RECVHOPLIMIT: /* bool; recv hop limit */
+ case IPV6_RECVRTHDR: /* bool; recv routing header */
+ case IPV6_RECVHOPOPTS: /* bool; recv hop-by-hop option */
+ case IPV6_RECVDSTOPTS: /* bool; recv dst option after rthdr */
+ case IPV6_USE_MIN_MTU: /* bool; send packets at the minimum MTU */
+ case IPV6_RECVPATHMTU: /* bool; notify an according MTU */
+ case IPV6_HOPLIMIT: /* int; send hop limit */
+ case IPV6_RECVTCLASS: /* bool; recv traffic class values */
+ case IPV6_AUTOFLOWLABEL: /* bool; attach flowlabel automagically */
+ case IPV6_TCLASS: /* int; send traffic class value */
+ case IPV6_DONTFRAG: /* bool; disable IPv6 fragmentation */
+ case IPV6_PREFER_TEMPADDR: /* int; prefer temporary addresses */
+ case IPV6_BINDANY: /* bool: allow bind to any address */
+#ifdef IPV6_BINDMULTI
+ case IPV6_BINDMULTI: /* bool; allow multibind to same addr/port*/
+#endif /* IPV6_BINDMULTI */
+#ifdef IPV6_RSS_LISTEN_BUCKET
+ case IPV6_RSS_LISTEN_BUCKET: /* int; set RSS listen bucket */
+#endif /* IPV6_RSS_LISTEN_BUCKET */
+#ifdef IPV6_FLOWID
+ case IPV6_FLOWID: /* int; flowid of given socket */
+#endif /* IPV6_FLOWID */
+#ifdef IPV6_FLOWTYPE
+ case IPV6_FLOWTYPE: /* int; flowtype of given socket */
+#endif /* IPV6_FLOWTYPE */
+#ifdef IPV6_RSSBUCKETID
+ case IPV6_RSSBUCKETID: /* int; RSS bucket ID of given socket */
+#endif /* IPV6_RSSBUCKETID */
+ if (get_user_u32(len, optlen)) {
+ return -TARGET_EFAULT;
+ }
+ if (len < 0) {
+ return -TARGET_EINVAL;
+ }
+ lv = sizeof(lv);
+ ret = get_errno(getsockopt(sockfd, level, optname,
+ &val, &lv));
+ if (ret < 0) {
+ return ret;
+ }
+ if (len < sizeof(int) && len > 0 && val >= 0 &&
+ val < 255) {
+ len = 1;
+ if (put_user_u32(len, optlen) ||
+ put_user_u8(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ } else {
+ if (len > sizeof(int)) {
+ len = sizeof(int);
+ }
+ if (put_user_u32(len, optlen) ||
+ put_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ break;
+
+ case IPV6_JOIN_GROUP: /* ipv6_mreq; join a group membership */
+ case IPV6_LEAVE_GROUP: /* ipv6_mreq; leave a group membership */
+ case ICMP6_FILTER: /* icmp6_filter; icmp6 filter */
+ case IPV6_IPSEC_POLICY: /* struct; get/set security policy */
+ case IPV6_FW_ADD: /* add a firewall rule to chain */
+ case IPV6_FW_DEL: /* delete a firewall rule from chain */
+ case IPV6_FW_FLUSH: /* flush firewall rule chain */
+ case IPV6_FW_ZERO: /* clear single/all firewall counter(s) */
+ case IPV6_FW_GET: /* get entire firewall rule chain */
+ case IPV6_RTHDRDSTOPTS: /* ip6_dest; send dst option before rthdr */
+ case IPV6_PATHMTU: /* mtuinfo; get the current path MTU */
+ case IPV6_PKTINFO: /* in6_pktinfo; send if, src addr */
+ case IPV6_NEXTHOP: /* sockaddr; next hop addr */
+ case IPV6_HOPOPTS: /* ip6_hbh; send hop-by-hop option */
+ case IPV6_DSTOPTS: /* ip6_dest; send dst option befor rthdr */
+ case IPV6_RTHDR: /* ip6_rthdr; send routing header */
+ case IPV6_MSFILTER: /* struct __msfilterreq; */
+ default:
+ goto unimplemented;
+ }
+ break;
+
+ default:
+unimplemented:
+ gemu_log("getsockopt level=%d optname=%d not yet supported\n",
+ level, optname);
+ ret = -TARGET_EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
#endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 13/37] bsd-user: Add do_bsd_getsockopt
2026-05-18 21:27 ` [PATCH v2 13/37] bsd-user: Add do_bsd_getsockopt Warner Losh
@ 2026-05-22 23:28 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:28 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the getsockopt(2) implementation handling SOL_SOCKET, IPPROTO_TCP,
> IPPROTO_IP, and IPPROTO_IPV6 levels with proper target-to-host option
> name translation.
>
> 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-socket.h | 284 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 284 insertions(+)
>
> diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
> index 897a1f4d2f..f8521780c1 100644
> --- a/bsd-user/freebsd/os-socket.h
> +++ b/bsd-user/freebsd/os-socket.h
> @@ -399,5 +399,289 @@ unimplemented:
> return ret;
> }
>
> +/* getsockopt(2) */
> +static inline abi_long do_bsd_getsockopt(int sockfd, int level, int optname,
> + abi_ulong optval_addr, abi_ulong optlen)
> +{
> + abi_long ret;
> + int len, val;
> + socklen_t lv;
> + void *p;
> +
> + switch (level) {
> + case TARGET_SOL_SOCKET:
> + level = SOL_SOCKET;
> + switch (optname) {
> +
> + /* These don't just return a single integer */
> + case TARGET_SO_LINGER:
> + case TARGET_SO_RCVTIMEO:
> + case TARGET_SO_SNDTIMEO:
> + case TARGET_SO_ACCEPTFILTER:
> + goto unimplemented;
> +
> + /* Options with 'int' argument. */
> + case TARGET_SO_DEBUG:
> + optname = SO_DEBUG;
> + goto int_case;
> +
> + case TARGET_SO_REUSEADDR:
> + optname = SO_REUSEADDR;
> + goto int_case;
> +
> + case TARGET_SO_REUSEPORT:
> + optname = SO_REUSEPORT;
> + goto int_case;
> +
> + case TARGET_SO_TYPE:
> + optname = SO_TYPE;
> + goto int_case;
> +
> + case TARGET_SO_ERROR:
> + optname = SO_ERROR;
> + goto int_case;
> +
> + case TARGET_SO_DONTROUTE:
> + optname = SO_DONTROUTE;
> + goto int_case;
> +
> + case TARGET_SO_BROADCAST:
> + optname = SO_BROADCAST;
> + goto int_case;
> +
> + case TARGET_SO_SNDBUF:
> + optname = SO_SNDBUF;
> + goto int_case;
> +
> + case TARGET_SO_RCVBUF:
> + optname = SO_RCVBUF;
> + goto int_case;
> +
> + case TARGET_SO_KEEPALIVE:
> + optname = SO_KEEPALIVE;
> + goto int_case;
> +
> + case TARGET_SO_OOBINLINE:
> + optname = SO_OOBINLINE;
> + goto int_case;
> +
> + case TARGET_SO_TIMESTAMP:
> + optname = SO_TIMESTAMP;
> + goto int_case;
> +
> + case TARGET_SO_RCVLOWAT:
> + optname = SO_RCVLOWAT;
> + goto int_case;
> +
> + case TARGET_SO_LISTENINCQLEN:
> + optname = SO_LISTENINCQLEN;
> + goto int_case;
> +
This duplicates what there is in previous patch.
We should add a target_to_host_sockopt that takes care of this.
> + default:
> +int_case:
> + if (get_user_u32(len, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + if (len < 0) {
> + return -TARGET_EINVAL;
> + }
> + lv = sizeof(lv);
> + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
> + if (ret < 0) {
> + return ret;
> + }
> + if (len > lv) {
> + len = lv;
> + }
> + if (len == 4) {
> + if (put_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + } else {
> + if (put_user_u8(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + if (put_user_u32(len, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + break;
> +
> + }
> + break;
> +
> + case IPPROTO_TCP:
> + /* TCP options all take an 'int' value. */
> + goto int_case;
> +
> + case IPPROTO_IP:
> + switch (optname) {
> + case IP_OPTIONS:
> + if (get_user_u32(len, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + lv = (socklen_t)len;
> + p = lock_user(VERIFY_WRITE, optval_addr, len, 0);
> + if (p == NULL) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(getsockopt(sockfd, level, optname, p, &lv));
> + unlock_user(p, optval_addr, len);
> + if (put_user_u32(lv, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + break;
> + case IP_HDRINCL:
> + case IP_TOS:
> + case IP_TTL:
> + case IP_RECVOPTS:
> + case IP_RECVRETOPTS:
> + case IP_RECVDSTADDR:
> +
> + case IP_RETOPTS:
> +#if defined(IP_RECVTOS)
> + case IP_RECVTOS:
> +#endif
> + case IP_MULTICAST_TTL:
> + case IP_MULTICAST_LOOP:
> + case IP_PORTRANGE:
> + case IP_IPSEC_POLICY:
> + case IP_ONESBCAST:
> + case IP_BINDANY:
> + if (get_user_u32(len, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + if (len < 0) {
> + return -TARGET_EINVAL;
> + }
> + lv = sizeof(lv);
> + ret = get_errno(getsockopt(sockfd, level, optname,
> + &val, &lv));
> + if (ret < 0) {
> + return ret;
> + }
> + if (len < sizeof(int) && len > 0 && val >= 0 &&
> + val < 255) {
> + len = 1;
> + if (put_user_u32(len, optlen) ||
> + put_user_u8(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + } else {
> + if (len > sizeof(int)) {
> + len = sizeof(int);
> + }
> + if (put_user_u32(len, optlen) ||
> + put_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + break;
> +
> + default:
> + goto unimplemented;
> + }
> + break;
> +
> + case IPPROTO_IPV6:
> + switch (optname) {
> + case IPV6_UNICAST_HOPS: /* int; IP6 hops */
> + case IPV6_MULTICAST_IF: /* u_int; set/get IP6 multicast i/f */
> + case IPV6_MULTICAST_HOPS: /* int; set/get IP6 multicast hops */
> + case IPV6_MULTICAST_LOOP: /* u_int; set/get IP6 multicast loopback */
> + case IPV6_PORTRANGE: /* int; range to choose for unspec port */
> + case IPV6_CHECKSUM: /* int; checksum offset for raw socket */
> + case IPV6_V6ONLY: /* bool; make AF_INET6 sockets v6 only */
> + case IPV6_RECVPKTINFO: /* bool; recv if, dst addr */
> + case IPV6_RECVHOPLIMIT: /* bool; recv hop limit */
> + case IPV6_RECVRTHDR: /* bool; recv routing header */
> + case IPV6_RECVHOPOPTS: /* bool; recv hop-by-hop option */
> + case IPV6_RECVDSTOPTS: /* bool; recv dst option after rthdr */
> + case IPV6_USE_MIN_MTU: /* bool; send packets at the minimum MTU */
> + case IPV6_RECVPATHMTU: /* bool; notify an according MTU */
> + case IPV6_HOPLIMIT: /* int; send hop limit */
> + case IPV6_RECVTCLASS: /* bool; recv traffic class values */
> + case IPV6_AUTOFLOWLABEL: /* bool; attach flowlabel automagically */
> + case IPV6_TCLASS: /* int; send traffic class value */
> + case IPV6_DONTFRAG: /* bool; disable IPv6 fragmentation */
> + case IPV6_PREFER_TEMPADDR: /* int; prefer temporary addresses */
> + case IPV6_BINDANY: /* bool: allow bind to any address */
> +#ifdef IPV6_BINDMULTI
> + case IPV6_BINDMULTI: /* bool; allow multibind to same addr/port*/
> +#endif /* IPV6_BINDMULTI */
> +#ifdef IPV6_RSS_LISTEN_BUCKET
> + case IPV6_RSS_LISTEN_BUCKET: /* int; set RSS listen bucket */
> +#endif /* IPV6_RSS_LISTEN_BUCKET */
> +#ifdef IPV6_FLOWID
> + case IPV6_FLOWID: /* int; flowid of given socket */
> +#endif /* IPV6_FLOWID */
> +#ifdef IPV6_FLOWTYPE
> + case IPV6_FLOWTYPE: /* int; flowtype of given socket */
> +#endif /* IPV6_FLOWTYPE */
> +#ifdef IPV6_RSSBUCKETID
> + case IPV6_RSSBUCKETID: /* int; RSS bucket ID of given socket */
> +#endif /* IPV6_RSSBUCKETID */
> + if (get_user_u32(len, optlen)) {
> + return -TARGET_EFAULT;
> + }
> + if (len < 0) {
> + return -TARGET_EINVAL;
> + }
> + lv = sizeof(lv);
> + ret = get_errno(getsockopt(sockfd, level, optname,
> + &val, &lv));
> + if (ret < 0) {
> + return ret;
> + }
> + if (len < sizeof(int) && len > 0 && val >= 0 &&
> + val < 255) {
> + len = 1;
> + if (put_user_u32(len, optlen) ||
> + put_user_u8(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + } else {
> + if (len > sizeof(int)) {
> + len = sizeof(int);
> + }
> + if (put_user_u32(len, optlen) ||
> + put_user_u32(val, optval_addr)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + break;
> +
> + case IPV6_JOIN_GROUP: /* ipv6_mreq; join a group membership */
> + case IPV6_LEAVE_GROUP: /* ipv6_mreq; leave a group membership */
> + case ICMP6_FILTER: /* icmp6_filter; icmp6 filter */
> + case IPV6_IPSEC_POLICY: /* struct; get/set security policy */
> + case IPV6_FW_ADD: /* add a firewall rule to chain */
> + case IPV6_FW_DEL: /* delete a firewall rule from chain */
> + case IPV6_FW_FLUSH: /* flush firewall rule chain */
> + case IPV6_FW_ZERO: /* clear single/all firewall counter(s) */
> + case IPV6_FW_GET: /* get entire firewall rule chain */
> + case IPV6_RTHDRDSTOPTS: /* ip6_dest; send dst option before rthdr */
> + case IPV6_PATHMTU: /* mtuinfo; get the current path MTU */
> + case IPV6_PKTINFO: /* in6_pktinfo; send if, src addr */
> + case IPV6_NEXTHOP: /* sockaddr; next hop addr */
> + case IPV6_HOPOPTS: /* ip6_hbh; send hop-by-hop option */
> + case IPV6_DSTOPTS: /* ip6_dest; send dst option befor rthdr */
> + case IPV6_RTHDR: /* ip6_rthdr; send routing header */
> + case IPV6_MSFILTER: /* struct __msfilterreq; */
> + default:
> + goto unimplemented;
> + }
> + break;
> +
> + default:
> +unimplemented:
> + gemu_log("getsockopt level=%d optname=%d not yet supported\n",
> + level, optname);
> + ret = -TARGET_EOPNOTSUPP;
> + break;
> + }
> + return ret;
> +}
> +
>
> #endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 14/37] bsd-user: Add FreeBSD socket helpers and sockaddr conversion
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (12 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 13/37] bsd-user: Add do_bsd_getsockopt Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:29 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions Warner Losh
` (23 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Complete os-socket.h with setfib, bindat, connectat, and accept4.
Add bsd-socket.c with target_to_host_sockaddr, host_to_target_sockaddr,
and target_to_host_ip_mreq helper functions, and add it to the build.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/bsd-socket.c | 98 ++++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-socket.h | 85 +++++++++++++++++++++++++++++++++++++-
bsd-user/meson.build | 1 +
bsd-user/qemu-bsd.h | 8 ++++
4 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/bsd-user/bsd-socket.c b/bsd-user/bsd-socket.c
new file mode 100644
index 0000000000..cc45400e4c
--- /dev/null
+++ b/bsd-user/bsd-socket.c
@@ -0,0 +1,98 @@
+/*
+ * BSD socket system call related helpers
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "qemu.h"
+#include "qemu-bsd.h"
+
+/*
+ * socket conversion
+ */
+abi_long target_to_host_sockaddr(struct sockaddr *addr, abi_ulong target_addr,
+ socklen_t len)
+{
+ const socklen_t unix_maxlen = sizeof(struct sockaddr_un);
+ sa_family_t sa_family;
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(VERIFY_READ, target_addr, len, 1);
+ if (target_saddr == 0) {
+ return -TARGET_EFAULT;
+ }
+
+ sa_family = target_saddr->sa_family;
+
+ /*
+ * Oops. The caller might send a incomplete sun_path; sun_path
+ * must be terminated by \0 (see the manual page), but unfortunately
+ * it is quite common to specify sockaddr_un length as
+ * "strlen(x->sun_path)" while it should be "strlen(...) + 1". We will
+ * fix that here if needed.
+ */
+ if (target_saddr->sa_family == AF_UNIX) {
+ if (len < unix_maxlen && len > 0) {
+ char *cp = (char *)target_saddr;
+
+ if (cp[len - 1] && !cp[len]) {
+ len++;
+ }
+ }
+ if (len > unix_maxlen) {
+ len = unix_maxlen;
+ }
+ }
+
+ memcpy(addr, target_saddr, len);
+ addr->sa_family = sa_family; /* type uint8_t */
+ addr->sa_len = target_saddr->sa_len; /* type uint8_t */
+ unlock_user(target_saddr, target_addr, 0);
+
+ return 0;
+}
+
+abi_long host_to_target_sockaddr(abi_ulong target_addr, struct sockaddr *addr,
+ socklen_t len)
+{
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(VERIFY_WRITE, target_addr, len, 0);
+ if (target_saddr == 0) {
+ return -TARGET_EFAULT;
+ }
+ memcpy(target_saddr, addr, len);
+ target_saddr->sa_family = addr->sa_family; /* type uint8_t */
+ target_saddr->sa_len = addr->sa_len; /* type uint8_t */
+ unlock_user(target_saddr, target_addr, len);
+
+ return 0;
+}
+
+abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, abi_ulong target_addr,
+ socklen_t len)
+{
+ struct target_ip_mreqn *target_smreqn;
+
+ target_smreqn = lock_user(VERIFY_READ, target_addr, len, 1);
+ if (target_smreqn == 0) {
+ return -TARGET_EFAULT;
+ }
+ mreqn->imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr;
+ mreqn->imr_address.s_addr = target_smreqn->imr_address.s_addr;
+ if (len == sizeof(struct target_ip_mreqn)) {
+ mreqn->imr_ifindex = tswap32(target_smreqn->imr_ifindex);
+ }
+ unlock_user(target_smreqn, target_addr, 0);
+
+ return 0;
+}
+
diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h
index f8521780c1..6ad16f73b5 100644
--- a/bsd-user/freebsd/os-socket.h
+++ b/bsd-user/freebsd/os-socket.h
@@ -334,7 +334,7 @@ static inline abi_long do_bsd_setsockopt(int sockfd, int level, int optname,
break;
case TARGET_SO_SNDLOWAT:
- optname = SO_RCVLOWAT;
+ optname = SO_SNDLOWAT;
break;
case TARGET_SO_RCVLOWAT:
@@ -683,5 +683,88 @@ unimplemented:
return ret;
}
+/* setfib(2) */
+static inline abi_long do_freebsd_setfib(abi_long fib)
+{
+
+ return get_errno(setfib(fib));
+}
+
+/* bindat(2) */
+static inline abi_long do_freebsd_bindat(int fd, int sockfd,
+ abi_ulong target_addr, socklen_t addrlen)
+{
+ abi_long ret;
+ void *addr;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ addr = alloca(addrlen + 1);
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ return get_errno(bindat(fd, sockfd, addr, addrlen));
+}
+
+/* connectat(2) */
+static inline abi_long do_freebsd_connectat(int fd, int sockfd,
+ abi_ulong target_addr, socklen_t addrlen)
+{
+ abi_long ret;
+ void *addr;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ addr = alloca(addrlen);
+
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ return get_errno(connectat(fd, sockfd, addr, addrlen));
+}
+
+/* accept4(2) */
+static inline abi_long do_freebsd_accept4(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr, int flags)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (target_addr == 0) {
+ return get_errno(accept(fd, NULL, NULL));
+ }
+ /* return EINVAL if addrlen pointer is invalid */
+ if (get_user_u32(addrlen, target_addrlen_addr)) {
+ return -TARGET_EINVAL;
+ }
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) {
+ return -TARGET_EINVAL;
+ }
+ addr = alloca(addrlen);
+
+ ret = get_errno(accept4(fd, addr, &addrlen, flags));
+ if (!is_error(ret)) {
+ if (is_error(host_to_target_sockaddr(target_addr, addr, addrlen))) {
+ close(ret);
+ ret = -TARGET_EFAULT;
+ } else if (put_user_u32(addrlen, target_addrlen_addr)) {
+ close(ret);
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
#endif /* BSD_USER_FREEBSD_OS_SOCKET_H */
diff --git a/bsd-user/meson.build b/bsd-user/meson.build
index 2abcae5122..ab6ddf2e9f 100644
--- a/bsd-user/meson.build
+++ b/bsd-user/meson.build
@@ -11,6 +11,7 @@ bsd_user_ss.add(files(
'bsd-mem.c',
'bsd-misc.c',
'bsd-proc.c',
+ 'bsd-socket.c',
'bsdload.c',
'elfload.c',
'main.c',
diff --git a/bsd-user/qemu-bsd.h b/bsd-user/qemu-bsd.h
index f7c8338213..b4ca8268c9 100644
--- a/bsd-user/qemu-bsd.h
+++ b/bsd-user/qemu-bsd.h
@@ -32,6 +32,14 @@ int host_to_target_waitstatus(int status);
void h2g_rusage(const struct rusage *rusage,
struct target_freebsd_rusage *target_rusage);
+/* bsd-socket.c */
+abi_long target_to_host_sockaddr(struct sockaddr *addr, abi_ulong target_addr,
+ socklen_t len);
+abi_long host_to_target_sockaddr(abi_ulong target_addr, struct sockaddr *addr,
+ socklen_t len);
+abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, abi_ulong target_addr,
+ socklen_t len);
+
/* bsd-misc.c */
abi_long host_to_target_uuid(abi_ulong target_addr, struct uuid *host_uuid);
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 14/37] bsd-user: Add FreeBSD socket helpers and sockaddr conversion
2026-05-18 21:27 ` [PATCH v2 14/37] bsd-user: Add FreeBSD socket helpers and sockaddr conversion Warner Losh
@ 2026-05-22 23:29 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:29 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Complete os-socket.h with setfib, bindat, connectat, and accept4.
> Add bsd-socket.c with target_to_host_sockaddr, host_to_target_sockaddr,
> and target_to_host_ip_mreq helper functions, and add it to the build.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/bsd-socket.c | 98 ++++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-socket.h | 85 +++++++++++++++++++++++++++++++++++++-
> bsd-user/meson.build | 1 +
> bsd-user/qemu-bsd.h | 8 ++++
> 4 files changed, 191 insertions(+), 1 deletion(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (13 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 14/37] bsd-user: Add FreeBSD socket helpers and sockaddr conversion Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:31 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 16/37] bsd-user: Add socket system call dispatch Warner Losh
` (22 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans, Michal Meloun
Add target-to-host and host-to-target control message conversion
functions (t2h_freebsd_cmsg and h2t_freebsd_cmsg) for sendmsg/recvmsg
support, and add os-socket.c to the FreeBSD build.
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: Michal Meloun <mmel@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/meson.build | 8 +-
bsd-user/freebsd/os-socket.c | 234 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 240 insertions(+), 2 deletions(-)
diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build
index 38f2debf7e..0fc779749d 100644
--- a/bsd-user/freebsd/meson.build
+++ b/bsd-user/freebsd/meson.build
@@ -4,9 +4,13 @@ bsd_syscall_nr = custom_target('bsd-syscall-h',
command: [sh, meson.current_source_dir() / 'scripts/syscallhdr.sh', '@INPUT@', '@OUTPUT@', 'FREEBSD'])
bsd_user_ss.add(files(
- 'os-stat.c',
+ 'os-extattr.c',
'os-proc.c',
+ 'os-socket.c',
+ 'os-stat.c',
'os-sys.c',
- 'os-syscall.c'),
+ 'os-syscall.c',
+ 'os-thread.c',
+ 'os-time.c'),
bsd_syscall_nr
)
diff --git a/bsd-user/freebsd/os-socket.c b/bsd-user/freebsd/os-socket.c
new file mode 100644
index 0000000000..8eb728240d
--- /dev/null
+++ b/bsd-user/freebsd/os-socket.c
@@ -0,0 +1,234 @@
+/*
+ * FreeBSD socket related system call helpers
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+
+abi_long t2h_freebsd_cmsg(struct msghdr *msgh,
+ struct target_msghdr *target_msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ socklen_t msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg, *target_cmsg_start;
+ socklen_t space = 0;
+
+ msg_controllen = tswap32(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof(struct target_cmsghdr)) {
+ goto the_end;
+ }
+ target_cmsg_addr = tswapal(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
+ target_cmsg_start = target_cmsg;
+ if (!target_cmsg) {
+ return -TARGET_EFAULT;
+ }
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+ int len = (unsigned char *)(target_cmsg) +
+ tswap32(target_cmsg->cmsg_len) - (unsigned char *)target_data;
+
+ space += CMSG_SPACE(len);
+ if (space > msgh->msg_controllen) {
+ space -= CMSG_SPACE(len);
+ /*
+ * This is a QEMU bug, since we allocated the payload area ourselves
+ * (unlike overflow in host-to-target conversion, which is just the
+ * guest giving us a buffer that's too small). It can't happen for
+ * the payload types we currently support; if it becomes an issue in
+ * future we would need to improve our allocation strategy to
+ * something more intelligent than "twice the size of the target
+ * buffer we're reading from".
+ */
+ gemu_log("Host cmsg overflow\n");
+ break;
+ }
+
+ if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) {
+ cmsg->cmsg_level = SOL_SOCKET;
+ } else {
+ cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
+ }
+ cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
+ cmsg->cmsg_len = CMSG_LEN(len);
+
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = len / sizeof(int);
+
+ for (i = 0; i < numfds; i++) {
+ __get_user(fd[i], target_fd + i);
+ }
+ } else if ((cmsg->cmsg_level == SOL_SOCKET) &&
+ (cmsg->cmsg_type == SCM_TIMESTAMP) &&
+ (len == sizeof(struct target_freebsd_timeval))) {
+ /* copy struct timeval to host */
+ struct timeval *tv = (struct timeval *)data;
+ struct target_freebsd_timeval *target_tv =
+ (struct target_freebsd_timeval *)target_data;
+ __get_user(tv->tv_sec, &target_tv->tv_sec);
+ __get_user(tv->tv_usec, &target_tv->tv_usec);
+ } else {
+ gemu_log("Unsupported target ancillary data: %d/%d\n",
+ cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(data, target_data, len);
+ }
+
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
+ target_cmsg_start);
+ }
+ unlock_user(target_cmsg_start, target_cmsg_addr, 0);
+the_end:
+ msgh->msg_controllen = space;
+ return 0;
+}
+
+abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh,
+ struct msghdr *msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ socklen_t msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg, *target_cmsg_start;
+ socklen_t space = 0;
+
+ msg_controllen = tswap32(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof(struct target_cmsghdr)) {
+ goto the_end;
+ }
+ target_cmsg_addr = tswapal(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
+ target_cmsg_start = target_cmsg;
+ if (!target_cmsg) {
+ return -TARGET_EFAULT;
+ }
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+ int len = (unsigned char *)(cmsg) + cmsg->cmsg_len -
+ (unsigned char *)data;
+
+ int tgt_len, tgt_space;
+
+ /*
+ * We never copy a half-header but may copy half-data; this is Linux's
+ * behaviour in put_cmsg(). Note that truncation here is a guest problem
+ * (which we report to the guest via the CTRUNC bit), unlike truncation
+ * in target_to_host_cmsg, which is a QEMU bug.
+ */
+ if (msg_controllen < sizeof(struct target_cmsghdr)) {
+ target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
+ break;
+ }
+
+ if (cmsg->cmsg_level == SOL_SOCKET) {
+ target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET);
+ } else {
+ target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
+ }
+ target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
+
+ /*
+ * Payload types which need a different size of payload on the target
+ * must adjust tgt_len here.
+ */
+ tgt_len = len;
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ switch (cmsg->cmsg_type) {
+ case SCM_TIMESTAMP:
+ tgt_len = sizeof(struct target_freebsd_timeval);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) {
+ target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
+ tgt_len = msg_controllen - sizeof(struct target_cmsghdr);
+ }
+
+ /*
+ * We must now copy-and-convert len bytes of payload into tgt_len bytes
+ * of destination space. Bear in mind that in both source and
+ * destination we may be dealing with a truncated value!
+ */
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ switch (cmsg->cmsg_type) {
+ case SCM_RIGHTS:
+ {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = tgt_len / sizeof(int);
+
+ for (i = 0; i < numfds; i++) {
+ __put_user(fd[i], target_fd + i);
+ }
+ break;
+ }
+ case SCM_TIMESTAMP:
+ {
+ struct timeval *tv = (struct timeval *)data;
+ struct target_freebsd_timeval *target_tv =
+ (struct target_freebsd_timeval *)target_data;
+
+ if (len != sizeof(struct timeval) ||
+ tgt_len != sizeof(struct target_freebsd_timeval)) {
+ goto unimplemented;
+ }
+
+ /* copy struct timeval to target */
+ __put_user(tv->tv_sec, &target_tv->tv_sec);
+ __put_user(tv->tv_usec, &target_tv->tv_usec);
+ break;
+ }
+ default:
+ goto unimplemented;
+ }
+ break;
+ default:
+ unimplemented:
+ gemu_log("Unsupported host ancillary data: %d/%d\n",
+ cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(target_data, data, MIN(len, tgt_len));
+ if (tgt_len > len) {
+ memset(target_data + len, 0, tgt_len - len);
+ }
+ }
+
+ target_cmsg->cmsg_len = tswap32(TARGET_CMSG_LEN(tgt_len));
+ tgt_space = TARGET_CMSG_SPACE(tgt_len);
+ if (msg_controllen < tgt_space) {
+ tgt_space = msg_controllen;
+ }
+ msg_controllen -= tgt_space;
+ space += tgt_space;
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
+ target_cmsg_start);
+ }
+ unlock_user(target_cmsg_start, target_cmsg_addr, space);
+the_end:
+ target_msgh->msg_controllen = tswap32(space);
+ return 0;
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions
2026-05-18 21:27 ` [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions Warner Losh
@ 2026-05-22 23:31 ` Pierrick Bouvier
2026-05-23 4:35 ` Warner Losh
0 siblings, 1 reply; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:31 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Mikaël Urankar, Michal Meloun
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add target-to-host and host-to-target control message conversion
> functions (t2h_freebsd_cmsg and h2t_freebsd_cmsg) for sendmsg/recvmsg
> support, and add os-socket.c to the FreeBSD build.
>
> 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: Michal Meloun <mmel@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/freebsd/meson.build | 8 +-
> bsd-user/freebsd/os-socket.c | 234 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 240 insertions(+), 2 deletions(-)
>
> diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build
> index 38f2debf7e..0fc779749d 100644
> --- a/bsd-user/freebsd/meson.build
> +++ b/bsd-user/freebsd/meson.build
> @@ -4,9 +4,13 @@ bsd_syscall_nr = custom_target('bsd-syscall-h',
> command: [sh, meson.current_source_dir() / 'scripts/syscallhdr.sh', '@INPUT@', '@OUTPUT@', 'FREEBSD'])
>
> bsd_user_ss.add(files(
> - 'os-stat.c',
> + 'os-extattr.c',
> 'os-proc.c',
> + 'os-socket.c',
> + 'os-stat.c',
> 'os-sys.c',
> - 'os-syscall.c'),
> + 'os-syscall.c',
> + 'os-thread.c',
> + 'os-time.c'),
> bsd_syscall_nr
> )
Most of additions here seem to belong to previous patches.
> diff --git a/bsd-user/freebsd/os-socket.c b/bsd-user/freebsd/os-socket.c
> new file mode 100644
> index 0000000000..8eb728240d
> --- /dev/null
> +++ b/bsd-user/freebsd/os-socket.c
> @@ -0,0 +1,234 @@
> +/*
> + * FreeBSD socket related system call helpers
> + *
> + * Copyright (c) 2013 Stacey D. Son
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#include "qemu/osdep.h"
> +
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +
> +#include "qemu.h"
> +#include "qemu-os.h"
> +
> +abi_long t2h_freebsd_cmsg(struct msghdr *msgh,
> + struct target_msghdr *target_msgh)
> +{
> + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
> + socklen_t msg_controllen;
> + abi_ulong target_cmsg_addr;
> + struct target_cmsghdr *target_cmsg, *target_cmsg_start;
> + socklen_t space = 0;
> +
> + msg_controllen = tswap32(target_msgh->msg_controllen);
> + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> + goto the_end;
> + }
> + target_cmsg_addr = tswapal(target_msgh->msg_control);
> + target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
> + target_cmsg_start = target_cmsg;
> + if (!target_cmsg) {
> + return -TARGET_EFAULT;
> + }
> +
> + while (cmsg && target_cmsg) {
> + void *data = CMSG_DATA(cmsg);
> + void *target_data = TARGET_CMSG_DATA(target_cmsg);
> + int len = (unsigned char *)(target_cmsg) +
> + tswap32(target_cmsg->cmsg_len) - (unsigned char *)target_data;
> +
> + space += CMSG_SPACE(len);
> + if (space > msgh->msg_controllen) {
> + space -= CMSG_SPACE(len);
> + /*
> + * This is a QEMU bug, since we allocated the payload area ourselves
> + * (unlike overflow in host-to-target conversion, which is just the
> + * guest giving us a buffer that's too small). It can't happen for
> + * the payload types we currently support; if it becomes an issue in
> + * future we would need to improve our allocation strategy to
> + * something more intelligent than "twice the size of the target
> + * buffer we're reading from".
> + */
> + gemu_log("Host cmsg overflow\n");
> + break;
> + }
> +
> + if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) {
> + cmsg->cmsg_level = SOL_SOCKET;
> + } else {
> + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
> + }
> + cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
> + cmsg->cmsg_len = CMSG_LEN(len);
> +
> + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
> + int *fd = (int *)data;
> + int *target_fd = (int *)target_data;
> + int i, numfds = len / sizeof(int);
> +
> + for (i = 0; i < numfds; i++) {
> + __get_user(fd[i], target_fd + i);
> + }
> + } else if ((cmsg->cmsg_level == SOL_SOCKET) &&
> + (cmsg->cmsg_type == SCM_TIMESTAMP) &&
> + (len == sizeof(struct target_freebsd_timeval))) {
> + /* copy struct timeval to host */
> + struct timeval *tv = (struct timeval *)data;
> + struct target_freebsd_timeval *target_tv =
> + (struct target_freebsd_timeval *)target_data;
> + __get_user(tv->tv_sec, &target_tv->tv_sec);
> + __get_user(tv->tv_usec, &target_tv->tv_usec);
> + } else {
> + gemu_log("Unsupported target ancillary data: %d/%d\n",
> + cmsg->cmsg_level, cmsg->cmsg_type);
> + memcpy(data, target_data, len);
> + }
> +
> + cmsg = CMSG_NXTHDR(msgh, cmsg);
> + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
> + target_cmsg_start);
> + }
> + unlock_user(target_cmsg_start, target_cmsg_addr, 0);
> +the_end:
> + msgh->msg_controllen = space;
> + return 0;
> +}
> +
> +abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh,
> + struct msghdr *msgh)
> +{
> + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
> + socklen_t msg_controllen;
> + abi_ulong target_cmsg_addr;
> + struct target_cmsghdr *target_cmsg, *target_cmsg_start;
> + socklen_t space = 0;
> +
> + msg_controllen = tswap32(target_msgh->msg_controllen);
> + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> + goto the_end;
> + }
> + target_cmsg_addr = tswapal(target_msgh->msg_control);
> + target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
> + target_cmsg_start = target_cmsg;
> + if (!target_cmsg) {
> + return -TARGET_EFAULT;
> + }
> +
> + while (cmsg && target_cmsg) {
> + void *data = CMSG_DATA(cmsg);
> + void *target_data = TARGET_CMSG_DATA(target_cmsg);
> + int len = (unsigned char *)(cmsg) + cmsg->cmsg_len -
> + (unsigned char *)data;
> +
> + int tgt_len, tgt_space;
> +
> + /*
> + * We never copy a half-header but may copy half-data; this is Linux's
> + * behaviour in put_cmsg(). Note that truncation here is a guest problem
> + * (which we report to the guest via the CTRUNC bit), unlike truncation
> + * in target_to_host_cmsg, which is a QEMU bug.
> + */
> + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> + target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
> + break;
> + }
> +
> + if (cmsg->cmsg_level == SOL_SOCKET) {
> + target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET);
> + } else {
> + target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
> + }
> + target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
> +
> + /*
> + * Payload types which need a different size of payload on the target
> + * must adjust tgt_len here.
> + */
> + tgt_len = len;
> + switch (cmsg->cmsg_level) {
> + case SOL_SOCKET:
> + switch (cmsg->cmsg_type) {
> + case SCM_TIMESTAMP:
> + tgt_len = sizeof(struct target_freebsd_timeval);
> + break;
> + default:
> + break;
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) {
> + target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
> + tgt_len = msg_controllen - sizeof(struct target_cmsghdr);
> + }
> +
> + /*
> + * We must now copy-and-convert len bytes of payload into tgt_len bytes
> + * of destination space. Bear in mind that in both source and
> + * destination we may be dealing with a truncated value!
> + */
> + switch (cmsg->cmsg_level) {
> + case SOL_SOCKET:
> + switch (cmsg->cmsg_type) {
> + case SCM_RIGHTS:
> + {
> + int *fd = (int *)data;
> + int *target_fd = (int *)target_data;
> + int i, numfds = tgt_len / sizeof(int);
> +
> + for (i = 0; i < numfds; i++) {
> + __put_user(fd[i], target_fd + i);
> + }
> + break;
> + }
> + case SCM_TIMESTAMP:
> + {
> + struct timeval *tv = (struct timeval *)data;
> + struct target_freebsd_timeval *target_tv =
> + (struct target_freebsd_timeval *)target_data;
> +
> + if (len != sizeof(struct timeval) ||
> + tgt_len != sizeof(struct target_freebsd_timeval)) {
> + goto unimplemented;
> + }
> +
> + /* copy struct timeval to target */
> + __put_user(tv->tv_sec, &target_tv->tv_sec);
> + __put_user(tv->tv_usec, &target_tv->tv_usec);
> + break;
> + }
> + default:
> + goto unimplemented;
> + }
> + break;
> + default:
> + unimplemented:
> + gemu_log("Unsupported host ancillary data: %d/%d\n",
> + cmsg->cmsg_level, cmsg->cmsg_type);
> + memcpy(target_data, data, MIN(len, tgt_len));
> + if (tgt_len > len) {
> + memset(target_data + len, 0, tgt_len - len);
> + }
> + }
> +
> + target_cmsg->cmsg_len = tswap32(TARGET_CMSG_LEN(tgt_len));
> + tgt_space = TARGET_CMSG_SPACE(tgt_len);
> + if (msg_controllen < tgt_space) {
> + tgt_space = msg_controllen;
> + }
> + msg_controllen -= tgt_space;
> + space += tgt_space;
> + cmsg = CMSG_NXTHDR(msgh, cmsg);
> + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
> + target_cmsg_start);
> + }
> + unlock_user(target_cmsg_start, target_cmsg_addr, space);
> +the_end:
> + target_msgh->msg_controllen = tswap32(space);
> + return 0;
> +}
>
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions
2026-05-22 23:31 ` Pierrick Bouvier
@ 2026-05-23 4:35 ` Warner Losh
0 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-23 4:35 UTC (permalink / raw)
To: Pierrick Bouvier
Cc: qemu-devel, Kyle Evans, Stacey Son, Mikaël Urankar,
Michal Meloun
[-- Attachment #1: Type: text/plain, Size: 11703 bytes --]
On Fri, May 22, 2026 at 5:31 PM Pierrick Bouvier <
pierrick.bouvier@oss.qualcomm.com> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote:
> > Add target-to-host and host-to-target control message conversion
> > functions (t2h_freebsd_cmsg and h2t_freebsd_cmsg) for sendmsg/recvmsg
> > support, and add os-socket.c to the FreeBSD build.
> >
> > 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: Michal Meloun <mmel@FreeBSD.org>
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > Assisted-by: Claude Opus 4.6 (1M context)
> > ---
> > bsd-user/freebsd/meson.build | 8 +-
> > bsd-user/freebsd/os-socket.c | 234
> +++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 240 insertions(+), 2 deletions(-)
> >
> > diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build
> > index 38f2debf7e..0fc779749d 100644
> > --- a/bsd-user/freebsd/meson.build
> > +++ b/bsd-user/freebsd/meson.build
> > @@ -4,9 +4,13 @@ bsd_syscall_nr = custom_target('bsd-syscall-h',
> > command: [sh, meson.current_source_dir() / 'scripts/syscallhdr.sh',
> '@INPUT@', '@OUTPUT@', 'FREEBSD'])
> >
> > bsd_user_ss.add(files(
> > - 'os-stat.c',
> > + 'os-extattr.c',
> > 'os-proc.c',
> > + 'os-socket.c',
> > + 'os-stat.c',
> > 'os-sys.c',
> > - 'os-syscall.c'),
> > + 'os-syscall.c',
> > + 'os-thread.c',
> > + 'os-time.c'),
> > bsd_syscall_nr
> > )
>
> Most of additions here seem to belong to previous patches.
>
I'll try to move these backwards in the series. I may have to reorder some
things
as well, since this is a known good compile point. It's worth trying.
Warner
> > diff --git a/bsd-user/freebsd/os-socket.c b/bsd-user/freebsd/os-socket.c
> > new file mode 100644
> > index 0000000000..8eb728240d
> > --- /dev/null
> > +++ b/bsd-user/freebsd/os-socket.c
> > @@ -0,0 +1,234 @@
> > +/*
> > + * FreeBSD socket related system call helpers
> > + *
> > + * Copyright (c) 2013 Stacey D. Son
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +#include "qemu/osdep.h"
> > +
> > +#include <sys/types.h>
> > +#include <sys/socket.h>
> > +#include <netinet/in.h>
> > +
> > +#include "qemu.h"
> > +#include "qemu-os.h"
> > +
> > +abi_long t2h_freebsd_cmsg(struct msghdr *msgh,
> > + struct target_msghdr *target_msgh)
> > +{
> > + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
> > + socklen_t msg_controllen;
> > + abi_ulong target_cmsg_addr;
> > + struct target_cmsghdr *target_cmsg, *target_cmsg_start;
> > + socklen_t space = 0;
> > +
> > + msg_controllen = tswap32(target_msgh->msg_controllen);
> > + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> > + goto the_end;
> > + }
> > + target_cmsg_addr = tswapal(target_msgh->msg_control);
> > + target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr,
> msg_controllen, 1);
> > + target_cmsg_start = target_cmsg;
> > + if (!target_cmsg) {
> > + return -TARGET_EFAULT;
> > + }
> > +
> > + while (cmsg && target_cmsg) {
> > + void *data = CMSG_DATA(cmsg);
> > + void *target_data = TARGET_CMSG_DATA(target_cmsg);
> > + int len = (unsigned char *)(target_cmsg) +
> > + tswap32(target_cmsg->cmsg_len) - (unsigned char
> *)target_data;
> > +
> > + space += CMSG_SPACE(len);
> > + if (space > msgh->msg_controllen) {
> > + space -= CMSG_SPACE(len);
> > + /*
> > + * This is a QEMU bug, since we allocated the payload area
> ourselves
> > + * (unlike overflow in host-to-target conversion, which is
> just the
> > + * guest giving us a buffer that's too small). It can't
> happen for
> > + * the payload types we currently support; if it becomes an
> issue in
> > + * future we would need to improve our allocation strategy
> to
> > + * something more intelligent than "twice the size of the
> target
> > + * buffer we're reading from".
> > + */
> > + gemu_log("Host cmsg overflow\n");
> > + break;
> > + }
> > +
> > + if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) {
> > + cmsg->cmsg_level = SOL_SOCKET;
> > + } else {
> > + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
> > + }
> > + cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
> > + cmsg->cmsg_len = CMSG_LEN(len);
> > +
> > + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type ==
> SCM_RIGHTS) {
> > + int *fd = (int *)data;
> > + int *target_fd = (int *)target_data;
> > + int i, numfds = len / sizeof(int);
> > +
> > + for (i = 0; i < numfds; i++) {
> > + __get_user(fd[i], target_fd + i);
> > + }
> > + } else if ((cmsg->cmsg_level == SOL_SOCKET) &&
> > + (cmsg->cmsg_type == SCM_TIMESTAMP) &&
> > + (len == sizeof(struct target_freebsd_timeval))) {
> > + /* copy struct timeval to host */
> > + struct timeval *tv = (struct timeval *)data;
> > + struct target_freebsd_timeval *target_tv =
> > + (struct target_freebsd_timeval *)target_data;
> > + __get_user(tv->tv_sec, &target_tv->tv_sec);
> > + __get_user(tv->tv_usec, &target_tv->tv_usec);
> > + } else {
> > + gemu_log("Unsupported target ancillary data: %d/%d\n",
> > + cmsg->cmsg_level, cmsg->cmsg_type);
> > + memcpy(data, target_data, len);
> > + }
> > +
> > + cmsg = CMSG_NXTHDR(msgh, cmsg);
> > + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
> > + target_cmsg_start);
> > + }
> > + unlock_user(target_cmsg_start, target_cmsg_addr, 0);
> > +the_end:
> > + msgh->msg_controllen = space;
> > + return 0;
> > +}
> > +
> > +abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh,
> > + struct msghdr *msgh)
> > +{
> > + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
> > + socklen_t msg_controllen;
> > + abi_ulong target_cmsg_addr;
> > + struct target_cmsghdr *target_cmsg, *target_cmsg_start;
> > + socklen_t space = 0;
> > +
> > + msg_controllen = tswap32(target_msgh->msg_controllen);
> > + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> > + goto the_end;
> > + }
> > + target_cmsg_addr = tswapal(target_msgh->msg_control);
> > + target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr,
> msg_controllen, 0);
> > + target_cmsg_start = target_cmsg;
> > + if (!target_cmsg) {
> > + return -TARGET_EFAULT;
> > + }
> > +
> > + while (cmsg && target_cmsg) {
> > + void *data = CMSG_DATA(cmsg);
> > + void *target_data = TARGET_CMSG_DATA(target_cmsg);
> > + int len = (unsigned char *)(cmsg) + cmsg->cmsg_len -
> > + (unsigned char *)data;
> > +
> > + int tgt_len, tgt_space;
> > +
> > + /*
> > + * We never copy a half-header but may copy half-data; this is
> Linux's
> > + * behaviour in put_cmsg(). Note that truncation here is a
> guest problem
> > + * (which we report to the guest via the CTRUNC bit), unlike
> truncation
> > + * in target_to_host_cmsg, which is a QEMU bug.
> > + */
> > + if (msg_controllen < sizeof(struct target_cmsghdr)) {
> > + target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
> > + break;
> > + }
> > +
> > + if (cmsg->cmsg_level == SOL_SOCKET) {
> > + target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET);
> > + } else {
> > + target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
> > + }
> > + target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
> > +
> > + /*
> > + * Payload types which need a different size of payload on the
> target
> > + * must adjust tgt_len here.
> > + */
> > + tgt_len = len;
> > + switch (cmsg->cmsg_level) {
> > + case SOL_SOCKET:
> > + switch (cmsg->cmsg_type) {
> > + case SCM_TIMESTAMP:
> > + tgt_len = sizeof(struct target_freebsd_timeval);
> > + break;
> > + default:
> > + break;
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) {
> > + target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
> > + tgt_len = msg_controllen - sizeof(struct target_cmsghdr);
> > + }
> > +
> > + /*
> > + * We must now copy-and-convert len bytes of payload into
> tgt_len bytes
> > + * of destination space. Bear in mind that in both source and
> > + * destination we may be dealing with a truncated value!
> > + */
> > + switch (cmsg->cmsg_level) {
> > + case SOL_SOCKET:
> > + switch (cmsg->cmsg_type) {
> > + case SCM_RIGHTS:
> > + {
> > + int *fd = (int *)data;
> > + int *target_fd = (int *)target_data;
> > + int i, numfds = tgt_len / sizeof(int);
> > +
> > + for (i = 0; i < numfds; i++) {
> > + __put_user(fd[i], target_fd + i);
> > + }
> > + break;
> > + }
> > + case SCM_TIMESTAMP:
> > + {
> > + struct timeval *tv = (struct timeval *)data;
> > + struct target_freebsd_timeval *target_tv =
> > + (struct target_freebsd_timeval *)target_data;
> > +
> > + if (len != sizeof(struct timeval) ||
> > + tgt_len != sizeof(struct target_freebsd_timeval)) {
> > + goto unimplemented;
> > + }
> > +
> > + /* copy struct timeval to target */
> > + __put_user(tv->tv_sec, &target_tv->tv_sec);
> > + __put_user(tv->tv_usec, &target_tv->tv_usec);
> > + break;
> > + }
> > + default:
> > + goto unimplemented;
> > + }
> > + break;
> > + default:
> > + unimplemented:
> > + gemu_log("Unsupported host ancillary data: %d/%d\n",
> > + cmsg->cmsg_level, cmsg->cmsg_type);
> > + memcpy(target_data, data, MIN(len, tgt_len));
> > + if (tgt_len > len) {
> > + memset(target_data + len, 0, tgt_len - len);
> > + }
> > + }
> > +
> > + target_cmsg->cmsg_len = tswap32(TARGET_CMSG_LEN(tgt_len));
> > + tgt_space = TARGET_CMSG_SPACE(tgt_len);
> > + if (msg_controllen < tgt_space) {
> > + tgt_space = msg_controllen;
> > + }
> > + msg_controllen -= tgt_space;
> > + space += tgt_space;
> > + cmsg = CMSG_NXTHDR(msgh, cmsg);
> > + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
> > + target_cmsg_start);
> > + }
> > + unlock_user(target_cmsg_start, target_cmsg_addr, space);
> > +the_end:
> > + target_msgh->msg_controllen = tswap32(space);
> > + return 0;
> > +}
> >
>
>
[-- Attachment #2: Type: text/html, Size: 15172 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 16/37] bsd-user: Add socket system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (14 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 15/37] bsd-user: Add os-socket.c with cmsg conversion functions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:31 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 17/37] bsd-user: Add os-time.h with clock and time-of-day functions Warner Losh
` (21 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Wire up socket-related system calls in the FreeBSD syscall dispatcher:
accept, accept4, bind, bindat, connect, connectat, getpeername,
getsockname, getsockopt, setsockopt, listen, recvfrom, recvmsg,
sendmsg, sendto, socket, socketpair, shutdown, and setfib.
Signed-off-by: Stacey Son <sson@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-syscall.c | 79 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 46fee46336..5d3a66e360 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -1073,6 +1073,85 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd_pdkill(arg1, arg2);
break;
+ /*
+ * socket related system calls
+ */
+ case TARGET_FREEBSD_NR_accept: /* accept(2) */
+ ret = do_bsd_accept(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_accept4: /* accept4(2) */
+ ret = do_freebsd_accept4(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_bind: /* bind(2) */
+ ret = do_bsd_bind(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_bindat: /* bindat(2) */
+ ret = do_freebsd_bindat(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_connect: /* connect(2) */
+ ret = do_bsd_connect(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_connectat: /* connectat(2) */
+ ret = do_freebsd_connectat(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_getpeername: /* getpeername(2) */
+ ret = do_bsd_getpeername(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getsockname: /* getsockname(2) */
+ ret = do_bsd_getsockname(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getsockopt: /* getsockopt(2) */
+ ret = do_bsd_getsockopt(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_setsockopt: /* setsockopt(2) */
+ ret = do_bsd_setsockopt(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_listen: /* listen(2) */
+ ret = get_errno(listen(arg1, arg2));
+ break;
+
+ case TARGET_FREEBSD_NR_recvfrom: /* recvfrom(2) */
+ ret = do_bsd_recvfrom(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_recvmsg: /* recvmsg(2) */
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 0);
+ break;
+
+ case TARGET_FREEBSD_NR_sendmsg: /* sendmsg(2) */
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 1);
+ break;
+
+ case TARGET_FREEBSD_NR_sendto: /* sendto(2) */
+ ret = do_bsd_sendto(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_socket: /* socket(2) */
+ ret = do_bsd_socket(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_socketpair: /* socketpair(2) */
+ ret = do_bsd_socketpair(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_shutdown: /* shutdown(2) */
+ ret = do_bsd_shutdown(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_setfib: /* setfib(2) */
+ ret = do_freebsd_setfib(arg1);
+ break;
+
/*
* sys{ctl, arch, call}
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 16/37] bsd-user: Add socket system call dispatch
2026-05-18 21:27 ` [PATCH v2 16/37] bsd-user: Add socket system call dispatch Warner Losh
@ 2026-05-22 23:31 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:31 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Wire up socket-related system calls in the FreeBSD syscall dispatcher:
> accept, accept4, bind, bindat, connect, connectat, getpeername,
> getsockname, getsockopt, setsockopt, listen, recvfrom, recvmsg,
> sendmsg, sendto, socket, socketpair, shutdown, and setfib.
>
> Signed-off-by: Stacey Son <sson@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-syscall.c | 79 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 79 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 17/37] bsd-user: Add os-time.h with clock and time-of-day functions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (15 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 16/37] bsd-user: Add socket system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:32 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 18/37] bsd-user: Add utimes, futimes, and ktimer functions to os-time.h Warner Losh
` (20 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Sean Bruno, Kyle Evans
Add the first part of FreeBSD time support: nanosleep, clock_nanosleep,
clock_gettime, clock_settime, clock_getres, gettimeofday, settimeofday,
adjtime, ntp_adjtime, and ntp_gettime. Also add safe_syscall wrappers
for nanosleep, clock_nanosleep, and kevent.
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-syscall.c | 9 ++
bsd-user/freebsd/os-time.h | 221 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 230 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 5d3a66e360..6fbd2da261 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -89,6 +89,15 @@ safe_syscall6(ssize_t, sendto, int, fd, const void *, buf, size_t, len, int,
safe_syscall3(ssize_t, recvmsg, int, s, struct msghdr *, msg, int, flags);
safe_syscall3(ssize_t, sendmsg, int, s, const struct msghdr *, msg, int, flags);
+/* used in os-time */
+safe_syscall2(int, nanosleep, const struct timespec *, rqtp, struct timespec *,
+ rmtp);
+safe_syscall4(int, clock_nanosleep, clockid_t, clock_id, int, flags,
+ const struct timespec *, rqtp, struct timespec *, rmtp);
+safe_syscall6(int, kevent, int, kq, const struct kevent *, changelist,
+ int, nchanges, struct kevent *, eventlist, int, nevents,
+ const struct timespec *, timeout);
+
/* used in os-proc */
safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
struct rusage *, rusage);
diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
new file mode 100644
index 0000000000..16b71e6234
--- /dev/null
+++ b/bsd-user/freebsd/os-time.h
@@ -0,0 +1,221 @@
+/*
+ * FreeBSD time related system call shims
+ *
+ * Copyright (c) 2013-2015 Stacey Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef FREEBSD_OS_TIME_H
+#define FREEBSD_OS_TIME_H
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/select.h>
+#include <sys/timex.h>
+#include <poll.h>
+#include <signal.h>
+#include <time.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+
+#include "bsd-socket.h"
+
+int safe_clock_nanosleep(clockid_t clock_id, int flags,
+ const struct timespec *rqtp, struct timespec *rmtp);
+int safe_nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+int safe_kevent(int, const struct kevent *, int, struct kevent *, int,
+ const struct timespec *);
+
+int __sys_ktimer_create(clockid_t, struct sigevent *restrict,
+ int *restrict);
+int __sys_ktimer_gettime(int, struct itimerspec *);
+int __sys_ktimer_settime(int, int, const struct itimerspec *restrict,
+ struct itimerspec *restrict);
+int __sys_ktimer_delete(int);
+
+/* nanosleep(2) */
+static inline abi_long do_freebsd_nanosleep(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ struct timespec req, rem;
+
+ ret = t2h_freebsd_timespec(&req, arg1);
+ if (!is_error(ret)) {
+ ret = get_errno(safe_nanosleep(&req, &rem));
+ if (ret == -TARGET_EINTR && arg2) {
+ ret = h2t_freebsd_timespec(arg2, &rem);
+ }
+ }
+
+ return ret;
+}
+
+/* clock_nanosleep(2) */
+static inline abi_long do_freebsd_clock_nanosleep(abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4)
+{
+ struct timespec req, rem;
+ abi_long ret;
+ int clkid, flags;
+
+ clkid = arg1;
+ /* XXX Translate? */
+ flags = arg2;
+ ret = t2h_freebsd_timespec(&req, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(safe_clock_nanosleep(clkid, flags, &req, &rem));
+ if (ret == -TARGET_EINTR && arg4) {
+ h2t_freebsd_timespec(arg4, &rem);
+ }
+ }
+
+ return ret;
+}
+
+/* clock_gettime(2) */
+static inline abi_long do_freebsd_clock_gettime(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ struct timespec ts;
+
+ ret = get_errno(clock_gettime(arg1, &ts));
+ if (!is_error(ret)) {
+ if (h2t_freebsd_timespec(arg2, &ts)) {
+ return -TARGET_EFAULT;
+ }
+ }
+
+ return ret;
+}
+
+/* clock_settime(2) */
+static inline abi_long do_freebsd_clock_settime(abi_long arg1, abi_long arg2)
+{
+ struct timespec ts;
+
+ if (t2h_freebsd_timespec(&ts, arg2) != 0) {
+ return -TARGET_EFAULT;
+ }
+
+ return get_errno(clock_settime(arg1, &ts));
+}
+
+/* clock_getres(2) */
+static inline abi_long do_freebsd_clock_getres(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ struct timespec ts;
+
+ ret = get_errno(clock_getres(arg1, &ts));
+ if (!is_error(ret)) {
+ if (h2t_freebsd_timespec(arg2, &ts)) {
+ return -TARGET_EFAULT;
+ }
+ }
+
+ return ret;
+}
+
+/* gettimeofday(2) */
+static inline abi_long do_freebsd_gettimeofday(abi_ulong arg1, abi_ulong arg2)
+{
+ abi_long ret;
+ struct timeval tv;
+ struct timezone tz, *target_tz; /* XXX */
+
+ if (arg2 != 0) {
+ if (!lock_user_struct(VERIFY_READ, target_tz, arg2, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(tz.tz_minuteswest, &target_tz->tz_minuteswest);
+ __get_user(tz.tz_dsttime, &target_tz->tz_dsttime);
+ unlock_user_struct(target_tz, arg2, 1);
+ }
+ ret = get_errno(gettimeofday(&tv, arg2 != 0 ? &tz : NULL));
+ if (!is_error(ret)) {
+ if (h2t_freebsd_timeval(&tv, arg1)) {
+ return -TARGET_EFAULT;
+ }
+ }
+
+ return ret;
+}
+
+/* settimeofday(2) */
+static inline abi_long do_freebsd_settimeofday(abi_long arg1, abi_long arg2)
+{
+ struct timeval tv;
+ struct timezone tz, *target_tz; /* XXX */
+
+ if (arg2 != 0) {
+ if (!lock_user_struct(VERIFY_READ, target_tz, arg2, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(tz.tz_minuteswest, &target_tz->tz_minuteswest);
+ __get_user(tz.tz_dsttime, &target_tz->tz_dsttime);
+ unlock_user_struct(target_tz, arg2, 1);
+ }
+ if (t2h_freebsd_timeval(&tv, arg1)) {
+ return -TARGET_EFAULT;
+ }
+
+ return get_errno(settimeofday(&tv, arg2 != 0 ? &tz : NULL));
+}
+
+/* adjtime(2) */
+static inline abi_long do_freebsd_adjtime(abi_ulong target_delta_addr,
+ abi_ulong target_old_addr)
+{
+ abi_long ret;
+ struct timeval host_delta, host_old;
+
+ ret = t2h_freebsd_timeval(&host_delta, target_delta_addr);
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ if (target_old_addr) {
+ ret = get_errno(adjtime(&host_delta, &host_old));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = h2t_freebsd_timeval(&host_old, target_old_addr);
+ } else {
+ ret = get_errno(adjtime(&host_delta, NULL));
+ }
+
+ return ret;
+}
+
+/* ntp_adjtime(2) */
+static inline abi_long do_freebsd_ntp_adjtime(abi_ulong target_tx_addr)
+{
+ abi_long ret;
+ struct timex host_tx;
+
+ ret = t2h_freebsd_timex(&host_tx, target_tx_addr);
+ if (ret == 0) {
+ ret = get_errno(ntp_adjtime(&host_tx));
+ }
+
+ return ret;
+}
+
+/* ntp_gettime(2) */
+static inline abi_long do_freebsd_ntp_gettime(abi_ulong target_ntv_addr)
+{
+ abi_long ret;
+ struct ntptimeval host_ntv;
+
+ ret = get_errno(ntp_gettime(&host_ntv));
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_ntptimeval(target_ntv_addr, &host_ntv);
+ }
+
+ return ret;
+}
+
+
+
+#endif /* FREEBSD_OS_TIME_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 17/37] bsd-user: Add os-time.h with clock and time-of-day functions
2026-05-18 21:27 ` [PATCH v2 17/37] bsd-user: Add os-time.h with clock and time-of-day functions Warner Losh
@ 2026-05-22 23:32 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:32 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Mikaël Urankar, Sean Bruno
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add the first part of FreeBSD time support: nanosleep, clock_nanosleep,
> clock_gettime, clock_settime, clock_getres, gettimeofday, settimeofday,
> adjtime, ntp_adjtime, and ntp_gettime. Also add safe_syscall wrappers
> for nanosleep, clock_nanosleep, and kevent.
>
> 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-syscall.c | 9 ++
> bsd-user/freebsd/os-time.h | 221 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 230 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 18/37] bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (16 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 17/37] bsd-user: Add os-time.h with clock and time-of-day functions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:38 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll " Warner Losh
` (19 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
Add time-related system call shims: utimes, lutimes, futimes,
futimesat, ktimer_create, ktimer_delete, ktimer_settime, and
ktimer_gettime.
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 | 210 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 210 insertions(+)
diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
index 16b71e6234..05fa043442 100644
--- a/bsd-user/freebsd/os-time.h
+++ b/bsd-user/freebsd/os-time.h
@@ -217,5 +217,215 @@ static inline abi_long do_freebsd_ntp_gettime(abi_ulong target_ntv_addr)
}
+/* utimes(2) */
+static inline abi_long do_freebsd_utimes(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ void *p;
+ struct timeval *tvp, tv[2];
+
+ if (arg2 != 0) {
+ if (t2h_freebsd_timeval(&tv[0], arg2) ||
+ t2h_freebsd_timeval(&tv[1], arg2 +
+ sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(utimes(p, tvp));
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* lutimes(2) */
+static inline abi_long do_freebsd_lutimes(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ void *p;
+ struct timeval *tvp, tv[2];
+
+ if (arg2 != 0) {
+ if (t2h_freebsd_timeval(&tv[0], arg2) ||
+ t2h_freebsd_timeval(&tv[1], arg2 +
+ sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(lutimes(p, tvp));
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* futimes(2) */
+static inline abi_long do_freebsd_futimes(abi_long arg1, abi_long arg2)
+{
+ struct timeval *tvp, tv[2];
+
+ if (arg2 != 0) {
+ if (t2h_freebsd_timeval(&tv[0], arg2) ||
+ t2h_freebsd_timeval(&tv[1], arg2 +
+ sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+
+ return get_errno(futimes(arg1, tvp));
+}
+
+/* futimesat(2) */
+static inline abi_long do_freebsd_futimesat(abi_long arg1, abi_long arg2,
+ abi_long arg3)
+{
+ abi_long ret;
+ void *p;
+ struct timeval *tvp, tv[2];
+
+ if (arg3 != 0) {
+ if (t2h_freebsd_timeval(&tv[0], arg3) ||
+ t2h_freebsd_timeval(&tv[1], arg3 +
+ sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+
+ p = lock_user_string(arg2);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(futimesat(arg1, p, tvp));
+ unlock_user(p, arg2, 0);
+
+ return ret;
+}
+
+/* timer_create(2) */
+static inline abi_long do_freebsd_ktimer_create(abi_long arg1, abi_long arg2,
+ abi_long arg3)
+{
+ /* args: clockid_t clockid, struct sigevent *sevp, int *timerid */
+ abi_long ret;
+
+ struct sigevent host_sevp = { 0 }, *phost_sevp = NULL;
+
+ int clkid = arg1;
+ int timer_index = next_free_host_timer();
+
+ if (timer_index < 0) {
+ ret = -TARGET_EAGAIN;
+ } else {
+ int *phtimer = g_posix_timers + timer_index;
+
+ if (arg2) {
+ phost_sevp = &host_sevp;
+ ret = target_to_host_sigevent(phost_sevp, arg2);
+ if (ret != 0) {
+ return -TARGET_EFAULT;
+ }
+ }
+
+ ret = get_errno(__sys_ktimer_create(clkid, phost_sevp, phtimer));
+ if (ret) {
+ phtimer = NULL;
+ } else {
+ if (put_user(TIMER_MAGIC | timer_index, arg3, int)) {
+ ret = -TARGET_EFAULT;
+ }
+ }
+ }
+ return ret;
+}
+
+/* timer_delete(2) */
+static inline abi_long do_freebsd_ktimer_delete(abi_long arg1)
+{
+ /* args: int timerid */
+ abi_long ret;
+ int timerid = get_timer_id(arg1);
+
+ if (timerid < 0) {
+ ret = timerid;
+ } else {
+ int htimer = g_posix_timers[timerid];
+ ret = get_errno(__sys_ktimer_delete(htimer));
+ g_posix_timers[timerid] = 0;
+ }
+ return ret;
+}
+
+/* timer_settime(2) */
+static inline abi_long do_freebsd_ktimer_settime(abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4)
+{
+ /*
+ * args: int timerid, int flags, const struct itimerspec *new_value,
+ * struct itimerspec * old_value
+ */
+ abi_long ret;
+ int timerid = get_timer_id(arg1);
+
+ if (timerid < 0) {
+ ret = timerid;
+ } else if (arg3 == 0) {
+ ret = -TARGET_EINVAL;
+ } else {
+ int htimer = g_posix_timers[timerid];
+ struct itimerspec hspec_new = {{0},}, hspec_old = {{0},};
+
+ if (target_to_host_itimerspec(&hspec_new, arg3)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(
+ __sys_ktimer_settime(htimer, arg2, &hspec_new, &hspec_old));
+ if (arg4 && host_to_target_itimerspec(arg4, &hspec_old)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* timer_gettime(2) */
+static inline abi_long do_freebsd_ktimer_gettime(abi_long arg1, abi_long arg2)
+{
+ /* args: int timerid, struct itimerspec *curr_value */
+ abi_long ret;
+ int timerid = get_timer_id(arg1);
+
+ if (timerid < 0) {
+ ret = timerid;
+ } else if (!arg2) {
+ ret = -TARGET_EFAULT;
+ } else {
+ int htimer = g_posix_timers[timerid];
+ struct itimerspec hspec;
+ ret = get_errno(__sys_ktimer_gettime(htimer, &hspec));
+
+ if (host_to_target_itimerspec(arg2, &hspec)) {
+ ret = -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
#endif /* FREEBSD_OS_TIME_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 18/37] bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
2026-05-18 21:27 ` [PATCH v2 18/37] bsd-user: Add utimes, futimes, and ktimer functions to os-time.h Warner Losh
@ 2026-05-22 23:38 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:38 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add time-related system call shims: utimes, lutimes, futimes,
> futimesat, ktimer_create, ktimer_delete, ktimer_settime, and
> ktimer_gettime.
>
> 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 | 210 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 210 insertions(+)
>
> diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
> index 16b71e6234..05fa043442 100644
> --- a/bsd-user/freebsd/os-time.h
> +++ b/bsd-user/freebsd/os-time.h
> @@ -217,5 +217,215 @@ static inline abi_long do_freebsd_ntp_gettime(abi_ulong target_ntv_addr)
> }
>
>
> +/* utimes(2) */
> +static inline abi_long do_freebsd_utimes(abi_long arg1, abi_long arg2)
> +{
> + abi_long ret;
> + void *p;
> + struct timeval *tvp, tv[2];
> +
> + if (arg2 != 0) {
> + if (t2h_freebsd_timeval(&tv[0], arg2) ||
> + t2h_freebsd_timeval(&tv[1], arg2 +
> + sizeof(struct target_freebsd_timeval))) {
> + return -TARGET_EFAULT;
> + }
> + tvp = tv;
> + } else {
> + tvp = NULL;
> + }
> + p = lock_user_string(arg1);
> + if (p == NULL) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(utimes(p, tvp));
> + unlock_user(p, arg1, 0);
> +
> + return ret;
> +}
> +
> +/* lutimes(2) */
> +static inline abi_long do_freebsd_lutimes(abi_long arg1, abi_long arg2)
> +{
> + abi_long ret;
> + void *p;
> + struct timeval *tvp, tv[2];
> +
> + if (arg2 != 0) {
> + if (t2h_freebsd_timeval(&tv[0], arg2) ||
> + t2h_freebsd_timeval(&tv[1], arg2 +
> + sizeof(struct target_freebsd_timeval))) {
> + return -TARGET_EFAULT;
> + }
> + tvp = tv;
> + } else {
> + tvp = NULL;
> + }
> + p = lock_user_string(arg1);
> + if (p == NULL) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(lutimes(p, tvp));
> + unlock_user(p, arg1, 0);
> +
> + return ret;
> +}
> +
> +/* futimes(2) */
> +static inline abi_long do_freebsd_futimes(abi_long arg1, abi_long arg2)
> +{
> + struct timeval *tvp, tv[2];
> +
> + if (arg2 != 0) {
> + if (t2h_freebsd_timeval(&tv[0], arg2) ||
> + t2h_freebsd_timeval(&tv[1], arg2 +
> + sizeof(struct target_freebsd_timeval))) {
> + return -TARGET_EFAULT;
> + }
> + tvp = tv;
> + } else {
> + tvp = NULL;
> + }
> +
> + return get_errno(futimes(arg1, tvp));
> +}
> +
> +/* futimesat(2) */
> +static inline abi_long do_freebsd_futimesat(abi_long arg1, abi_long arg2,
> + abi_long arg3)
> +{
> + abi_long ret;
> + void *p;
> + struct timeval *tvp, tv[2];
> +
> + if (arg3 != 0) {
> + if (t2h_freebsd_timeval(&tv[0], arg3) ||
> + t2h_freebsd_timeval(&tv[1], arg3 +
> + sizeof(struct target_freebsd_timeval))) {
> + return -TARGET_EFAULT;
> + }
> + tvp = tv;
> + } else {
> + tvp = NULL;
> + }
> +
> + p = lock_user_string(arg2);
> + if (p == NULL) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(futimesat(arg1, p, tvp));
> + unlock_user(p, arg2, 0);
> +
> + return ret;
> +}
> +
The functions above are quite duplicated.
We could factor the first part at least, setting tvp.
The lock_user_string part can be replaced with LOCK_PATH.
> +/* timer_create(2) */
> +static inline abi_long do_freebsd_ktimer_create(abi_long arg1, abi_long arg2,
> + abi_long arg3)
> +{
> + /* args: clockid_t clockid, struct sigevent *sevp, int *timerid */
> + abi_long ret;
> +
> + struct sigevent host_sevp = { 0 }, *phost_sevp = NULL;
> +
> + int clkid = arg1;
> + int timer_index = next_free_host_timer();
> +
> + if (timer_index < 0) {
> + ret = -TARGET_EAGAIN;
> + } else {
> + int *phtimer = g_posix_timers + timer_index;
> +
> + if (arg2) {
> + phost_sevp = &host_sevp;
> + ret = target_to_host_sigevent(phost_sevp, arg2);
> + if (ret != 0) {
> + return -TARGET_EFAULT;
> + }
> + }
> +
> + ret = get_errno(__sys_ktimer_create(clkid, phost_sevp, phtimer));
> + if (ret) {
> + phtimer = NULL;
> + } else {
> + if (put_user(TIMER_MAGIC | timer_index, arg3, int)) {
> + ret = -TARGET_EFAULT;
> + }
> + }
> + }
> + return ret;
> +}
> +
> +/* timer_delete(2) */
> +static inline abi_long do_freebsd_ktimer_delete(abi_long arg1)
> +{
> + /* args: int timerid */
> + abi_long ret;
> + int timerid = get_timer_id(arg1);
> +
> + if (timerid < 0) {
> + ret = timerid;
> + } else {
> + int htimer = g_posix_timers[timerid];
> + ret = get_errno(__sys_ktimer_delete(htimer));
> + g_posix_timers[timerid] = 0;
> + }
> + return ret;
> +}
> +
> +/* timer_settime(2) */
> +static inline abi_long do_freebsd_ktimer_settime(abi_long arg1, abi_long arg2,
> + abi_long arg3, abi_long arg4)
> +{
> + /*
> + * args: int timerid, int flags, const struct itimerspec *new_value,
> + * struct itimerspec * old_value
> + */
> + abi_long ret;
> + int timerid = get_timer_id(arg1);
> +
> + if (timerid < 0) {
> + ret = timerid;
> + } else if (arg3 == 0) {
> + ret = -TARGET_EINVAL;
> + } else {
> + int htimer = g_posix_timers[timerid];
> + struct itimerspec hspec_new = {{0},}, hspec_old = {{0},};
> +
> + if (target_to_host_itimerspec(&hspec_new, arg3)) {
> + return -TARGET_EFAULT;
> + }
> + ret = get_errno(
> + __sys_ktimer_settime(htimer, arg2, &hspec_new, &hspec_old));
> + if (arg4 && host_to_target_itimerspec(arg4, &hspec_old)) {
> + return -TARGET_EFAULT;
> + }
> + }
> + return ret;
> +}
> +
> +/* timer_gettime(2) */
> +static inline abi_long do_freebsd_ktimer_gettime(abi_long arg1, abi_long arg2)
> +{
> + /* args: int timerid, struct itimerspec *curr_value */
> + abi_long ret;
> + int timerid = get_timer_id(arg1);
> +
> + if (timerid < 0) {
> + ret = timerid;
> + } else if (!arg2) {
> + ret = -TARGET_EFAULT;
> + } else {
> + int htimer = g_posix_timers[timerid];
> + struct itimerspec hspec;
> + ret = get_errno(__sys_ktimer_gettime(htimer, &hspec));
> +
> + if (host_to_target_itimerspec(arg2, &hspec)) {
> + ret = -TARGET_EFAULT;
> + }
> + }
> + return ret;
> +}
> +
>
> #endif /* FREEBSD_OS_TIME_H */
>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll to os-time.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (17 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 18/37] bsd-user: Add utimes, futimes, and ktimer functions to os-time.h Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:46 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 20/37] bsd-user: Add kqueue and kevent functions " Warner Losh
` (18 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
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;
+ }
+
+ 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
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll to os-time.h
2026-05-18 21:27 ` [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll " Warner Losh
@ 2026-05-22 23:46 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:46 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
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 */
>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 20/37] bsd-user: Add kqueue and kevent functions to os-time.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (18 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll " Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:49 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 21/37] bsd-user: Add sigtimedwait, itimer, and futimens " Warner Losh
` (17 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Sean Bruno, Kyle Evans
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 */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 20/37] bsd-user: Add kqueue and kevent functions to os-time.h
2026-05-18 21:27 ` [PATCH v2 20/37] bsd-user: Add kqueue and kevent functions " Warner Losh
@ 2026-05-22 23:49 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:49 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Mikaël Urankar, Sean Bruno
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.
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 21/37] bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (19 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 20/37] bsd-user: Add kqueue and kevent functions " Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:50 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 22/37] bsd-user: Add os-time.c and time-related type definitions Warner Losh
` (16 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
Complete os-time.h with sigtimedwait, setitimer, getitimer,
clock_getcpuclockid2, futimens, and utimensat system call shims.
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 | 194 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 184 insertions(+), 10 deletions(-)
diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
index 078355392d..f24b6f35db 100644
--- a/bsd-user/freebsd/os-time.h
+++ b/bsd-user/freebsd/os-time.h
@@ -124,19 +124,19 @@ static inline abi_long do_freebsd_gettimeofday(abi_ulong arg1, abi_ulong arg2)
struct timeval tv;
struct timezone tz, *target_tz; /* XXX */
- if (arg2 != 0) {
- if (!lock_user_struct(VERIFY_READ, target_tz, arg2, 0)) {
- return -TARGET_EFAULT;
- }
- __get_user(tz.tz_minuteswest, &target_tz->tz_minuteswest);
- __get_user(tz.tz_dsttime, &target_tz->tz_dsttime);
- unlock_user_struct(target_tz, arg2, 1);
- }
ret = get_errno(gettimeofday(&tv, arg2 != 0 ? &tz : NULL));
if (!is_error(ret)) {
if (h2t_freebsd_timeval(&tv, arg1)) {
return -TARGET_EFAULT;
}
+ if (arg2 != 0) {
+ if (!lock_user_struct(VERIFY_WRITE, target_tz, arg2, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(tz.tz_minuteswest, &target_tz->tz_minuteswest);
+ __put_user(tz.tz_dsttime, &target_tz->tz_dsttime);
+ unlock_user_struct(target_tz, arg2, 1);
+ }
}
return ret;
@@ -149,12 +149,12 @@ static inline abi_long do_freebsd_settimeofday(abi_long arg1, abi_long arg2)
struct timezone tz, *target_tz; /* XXX */
if (arg2 != 0) {
- if (!lock_user_struct(VERIFY_READ, target_tz, arg2, 0)) {
+ if (!lock_user_struct(VERIFY_READ, target_tz, arg2, 1)) {
return -TARGET_EFAULT;
}
__get_user(tz.tz_minuteswest, &target_tz->tz_minuteswest);
__get_user(tz.tz_dsttime, &target_tz->tz_dsttime);
- unlock_user_struct(target_tz, arg2, 1);
+ unlock_user_struct(target_tz, arg2, 0);
}
if (t2h_freebsd_timeval(&tv, arg1)) {
return -TARGET_EFAULT;
@@ -595,6 +595,7 @@ static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long arg1,
/* Unlike poll(), ppoll() uses struct timespec. */
if (arg3) {
if (t2h_freebsd_timespec(&ts, arg3)) {
+ unlock_user(target_pfd, arg1, 0);
return -TARGET_EFAULT;
}
ts_ptr = &ts;
@@ -605,6 +606,7 @@ static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long arg1,
if (arg4 != 0) {
p = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1);
if (p == NULL) {
+ unlock_user(target_pfd, arg1, 0);
return -TARGET_EFAULT;
}
target_to_host_sigset(&tstate->sigsuspend_mask, p);
@@ -802,5 +804,177 @@ static inline abi_long do_freebsd_kevent(abi_long arg1, abi_ulong arg2,
}
/* sigtimedwait(2) */
+static inline abi_long do_freebsd_sigtimedwait(abi_ulong arg1, abi_ulong arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ sigset_t set;
+ struct timespec uts, *puts;
+ siginfo_t uinfo;
+
+ p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ if (arg3) {
+ puts = &uts;
+ ret = t2h_freebsd_timespec(puts, arg3);
+ if (ret != 0) {
+ return ret;
+ }
+ } else {
+ puts = NULL;
+ }
+ ret = get_errno(sigtimedwait(&set, &uinfo, puts));
+ if (!is_error(ret) && arg2) {
+ p = lock_user(VERIFY_WRITE, arg2, sizeof(target_siginfo_t), 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ host_to_target_siginfo(p, &uinfo);
+ unlock_user(p, arg2, sizeof(target_siginfo_t));
+ }
+ return ret;
+}
+
+/* setitimer(2) */
+static inline abi_long do_freebsd_setitimer(int arg1, abi_ulong arg2,
+ abi_ulong arg3)
+{
+ abi_long ret = 0;
+ struct itimerval value, ovalue, *pvalue;
+
+ if (arg2) {
+ pvalue = &value;
+ if (t2h_freebsd_timeval(&pvalue->it_interval, arg2) ||
+ t2h_freebsd_timeval(&pvalue->it_value,
+ arg2 + sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ } else {
+ pvalue = NULL;
+ }
+ ret = get_errno(setitimer(arg1, pvalue, &ovalue));
+ if (!is_error(ret) && arg3) {
+ if (h2t_freebsd_timeval(&ovalue.it_interval, arg3) ||
+ h2t_freebsd_timeval(&ovalue.it_value,
+ arg3 + sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* getitimer(2) */
+static inline abi_long do_freebsd_getitimer(int arg1, abi_ulong arg2)
+{
+ abi_long ret = 0;
+ struct itimerval value;
+
+ ret = get_errno(getitimer(arg1, &value));
+ if (!is_error(ret) && arg2) {
+ if (h2t_freebsd_timeval(&value.it_interval, arg2) ||
+ h2t_freebsd_timeval(&value.it_value,
+ arg2 + sizeof(struct target_freebsd_timeval))) {
+ return -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+/* clock_getcpuclockid2(id_t, int, clockid_t *) Not documented. */
+static inline abi_long do_freebsd_clock_getcpuclockid2(abi_ulong arg1,
+ abi_ulong arg2, abi_ulong arg3, abi_ulong arg4)
+{
+ abi_long ret = 0;
+ id_t id; /* 64-bit value */
+ int which;
+ abi_ulong target_clk_id_addr;
+ clockid_t clk_id;
+
+#if TARGET_ABI_BITS == 32
+ id = (id_t)target_arg64(arg1, arg2);
+ which = (int)arg3;
+ target_clk_id_addr = arg4;
+#else
+ id = (id_t)arg1;
+ which = (int)arg2;
+ target_clk_id_addr = arg3;
+#endif
+
+ if (target_clk_id_addr == 0) {
+ return -TARGET_EINVAL;
+ }
+
+ switch (which) {
+ case TARGET_CPUCLOCK_WHICH_PID:
+ ret = get_errno(clock_getcpuclockid2(id, CPUCLOCK_WHICH_PID, &clk_id));
+ break;
+
+ case TARGET_CPUCLOCK_WHICH_TID:
+ ret = get_errno(clock_getcpuclockid2(id, CPUCLOCK_WHICH_TID, &clk_id));
+ break;
+
+ default:
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
+ if (!ret && put_user_s32(clk_id, target_clk_id_addr)) {
+ ret = -TARGET_EFAULT;
+ }
+
+ return ret;
+}
+
+static inline abi_long do_freebsd_futimens(abi_ulong arg1,
+ abi_ulong arg2)
+{
+ struct timespec *tvp, tv[2];
+
+ if (arg2 != 0) {
+ if (t2h_freebsd_timespec(&tv[0], arg2) ||
+ t2h_freebsd_timespec(&tv[1], arg2 +
+ sizeof(struct target_freebsd_timespec))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+
+ return get_errno(futimens(arg1, tvp));
+}
+
+static inline abi_long do_freebsd_utimensat(abi_ulong arg1,
+ abi_ulong arg2, abi_ulong arg3, abi_ulong arg4)
+{
+ abi_long ret = 0;
+ void *p;
+ struct timespec *tvp, tv[2];
+
+ if (arg3 != 0) {
+ if (t2h_freebsd_timespec(&tv[0], arg3) ||
+ t2h_freebsd_timespec(&tv[1], arg3 +
+ sizeof(struct target_freebsd_timespec))) {
+ return -TARGET_EFAULT;
+ }
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+
+ p = lock_user_string(arg2);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(utimensat(arg1, p, tvp,
+ target_to_host_bitmask(arg4, fcntl_flags_tbl)));
+ unlock_user(p, arg2, 0);
+ return ret;
+}
#endif /* FREEBSD_OS_TIME_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 21/37] bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
2026-05-18 21:27 ` [PATCH v2 21/37] bsd-user: Add sigtimedwait, itimer, and futimens " Warner Losh
@ 2026-05-22 23:50 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:50 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Complete os-time.h with sigtimedwait, setitimer, getitimer,
> clock_getcpuclockid2, futimens, and utimensat system call shims.
>
> 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 | 194 ++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 184 insertions(+), 10 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 22/37] bsd-user: Add os-time.c and time-related type definitions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (20 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 21/37] bsd-user: Add sigtimedwait, itimer, and futimens " Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:52 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 23/37] bsd-user: Add time system call dispatch Warner Losh
` (15 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son,
Mikaël Urankar, Kyle Evans
Add os-time.c with time conversion helpers (t2h/h2t functions for
timespec, timeval, timex, ntptimeval, itimerspec) and POSIX timer
helper functions. Also add target_freebsd_timex, target_freebsd_ntptimeval,
target_freebsd_itimerspec, kevent structures, and timer definitions
to syscall_defs.h.
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-syscall.c | 2 +
bsd-user/freebsd/os-time.c | 346 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-time.h | 3 +
3 files changed, 351 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 6fbd2da261..020d16fda3 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -98,6 +98,8 @@ safe_syscall6(int, kevent, int, kq, const struct kevent *, changelist,
int, nchanges, struct kevent *, eventlist, int, nevents,
const struct timespec *, timeout);
+int g_posix_timers[32] = { 0, } ;
+
/* used in os-proc */
safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
struct rusage *, rusage);
diff --git a/bsd-user/freebsd/os-time.c b/bsd-user/freebsd/os-time.c
new file mode 100644
index 0000000000..f1c1cc2c1a
--- /dev/null
+++ b/bsd-user/freebsd/os-time.c
@@ -0,0 +1,346 @@
+/*
+ * FreeBSD time related system call helpers
+ *
+ * Copyright (c) 2013-2015 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#include <errno.h>
+#include <time.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/umtx.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+
+/*
+ * FreeBSD time conversion functions
+ */
+abi_long t2h_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr)
+{
+ struct target_freebsd_timeval *target_tv;
+
+ if (!lock_user_struct(VERIFY_READ, target_tv, target_tv_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(tv->tv_sec, &target_tv->tv_sec);
+ __get_user(tv->tv_usec, &target_tv->tv_usec);
+ unlock_user_struct(target_tv, target_tv_addr, 0);
+
+ return 0;
+}
+
+abi_long h2t_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr)
+{
+ struct target_freebsd_timeval *target_tv;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_tv, target_tv_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(tv->tv_sec, &target_tv->tv_sec);
+ __put_user(tv->tv_usec, &target_tv->tv_usec);
+ unlock_user_struct(target_tv, target_tv_addr, 1);
+
+ return 0;
+}
+
+abi_long t2h_freebsd_timespec(struct timespec *ts, abi_ulong target_ts_addr)
+{
+ struct target_freebsd_timespec *target_ts;
+
+ if (!lock_user_struct(VERIFY_READ, target_ts, target_ts_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(ts->tv_sec, &target_ts->tv_sec);
+ __get_user(ts->tv_nsec, &target_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_ts_addr, 0);
+
+ return 0;
+}
+
+abi_long h2t_freebsd_timespec(abi_ulong target_ts_addr, struct timespec *ts)
+{
+ struct target_freebsd_timespec *target_ts;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_ts, target_ts_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(ts->tv_sec, &target_ts->tv_sec);
+ __put_user(ts->tv_nsec, &target_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_ts_addr, 1);
+
+ return 0;
+}
+
+abi_long t2h_freebsd_umtx_time(abi_ulong target_ut_addr,
+ abi_ulong target_ut_size, void *host_t, size_t *host_tsz)
+{
+ abi_long ret;
+
+ if (target_ut_size <= sizeof(struct target_freebsd_timespec)) {
+ ret = t2h_freebsd_timespec((struct timespec *)host_t, target_ut_addr);
+ if (ret == 0) {
+ *host_tsz = sizeof(struct timespec);
+ }
+ return ret;
+ } else {
+ struct target_freebsd__umtx_time *target_ut;
+ struct _umtx_time *ut = (struct _umtx_time *)host_t;
+
+ if (!lock_user_struct(VERIFY_READ, target_ut, target_ut_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ if (t2h_freebsd_timespec(&ut->_timeout, h2g(&target_ut->_timeout))) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(ut->_flags, &target_ut->_flags);
+ __get_user(ut->_clockid, &target_ut->_clockid);
+ unlock_user_struct(target_ut, target_ut_addr, 0);
+
+ if (target_ut_size > sizeof(struct target_freebsd__umtx_time)) {
+ *host_tsz = sizeof(struct _umtx_time) + sizeof(struct timespec);
+ } else {
+ *host_tsz = sizeof(struct _umtx_time);
+ }
+
+ return 0;
+ }
+}
+
+abi_long t2h_freebsd_timex(struct timex *host_tx, abi_ulong target_tx_addr)
+{
+ struct target_freebsd_timex *target_tx;
+
+ if (!lock_user_struct(VERIFY_READ, target_tx, target_tx_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(host_tx->modes, &target_tx->modes);
+ __get_user(host_tx->offset, &target_tx->offset);
+ __get_user(host_tx->freq, &target_tx->freq);
+ __get_user(host_tx->maxerror, &target_tx->maxerror);
+ __get_user(host_tx->esterror, &target_tx->esterror);
+ __get_user(host_tx->status, &target_tx->status);
+ __get_user(host_tx->constant, &target_tx->constant);
+ __get_user(host_tx->precision, &target_tx->precision);
+ __get_user(host_tx->tolerance, &target_tx->tolerance);
+ __get_user(host_tx->ppsfreq, &target_tx->ppsfreq);
+ __get_user(host_tx->jitter, &target_tx->jitter);
+ __get_user(host_tx->shift, &target_tx->shift);
+ __get_user(host_tx->stabil, &target_tx->stabil);
+ __get_user(host_tx->jitcnt, &target_tx->jitcnt);
+ __get_user(host_tx->calcnt, &target_tx->calcnt);
+ __get_user(host_tx->errcnt, &target_tx->errcnt);
+ __get_user(host_tx->stbcnt, &target_tx->stbcnt);
+ unlock_user_struct(target_tx, target_tx_addr, 0);
+
+ return 0;
+}
+
+abi_long h2t_freebsd_timex(abi_ulong target_tx_addr, struct timex *host_tx)
+{
+ struct target_freebsd_timex *target_tx;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_tx, target_tx_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(host_tx->modes, &target_tx->modes);
+ __put_user(host_tx->offset, &target_tx->offset);
+ __put_user(host_tx->freq, &target_tx->freq);
+ __put_user(host_tx->maxerror, &target_tx->maxerror);
+ __put_user(host_tx->esterror, &target_tx->esterror);
+ __put_user(host_tx->status, &target_tx->status);
+ __put_user(host_tx->constant, &target_tx->constant);
+ __put_user(host_tx->precision, &target_tx->precision);
+ __put_user(host_tx->tolerance, &target_tx->tolerance);
+ __put_user(host_tx->ppsfreq, &target_tx->ppsfreq);
+ __put_user(host_tx->jitter, &target_tx->jitter);
+ __put_user(host_tx->shift, &target_tx->shift);
+ __put_user(host_tx->stabil, &target_tx->stabil);
+ __put_user(host_tx->jitcnt, &target_tx->jitcnt);
+ __put_user(host_tx->calcnt, &target_tx->calcnt);
+ __put_user(host_tx->errcnt, &target_tx->errcnt);
+ __put_user(host_tx->stbcnt, &target_tx->stbcnt);
+ unlock_user_struct(target_tx, target_tx_addr, 1);
+
+ return 0;
+}
+
+abi_long h2t_freebsd_ntptimeval(abi_ulong target_ntv_addr,
+ struct ntptimeval *ntv)
+{
+ struct target_freebsd_ntptimeval *target_ntv;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_ntv, target_ntv_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(ntv->time.tv_sec, &target_ntv->time.tv_sec);
+ __put_user(ntv->time.tv_nsec, &target_ntv->time.tv_nsec);
+ __put_user(ntv->maxerror, &target_ntv->maxerror);
+ __put_user(ntv->esterror, &target_ntv->esterror);
+ __put_user(ntv->tai, &target_ntv->tai);
+ __put_user(ntv->time_state, &target_ntv->time_state);
+ unlock_user_struct(target_ntv, target_ntv_addr, 1);
+
+ return 0;
+}
+
+/*
+ * select(2) fdset copy functions
+ */
+abi_ulong copy_from_user_fdset(fd_set *fds, abi_ulong target_fds_addr, int n)
+{
+ int i, nw, j, k;
+ abi_ulong b, *target_fds;
+
+ nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
+ target_fds = lock_user(VERIFY_READ, target_fds_addr,
+ sizeof(abi_ulong) * nw, 1);
+ if (target_fds == NULL) {
+ return -TARGET_EFAULT;
+ }
+ FD_ZERO(fds);
+ k = 0;
+ for (i = 0; i < nw; i++) {
+ /* grab the abi_ulong */
+ __get_user(b, &target_fds[i]);
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ /* check the bit inside the abi_ulong */
+ if ((b >> j) & 1) {
+ FD_SET(k, fds);
+ }
+ k++;
+ }
+ }
+ unlock_user(target_fds, target_fds_addr, 0);
+
+ return 0;
+}
+
+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;
+}
+
+abi_long copy_to_user_fdset(abi_ulong target_fds_addr, const fd_set *fds, int n)
+{
+ int i, nw, j, k;
+ abi_long v;
+ abi_ulong *target_fds;
+
+ nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
+ target_fds = lock_user(VERIFY_WRITE, target_fds_addr,
+ sizeof(abi_ulong) * nw, 0);
+ if (target_fds == NULL) {
+ return -TARGET_EFAULT;
+ }
+ k = 0;
+ for (i = 0; i < nw; i++) {
+ v = 0;
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ v |= ((FD_ISSET(k, fds) != 0) << j);
+ k++;
+ }
+ __put_user(v, &target_fds[i]);
+ }
+ unlock_user(target_fds, target_fds_addr, sizeof(abi_ulong) * nw);
+
+ return 0;
+}
+
+abi_int next_free_host_timer(void)
+{
+ int k ;
+ /* FIXME: Does finding the next free slot require a lock? */
+ for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) {
+ if (g_posix_timers[k] == 0) {
+ g_posix_timers[k] = 1;
+ return k;
+ }
+ }
+ return -1;
+}
+
+int host_to_target_timerid(int timerid)
+{
+ int k;
+
+ for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) {
+ if (g_posix_timers[k] == timerid) {
+ return TIMER_MAGIC | k;
+ }
+ }
+
+ return -1;
+}
+
+abi_long target_to_host_itimerspec(struct itimerspec *host_itspec,
+ abi_ulong target_addr)
+{
+ struct target_freebsd_itimerspec *target_itspec;
+
+ if (!lock_user_struct(VERIFY_READ, target_itspec, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+
+ host_itspec->it_interval.tv_sec =
+ tswapal(target_itspec->it_interval.tv_sec);
+ host_itspec->it_interval.tv_nsec =
+ tswapal(target_itspec->it_interval.tv_nsec);
+ host_itspec->it_value.tv_sec = tswapal(target_itspec->it_value.tv_sec);
+ host_itspec->it_value.tv_nsec = tswapal(target_itspec->it_value.tv_nsec);
+
+ unlock_user_struct(target_itspec, target_addr, 0);
+ return 0;
+}
+
+abi_long host_to_target_itimerspec(abi_ulong target_addr,
+ struct itimerspec *host_its)
+{
+ struct target_freebsd_itimerspec *target_itspec;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_itspec, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ target_itspec->it_interval.tv_sec = tswapal(host_its->it_interval.tv_sec);
+ target_itspec->it_interval.tv_nsec = tswapal(host_its->it_interval.tv_nsec);
+
+ target_itspec->it_value.tv_sec = tswapal(host_its->it_value.tv_sec);
+ target_itspec->it_value.tv_nsec = tswapal(host_its->it_value.tv_nsec);
+
+ unlock_user_struct(target_itspec, target_addr, 1);
+ return 0;
+}
+
+/* Convert QEMU provided timer ID back to internal 16bit index format */
+int get_timer_id(abi_long arg)
+{
+ int timerid = arg;
+
+ if ((timerid & TIMER_MAGIC_MASK) != TIMER_MAGIC) {
+ return -TARGET_EINVAL;
+ }
+
+ timerid &= 0xffff;
+
+ if (timerid >= ARRAY_SIZE(g_posix_timers)) {
+ return -TARGET_EINVAL;
+ }
+
+ return timerid;
+}
diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
index f24b6f35db..e91a3f9bcf 100644
--- a/bsd-user/freebsd/os-time.h
+++ b/bsd-user/freebsd/os-time.h
@@ -198,6 +198,9 @@ static inline abi_long do_freebsd_ntp_adjtime(abi_ulong target_tx_addr)
if (ret == 0) {
ret = get_errno(ntp_adjtime(&host_tx));
}
+ if (!is_error(ret)) {
+ h2t_freebsd_timex(target_tx_addr, &host_tx);
+ }
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 22/37] bsd-user: Add os-time.c and time-related type definitions
2026-05-18 21:27 ` [PATCH v2 22/37] bsd-user: Add os-time.c and time-related type definitions Warner Losh
@ 2026-05-22 23:52 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:52 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son, Mikaël Urankar
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add os-time.c with time conversion helpers (t2h/h2t functions for
> timespec, timeval, timex, ntptimeval, itimerspec) and POSIX timer
> helper functions. Also add target_freebsd_timex, target_freebsd_ntptimeval,
> target_freebsd_itimerspec, kevent structures, and timer definitions
> to syscall_defs.h.
>
> 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-syscall.c | 2 +
> bsd-user/freebsd/os-time.c | 346 ++++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-time.h | 3 +
> 3 files changed, 351 insertions(+)
>
> diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
> index 6fbd2da261..020d16fda3 100644
> --- a/bsd-user/freebsd/os-syscall.c
> +++ b/bsd-user/freebsd/os-syscall.c
> @@ -98,6 +98,8 @@ safe_syscall6(int, kevent, int, kq, const struct kevent *, changelist,
> int, nchanges, struct kevent *, eventlist, int, nevents,
> const struct timespec *, timeout);
>
> +int g_posix_timers[32] = { 0, } ;
> +
> /* used in os-proc */
> safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options,
> struct rusage *, rusage);
> diff --git a/bsd-user/freebsd/os-time.c b/bsd-user/freebsd/os-time.c
> new file mode 100644
> index 0000000000..f1c1cc2c1a
> --- /dev/null
> +++ b/bsd-user/freebsd/os-time.c
> @@ -0,0 +1,346 @@
> +/*
> + * FreeBSD time related system call helpers
> + *
> + * Copyright (c) 2013-2015 Stacey D. Son
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#include "qemu/osdep.h"
> +
> +#include <errno.h>
> +#include <time.h>
> +#include <sys/timex.h>
> +#include <sys/types.h>
> +#include <sys/select.h>
> +#include <sys/umtx.h>
> +
> +#include "qemu.h"
> +#include "qemu-os.h"
> +
> +/*
> + * FreeBSD time conversion functions
> + */
> +abi_long t2h_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr)
> +{
> + struct target_freebsd_timeval *target_tv;
> +
> + if (!lock_user_struct(VERIFY_READ, target_tv, target_tv_addr, 1)) {
> + return -TARGET_EFAULT;
> + }
> + __get_user(tv->tv_sec, &target_tv->tv_sec);
> + __get_user(tv->tv_usec, &target_tv->tv_usec);
> + unlock_user_struct(target_tv, target_tv_addr, 0);
> +
> + return 0;
> +}
> +
> +abi_long h2t_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr)
> +{
> + struct target_freebsd_timeval *target_tv;
> +
> + if (!lock_user_struct(VERIFY_WRITE, target_tv, target_tv_addr, 0)) {
> + return -TARGET_EFAULT;
> + }
> + __put_user(tv->tv_sec, &target_tv->tv_sec);
> + __put_user(tv->tv_usec, &target_tv->tv_usec);
> + unlock_user_struct(target_tv, target_tv_addr, 1);
> +
> + return 0;
> +}
> +
> +abi_long t2h_freebsd_timespec(struct timespec *ts, abi_ulong target_ts_addr)
> +{
> + struct target_freebsd_timespec *target_ts;
> +
> + if (!lock_user_struct(VERIFY_READ, target_ts, target_ts_addr, 1)) {
> + return -TARGET_EFAULT;
> + }
> + __get_user(ts->tv_sec, &target_ts->tv_sec);
> + __get_user(ts->tv_nsec, &target_ts->tv_nsec);
> + unlock_user_struct(target_ts, target_ts_addr, 0);
> +
> + return 0;
> +}
> +
> +abi_long h2t_freebsd_timespec(abi_ulong target_ts_addr, struct timespec *ts)
> +{
> + struct target_freebsd_timespec *target_ts;
> +
> + if (!lock_user_struct(VERIFY_WRITE, target_ts, target_ts_addr, 0)) {
> + return -TARGET_EFAULT;
> + }
> + __put_user(ts->tv_sec, &target_ts->tv_sec);
> + __put_user(ts->tv_nsec, &target_ts->tv_nsec);
> + unlock_user_struct(target_ts, target_ts_addr, 1);
> +
> + return 0;
> +}
> +
> +abi_long t2h_freebsd_umtx_time(abi_ulong target_ut_addr,
> + abi_ulong target_ut_size, void *host_t, size_t *host_tsz)
> +{
> + abi_long ret;
> +
> + if (target_ut_size <= sizeof(struct target_freebsd_timespec)) {
> + ret = t2h_freebsd_timespec((struct timespec *)host_t, target_ut_addr);
> + if (ret == 0) {
> + *host_tsz = sizeof(struct timespec);
> + }
> + return ret;
> + } else {
> + struct target_freebsd__umtx_time *target_ut;
> + struct _umtx_time *ut = (struct _umtx_time *)host_t;
> +
> + if (!lock_user_struct(VERIFY_READ, target_ut, target_ut_addr, 1)) {
> + return -TARGET_EFAULT;
> + }
> + if (t2h_freebsd_timespec(&ut->_timeout, h2g(&target_ut->_timeout))) {
> + return -TARGET_EFAULT;
> + }
> + __get_user(ut->_flags, &target_ut->_flags);
> + __get_user(ut->_clockid, &target_ut->_clockid);
> + unlock_user_struct(target_ut, target_ut_addr, 0);
> +
> + if (target_ut_size > sizeof(struct target_freebsd__umtx_time)) {
> + *host_tsz = sizeof(struct _umtx_time) + sizeof(struct timespec);
> + } else {
> + *host_tsz = sizeof(struct _umtx_time);
> + }
> +
> + return 0;
> + }
> +}
> +
> +abi_long t2h_freebsd_timex(struct timex *host_tx, abi_ulong target_tx_addr)
> +{
> + struct target_freebsd_timex *target_tx;
> +
> + if (!lock_user_struct(VERIFY_READ, target_tx, target_tx_addr, 1)) {
> + return -TARGET_EFAULT;
> + }
> + __get_user(host_tx->modes, &target_tx->modes);
> + __get_user(host_tx->offset, &target_tx->offset);
> + __get_user(host_tx->freq, &target_tx->freq);
> + __get_user(host_tx->maxerror, &target_tx->maxerror);
> + __get_user(host_tx->esterror, &target_tx->esterror);
> + __get_user(host_tx->status, &target_tx->status);
> + __get_user(host_tx->constant, &target_tx->constant);
> + __get_user(host_tx->precision, &target_tx->precision);
> + __get_user(host_tx->tolerance, &target_tx->tolerance);
> + __get_user(host_tx->ppsfreq, &target_tx->ppsfreq);
> + __get_user(host_tx->jitter, &target_tx->jitter);
> + __get_user(host_tx->shift, &target_tx->shift);
> + __get_user(host_tx->stabil, &target_tx->stabil);
> + __get_user(host_tx->jitcnt, &target_tx->jitcnt);
> + __get_user(host_tx->calcnt, &target_tx->calcnt);
> + __get_user(host_tx->errcnt, &target_tx->errcnt);
> + __get_user(host_tx->stbcnt, &target_tx->stbcnt);
> + unlock_user_struct(target_tx, target_tx_addr, 0);
> +
> + return 0;
> +}
> +
> +abi_long h2t_freebsd_timex(abi_ulong target_tx_addr, struct timex *host_tx)
> +{
> + struct target_freebsd_timex *target_tx;
> +
> + if (!lock_user_struct(VERIFY_WRITE, target_tx, target_tx_addr, 0)) {
> + return -TARGET_EFAULT;
> + }
> + __put_user(host_tx->modes, &target_tx->modes);
> + __put_user(host_tx->offset, &target_tx->offset);
> + __put_user(host_tx->freq, &target_tx->freq);
> + __put_user(host_tx->maxerror, &target_tx->maxerror);
> + __put_user(host_tx->esterror, &target_tx->esterror);
> + __put_user(host_tx->status, &target_tx->status);
> + __put_user(host_tx->constant, &target_tx->constant);
> + __put_user(host_tx->precision, &target_tx->precision);
> + __put_user(host_tx->tolerance, &target_tx->tolerance);
> + __put_user(host_tx->ppsfreq, &target_tx->ppsfreq);
> + __put_user(host_tx->jitter, &target_tx->jitter);
> + __put_user(host_tx->shift, &target_tx->shift);
> + __put_user(host_tx->stabil, &target_tx->stabil);
> + __put_user(host_tx->jitcnt, &target_tx->jitcnt);
> + __put_user(host_tx->calcnt, &target_tx->calcnt);
> + __put_user(host_tx->errcnt, &target_tx->errcnt);
> + __put_user(host_tx->stbcnt, &target_tx->stbcnt);
> + unlock_user_struct(target_tx, target_tx_addr, 1);
> +
> + return 0;
> +}
> +
> +abi_long h2t_freebsd_ntptimeval(abi_ulong target_ntv_addr,
> + struct ntptimeval *ntv)
> +{
> + struct target_freebsd_ntptimeval *target_ntv;
> +
> + if (!lock_user_struct(VERIFY_WRITE, target_ntv, target_ntv_addr, 0)) {
> + return -TARGET_EFAULT;
> + }
> + __put_user(ntv->time.tv_sec, &target_ntv->time.tv_sec);
> + __put_user(ntv->time.tv_nsec, &target_ntv->time.tv_nsec);
> + __put_user(ntv->maxerror, &target_ntv->maxerror);
> + __put_user(ntv->esterror, &target_ntv->esterror);
> + __put_user(ntv->tai, &target_ntv->tai);
> + __put_user(ntv->time_state, &target_ntv->time_state);
> + unlock_user_struct(target_ntv, target_ntv_addr, 1);
> +
> + return 0;
> +}
> +
> +/*
> + * select(2) fdset copy functions
> + */
> +abi_ulong copy_from_user_fdset(fd_set *fds, abi_ulong target_fds_addr, int n)
> +{
> + int i, nw, j, k;
> + abi_ulong b, *target_fds;
> +
> + nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
> + target_fds = lock_user(VERIFY_READ, target_fds_addr,
> + sizeof(abi_ulong) * nw, 1);
> + if (target_fds == NULL) {
> + return -TARGET_EFAULT;
> + }
> + FD_ZERO(fds);
> + k = 0;
> + for (i = 0; i < nw; i++) {
> + /* grab the abi_ulong */
> + __get_user(b, &target_fds[i]);
> + for (j = 0; j < TARGET_ABI_BITS; j++) {
> + /* check the bit inside the abi_ulong */
> + if ((b >> j) & 1) {
> + FD_SET(k, fds);
> + }
> + k++;
> + }
> + }
> + unlock_user(target_fds, target_fds_addr, 0);
> +
> + return 0;
> +}
> +
> +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;
> +}
> +
> +abi_long copy_to_user_fdset(abi_ulong target_fds_addr, const fd_set *fds, int n)
> +{
> + int i, nw, j, k;
> + abi_long v;
> + abi_ulong *target_fds;
> +
> + nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
> + target_fds = lock_user(VERIFY_WRITE, target_fds_addr,
> + sizeof(abi_ulong) * nw, 0);
> + if (target_fds == NULL) {
> + return -TARGET_EFAULT;
> + }
> + k = 0;
> + for (i = 0; i < nw; i++) {
> + v = 0;
> + for (j = 0; j < TARGET_ABI_BITS; j++) {
> + v |= ((FD_ISSET(k, fds) != 0) << j);
> + k++;
> + }
> + __put_user(v, &target_fds[i]);
> + }
> + unlock_user(target_fds, target_fds_addr, sizeof(abi_ulong) * nw);
> +
> + return 0;
> +}
It seems the two functions above do not belong to current patch, but
should be present in N-2 patch at least (ideally, in a third one, before
os-time part of the series)
> +
> +abi_int next_free_host_timer(void)
> +{
> + int k ;
> + /* FIXME: Does finding the next free slot require a lock? */
> + for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) {
> + if (g_posix_timers[k] == 0) {
> + g_posix_timers[k] = 1;
> + return k;
> + }
> + }
> + return -1;
> +}
> +
> +int host_to_target_timerid(int timerid)
> +{
> + int k;
> +
> + for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) {
> + if (g_posix_timers[k] == timerid) {
> + return TIMER_MAGIC | k;
> + }
> + }
> +
> + return -1;
> +}
> +
> +abi_long target_to_host_itimerspec(struct itimerspec *host_itspec,
> + abi_ulong target_addr)
> +{
> + struct target_freebsd_itimerspec *target_itspec;
> +
> + if (!lock_user_struct(VERIFY_READ, target_itspec, target_addr, 1)) {
> + return -TARGET_EFAULT;
> + }
> +
> + host_itspec->it_interval.tv_sec =
> + tswapal(target_itspec->it_interval.tv_sec);
> + host_itspec->it_interval.tv_nsec =
> + tswapal(target_itspec->it_interval.tv_nsec);
> + host_itspec->it_value.tv_sec = tswapal(target_itspec->it_value.tv_sec);
> + host_itspec->it_value.tv_nsec = tswapal(target_itspec->it_value.tv_nsec);
> +
> + unlock_user_struct(target_itspec, target_addr, 0);
> + return 0;
> +}
> +
> +abi_long host_to_target_itimerspec(abi_ulong target_addr,
> + struct itimerspec *host_its)
> +{
> + struct target_freebsd_itimerspec *target_itspec;
> +
> + if (!lock_user_struct(VERIFY_WRITE, target_itspec, target_addr, 0)) {
> + return -TARGET_EFAULT;
> + }
> +
> + target_itspec->it_interval.tv_sec = tswapal(host_its->it_interval.tv_sec);
> + target_itspec->it_interval.tv_nsec = tswapal(host_its->it_interval.tv_nsec);
> +
> + target_itspec->it_value.tv_sec = tswapal(host_its->it_value.tv_sec);
> + target_itspec->it_value.tv_nsec = tswapal(host_its->it_value.tv_nsec);
> +
> + unlock_user_struct(target_itspec, target_addr, 1);
> + return 0;
> +}
> +
> +/* Convert QEMU provided timer ID back to internal 16bit index format */
> +int get_timer_id(abi_long arg)
> +{
> + int timerid = arg;
> +
> + if ((timerid & TIMER_MAGIC_MASK) != TIMER_MAGIC) {
> + return -TARGET_EINVAL;
> + }
> +
> + timerid &= 0xffff;
> +
> + if (timerid >= ARRAY_SIZE(g_posix_timers)) {
> + return -TARGET_EINVAL;
> + }
> +
> + return timerid;
> +}
> diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h
> index f24b6f35db..e91a3f9bcf 100644
> --- a/bsd-user/freebsd/os-time.h
> +++ b/bsd-user/freebsd/os-time.h
> @@ -198,6 +198,9 @@ static inline abi_long do_freebsd_ntp_adjtime(abi_ulong target_tx_addr)
> if (ret == 0) {
> ret = get_errno(ntp_adjtime(&host_tx));
> }
> + if (!is_error(ret)) {
> + h2t_freebsd_timex(target_tx_addr, &host_tx);
> + }
>
> return ret;
> }
>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 23/37] bsd-user: Add time system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (21 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 22/37] bsd-user: Add os-time.c and time-related type definitions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:52 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 24/37] bsd-user: Add thread, umtx, and ACL type definitions Warner Losh
` (14 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Wire up time-related system calls in the FreeBSD syscall dispatcher:
nanosleep, clock_nanosleep, clock_gettime, clock_settime, clock_getres,
gettimeofday, settimeofday, adjtime, ntp_adjtime, ntp_gettime,
clock_getcpuclockid2, utimes, lutimes, futimes, futimesat,
ktimer_create, ktimer_delete, ktimer_settime, ktimer_gettime,
select, pselect, ppoll, kqueue, freebsd11_kevent, kevent,
setitimer, getitimer, futimens, utimensat, and sigtimedwait.
Signed-off-by: Stacey Son <sson@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-syscall.c | 124 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 020d16fda3..cc66be3b7c 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -48,6 +48,7 @@
#include "os-signal.h"
#include "os-file.h"
#include "os-socket.h"
+#include "os-time.h"
#include "os-misc.h"
/* I/O */
@@ -1037,9 +1038,132 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_getdtablesize();
break;
+ /*
+ * time related system calls.
+ */
+ case TARGET_FREEBSD_NR_nanosleep: /* nanosleep(2) */
+ ret = do_freebsd_nanosleep(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_clock_nanosleep: /* clock_nanosleep(2) */
+ ret = do_freebsd_clock_nanosleep(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_clock_gettime: /* clock_gettime(2) */
+ ret = do_freebsd_clock_gettime(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_clock_settime: /* clock_settime(2) */
+ ret = do_freebsd_clock_settime(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_clock_getres: /* clock_getres(2) */
+ ret = do_freebsd_clock_getres(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_gettimeofday: /* gettimeofday(2) */
+ ret = do_freebsd_gettimeofday(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_settimeofday: /* settimeofday(2) */
+ ret = do_freebsd_settimeofday(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_adjtime: /* adjtime(2) */
+ ret = do_freebsd_adjtime(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_ntp_adjtime: /* ntp_adjtime(2) */
+ ret = do_freebsd_ntp_adjtime(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_clock_getcpuclockid2: /* Not documented. */
+ ret = do_freebsd_clock_getcpuclockid2(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_ntp_gettime: /* ntp_gettime(2) */
+ ret = do_freebsd_ntp_gettime(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_utimes: /* utimes(2) */
+ ret = do_freebsd_utimes(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_lutimes: /* lutimes(2) */
+ ret = do_freebsd_lutimes(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_futimes: /* futimes(2) */
+ ret = do_freebsd_futimes(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_futimesat: /* futimesat(2) */
+ ret = do_freebsd_futimesat(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_ktimer_create: /* timer_create(2) */
+ ret = do_freebsd_ktimer_create(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_ktimer_delete: /* timer_delete(2) */
+ ret = do_freebsd_ktimer_delete(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_ktimer_settime: /* timer_settime(2) */
+ ret = do_freebsd_ktimer_settime(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_ktimer_gettime: /* timer_gettime(2) */
+ ret = do_freebsd_ktimer_gettime(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_select: /* select(2) */
+ ret = do_freebsd_select(env, arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_pselect: /* pselect(2) */
+ ret = do_freebsd_pselect(env, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_ppoll: /* ppoll(2) */
+ ret = do_freebsd_ppoll(env, arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_kqueue: /* kqueue(2) */
+ ret = do_freebsd_kqueue();
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_kevent: /* kevent(2) */
+ ret = do_freebsd_freebsd11_kevent(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_kevent: /* kevent(2) */
+ ret = do_freebsd_kevent(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_setitimer: /* setitimer(2) */
+ ret = do_freebsd_setitimer(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getitimer: /* getitimer(2) */
+ ret = do_freebsd_getitimer(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_futimens: /* futimens(2) */
+ ret = do_freebsd_futimens(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_utimensat: /* utimensat(2) */
+ ret = do_freebsd_utimensat(arg1, arg2, arg3, arg4);
+ break;
+
/*
* signal system calls
*/
+ case TARGET_FREEBSD_NR_sigtimedwait: /* sigtimedwait(2) */
+ ret = do_freebsd_sigtimedwait(arg1, arg2, arg3);
+ break;
+
case TARGET_FREEBSD_NR_sigaction: /* sigaction(2) */
ret = do_bsd_sigaction(arg1, arg2, arg3);
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 23/37] bsd-user: Add time system call dispatch
2026-05-18 21:27 ` [PATCH v2 23/37] bsd-user: Add time system call dispatch Warner Losh
@ 2026-05-22 23:52 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:52 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Wire up time-related system calls in the FreeBSD syscall dispatcher:
> nanosleep, clock_nanosleep, clock_gettime, clock_settime, clock_getres,
> gettimeofday, settimeofday, adjtime, ntp_adjtime, ntp_gettime,
> clock_getcpuclockid2, utimes, lutimes, futimes, futimesat,
> ktimer_create, ktimer_delete, ktimer_settime, ktimer_gettime,
> select, pselect, ppoll, kqueue, freebsd11_kevent, kevent,
> setitimer, getitimer, futimens, utimensat, and sigtimedwait.
>
> Signed-off-by: Stacey Son <sson@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-syscall.c | 124 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 124 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 24/37] bsd-user: Add thread, umtx, and ACL type definitions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (22 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 23/37] bsd-user: Add time system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:53 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 25/37] bsd-user: Add cpu_copy and make init_task_state non-static Warner Losh
` (13 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Add target_freebsd_thr_param, target_freebsd_rtprio,
new_freebsd_thread_info_t, TARGET_UMTX_OP_* opcodes, umutex/urwlock
flags, and target_freebsd_acl structures to syscall_defs.h. Add forward
declarations for os-time.c, os-socket.c, os-thread.c, and os-extattr.c
functions to qemu-os.h. Add thread and umtx function declarations to
qemu.h.
Signed-off-by: Stacey Son <sson@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/qemu-os.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++
bsd-user/qemu.h | 41 +++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+)
diff --git a/bsd-user/freebsd/qemu-os.h b/bsd-user/freebsd/qemu-os.h
index 12adc50928..b1ff5c0cd5 100644
--- a/bsd-user/freebsd/qemu-os.h
+++ b/bsd-user/freebsd/qemu-os.h
@@ -32,6 +32,54 @@
struct freebsd11_stat;
+/* os-time.c */
+abi_long t2h_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr);
+abi_long h2t_freebsd_timeval(struct timeval *tv, abi_ulong target_tv_addr);
+
+abi_long t2h_freebsd_timespec(struct timespec *ts, abi_ulong target_ts_addr);
+abi_long h2t_freebsd_timespec(abi_ulong target_ts_addr, struct timespec *ts);
+
+abi_long t2h_freebsd_umtx_time(abi_ulong target_ut_addr,
+ abi_ulong target_ut_size, void *host_t, size_t *host_tsz);
+
+abi_long t2h_freebsd_timex(struct timex *host_tx, abi_ulong target_tx_addr);
+abi_long h2t_freebsd_timex(abi_ulong target_tx_addr, struct timex *host_tx);
+
+abi_long h2t_freebsd_ntptimeval(abi_ulong target_ntv_addr,
+ struct ntptimeval *ntv);
+
+abi_ulong copy_from_user_fdset(fd_set *fds, abi_ulong target_fds_addr, int n);
+abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr,
+ abi_ulong target_fds_addr, int n);
+abi_long copy_to_user_fdset(abi_ulong target_fds_addr, const fd_set *fds,
+ int n);
+
+abi_int next_free_host_timer(void);
+int host_to_target_timerid(int timerid);
+abi_long target_to_host_itimerspec(struct itimerspec *host_itspec,
+ abi_ulong target_addr);
+abi_long host_to_target_itimerspec(abi_ulong target_addr,
+ struct itimerspec *host_its);
+int get_timer_id(abi_long arg);
+
+/* os-socket.c */
+abi_long t2h_freebsd_cmsg(struct msghdr *msgh,
+ struct target_msghdr *target_msgh);
+abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh,
+ struct msghdr *msgh);
+
+/* os-thread.c */
+abi_long t2h_freebsd_rtprio(struct rtprio *host_rtp, abi_ulong target_addr);
+abi_long h2t_freebsd_rtprio(abi_ulong target_addr, struct rtprio *host_rtp);
+abi_long do_freebsd_thr_new(CPUArchState *env, abi_ulong target_param_addr,
+ int32_t param_size);
+
+/* os-extattr.c */
+struct acl;
+abi_long t2h_freebsd_acl(struct acl *host_acl, abi_ulong target_addr);
+abi_long h2t_freebsd_acl(abi_ulong target_addr, struct acl *host_acl);
+abi_long t2h_freebsd_acl_type(acl_type_t *host_type, abi_long target_type);
+
/* os-stat.c */
abi_long h2t_freebsd11_stat(abi_ulong target_addr,
struct freebsd11_stat *host_st);
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 60d1adf560..069baa7011 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -249,6 +249,47 @@ abi_long do_freebsd_sysctlbyname(CPUArchState *env, abi_ulong namep,
abi_ulong newlen);
abi_long do_freebsd_sysarch(CPUArchState *env, abi_long arg1, abi_long arg2);
+/* os-thread.c */
+extern pthread_mutex_t *new_freebsd_thread_lock_ptr;
+extern pthread_mutex_t *freebsd_umtx_wait_lck_ptr;
+void *new_freebsd_thread_start(void *arg);
+abi_long freebsd_lock_umtx(abi_ulong target_addr, abi_long tid,
+ size_t tsz, void *t);
+abi_long freebsd_unlock_umtx(abi_ulong target_addr, abi_long id);
+abi_long freebsd_umtx_wait(abi_ulong targ_addr, abi_ulong id,
+ size_t tsz, void *t);
+abi_long freebsd_umtx_wake(abi_ulong target_addr, uint32_t n_wake);
+abi_long freebsd_umtx_wake_unsafe(abi_ulong target_addr, uint32_t n_wake);
+abi_long freebsd_umtx_mutex_wake(abi_ulong target_addr, abi_long val);
+abi_long freebsd_umtx_wait_uint(abi_ulong obj, uint32_t val, size_t tsz,
+ void *t);
+abi_long freebsd_umtx_wait_uint_private(abi_ulong obj, uint32_t val,
+ size_t tsz, void *t);
+abi_long freebsd_umtx_wake_private(abi_ulong obj, uint32_t val);
+abi_long freebsd_umtx_nwake_private(abi_ulong obj, uint32_t val);
+abi_long freebsd_umtx_mutex_wake2(abi_ulong obj, uint32_t val);
+abi_long freebsd_umtx_sem2_wait(abi_ulong obj, size_t tsz, void *t);
+abi_long freebsd_umtx_sem2_wake(abi_ulong obj);
+abi_long freebsd_umtx_sem_wait(abi_ulong obj, size_t tsz, void *t);
+abi_long freebsd_umtx_sem_wake(abi_ulong obj);
+abi_long freebsd_lock_umutex(abi_ulong target_addr, uint32_t id,
+ void *ts, size_t tsz, int mode, abi_ulong val);
+abi_long freebsd_unlock_umutex(abi_ulong target_addr, uint32_t id);
+abi_long freebsd_cv_wait(abi_ulong target_ucond_addr,
+ abi_ulong target_umtx_addr, struct timespec *ts, int wflags);
+abi_long freebsd_cv_signal(abi_ulong target_ucond_addr);
+abi_long freebsd_cv_broadcast(abi_ulong target_ucond_addr);
+abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag,
+ size_t tsz, void *t);
+abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag,
+ size_t tsz, void *t);
+abi_long freebsd_rw_unlock(abi_ulong target_addr);
+abi_long freebsd_umtx_shm(abi_ulong target_addr, long fflag);
+abi_long freebsd_umtx_robust_list(abi_ulong target_addr, size_t rbsize);
+abi_long freebsd_set_ceiling(abi_ulong target_addr, uint32_t ceiling,
+ uint32_t *old_ceiling);
+CPUArchState *cpu_copy(CPUArchState *env);
+
/* user access */
#define VERIFY_READ PAGE_READ
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 24/37] bsd-user: Add thread, umtx, and ACL type definitions
2026-05-18 21:27 ` [PATCH v2 24/37] bsd-user: Add thread, umtx, and ACL type definitions Warner Losh
@ 2026-05-22 23:53 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:53 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add target_freebsd_thr_param, target_freebsd_rtprio,
> new_freebsd_thread_info_t, TARGET_UMTX_OP_* opcodes, umutex/urwlock
> flags, and target_freebsd_acl structures to syscall_defs.h. Add forward
> declarations for os-time.c, os-socket.c, os-thread.c, and os-extattr.c
> functions to qemu-os.h. Add thread and umtx function declarations to
> qemu.h.
>
> Signed-off-by: Stacey Son <sson@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/qemu-os.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++
> bsd-user/qemu.h | 41 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 89 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 25/37] bsd-user: Add cpu_copy and make init_task_state non-static
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (23 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 24/37] bsd-user: Add thread, umtx, and ACL type definitions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-22 23:53 ` Pierrick Bouvier
2026-05-18 21:27 ` [PATCH v2 26/37] bsd-user: Add os-thread.h with thr and context functions Warner Losh
` (12 subsequent siblings)
37 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add cpu_copy() function for thread creation support and make
init_task_state() non-static so it can be called from os-thread.c.
Move cpu_type to file scope so cpu_copy() can access it.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/main.c | 32 ++++++++++++++++++++++++++++++--
bsd-user/qemu.h | 1 +
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 73aae8c327..3c150b54cd 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -49,6 +49,7 @@
#include "qemu/guest-random.h"
#include "gdbstub/user.h"
#include "exec/page-vary.h"
+#include "exec/watchpoint.h"
#include "host-os.h"
#include "target_arch_cpu.h"
@@ -215,7 +216,7 @@ bool qemu_cpu_is_self(CPUState *cpu)
}
/* Assumes contents are already zeroed. */
-static void init_task_state(TaskState *ts)
+void init_task_state(TaskState *ts)
{
ts->sigaltstack_used = (struct target_sigaltstack) {
.ss_sp = 0,
@@ -224,6 +225,34 @@ static void init_task_state(TaskState *ts)
};
}
+static const char *cpu_type;
+
+CPUArchState *cpu_copy(CPUArchState *env)
+{
+ CPUState *cpu = env_cpu(env);
+ CPUState *new_cpu = cpu_create(cpu_type);
+ CPUArchState *new_env = cpu_env(new_cpu);
+ CPUBreakpoint *bp;
+
+ /* Reset non arch specific state */
+ cpu_reset(new_cpu);
+
+ new_cpu->tcg_cflags = cpu->tcg_cflags;
+ memcpy(new_env, env, sizeof(CPUArchState));
+
+ /*
+ * Clone all break/watchpoints.
+ * Note: Once we support ptrace with hw-debug register access, make sure
+ * BP_CPU break/watchpoints are handled correctly on clone.
+ */
+ QTAILQ_INIT(&new_cpu->breakpoints);
+ QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
+ cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL);
+ }
+
+ return new_env;
+}
+
static QemuPluginList plugins = QTAILQ_HEAD_INITIALIZER(plugins);
void gemu_log(const char *fmt, ...)
@@ -256,7 +285,6 @@ int main(int argc, char **argv)
{
const char *filename;
const char *cpu_model;
- const char *cpu_type;
const char *log_file = NULL;
const char *log_mask = NULL;
const char *seed_optarg = NULL;
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 069baa7011..d2dd8c2ee8 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -122,6 +122,7 @@ struct TaskState {
struct target_sigaltstack sigaltstack_used;
} __attribute__((aligned(16)));
+void init_task_state(TaskState *ts);
void stop_all_tasks(void);
extern const char *interp_prefix;
extern const char *qemu_uname_release;
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 25/37] bsd-user: Add cpu_copy and make init_task_state non-static
2026-05-18 21:27 ` [PATCH v2 25/37] bsd-user: Add cpu_copy and make init_task_state non-static Warner Losh
@ 2026-05-22 23:53 ` Pierrick Bouvier
0 siblings, 0 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-22 23:53 UTC (permalink / raw)
To: Warner Losh, qemu-devel; +Cc: Kyle Evans, Stacey Son
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Add cpu_copy() function for thread creation support and make
> init_task_state() non-static so it can be called from os-thread.c.
> Move cpu_type to file scope so cpu_copy() can access it.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> Assisted-by: Claude Opus 4.6 (1M context)
> ---
> bsd-user/main.c | 32 ++++++++++++++++++++++++++++++--
> bsd-user/qemu.h | 1 +
> 2 files changed, 31 insertions(+), 2 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 26/37] bsd-user: Add os-thread.h with thr and context functions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (24 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 25/37] bsd-user: Add cpu_copy and make init_task_state non-static Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 27/37] bsd-user: Add do_freebsd__umtx_op to os-thread.h Warner Losh
` (11 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans,
Jessica Clarke, Michal Meloun
Add thread management system call shims: thr_self, thr_exit, thr_kill,
thr_kill2, thr_suspend, thr_wake, thr_set_name, rtprio_thread,
getcontext, setcontext, and swapcontext.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Signed-off-by: Michal Meloun <mmel@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-syscall.c | 6 ++
bsd-user/freebsd/os-thread.h | 244 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 250 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index cc66be3b7c..f2c3ff25e8 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -49,6 +49,7 @@
#include "os-file.h"
#include "os-socket.h"
#include "os-time.h"
+#include "os-thread.h"
#include "os-misc.h"
/* I/O */
@@ -90,6 +91,11 @@ safe_syscall6(ssize_t, sendto, int, fd, const void *, buf, size_t, len, int,
safe_syscall3(ssize_t, recvmsg, int, s, struct msghdr *, msg, int, flags);
safe_syscall3(ssize_t, sendmsg, int, s, const struct msghdr *, msg, int, flags);
+/* used in os-thread */
+safe_syscall1(int, thr_suspend, struct timespec *, timeout);
+safe_syscall5(int, _umtx_op, void *, obj, int, op, unsigned long, val, void *,
+ uaddr, void *, uaddr2);
+
/* used in os-time */
safe_syscall2(int, nanosleep, const struct timespec *, rqtp, struct timespec *,
rmtp);
diff --git a/bsd-user/freebsd/os-thread.h b/bsd-user/freebsd/os-thread.h
new file mode 100644
index 0000000000..bb8f279060
--- /dev/null
+++ b/bsd-user/freebsd/os-thread.h
@@ -0,0 +1,244 @@
+/*
+ * FreeBSD thread and user mutex related system call shims
+ *
+ * Copyright (c) 2013-2015 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef FREEBSD_OS_THREAD_H
+#define FREEBSD_OS_THREAD_H
+
+#include <sys/thr.h>
+#include <sys/rtprio.h>
+#include <sys/umtx.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+
+int safe_thr_suspend(struct timespec *timeout);
+int safe__umtx_op(void *, int, unsigned long, void *, void *);
+
+#if defined(HOST_BIG_ENDIAN) == defined(TARGET_BIG_ENDIAN) && \
+ (TARGET_ABI_BITS == HOST_LONG_BITS || defined(UMTX_OP__32BIT))
+#define _UMTX_OPTIMIZED
+#if defined(TARGET_ABI32)
+#define QEMU_UMTX_OP(n) (UMTX_OP__32BIT | (n))
+#else
+#define QEMU_UMTX_OP(n) (n)
+#endif /* TARGET_ABI32 */
+#else
+#define QEMU_UMTX_OP(n) (n)
+#endif
+
+static inline abi_long do_freebsd_thr_self(abi_ulong target_id)
+{
+ abi_long ret;
+ long tid;
+
+ ret = get_errno(thr_self(&tid));
+ if (!is_error(ret)) {
+ if (put_user_sal(tid, target_id)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+static inline abi_long do_freebsd_thr_exit(CPUArchState *env,
+ abi_ulong tid_addr)
+{
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts;
+
+ if (block_signals()) {
+ return -TARGET_ERESTART;
+ }
+
+ pthread_mutex_lock(new_freebsd_thread_lock_ptr);
+
+ ts = cpu->opaque;
+
+ if (tid_addr) {
+ /* Signal target userland that it can free the stack. */
+ if (!put_user_sal(1, tid_addr)) {
+ freebsd_umtx_wake_unsafe(tid_addr, INT_MAX);
+ }
+ }
+
+ object_unparent(OBJECT(env_cpu(env)));
+ object_unref(OBJECT(env_cpu(env)));
+ /*
+ * At this point the CPU should be unrealized and removed
+ * from cpu lists. We can clean-up the rest of the thread
+ * data without the lock held.
+ */
+
+ pthread_mutex_unlock(new_freebsd_thread_lock_ptr);
+
+ thread_cpu = NULL;
+ g_free(ts);
+ rcu_unregister_thread();
+ pthread_exit(NULL);
+ /* Doesn't return */
+ return 0;
+}
+
+static inline abi_long do_freebsd_thr_kill(long id, int sig)
+{
+
+ return get_errno(thr_kill(id, target_to_host_signal(sig)));
+}
+
+static inline abi_long do_freebsd_thr_kill2(pid_t pid, long id, int sig)
+{
+
+ return get_errno(thr_kill2(pid, id, target_to_host_signal(sig)));
+}
+
+static inline abi_long do_freebsd_thr_suspend(abi_ulong target_ts)
+{
+ abi_long ret;
+ struct timespec ts;
+
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(safe_thr_suspend(&ts));
+ } else {
+ ret = get_errno(safe_thr_suspend(NULL));
+ }
+ return ret;
+}
+
+static inline abi_long do_freebsd_thr_wake(long tid)
+{
+
+ return get_errno(thr_wake(tid));
+}
+
+static inline abi_long do_freebsd_thr_set_name(long tid, abi_ulong target_name)
+{
+ abi_long ret;
+ void *p;
+
+ p = lock_user_string(target_name);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(thr_set_name(tid, p));
+ unlock_user(p, target_name, 0);
+
+ return ret;
+}
+
+static inline abi_long do_freebsd_rtprio_thread(int function, lwpid_t lwpid,
+ abi_ulong target_addr)
+{
+ int ret;
+ struct rtprio rtp;
+
+ ret = t2h_freebsd_rtprio(&rtp, target_addr);
+ if (!is_error(ret)) {
+ ret = get_errno(rtprio_thread(function, lwpid, &rtp));
+ }
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_rtprio(target_addr, &rtp);
+ }
+ return ret;
+}
+
+static inline abi_long do_freebsd_getcontext(CPUArchState *env, abi_ulong arg1)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+
+ if (arg1 == 0) {
+ return -TARGET_EINVAL;
+ }
+ ret = do_sigprocmask(0, NULL, &sigmask);
+ if (!is_error(ret)) {
+ ucp = lock_user(VERIFY_WRITE, arg1, sizeof(target_ucontext_t), 0);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_mcontext(env, &ucp->uc_mcontext, TARGET_MC_GET_CLEAR_RET);
+ host_to_target_sigset(&ucp->uc_sigmask, &sigmask);
+ memset(ucp->__spare__, 0, sizeof(ucp->__spare__));
+ unlock_user(ucp, arg1, sizeof(target_ucontext_t));
+ }
+ return ret;
+}
+
+static inline abi_long do_freebsd_setcontext(CPUArchState *env, abi_ulong arg1)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+ if (arg1 == 0) {
+ return -TARGET_EINVAL;
+ }
+ ucp = lock_user(VERIFY_READ, arg1, sizeof(target_ucontext_t), 1);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = set_mcontext(env, &ucp->uc_mcontext, 0);
+ target_to_host_sigset(&sigmask, &ucp->uc_sigmask);
+ unlock_user(ucp, arg1, 0);
+ if (!is_error(ret)) {
+ (void)do_sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ }
+ return ret == 0 ? -TARGET_EJUSTRETURN : ret;
+}
+
+/* swapcontext(2) */
+static inline abi_long do_freebsd_swapcontext(CPUArchState *env, abi_ulong arg1,
+ abi_ulong arg2)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+
+ if (arg1 == 0 || arg2 == 0) {
+ return -TARGET_EINVAL;
+ }
+ /* Save current context in arg1. */
+ ret = do_sigprocmask(0, NULL, &sigmask);
+ if (!is_error(ret)) {
+ ucp = lock_user(VERIFY_WRITE, arg1, sizeof(target_ucontext_t), 0);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_mcontext(env, &ucp->uc_mcontext, TARGET_MC_GET_CLEAR_RET);
+ host_to_target_sigset(&ucp->uc_sigmask, &sigmask);
+ memset(ucp->__spare__, 0, sizeof(ucp->__spare__));
+ unlock_user(ucp, arg1, sizeof(target_ucontext_t));
+ }
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ /* Restore the context in arg2 to the current context. */
+ ucp = lock_user(VERIFY_READ, arg2, sizeof(target_ucontext_t), 1);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = set_mcontext(env, &ucp->uc_mcontext, 0);
+ target_to_host_sigset(&sigmask, &ucp->uc_sigmask);
+ unlock_user(ucp, arg2, 0);
+ if (!is_error(ret)) {
+ (void)do_sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ }
+ return ret == 0 ? -TARGET_EJUSTRETURN : ret;
+}
+
+
+#define safe_g2h_untagged(x) ((x) != 0 ? g2h_untagged(x) : NULL)
+
+/*
+ * undocumented _umtx_op(void *obj, int op, u_long val, void *uaddr,
+ * void *target_time);
+ */
+
+#endif /* FREEBSD_OS_THREAD_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 27/37] bsd-user: Add do_freebsd__umtx_op to os-thread.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (25 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 26/37] bsd-user: Add os-thread.h with thr and context functions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 28/37] bsd-user: Add os-thread.c with umtx, mutex, and thread creation Warner Losh
` (10 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans,
Jessica Clarke, Sean Bruno
Add the _umtx_op system call implementation with support for
UMTX lock/unlock, mutex operations, condition variables, reader-writer
locks, semaphores, shared memory, and robust lists.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-thread.h | 352 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 352 insertions(+)
diff --git a/bsd-user/freebsd/os-thread.h b/bsd-user/freebsd/os-thread.h
index bb8f279060..98fcd61009 100644
--- a/bsd-user/freebsd/os-thread.h
+++ b/bsd-user/freebsd/os-thread.h
@@ -240,5 +240,357 @@ static inline abi_long do_freebsd_swapcontext(CPUArchState *env, abi_ulong arg1,
* undocumented _umtx_op(void *obj, int op, u_long val, void *uaddr,
* void *target_time);
*/
+static inline abi_long do_freebsd__umtx_op(abi_ulong obj, int op, abi_ulong val,
+ abi_ulong uaddr, abi_ulong target_time)
+{
+ abi_long ret;
+#ifndef _UMTX_OPTIMIZED
+ struct _umtx_time ut[2];
+ struct timespec ts;
+ size_t utsz;
+ long tid;
+#endif
+
+ switch (op) {
+ case TARGET_UMTX_OP_WAIT:
+ /* args: obj *, val, (void *)sizeof(ut), ut * */
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait(obj, val, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_umtx_wait(obj, tswapal(val), utsz, &ut);
+ } else {
+ ret = freebsd_umtx_wait(obj, tswapal(val), 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_WAKE:
+ /* args: obj *, nr_wakeup */
+ ret = freebsd_umtx_wake(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_LOCK:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_lock_umutex(obj, 0, safe_g2h_untagged(target_time), uaddr,
+ 0, val);
+#else
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_lock_umutex(obj, tid, ut, utsz, 0, tswapal(val));
+ } else {
+ ret = freebsd_lock_umutex(obj, tid, NULL, 0, 0, tswapal(val));
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_UNLOCK:
+#ifdef _UMTX_OPTIMIZED
+ ret = freebsd_unlock_umutex(obj, 0);
+#else
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_unlock_umutex(obj, tid);
+#endif
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_TRYLOCK:
+#ifdef _UMTX_OPTIMIZED
+ ret = freebsd_lock_umutex(obj, 0, NULL, 0, TARGET_UMUTEX_TRY, val);
+#else
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_lock_umutex(obj, tid, NULL, 0, TARGET_UMUTEX_TRY,
+ tswapal(val));
+#endif
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_WAIT:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_lock_umutex(obj, 0, safe_g2h_untagged(target_time), uaddr,
+ TARGET_UMUTEX_WAIT, val);
+#else
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_lock_umutex(obj, tid, ut, utsz, TARGET_UMUTEX_WAIT,
+ tswapal(val));
+ } else {
+ ret = freebsd_lock_umutex(obj, tid, NULL, 0, TARGET_UMUTEX_WAIT,
+ tswapal(val));
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_WAKE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_mutex_wake(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_SET_CEILING:
+ ret = 0; /* XXX quietly ignore these things for now */
+ break;
+
+ case TARGET_UMTX_OP_CV_WAIT:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_cv_wait(obj, uaddr, safe_g2h_untagged(target_time), val);
+#else
+ /*
+ * Initialization of the struct conv is done by
+ * bzero'ing everything in userland.
+ */
+ if (target_time != 0) {
+ if (t2h_freebsd_timespec(&ts, target_time)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_cv_wait(obj, uaddr, &ts, val);
+ } else {
+ ret = freebsd_cv_wait(obj, uaddr, NULL, val);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_CV_SIGNAL:
+ /*
+ * XXX
+ * User code may check if c_has_waiters is zero. Other
+ * than that it is assume that user code doesn't do
+ * much with the struct conv fields and is pretty
+ * much opauque to userland.
+ */
+ ret = freebsd_cv_signal(obj);
+ break;
+
+ case TARGET_UMTX_OP_CV_BROADCAST:
+ /*
+ * XXX
+ * User code may check if c_has_waiters is zero. Other
+ * than that it is assume that user code doesn't do
+ * much with the struct conv fields and is pretty
+ * much opauque to userland.
+ */
+ ret = freebsd_cv_broadcast(obj);
+ break;
+
+ case TARGET_UMTX_OP_WAIT_UINT:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait_uint(obj, val, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (!access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+ /* args: obj *, val, (void *)sizeof(ut), ut * */
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_umtx_wait_uint(obj, tswap32((uint32_t)val),
+ utsz, &ut);
+ } else {
+ ret = freebsd_umtx_wait_uint(obj, tswap32((uint32_t)val), 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_WAIT_UINT_PRIVATE:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait_uint_private(obj, val, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (!access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+ /* args: obj *, val, (void *)sizeof(ut), ut * */
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_umtx_wait_uint_private(obj, tswap32((uint32_t)val),
+ utsz, &ut);
+ } else {
+ ret = freebsd_umtx_wait_uint_private(obj, tswap32((uint32_t)val),
+ 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_WAKE_PRIVATE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_wake_private(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_RW_RDLOCK:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_rw_rdlock(obj, val, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_rw_rdlock(obj, val, utsz, &ut);
+ } else {
+ ret = freebsd_rw_rdlock(obj, val, 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_RW_WRLOCK:
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_rw_wrlock(obj, val, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_rw_wrlock(obj, val, utsz, &ut);
+ } else {
+ ret = freebsd_rw_wrlock(obj, val, 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_RW_UNLOCK:
+ ret = freebsd_rw_unlock(obj);
+ break;
+
+#ifdef UMTX_OP_MUTEX_WAKE2
+ case TARGET_UMTX_OP_MUTEX_WAKE2:
+ ret = freebsd_umtx_mutex_wake2(obj, val);
+ break;
+#endif /* UMTX_OP_MUTEX_WAKE2 */
+
+#ifdef UMTX_OP_NWAKE_PRIVATE
+ case TARGET_UMTX_OP_NWAKE_PRIVATE:
+ ret = freebsd_umtx_nwake_private(obj, val);
+ break;
+#endif /* UMTX_OP_NWAKE_PRIVATE */
+
+ case TARGET_UMTX_OP_SEM2_WAIT:
+ /* args: obj *, val, (void *)sizeof(ut), ut * */
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(
+ (uaddr > sizeof(struct target_freebsd__umtx_time) ? VERIFY_WRITE :
+ VERIFY_READ), target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_sem2_wait(obj, uaddr,
+ safe_g2h_untagged(target_time));
+#else
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ /* Kernel writes out the ut[1] if utsz >= _umtx_time + timespec. */
+ ret = freebsd_umtx_sem2_wait(obj, utsz, ut);
+ if (ret == -TARGET_EINTR && (ut[0]._flags & UMTX_ABSTIME) == 0 &&
+ utsz >= sizeof(struct target_freebsd__umtx_time) +
+ sizeof(struct target_freebsd_timespec)) {
+ abi_ulong cret;
+
+ cret = h2t_freebsd_timespec(target_time +
+ sizeof(struct target_freebsd__umtx_time), &ut[1]._timeout);
+ if (is_error(cret)) {
+ ret = cret;
+ }
+ }
+ } else {
+ ret = freebsd_umtx_sem2_wait(obj, 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_SEM2_WAKE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_sem2_wake(obj);
+ break;
+ case TARGET_UMTX_OP_SEM_WAIT:
+ /* args: obj *, val, (void *)sizeof(ut), ut * */
+#ifdef _UMTX_OPTIMIZED
+ if (target_time != 0 && !access_ok(VERIFY_READ, target_time, uaddr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_sem_wait(obj, uaddr, safe_g2h_untagged(target_time));
+#else
+ if (target_time != 0) {
+ ret = t2h_freebsd_umtx_time(target_time, uaddr, ut, &utsz);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_umtx_sem_wait(obj, utsz, ut);
+ } else {
+ ret = freebsd_umtx_sem_wait(obj, 0, NULL);
+ }
+#endif
+ break;
+
+ case TARGET_UMTX_OP_SEM_WAKE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_sem_wake(obj);
+ break;
+ case UMTX_OP_SHM:
+ ret = freebsd_umtx_shm(uaddr, val);
+ break;
+ case TARGET_UMTX_OP_ROBUST_LISTS:
+ ret = freebsd_umtx_robust_list(uaddr, val);
+ break;
+ default:
+ return -TARGET_EINVAL;
+ }
+ return ret;
+}
#endif /* FREEBSD_OS_THREAD_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 28/37] bsd-user: Add os-thread.c with umtx, mutex, and thread creation
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (26 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 27/37] bsd-user: Add do_freebsd__umtx_op to os-thread.h Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 29/37] bsd-user: Add thread system call dispatch Warner Losh
` (9 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans,
Jessica Clarke, Sean Bruno, Michal Meloun
Add the FreeBSD threading implementation including: umtx wait/wake
operations, mutex lock/unlock, condition variables, reader-writer
locks, semaphores, thread creation (thr_new), rtprio conversion,
robust list support, and shared memory operations. This is a large
implementation file that provides the backing for the _umtx_op
syscall and related thread operations.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
Signed-off-by: Michal Meloun <mmel@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-thread.c | 1682 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1682 insertions(+)
diff --git a/bsd-user/freebsd/os-thread.c b/bsd-user/freebsd/os-thread.c
new file mode 100644
index 0000000000..dcf2216525
--- /dev/null
+++ b/bsd-user/freebsd/os-thread.c
@@ -0,0 +1,1682 @@
+/*
+ * FreeBSD thr emulation support code
+ *
+ * Copyright (c) 2013-2015 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#include <machine/atomic.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+#include "signal-common.h"
+#include "target_arch_cpu.h"
+#include "target_arch_thread.h"
+#include "tcg/startup.h"
+#include "exec/tb-flush.h"
+
+#include "os-thread.h"
+
+/* #define DEBUG_UMTX(...) fprintf(stderr, __VA_ARGS__) */
+/* #define DEBUG_UMTX(...) qemu_log(__VA_ARGS__) */
+#define DEBUG_UMTX(...)
+
+#define DETECT_DEADLOCK 0
+#define DEADLOCK_TO 1200
+
+#define NEW_STACK_SIZE 0x40000
+
+/* sys/_umtx.h */
+struct target_umtx {
+ abi_ulong u_owner; /* Owner of the mutex. */
+};
+
+struct target_umutex {
+ uint32_t m_owner; /* Owner of the mutex */
+ uint32_t m_flags; /* Flags of the mutex */
+ uint32_t m_ceiling[2]; /* Priority protect ceiling */
+ abi_ulong m_rb_lnk; /* Robust linkage. */
+#if TARGET_ABI_BITS == 32
+ uint32_t m_pad;
+#endif
+ uint32_t m_spare;
+ uint32_t m_count; /* QEMU-internal; takes one spare. */
+};
+
+struct target_ucond {
+ uint32_t c_has_waiters; /* Has waiters in kernel */
+ uint32_t c_flags; /* Flags of the condition variable */
+ uint32_t c_clockid; /* Clock id */
+ uint32_t c_spare[1];
+};
+
+struct target_urwlock {
+ uint32_t rw_state;
+ uint32_t rw_flags;
+ uint32_t rw_blocked_readers;
+ uint32_t rw_blocked_writers;
+ uint32_t rw_spare[4];
+};
+
+struct target__usem {
+ uint32_t _has_waiters;
+ uint32_t _count;
+ uint32_t _flags;
+};
+
+struct target__usem2 {
+ uint32_t _count;
+ uint32_t _flags;
+};
+
+struct target_umtx_robust_lists_params {
+ abi_ulong robust_list_offset;
+#if TARGET_ABI_BITS == 32
+ uint32_t m_pad1;
+#endif
+ abi_ulong robust_priv_list_offset;
+#if TARGET_ABI_BITS == 32
+ uint32_t m_pad2;
+#endif
+ abi_ulong robust_inact_offset;
+#if TARGET_ABI_BITS == 32
+ uint32_t m_pad3;
+#endif
+};
+
+/*
+ * !!! These mutexes must be reset in fork_end() (in bsd-user/main.c).
+ */
+static pthread_mutex_t new_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t *new_freebsd_thread_lock_ptr = &new_thread_lock;
+static pthread_mutex_t umtx_wait_lck = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t *freebsd_umtx_wait_lck_ptr = &umtx_wait_lck;
+
+static void rtp_to_schedparam(const struct rtprio *rtp, int *policy,
+ struct sched_param *param)
+{
+
+ switch (rtp->type) {
+ case RTP_PRIO_REALTIME:
+ *policy = SCHED_RR;
+ param->sched_priority = RTP_PRIO_MAX - rtp->prio;
+ break;
+
+ case RTP_PRIO_FIFO:
+ *policy = SCHED_FIFO;
+ param->sched_priority = RTP_PRIO_MAX - rtp->prio;
+ break;
+
+ default:
+ *policy = SCHED_OTHER;
+ param->sched_priority = 0;
+ break;
+ }
+}
+
+void *new_freebsd_thread_start(void *arg)
+{
+ new_freebsd_thread_info_t *info = arg;
+ CPUArchState *env;
+ CPUState *cpu;
+ long tid;
+
+ rcu_register_thread();
+ tcg_register_thread();
+ env = info->env;
+ cpu = env_cpu(env);
+ thread_cpu = cpu;
+ (void)thr_self(&tid);
+
+ /* copy out the child TID to both locations */
+ if (info->param.child_tid) {
+ put_user_ual(tid, info->param.child_tid);
+ }
+ if (info->param.parent_tid) {
+ put_user_ual(tid, info->param.parent_tid);
+ }
+
+ /* Set arch dependent registers to start thread. */
+ target_thread_set_upcall(env, info->param.start_func, info->param.arg,
+ info->param.stack_base, info->param.stack_size);
+ target_cpu_set_tls(env, info->param.tls_base);
+
+ /* Enable signals */
+ sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
+ /* Signal to the parent that we're ready. */
+ pthread_mutex_lock(&info->mutex);
+ pthread_cond_broadcast(&info->cond);
+ pthread_mutex_unlock(&info->mutex);
+ /* Wait until the parent has finished. */
+ pthread_mutex_lock(new_freebsd_thread_lock_ptr);
+ pthread_mutex_unlock(new_freebsd_thread_lock_ptr);
+
+ cpu_loop(env);
+ /* never exits */
+
+ return NULL;
+}
+
+/*
+ * FreeBSD user mutex (_umtx) emulation
+ */
+static int tcmpset_al(abi_ulong *addr, abi_ulong a, abi_ulong b)
+{
+ abi_ulong current = tswapal(a);
+ abi_ulong new = tswapal(b);
+
+#ifdef TARGET_ABI32
+ return atomic_cmpset_acq_32(addr, current, new);
+#else
+ return atomic_cmpset_acq_64(addr, current, new);
+#endif
+}
+
+#ifdef _UMTX_OPTIMIZED
+static int optimized_umtx_op(abi_ulong obj, int op, abi_ulong val,
+ void *uaddr1, void *uaddr2)
+{
+
+ return get_errno(safe__umtx_op(g2h_untagged(obj), QEMU_UMTX_OP(op), val,
+ uaddr1, uaddr2));
+}
+
+#else /* !_UMTX_OPTIMIZED */
+
+/*
+ * _cv_mutex keeps other threads from doing a signal or broadcast until
+ * the thread is actually asleep and ready. This is a global mutex for all
+ * condition vars so I am sure performance may be a problem if there are lots
+ * of CVs.
+ *
+ */
+static struct umutex _cv_mutex;
+
+static int tcmpset_32(uint32_t *addr, uint32_t a, uint32_t b)
+{
+ uint32_t current = tswap32(a);
+ uint32_t new = tswap32(b);
+
+ return atomic_cmpset_acq_32(addr, current, new);
+}
+
+#endif /* _UMTX_OPTIMIZED */
+
+static abi_long _umtx_wait_uint(uint32_t *addr, uint32_t target_val,
+ size_t tsz, void *t, const char *where)
+{
+#if DETECT_DEADLOCK
+ abi_long ret;
+ long cnt = 0;
+
+ /* target_val has already been tswap'ed. */
+ if (t == NULL) {
+ struct timespec ts;
+
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+
+ do {
+ if (target_val != *addr) {
+ return 0;
+ }
+
+ ret = get_errno(safe__umtx_op(addr, QEMU_UMTX_OP(UMTX_OP_WAIT_UINT),
+ target_val, NULL, &ts));
+
+ if (ret != -TARGET_ETIMEDOUT) {
+ return ret;
+ }
+ if (cnt++ > DEADLOCK_TO) {
+ fprintf(stderr, "QEMU: Deadlock in %s from %s\n",
+ __func__, where);
+ abort();
+ }
+ } while (1);
+ } else
+#endif
+ return get_errno(safe__umtx_op(addr, QEMU_UMTX_OP(UMTX_OP_WAIT_UINT),
+ target_val, (void *)tsz, t));
+}
+
+abi_long freebsd_umtx_wait_uint(abi_ulong obj, uint32_t target_val,
+ size_t tsz, void *t)
+{
+ /* target_val has already been tswap'ed. */
+
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x, %d, %p)\n", __func__,
+ g2h_untagged(obj), UMTX_OP_WAIT_UINT, target_val, (int)tsz, t);
+
+ return _umtx_wait_uint(g2h_untagged(obj), target_val, tsz, t, __func__);
+}
+
+static abi_long _umtx_wait_uint_private(uint32_t *addr, uint32_t target_val,
+ size_t tsz, void *t, const char *where)
+{
+#if DETECT_DEADLOCK
+ abi_long ret;
+ long cnt = 0;
+
+ /* target_val has already been tswap'ed. */
+ if (t == NULL) {
+ struct timespec ts;
+
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+
+ do {
+ if (target_val != *addr) {
+ return 0;
+ }
+
+ ret = get_errno(safe__umtx_op(addr,
+ QEMU_UMTX_OP(UMTX_OP_WAIT_UINT_PRIVATE), target_val, NULL,
+ &ts));
+
+ if (ret != -TARGET_ETIMEDOUT) {
+ return ret;
+ }
+ if (cnt++ > DEADLOCK_TO) {
+ fprintf(stderr, "QEMU: Deadlock in %s from %s\n", __func__,
+ where);
+ abort();
+ }
+ } while (1);
+ } else
+#endif /* DETECT_DEADLOCK */
+ {
+ return get_errno(safe__umtx_op(addr,
+ QEMU_UMTX_OP(UMTX_OP_WAIT_UINT_PRIVATE), target_val, (void *)tsz,
+ t));
+ }
+}
+
+abi_long freebsd_umtx_wait_uint_private(abi_ulong obj, uint32_t target_val,
+ size_t tsz, void *t)
+{
+ DEBUG_UMTX("<WAIT_UINT_PRIVATE> %s: _umtx_op(%p (%u), %d, 0x%x, %d, %p)\n",
+ __func__, g2h_untagged(obj), tswap32(*(uint32_t *)g2h_untagged(obj)),
+ UMTX_OP_WAIT_UINT_PRIVATE, target_val, (int)tsz, t);
+
+ return _umtx_wait_uint_private(g2h_untagged(obj), target_val, tsz, t,
+ __func__);
+}
+
+static abi_long _umtx_wait(abi_ulong *addr, abi_ulong target_val, size_t tsz,
+ void *t, const char *where)
+{
+#if DETECT_DEADLOCK
+ abi_long ret;
+ long cnt = 0;
+
+ /* target_val has already been tswap'ed. */
+
+ if (t == NULL) {
+ struct timespec ts;
+
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+
+ do {
+ if (target_val != *addr) {
+ return 0;
+ }
+
+ ret = get_errno(safe__umtx_op(addr, QEMU_UMTX_OP(UMTX_OP_WAIT),
+ target_val, NULL, &ts));
+ if (ret != -TARGET_ETIMEDOUT) {
+ return ret;
+ }
+
+ if (cnt++ > DEADLOCK_TO) {
+ fprintf(stderr, "QEMU: Deadlock in %s from %s\n", __func__,
+ where);
+ abort();
+ }
+ } while (1);
+ } else
+#endif /* DETECT_DEADLOCK */
+ {
+ return get_errno(safe__umtx_op(addr, QEMU_UMTX_OP(UMTX_OP_WAIT),
+ target_val, (void *)tsz, t));
+ }
+}
+
+abi_long freebsd_umtx_wait(abi_ulong targ_addr, abi_ulong target_id, size_t tsz,
+ void *t)
+{
+
+ /* target_id has already been tswap'ed. */
+
+ /* We want to check the user memory but not lock it. We might sleep. */
+ if (!access_ok(VERIFY_READ, targ_addr, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%llx, %d, %p)\n",
+ __func__, g2h_untagged(targ_addr), UMTX_OP_WAIT, (long long)target_id,
+ (int)tsz, t);
+ return _umtx_wait(g2h_untagged(targ_addr), target_id, tsz, t, __func__);
+}
+
+
+abi_long freebsd_umtx_wake_private(abi_ulong obj, uint32_t val)
+{
+
+ DEBUG_UMTX("<WAKE_PRIVATE> %s: _umtx_op(%p (%d), %d, %u, NULL, NULL)\n",
+ __func__, g2h_untagged(obj), tswap32(*(uint32_t *)g2h_untagged(obj)),
+ UMTX_OP_WAKE_PRIVATE, val);
+ return get_errno(safe__umtx_op(g2h_untagged(obj),
+ QEMU_UMTX_OP(UMTX_OP_WAKE_PRIVATE), val, NULL, NULL));
+}
+
+#if defined(UMTX_OP_NWAKE_PRIVATE)
+#define BATCH_SIZE 128
+abi_long freebsd_umtx_nwake_private(abi_ulong target_array_addr, uint32_t num)
+{
+#ifdef _UMTX_OPTIMIZED
+ abi_ulong *tp;
+ uintptr_t uaddrs[BATCH_SIZE];
+ int count, error, i, j;
+
+ if (!access_ok(VERIFY_READ, target_array_addr, num * sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+
+ /*
+ * If we haven't relocated the guest, there's a 1:1 mapping so we can avoid
+ * having to g2h_untagged() each address and just pass it through as-is.
+ */
+ if (!have_guest_base && !reserved_va) {
+ return optimized_umtx_op(target_array_addr, UMTX_OP_NWAKE_PRIVATE, num,
+ NULL, NULL);
+ }
+
+ tp = (abi_ulong *)g2h_untagged(target_array_addr);
+ for (i = 0, count = num; i < num; i += BATCH_SIZE, count -= BATCH_SIZE) {
+ for (j = i; j < i + MIN(BATCH_SIZE, count); j++) {
+ uaddrs[j % BATCH_SIZE] = (uintptr_t)g2h_untagged(tp[j]);
+ }
+
+ /*
+ * This one should not be passed as compat32 at this point; we've
+ * converted them all to host pointers.
+ */
+ error = safe__umtx_op(uaddrs, UMTX_OP_NWAKE_PRIVATE,
+ MIN(BATCH_SIZE, count), NULL, NULL);
+ if (error != 0) {
+ return error;
+ }
+ }
+
+ return 0;
+#else
+ int i;
+ abi_ulong *uaddr;
+ abi_long ret = 0;
+
+ DEBUG_UMTX("<NWAKE_PRIVATE> %s: _umtx_op(%p, %d, %d, NULL, NULL) Waking: ",
+ __func__, g2h_untagged(target_array_addr), UMTX_OP_NWAKE_PRIVATE, num);
+
+ if (!access_ok(VERIFY_READ, target_array_addr, num * sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+
+ uaddr = (abi_ulong *)g2h_untagged(target_array_addr);
+ for (i = 0; i < (int32_t)num; i++) {
+ DEBUG_UMTX("%p (%u) ", g2h_untagged(tswapal(uaddr[i])),
+ tswap32(*(uint32_t *)g2h_untagged(tswapal(uaddr[i]))));
+ ret = get_errno(safe__umtx_op(g2h_untagged(tswapal(uaddr[i])),
+ UMTX_OP_WAKE_PRIVATE, INT_MAX, NULL, NULL));
+ if (is_error(ret)) {
+ DEBUG_UMTX("\n");
+ return ret;
+ }
+ }
+ DEBUG_UMTX("\n");
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+#endif /* UMTX_OP_NWAKE_PRIVATE */
+
+#if defined(UMTX_OP_MUTEX_WAKE2)
+abi_long freebsd_umtx_mutex_wake2(abi_ulong target_addr, uint32_t flags)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_umutex))) {
+ return -TARGET_EFAULT;
+ }
+
+ DEBUG_UMTX("<MUTEX WAKE2> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_MUTEX_WAKE2, flags);
+ return optimized_umtx_op(target_addr, UMTX_OP_MUTEX_WAKE2, flags, NULL,
+ NULL);
+#else
+ uint32_t count, owner, *addr;
+ struct target_umutex *target_umutex;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ pthread_mutex_lock(&umtx_wait_lck);
+ __get_user(count, &target_umutex->m_count);
+ __get_user(owner, &target_umutex->m_owner);
+ while ((owner & TARGET_UMUTEX_CONTESTED) == 0 && (count > 1 ||
+ (count == 1 && (owner & ~TARGET_UMUTEX_CONTESTED) != 0))) {
+ if (tcmpset_32(&target_umutex->m_owner, owner,
+ (owner | TARGET_UMUTEX_CONTESTED)))
+ break;
+
+ /* owner has changed */
+ __get_user(owner, &target_umutex->m_owner);
+ }
+ pthread_mutex_unlock(&umtx_wait_lck);
+ addr = &target_umutex->m_owner;
+ /* tcmpset_32 above writes to guest, and addr is just a key below */
+ unlock_user_struct(target_umutex, target_addr, 0);
+
+ return get_errno(safe__umtx_op(addr, UMTX_OP_WAKE_PRIVATE, 1, NULL,
+ NULL));
+#endif /* _UMTX_OPTIMIZED */
+}
+#endif /* UMTX_OP_MUTEX_WAKE2 */
+
+abi_long freebsd_umtx_sem2_wait(abi_ulong obj, size_t tsz, void *t)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, obj, sizeof(struct target__usem2))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(obj, UMTX_OP_SEM2_WAIT, 1,
+ (void *)(uintptr_t)tsz, t);
+#else
+ struct target__usem2 *t__usem2;
+ uint32_t count, flags;
+ uint32_t *addr;
+ abi_long ret = 0;
+
+ if (!lock_user_struct(VERIFY_WRITE, t__usem2, obj, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ /*
+ * Make sure the count field has the has USEM_HAS_WAITERS flag set
+ * so userland will always call freebsd_umtx_sem2_wake().
+ */
+ for (;;) {
+ __get_user(count, &t__usem2->_count);
+ if (USEM_COUNT(count) != 0) {
+ unlock_user_struct(t__usem2, obj, 1);
+ return 0;
+ }
+ if ((count & USEM_HAS_WAITERS) != 0) {
+ break;
+ }
+ if (tcmpset_32(&t__usem2->_count, count, (count | USEM_HAS_WAITERS))) {
+ break;
+ }
+ }
+
+ __get_user(flags, &t__usem2->_flags);
+ addr = &t__usem2->_count;
+ unlock_user_struct(t__usem2, obj, 1);
+
+ if ((flags & USYNC_PROCESS_SHARED) == 0) {
+ DEBUG_UMTX("<WAIT SEM2> %s: _umtx_op(%p, %d, %p)\n",
+ __func__, addr, UMTX_OP_WAIT_UINT_PRIVATE, (int)tsz, t);
+
+#if DETECT_DEADLOCK
+ if (t != NULL) {
+ ret = _umtx_wait_uint_private(addr, tswap32(USEM_HAS_WAITERS), tsz,
+ t, __func__);
+ } else {
+ for (;;) {
+ struct timespec ts;
+
+ ts.tv_sec = 120;
+ ts.tv_nsec = 0;
+
+ ret = _umtx_wait_uint_private(addr, tswap32(USEM_HAS_WAITERS),
+ 0, (void *)&ts, __func__);
+ if (ret == 0) {
+ break;
+ }
+ if (ret != -ETIMEDOUT) {
+ break;
+ }
+ if (!lock_user_struct(VERIFY_READ, t__usem2, obj, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(count, &t__usem2->_count);
+ unlock_user_struct(t__usem2, obj, 0);
+ if (USEM_COUNT(count) != 0) {
+ fprintf(stderr, "QEMU:(%s) TIMEOUT (count!=0)\n", __func__);
+ ret = 0;
+ break;
+ }
+ if (ret == -ETIMEDOUT) {
+ fprintf(stderr, "QEMU:(%s) TIMEOUT (exiting)\n", __func__);
+ exit(-1);
+ }
+ }
+ }
+#else
+ ret = _umtx_wait_uint_private(addr, tswap32(USEM_HAS_WAITERS),
+ tsz, t, __func__);
+#endif /* DETECT_DEADLOCK */
+ } else {
+ DEBUG_UMTX("<WAIT SEM2> %s: _umtx_op(%p, %d, %p)\n",
+ __func__, addr, UMTX_OP_WAIT_UINT, (int)tsz, t);
+#if DETECT_DEADLOCK
+ if (t != NULL) {
+ ret = _umtx_wait_uint(addr, tswap32(USEM_HAS_WAITERS), tsz, t,
+ __func__);
+ } else {
+ for (;;) {
+ struct timespec ts;
+
+ ts.tv_sec = 120;
+ ts.tv_nsec = 0;
+
+ ret = _umtx_wait_uint(addr, tswap32(USEM_HAS_WAITERS), 0,
+ (void *)&ts, __func__);
+ if (ret == 0) {
+ break;
+ }
+ if (ret != -ETIMEDOUT) {
+ break;
+ }
+ if (!lock_user_struct(VERIFY_READ, t__usem2, obj, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(count, &t__usem2->_count);
+ unlock_user_struct(t__usem2, obj, 0);
+ if (USEM_COUNT(count) != 0) {
+ fprintf(stderr, "QEMU:(%s) TIMEOUT (count!=0)\n", __func__);
+ ret = 0;
+ break;
+ }
+ if (ret == -ETIMEDOUT) {
+ fprintf(stderr, "QEMU:(%s) TIMEOUT (exiting)\n", __func__);
+ exit(-1);
+ }
+ }
+ }
+#else
+ ret = _umtx_wait_uint(addr, tswap32(USEM_HAS_WAITERS), tsz, t,
+ __func__);
+#endif /* DETECT_DEADLOCK */
+ }
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_umtx_sem2_wake(abi_ulong obj)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_READ, obj, sizeof(struct target__usem2))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(obj, UMTX_OP_SEM2_WAKE, 1, NULL, NULL);
+#else
+ struct target__usem2 *t__usem2;
+ uint32_t *addr, flags;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_READ, t__usem2, obj, 1)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(flags, &t__usem2->_flags);
+ addr = &t__usem2->_count;
+ unlock_user_struct(t__usem2, obj, 0);
+
+ if ((flags & USYNC_PROCESS_SHARED) == 0) {
+ DEBUG_UMTX("<WAKE SEM2> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, addr, UMTX_OP_WAKE_PRIVATE, INT_MAX);
+ ret = get_errno(safe__umtx_op(addr, UMTX_OP_WAKE_PRIVATE, INT_MAX, NULL,
+ NULL));
+ } else {
+ DEBUG_UMTX("<WAKE SEM2> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, addr, UMTX_OP_WAKE, INT_MAX);
+ ret = get_errno(safe__umtx_op(addr, UMTX_OP_WAKE, INT_MAX, NULL, NULL));
+ }
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_umtx_sem_wait(abi_ulong obj, size_t tsz, void *t)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, obj, sizeof(struct target__usem))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(obj, UMTX_OP_SEM_WAIT, 1,
+ (void *)(uintptr_t)tsz, t);
+#else
+ struct target__usem *t__usem;
+ uint32_t count, flags, *addr;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_WRITE, t__usem, obj, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(count, &t__usem->_count);
+ if (count != 0) {
+ unlock_user_struct(t__usem, obj, 1);
+ return 0;
+ }
+
+ /*
+ * Make sure the _has_waiters field is set so userland will always
+ * call freebsd_umtx_sem_wake().
+ */
+ __put_user(1, &t__usem->_has_waiters);
+
+ __get_user(flags, &t__usem->_flags);
+ addr = &t__usem->_count;
+ unlock_user_struct(t__usem, obj, 1);
+
+ if ((flags & USYNC_PROCESS_SHARED) == 0) {
+ DEBUG_UMTX("<WAIT SEM> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, &t__usem->_count, UMTX_OP_WAKE_PRIVATE, 0);
+ ret = _umtx_wait_uint_private(addr, 0, tsz, t, __func__);
+ } else {
+ DEBUG_UMTX("<WAIT SEM> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, &t__usem->_count, UMTX_OP_WAKE, 0);
+ ret = _umtx_wait_uint(addr, 0, tsz, t, __func__);
+ }
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_umtx_sem_wake(abi_ulong obj)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, obj, sizeof(struct target__usem))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(obj, UMTX_OP_SEM_WAKE, 1, NULL, NULL);
+#else
+ struct target__usem *t__usem;
+ uint32_t flags, *addr;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_READ, t__usem, obj, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(flags, &t__usem->_flags);
+ addr = &t__usem->_count;
+ unlock_user_struct(t__usem, obj, 0);
+
+ if ((flags & USYNC_PROCESS_SHARED) == 0) {
+ DEBUG_UMTX("<WAKE SEM> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, &t__usem->_count, UMTX_OP_WAKE_PRIVATE, 1);
+ ret = get_errno(safe__umtx_op(addr, UMTX_OP_WAKE_PRIVATE, 1, NULL,
+ NULL));
+ } else {
+ DEBUG_UMTX("<WAKE SEM> %s: _umtx_op(%p, %d, %d, NULL, NULL)\n",
+ __func__, &t__usem->_count, UMTX_OP_WAKE, 1);
+ ret = get_errno(safe__umtx_op(addr, UMTX_OP_WAKE, 1, NULL, NULL));
+ }
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long t2h_freebsd_rtprio(struct rtprio *host_rtp, abi_ulong target_addr)
+{
+ struct target_freebsd_rtprio *target_rtp;
+
+ if (!lock_user_struct(VERIFY_READ, target_rtp, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(host_rtp->type, &target_rtp->type);
+ __get_user(host_rtp->prio, &target_rtp->prio);
+ unlock_user_struct(target_rtp, target_addr, 0);
+ return 0;
+}
+
+abi_long h2t_freebsd_rtprio(abi_ulong target_addr, struct rtprio *host_rtp)
+{
+ struct target_freebsd_rtprio *target_rtp;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_rtp, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(host_rtp->type, &target_rtp->type);
+ __put_user(host_rtp->prio, &target_rtp->prio);
+ unlock_user_struct(target_rtp, target_addr, 1);
+ return 0;
+}
+
+/* XXX We should never see this? OP_LOCK and OP_UNLOCK are now RESERVED{0,1} */
+abi_long freebsd_lock_umtx(abi_ulong target_addr, abi_long id, size_t tsz,
+ void *t)
+{
+ abi_long ret;
+ abi_long owner;
+
+ gemu_log("This is unreachable.");
+
+ /*
+ * XXX Note that memory at umtx_addr can change and so we need to be
+ * careful and check for faults.
+ */
+ for (;;) {
+ struct target_umtx *target_umtx;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ /* Check the simple uncontested case. */
+ if (tcmpset_al(&target_umtx->u_owner,
+ TARGET_UMTX_UNOWNED, id)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ /* Check to see if the lock is contested but free. */
+ __get_user(owner, &target_umtx->u_owner);
+
+ if (TARGET_UMTX_CONTESTED == owner) {
+ if (tcmpset_al(&target_umtx->u_owner, TARGET_UMTX_CONTESTED,
+ id | TARGET_UMTX_CONTESTED)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ /* We failed because it changed on us, restart. */
+ unlock_user_struct(target_umtx, target_addr, 1);
+ continue;
+ }
+
+ /* Set the contested bit and sleep. */
+ do {
+ __get_user(owner, &target_umtx->u_owner);
+ if (owner & TARGET_UMTX_CONTESTED) {
+ break;
+ }
+ } while (!tcmpset_al(&target_umtx->u_owner, owner,
+ owner | TARGET_UMTX_CONTESTED));
+
+ __get_user(owner, &target_umtx->u_owner);
+ unlock_user_struct(target_umtx, target_addr, 1);
+
+ /* Byte swap, if needed, to match what is stored in user mem. */
+ owner = tswapal(owner);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%llx, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_WAIT,
+ (long long)owner);
+ ret = _umtx_wait(g2h_untagged(target_addr), owner, tsz, t, __func__);
+ if (is_error(ret)) {
+ return ret;
+ }
+ }
+}
+
+/* XXX We should never see this? OP_LOCK and OP_UNLOCK are now RESERVED{0,1} */
+abi_long freebsd_unlock_umtx(abi_ulong target_addr, abi_long id)
+{
+ abi_ulong owner;
+ struct target_umtx *target_umtx;
+
+ gemu_log("This is unreachable.");
+ if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(owner, &target_umtx->u_owner);
+ if ((owner & ~TARGET_UMTX_CONTESTED) != id) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ /* Check the simple uncontested case. */
+ if ((owner & ~TARGET_UMTX_CONTESTED) == 0) {
+ if (tcmpset_al(&target_umtx->u_owner, owner,
+ TARGET_UMTX_UNOWNED)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ }
+ /* This is a contested lock. Unlock it. */
+ __put_user(TARGET_UMTX_UNOWNED, &target_umtx->u_owner);
+ unlock_user_struct(target_umtx, target_addr, 1);
+
+ /* Wake up all those contesting it. */
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_WAKE, 0);
+ return get_errno(safe__umtx_op(g2h_untagged(target_addr),
+ QEMU_UMTX_OP(UMTX_OP_WAKE), 0, 0, 0));
+}
+
+abi_long freebsd_umtx_wake(abi_ulong target_addr, uint32_t n_wake)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_WAKE, n_wake);
+ return get_errno(safe__umtx_op(g2h_untagged(target_addr),
+ QEMU_UMTX_OP(UMTX_OP_WAKE), n_wake, NULL, 0));
+}
+
+abi_long freebsd_umtx_wake_unsafe(abi_ulong target_addr, uint32_t n_wake)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_WAKE, n_wake);
+ return get_errno(_umtx_op(g2h_untagged(target_addr),
+ QEMU_UMTX_OP(UMTX_OP_WAKE), n_wake, NULL, 0));
+}
+
+abi_long freebsd_umtx_mutex_wake(abi_ulong obj, abi_long val)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%llx, NULL, NULL)\n",
+ __func__, g2h_untagged(obj), UMTX_OP_WAKE, (long long)val);
+ return get_errno(safe__umtx_op(g2h_untagged(obj),
+ QEMU_UMTX_OP(UMTX_OP_MUTEX_WAKE), val, NULL, NULL));
+}
+
+abi_long freebsd_lock_umutex(abi_ulong target_addr, uint32_t id,
+ void *ts, size_t tsz, int mode, abi_ulong val)
+{
+#ifdef _UMTX_OPTIMIZED
+ int op;
+
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_umutex))) {
+ return -TARGET_EFAULT;
+ }
+
+ switch (mode) {
+ case TARGET_UMUTEX_WAIT:
+ op = UMTX_OP_MUTEX_WAIT;
+ DEBUG_UMTX("<MUTEX WAIT> %s: _umtx_op(%p, %d, 0x%llx, %p, %p)\n",
+ __func__, g2h_untagged(target_addr), op, (long long)val,
+ (void *)(uintptr_t)tsz, ts);
+ break;
+ case TARGET_UMUTEX_TRY:
+ op = UMTX_OP_MUTEX_TRYLOCK;
+ DEBUG_UMTX("<MUTEX TRYLOCK> %s: _umtx_op(%p, %d, 0x%llx, %p, %p)\n",
+ __func__, g2h_untagged(target_addr), op, (long long)val,
+ (void *)(uintptr_t)tsz, ts);
+ break;
+ default:
+ op = UMTX_OP_MUTEX_LOCK;
+ DEBUG_UMTX("<MUTEX LOCK> %s: _umtx_op(%p, %d, 0x%llx, %p, %p)\n",
+ __func__, g2h_untagged(target_addr), op, (long long)val,
+ (void *)(uintptr_t)tsz, ts);
+ break;
+ }
+
+ return optimized_umtx_op(target_addr, op, val, (void *)(uintptr_t)tsz, ts);
+#else
+ struct target_umutex *target_umutex;
+ uint32_t owner, flags, count, *addr;
+ int ret = 0;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ for (;;) {
+
+ __get_user(owner, &target_umutex->m_owner);
+
+ if ((owner & ~TARGET_UMUTEX_CONTESTED) == 0) {
+ /* Lock is unowned. */
+ if (TARGET_UMUTEX_WAIT == mode) {
+ /* Waiting on an unlocked mutex; bail out. */
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return 0;
+ }
+
+ /* Attempt to acquire it, preserve the contested bit ("owner"). */
+ while ((owner & ~TARGET_UMUTEX_CONTESTED) == 0 &&
+ !tcmpset_32(&target_umutex->m_owner, owner, owner | id)) {
+ __get_user(owner, &target_umutex->m_owner);
+ }
+
+ if ((owner & ~TARGET_UMUTEX_CONTESTED) == 0) {
+ /*
+ * The acquire succeeded, because we didn't observe owner with
+ * a different id.
+ */
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return 0;
+ }
+
+ /* Otherwise, someone beat us to it; carry on. */
+ }
+
+ __get_user(flags, &target_umutex->m_flags);
+ if ((flags & TARGET_UMUTEX_ERROR_CHECK) != 0 &&
+ (owner & ~TARGET_UMUTEX_CONTESTED) == id) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return -TARGET_EDEADLK;
+ }
+
+ if (TARGET_UMUTEX_TRY == mode) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return -TARGET_EBUSY;
+ }
+
+ /* Set the contested bit and sleep. */
+ while ((owner & TARGET_UMUTEX_CONTESTED) == 0) {
+ if (tcmpset_32(&target_umutex->m_owner, owner,
+ owner | TARGET_UMUTEX_CONTESTED)) {
+ /*
+ * Keep our local view of owner consistent with what we think
+ * we've set it to. We're about to sleep on it, and we don't
+ * really want a spurious return from _umtx_op because of this.
+ */
+ owner |= TARGET_UMUTEX_CONTESTED;
+
+ break;
+ } else {
+ __get_user(owner, &target_umutex->m_owner);
+ }
+ }
+
+ /*
+ * If it changed during the above loop, we may be able to acquire now.
+ */
+ if ((owner & ~TARGET_UMUTEX_CONTESTED) == 0) {
+ continue;
+ }
+
+ pthread_mutex_lock(&umtx_wait_lck);
+ __get_user(count, &target_umutex->m_count);
+ count++;
+ __put_user(count, &target_umutex->m_count);
+ pthread_mutex_unlock(&umtx_wait_lck);
+
+ addr = &target_umutex->m_owner;
+
+ /* addr used only as key below, not dereferenced */
+ unlock_user_struct(target_umutex, target_addr, 1);
+
+ DEBUG_UMTX("<WAIT UMUTEX> %s: _umtx_op(%p, %d, 0x%x, %d, %jx) "
+ "count = %d\n", __func__, g2h_untagged(target_addr),
+ UMTX_OP_WAIT_PRIVATE, tswap32(target_umutex->m_owner), tsz,
+ (uintmax_t)ts, count);
+ ret = _umtx_wait_uint_private(addr, owner, tsz, (void *)ts, __func__);
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ pthread_mutex_lock(&umtx_wait_lck);
+ __get_user(count, &target_umutex->m_count);
+ count--;
+ __put_user(count, &target_umutex->m_count);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ if (ret != 0) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ break;
+ }
+ }
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_unlock_umutex(abi_ulong target_addr, uint32_t id)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_umutex))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_addr, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+#else
+ struct target_umutex *target_umutex;
+ uint32_t count, owner, *addr, flags;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ /* Make sure we own this mutex. */
+ __get_user(owner, &target_umutex->m_owner);
+ if ((owner & ~TARGET_UMUTEX_CONTESTED) != id) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ pthread_mutex_lock(&umtx_wait_lck);
+ __get_user(count, &target_umutex->m_count);
+
+ /* Unlock it; set the contested bit as needed. */
+ flags = TARGET_UMUTEX_UNOWNED;
+ if (count > 1) {
+ flags |= TARGET_UMUTEX_CONTESTED;
+ }
+ __put_user(flags, &target_umutex->m_owner);
+ pthread_mutex_unlock(&umtx_wait_lck);
+
+ addr = &target_umutex->m_owner;
+
+ /* addr is used only as a key, so we can unlock before we use it below */
+ unlock_user_struct(target_umutex, target_addr, 1);
+
+ /*
+ * And wake up any that may be contested it. We used to only do this if the
+ * lock wasn't contested coming in, but that could have changed in the
+ * interim. Unconditionally issue the wakeup, in conjunction with the
+ * previous change of owner this should catch all cases.
+ */
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_addr), UMTX_OP_WAKE, 0);
+ return get_errno(safe__umtx_op(addr, UMTX_OP_WAKE_PRIVATE, 1, NULL, NULL));
+#endif /* _UMTX_OPTIMIZED */
+}
+
+/*
+ * wflags CVWAIT_CHECK_UNPARKING, CVWAIT_ABSTIME, CVWAIT_CLOCKID
+ */
+abi_long freebsd_cv_wait(abi_ulong target_ucond_addr,
+ abi_ulong target_umtx_addr, struct timespec *ts, int wflags)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_ucond_addr, UMTX_OP_CV_WAIT, wflags,
+ g2h_untagged(target_umtx_addr), ts);
+#else
+ abi_long ret;
+ long tid;
+ struct target_ucond *target_ucond;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_ucond, target_ucond_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Check the clock ID if needed. */
+ if ((wflags & TARGET_CVWAIT_CLOCKID) != 0) {
+ uint32_t clockid;
+
+ __get_user(clockid, &target_ucond->c_clockid);
+ if (clockid >= CLOCK_THREAD_CPUTIME_ID) {
+ /* Only HW clock id will work. */
+ unlock_user_struct(target_ucond, target_ucond_addr, 1);
+ return -TARGET_EINVAL;
+ }
+ }
+
+ thr_self(&tid);
+
+ /* Lock the _cv_mutex so we can safely unlock the user mutex */
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+
+ /* Set c_has_waiters before releasing the user mutex! */
+ __put_user(1, &target_ucond->c_has_waiters);
+
+ /* unlock the user mutex */
+ ret = freebsd_unlock_umutex(target_umtx_addr, tid);
+ if (is_error(ret)) {
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+ unlock_user_struct(target_ucond, target_ucond_addr, 1);
+ return ret;
+ }
+
+ /* UMTX_OP_CV_WAIT unlocks _cv_mutex */
+ DEBUG_UMTX("<CV_WAIT> %s: _umtx_op(%p, %d, 0x%x, %p, NULL)\n",
+ __func__, g2h_untagged(target_ucond_addr), UMTX_OP_CV_WAIT, wflags,
+ &_cv_mutex);
+ ret = safe__umtx_op(g2h_untagged(target_ucond_addr), UMTX_OP_CV_WAIT,
+ wflags, &_cv_mutex, ts);
+
+ if (is_error(ret)) {
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+ unlock_user_struct(target_ucond, target_ucond_addr, 1);
+ return ret;
+ }
+ ret = freebsd_lock_umutex(target_umtx_addr, tid, NULL, 0, TARGET_UMUTEX_TRY,
+ 0);
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+ unlock_user_struct(target_ucond, target_ucond_addr, 1);
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_cv_signal(abi_ulong target_ucond_addr)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_ucond_addr, UMTX_OP_CV_SIGNAL, 0, NULL,
+ NULL);
+#else
+ abi_long ret;
+
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+ DEBUG_UMTX("<CV_SIGNAL> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_ucond_addr), UMTX_OP_CV_SIGNAL, 0);
+ ret = get_errno(safe__umtx_op(g2h_untagged(target_ucond_addr),
+ UMTX_OP_CV_SIGNAL, 0, NULL, NULL));
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_cv_broadcast(abi_ulong target_ucond_addr)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_ucond_addr, UMTX_OP_CV_BROADCAST, 0, NULL,
+ NULL);
+#else
+ int ret;
+
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+ DEBUG_UMTX("<CV_BROADCAST> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h_untagged(target_ucond_addr), UMTX_OP_CV_BROADCAST, 0);
+ ret = get_errno(safe__umtx_op(g2h_untagged(target_ucond_addr),
+ UMTX_OP_CV_BROADCAST, 0, NULL, NULL));
+ safe__umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+
+ return ret;
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag, size_t tsz,
+ void *t)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_urwlock))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_addr, UMTX_OP_RW_RDLOCK, fflag,
+ (void *)(uintptr_t)tsz, t);
+#else
+ struct target_urwlock *target_urwlock;
+ uint32_t flags, wrflags;
+ uint32_t state;
+ uint32_t blocked_readers;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(flags, &target_urwlock->rw_flags);
+ wrflags = TARGET_URWLOCK_WRITE_OWNER;
+ if (!(fflag & TARGET_URWLOCK_PREFER_READER) &&
+ !(flags & TARGET_URWLOCK_PREFER_READER)) {
+ wrflags |= TARGET_URWLOCK_WRITE_WAITERS;
+ }
+ for (;;) {
+ __get_user(state, &target_urwlock->rw_state);
+ /* try to lock it */
+ while (!(state & wrflags)) {
+ if (TARGET_URWLOCK_READER_COUNT(state) ==
+ TARGET_URWLOCK_MAX_READERS) {
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return -TARGET_EAGAIN;
+ }
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ (state + 1))) {
+ /* The acquired succeeded. */
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return 0;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ /* set read contention bit */
+ if (!tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_READ_WAITERS)) {
+ /* The state has changed. Start over. */
+ continue;
+ }
+
+ /* contention bit is set, increase read waiter count */
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_readers,
+ blocked_readers, blocked_readers + 1)) {
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ }
+
+ ret = 0;
+ while (state & wrflags) {
+ /* sleep/wait */
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x (0x%x), NULL, NULL)\n",
+ __func__, &target_urwlock->rw_state,
+ UMTX_OP_WAIT_UINT, tswap32(state),
+ target_urwlock->rw_state);
+ ret = _umtx_wait_uint(&target_urwlock->rw_state, tswap32(state),
+ tsz, t, __func__);
+ if (is_error(ret)) {
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock,
+ target_addr, 0)) {
+ return ret;
+ }
+ goto rdlock_decrement;
+ }
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr,
+ 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* decrease read waiter count */
+rdlock_decrement:
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_readers,
+ blocked_readers, (blocked_readers - 1))) {
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ }
+ if (blocked_readers == 1) {
+ /* clear read contention bit */
+ __get_user(state, &target_urwlock->rw_state);
+ while (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_READ_WAITERS)) {
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ }
+ if (is_error(ret)) {
+ /* tcmpset_32 operates on the target memory, so don't double copy */
+ unlock_user_struct(target_urwlock, target_addr, 0);
+ return ret;
+ }
+ }
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag, size_t tsz,
+ void *t)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_urwlock))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_addr, UMTX_OP_RW_WRLOCK, fflag,
+ (void *)(uintptr_t)tsz, t);
+#else
+ struct target_urwlock *target_urwlock;
+ uint32_t blocked_readers, blocked_writers;
+ uint32_t state;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ blocked_readers = 0;
+ for (;;) {
+ __get_user(state, &target_urwlock->rw_state);
+ while (!(state & TARGET_URWLOCK_WRITE_OWNER) &&
+ TARGET_URWLOCK_READER_COUNT(state) == 0) {
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_WRITE_OWNER)) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return 0;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ if (!(state & (TARGET_URWLOCK_WRITE_OWNER |
+ TARGET_URWLOCK_WRITE_WAITERS)) &&
+ blocked_readers != 0) {
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, &target_urwlock->rw_state, UMTX_OP_WAKE,
+ tswap32(state));
+ ret = get_errno(safe__umtx_op(&target_urwlock->rw_state,
+ UMTX_OP_WAKE, INT_MAX, NULL, NULL));
+ unlock_user_struct(target_urwlock, target_addr, ret == 0);
+ return ret;
+ }
+ /* re-read the state */
+ __get_user(state, &target_urwlock->rw_state);
+
+ /* and set TARGET_URWLOCK_WRITE_WAITERS */
+ while (((state & TARGET_URWLOCK_WRITE_OWNER) ||
+ TARGET_URWLOCK_READER_COUNT(state) != 0) &&
+ (state & TARGET_URWLOCK_WRITE_WAITERS) == 0) {
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_WRITE_WAITERS)) {
+ break;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* contention bit is set, increase write waiter count */
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_writers,
+ blocked_writers, blocked_writers + 1)) {
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ }
+
+ /* sleep */
+ ret = 0;
+ while ((state & TARGET_URWLOCK_WRITE_OWNER) ||
+ (TARGET_URWLOCK_READER_COUNT(state) != 0)) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x(0x%x), NULL, NULL)\n",
+ __func__, &target_urwlock->rw_blocked_writers,
+ UMTX_OP_WAIT_UINT, tswap32(state),
+ target_urwlock->rw_state);
+ ret = _umtx_wait_uint(&target_urwlock->rw_state,
+ tswap32(state), tsz, t, __func__);
+ if (is_error(ret)) {
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock,
+ target_addr, 0)) {
+ return ret;
+ }
+ goto wrlock_decrement;
+ }
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr,
+ 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* decrease the write waiter count */
+wrlock_decrement:
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_writers,
+ blocked_writers, (blocked_writers - 1))) {
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ }
+ if (blocked_writers == 1) {
+ /* clear write contention bit */
+ __get_user(state, &target_urwlock->rw_state);
+ while (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_WRITE_WAITERS)) {
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ } else {
+ blocked_readers = 0;
+ }
+ if (is_error(ret)) {
+ /* tcmpset_32 operates on the target memory, so don't double copy */
+ unlock_user_struct(target_urwlock, target_addr, 0);
+ return ret;
+ }
+ }
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long freebsd_rw_unlock(abi_ulong target_addr)
+{
+#ifdef _UMTX_OPTIMIZED
+ if (!access_ok(VERIFY_WRITE, target_addr, sizeof(struct target_urwlock))) {
+ return -TARGET_EFAULT;
+ }
+
+ return optimized_umtx_op(target_addr, UMTX_OP_RW_UNLOCK, 0, NULL, NULL);
+#else
+ struct target_urwlock *target_urwlock;
+ uint32_t flags, state, count = 0;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(flags, &target_urwlock->rw_flags);
+ __get_user(state, &target_urwlock->rw_state);
+
+ if (state & TARGET_URWLOCK_WRITE_OWNER) {
+ for (;;) {
+ if (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_WRITE_OWNER)) {
+ /*
+ * Update the state here because we want to make sure that
+ * another thread didn't unste the flag from underneath us.
+ * If they did, we throw EPERM as the kernel does.
+ */
+ __get_user(state, &target_urwlock->rw_state);
+ if (!(state & TARGET_URWLOCK_WRITE_OWNER)) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ } else {
+ break;
+ }
+ }
+ } else if (TARGET_URWLOCK_READER_COUNT(state) != 0) {
+ /* decrement reader count */
+ for (;;) {
+ if (!tcmpset_32(&target_urwlock->rw_state, state, (state - 1))) {
+ /*
+ * Just as in the branch above; we update the state here because
+ * we want to make sure the reader count didn't hit 0 while we
+ * are still trying to decrement this. The kernel also returns
+ * EPERM here.
+ */
+ __get_user(state, &target_urwlock->rw_state);
+ if (TARGET_URWLOCK_READER_COUNT(state) == 0) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+
+ if (!(flags & TARGET_URWLOCK_PREFER_READER)) {
+ if (state & TARGET_URWLOCK_WRITE_WAITERS) {
+ count = 1;
+ } else if (state & TARGET_URWLOCK_READ_WAITERS) {
+ count = INT_MAX;
+ }
+ } else {
+ if (state & TARGET_URWLOCK_READ_WAITERS) {
+ count = INT_MAX;
+ } else if (state & TARGET_URWLOCK_WRITE_WAITERS) {
+ count = 1;
+ }
+ }
+
+ /* rw_state used below as key only */
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ if (count != 0) {
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, &target_urwlock->rw_state, UMTX_OP_WAKE, count);
+ return get_errno(safe__umtx_op(&target_urwlock->rw_state, UMTX_OP_WAKE,
+ count, NULL, NULL));
+ } else {
+ return 0;
+ }
+#endif /* _UMTX_OPTIMIZED */
+}
+
+abi_long
+freebsd_umtx_shm(abi_ulong target_addr, long fflag)
+{
+
+ return get_errno(safe__umtx_op(NULL, QEMU_UMTX_OP(UMTX_OP_SHM), fflag,
+ g2h_untagged(target_addr), NULL));
+}
+
+abi_long
+freebsd_umtx_robust_list(abi_ulong target_addr, size_t rbsize)
+{
+#ifdef _UMTX_OPTIMIZED
+ struct target_umtx_robust_lists_params *tparams;
+ struct umtx_robust_lists_params hparams;
+ abi_long error;
+
+ if (rbsize < sizeof(*tparams)) {
+ return -TARGET_EINVAL;
+ }
+
+ if (!lock_user_struct(VERIFY_READ, tparams, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+
+ hparams.robust_list_offset =
+ (uintptr_t)g2h_untagged(tparams->robust_list_offset);
+ hparams.robust_priv_list_offset =
+ (uintptr_t)g2h_untagged(tparams->robust_priv_list_offset);
+ hparams.robust_inact_offset =
+ (uintptr_t)g2h_untagged(tparams->robust_inact_offset);
+
+ error = optimized_umtx_op(0, UMTX_OP_ROBUST_LISTS, sizeof(hparams),
+ &hparams, NULL);
+ unlock_user_struct(tparams, target_addr, 0);
+ return error;
+#else
+ gemu_log("safe__umtx_op(..., UMTX_OP_ROBUST_LISTS. ...) not supported\n");
+ return -TARGET_EOPNOTSUPP;
+#endif
+}
+
+abi_long do_freebsd_thr_new(CPUArchState *env,
+ abi_ulong target_param_addr, int32_t param_size)
+{
+ new_freebsd_thread_info_t info;
+ pthread_attr_t attr;
+ TaskState *ts;
+ CPUArchState *new_env;
+ CPUState *new_cpu;
+ struct target_freebsd_thr_param *target_param;
+ abi_ulong target_rtp_addr;
+ struct target_freebsd_rtprio *target_rtp;
+ struct rtprio *rtp_ptr, rtp;
+ CPUState *cpu = env_cpu(env);
+ TaskState *parent_ts = (TaskState *)cpu->opaque;
+ sigset_t sigmask;
+ struct sched_param sched_param;
+ int sched_policy;
+ int ret = 0;
+
+ memset(&info, 0, sizeof(info));
+
+ if (!lock_user_struct(VERIFY_READ, target_param, target_param_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ info.param.start_func = tswapal(target_param->start_func);
+ info.param.arg = tswapal(target_param->arg);
+ info.param.stack_base = tswapal(target_param->stack_base);
+ info.param.stack_size = tswapal(target_param->stack_size);
+ info.param.tls_base = tswapal(target_param->tls_base);
+ info.param.tls_size = tswapal(target_param->tls_size);
+ info.param.child_tid = tswapal(target_param->child_tid);
+ info.param.parent_tid = tswapal(target_param->parent_tid);
+ info.param.flags = tswap32(target_param->flags);
+ target_rtp_addr = info.param.rtp = tswapal(target_param->rtp);
+ unlock_user_struct(target_param, target_param_addr, 0);
+
+ thr_self(&info.parent_tid);
+
+ if (target_rtp_addr) {
+ if (!lock_user_struct(VERIFY_READ, target_rtp, target_rtp_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ rtp.type = tswap16(target_rtp->type);
+ rtp.prio = tswap16(target_rtp->prio);
+ unlock_user_struct(target_rtp, target_rtp_addr, 0);
+ rtp_ptr = &rtp;
+ } else {
+ rtp_ptr = NULL;
+ }
+
+ /* Create a new CPU instance. */
+ ts = g_malloc0(sizeof(TaskState));
+ init_task_state(ts);
+
+ /* Grab a mutex so that thread setup appears atomic. */
+ pthread_mutex_lock(new_freebsd_thread_lock_ptr);
+
+ /*
+ * If this is our first additional thread, we need to ensure we
+ * generate code for parallel execution and flush old translations.
+ * Do this now so that the copy gets CF_PARALLEL too.
+ */
+ if (!(cpu->tcg_cflags & CF_PARALLEL)) {
+ cpu->tcg_cflags |= CF_PARALLEL;
+ tb_flush__exclusive_or_serial();
+ }
+
+ new_env = cpu_copy(env);
+
+ new_cpu = env_cpu(new_env);
+ new_cpu->opaque = ts;
+ ts->bprm = parent_ts->bprm;
+ ts->info = parent_ts->info;
+ ts->signal_mask = parent_ts->signal_mask;
+ ts->ts_tid = qemu_get_thread_id();
+
+ pthread_mutex_init(&info.mutex, NULL);
+ pthread_mutex_lock(&info.mutex);
+ pthread_cond_init(&info.cond, NULL);
+ info.env = new_env;
+
+ /* XXX check return values... */
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if (rtp_ptr) {
+ rtp_to_schedparam(&rtp, &sched_policy, &sched_param);
+ pthread_attr_setschedpolicy(&attr, sched_policy);
+ pthread_attr_setschedparam(&attr, &sched_param);
+ }
+
+ /*
+ * It is not safe to deliver signals until the child has finished
+ * initializing, so temporarily block all signals.
+ */
+ sigfillset(&sigmask);
+ sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
+
+ ret = pthread_create(&info.thread, &attr, new_freebsd_thread_start, &info);
+
+ sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
+ pthread_attr_destroy(&attr);
+ if (ret == 0) {
+ /* Wait for the child to initialize. */
+ pthread_cond_wait(&info.cond, &info.mutex);
+ } else {
+ /* Creation of new thread failed. */
+ object_unparent(OBJECT(new_cpu));
+ object_unref(OBJECT(new_cpu));
+ g_free(ts);
+ ret = -host_to_target_errno(errno);
+ }
+
+ pthread_mutex_unlock(&info.mutex);
+ pthread_cond_destroy(&info.cond);
+ pthread_mutex_destroy(&info.mutex);
+ pthread_mutex_unlock(new_freebsd_thread_lock_ptr);
+
+ return ret;
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 29/37] bsd-user: Add thread system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (27 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 28/37] bsd-user: Add os-thread.c with umtx, mutex, and thread creation Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 30/37] bsd-user: Add os-extattr.h with file and fd extattr functions Warner Losh
` (8 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Wire up thread-related system calls: thr_new, thr_set_name, thr_self,
thr_suspend, thr_wake, thr_kill, thr_kill2, thr_exit, rtprio_thread,
getcontext, setcontext, swapcontext, and _umtx_op.
Signed-off-by: Stacey Son <sson@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-syscall.c | 55 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index f2c3ff25e8..0c729da0ab 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -1293,6 +1293,61 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd_setfib(arg1);
break;
+ /*
+ * thread system calls
+ */
+ case TARGET_FREEBSD_NR_thr_new: /* thr_new(2) */
+ ret = do_freebsd_thr_new(env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_set_name: /* thr_set_name(2) */
+ ret = do_freebsd_thr_set_name(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_self: /* thr_self(2) */
+ ret = do_freebsd_thr_self(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_suspend: /* thr_suspend(2) */
+ ret = do_freebsd_thr_suspend(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_wake: /* thr_wake(2) */
+ ret = do_freebsd_thr_wake(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_kill: /* thr_kill(2) */
+ ret = do_freebsd_thr_kill(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_kill2: /* thr_kill2(2) */
+ ret = do_freebsd_thr_kill2(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_exit: /* thr_exit(2) */
+ ret = do_freebsd_thr_exit(env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_rtprio_thread: /* rtprio_thread(2) */
+ ret = do_freebsd_rtprio_thread(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getcontext: /* getcontext(2) */
+ ret = do_freebsd_getcontext(env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_setcontext: /* setcontext(2) */
+ ret = do_freebsd_setcontext(env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_swapcontext: /* swapcontext(2) */
+ ret = do_freebsd_swapcontext(env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR__umtx_op: /* undocumented */
+ ret = do_freebsd__umtx_op(arg1, arg2, arg3, arg4, arg5);
+ break;
+
/*
* sys{ctl, arch, call}
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 30/37] bsd-user: Add os-extattr.h with file and fd extattr functions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (28 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 29/37] bsd-user: Add thread system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 31/37] bsd-user: Add link and list extattr functions to os-extattr.h Warner Losh
` (7 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add the first part of extended attribute support: extattrctl,
extattr_set_file, extattr_get_file, extattr_delete_file,
extattr_set_fd, extattr_get_fd, and extattr_delete_fd.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-extattr.h | 208 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 208 insertions(+)
diff --git a/bsd-user/freebsd/os-extattr.h b/bsd-user/freebsd/os-extattr.h
new file mode 100644
index 0000000000..f1dfa344cf
--- /dev/null
+++ b/bsd-user/freebsd/os-extattr.h
@@ -0,0 +1,208 @@
+/*
+ * FreeBSD extended attributes and ACL system call support
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <sys/extattr.h>
+#include <sys/acl.h>
+
+/* extattrctl() */
+static inline abi_long do_freebsd_extattrctl(abi_ulong arg1, abi_ulong arg2,
+ abi_ulong arg3, abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *p, *a, *f;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ f = lock_user_string(arg3);
+ if (f == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg5);
+ if (a == NULL) {
+ unlock_user(f, arg3, 0);
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattrctl(path(p), arg2, f, arg4, a));
+ unlock_user(a, arg5, 0);
+ unlock_user(f, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_set_file(2) */
+static inline abi_long do_freebsd_extattr_set_file(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3,
+ abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *p, *a, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ d = lock_user(VERIFY_READ, arg4, arg5, 1);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_set_file(path(p), arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_get_file(2) */
+static inline abi_long do_freebsd_extattr_get_file(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3,
+ abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *p, *a, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ if (arg4 && arg5 > 0) {
+ d = lock_user(VERIFY_WRITE, arg4, arg5, 0);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_get_file(path(p), arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ } else {
+ ret = get_errno(extattr_get_file(path(p), arg2, a, NULL, arg5));
+ }
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_delete_file(2) */
+static inline abi_long do_freebsd_extattr_delete_file(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p, *a;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_delete_file(path(p), arg2, a));
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_set_fd(2) */
+static inline abi_long do_freebsd_extattr_set_fd(abi_long arg1, abi_long arg2,
+ abi_ulong arg3, abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *a, *d;
+
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ return -TARGET_EFAULT;
+ }
+ d = lock_user(VERIFY_READ, arg4, arg5, 1);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_set_fd(arg1, arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ unlock_user(a, arg3, 0);
+
+ return ret;
+}
+
+/* extattr_get_fd(2) */
+static inline abi_long do_freebsd_extattr_get_fd(abi_long arg1, abi_long arg2,
+ abi_ulong arg3, abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *a, *d;
+
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ return -TARGET_EFAULT;
+ }
+
+ if (arg4 && arg5 > 0) {
+ d = lock_user(VERIFY_WRITE, arg4, arg5, 0);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_get_fd(arg1, arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ } else {
+ ret = get_errno(extattr_get_fd(arg1, arg2, a, NULL, arg5));
+ }
+ unlock_user(a, arg3, 0);
+
+ return ret;
+}
+
+/* extattr_delete_fd(2) */
+static inline abi_long do_freebsd_extattr_delete_fd(abi_long arg1,
+ abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *a;
+
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_delete_fd(arg1, arg2, a));
+ unlock_user(a, arg3, 0);
+
+ return ret;
+}
+
+
+#endif /* FREEBSD_OS_EXTATTR_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 31/37] bsd-user: Add link and list extattr functions to os-extattr.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (29 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 30/37] bsd-user: Add os-extattr.h with file and fd extattr functions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 32/37] bsd-user: Add ACL system call shims " Warner Losh
` (6 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add extattr_get_link, extattr_set_link, extattr_delete_link,
extattr_list_fd, extattr_list_file, and extattr_list_link.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-extattr.h | 172 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 172 insertions(+)
diff --git a/bsd-user/freebsd/os-extattr.h b/bsd-user/freebsd/os-extattr.h
index f1dfa344cf..788e40ef30 100644
--- a/bsd-user/freebsd/os-extattr.h
+++ b/bsd-user/freebsd/os-extattr.h
@@ -204,5 +204,177 @@ static inline abi_long do_freebsd_extattr_delete_fd(abi_long arg1,
return ret;
}
+/* extattr_get_link(2) */
+static inline abi_long do_freebsd_extattr_get_link(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3,
+ abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *p, *a, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ if (arg4 && arg5 > 0) {
+ d = lock_user(VERIFY_WRITE, arg4, arg5, 0);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_get_link(path(p), arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ } else {
+ ret = get_errno(extattr_get_link(path(p), arg2, a, NULL, arg5));
+ }
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_set_link(2) */
+static inline abi_long do_freebsd_extattr_set_link(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3,
+ abi_ulong arg4,
+ abi_ulong arg5)
+{
+ abi_long ret;
+ void *p, *a, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ d = lock_user(VERIFY_READ, arg4, arg5, 1);
+ if (d == NULL) {
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_set_link(path(p), arg2, a, d, arg5));
+ unlock_user(d, arg4, arg5);
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_delete_link(2) */
+static inline abi_long do_freebsd_extattr_delete_link(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p, *a;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ a = lock_user_string(arg3);
+ if (a == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_delete_link(path(p), arg2, a));
+ unlock_user(a, arg3, 0);
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_list_fd(2) */
+static inline abi_long do_freebsd_extattr_list_fd(
+ abi_long arg1, abi_long arg2, abi_ulong arg3, abi_ulong arg4)
+{
+ abi_long ret;
+ void *d;
+
+ if (arg3 && arg4 > 0) {
+ d = lock_user(VERIFY_WRITE, arg3, arg4, 0);
+ if (d == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_list_fd(arg1, arg2, d, arg4));
+ unlock_user(d, arg3, arg4);
+ } else {
+ ret = get_errno(extattr_list_fd(arg1, arg2, NULL, arg4));
+ }
+ return ret;
+}
+
+/* extattr_list_file(2) */
+static inline abi_long do_freebsd_extattr_list_file(
+ abi_long arg1, abi_long arg2, abi_ulong arg3, abi_ulong arg4)
+{
+ abi_long ret;
+ void *p, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ if (arg3 && arg4 > 0) {
+ d = lock_user(VERIFY_WRITE, arg3, arg4, 0);
+ if (d == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_list_file(path(p), arg2, d, arg4));
+ unlock_user(d, arg3, arg4);
+ } else {
+ ret = get_errno(extattr_list_file(path(p), arg2, NULL, arg4));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* extattr_list_link(2) */
+static inline abi_long do_freebsd_extattr_list_link(
+ abi_long arg1, abi_long arg2, abi_ulong arg3, abi_ulong arg4)
+{
+ abi_long ret;
+ void *p, *d;
+
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ if (arg3 && arg4 > 0) {
+ d = lock_user(VERIFY_WRITE, arg3, arg4, 0);
+ if (d == NULL) {
+ unlock_user(p, arg1, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(extattr_list_link(path(p), arg2, d, arg4));
+ unlock_user(d, arg3, arg4);
+ } else {
+ ret = get_errno(extattr_list_link(path(p), arg2, NULL, arg4));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/*
+ * Access Control Lists
+ */
+
#endif /* FREEBSD_OS_EXTATTR_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 32/37] bsd-user: Add ACL system call shims to os-extattr.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (30 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 31/37] bsd-user: Add link and list extattr functions to os-extattr.h Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 33/37] bsd-user: Add os-extattr.c with ACL conversion functions Warner Losh
` (5 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Complete os-extattr.h with ACL operations: __acl_aclcheck_fd,
__acl_aclcheck_file, __acl_aclcheck_link, __acl_delete_fd,
__acl_delete_file, __acl_delete_link, __acl_get_fd, __acl_get_file,
__acl_get_link, __acl_set_fd, __acl_set_file, and __acl_set_link.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-extattr.h | 290 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 286 insertions(+), 4 deletions(-)
diff --git a/bsd-user/freebsd/os-extattr.h b/bsd-user/freebsd/os-extattr.h
index 788e40ef30..5f5678b380 100644
--- a/bsd-user/freebsd/os-extattr.h
+++ b/bsd-user/freebsd/os-extattr.h
@@ -65,7 +65,7 @@ static inline abi_long do_freebsd_extattr_set_file(abi_ulong arg1,
return -TARGET_EFAULT;
}
ret = get_errno(extattr_set_file(path(p), arg2, a, d, arg5));
- unlock_user(d, arg4, arg5);
+ unlock_user(d, arg4, 0);
unlock_user(a, arg3, 0);
unlock_user(p, arg1, 0);
@@ -151,7 +151,7 @@ static inline abi_long do_freebsd_extattr_set_fd(abi_long arg1, abi_long arg2,
return -TARGET_EFAULT;
}
ret = get_errno(extattr_set_fd(arg1, arg2, a, d, arg5));
- unlock_user(d, arg4, arg5);
+ unlock_user(d, arg4, 0);
unlock_user(a, arg3, 0);
return ret;
@@ -267,7 +267,7 @@ static inline abi_long do_freebsd_extattr_set_link(abi_ulong arg1,
return -TARGET_EFAULT;
}
ret = get_errno(extattr_set_link(path(p), arg2, a, d, arg5));
- unlock_user(d, arg4, arg5);
+ unlock_user(d, arg4, 0);
unlock_user(a, arg3, 0);
unlock_user(p, arg1, 0);
@@ -376,5 +376,287 @@ static inline abi_long do_freebsd_extattr_list_link(
* Access Control Lists
*/
+/* __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_aclcheck_fd(abi_long arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ struct acl host_acl;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_aclcheck_fd(arg1, type, &host_acl));
+ }
+
+ return ret;
+}
+
+/* __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_aclcheck_file(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ struct acl host_acl;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_aclcheck_file(path(p), type, &host_acl));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* __acl_aclcheck_link(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_aclcheck_link(abi_ulong arg1,
+ abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ struct acl host_acl;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_aclcheck_link(path(p), type, &host_acl));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_delete_fd(int filedes, acl_type_t type); */
+static inline abi_long do_freebsd__acl_delete_fd(abi_long arg1, abi_long arg2)
+{
+ abi_long ret;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ return get_errno(__acl_delete_fd(arg1, type));
+}
+
+/* int __acl_delete_file(const char *path, acl_type_t type); */
+static inline abi_long do_freebsd__acl_delete_file(abi_ulong arg1,
+ abi_long arg2)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(__acl_delete_file(path(p), type));
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_delete_link(const char *path, acl_type_t type); */
+static inline abi_long do_freebsd__acl_delete_link(abi_ulong arg1,
+ abi_long arg2)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(__acl_delete_link(path(p), type));
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_get_fd(int filedes, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_get_fd(abi_long arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ acl_type_t type;
+ struct acl host_acl;
+
+ memset(&host_acl, 0, sizeof(struct acl));
+ host_acl.acl_maxcnt = ACL_MAX_ENTRIES;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = get_errno(__acl_get_fd(arg1, type, &host_acl));
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_acl(arg3, &host_acl);
+ }
+
+ return ret;
+}
+
+/* __acl_get_file(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_get_file(abi_ulong arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+ struct acl host_acl;
+
+ memset(&host_acl, 0, sizeof(struct acl));
+ host_acl.acl_maxcnt = ACL_MAX_ENTRIES;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(__acl_get_file(path(p), type, &host_acl));
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_acl(arg3, &host_acl);
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_get_link(abi_ulong arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+ struct acl host_acl;
+
+ memset(&host_acl, 0, sizeof(struct acl));
+ host_acl.acl_maxcnt = ACL_MAX_ENTRIES;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(__acl_get_link(path(p), type, &host_acl));
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_acl(arg3, &host_acl);
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_set_fd(int filedes, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_set_fd(abi_long arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ acl_type_t type;
+ struct acl host_acl;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_set_fd(arg1, type, &host_acl));
+ }
+
+ return ret;
+}
+
+/* int __acl_set_file(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_set_file(abi_ulong arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+ struct acl host_acl;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_set_file(path(p), type, &host_acl));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
+
+/* int __acl_set_link(const char *path, acl_type_t type, struct acl *aclp); */
+static inline abi_long do_freebsd__acl_set_link(abi_ulong arg1, abi_long arg2,
+ abi_ulong arg3)
+{
+ abi_long ret;
+ void *p;
+ acl_type_t type;
+ struct acl host_acl;
+
+ ret = t2h_freebsd_acl_type(&type, arg2);
+ if (is_error(ret)) {
+ return ret;
+ }
+ p = lock_user_string(arg1);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = t2h_freebsd_acl(&host_acl, arg3);
+ if (!is_error(ret)) {
+ ret = get_errno(__acl_set_link(path(p), type, &host_acl));
+ }
+ unlock_user(p, arg1, 0);
+
+ return ret;
+}
-#endif /* FREEBSD_OS_EXTATTR_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 33/37] bsd-user: Add os-extattr.c with ACL conversion functions
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (31 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 32/37] bsd-user: Add ACL system call shims " Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 34/37] bsd-user: Add extended attribute and ACL system call dispatch Warner Losh
` (4 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Add os-extattr.c with t2h_freebsd_acl, h2t_freebsd_acl, and
t2h_freebsd_acl_type helper functions for ACL operations. Add
os-extattr.c to the build and include os-extattr.h in the
syscall dispatcher.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-extattr.c | 116 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-syscall.c | 2 +
2 files changed, 118 insertions(+)
diff --git a/bsd-user/freebsd/os-extattr.c b/bsd-user/freebsd/os-extattr.c
new file mode 100644
index 0000000000..0f0c7123dd
--- /dev/null
+++ b/bsd-user/freebsd/os-extattr.c
@@ -0,0 +1,116 @@
+/*
+ * FreeBSD extend attributes and ACL conversions
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#ifndef _ACL_PRIVATE
+#define _ACL_PRIVATE
+#endif
+#include <sys/acl.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+
+/*
+ * FreeBSD ACL conversion.
+ */
+abi_long t2h_freebsd_acl(struct acl *host_acl, abi_ulong target_addr)
+{
+ uint32_t i;
+ struct target_freebsd_acl *target_acl;
+
+ if (!lock_user_struct(VERIFY_READ, target_acl, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(host_acl->acl_maxcnt, &target_acl->acl_maxcnt);
+ __get_user(host_acl->acl_cnt, &target_acl->acl_cnt);
+
+ if (host_acl->acl_maxcnt > ACL_MAX_ENTRIES) {
+ unlock_user_struct(target_acl, target_addr, 0);
+ return -TARGET_EINVAL;
+ }
+
+ for (i = 0; i < host_acl->acl_maxcnt; i++) {
+ __get_user(host_acl->acl_entry[i].ae_tag,
+ &target_acl->acl_entry[i].ae_tag);
+ __get_user(host_acl->acl_entry[i].ae_id,
+ &target_acl->acl_entry[i].ae_id);
+ __get_user(host_acl->acl_entry[i].ae_perm,
+ &target_acl->acl_entry[i].ae_perm);
+ __get_user(host_acl->acl_entry[i].ae_entry_type,
+ &target_acl->acl_entry[i].ae_entry_type);
+ __get_user(host_acl->acl_entry[i].ae_flags,
+ &target_acl->acl_entry[i].ae_flags);
+ }
+
+ unlock_user_struct(target_acl, target_addr, 0);
+ return 0;
+}
+
+abi_long h2t_freebsd_acl(abi_ulong target_addr, struct acl *host_acl)
+{
+ uint32_t i;
+ struct target_freebsd_acl *target_acl;
+
+ if (host_acl->acl_maxcnt > ACL_MAX_ENTRIES) {
+ return -TARGET_EINVAL;
+ }
+
+ if (!lock_user_struct(VERIFY_WRITE, target_acl, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __put_user(host_acl->acl_maxcnt, &target_acl->acl_maxcnt);
+ __put_user(host_acl->acl_cnt, &target_acl->acl_cnt);
+
+ for (i = 0; i < host_acl->acl_maxcnt; i++) {
+ __put_user(host_acl->acl_entry[i].ae_tag,
+ &target_acl->acl_entry[i].ae_tag);
+ __put_user(host_acl->acl_entry[i].ae_id,
+ &target_acl->acl_entry[i].ae_id);
+ __put_user(host_acl->acl_entry[i].ae_perm,
+ &target_acl->acl_entry[i].ae_perm);
+ __put_user(host_acl->acl_entry[i].ae_entry_type,
+ &target_acl->acl_entry[i].ae_entry_type);
+ __put_user(host_acl->acl_entry[i].ae_flags,
+ &target_acl->acl_entry[i].ae_flags);
+ }
+
+ unlock_user_struct(target_acl, target_addr, 1);
+ return 0;
+}
+
+abi_long t2h_freebsd_acl_type(acl_type_t *host_type, abi_long target_type)
+{
+
+ switch (target_type) {
+ case TARGET_FREEBSD_ACL_TYPE_ACCESS_OLD:
+ *host_type = ACL_TYPE_ACCESS_OLD;
+ break;
+
+ case TARGET_FREEBSD_ACL_TYPE_DEFAULT_OLD:
+ *host_type = ACL_TYPE_DEFAULT_OLD;
+ break;
+
+ case TARGET_FREEBSD_ACL_TYPE_ACCESS:
+ *host_type = ACL_TYPE_ACCESS;
+ break;
+
+ case TARGET_FREEBSD_ACL_TYPE_DEFAULT:
+ *host_type = ACL_TYPE_DEFAULT;
+ break;
+
+ case TARGET_FREEBSD_ACL_TYPE_NFS4:
+ *host_type = ACL_TYPE_NFS4;
+ break;
+
+ default:
+ return -TARGET_EINVAL;
+ }
+ return 0;
+}
+
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 0c729da0ab..f385034a53 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#define _ACL_PRIVATE
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qemu/path.h"
@@ -43,6 +44,7 @@
#include "bsd-socket.h"
/* BSD dependent syscall shims */
+#include "os-extattr.h"
#include "os-stat.h"
#include "os-proc.h"
#include "os-signal.h"
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 34/37] bsd-user: Add extended attribute and ACL system call dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (32 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 33/37] bsd-user: Add os-extattr.c with ACL conversion functions Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 35/37] bsd-user: Add scheduler and cpuset functions to os-misc.h Warner Losh
` (3 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son
Wire up extended attribute and ACL system calls: extattrctl,
extattr_set/get/delete for file, fd, and link variants,
extattr_list for fd, file, and link, and all __acl_* operations
(aclcheck, delete, get, set for fd, file, and link).
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-syscall.c | 103 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index f385034a53..7825e042b0 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -1365,6 +1365,109 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd_sysarch(env, arg1, arg2);
break;
+ /*
+ * extended attributes and ACL system calls
+ */
+ case TARGET_FREEBSD_NR_extattrctl: /* extattrctl() */
+ ret = do_freebsd_extattrctl(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_set_file: /* extattr_set_file(2) */
+ ret = do_freebsd_extattr_set_file(arg1, arg2, arg3, arg4, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_get_file: /* extattr_get_file(2) */
+ ret = do_freebsd_extattr_get_file(arg1, arg2, arg3, arg4, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_delete_file: /* extattr_delete_file(2) */
+ ret = do_freebsd_extattr_delete_file(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_set_fd: /* extattr_set_fd(2) */
+ ret = do_freebsd_extattr_set_fd(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_get_fd: /* extattr_get_fd(2) */
+ ret = do_freebsd_extattr_get_fd(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_delete_fd: /* extattr_delete_fd(2) */
+ ret = do_freebsd_extattr_delete_fd(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_get_link: /* extattr_get_link(2) */
+ ret = do_freebsd_extattr_get_link(arg1, arg2, arg3, arg4, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_set_link: /* extattr_set_link(2) */
+ ret = do_freebsd_extattr_set_link(arg1, arg2, arg3, arg4, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_delete_link: /* extattr_delete_link(2) */
+ ret = do_freebsd_extattr_delete_link(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_list_fd: /* extattr_list_fd(2) */
+ ret = do_freebsd_extattr_list_fd(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_list_file: /* extattr_list_file(2) */
+ ret = do_freebsd_extattr_list_file(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_extattr_list_link: /* extattr_list_link(2) */
+ ret = do_freebsd_extattr_list_link(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_aclcheck_fd: /* __acl_aclcheck_fd() */
+ ret = do_freebsd__acl_aclcheck_fd(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_aclcheck_file: /* __acl_aclcheck_file() */
+ ret = do_freebsd__acl_aclcheck_file(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_aclcheck_link: /* __acl_aclcheck_link() */
+ ret = do_freebsd__acl_aclcheck_link(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_delete_fd: /* __acl_delete_fd() */
+ ret = do_freebsd__acl_delete_fd(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_delete_file: /* __acl_delete_file() */
+ ret = do_freebsd__acl_delete_file(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_delete_link: /* __acl_delete_link() */
+ ret = do_freebsd__acl_delete_link(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_get_fd: /* __acl_get_fd() */
+ ret = do_freebsd__acl_get_fd(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_get_file: /* __acl_get_file() */
+ ret = do_freebsd__acl_get_file(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_get_link: /* __acl_get_link() */
+ ret = do_freebsd__acl_get_link(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_set_fd: /* __acl_get_fd() */
+ ret = do_freebsd__acl_set_fd(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_set_file: /* __acl_set_file() */
+ ret = do_freebsd__acl_set_file(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___acl_set_link: /* __acl_set_link() */
+ ret = do_freebsd__acl_set_link(arg1, arg2, arg3);
+ break;
+
default:
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
ret = -TARGET_ENOSYS;
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 35/37] bsd-user: Add scheduler and cpuset functions to os-misc.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (33 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 34/37] bsd-user: Add extended attribute and ACL system call dispatch Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 36/37] bsd-user: Add kmod, posix, and misc " Warner Losh
` (2 subsequent siblings)
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Sean Bruno,
Mikaël Urankar
Expand os-misc.h with scheduler functions (sched_setparam,
sched_getparam, sched_setscheduler, sched_getscheduler,
sched_rr_get_interval) and CPU affinity functions (cpuset,
cpuset_setid, cpuset_getid, cpuset_getaffinity, cpuset_setaffinity).
Also add sched_yield, sched_get_priority_min, and
sched_get_priority_max to bsd-proc.h.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/bsd-proc.h | 20 +++-
bsd-user/freebsd/os-misc.h | 288 +++++++++++++++++++++++++++++++++++++--------
2 files changed, 257 insertions(+), 51 deletions(-)
diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
index 62052c70b9..b9973dbedf 100644
--- a/bsd-user/bsd-proc.h
+++ b/bsd-user/bsd-proc.h
@@ -400,4 +400,22 @@ static inline abi_long do_bsd_setpriority(abi_long which, abi_long who,
return get_errno(setpriority(which, who, prio));
}
-#endif /* !BSD_PROC_H_ */
+/* sched_yield(2) */
+static inline abi_long do_bsd_sched_yield(void)
+{
+ return get_errno(sched_yield());
+}
+
+/* sched_get_priority_min(2) */
+static inline abi_long do_bsd_sched_get_priority_min(int policy)
+{
+ return get_errno(sched_get_priority_min(policy));
+}
+
+/* sched_get_priority_max(2) */
+static inline abi_long do_bsd_sched_get_priority_max(int policy)
+{
+ return get_errno(sched_get_priority_max(policy));
+}
+
+#endif /* !BSD_PROC_H */
diff --git a/bsd-user/freebsd/os-misc.h b/bsd-user/freebsd/os-misc.h
index d9979b2a8b..74ed1d2f47 100644
--- a/bsd-user/freebsd/os-misc.h
+++ b/bsd-user/freebsd/os-misc.h
@@ -11,76 +11,264 @@
#include <sys/cpuset.h>
#include <sys/random.h>
#include <sched.h>
+#include <kenv.h>
-/*
- * shm_open2 isn't exported, but the __sys_ alias is. We can use either for the
- * static version, but to dynamically link we have to use the sys version.
- */
-int __sys_shm_open2(const char *path, int flags, mode_t mode, int shmflags,
+int shm_open2(const char *path, int flags, mode_t mode, int shmflags,
const char *);
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300048
-/* shm_open2(2) */
-static inline abi_long do_freebsd_shm_open2(abi_ulong pathptr, abi_ulong flags,
- abi_long mode, abi_ulong shmflags, abi_ulong nameptr)
+/* sched_setparam(2) */
+static inline abi_long do_freebsd_sched_setparam(pid_t pid,
+ abi_ulong target_sp_addr)
{
- int ret;
- void *uname, *upath;
+ abi_long ret;
+ struct sched_param host_sp;
- if (pathptr == (uintptr_t)SHM_ANON) {
- upath = SHM_ANON;
- } else {
- upath = lock_user_string(pathptr);
- if (upath == NULL) {
- return -TARGET_EFAULT;
- }
+ ret = get_user_s32(host_sp.sched_priority, target_sp_addr);
+ if (!is_error(ret)) {
+ ret = get_errno(sched_setparam(pid, &host_sp));
}
+ return ret;
+}
- uname = NULL;
- if (nameptr != 0) {
- uname = lock_user_string(nameptr);
- if (uname == NULL) {
- unlock_user(upath, pathptr, 0);
- return -TARGET_EFAULT;
- }
+/* sched_get_param(2) */
+static inline abi_long do_freebsd_sched_getparam(pid_t pid,
+ abi_ulong target_sp_addr)
+{
+ abi_long ret;
+ struct sched_param host_sp;
+
+ ret = get_errno(sched_getparam(pid, &host_sp));
+ if (!is_error(ret)) {
+ ret = put_user_s32(host_sp.sched_priority, target_sp_addr);
}
- ret = get_errno(__sys_shm_open2(upath,
- target_to_host_bitmask(flags, fcntl_flags_tbl), mode,
- target_to_host_bitmask(shmflags, shmflag_flags_tbl), uname));
+ return ret;
+}
+
+/* sched_setscheduler(2) */
+static inline abi_long do_freebsd_sched_setscheduler(pid_t pid, int policy,
+ abi_ulong target_sp_addr)
+{
+ abi_long ret;
+ struct sched_param host_sp;
- if (upath != SHM_ANON) {
- unlock_user(upath, pathptr, 0);
+ ret = get_user_s32(host_sp.sched_priority, target_sp_addr);
+ if (!is_error(ret)) {
+ ret = get_errno(sched_setscheduler(pid, policy, &host_sp));
}
- if (uname != NULL) {
- unlock_user(uname, nameptr, 0);
+ return ret;
+}
+
+/* sched_getscheduler(2) */
+static inline abi_long do_freebsd_sched_getscheduler(pid_t pid)
+{
+
+ return get_errno(sched_getscheduler(pid));
+}
+
+/* sched_getscheduler(2) */
+static inline abi_long do_freebsd_sched_rr_get_interval(pid_t pid,
+ abi_ulong target_ts_addr)
+{
+ abi_long ret;
+ struct timespec host_ts;
+
+ ret = get_errno(sched_rr_get_interval(pid, &host_ts));
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_timespec(target_ts_addr, &host_ts);
}
return ret;
}
-#endif /* __FreeBSD_version >= 1300048 */
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300049
-/* shm_rename(2) */
-static inline abi_long do_freebsd_shm_rename(abi_ulong fromptr, abi_ulong toptr,
- abi_ulong flags)
+/* cpuset(2) */
+static inline abi_long do_freebsd_cpuset(abi_ulong target_cpuid)
{
- int ret;
- void *ufrom, *uto;
+ abi_long ret;
+ cpusetid_t setid;
- ufrom = lock_user_string(fromptr);
- if (ufrom == NULL) {
- return -TARGET_EFAULT;
+ ret = get_errno(cpuset(&setid));
+ if (is_error(ret)) {
+ return ret;
}
- uto = lock_user_string(toptr);
- if (uto == NULL) {
- unlock_user(ufrom, fromptr, 0);
- return -TARGET_EFAULT;
+ return put_user_s32(setid, target_cpuid);
+}
+
+#define target_to_host_cpuset_which(hp, t) { \
+ (*hp) = t; \
+} while (0)
+
+#define target_to_host_cpuset_level(hp, t) { \
+ (*hp) = t; \
+} while (0)
+
+/* cpuset_setid(2) */
+static inline abi_long do_freebsd_cpuset_setid(CPUArchState *env, abi_long arg1,
+ abi_ulong arg2, abi_ulong arg3, abi_ulong arg4, abi_ulong arg5)
+{
+ id_t id; /* 64-bit value */
+ cpusetid_t setid;
+ cpuwhich_t which;
+
+ target_to_host_cpuset_which(&which, arg1);
+#if TARGET_ABI_BITS == 32
+ /* See if we need to align the register pairs */
+ if (regpairs_aligned(env)) {
+ id = target_arg64(arg3, arg4);
+ setid = arg5;
+ } else {
+ id = target_arg64(arg2, arg3);
+ setid = arg4;
}
- ret = get_errno(shm_rename(ufrom, uto, flags));
- unlock_user(ufrom, fromptr, 0);
- unlock_user(uto, toptr, 0);
+#else
+ id = arg2;
+ setid = arg3;
+#endif
+ return get_errno(cpuset_setid(which, id, setid));
+}
+
+/* cpuset_getid(2) */
+static inline abi_long do_freebsd_cpuset_getid(abi_long arg1, abi_ulong arg2,
+ abi_ulong arg3, abi_ulong arg4, abi_ulong arg5)
+{
+ abi_long ret;
+ id_t id; /* 64-bit value */
+ cpusetid_t setid;
+ cpuwhich_t which;
+ cpulevel_t level;
+ abi_ulong target_setid;
+
+ target_to_host_cpuset_which(&which, arg1)
+ ;
+ target_to_host_cpuset_level(&level, arg2);
+#if TARGET_ABI_BITS == 32
+ id = target_arg64(arg3, arg4);
+ target_setid = arg5;
+#else
+ id = arg3;
+ target_setid = arg4;
+#endif
+ ret = get_errno(cpuset_getid(level, which, id, &setid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ return put_user_s32(setid, target_setid);
+}
+
+static abi_ulong copy_from_user_cpuset_mask(cpuset_t *mask,
+ abi_ulong target_mask_addr)
+{
+ int i, j, k;
+ abi_ulong b, *target_mask;
+
+ target_mask = lock_user(VERIFY_READ, target_mask_addr,
+ CPU_SETSIZE / 8, 1);
+ if (target_mask == NULL) {
+ return -TARGET_EFAULT;
+ }
+ CPU_ZERO(mask);
+ k = 0;
+ for (i = 0; i < ((CPU_SETSIZE / 8) / sizeof(abi_ulong)); i++) {
+ __get_user(b, &target_mask[i]);
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ if ((b >> j) & 1) {
+ CPU_SET(k, mask);
+ }
+ k++;
+ }
+ }
+ unlock_user(target_mask, target_mask_addr, 0);
+
+ return 0;
+}
+
+static abi_ulong copy_to_user_cpuset_mask(abi_ulong target_mask_addr,
+ cpuset_t *mask)
+{
+ int i, j, k;
+ abi_ulong b, *target_mask;
+
+ target_mask = lock_user(VERIFY_WRITE, target_mask_addr,
+ CPU_SETSIZE / 8, 0);
+ if (target_mask == NULL) {
+ return -TARGET_EFAULT;
+ }
+ k = 0;
+ for (i = 0; i < ((CPU_SETSIZE / 8) / sizeof(abi_ulong)); i++) {
+ b = 0;
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ b |= ((CPU_ISSET(k, mask) != 0) << j);
+ k++;
+ }
+ __put_user(b, &target_mask[i]);
+ }
+ unlock_user(target_mask, target_mask_addr, (CPU_SETSIZE / 8));
+
+ return 0;
+}
+
+/* cpuset_getaffinity(2) */
+/* cpuset_getaffinity(cpulevel_t, cpuwhich_t, id_t, size_t, cpuset_t *); */
+static inline abi_long do_freebsd_cpuset_getaffinity(cpulevel_t level,
+ cpuwhich_t which, abi_ulong arg3, abi_ulong arg4, abi_ulong arg5,
+ abi_ulong arg6)
+{
+ cpuset_t mask;
+ abi_long ret;
+ id_t id; /* 64-bit */
+ abi_ulong setsize, target_mask;
+
+#if TARGET_ABI_BITS == 32
+ id = (id_t)target_arg64(arg3, arg4);
+ setsize = arg5;
+ target_mask = arg6;
+#else
+ id = (id_t)arg3;
+ setsize = arg4;
+ target_mask = arg5;
+#endif
+
+ ret = get_errno(cpuset_getaffinity(level, which, id, setsize, &mask));
+ if (ret == 0) {
+ ret = copy_to_user_cpuset_mask(target_mask, &mask);
+ }
return ret;
}
-#endif /* __FreeBSD_version >= 1300049 */
+
+/* cpuset_setaffinity(2) */
+/* cpuset_setaffinity(cpulevel_t, cpuwhich_t, id_t, size_t, const cpuset_t *);*/
+static inline abi_long do_freebsd_cpuset_setaffinity(cpulevel_t level,
+ cpuwhich_t which, abi_ulong arg3, abi_ulong arg4, abi_ulong arg5,
+ abi_ulong arg6)
+{
+ cpuset_t mask;
+ abi_long ret;
+ id_t id; /* 64-bit */
+ abi_ulong setsize, target_mask;
+
+#if TARGET_ABI_BITS == 32
+ id = (id_t)target_arg64(arg3, arg4);
+ setsize = arg5;
+ target_mask = arg6;
+#else
+ id = (id_t)arg3;
+ setsize = arg4;
+ target_mask = arg5;
+#endif
+
+ ret = copy_from_user_cpuset_mask(&mask, target_mask);
+ if (ret == 0) {
+ ret = get_errno(cpuset_setaffinity(level, which, id, setsize,
+ &mask));
+ }
+
+ return ret;
+}
+
+/*
+ * Pretend there are no modules loaded into the kernel. Don't allow loading or
+ * unloading of modules. This works well for tests, and little else seems to
+ * care. Will reevaluate if examples are found that do matter.
+ */
+
#endif /* OS_MISC_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 36/37] bsd-user: Add kmod, posix, and misc functions to os-misc.h
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (34 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 35/37] bsd-user: Add scheduler and cpuset functions to os-misc.h Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-18 21:27 ` [PATCH v2 37/37] bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch Warner Losh
2026-05-23 0:03 ` [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Pierrick Bouvier
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Karim Taha,
Sean Bruno, Alexander Kabaev
Complete os-misc.h with kernel module functions (modfnext, modfind,
kldload, kldunload, kldunloadf, kldfind, kldnext, kldstat,
kldfirstmod, kldsym), POSIX functions (posix_fallocate, posix_openpt),
getrandom, and kenv.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Karim Taha <kariem.taha2.7@gmail.com>
Signed-off-by: Sean Bruno <sbruno@FreeBSD.org>
Signed-off-by: Alexander Kabaev <kan@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Assisted-by: Claude Opus 4.6 (1M context)
---
bsd-user/freebsd/os-misc.h | 274 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 274 insertions(+)
diff --git a/bsd-user/freebsd/os-misc.h b/bsd-user/freebsd/os-misc.h
index 74ed1d2f47..cc33d16abb 100644
--- a/bsd-user/freebsd/os-misc.h
+++ b/bsd-user/freebsd/os-misc.h
@@ -270,5 +270,279 @@ static inline abi_long do_freebsd_cpuset_setaffinity(cpulevel_t level,
* care. Will reevaluate if examples are found that do matter.
*/
+/* modfnext(2) */
+static inline abi_long do_freebsd_modfnext(abi_long modid)
+{
+ return -TARGET_ENOENT;
+}
+
+/* modfind(2) */
+static inline abi_long do_freebsd_modfind(abi_ulong target_name)
+{
+ return -TARGET_ENOENT;
+}
+
+/* kldload(2) */
+static inline abi_long do_freebsd_kldload(abi_ulong target_name)
+{
+ return -TARGET_EPERM;
+}
+
+/* kldunload(2) */
+static inline abi_long do_freebsd_kldunload(abi_long fileid)
+{
+ return -TARGET_EPERM;
+}
+
+/* kldunloadf(2) */
+static inline abi_long do_freebsd_kldunloadf(abi_long fileid, abi_long flags)
+{
+ return -TARGET_EPERM;
+}
+
+/* kldfind(2) */
+static inline abi_long do_freebsd_kldfind(abi_ulong target_name)
+{
+ return -TARGET_ENOENT;
+}
+
+/* kldnext(2) */
+static inline abi_long do_freebsd_kldnext(abi_long fileid)
+{
+ return -TARGET_ENOENT;
+}
+
+
+/* kldstat(2) */
+static inline abi_long do_freebsd_kldstat(abi_long fileid,
+ abi_ulong target_stat)
+{
+ return -TARGET_ENOENT;
+}
+
+/* kldfirstmod(2) */
+static inline abi_long do_freebsd_kldfirstmod(abi_long fileid)
+{
+ return -TARGET_ENOENT;
+}
+
+/* kldsym(2) */
+static inline abi_long do_freebsd_kldsym(abi_long fileid, abi_long cmd,
+ abi_ulong target_data)
+{
+ return -TARGET_ENOENT;
+}
+
+/*
+ * New posix calls
+ */
+
+#if TARGET_ABI_BITS == 32
+static inline uint64_t target_offset64(uint32_t word0, uint32_t word1)
+{
+#ifdef TARGET_BIG_ENDIAN
+ return ((uint64_t)word0 << 32) | word1;
+#else
+ return ((uint64_t)word1 << 32) | word0;
+#endif
+}
+#else /* TARGET_ABI_BITS == 32 */
+static inline uint64_t target_offset64(uint64_t word0, uint64_t word1)
+{
+ return word0;
+}
+#endif /* TARGET_ABI_BITS != 32 */
+
+/* posix_fallocate(2) */
+static inline abi_long do_freebsd_posix_fallocate(abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
+{
+
+#if TARGET_ABI_BITS == 32
+ return get_errno(posix_fallocate(arg1, target_offset64(arg3, arg4),
+ target_offset64(arg5, arg6)));
+#else
+ return get_errno(posix_fallocate(arg1, arg2, arg3));
+#endif
+}
+
+/* posix_openpt(2) */
+static inline abi_long do_freebsd_posix_openpt(abi_long flags)
+{
+
+ return get_errno(posix_openpt(flags));
+}
+
+/*
+ * shm_open2 isn't exported, but the __sys_ alias is. We can use either for the
+ * static version, but to dynamically link we have to use the sys version.
+ */
+int __sys_shm_open2(const char *path, int flags, mode_t mode, int shmflags,
+ const char *);
+
+/* shm_open2(2) */
+static inline abi_long do_freebsd_shm_open2(abi_ulong pathptr, abi_ulong flags,
+ abi_long mode, abi_ulong shmflags, abi_ulong nameptr)
+{
+ int ret;
+ void *uname, *upath;
+
+ if (pathptr == (uintptr_t)SHM_ANON) {
+ upath = SHM_ANON;
+ } else {
+ upath = lock_user_string(pathptr);
+ if (upath == NULL) {
+ return -TARGET_EFAULT;
+ }
+ }
+
+ uname = NULL;
+ if (nameptr != 0) {
+ uname = lock_user_string(nameptr);
+ if (uname == NULL) {
+ unlock_user(upath, pathptr, 0);
+ return -TARGET_EFAULT;
+ }
+ }
+ ret = get_errno(__sys_shm_open2(upath,
+ target_to_host_bitmask(flags, fcntl_flags_tbl), mode,
+ target_to_host_bitmask(shmflags, shmflag_flags_tbl), uname));
+
+ if (upath != SHM_ANON) {
+ unlock_user(upath, pathptr, 0);
+ }
+ if (uname != NULL) {
+ unlock_user(uname, nameptr, 0);
+ }
+ return ret;
+}
+
+/* shm_rename(2) */
+static inline abi_long do_freebsd_shm_rename(abi_ulong fromptr, abi_ulong toptr,
+ abi_ulong flags)
+{
+ int ret;
+ void *ufrom, *uto;
+
+ ufrom = lock_user_string(fromptr);
+ if (ufrom == NULL) {
+ return -TARGET_EFAULT;
+ }
+ uto = lock_user_string(toptr);
+ if (uto == NULL) {
+ unlock_user(ufrom, fromptr, 0);
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(shm_rename(ufrom, uto, flags));
+ unlock_user(ufrom, fromptr, 0);
+ unlock_user(uto, toptr, 0);
+
+ return ret;
+}
+
+#if defined(CONFIG_GETRANDOM)
+static inline abi_long do_freebsd_getrandom(abi_ulong buf, abi_ulong buflen,
+ abi_ulong flags)
+{
+ abi_long ret;
+ void *p;
+
+ p = lock_user(VERIFY_WRITE, buf, buflen, 0);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(getrandom(p, buflen, flags));
+ unlock_user(p, buf, ret);
+
+ return ret;
+}
+#endif
+
+static inline abi_long do_freebsd_kenv(abi_long action, abi_ulong name,
+ abi_ulong value, abi_long len)
+{
+ abi_long ret;
+ void *gname = NULL; /* unlocked in cases where set */
+ void *gvalue = NULL; /* unlocked in cases where set */
+
+ ret = -TARGET_EINVAL;
+ switch (action) {
+ case KENV_GET:
+ gname = lock_user_string(name);
+ if (gname == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ gvalue = lock_user(VERIFY_WRITE, value, len, 0);
+ if (gvalue == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ ret = get_errno(kenv(action, gname, gvalue, len));
+ if (ret > 0) {
+ len = ret;
+ } else {
+ len = 0;
+ }
+ break;
+ case KENV_SET:
+ gname = lock_user_string(name);
+ if (gname == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ gvalue = lock_user(VERIFY_READ, value, len, 1);
+ if (gvalue == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ ret = get_errno(kenv(action, gname, gvalue, len));
+ unlock_user(gvalue, value, 0);
+ gvalue = NULL;
+ break;
+ case KENV_UNSET:
+ gname = lock_user_string(name);
+ if (gname == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ /* value and name ignored, per kenv(2) */
+ ret = get_errno(kenv(action, gname, NULL, 0));
+ break;
+ /* All three treated the same */
+ case KENV_DUMP:
+ case KENV_DUMP_LOADER:
+ case KENV_DUMP_STATIC:
+ /* value == NULL -> just return length */
+ if (value != 0) {
+ gvalue = lock_user(VERIFY_WRITE, value, len, 0);
+ if (gvalue == NULL) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+ }
+ /* name is ignored, per kenv(2) */
+ ret = get_errno(kenv(action, NULL, gvalue, len));
+ if (ret > 0) {
+ len = ret;
+ } else {
+ len = 0;
+ }
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
+ /* Unmap everything mapped */
+ if (gvalue != NULL) {
+ unlock_user(gvalue, value, len);
+ }
+ if (gname != NULL) {
+ unlock_user(gname, name, 0);
+ }
+
+ return ret;
+}
#endif /* OS_MISC_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* [PATCH v2 37/37] bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (35 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 36/37] bsd-user: Add kmod, posix, and misc " Warner Losh
@ 2026-05-18 21:27 ` Warner Losh
2026-05-23 0:03 ` [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Pierrick Bouvier
37 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-18 21:27 UTC (permalink / raw)
To: qemu-devel
Cc: Kyle Evans, Pierrick Bouvier, Warner Losh, Stacey Son, Kyle Evans
Wire up remaining system calls: sched_setparam, sched_getparam,
sched_setscheduler, sched_getscheduler, sched_get_priority_max,
sched_get_priority_min, sched_rr_get_interval, sched_yield,
cpuset, cpuset_setid, cpuset_getid, cpuset_getaffinity,
cpuset_setaffinity, modfnext, modfind, kldload, kldunload,
kldunloadf, kldfind, kldnext, kldstat, kldfirstmod, kldsym,
posix_fallocate, posix_openpt, getrandom, kenv, cap_rights_limit,
cap_ioctls_limit, cap_fcntls_limit, cap_enter, syscall, and
__syscall.
Signed-off-by: Stacey Son <sson@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/bsd-proc.h | 2 +-
bsd-user/freebsd/os-syscall.c | 522 ++++++++++++++++++++++++++----------------
2 files changed, 329 insertions(+), 195 deletions(-)
diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
index b9973dbedf..70a72a6a94 100644
--- a/bsd-user/bsd-proc.h
+++ b/bsd-user/bsd-proc.h
@@ -418,4 +418,4 @@ static inline abi_long do_bsd_sched_get_priority_max(int policy)
return get_errno(sched_get_priority_max(policy));
}
-#endif /* !BSD_PROC_H */
+#endif /* !BSD_PROC_H_ */
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 7825e042b0..c8f88efc6f 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -532,6 +532,10 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_closefrom(arg1);
break;
+ case TARGET_FREEBSD_NR_close_range: /* close_range(2) */
+ ret = do_freebsd_close_range(arg1, arg2, arg3);
+ break;
+
case TARGET_FREEBSD_NR_revoke: /* revoke(2) */
ret = do_bsd_revoke(arg1);
break;
@@ -696,6 +700,10 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_chflags(arg1, arg2);
break;
+ case TARGET_FREEBSD_NR_chflagsat: /* chflagsat(2) */
+ ret = do_bsd_chflagsat(arg1, arg2, arg3, arg4);
+ break;
+
case TARGET_FREEBSD_NR_lchflags: /* lchflags(2) */
ret = do_bsd_lchflags(arg1, arg2);
break;
@@ -740,6 +748,10 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_poll(arg1, arg2, arg3);
break;
+ case TARGET_FREEBSD_NR_ppoll: /* ppoll(2) */
+ ret = do_freebsd_ppoll(env, arg1, arg2, arg3, arg4);
+ break;
+
case TARGET_FREEBSD_NR_lseek: /* lseek(2) */
ret = do_bsd_lseek(env, arg1, arg2, arg3, arg4, arg5);
break;
@@ -766,14 +778,6 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_swapoff(arg1, arg2);
break;
- case TARGET_FREEBSD_NR_chflagsat: /* chflagsat(2) */
- ret = do_bsd_chflagsat(arg1, arg2, arg3, arg4);
- break;
-
- case TARGET_FREEBSD_NR_close_range: /* close_range(2) */
- ret = do_freebsd_close_range(arg1, arg2, arg3);
- break;
-
case TARGET_FREEBSD_NR___realpathat:
/* __realpathat(2) (XXX no realpathat()) */
ret = do_freebsd_realpathat(arg1, arg2, arg3, arg4, arg5);
@@ -787,125 +791,12 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd___specialfd(arg1, arg2, arg3);
break;
- /*
- * ioctl(2)
- */
- case TARGET_FREEBSD_NR_ioctl: /* ioctl(2) */
- ret = do_bsd_ioctl(arg1, arg2, arg3);
- break;
-
- /*
- * stat system calls
- */
- case TARGET_FREEBSD_NR_freebsd11_stat: /* stat(2) */
- ret = do_freebsd11_stat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_lstat: /* lstat(2) */
- ret = do_freebsd11_lstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_fstat: /* fstat(2) */
- ret = do_freebsd11_fstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_fstat: /* fstat(2) */
- ret = do_freebsd_fstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_fstatat: /* fstatat(2) */
- ret = do_freebsd11_fstatat(arg1, arg2, arg3, arg4);
- break;
-
- case TARGET_FREEBSD_NR_fstatat: /* fstatat(2) */
- ret = do_freebsd_fstatat(arg1, arg2, arg3, arg4);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_nstat: /* undocumented */
- ret = do_freebsd11_nstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_nfstat: /* undocumented */
- ret = do_freebsd11_nfstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_nlstat: /* undocumented */
- ret = do_freebsd11_nlstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_getfh: /* getfh(2) */
- ret = do_freebsd_getfh(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_lgetfh: /* lgetfh(2) */
- ret = do_freebsd_lgetfh(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_fhopen: /* fhopen(2) */
- ret = do_freebsd_fhopen(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_fhstat: /* fhstat(2) */
- ret = do_freebsd11_fhstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_fhstat: /* fhstat(2) */
- ret = do_freebsd_fhstat(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_fhstatfs: /* fhstatfs(2) */
- ret = do_freebsd11_fhstatfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_fhstatfs: /* fhstatfs(2) */
- ret = do_freebsd_fhstatfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_statfs: /* statfs(2) */
- ret = do_freebsd11_statfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_statfs: /* statfs(2) */
- ret = do_freebsd_statfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_fstatfs: /* fstatfs(2) */
- ret = do_freebsd11_fstatfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_fstatfs: /* fstatfs(2) */
- ret = do_freebsd_fstatfs(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_getfsstat: /* getfsstat(2) */
- ret = do_freebsd11_getfsstat(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_getfsstat: /* getfsstat(2) */
- ret = do_freebsd_getfsstat(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_getdents: /* getdents(2) */
- ret = do_freebsd11_getdents(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_getdirentries: /* getdirentries(2) */
- ret = do_freebsd_getdirentries(arg1, arg2, arg3, arg4);
- break;
-
- case TARGET_FREEBSD_NR_freebsd11_getdirentries: /* getdirentries(2) */
- ret = do_freebsd11_getdirentries(arg1, arg2, arg3, arg4);
- break;
- case TARGET_FREEBSD_NR_fcntl: /* fcntl(2) */
- ret = do_freebsd_fcntl(arg1, arg2, arg3);
- break;
-
/*
* Memory management system calls.
*/
case TARGET_FREEBSD_NR_mmap: /* mmap(2) */
ret = do_bsd_mmap(env, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8);
+ arg8);
break;
case TARGET_FREEBSD_NR_munmap: /* munmap(2) */
@@ -952,17 +843,13 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_shm_open(arg1, arg2, arg3);
break;
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300048
case TARGET_FREEBSD_NR_shm_open2: /* shm_open2(2) */
ret = do_freebsd_shm_open2(arg1, arg2, arg3, arg4, arg5);
break;
-#endif
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300049
case TARGET_FREEBSD_NR_shm_rename: /* shm_rename(2) */
ret = do_freebsd_shm_rename(arg1, arg2, arg3);
break;
-#endif
case TARGET_FREEBSD_NR_shm_unlink: /* shm_unlink(2) */
ret = do_bsd_shm_unlink(arg1);
@@ -984,68 +871,10 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_bsd_shmdt(arg1);
break;
- /*
- * System V Semaphores
- */
- case TARGET_FREEBSD_NR_semget: /* semget(2) */
- ret = do_bsd_semget(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_semop: /* semop(2) */
- ret = do_bsd_semop(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR___semctl: { /* __semctl() undocumented */
- ret = do_bsd___semctl(arg1, arg2, arg3, arg4);
- break;
- }
-
- /*
- * System V Messages
- */
- case TARGET_FREEBSD_NR_msgctl: /* msgctl(2) */
- ret = do_bsd_msgctl(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_msgget: /* msgget(2) */
- ret = do_bsd_msgget(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_msgsnd: /* msgsnd(2) */
- ret = do_bsd_msgsnd(arg1, arg2, arg3, arg4);
- break;
-
- case TARGET_FREEBSD_NR_msgrcv: /* msgrcv(2) */
- ret = do_bsd_msgrcv(arg1, arg2, arg3, arg4, arg5);
- break;
-
case TARGET_FREEBSD_NR_freebsd11_vadvise:
ret = do_bsd_vadvise();
break;
- /*
- * Misc
- */
- case TARGET_FREEBSD_NR_break:
- ret = do_obreak(arg1);
- break;
-
- case TARGET_FREEBSD_NR_quotactl: /* quotactl(2) */
- ret = do_bsd_quotactl(arg1, arg2, arg3);
- break;
-
- case TARGET_FREEBSD_NR_reboot: /* reboot(2) */
- ret = do_bsd_reboot(arg1);
- break;
-
- case TARGET_FREEBSD_NR_uuidgen: /* uuidgen(2) */
- ret = do_bsd_uuidgen(arg1, arg2);
- break;
-
- case TARGET_FREEBSD_NR_getdtablesize: /* getdtablesize(2) */
- ret = do_bsd_getdtablesize();
- break;
-
/*
* time related system calls.
*/
@@ -1133,10 +962,6 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd_pselect(env, arg1, arg2, arg3, arg4, arg5, arg6);
break;
- case TARGET_FREEBSD_NR_ppoll: /* ppoll(2) */
- ret = do_freebsd_ppoll(env, arg1, arg2, arg3, arg4);
- break;
-
case TARGET_FREEBSD_NR_kqueue: /* kqueue(2) */
ret = do_freebsd_kqueue();
break;
@@ -1350,6 +1175,119 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd__umtx_op(arg1, arg2, arg3, arg4, arg5);
break;
+ /*
+ * ioctl(2)
+ */
+ case TARGET_FREEBSD_NR_ioctl: /* ioctl(2) */
+ ret = do_bsd_ioctl(arg1, arg2, arg3);
+ break;
+
+ /*
+ * stat system calls
+ */
+ case TARGET_FREEBSD_NR_freebsd11_stat: /* stat(2) */
+ ret = do_freebsd11_stat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_lstat: /* lstat(2) */
+ ret = do_freebsd11_lstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_fstat: /* fstat(2) */
+ ret = do_freebsd11_fstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_fstat: /* fstat(2) */
+ ret = do_freebsd_fstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_fstatat: /* fstatat(2) */
+ ret = do_freebsd11_fstatat(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_fstatat: /* fstatat(2) */
+ ret = do_freebsd_fstatat(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_nstat: /* undocumented */
+ ret = do_freebsd11_nstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_nfstat: /* undocumented */
+ ret = do_freebsd11_nfstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_nlstat: /* undocumented */
+ ret = do_freebsd11_nlstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_getfh: /* getfh(2) */
+ ret = do_freebsd_getfh(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_lgetfh: /* lgetfh(2) */
+ ret = do_freebsd_lgetfh(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_fhopen: /* fhopen(2) */
+ ret = do_freebsd_fhopen(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_fhstat: /* fhstat(2) */
+ ret = do_freebsd11_fhstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_fhstat: /* fhstat(2) */
+ ret = do_freebsd_fhstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_fhstatfs: /* fhstatfs(2) */
+ ret = do_freebsd11_fhstatfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_fhstatfs: /* fhstatfs(2) */
+ ret = do_freebsd_fhstatfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_statfs: /* statfs(2) */
+ ret = do_freebsd11_statfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_statfs: /* statfs(2) */
+ ret = do_freebsd_statfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_fstatfs: /* fstatfs(2) */
+ ret = do_freebsd11_fstatfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_fstatfs: /* fstatfs(2) */
+ ret = do_freebsd_fstatfs(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_getfsstat: /* getfsstat(2) */
+ ret = do_freebsd11_getfsstat(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getfsstat: /* getfsstat(2) */
+ ret = do_freebsd_getfsstat(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_getdents: /* getdents(2) */
+ ret = do_freebsd11_getdents(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getdirentries: /* getdirentries(2) */
+ ret = do_freebsd_getdirentries(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_freebsd11_getdirentries: /* getdirentries(2) */
+ ret = do_freebsd11_getdirentries(arg1, arg2, arg3, arg4);
+ break;
+ case TARGET_FREEBSD_NR_fcntl: /* fcntl(2) */
+ ret = do_freebsd_fcntl(arg1, arg2, arg3);
+ break;
+
/*
* sys{ctl, arch, call}
*/
@@ -1365,19 +1303,25 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd_sysarch(env, arg1, arg2);
break;
+ case TARGET_FREEBSD_NR_syscall: /* syscall(2) */
+ case TARGET_FREEBSD_NR___syscall: /* __syscall(2) */
+ ret = do_freebsd_syscall(env, arg1 & 0xffff, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8, 0);
+ break;
+
/*
- * extended attributes and ACL system calls
+ * extended attributes system calls
*/
case TARGET_FREEBSD_NR_extattrctl: /* extattrctl() */
ret = do_freebsd_extattrctl(arg1, arg2, arg3, arg4, arg5);
break;
case TARGET_FREEBSD_NR_extattr_set_file: /* extattr_set_file(2) */
- ret = do_freebsd_extattr_set_file(arg1, arg2, arg3, arg4, arg4);
+ ret = do_freebsd_extattr_set_file(arg1, arg2, arg3, arg4, arg5);
break;
case TARGET_FREEBSD_NR_extattr_get_file: /* extattr_get_file(2) */
- ret = do_freebsd_extattr_get_file(arg1, arg2, arg3, arg4, arg4);
+ ret = do_freebsd_extattr_get_file(arg1, arg2, arg3, arg4, arg5);
break;
case TARGET_FREEBSD_NR_extattr_delete_file: /* extattr_delete_file(2) */
@@ -1397,11 +1341,11 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_extattr_get_link: /* extattr_get_link(2) */
- ret = do_freebsd_extattr_get_link(arg1, arg2, arg3, arg4, arg4);
+ ret = do_freebsd_extattr_get_link(arg1, arg2, arg3, arg4, arg5);
break;
case TARGET_FREEBSD_NR_extattr_set_link: /* extattr_set_link(2) */
- ret = do_freebsd_extattr_set_link(arg1, arg2, arg3, arg4, arg4);
+ ret = do_freebsd_extattr_set_link(arg1, arg2, arg3, arg4, arg5);
break;
case TARGET_FREEBSD_NR_extattr_delete_link: /* extattr_delete_link(2) */
@@ -1413,7 +1357,7 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
break;
case TARGET_FREEBSD_NR_extattr_list_file: /* extattr_list_file(2) */
- ret = do_freebsd_extattr_list_file(arg1, arg2, arg3, arg4);
+ ret = do_freebsd_extattr_list_file(arg1, arg2, arg3, arg4);
break;
case TARGET_FREEBSD_NR_extattr_list_link: /* extattr_list_link(2) */
@@ -1468,6 +1412,196 @@ static abi_long freebsd_syscall(CPUArchState *env, int num, abi_long arg1,
ret = do_freebsd__acl_set_link(arg1, arg2, arg3);
break;
+ /*
+ * System V Semaphores
+ */
+ case TARGET_FREEBSD_NR_semget: /* semget(2) */
+ ret = do_bsd_semget(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_semop: /* semop(2) */
+ ret = do_bsd_semop(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR___semctl: { /* __semctl() undocumented */
+ ret = do_bsd___semctl(arg1, arg2, arg3, arg4);
+ break;
+ }
+
+ /*
+ * System V Messages
+ */
+ case TARGET_FREEBSD_NR_msgctl: /* msgctl(2) */
+ ret = do_bsd_msgctl(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_msgget: /* msgget(2) */
+ ret = do_bsd_msgget(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_msgsnd: /* msgsnd(2) */
+ ret = do_bsd_msgsnd(arg1, arg2, arg3, arg4);
+ break;
+
+ case TARGET_FREEBSD_NR_msgrcv: /* msgrcv(2) */
+ ret = do_bsd_msgrcv(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ /*
+ * FreeBSD scheduler control
+ */
+ case TARGET_FREEBSD_NR_sched_setparam: /* sched_setparam(2) */
+ ret = do_freebsd_sched_setparam(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_getparam: /* sched_getparam(2) */
+ ret = do_freebsd_sched_getparam(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_setscheduler: /* sched_setscheduler(2) */
+ ret = do_freebsd_sched_setscheduler(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_getscheduler: /* sched_getscheduler(2) */
+ ret = do_freebsd_sched_getscheduler(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_get_priority_max: /* sched_get_priority_max(2)*/
+ ret = do_bsd_sched_get_priority_max(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_get_priority_min: /* sched_get_priority_min(2)*/
+ ret = do_bsd_sched_get_priority_min(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_rr_get_interval: /* sched_rr_get_interval(2) */
+ ret = do_freebsd_sched_rr_get_interval(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_sched_yield: /* sched_yield(2)*/
+ ret = do_bsd_sched_yield();
+ break;
+
+
+ /*
+ * FreeBSD CPU affinity sets management
+ */
+ case TARGET_FREEBSD_NR_cpuset: /* cpuset(2) */
+ ret = do_freebsd_cpuset(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_cpuset_setid: /* cpuset_setid(2) */
+ ret = do_freebsd_cpuset_setid(env, arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_cpuset_getid: /* cpuset_getid(2) */
+ ret = do_freebsd_cpuset_getid(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ case TARGET_FREEBSD_NR_cpuset_getaffinity: /* cpuset_getaffinity(2) */
+ ret = do_freebsd_cpuset_getaffinity(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_cpuset_setaffinity: /* cpuset_setaffinity(2) */
+ ret = do_freebsd_cpuset_setaffinity(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ /*
+ * FreeBSD kernel module
+ */
+ case TARGET_FREEBSD_NR_modfnext: /* modfnext(2) */
+ ret = do_freebsd_modfnext(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_modfind: /* modfind(2) */
+ ret = do_freebsd_modfind(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldload: /* kldload(2) */
+ ret = do_freebsd_kldload(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldunload: /* kldunload(2) */
+ ret = do_freebsd_kldunload(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldunloadf: /* kldunloadf(2) */
+ ret = do_freebsd_kldunloadf(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_kldfind: /* kldfind(2) */
+ ret = do_freebsd_kldfind(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldnext: /* kldnext(2) */
+ ret = do_freebsd_kldnext(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldstat: /* kldstat(2) */
+ ret = do_freebsd_kldstat(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_kldfirstmod: /* kldfirstmod(2) */
+ ret = do_freebsd_kldfirstmod(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_kldsym: /* kldsym(2) */
+ ret = do_freebsd_kldsym(arg1, arg2, arg3);
+ break;
+
+ /*
+ * FreeBSD additional posix support
+ */
+ case TARGET_FREEBSD_NR_posix_fallocate: /* posix_fallocate(2) */
+ ret = do_freebsd_posix_fallocate(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_posix_openpt: /* posix_openpt(2) */
+ ret = do_freebsd_posix_openpt(arg1);
+ break;
+
+ /*
+ * Misc
+ */
+ case TARGET_FREEBSD_NR_quotactl: /* quotactl(2) */
+ ret = do_bsd_quotactl(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_reboot: /* reboot(2) */
+ ret = do_bsd_reboot(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_uuidgen: /* uuidgen(2) */
+ ret = do_bsd_uuidgen(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_getdtablesize: /* getdtablesize(2) */
+ ret = do_bsd_getdtablesize();
+ break;
+
+ case TARGET_FREEBSD_NR_break:
+ ret = do_obreak(arg1);
+ break;
+
+#if defined(CONFIG_GETRANDOM)
+ case TARGET_FREEBSD_NR_getrandom:
+ ret = do_freebsd_getrandom(arg1, arg2, arg3);
+ break;
+#endif
+ case TARGET_FREEBSD_NR_kenv:
+ ret = do_freebsd_kenv(arg1, arg2, arg3, arg4);
+ break;
+
+ /* XXX */
+ case TARGET_FREEBSD_NR_cap_rights_limit:
+ case TARGET_FREEBSD_NR_cap_ioctls_limit:
+ case TARGET_FREEBSD_NR_cap_fcntls_limit:
+ ret = -TARGET_EINVAL;
+ break;
+ case TARGET_FREEBSD_NR_cap_enter:
+ ret = 0;
+ break;
+
default:
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
ret = -TARGET_ENOSYS;
--
2.52.0
^ permalink raw reply related [flat|nested] 73+ messages in thread* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-18 21:27 [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Warner Losh
` (36 preceding siblings ...)
2026-05-18 21:27 ` [PATCH v2 37/37] bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch Warner Losh
@ 2026-05-23 0:03 ` Pierrick Bouvier
2026-05-23 0:40 ` Warner Losh
37 siblings, 1 reply; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-23 0:03 UTC (permalink / raw)
To: Warner Losh, qemu-devel
Cc: Kyle Evans, Stacey Son, Jessica Clarke, Mikaël Urankar,
Michal Meloun, Sean Bruno, Karim Taha, Alexander Kabaev
On 5/18/2026 2:27 PM, Warner Losh wrote:
> Upstream the file, thread, socket and remaining signal system calls (too
> numerous to list here).
>
> This series is an ambitous use of claude to help me upstream all the
> remaining system calls. I've batched them together in what I think are
> reasonable chunks, and had claude do the grunt work of copying the code
> over and attributing the commits from our complex branching history. The
> chopping up was a bit arbitrary, but I think it's good. The commit
> messages may be a little weak, but may also be OK. I've also double
> checked the style and made fixes upstream for them as well. Claude also
> reviewed all these changes and found a few bugs that I've fixed. I've
> personally read through them all and haven't found anything glaring. I
> fixed all the bugs that were found, in most cases differently than
> claude's suggestions.
>
> I've added 'Assisted-by: Claude...' to all these commits to reflect my
> leaning on Claude so hard. This use falls within the 'non-creative' use
> that the Qemu project has said is OK. If that's not the right thing to
> do, I can remove them.
>
> This leaves sysctl translation, the powerpc architecture support,
> coredumps and a transition to 'truss' based system call tracing. With
> these changes applied we'er down from a high of about 30k lines of diffs
> to only 5k (not counting genereted, but checked in files in blitz). The
> changes are 8k, so maybe a bit ambitious from that perspective as well.
>
> There's a few lines over 80 that I've not cleaned up. Let me know if
> that's a problem. The other warnings are about adding files, and there's
> no new MAINTAINERS entry needed. And the 'arch dependent defines should
> be avoided' are needed to cope with different FreeBSD build systems
> having different system calls.
>
> Note, this is called out below too, but in v2 I've folded back all the
> fixes based on some out-of-band feedback I recieved to do this the
> normal way and the qemu project isn't interested in the fixes to fixes.
>
> This is a big experiment, in many ways, for me, so I'm interested in
> whatever feedback you may have to make things better in the future.
>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> ---
> Changes in v2:
> - don't write offsets on errors for copy_file_range
> - pipe2: Check the raw system call value directly
> - setsockopt: Check target_to_host_ip_mreq return value
> - <several places>: Check return value of host_to_target_sockaddr
> - do_bsd_setsockopt: Read in the socket option
> - ntp_gettime: write back time on non-errors, not just 0
> - do_freebsd_sigtimedwait check t2h_freebsd_timespec return
> - do_freebsd_swapcontext writing incorrectly, readonly data (0 to user_unlock)
> - do_thr_set_name needs get_errno()
> - Have ntp_adjtime write back the structure
> - kevent_freebsd11 no need to explicitly mask, datatypes do this for us
> - do_freebsd_setcontext just reads the mcontext, 0 in unlock_user
> - target_to_host_sigevent is read-only, so pass 0 in unlock_user
> - target_to_host_ip_mreq needs tswap32 for int32_t
> - use get_errno on kenv return values to report errors better
> - kevent: only copy the number of events returns, not whole buffer
> - Copy tolerance field of timex
> - freebsd_umtx_sem_wake use VERIFY_READ instead of write to read struct
> - Avoid double g2h_untagged mistakes in !_UMTX_OPTIMIZED
> - Fixed additional missing unlock in freebsd_rw_rdlock and freebsd_rw_wrlock in !_UMTX_OPTIMIZED
> - Squashed all the bug commit back to the appropriate earleir patch
> - Link to v1: https://lore.kernel.org/qemu-devel/20260515-misc-2026q2-v1-0-5438ca41b27a@bsdimp.com
>
> ---
> Warner Losh (37):
> bsd-user: catchup to locking / mapping routines in bsd-misc.
> bsd-user: Rename cpu_env to env throughout bsd-user
> bsd-user: Add bsd-signal.h with sigaction through sigreturn
> bsd-user: Add signal shims sigwait through kill and os-signal.h
> bsd-user: Add signal system call dispatch
> bsd-user: Add poll, lseek, pipe, and swap system call shims
> bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and more
> bsd-user: Add file operation system call dispatch
> bsd-user: Add bsd-socket.h with bind through getsockname
> bsd-user: Add socket shims socketpair through shutdown
> bsd-user: Add os-socket.h with sendrecvmsg and message structures
> bsd-user: Add do_bsd_setsockopt and socket option definitions
> bsd-user: Add do_bsd_getsockopt
> bsd-user: Add FreeBSD socket helpers and sockaddr conversion
> bsd-user: Add os-socket.c with cmsg conversion functions
> bsd-user: Add socket system call dispatch
> bsd-user: Add os-time.h with clock and time-of-day functions
> bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
> bsd-user: Add select, pselect, and ppoll to os-time.h
> bsd-user: Add kqueue and kevent functions to os-time.h
> bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
> bsd-user: Add os-time.c and time-related type definitions
> bsd-user: Add time system call dispatch
> bsd-user: Add thread, umtx, and ACL type definitions
> bsd-user: Add cpu_copy and make init_task_state non-static
> bsd-user: Add os-thread.h with thr and context functions
> bsd-user: Add do_freebsd__umtx_op to os-thread.h
> bsd-user: Add os-thread.c with umtx, mutex, and thread creation
> bsd-user: Add thread system call dispatch
> bsd-user: Add os-extattr.h with file and fd extattr functions
> bsd-user: Add link and list extattr functions to os-extattr.h
> bsd-user: Add ACL system call shims to os-extattr.h
> bsd-user: Add os-extattr.c with ACL conversion functions
> bsd-user: Add extended attribute and ACL system call dispatch
> bsd-user: Add scheduler and cpuset functions to os-misc.h
> bsd-user: Add kmod, posix, and misc functions to os-misc.h
> bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch
>
> bsd-user/aarch64/target.h | 2 +-
> bsd-user/arm/target.h | 2 +-
> bsd-user/bsd-file.h | 164 +++-
> bsd-user/bsd-mem.h | 4 +-
> bsd-user/bsd-proc.h | 24 +-
> bsd-user/bsd-signal.h | 231 ++++++
> bsd-user/bsd-socket.c | 98 +++
> bsd-user/bsd-socket.h | 283 +++++++
> bsd-user/freebsd/meson.build | 8 +-
> bsd-user/freebsd/os-extattr.c | 116 +++
> bsd-user/freebsd/os-extattr.h | 662 ++++++++++++++++
> bsd-user/freebsd/os-file.h | 129 ++++
> bsd-user/freebsd/os-misc.h | 470 +++++++++++-
> bsd-user/freebsd/os-proc.c | 4 +-
> bsd-user/freebsd/os-proc.h | 26 +-
> bsd-user/freebsd/os-signal.h | 20 +
> bsd-user/freebsd/os-socket.c | 234 ++++++
> bsd-user/freebsd/os-socket.h | 770 +++++++++++++++++++
> bsd-user/freebsd/os-stat.h | 8 +-
> bsd-user/freebsd/os-sys.c | 4 +-
> bsd-user/freebsd/os-syscall.c | 790 +++++++++++++++++--
> bsd-user/freebsd/os-thread.c | 1682 +++++++++++++++++++++++++++++++++++++++++
> bsd-user/freebsd/os-thread.h | 596 +++++++++++++++
> bsd-user/freebsd/os-time.c | 346 +++++++++
> bsd-user/freebsd/os-time.h | 983 ++++++++++++++++++++++++
> bsd-user/freebsd/qemu-os.h | 48 ++
> bsd-user/i386/target.h | 2 +-
> bsd-user/main.c | 32 +-
> bsd-user/meson.build | 1 +
> bsd-user/qemu-bsd.h | 26 +
> bsd-user/qemu.h | 48 +-
> bsd-user/riscv/target.h | 2 +-
> bsd-user/signal-common.h | 4 +
> bsd-user/signal.c | 67 ++
> bsd-user/syscall_defs.h | 458 +++++++++--
> bsd-user/x86_64/target.h | 2 +-
> 36 files changed, 8150 insertions(+), 196 deletions(-)
> ---
> base-commit: edcc429e9e41a8e0e415dcdab6aa52ad17ef8889
> change-id: 20260504-misc-2026q2-a5af31ac032a
>
> Best regards,
After reviewing (what I can) two thirds of the series, I don't feel very
at ease with validating the rest. Not that there is problem in itself,
but it's a massive add, and I'm not sure how we can ensure it's
implemented correctly without any test exercising it.
It's not particular to bsd-user, linux-user does not have test for all
syscalls implemented neither.
However, because we are adding most of it at once, it's a bit different.
Dropping 8150 new lines at once without any test looks a bit complicated
to me.
A few questions about the series in itself:
- Where does most of the implementation comes from? Was it downstream on
FreeBSD side, or written from scratch for this series?
- How did you validate it works, beyond it compiles?
Regards,
Pierrick
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-23 0:03 ` [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls Pierrick Bouvier
@ 2026-05-23 0:40 ` Warner Losh
2026-05-23 1:19 ` Pierrick Bouvier
0 siblings, 1 reply; 73+ messages in thread
From: Warner Losh @ 2026-05-23 0:40 UTC (permalink / raw)
To: Pierrick Bouvier
Cc: qemu-devel, Kyle Evans, Stacey Son, Jessica Clarke,
Mikaël Urankar, Michal Meloun, Sean Bruno, Karim Taha,
Alexander Kabaev
[-- Attachment #1: Type: text/plain, Size: 11665 bytes --]
On Fri, May 22, 2026 at 6:04 PM Pierrick Bouvier <
pierrick.bouvier@oss.qualcomm.com> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote:
> > Upstream the file, thread, socket and remaining signal system calls (too
> > numerous to list here).
> >
> > This series is an ambitous use of claude to help me upstream all the
> > remaining system calls. I've batched them together in what I think are
> > reasonable chunks, and had claude do the grunt work of copying the code
> > over and attributing the commits from our complex branching history. The
> > chopping up was a bit arbitrary, but I think it's good. The commit
> > messages may be a little weak, but may also be OK. I've also double
> > checked the style and made fixes upstream for them as well. Claude also
> > reviewed all these changes and found a few bugs that I've fixed. I've
> > personally read through them all and haven't found anything glaring. I
> > fixed all the bugs that were found, in most cases differently than
> > claude's suggestions.
> >
> > I've added 'Assisted-by: Claude...' to all these commits to reflect my
> > leaning on Claude so hard. This use falls within the 'non-creative' use
> > that the Qemu project has said is OK. If that's not the right thing to
> > do, I can remove them.
> >
> > This leaves sysctl translation, the powerpc architecture support,
> > coredumps and a transition to 'truss' based system call tracing. With
> > these changes applied we'er down from a high of about 30k lines of diffs
> > to only 5k (not counting genereted, but checked in files in blitz). The
> > changes are 8k, so maybe a bit ambitious from that perspective as well.
> >
> > There's a few lines over 80 that I've not cleaned up. Let me know if
> > that's a problem. The other warnings are about adding files, and there's
> > no new MAINTAINERS entry needed. And the 'arch dependent defines should
> > be avoided' are needed to cope with different FreeBSD build systems
> > having different system calls.
> >
> > Note, this is called out below too, but in v2 I've folded back all the
> > fixes based on some out-of-band feedback I recieved to do this the
> > normal way and the qemu project isn't interested in the fixes to fixes.
> >
> > This is a big experiment, in many ways, for me, so I'm interested in
> > whatever feedback you may have to make things better in the future.
> >
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > ---
> > Changes in v2:
> > - don't write offsets on errors for copy_file_range
> > - pipe2: Check the raw system call value directly
> > - setsockopt: Check target_to_host_ip_mreq return value
> > - <several places>: Check return value of host_to_target_sockaddr
> > - do_bsd_setsockopt: Read in the socket option
> > - ntp_gettime: write back time on non-errors, not just 0
> > - do_freebsd_sigtimedwait check t2h_freebsd_timespec return
> > - do_freebsd_swapcontext writing incorrectly, readonly data (0 to
> user_unlock)
> > - do_thr_set_name needs get_errno()
> > - Have ntp_adjtime write back the structure
> > - kevent_freebsd11 no need to explicitly mask, datatypes do this for us
> > - do_freebsd_setcontext just reads the mcontext, 0 in unlock_user
> > - target_to_host_sigevent is read-only, so pass 0 in unlock_user
> > - target_to_host_ip_mreq needs tswap32 for int32_t
> > - use get_errno on kenv return values to report errors better
> > - kevent: only copy the number of events returns, not whole buffer
> > - Copy tolerance field of timex
> > - freebsd_umtx_sem_wake use VERIFY_READ instead of write to read struct
> > - Avoid double g2h_untagged mistakes in !_UMTX_OPTIMIZED
> > - Fixed additional missing unlock in freebsd_rw_rdlock and
> freebsd_rw_wrlock in !_UMTX_OPTIMIZED
> > - Squashed all the bug commit back to the appropriate earleir patch
> > - Link to v1:
> https://lore.kernel.org/qemu-devel/20260515-misc-2026q2-v1-0-5438ca41b27a@bsdimp.com
> >
> > ---
> > Warner Losh (37):
> > bsd-user: catchup to locking / mapping routines in bsd-misc.
> > bsd-user: Rename cpu_env to env throughout bsd-user
> > bsd-user: Add bsd-signal.h with sigaction through sigreturn
> > bsd-user: Add signal shims sigwait through kill and os-signal.h
> > bsd-user: Add signal system call dispatch
> > bsd-user: Add poll, lseek, pipe, and swap system call shims
> > bsd-user: Add os-file.h with pipe2, chflagsat, close_range, and
> more
> > bsd-user: Add file operation system call dispatch
> > bsd-user: Add bsd-socket.h with bind through getsockname
> > bsd-user: Add socket shims socketpair through shutdown
> > bsd-user: Add os-socket.h with sendrecvmsg and message structures
> > bsd-user: Add do_bsd_setsockopt and socket option definitions
> > bsd-user: Add do_bsd_getsockopt
> > bsd-user: Add FreeBSD socket helpers and sockaddr conversion
> > bsd-user: Add os-socket.c with cmsg conversion functions
> > bsd-user: Add socket system call dispatch
> > bsd-user: Add os-time.h with clock and time-of-day functions
> > bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
> > bsd-user: Add select, pselect, and ppoll to os-time.h
> > bsd-user: Add kqueue and kevent functions to os-time.h
> > bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
> > bsd-user: Add os-time.c and time-related type definitions
> > bsd-user: Add time system call dispatch
> > bsd-user: Add thread, umtx, and ACL type definitions
> > bsd-user: Add cpu_copy and make init_task_state non-static
> > bsd-user: Add os-thread.h with thr and context functions
> > bsd-user: Add do_freebsd__umtx_op to os-thread.h
> > bsd-user: Add os-thread.c with umtx, mutex, and thread creation
> > bsd-user: Add thread system call dispatch
> > bsd-user: Add os-extattr.h with file and fd extattr functions
> > bsd-user: Add link and list extattr functions to os-extattr.h
> > bsd-user: Add ACL system call shims to os-extattr.h
> > bsd-user: Add os-extattr.c with ACL conversion functions
> > bsd-user: Add extended attribute and ACL system call dispatch
> > bsd-user: Add scheduler and cpuset functions to os-misc.h
> > bsd-user: Add kmod, posix, and misc functions to os-misc.h
> > bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch
> >
> > bsd-user/aarch64/target.h | 2 +-
> > bsd-user/arm/target.h | 2 +-
> > bsd-user/bsd-file.h | 164 +++-
> > bsd-user/bsd-mem.h | 4 +-
> > bsd-user/bsd-proc.h | 24 +-
> > bsd-user/bsd-signal.h | 231 ++++++
> > bsd-user/bsd-socket.c | 98 +++
> > bsd-user/bsd-socket.h | 283 +++++++
> > bsd-user/freebsd/meson.build | 8 +-
> > bsd-user/freebsd/os-extattr.c | 116 +++
> > bsd-user/freebsd/os-extattr.h | 662 ++++++++++++++++
> > bsd-user/freebsd/os-file.h | 129 ++++
> > bsd-user/freebsd/os-misc.h | 470 +++++++++++-
> > bsd-user/freebsd/os-proc.c | 4 +-
> > bsd-user/freebsd/os-proc.h | 26 +-
> > bsd-user/freebsd/os-signal.h | 20 +
> > bsd-user/freebsd/os-socket.c | 234 ++++++
> > bsd-user/freebsd/os-socket.h | 770 +++++++++++++++++++
> > bsd-user/freebsd/os-stat.h | 8 +-
> > bsd-user/freebsd/os-sys.c | 4 +-
> > bsd-user/freebsd/os-syscall.c | 790 +++++++++++++++++--
> > bsd-user/freebsd/os-thread.c | 1682
> +++++++++++++++++++++++++++++++++++++++++
> > bsd-user/freebsd/os-thread.h | 596 +++++++++++++++
> > bsd-user/freebsd/os-time.c | 346 +++++++++
> > bsd-user/freebsd/os-time.h | 983 ++++++++++++++++++++++++
> > bsd-user/freebsd/qemu-os.h | 48 ++
> > bsd-user/i386/target.h | 2 +-
> > bsd-user/main.c | 32 +-
> > bsd-user/meson.build | 1 +
> > bsd-user/qemu-bsd.h | 26 +
> > bsd-user/qemu.h | 48 +-
> > bsd-user/riscv/target.h | 2 +-
> > bsd-user/signal-common.h | 4 +
> > bsd-user/signal.c | 67 ++
> > bsd-user/syscall_defs.h | 458 +++++++++--
> > bsd-user/x86_64/target.h | 2 +-
> > 36 files changed, 8150 insertions(+), 196 deletions(-)
> > ---
> > base-commit: edcc429e9e41a8e0e415dcdab6aa52ad17ef8889
> > change-id: 20260504-misc-2026q2-a5af31ac032a
> >
> > Best regards,
>
> After reviewing (what I can) two thirds of the series, I don't feel very
> at ease with validating the rest. Not that there is problem in itself,
> but it's a massive add, and I'm not sure how we can ensure it's
> implemented correctly without any test exercising it.
> It's not particular to bsd-user, linux-user does not have test for all
> syscalls implemented neither.
>
Yes. I'd like a low-level test like that. The upstream bsd-user has been too
incomplete to run real programs prior to this series. And there's still some
issues that remain after these system calls (which is next up on my list: I
think elfload needs some patches for PIE).
> However, because we are adding most of it at once, it's a bit different.
> Dropping 8150 new lines at once without any test looks a bit complicated
> to me.
>
> A few questions about the series in itself:
> - Where does most of the implementation comes from? Was it downstream on
> FreeBSD side, or written from scratch for this series?
>
We've had almost all of this code, modulo some big fixes I've added, in
blitz branch
for years. Stacey Son implemented most of this a decade ago now and had some
issue upstream at the time, IIRC, but I have only sketchy details of the
project then.
We use it inside our poudriere jails to build thousands of ports to test
it. Though there
are issues with many ports, enough work to keep going.
I also run a number of random commands in the jail that's nothing but
cross-arch binaries
as well.
Sometimes I run the Kyua tests, but it's a bit hard to read the output
because many are broken,
many can't work in qemu or in a jail, and there's some that are broken for
a given arch. We don't
have good baselines to compare against and I've not spent the time to get
this into good shape.
Not the most formal set of things we do, but it works.
> - How did you validate it works, beyond it compiles?
>
Generally on the blitz side, all of the above. On the qemu-project side, I
usually just do the bit
about messing around in the jail to test the basics.
I'm happy cleaving this massive set into some smaller sets if that would
help, but that still wouldn't
answer the 'how the heck do we test it'.
I've also had claude do code reviews of this branch. It's found maybe 100
bugs that I've fixed
(maybe 25 more between v2 and v3). The vast majority of the bugs are
lock/unlock issues. Either
leaks on error paths, failure to check for TARGET_EFAULT in places or
passing args that don't
matter unless you compile CONFIG_DEBUG_REMAP to indicate copying, etc. Some
real bugs
have been found, including one that might lead to a multi-threaded hang.
I have been putting off dealing with the PIE issue which blocks building
most ports at the moment,
but there's two pull requests to address it in blitz. When I tried them I
hit some problems, but
maybe I've fixed those in other ways. If I fixed that, I'd know that the
base code works, but
maybe all the pieces aren't in qemu-project yet.
Based on all that, what's your advice?
Warner
> Regards,
> Pierrick
>
[-- Attachment #2: Type: text/html, Size: 15157 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-23 0:40 ` Warner Losh
@ 2026-05-23 1:19 ` Pierrick Bouvier
2026-05-23 18:56 ` Helge Deller
2026-06-03 9:18 ` Daniel P. Berrangé
0 siblings, 2 replies; 73+ messages in thread
From: Pierrick Bouvier @ 2026-05-23 1:19 UTC (permalink / raw)
To: Warner Losh
Cc: qemu-devel, Kyle Evans, Stacey Son, Jessica Clarke,
Mikaël Urankar, Michal Meloun, Sean Bruno, Karim Taha,
Alexander Kabaev
On 5/22/2026 5:40 PM, Warner Losh wrote:
>
>
> On Fri, May 22, 2026 at 6:04 PM Pierrick Bouvier
> <pierrick.bouvier@oss.qualcomm.com
> <mailto:pierrick.bouvier@oss.qualcomm.com>> wrote:
>
> On 5/18/2026 2:27 PM, Warner Losh wrote:
> > Upstream the file, thread, socket and remaining signal system
> calls (too
> > numerous to list here).
> >
> > This series is an ambitous use of claude to help me upstream all the
> > remaining system calls. I've batched them together in what I think are
> > reasonable chunks, and had claude do the grunt work of copying the
> code
> > over and attributing the commits from our complex branching
> history. The
> > chopping up was a bit arbitrary, but I think it's good. The commit
> > messages may be a little weak, but may also be OK. I've also double
> > checked the style and made fixes upstream for them as well. Claude
> also
> > reviewed all these changes and found a few bugs that I've fixed. I've
> > personally read through them all and haven't found anything glaring. I
> > fixed all the bugs that were found, in most cases differently than
> > claude's suggestions.
> >
> > I've added 'Assisted-by: Claude...' to all these commits to reflect my
> > leaning on Claude so hard. This use falls within the 'non-
> creative' use
> > that the Qemu project has said is OK. If that's not the right thing to
> > do, I can remove them.
> >
> > This leaves sysctl translation, the powerpc architecture support,
> > coredumps and a transition to 'truss' based system call tracing. With
> > these changes applied we'er down from a high of about 30k lines of
> diffs
> > to only 5k (not counting genereted, but checked in files in
> blitz). The
> > changes are 8k, so maybe a bit ambitious from that perspective as
> well.
> >
> > There's a few lines over 80 that I've not cleaned up. Let me know if
> > that's a problem. The other warnings are about adding files, and
> there's
> > no new MAINTAINERS entry needed. And the 'arch dependent defines
> should
> > be avoided' are needed to cope with different FreeBSD build systems
> > having different system calls.
> >
> > Note, this is called out below too, but in v2 I've folded back all the
> > fixes based on some out-of-band feedback I recieved to do this the
> > normal way and the qemu project isn't interested in the fixes to
> fixes.
> >
> > This is a big experiment, in many ways, for me, so I'm interested in
> > whatever feedback you may have to make things better in the future.
> >
> > Signed-off-by: Warner Losh <imp@bsdimp.com <mailto:imp@bsdimp.com>>
> > ---
> > Changes in v2:
> > - don't write offsets on errors for copy_file_range
> > - pipe2: Check the raw system call value directly
> > - setsockopt: Check target_to_host_ip_mreq return value
> > - <several places>: Check return value of host_to_target_sockaddr
> > - do_bsd_setsockopt: Read in the socket option
> > - ntp_gettime: write back time on non-errors, not just 0
> > - do_freebsd_sigtimedwait check t2h_freebsd_timespec return
> > - do_freebsd_swapcontext writing incorrectly, readonly data (0 to
> user_unlock)
> > - do_thr_set_name needs get_errno()
> > - Have ntp_adjtime write back the structure
> > - kevent_freebsd11 no need to explicitly mask, datatypes do this
> for us
> > - do_freebsd_setcontext just reads the mcontext, 0 in unlock_user
> > - target_to_host_sigevent is read-only, so pass 0 in unlock_user
> > - target_to_host_ip_mreq needs tswap32 for int32_t
> > - use get_errno on kenv return values to report errors better
> > - kevent: only copy the number of events returns, not whole buffer
> > - Copy tolerance field of timex
> > - freebsd_umtx_sem_wake use VERIFY_READ instead of write to read
> struct
> > - Avoid double g2h_untagged mistakes in !_UMTX_OPTIMIZED
> > - Fixed additional missing unlock in freebsd_rw_rdlock and
> freebsd_rw_wrlock in !_UMTX_OPTIMIZED
> > - Squashed all the bug commit back to the appropriate earleir patch
> > - Link to v1: https://lore.kernel.org/qemu-devel/20260515-
> misc-2026q2-v1-0-5438ca41b27a@bsdimp.com <https://lore.kernel.org/
> qemu-devel/20260515-misc-2026q2-v1-0-5438ca41b27a@bsdimp.com>
> >
> > ---
> > Warner Losh (37):
> > bsd-user: catchup to locking / mapping routines in bsd-misc.
> > bsd-user: Rename cpu_env to env throughout bsd-user
> > bsd-user: Add bsd-signal.h with sigaction through sigreturn
> > bsd-user: Add signal shims sigwait through kill and os-signal.h
> > bsd-user: Add signal system call dispatch
> > bsd-user: Add poll, lseek, pipe, and swap system call shims
> > bsd-user: Add os-file.h with pipe2, chflagsat, close_range,
> and more
> > bsd-user: Add file operation system call dispatch
> > bsd-user: Add bsd-socket.h with bind through getsockname
> > bsd-user: Add socket shims socketpair through shutdown
> > bsd-user: Add os-socket.h with sendrecvmsg and message
> structures
> > bsd-user: Add do_bsd_setsockopt and socket option definitions
> > bsd-user: Add do_bsd_getsockopt
> > bsd-user: Add FreeBSD socket helpers and sockaddr conversion
> > bsd-user: Add os-socket.c with cmsg conversion functions
> > bsd-user: Add socket system call dispatch
> > bsd-user: Add os-time.h with clock and time-of-day functions
> > bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
> > bsd-user: Add select, pselect, and ppoll to os-time.h
> > bsd-user: Add kqueue and kevent functions to os-time.h
> > bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
> > bsd-user: Add os-time.c and time-related type definitions
> > bsd-user: Add time system call dispatch
> > bsd-user: Add thread, umtx, and ACL type definitions
> > bsd-user: Add cpu_copy and make init_task_state non-static
> > bsd-user: Add os-thread.h with thr and context functions
> > bsd-user: Add do_freebsd__umtx_op to os-thread.h
> > bsd-user: Add os-thread.c with umtx, mutex, and thread creation
> > bsd-user: Add thread system call dispatch
> > bsd-user: Add os-extattr.h with file and fd extattr functions
> > bsd-user: Add link and list extattr functions to os-extattr.h
> > bsd-user: Add ACL system call shims to os-extattr.h
> > bsd-user: Add os-extattr.c with ACL conversion functions
> > bsd-user: Add extended attribute and ACL system call dispatch
> > bsd-user: Add scheduler and cpuset functions to os-misc.h
> > bsd-user: Add kmod, posix, and misc functions to os-misc.h
> > bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch
> >
> > bsd-user/aarch64/target.h | 2 +-
> > bsd-user/arm/target.h | 2 +-
> > bsd-user/bsd-file.h | 164 +++-
> > bsd-user/bsd-mem.h | 4 +-
> > bsd-user/bsd-proc.h | 24 +-
> > bsd-user/bsd-signal.h | 231 ++++++
> > bsd-user/bsd-socket.c | 98 +++
> > bsd-user/bsd-socket.h | 283 +++++++
> > bsd-user/freebsd/meson.build | 8 +-
> > bsd-user/freebsd/os-extattr.c | 116 +++
> > bsd-user/freebsd/os-extattr.h | 662 ++++++++++++++++
> > bsd-user/freebsd/os-file.h | 129 ++++
> > bsd-user/freebsd/os-misc.h | 470 +++++++++++-
> > bsd-user/freebsd/os-proc.c | 4 +-
> > bsd-user/freebsd/os-proc.h | 26 +-
> > bsd-user/freebsd/os-signal.h | 20 +
> > bsd-user/freebsd/os-socket.c | 234 ++++++
> > bsd-user/freebsd/os-socket.h | 770 +++++++++++++++++++
> > bsd-user/freebsd/os-stat.h | 8 +-
> > bsd-user/freebsd/os-sys.c | 4 +-
> > bsd-user/freebsd/os-syscall.c | 790 +++++++++++++++++--
> > bsd-user/freebsd/os-thread.c | 1682 ++++++++++++++++++++++++++++
> +++++++++++++
> > bsd-user/freebsd/os-thread.h | 596 +++++++++++++++
> > bsd-user/freebsd/os-time.c | 346 +++++++++
> > bsd-user/freebsd/os-time.h | 983 ++++++++++++++++++++++++
> > bsd-user/freebsd/qemu-os.h | 48 ++
> > bsd-user/i386/target.h | 2 +-
> > bsd-user/main.c | 32 +-
> > bsd-user/meson.build | 1 +
> > bsd-user/qemu-bsd.h | 26 +
> > bsd-user/qemu.h | 48 +-
> > bsd-user/riscv/target.h | 2 +-
> > bsd-user/signal-common.h | 4 +
> > bsd-user/signal.c | 67 ++
> > bsd-user/syscall_defs.h | 458 +++++++++--
> > bsd-user/x86_64/target.h | 2 +-
> > 36 files changed, 8150 insertions(+), 196 deletions(-)
> > ---
> > base-commit: edcc429e9e41a8e0e415dcdab6aa52ad17ef8889
> > change-id: 20260504-misc-2026q2-a5af31ac032a
> >
> > Best regards,
>
> After reviewing (what I can) two thirds of the series, I don't feel very
> at ease with validating the rest. Not that there is problem in itself,
> but it's a massive add, and I'm not sure how we can ensure it's
> implemented correctly without any test exercising it.
> It's not particular to bsd-user, linux-user does not have test for all
> syscalls implemented neither.
>
>
> Yes. I'd like a low-level test like that. The upstream bsd-user has been too
> incomplete to run real programs prior to this series. And there's still some
> issues that remain after these system calls (which is next up on my list: I
> think elfload needs some patches for PIE).
>
>
> However, because we are adding most of it at once, it's a bit different.
> Dropping 8150 new lines at once without any test looks a bit complicated
> to me.
>
> A few questions about the series in itself:
> - Where does most of the implementation comes from? Was it downstream on
> FreeBSD side, or written from scratch for this series?
>
>
> We've had almost all of this code, modulo some big fixes I've added, in
> blitz branch
> for years. Stacey Son implemented most of this a decade ago now and had some
> issue upstream at the time, IIRC, but I have only sketchy details of the
> project then.
>
> We use it inside our poudriere jails to build thousands of ports to test
> it. Though there
> are issues with many ports, enough work to keep going.
>
> I also run a number of random commands in the jail that's nothing but
> cross-arch binaries
> as well.
>
That's good to hear this has been used downstream for a long time. That
was my feeling after our conversation, but just wanted to double check this.
> Sometimes I run the Kyua tests, but it's a bit hard to read the output
> because many are broken,
> many can't work in qemu or in a jail, and there's some that are broken
> for a given arch. We don't
> have good baselines to compare against and I've not spent the time to
> get this into good shape.
>
> Not the most formal set of things we do, but it works.
>
>
> - How did you validate it works, beyond it compiles?
>
>
> Generally on the blitz side, all of the above. On the qemu-project side,
> I usually just do the bit
> about messing around in the jail to test the basics.
>
> I'm happy cleaving this massive set into some smaller sets if that would
> help, but that still wouldn't
> answer the 'how the heck do we test it'.
>
It would require a massive effort for no benefit, since it doesn't
increase testability of the whole.
> I've also had claude do code reviews of this branch. It's found maybe
> 100 bugs that I've fixed
> (maybe 25 more between v2 and v3). The vast majority of the bugs are
> lock/unlock issues. Either
> leaks on error paths, failure to check for TARGET_EFAULT in places or
> passing args that don't
> matter unless you compile CONFIG_DEBUG_REMAP to indicate copying, etc.
> Some real bugs
> have been found, including one that might lead to a multi-threaded hang.
>
This is better than nothing, and it's a common usage I have with Claude
too. I just hope the industry does not settle on "let the AI review"
instead of test/cover approach.
> I have been putting off dealing with the PIE issue which blocks building
> most ports at the moment,
> but there's two pull requests to address it in blitz. When I tried them
> I hit some problems, but
> maybe I've fixed those in other ways. If I fixed that, I'd know that the
> base code works, but
> maybe all the pieces aren't in qemu-project yet.
>
> Based on all that, what's your advice?
>
In an ideal world, I would advise to upstream the minimum to be able to
run hello-world. Once done, add tests for all existing syscalls.
Then add new syscalls with their tests. I tend to think that good code
is not the one being the most elegant/performant, but the one that you
can modify with trust in what you did, which means covered by a test.
In the real world, seems like we don't really have another option than
taking it, except if you're ready to spend 6 months to add tests for
this series. I'll let other reviewers finish the work for this series.
I'm not against the pragmatic approach of taking it, and let it be
tested on the field. It would be unfair considering how the rest of QEMU
is not always tested.
Sorry, I don't have any magic advice that allows to workaround the cost
- time - quality triangle.
> Warner
>
>
>
> Regards,
> Pierrick
>
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-23 1:19 ` Pierrick Bouvier
@ 2026-05-23 18:56 ` Helge Deller
2026-05-25 2:17 ` Warner Losh
2026-06-03 9:18 ` Daniel P. Berrangé
1 sibling, 1 reply; 73+ messages in thread
From: Helge Deller @ 2026-05-23 18:56 UTC (permalink / raw)
To: Pierrick Bouvier, Warner Losh
Cc: qemu-devel, Kyle Evans, Stacey Son, Jessica Clarke,
Mikaël Urankar, Michal Meloun, Sean Bruno, Karim Taha,
Alexander Kabaev
On 5/23/26 03:19, Pierrick Bouvier wrote:
> On 5/22/2026 5:40 PM, Warner Losh wrote:
>>
>>
>> On Fri, May 22, 2026 at 6:04 PM Pierrick Bouvier
>> <pierrick.bouvier@oss.qualcomm.com
>> <mailto:pierrick.bouvier@oss.qualcomm.com>> wrote:
>>
>> On 5/18/2026 2:27 PM, Warner Losh wrote:
>> > Upstream the file, thread, socket and remaining signal system
>> calls (too
>> > numerous to list here).
>> >
>> > This series is an ambitous use of claude to help me upstream all the
>> > remaining system calls. I've batched them together in what I think are
>> > reasonable chunks, and had claude do the grunt work of copying the
>> code
>> > over and attributing the commits from our complex branching
>> history. The
>> > chopping up was a bit arbitrary, but I think it's good. The commit
>> > messages may be a little weak, but may also be OK. I've also double
>> > checked the style and made fixes upstream for them as well. Claude
>> also
>> > reviewed all these changes and found a few bugs that I've fixed. I've
>> > personally read through them all and haven't found anything glaring. I
>> > fixed all the bugs that were found, in most cases differently than
>> > claude's suggestions.
>> >
>> > I've added 'Assisted-by: Claude...' to all these commits to reflect my
>> > leaning on Claude so hard. This use falls within the 'non-
>> creative' use
>> > that the Qemu project has said is OK. If that's not the right thing to
>> > do, I can remove them.
>> >
>> > This leaves sysctl translation, the powerpc architecture support,
>> > coredumps and a transition to 'truss' based system call tracing. With
>> > these changes applied we'er down from a high of about 30k lines of
>> diffs
>> > to only 5k (not counting genereted, but checked in files in
>> blitz). The
>> > changes are 8k, so maybe a bit ambitious from that perspective as
>> well.
>> >
>> > There's a few lines over 80 that I've not cleaned up. Let me know if
>> > that's a problem. The other warnings are about adding files, and
>> there's
>> > no new MAINTAINERS entry needed. And the 'arch dependent defines
>> should
>> > be avoided' are needed to cope with different FreeBSD build systems
>> > having different system calls.
>> >
>> > Note, this is called out below too, but in v2 I've folded back all the
>> > fixes based on some out-of-band feedback I recieved to do this the
>> > normal way and the qemu project isn't interested in the fixes to
>> fixes.
>> >
>> > This is a big experiment, in many ways, for me, so I'm interested in
>> > whatever feedback you may have to make things better in the future.
>> >
>> > Signed-off-by: Warner Losh <imp@bsdimp.com <mailto:imp@bsdimp.com>>
>> > ---
>> > Changes in v2:
>> > - don't write offsets on errors for copy_file_range
>> > - pipe2: Check the raw system call value directly
>> > - setsockopt: Check target_to_host_ip_mreq return value
>> > - <several places>: Check return value of host_to_target_sockaddr
>> > - do_bsd_setsockopt: Read in the socket option
>> > - ntp_gettime: write back time on non-errors, not just 0
>> > - do_freebsd_sigtimedwait check t2h_freebsd_timespec return
>> > - do_freebsd_swapcontext writing incorrectly, readonly data (0 to
>> user_unlock)
>> > - do_thr_set_name needs get_errno()
>> > - Have ntp_adjtime write back the structure
>> > - kevent_freebsd11 no need to explicitly mask, datatypes do this
>> for us
>> > - do_freebsd_setcontext just reads the mcontext, 0 in unlock_user
>> > - target_to_host_sigevent is read-only, so pass 0 in unlock_user
>> > - target_to_host_ip_mreq needs tswap32 for int32_t
>> > - use get_errno on kenv return values to report errors better
>> > - kevent: only copy the number of events returns, not whole buffer
>> > - Copy tolerance field of timex
>> > - freebsd_umtx_sem_wake use VERIFY_READ instead of write to read
>> struct
>> > - Avoid double g2h_untagged mistakes in !_UMTX_OPTIMIZED
>> > - Fixed additional missing unlock in freebsd_rw_rdlock and
>> freebsd_rw_wrlock in !_UMTX_OPTIMIZED
>> > - Squashed all the bug commit back to the appropriate earleir patch
>> > - Link to v1: https://lore.kernel.org/qemu-devel/20260515-
>> misc-2026q2-v1-0-5438ca41b27a@bsdimp.com <https://lore.kernel.org/
>> qemu-devel/20260515-misc-2026q2-v1-0-5438ca41b27a@bsdimp.com>
>> >
>> > ---
>> > Warner Losh (37):
>> > bsd-user: catchup to locking / mapping routines in bsd-misc.
>> > bsd-user: Rename cpu_env to env throughout bsd-user
>> > bsd-user: Add bsd-signal.h with sigaction through sigreturn
>> > bsd-user: Add signal shims sigwait through kill and os-signal.h
>> > bsd-user: Add signal system call dispatch
>> > bsd-user: Add poll, lseek, pipe, and swap system call shims
>> > bsd-user: Add os-file.h with pipe2, chflagsat, close_range,
>> and more
>> > bsd-user: Add file operation system call dispatch
>> > bsd-user: Add bsd-socket.h with bind through getsockname
>> > bsd-user: Add socket shims socketpair through shutdown
>> > bsd-user: Add os-socket.h with sendrecvmsg and message
>> structures
>> > bsd-user: Add do_bsd_setsockopt and socket option definitions
>> > bsd-user: Add do_bsd_getsockopt
>> > bsd-user: Add FreeBSD socket helpers and sockaddr conversion
>> > bsd-user: Add os-socket.c with cmsg conversion functions
>> > bsd-user: Add socket system call dispatch
>> > bsd-user: Add os-time.h with clock and time-of-day functions
>> > bsd-user: Add utimes, futimes, and ktimer functions to os-time.h
>> > bsd-user: Add select, pselect, and ppoll to os-time.h
>> > bsd-user: Add kqueue and kevent functions to os-time.h
>> > bsd-user: Add sigtimedwait, itimer, and futimens to os-time.h
>> > bsd-user: Add os-time.c and time-related type definitions
>> > bsd-user: Add time system call dispatch
>> > bsd-user: Add thread, umtx, and ACL type definitions
>> > bsd-user: Add cpu_copy and make init_task_state non-static
>> > bsd-user: Add os-thread.h with thr and context functions
>> > bsd-user: Add do_freebsd__umtx_op to os-thread.h
>> > bsd-user: Add os-thread.c with umtx, mutex, and thread creation
>> > bsd-user: Add thread system call dispatch
>> > bsd-user: Add os-extattr.h with file and fd extattr functions
>> > bsd-user: Add link and list extattr functions to os-extattr.h
>> > bsd-user: Add ACL system call shims to os-extattr.h
>> > bsd-user: Add os-extattr.c with ACL conversion functions
>> > bsd-user: Add extended attribute and ACL system call dispatch
>> > bsd-user: Add scheduler and cpuset functions to os-misc.h
>> > bsd-user: Add kmod, posix, and misc functions to os-misc.h
>> > bsd-user: Add scheduler, cpuset, kmod, and misc syscall dispatch
>> >
>> > bsd-user/aarch64/target.h | 2 +-
>> > bsd-user/arm/target.h | 2 +-
>> > bsd-user/bsd-file.h | 164 +++-
>> > bsd-user/bsd-mem.h | 4 +-
>> > bsd-user/bsd-proc.h | 24 +-
>> > bsd-user/bsd-signal.h | 231 ++++++
>> > bsd-user/bsd-socket.c | 98 +++
>> > bsd-user/bsd-socket.h | 283 +++++++
>> > bsd-user/freebsd/meson.build | 8 +-
>> > bsd-user/freebsd/os-extattr.c | 116 +++
>> > bsd-user/freebsd/os-extattr.h | 662 ++++++++++++++++
>> > bsd-user/freebsd/os-file.h | 129 ++++
>> > bsd-user/freebsd/os-misc.h | 470 +++++++++++-
>> > bsd-user/freebsd/os-proc.c | 4 +-
>> > bsd-user/freebsd/os-proc.h | 26 +-
>> > bsd-user/freebsd/os-signal.h | 20 +
>> > bsd-user/freebsd/os-socket.c | 234 ++++++
>> > bsd-user/freebsd/os-socket.h | 770 +++++++++++++++++++
>> > bsd-user/freebsd/os-stat.h | 8 +-
>> > bsd-user/freebsd/os-sys.c | 4 +-
>> > bsd-user/freebsd/os-syscall.c | 790 +++++++++++++++++--
>> > bsd-user/freebsd/os-thread.c | 1682 ++++++++++++++++++++++++++++
>> +++++++++++++
>> > bsd-user/freebsd/os-thread.h | 596 +++++++++++++++
>> > bsd-user/freebsd/os-time.c | 346 +++++++++
>> > bsd-user/freebsd/os-time.h | 983 ++++++++++++++++++++++++
>> > bsd-user/freebsd/qemu-os.h | 48 ++
>> > bsd-user/i386/target.h | 2 +-
>> > bsd-user/main.c | 32 +-
>> > bsd-user/meson.build | 1 +
>> > bsd-user/qemu-bsd.h | 26 +
>> > bsd-user/qemu.h | 48 +-
>> > bsd-user/riscv/target.h | 2 +-
>> > bsd-user/signal-common.h | 4 +
>> > bsd-user/signal.c | 67 ++
>> > bsd-user/syscall_defs.h | 458 +++++++++--
>> > bsd-user/x86_64/target.h | 2 +-
>> > 36 files changed, 8150 insertions(+), 196 deletions(-)
>> > ---
>> > base-commit: edcc429e9e41a8e0e415dcdab6aa52ad17ef8889
>> > change-id: 20260504-misc-2026q2-a5af31ac032a
>> >
>> > Best regards,
>>
>> After reviewing (what I can) two thirds of the series, I don't feel very
>> at ease with validating the rest. Not that there is problem in itself,
>> but it's a massive add, and I'm not sure how we can ensure it's
>> implemented correctly without any test exercising it.
>> It's not particular to bsd-user, linux-user does not have test for all
>> syscalls implemented neither.
>>
>>
>> Yes. I'd like a low-level test like that. The upstream bsd-user has been too
>> incomplete to run real programs prior to this series. And there's still some
>> issues that remain after these system calls (which is next up on my list: I
>> think elfload needs some patches for PIE).
>>
>>
>> However, because we are adding most of it at once, it's a bit different.
>> Dropping 8150 new lines at once without any test looks a bit complicated
>> to me.
>>
>> A few questions about the series in itself:
>> - Where does most of the implementation comes from? Was it downstream on
>> FreeBSD side, or written from scratch for this series?
>>
>>
>> We've had almost all of this code, modulo some big fixes I've added, in
>> blitz branch
>> for years. Stacey Son implemented most of this a decade ago now and had some
>> issue upstream at the time, IIRC, but I have only sketchy details of the
>> project then.
>>
>> We use it inside our poudriere jails to build thousands of ports to test
>> it. Though there are issues with many ports, enough work to keep going.
So, this seems to be similar to what I do, and it's actually a good test case for linux-user.
I'm running some debian buildd servers which use qemu-hppa to build debian packages
for the hppa architecture. With that setup I found quite some bugs in qemu-user
and now it's pretty stable.
>> I also run a number of random commands in the jail that's nothing but
>> cross-arch binaries
>> as well.
>>
>
> That's good to hear this has been used downstream for a long time. That
> was my feeling after our conversation, but just wanted to double check this.
>
>> Sometimes I run the Kyua tests, but it's a bit hard to read the output
>> because many are broken,
>> many can't work in qemu or in a jail, and there's some that are broken
>> for a given arch. We don't
>> have good baselines to compare against and I've not spent the time to
>> get this into good shape.
>>
>> Not the most formal set of things we do, but it works.
>>
>>
>> - How did you validate it works, beyond it compiles?
>>
>>
>> Generally on the blitz side, all of the above. On the qemu-project side,
>> I usually just do the bit
>> about messing around in the jail to test the basics.
>>
>> I'm happy cleaving this massive set into some smaller sets if that would
>> help, but that still wouldn't
>> answer the 'how the heck do we test it'.
>>
>
> It would require a massive effort for no benefit, since it doesn't
> increase testability of the whole.
>
>> I've also had claude do code reviews of this branch. It's found maybe
>> 100 bugs that I've fixed
>> (maybe 25 more between v2 and v3). The vast majority of the bugs are
>> lock/unlock issues. Either
>> leaks on error paths, failure to check for TARGET_EFAULT in places or
>> passing args that don't
>> matter unless you compile CONFIG_DEBUG_REMAP to indicate copying, etc.
>> Some real bugs
>> have been found, including one that might lead to a multi-threaded hang.
>>
>
> This is better than nothing, and it's a common usage I have with Claude
> too. I just hope the industry does not settle on "let the AI review"
> instead of test/cover approach.
>
>> I have been putting off dealing with the PIE issue which blocks building
>> most ports at the moment,
>> but there's two pull requests to address it in blitz. When I tried them
>> I hit some problems, but
>> maybe I've fixed those in other ways. If I fixed that, I'd know that the
>> base code works, but
>> maybe all the pieces aren't in qemu-project yet.
>>
>> Based on all that, what's your advice?
>>
>
> In an ideal world, I would advise to upstream the minimum to be able to
> run hello-world. Once done, add tests for all existing syscalls.
> Then add new syscalls with their tests. I tend to think that good code
> is not the one being the most elegant/performant, but the one that you
> can modify with trust in what you did, which means covered by a test.
>
> In the real world, seems like we don't really have another option than
> taking it, except if you're ready to spend 6 months to add tests for
> this series. I'll let other reviewers finish the work for this series.
>
> I'm not against the pragmatic approach of taking it, and let it be
> tested on the field.
+1 from me.
> It would be unfair considering how the rest of QEMU is not always tested.
True.
Helge
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-23 18:56 ` Helge Deller
@ 2026-05-25 2:17 ` Warner Losh
0 siblings, 0 replies; 73+ messages in thread
From: Warner Losh @ 2026-05-25 2:17 UTC (permalink / raw)
To: Helge Deller
Cc: Pierrick Bouvier, qemu-devel, Kyle Evans, Stacey Son,
Jessica Clarke, Mikaël Urankar, Michal Meloun, Sean Bruno,
Karim Taha, Alexander Kabaev
[-- Attachment #1: Type: text/plain, Size: 16665 bytes --]
On Sat, May 23, 2026 at 12:57 PM Helge Deller <deller@gmx.de> wrote:
> On 5/23/26 03:19, Pierrick Bouvier wrote:
> > On 5/22/2026 5:40 PM, Warner Losh wrote:
> >>
> >>
> >> On Fri, May 22, 2026 at 6:04 PM Pierrick Bouvier
> >> <pierrick.bouvier@oss.qualcomm.com
> >> <mailto:pierrick.bouvier@oss.qualcomm.com>> wrote:
> >>
> >> On 5/18/2026 2:27 PM, Warner Losh wrote:
> >> > Upstream the file, thread, socket and remaining signal system
> >> calls (too
> >> > numerous to list here).
> >> >
> >> > This series is an ambitous use of claude to help me upstream all
> the
> >> > remaining system calls. I've batched them together in what I
> think are
> >> > reasonable chunks, and had claude do the grunt work of copying
> the
> >> code
> >> > over and attributing the commits from our complex branching
> >> history. The
> >> > chopping up was a bit arbitrary, but I think it's good. The
> commit
> >> > messages may be a little weak, but may also be OK. I've also
> double
> >> > checked the style and made fixes upstream for them as well.
> Claude
> >> also
> >> > reviewed all these changes and found a few bugs that I've fixed.
> I've
> >> > personally read through them all and haven't found anything
> glaring. I
> >> > fixed all the bugs that were found, in most cases differently
> than
> >> > claude's suggestions.
> >> >
> >> > I've added 'Assisted-by: Claude...' to all these commits to
> reflect my
> >> > leaning on Claude so hard. This use falls within the 'non-
> >> creative' use
> >> > that the Qemu project has said is OK. If that's not the right
> thing to
> >> > do, I can remove them.
> >> >
> >> > This leaves sysctl translation, the powerpc architecture support,
> >> > coredumps and a transition to 'truss' based system call tracing.
> With
> >> > these changes applied we'er down from a high of about 30k lines
> of
> >> diffs
> >> > to only 5k (not counting genereted, but checked in files in
> >> blitz). The
> >> > changes are 8k, so maybe a bit ambitious from that perspective as
> >> well.
> >> >
> >> > There's a few lines over 80 that I've not cleaned up. Let me
> know if
> >> > that's a problem. The other warnings are about adding files, and
> >> there's
> >> > no new MAINTAINERS entry needed. And the 'arch dependent defines
> >> should
> >> > be avoided' are needed to cope with different FreeBSD build
> systems
> >> > having different system calls.
> >> >
> >> > Note, this is called out below too, but in v2 I've folded back
> all the
> >> > fixes based on some out-of-band feedback I recieved to do this
> the
> >> > normal way and the qemu project isn't interested in the fixes to
> >> fixes.
> >> >
> >> > This is a big experiment, in many ways, for me, so I'm
> interested in
> >> > whatever feedback you may have to make things better in the
> future.
> >> >
> >> > Signed-off-by: Warner Losh <imp@bsdimp.com <mailto:
> imp@bsdimp.com>>
> >> > ---
> >> > Changes in v2:
> >> > - don't write offsets on errors for copy_file_range
> >> > - pipe2: Check the raw system call value directly
> >> > - setsockopt: Check target_to_host_ip_mreq return value
> >> > - <several places>: Check return value of host_to_target_sockaddr
> >> > - do_bsd_setsockopt: Read in the socket option
> >> > - ntp_gettime: write back time on non-errors, not just 0
> >> > - do_freebsd_sigtimedwait check t2h_freebsd_timespec return
> >> > - do_freebsd_swapcontext writing incorrectly, readonly data (0 to
> >> user_unlock)
> >> > - do_thr_set_name needs get_errno()
> >> > - Have ntp_adjtime write back the structure
> >> > - kevent_freebsd11 no need to explicitly mask, datatypes do this
> >> for us
> >> > - do_freebsd_setcontext just reads the mcontext, 0 in unlock_user
> >> > - target_to_host_sigevent is read-only, so pass 0 in unlock_user
> >> > - target_to_host_ip_mreq needs tswap32 for int32_t
> >> > - use get_errno on kenv return values to report errors better
> >> > - kevent: only copy the number of events returns, not whole
> buffer
> >> > - Copy tolerance field of timex
> >> > - freebsd_umtx_sem_wake use VERIFY_READ instead of write to read
> >> struct
> >> > - Avoid double g2h_untagged mistakes in !_UMTX_OPTIMIZED
> >> > - Fixed additional missing unlock in freebsd_rw_rdlock and
> >> freebsd_rw_wrlock in !_UMTX_OPTIMIZED
> >> > - Squashed all the bug commit back to the appropriate earleir
> patch
> >> > - Link to v1: https://lore.kernel.org/qemu-devel/20260515-
> >> misc-2026q2-v1-0-5438ca41b27a@bsdimp.com <https://lore.kernel.org/
> >> qemu-devel/20260515-misc-2026q2-v1-0-5438ca41b27a@bsdimp.com>
> >> >
> >> > ---
> >> > Warner Losh (37):
> >> > bsd-user: catchup to locking / mapping routines in
> bsd-misc.
> >> > bsd-user: Rename cpu_env to env throughout bsd-user
> >> > bsd-user: Add bsd-signal.h with sigaction through sigreturn
> >> > bsd-user: Add signal shims sigwait through kill and
> os-signal.h
> >> > bsd-user: Add signal system call dispatch
> >> > bsd-user: Add poll, lseek, pipe, and swap system call shims
> >> > bsd-user: Add os-file.h with pipe2, chflagsat, close_range,
> >> and more
> >> > bsd-user: Add file operation system call dispatch
> >> > bsd-user: Add bsd-socket.h with bind through getsockname
> >> > bsd-user: Add socket shims socketpair through shutdown
> >> > bsd-user: Add os-socket.h with sendrecvmsg and message
> >> structures
> >> > bsd-user: Add do_bsd_setsockopt and socket option
> definitions
> >> > bsd-user: Add do_bsd_getsockopt
> >> > bsd-user: Add FreeBSD socket helpers and sockaddr
> conversion
> >> > bsd-user: Add os-socket.c with cmsg conversion functions
> >> > bsd-user: Add socket system call dispatch
> >> > bsd-user: Add os-time.h with clock and time-of-day
> functions
> >> > bsd-user: Add utimes, futimes, and ktimer functions to
> os-time.h
> >> > bsd-user: Add select, pselect, and ppoll to os-time.h
> >> > bsd-user: Add kqueue and kevent functions to os-time.h
> >> > bsd-user: Add sigtimedwait, itimer, and futimens to
> os-time.h
> >> > bsd-user: Add os-time.c and time-related type definitions
> >> > bsd-user: Add time system call dispatch
> >> > bsd-user: Add thread, umtx, and ACL type definitions
> >> > bsd-user: Add cpu_copy and make init_task_state non-static
> >> > bsd-user: Add os-thread.h with thr and context functions
> >> > bsd-user: Add do_freebsd__umtx_op to os-thread.h
> >> > bsd-user: Add os-thread.c with umtx, mutex, and thread
> creation
> >> > bsd-user: Add thread system call dispatch
> >> > bsd-user: Add os-extattr.h with file and fd extattr
> functions
> >> > bsd-user: Add link and list extattr functions to
> os-extattr.h
> >> > bsd-user: Add ACL system call shims to os-extattr.h
> >> > bsd-user: Add os-extattr.c with ACL conversion functions
> >> > bsd-user: Add extended attribute and ACL system call
> dispatch
> >> > bsd-user: Add scheduler and cpuset functions to os-misc.h
> >> > bsd-user: Add kmod, posix, and misc functions to os-misc.h
> >> > bsd-user: Add scheduler, cpuset, kmod, and misc syscall
> dispatch
> >> >
> >> > bsd-user/aarch64/target.h | 2 +-
> >> > bsd-user/arm/target.h | 2 +-
> >> > bsd-user/bsd-file.h | 164 +++-
> >> > bsd-user/bsd-mem.h | 4 +-
> >> > bsd-user/bsd-proc.h | 24 +-
> >> > bsd-user/bsd-signal.h | 231 ++++++
> >> > bsd-user/bsd-socket.c | 98 +++
> >> > bsd-user/bsd-socket.h | 283 +++++++
> >> > bsd-user/freebsd/meson.build | 8 +-
> >> > bsd-user/freebsd/os-extattr.c | 116 +++
> >> > bsd-user/freebsd/os-extattr.h | 662 ++++++++++++++++
> >> > bsd-user/freebsd/os-file.h | 129 ++++
> >> > bsd-user/freebsd/os-misc.h | 470 +++++++++++-
> >> > bsd-user/freebsd/os-proc.c | 4 +-
> >> > bsd-user/freebsd/os-proc.h | 26 +-
> >> > bsd-user/freebsd/os-signal.h | 20 +
> >> > bsd-user/freebsd/os-socket.c | 234 ++++++
> >> > bsd-user/freebsd/os-socket.h | 770 +++++++++++++++++++
> >> > bsd-user/freebsd/os-stat.h | 8 +-
> >> > bsd-user/freebsd/os-sys.c | 4 +-
> >> > bsd-user/freebsd/os-syscall.c | 790 +++++++++++++++++--
> >> > bsd-user/freebsd/os-thread.c | 1682
> ++++++++++++++++++++++++++++
> >> +++++++++++++
> >> > bsd-user/freebsd/os-thread.h | 596 +++++++++++++++
> >> > bsd-user/freebsd/os-time.c | 346 +++++++++
> >> > bsd-user/freebsd/os-time.h | 983 ++++++++++++++++++++++++
> >> > bsd-user/freebsd/qemu-os.h | 48 ++
> >> > bsd-user/i386/target.h | 2 +-
> >> > bsd-user/main.c | 32 +-
> >> > bsd-user/meson.build | 1 +
> >> > bsd-user/qemu-bsd.h | 26 +
> >> > bsd-user/qemu.h | 48 +-
> >> > bsd-user/riscv/target.h | 2 +-
> >> > bsd-user/signal-common.h | 4 +
> >> > bsd-user/signal.c | 67 ++
> >> > bsd-user/syscall_defs.h | 458 +++++++++--
> >> > bsd-user/x86_64/target.h | 2 +-
> >> > 36 files changed, 8150 insertions(+), 196 deletions(-)
> >> > ---
> >> > base-commit: edcc429e9e41a8e0e415dcdab6aa52ad17ef8889
> >> > change-id: 20260504-misc-2026q2-a5af31ac032a
> >> >
> >> > Best regards,
> >>
> >> After reviewing (what I can) two thirds of the series, I don't
> feel very
> >> at ease with validating the rest. Not that there is problem in
> itself,
> >> but it's a massive add, and I'm not sure how we can ensure it's
> >> implemented correctly without any test exercising it.
> >> It's not particular to bsd-user, linux-user does not have test for
> all
> >> syscalls implemented neither.
> >>
> >>
> >> Yes. I'd like a low-level test like that. The upstream bsd-user has
> been too
> >> incomplete to run real programs prior to this series. And there's still
> some
> >> issues that remain after these system calls (which is next up on my
> list: I
> >> think elfload needs some patches for PIE).
> >>
> >>
> >> However, because we are adding most of it at once, it's a bit
> different.
> >> Dropping 8150 new lines at once without any test looks a bit
> complicated
> >> to me.
> >>
> >> A few questions about the series in itself:
> >> - Where does most of the implementation comes from? Was it
> downstream on
> >> FreeBSD side, or written from scratch for this series?
> >>
> >>
> >> We've had almost all of this code, modulo some big fixes I've added, in
> >> blitz branch
> >> for years. Stacey Son implemented most of this a decade ago now and had
> some
> >> issue upstream at the time, IIRC, but I have only sketchy details of the
> >> project then.
> >>
> >> We use it inside our poudriere jails to build thousands of ports to test
> >> it. Though there are issues with many ports, enough work to keep going.
>
> So, this seems to be similar to what I do, and it's actually a good test
> case for linux-user.
> I'm running some debian buildd servers which use qemu-hppa to build debian
> packages
> for the hppa architecture. With that setup I found quite some bugs in
> qemu-user
> and now it's pretty stable.
>
> >> I also run a number of random commands in the jail that's nothing but
> >> cross-arch binaries
> >> as well.
> >>
> >
> > That's good to hear this has been used downstream for a long time. That
> > was my feeling after our conversation, but just wanted to double check
> this.
> >
> >> Sometimes I run the Kyua tests, but it's a bit hard to read the output
> >> because many are broken,
> >> many can't work in qemu or in a jail, and there's some that are broken
> >> for a given arch. We don't
> >> have good baselines to compare against and I've not spent the time to
> >> get this into good shape.
> >>
> >> Not the most formal set of things we do, but it works.
> >>
> >>
> >> - How did you validate it works, beyond it compiles?
> >>
> >>
> >> Generally on the blitz side, all of the above. On the qemu-project side,
> >> I usually just do the bit
> >> about messing around in the jail to test the basics.
> >>
> >> I'm happy cleaving this massive set into some smaller sets if that would
> >> help, but that still wouldn't
> >> answer the 'how the heck do we test it'.
> >>
> >
> > It would require a massive effort for no benefit, since it doesn't
> > increase testability of the whole.
> >
> >> I've also had claude do code reviews of this branch. It's found maybe
> >> 100 bugs that I've fixed
> >> (maybe 25 more between v2 and v3). The vast majority of the bugs are
> >> lock/unlock issues. Either
> >> leaks on error paths, failure to check for TARGET_EFAULT in places or
> >> passing args that don't
> >> matter unless you compile CONFIG_DEBUG_REMAP to indicate copying, etc.
> >> Some real bugs
> >> have been found, including one that might lead to a multi-threaded hang.
> >>
> >
> > This is better than nothing, and it's a common usage I have with Claude
> > too. I just hope the industry does not settle on "let the AI review"
> > instead of test/cover approach.
> >
> >> I have been putting off dealing with the PIE issue which blocks building
> >> most ports at the moment,
> >> but there's two pull requests to address it in blitz. When I tried them
> >> I hit some problems, but
> >> maybe I've fixed those in other ways. If I fixed that, I'd know that the
> >> base code works, but
> >> maybe all the pieces aren't in qemu-project yet.
> >>
> >> Based on all that, what's your advice?
> >>
> >
> > In an ideal world, I would advise to upstream the minimum to be able to
> > run hello-world. Once done, add tests for all existing syscalls.
> > Then add new syscalls with their tests. I tend to think that good code
> > is not the one being the most elegant/performant, but the one that you
> > can modify with trust in what you did, which means covered by a test.
>
Hello World was upstreamed about 3 years ago now. The rest has been insanely
slow to get going...
> > In the real world, seems like we don't really have another option than
> > taking it, except if you're ready to spend 6 months to add tests for
> > this series. I'll let other reviewers finish the work for this series.
> >
> > I'm not against the pragmatic approach of taking it, and let it be
> > tested on the field.
>
> +1 from me.
>
Yea, I'm trying to see how close to being able to build something with
Poudriere
(the FreeBSD building tool that has this tightly integrated) was can get.
Right
now I'm running into umtx hangs, which are a joy to debug.
> > It would be unfair considering how the rest of QEMU is not always tested.
>
> True.
>
Yea... I have unfair feelings from time to time, but I've just decided to
push back
on the suggestions that feel that way that don't really help.
I'm working towards having blitz being able to run poudriere again...
Somewhere
between 9.2 and now something broke things, but I think I got those things
fixed,
and then again around 10.1 or 10.2 there was another. It's one reason I
want it
in upstream: we can then have some minimal acceptance test (that's less
than a
full poudriere bulk build, since that's hours of CPU time) that will
hopefully keep
things from breaking... Even just 'install freebsd/$ARCH into a jail and
build hello
world' would catch a huge class of bugs. Some basic locking tests would
catch
the rest of the big-ticket items.
Warner
> Helge
>
[-- Attachment #2: Type: text/html, Size: 21910 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH v2 00/37] bsd-user: Upstream most of the remaining system calls
2026-05-23 1:19 ` Pierrick Bouvier
2026-05-23 18:56 ` Helge Deller
@ 2026-06-03 9:18 ` Daniel P. Berrangé
1 sibling, 0 replies; 73+ messages in thread
From: Daniel P. Berrangé @ 2026-06-03 9:18 UTC (permalink / raw)
To: Pierrick Bouvier
Cc: Warner Losh, qemu-devel, Kyle Evans, Stacey Son, Jessica Clarke,
Mikaël Urankar, Michal Meloun, Sean Bruno, Karim Taha,
Alexander Kabaev
On Fri, May 22, 2026 at 06:19:16PM -0700, Pierrick Bouvier wrote:
> On 5/22/2026 5:40 PM, Warner Losh wrote:
> >
> >
> > On Fri, May 22, 2026 at 6:04 PM Pierrick Bouvier
> > <pierrick.bouvier@oss.qualcomm.com
> > <mailto:pierrick.bouvier@oss.qualcomm.com>> wrote:
> >
> > On 5/18/2026 2:27 PM, Warner Losh wrote:
> > > Upstream the file, thread, socket and remaining signal system
> > calls (too
> > > numerous to list here).
> > >
> > > This series is an ambitous use of claude to help me upstream all the
> > > remaining system calls. I've batched them together in what I think are
> > > reasonable chunks, and had claude do the grunt work of copying the
> > code
> > > over and attributing the commits from our complex branching
> > history. The
> > > chopping up was a bit arbitrary, but I think it's good. The commit
> > > messages may be a little weak, but may also be OK. I've also double
> > > checked the style and made fixes upstream for them as well. Claude
> > also
> > > reviewed all these changes and found a few bugs that I've fixed. I've
> > > personally read through them all and haven't found anything glaring. I
> > > fixed all the bugs that were found, in most cases differently than
> > > claude's suggestions.
> > >
> > > I've added 'Assisted-by: Claude...' to all these commits to reflect my
> > > leaning on Claude so hard. This use falls within the 'non-
> > creative' use
> > > that the Qemu project has said is OK. If that's not the right thing to
> > > do, I can remove them.
> > >
> > > This leaves sysctl translation, the powerpc architecture support,
> > > coredumps and a transition to 'truss' based system call tracing. With
> > > these changes applied we'er down from a high of about 30k lines of
> > diffs
> > > to only 5k (not counting genereted, but checked in files in
> > blitz). The
> > > changes are 8k, so maybe a bit ambitious from that perspective as
> > well.
> > >
> > > There's a few lines over 80 that I've not cleaned up. Let me know if
> > > that's a problem. The other warnings are about adding files, and
> > there's
> > > no new MAINTAINERS entry needed. And the 'arch dependent defines
> > should
> > > be avoided' are needed to cope with different FreeBSD build systems
> > > having different system calls.
> > >
> > > Note, this is called out below too, but in v2 I've folded back all the
> > > fixes based on some out-of-band feedback I recieved to do this the
> > > normal way and the qemu project isn't interested in the fixes to
> > fixes.
> > >
> > > This is a big experiment, in many ways, for me, so I'm interested in
> > > whatever feedback you may have to make things better in the future.
> > >
> > > Signed-off-by: Warner Losh <imp@bsdimp.com <mailto:imp@bsdimp.com>>
snip
> > After reviewing (what I can) two thirds of the series, I don't feel very
> > at ease with validating the rest. Not that there is problem in itself,
> > but it's a massive add, and I'm not sure how we can ensure it's
> > implemented correctly without any test exercising it.
> > It's not particular to bsd-user, linux-user does not have test for all
> > syscalls implemented neither.
> >
> >
> > Yes. I'd like a low-level test like that. The upstream bsd-user has been too
> > incomplete to run real programs prior to this series. And there's still some
> > issues that remain after these system calls (which is next up on my list: I
> > think elfload needs some patches for PIE).
snip
> In the real world, seems like we don't really have another option than
> taking it, except if you're ready to spend 6 months to add tests for
> this series. I'll let other reviewers finish the work for this series.
>
> I'm not against the pragmatic approach of taking it, and let it be
> tested on the field. It would be unfair considering how the rest of QEMU
> is not always tested.
Yep, as a general rule we have no requirement for formal functional
tests for contributions to QEMU. The expectation is that the person
submitting the code has tested that it does what they claim. Anything
beyond that is a "nice to have". Especially for brand new features,
we don't need to worry about regressions either.
This bsd-user code has had a complex history out of tree, so it is an
unusual situation compared to most other contributions we receive.
The FreeBSD maintainers have had a long term burden carrying this out
of tree for too long but it was actually used, while we've shipped a
non-functional version upstream that no one used.
In terms of code review we can assume that this is all already peer
reviewed by multiple people in the FreeBSD community. So unless someone
in QEMU community happens to have specific FreeBSD knowledge, I don't
think we really need to review this from a functional correctness POV.
The multiple Signed-off-bys already indicate sufficient functional
review IMHO.
Rather my expectation for merge is that we're doing more of a "sanity
check" that the patches are something that looks reasonable to accept,
following the normal QEMU coding practices/styles/etc.
Some formal in tree tests would be nice, but I'd say we should focus
on getting the current out of tree backlog merged, and worry about
tests later once the burden of merging the backlog is eliminated.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 73+ messages in thread