summaryrefslogtreecommitdiffstats
path: root/bsd-user/freebsd/os-syscall.c
diff options
context:
space:
mode:
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)