summaryrefslogtreecommitdiffstats
path: root/bsd-user/freebsd/os-syscall.c
diff options
context:
space:
mode:
authorRichard Henderson2022-06-12 05:51:18 +0200
committerRichard Henderson2022-06-12 05:51:18 +0200
commitb871cc83d69a4463ac641286eef7d773ad5b5aaa (patch)
tree364d4f8223a5c7d2929112cee870991438edaf76 /bsd-user/freebsd/os-syscall.c
parentMerge tag 'mips-20220611' of https://github.com/philmd/qemu into staging (diff)
parentbsd-user/freebsd/os-syscall.c: Implement exit (diff)
downloadqemu-b871cc83d69a4463ac641286eef7d773ad5b5aaa.tar.gz
qemu-b871cc83d69a4463ac641286eef7d773ad5b5aaa.tar.xz
qemu-b871cc83d69a4463ac641286eef7d773ad5b5aaa.zip
Merge tag 'bsd-user-preen-2022q2-pull-request' of ssh://github.com/qemu-bsd-user/qemu-bsd-user into staging
bsd-user upstreaming: read, write and exit This series of patches continues the effort to get system calls working upstream. This series was cleaved off a prior series to give me time to rework based on the feedback from the first time I posted these. read, write and exit are implemented, along with a few helper functions and tracing. # -----BEGIN PGP SIGNATURE----- # Comment: GPGTools - https://gpgtools.org # # iQIzBAABCgAdFiEEIDX4lLAKo898zeG3bBzRKH2wEQAFAmKknqsACgkQbBzRKH2w # EQDJFhAA3ARnphfxKUvKHzi6ARza8YRFNJs/X48l78xugW8Fx2+UFxn/oPzVYuM3 # TUIIRNIXQxYDWP5rNN4zNfYjn1F32qx4PrLzzHw4t/0pNvNDvbVKyw+OB1pNUVI9 # 6hNH7jTLczi4OwZFPC4bJdFFHI55t4PCHxPaMQQO76lEsFzz3+9OcNNj3/j5aTBH # kY3FsmOIKFEFuqDANjdn13lBlkNNWj5WMIsEQd+k1TPTfG3EWDlfHVJVezx9j5uH # KMUjRRqBANLVJuqhog7sVAdkR6o/aKuq16nPVAgkLeJrsjwljoO1bGL3DTFWzhAo # v3+S3a5gCReLXA/Z+dGCuJysmimsm+XdJWHyNIwDCV/UUg9tXc9/BI/eBSK7MLbz # hvURY3cRSSVY+xiu5ts1OCp1bbqMaYbpIrv1mLZMA/32Y1KNv0LwSI0ClGag0MJ1 # rSVC3i/TGYn8Dk7TlfKkrKn751ZCCDsxZGROEcPlTV84Mc7080lpgYZKlgLCPJNT # WWaVSBs9YhHhmoG3R3GurfsHnnRr8Y8iZkmndoCJdeSlmZh/1oD31PWKhnydsPa9 # jQCENkAM0dOM/r+hV+paFxOPkgklu7KLtjqGnVRxgB4j37jA9KDyw+UXLmjipP0K # IwPlsNIfk1LujID3o5nJVgf/K+TJpXS7JDcUsuB9+FvlkeEMWm8= # =nOc2 # -----END PGP SIGNATURE----- # gpg: Signature made Sat 11 Jun 2022 06:54:51 AM PDT # gpg: using RSA key 2035F894B00AA3CF7CCDE1B76C1CD1287DB01100 # gpg: Good signature from "Warner Losh <wlosh@netflix.com>" [unknown] # gpg: aka "Warner Losh <imp@bsdimp.com>" [unknown] # gpg: aka "Warner Losh <imp@freebsd.org>" [unknown] # gpg: aka "Warner Losh <imp@village.org>" [unknown] # gpg: aka "Warner Losh <wlosh@bsdimp.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 2035 F894 B00A A3CF 7CCD E1B7 6C1C D128 7DB0 1100 * tag 'bsd-user-preen-2022q2-pull-request' of ssh://github.com/qemu-bsd-user/qemu-bsd-user: bsd-user/freebsd/os-syscall.c: Implement exit bsd-user/bsd-file.h: Meat of the write system calls bsd-user/bsd-file.h: Add implementations for read, pread, readv and preadv bsd-user/freebsd/os-syscall.c: Tracing and error boilerplate bsd-user/freebsd/os-syscall.c: unlock_iovec bsd-user/freebsd/os-syscall.c: lock_iovec Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'bsd-user/freebsd/os-syscall.c')
-rw-r--r--bsd-user/freebsd/os-syscall.c217
1 files changed, 213 insertions, 4 deletions
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index d272478e7b..71aa0d38e0 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -41,6 +41,22 @@
#include "user/syscall-trace.h"
#include "bsd-file.h"
+#include "bsd-proc.h"
+
+/* I/O */
+safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes);
+safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t,
+ offset);
+safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt);
+safe_syscall4(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt,
+ off_t, offset);
+
+safe_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes);
+safe_syscall4(ssize_t, pwrite, int, fd, void *, buf, size_t, nbytes, off_t,
+ offset);
+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);
void target_set_brk(abi_ulong new_brk)
{
@@ -74,16 +90,209 @@ bool is_error(abi_long ret)
}
/*
- * do_syscall() should always have a single exit point at the end so that
- * actions, such as logging of syscall results, can be performed. All errnos
- * that do_syscall() returns must be -TARGET_<errcode>.
+ * Unlocks a iovec. Unlike unlock_iovec, it assumes the tvec array itself is
+ * already locked from target_addr. It will be unlocked as well as all the iovec
+ * elements.
+ */
+static void helper_unlock_iovec(struct target_iovec *target_vec,
+ abi_ulong target_addr, struct iovec *vec,
+ int count, int copy)
+{
+ for (int i = 0; i < count; i++) {
+ abi_ulong base = tswapal(target_vec[i].iov_base);
+
+ if (vec[i].iov_base) {
+ unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
+ }
+ }
+ unlock_user(target_vec, target_addr, 0);
+}
+
+struct iovec *lock_iovec(int type, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ struct iovec *vec;
+ abi_ulong total_len, max_len;
+ int i;
+ int err = 0;
+
+ if (count == 0) {
+ errno = 0;
+ return NULL;
+ }
+ if (count < 0 || count > IOV_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ vec = g_try_new0(struct iovec, count);
+ if (vec == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ target_vec = lock_user(VERIFY_READ, target_addr,
+ count * sizeof(struct target_iovec), 1);
+ if (target_vec == NULL) {
+ err = EFAULT;
+ goto fail2;
+ }
+
+ max_len = 0x7fffffff & MIN(TARGET_PAGE_MASK, PAGE_MASK);
+ total_len = 0;
+
+ for (i = 0; i < count; i++) {
+ abi_ulong base = tswapal(target_vec[i].iov_base);
+ abi_long len = tswapal(target_vec[i].iov_len);
+
+ if (len < 0) {
+ err = EINVAL;
+ goto fail;
+ } else if (len == 0) {
+ /* Zero length pointer is ignored. */
+ vec[i].iov_base = 0;
+ } else {
+ vec[i].iov_base = lock_user(type, base, len, copy);
+ /*
+ * If the first buffer pointer is bad, this is a fault. But
+ * subsequent bad buffers will result in a partial write; this is
+ * realized by filling the vector with null pointers and zero
+ * lengths.
+ */
+ if (!vec[i].iov_base) {
+ if (i == 0) {
+ err = EFAULT;
+ goto fail;
+ } else {
+ /*
+ * Fail all the subsequent addresses, they are already
+ * zero'd.
+ */
+ goto out;
+ }
+ }
+ if (len > max_len - total_len) {
+ len = max_len - total_len;
+ }
+ }
+ vec[i].iov_len = len;
+ total_len += len;
+ }
+out:
+ unlock_user(target_vec, target_addr, 0);
+ return vec;
+
+fail:
+ helper_unlock_iovec(target_vec, target_addr, vec, i, copy);
+fail2:
+ g_free(vec);
+ errno = err;
+ return NULL;
+}
+
+void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+
+ target_vec = lock_user(VERIFY_READ, target_addr,
+ count * sizeof(struct target_iovec), 1);
+ if (target_vec) {
+ helper_unlock_iovec(target_vec, target_addr, vec, count, copy);
+ }
+
+ g_free(vec);
+}
+
+/*
+ * All errnos that freebsd_syscall() returns must be -TARGET_<errcode>.
+ */
+static abi_long freebsd_syscall(void *cpu_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)
+{
+ abi_long ret;
+
+ switch (num) {
+ /*
+ * process system calls
+ */
+ case TARGET_FREEBSD_NR_exit: /* exit(2) */
+ ret = do_bsd_exit(cpu_env, arg1);
+ break;
+
+ /*
+ * File system calls.
+ */
+ case TARGET_FREEBSD_NR_read: /* read(2) */
+ ret = do_bsd_read(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_pread: /* pread(2) */
+ ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_readv: /* readv(2) */
+ ret = do_bsd_readv(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_preadv: /* preadv(2) */
+ ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ case TARGET_FREEBSD_NR_write: /* write(2) */
+ ret = do_bsd_write(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */
+ ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ case TARGET_FREEBSD_NR_writev: /* writev(2) */
+ ret = do_bsd_writev(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */
+ ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * do_freebsd_syscall() should always have a single exit point at the end so
+ * that actions, such as logging of syscall results, can be performed. This
+ * 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 arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8)
{
- return 0;
+ CPUState *cpu = env_cpu(cpu_env);
+ int ret;
+
+ trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ if (do_strace) {
+ print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6,
+ arg7, arg8);
+ if (do_strace) {
+ print_freebsd_syscall_ret(num, ret);
+ }
+ trace_guest_user_syscall_ret(cpu, num, ret);
+
+ return ret;
}
void syscall_init(void)