Commit 45434469d1 for qemu.org
commit 45434469d1ef8b490e4db2aecea2e8565f7f85fa
Author: Warner Losh <imp@bsdimp.com>
Date: Tue May 5 00:40:45 2026 -0600
bsd-user: Add scheduler and cpuset functions to os-misc.h
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>
Assisted-by: Claude Opus 4.6 (1M context)
Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
index 7efc5b4c92..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));
}
+/* 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..0f87b21cc8 100644
--- a/bsd-user/freebsd/os-misc.h
+++ b/bsd-user/freebsd/os-misc.h
@@ -11,6 +11,363 @@
#include <sys/cpuset.h>
#include <sys/random.h>
#include <sched.h>
+#include <kenv.h>
+
+/* sched_setparam(2) */
+static inline abi_long do_freebsd_sched_setparam(pid_t pid,
+ abi_ulong target_sp_addr)
+{
+ abi_long ret;
+ struct sched_param host_sp;
+
+ 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;
+}
+
+/* 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);
+ }
+ 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;
+
+ ret = get_user_s32(host_sp.sched_priority, target_sp_addr);
+ if (!is_error(ret)) {
+ ret = get_errno(sched_setscheduler(pid, policy, &host_sp));
+ }
+ return ret;
+}
+
+/* sched_getscheduler(2) */
+static inline abi_long do_freebsd_sched_getscheduler(pid_t pid)
+{
+
+ return get_errno(sched_getscheduler(pid));
+}
+
+/* sched_rr_get_interval(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;
+}
+
+/* cpuset(2) */
+static inline abi_long do_freebsd_cpuset(abi_ulong target_cpuid)
+{
+ abi_long ret;
+ cpusetid_t setid;
+
+ ret = get_errno(cpuset(&setid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ return put_user_s32(setid, target_cpuid);
+}
+
+#define target_to_host_cpuset_which(hp, t) do { \
+ (*hp) = t; \
+} while (0)
+
+#define target_to_host_cpuset_level(hp, t) do { \
+ (*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;
+ }
+#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_long 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_long 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;
+}
+
+/* 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.
+ */
+
+/* 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
@@ -19,7 +376,6 @@
int __sys_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)
@@ -56,9 +412,7 @@ static inline abi_long do_freebsd_shm_open2(abi_ulong pathptr, abi_ulong flags,
}
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)
@@ -81,6 +435,110 @@ static inline abi_long do_freebsd_shm_rename(abi_ulong fromptr, abi_ulong toptr,
return ret;
}
-#endif /* __FreeBSD_version >= 1300049 */
+
+#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 */