
/*--------------------------------------------------------------------*/
/*--- Platform-specific syscalls stuff.    syswrap-amd64-freebsd.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2000-2005 Nicholas Nethercote
      njn@valgrind.org

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>.

   The GNU General Public License is contained in the file COPYING.
*/

#if defined(VGP_amd64_freebsd)

#include "pub_core_basics.h"
#include "pub_core_vki.h"
#include "pub_core_vkiscnums.h"
#include "pub_core_threadstate.h"
#include "pub_core_aspacemgr.h"
#include "pub_core_debuglog.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcproc.h"
#include "pub_core_libcsignal.h"
#include "pub_core_machine.h"
#include "pub_core_options.h"
#include "pub_core_scheduler.h"
#include "pub_core_sigframe.h"
#include "pub_core_signals.h"
#include "pub_core_syscall.h"
#include "pub_core_syswrap.h"
#include "pub_core_tooliface.h"
#include "pub_core_stacks.h"        // VG_(register_stack)

#include "priv_types_n_macros.h"
#include "priv_syswrap-generic.h"    /* for decls of generic wrappers */
#include "priv_syswrap-freebsd.h"    /* for decls of freebsd-ish wrappers */
#include "priv_syswrap-main.h"

/* ---------------------------------------------------------------------
   clone() handling
   ------------------------------------------------------------------ */

/* Call f(arg1), but first switch stacks, using 'stack' as the new
   stack, and use 'retaddr' as f's return-to address.  Also, clear all
   the integer registers before entering f. */
__attribute__((noreturn))
void ML_(call_on_new_stack_0_1) ( Addr stack,
                                  Addr retaddr,
                                  void (*f)(Word),
                                  Word arg1 );
// %rdi == stack
// %rsi == retaddr
// %rdx == f
// %rcx == arg1
asm(
   ".text\n"
   ".globl vgModuleLocal_call_on_new_stack_0_1\n"
   "vgModuleLocal_call_on_new_stack_0_1:\n"
   "   movq   %rdi, %rsp\n"   // set stack
   "   pushq  %rsi\n"         // retaddr to stack
   "   pushq  %rdx\n"         // f to stack
   "   pushq  %rcx\n"         // arg1 to stack
   "   movq $0, %rax\n"       // zero all GP regs
   "   movq $0, %rbx\n"
   "   movq $0, %rcx\n"
   "   movq $0, %rdx\n"
   "   movq $0, %rsi\n"
   "   movq $0, %rdi\n"
   "   movq $0, %rbp\n"
   "   movq $0, %r8\n"
   "   movq $0, %r9\n"
   "   movq $0, %r10\n"
   "   movq $0, %r11\n"
   "   movq $0, %r12\n"
   "   movq $0, %r13\n"
   "   movq $0, %r14\n"
   "   movq $0, %r15\n"
   "   popq   %rdi\n"         // arg1 to correct arg reg
   "   ret\n"                 // jump to f
   "   ud2\n"                 // should never get here
   ".previous\n"
);


/* ---------------------------------------------------------------------
   More thread stuff
   ------------------------------------------------------------------ */

void VG_(cleanup_thread) ( ThreadArchState *arch )
{
}

/* ---------------------------------------------------------------------
   PRE/POST wrappers for amd64/FreeBSD-specific syscalls
   ------------------------------------------------------------------ */

#define PRE(name)       DEFN_PRE_TEMPLATE(freebsd, name)
#define POST(name)      DEFN_POST_TEMPLATE(freebsd, name)

// SYS_sysarch 165
// int sysarch(int number, void *args);
PRE(sys_sysarch)
{
   ThreadState *tst;
   void **p;

   PRINT("sys_sysarch ( %" FMT_REGWORD "u, %#" FMT_REGWORD "x )", ARG1, ARG2);
   PRE_REG_READ2(int, "sysarch", int, number, void *, args);
   switch (ARG1) {
   case VKI_AMD64_SET_FSBASE:
      PRINT("sys_amd64_set_fsbase ( %#lx )", ARG2);

      if (ML_(safe_to_deref)((void**)ARG2, sizeof(void*))) {
         /* On FreeBSD, the syscall loads the %gs selector for us, so do it now. */
         tst = VG_(get_ThreadState)(tid);
         p = (void**)ARG2;
         tst->arch.vex.guest_FS_CONST = (UWord)*p;
         /* "do" the syscall ourselves; the kernel never sees it */
         SET_STATUS_Success2((ULong)*p, tst->arch.vex.guest_RDX );
      } else {
         // ????
         SET_STATUS_Failure( VKI_EINVAL );
      }

      break;
   case VKI_AMD64_GET_FSBASE:
      PRINT("sys_amd64_get_fsbase ( %#lx )", ARG2);
      PRE_MEM_WRITE( "amd64_get_fsbase(basep)", ARG2, sizeof(void *) );
      if (ML_(safe_to_deref)((void**)ARG2, sizeof(void*))) {
         /* "do" the syscall ourselves; the kernel never sees it */
         tst = VG_(get_ThreadState)(tid);
         SET_STATUS_Success2( tst->arch.vex.guest_FS_CONST, tst->arch.vex.guest_RDX );
      } else {
         SET_STATUS_Failure( VKI_EINVAL );
      }
      break;
   case VKI_AMD64_GET_XFPUSTATE:
      PRINT("sys_amd64_get_xfpustate ( %#lx )", ARG2);
      PRE_MEM_WRITE( "amd64_get_xfpustate(basep)", ARG2, sizeof(void *) );

      // @todo PJF need a test for this
      // I think that it will fail in the POST if ARG2 is not a valid pointer

      /* "do" the syscall ourselves; the kernel never sees it */
      tst = VG_(get_ThreadState)(tid);
      SET_STATUS_Success2( tst->arch.vex.guest_FPTAG[0], tst->arch.vex.guest_FPTAG[0] );
      break;
   default:
      VG_(message) (Vg_UserMsg, "unhandled sysarch cmd %lu", ARG1);
      VG_(unimplemented) ("unhandled sysarch cmd");
      break;
   }
}

POST(sys_sysarch)
{
   switch (ARG1) {
   case VKI_AMD64_SET_FSBASE:
      break;
   case VKI_AMD64_GET_FSBASE:
      POST_MEM_WRITE( ARG2, sizeof(void *) );
      break;
   case VKI_AMD64_GET_XFPUSTATE:
      POST_MEM_WRITE( ARG2, sizeof(void *) );
      break;
   default:
      break;
   }
}

// freebsd6_pread 173
#if (FREEBSD_VERS <= FREEBSD_10)
PRE(sys_freebsd6_pread)
{
   *flags |= SfMayBlock;
   PRINT("sys_freebsd6_pread ( %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %lu, %" FMT_REGWORD "u, %" FMT_REGWORD "u )", ARG1, ARG2, ARG3, ARG4, ARG5);
   PRE_REG_READ5(ssize_t, "read",
                 unsigned int, fd, char *, buf, vki_size_t, count,
                 int, pad, unsigned long, off);

   if (!ML_(fd_allowed)(ARG1, "freebsd6_pread", tid, False))
      SET_STATUS_Failure( VKI_EBADF );
   else
      PRE_MEM_WRITE( "freebsd6_pread(buf)", ARG2, ARG3 );
}

POST(sys_freebsd6_pread)
{
   vg_assert(SUCCESS);
   POST_MEM_WRITE( ARG2, RES );
}
#endif

// freebsd6_pwrite 174
#if (FREEBSD_VERS <= FREEBSD_10)
PRE(sys_freebsd6_pwrite)
{
   Bool ok;
   *flags |= SfMayBlock;
   PRINT("sys_freebsd6_pwrite ( %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "u )", SARG1, ARG2, ARG3, ARG4, ARG5);
   PRE_REG_READ5(ssize_t, "write",
                 unsigned int, fd, const char *, buf, vki_size_t, count,
                 int, pad, unsigned long, off);
   /* check to see if it is allowed.  If not, try for an exemption from
      --sim-hints=enable-outer (used for self hosting). */
   ok = ML_(fd_allowed)(ARG1, "freebsd6_pwrite", tid, False);
   if (!ok && ARG1 == 2/*stderr*/
         && SimHintiS(SimHint_enable_outer, VG_(clo_sim_hints)))
      ok = True;
   if (!ok)
      SET_STATUS_Failure( VKI_EBADF );
   else
      PRE_MEM_READ( "freebsd6_pwrite(buf)", ARG2, ARG3 );
}
#endif

// SYS_freebsd6_mmap 197
#if (FREEBSD_VERS <= FREEBSD_10)
/* This is here because on x86 the off_t is passed in 2 regs. Don't ask about pad.  */

/* caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, int pad, off_t pos); */
/*              ARG1           ARG2       ARG3      ARG4       ARG5    ARG6     ARG7 */

PRE(sys_freebsd6_mmap)
{
   SysRes r;

   PRINT("sys_mmap ( %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "u, pad%" FMT_REGWORD "u, 0x%" FMT_REGWORD "x)",
         ARG1, (UWord)ARG2, ARG3, ARG4, ARG5, ARG6, ARG7 );
   PRE_REG_READ7(long, "mmap",
                 char *, addr, unsigned long, len, int, prot,  int, flags,
                 int, fd,  int, pad, unsigned long, pos);

   r = ML_(generic_PRE_sys_mmap)( tid, ARG1, ARG2, ARG3, ARG4, ARG5, ARG7 );
   SET_STATUS_from_SysRes(r);
}
#endif

// freebsd6_lseek 199
#if (FREEBSD_VERS <= FREEBSD_10)
PRE(sys_freebsd6_lseek)
{
   PRINT("sys_freebsd6_lseek ( %" FMT_REGWORD "u, 0x%" FMT_REGWORD "x, %#" FMT_REGWORD "x, %" FMT_REGWORD "u )", ARG1,ARG2,ARG3,ARG4);
   PRE_REG_READ4(long, "lseek",
                 unsigned int, fd, int, pad, unsigned long, offset,
                 unsigned int, whence);
}
#endif

// freebsd6_truncate 200
#if (FREEBSD_VERS <= FREEBSD_10)
PRE(sys_freebsd6_truncate)
{
   *flags |= SfMayBlock;
   PRINT("sys_truncate ( %#" FMT_REGWORD "x(%s), %" FMT_REGWORD "u )", ARG1,(char *)ARG1,ARG3);
   PRE_REG_READ3(long, "truncate",
                 const char *, path, int, pad, unsigned int, length);
   PRE_MEM_RASCIIZ( "truncate(path)", ARG1 );
}
#endif

// freebsd6_ftruncate 201
#if (FREEBSD_VERS <= FREEBSD_10)
PRE(sys_freebsd6_ftruncate)
{
   *flags |= SfMayBlock;
   PRINT("sys_ftruncate ( %" FMT_REGWORD "u, %" FMT_REGWORD "u )", ARG1,ARG3);
   PRE_REG_READ3(long, "ftruncate", unsigned int, fd, int, pad,
                 unsigned int, length);
}
#endif

// SYS_rfork 251
// pid_t rfork(int flags);
PRE(sys_rfork)
{
   PRINT("sys_rfork ( %#" FMT_REGWORD "x )", ARG1 );
   PRE_REG_READ1(long, "rfork", int, flags);

   VG_(message)(Vg_UserMsg, "rfork() not implemented");
   VG_(unimplemented)("Valgrind does not support rfork().");

   SET_STATUS_Failure(VKI_ENOSYS);
}

// SYS_preadv  289
// ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
PRE(sys_preadv)
{
   Int i;
   struct vki_iovec * vec;
   *flags |= SfMayBlock;
   PRINT("sys_preadv ( %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %"
         FMT_REGWORD "d, %" FMT_REGWORD "d )", SARG1, ARG2, SARG3, SARG4);
   PRE_REG_READ4(ssize_t, "preadv",
                 int, fd, const struct iovec *, iovr,
                 int, iovcnt, vki_off_t, offset);
   if (!ML_(fd_allowed)(ARG1, "preadv", tid, False)) {
      SET_STATUS_Failure( VKI_EBADF );
   } else {
      if ((Int)ARG3 >= 0)
         PRE_MEM_READ( "preadv(iov)", ARG2, ARG3 * sizeof(struct vki_iovec) );

      if (ML_(safe_to_deref)((struct vki_iovec *)ARG2, ARG3 * sizeof(struct vki_iovec))) {
         vec = (struct vki_iovec *)(Addr)ARG2;
         for (i = 0; i < (Int)ARG3; i++)
            PRE_MEM_WRITE( "preadv(iov[...])",
                           (Addr)vec[i].iov_base, vec[i].iov_len );
      }
   }
}

POST(sys_preadv)
{
   vg_assert(SUCCESS);
   if (RES > 0) {
      Int i;
      struct vki_iovec * vec = (struct vki_iovec *)(Addr)ARG2;
      Int remains = RES;

      /* RES holds the number of bytes read. */
      for (i = 0; i < (Int)ARG3; i++) {
         Int nReadThisBuf = vec[i].iov_len;
         if (nReadThisBuf > remains) nReadThisBuf = remains;
         POST_MEM_WRITE( (Addr)vec[i].iov_base, nReadThisBuf );
         remains -= nReadThisBuf;
         if (remains < 0) VG_(core_panic)("preadv: remains < 0");
      }
   }
}

// SYS_pwritev 290
// ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
PRE(sys_pwritev)
{
   Int i;
   struct vki_iovec * vec;
   *flags |= SfMayBlock;
   PRINT("sys_pwritev ( %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %"
         FMT_REGWORD "d, %" FMT_REGWORD "d )", SARG1, ARG2, SARG3, SARG4);

   PRE_REG_READ4(ssize_t, "pwritev",
                 int, fd, const struct iovec *, iov,
                 int, iovcnt,
                 vki_off_t, offset);
   if (!ML_(fd_allowed)(ARG1, "pwritev", tid, False)) {
      SET_STATUS_Failure( VKI_EBADF );
   } else {
      if ((Int)ARG3 >= 0)
         PRE_MEM_READ( "pwritev(vector)", ARG2, ARG3 * sizeof(struct vki_iovec) );
      if (ML_(safe_to_deref)((struct vki_iovec *)ARG2, ARG3 * sizeof(struct vki_iovec))) {
         vec = (struct vki_iovec *)(Addr)ARG2;
         for (i = 0; i < (Int)ARG3; i++)
            PRE_MEM_READ( "pwritev(iov[...])",
                          (Addr)vec[i].iov_base, vec[i].iov_len );
      }
   }
}

// SYS_sendfile   393
// int sendfile(int fd, int s, off_t offset, size_t nbytes,
//         struct sf_hdtr *hdtr, off_t *sbytes, int flags);
PRE(sys_sendfile)
{
   *flags |= SfMayBlock;

   PRINT("sys_sendfile ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %lu, %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %#" FMT_REGWORD "x, %" FMT_REGWORD "d )",
         SARG1,SARG2,ARG3,ARG4,ARG5,ARG6,SARG7);
   PRE_REG_READ7(int, "sendfile",
                 int, fd, int, s, vki_off_t, offset, size_t, nbytes,
                 void *, hdtr, vki_off_t *, sbytes, int, flags);

   if (ARG5 != 0)
      PRE_MEM_READ("sendfile(hdtr)", ARG5, sizeof(struct vki_sf_hdtr));

   if (ARG6 != 0)
      PRE_MEM_WRITE( "sendfile(sbytes)", ARG6, sizeof(vki_off_t) );
}

POST(sys_sendfile)
{
   if (ARG6 != 0 ) {
      POST_MEM_WRITE( ARG6, sizeof( vki_off_t ) );
   }
}

// SYS_sigreturn  417
// int sigreturn(const ucontext_t *scp);
PRE(sys_sigreturn)
{
   PRINT("sys_sigreturn ( %#" FMT_REGWORD "x )", ARG1);
   PRE_REG_READ1(int, "sigreturn",
                 struct vki_ucontext *, ucp);

   PRE_MEM_READ( "sigreturn(ucp)", ARG1, sizeof(struct vki_ucontext) );
   PRE_MEM_WRITE( "sigreturn(ucp)", ARG1, sizeof(struct vki_ucontext) );
}

static void restore_mcontext(ThreadState *tst, struct vki_mcontext *sc)
{
   tst->arch.vex.guest_RAX     = sc->rax;
   tst->arch.vex.guest_RCX     = sc->rcx;
   tst->arch.vex.guest_RDX     = sc->rdx;
   tst->arch.vex.guest_RBX     = sc->rbx;
   tst->arch.vex.guest_RBP     = sc->rbp;
   tst->arch.vex.guest_RSP     = sc->rsp;
   tst->arch.vex.guest_RSI     = sc->rsi;
   tst->arch.vex.guest_RDI     = sc->rdi;
   tst->arch.vex.guest_R8      = sc->r8;
   tst->arch.vex.guest_R9      = sc->r9;
   tst->arch.vex.guest_R10     = sc->r10;
   tst->arch.vex.guest_R11     = sc->r11;
   tst->arch.vex.guest_R12     = sc->r12;
   tst->arch.vex.guest_R13     = sc->r13;
   tst->arch.vex.guest_R14     = sc->r14;
   tst->arch.vex.guest_R15     = sc->r15;
   tst->arch.vex.guest_RIP     = sc->rip;
   /*
    * XXX: missing support for other flags.
    */
   if (sc->rflags & 0x0001)
      LibVEX_GuestAMD64_put_rflag_c(1, &tst->arch.vex);
   else
      LibVEX_GuestAMD64_put_rflag_c(0, &tst->arch.vex);
}

static void fill_mcontext(ThreadState *tst, struct vki_mcontext *sc)
{
   sc->rax = tst->arch.vex.guest_RAX;
   sc->rcx = tst->arch.vex.guest_RCX;
   sc->rdx = tst->arch.vex.guest_RDX;
   sc->rbx = tst->arch.vex.guest_RBX;
   sc->rbp = tst->arch.vex.guest_RBP;
   sc->rsp = tst->arch.vex.guest_RSP;
   sc->rsi = tst->arch.vex.guest_RSI;
   sc->rdi = tst->arch.vex.guest_RDI;
   sc->r8 = tst->arch.vex.guest_R8;
   sc->r9 = tst->arch.vex.guest_R9;
   sc->r10 = tst->arch.vex.guest_R10;
   sc->r11 = tst->arch.vex.guest_R11;
   sc->r12 = tst->arch.vex.guest_R12;
   sc->r13 = tst->arch.vex.guest_R13;
   sc->r14 = tst->arch.vex.guest_R14;
   sc->r15 = tst->arch.vex.guest_R15;
   sc->rip = tst->arch.vex.guest_RIP;
   /*
      Not supported by VEX.
      sc->cs = tst->arch.vex.guest_CS;
      sc->ss = tst->arch.vex.guest_SS;
      sc->ds = tst->arch.vex.guest_DS;
      sc->es = tst->arch.vex.guest_ES;
      sc->fs = tst->arch.vex.guest_FS;
      sc->gs = tst->arch.vex.guest_GS;
   */
   sc->rflags = LibVEX_GuestAMD64_get_rflags(&tst->arch.vex);
   /*
      not yet.
      VG_(memcpy)(&sc->fpstate, fpstate, sizeof(*fpstate));
   */
   sc->fpformat = VKI_FPFMT_NODEV;
   sc->ownedfp = VKI_FPOWNED_NONE;
   sc->len = sizeof(*sc);
   VG_(memset)(sc->spare2, 0, sizeof(sc->spare2));
}

// SYS_getcontext 421
// int getcontext(ucontext_t *ucp);
PRE(sys_getcontext)
{
   ThreadState* tst;
   struct vki_ucontext *uc;

   PRINT("sys_getcontext ( %#" FMT_REGWORD "x )", ARG1);
   PRE_REG_READ1(int, "getcontext",
                 struct vki_ucontext *, ucp);
   PRE_MEM_WRITE( "getcontext(ucp)", ARG1, sizeof(struct vki_ucontext) );
   uc = (struct vki_ucontext *)ARG1;
   if (!ML_(safe_to_deref)(uc, sizeof(struct vki_ucontext))) {
      SET_STATUS_Failure(VKI_EFAULT);
      return;
   }
   tst = VG_(get_ThreadState)(tid);
   fill_mcontext(tst, &uc->uc_mcontext);
   uc->uc_mcontext.rax = 0;
   uc->uc_mcontext.rdx = 0;
   uc->uc_mcontext.rflags &= ~0x0001;  /* PSL_C */
   uc->uc_sigmask = tst->sig_mask;
   VG_(memset)(uc->__spare__, 0, sizeof(uc->__spare__));
   SET_STATUS_Success(0);
}

// SYS_setcontext 422
// int setcontext(const ucontext_t *ucp);
PRE(sys_setcontext)
{
   ThreadState* tst;
   struct vki_ucontext *uc;

   PRINT("sys_setcontext ( %#" FMT_REGWORD "x )", ARG1);
   PRE_REG_READ1(long, "setcontext",
                 struct vki_ucontext *, ucp);

   PRE_MEM_READ( "setcontext(ucp)", ARG1, sizeof(struct vki_ucontext) );
   PRE_MEM_WRITE( "setcontext(ucp)", ARG1, sizeof(struct vki_ucontext) );

   vg_assert(VG_(is_valid_tid)(tid));
   vg_assert(tid >= 1 && tid < VG_N_THREADS);
   vg_assert(VG_(is_running_thread)(tid));

   tst = VG_(get_ThreadState)(tid);
   uc = (struct vki_ucontext *)ARG1;
   if (!ML_(safe_to_deref)(uc, sizeof(struct vki_ucontext)) || uc->uc_mcontext.len != sizeof(uc->uc_mcontext)) {
      SET_STATUS_Failure(VKI_EFAULT);
      return;
   }

   restore_mcontext(tst, &uc->uc_mcontext);
   tst->sig_mask = uc->uc_sigmask;
   tst->tmp_sig_mask = uc->uc_sigmask;

   /* Tell the driver not to update the guest state with the "result",
      and set a bogus result to keep it happy. */
   *flags |= SfNoWriteResult;
   SET_STATUS_Success(0);

   /* Check to see if some any signals arose as a result of this. */
   *flags |= SfPollAfter;
}

// SYS_swapcontext   423
// int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
PRE(sys_swapcontext)
{
   struct vki_ucontext *ucp, *oucp;
   ThreadState* tst;

   PRINT("sys_swapcontext ( %#" FMT_REGWORD "x, %#" FMT_REGWORD "x )", ARG1, ARG2);
   PRE_REG_READ2(long, "swapcontext",
                 struct vki_ucontext *, oucp, struct vki_ucontext *, ucp);

   PRE_MEM_READ( "swapcontext(ucp)", ARG2, sizeof(struct vki_ucontext) );
   PRE_MEM_WRITE( "swapcontext(oucp)", ARG1, sizeof(struct vki_ucontext) );

   oucp = (struct vki_ucontext *)ARG1;
   ucp = (struct vki_ucontext *)ARG2;
   if (!ML_(safe_to_deref)(oucp, sizeof(struct vki_ucontext)) ||
         !ML_(safe_to_deref)(ucp, sizeof(struct vki_ucontext)) ||
         ucp->uc_mcontext.len != sizeof(ucp->uc_mcontext)) {
      SET_STATUS_Failure(VKI_EINVAL);
      return;
   }
   tst = VG_(get_ThreadState)(tid);

   /*
    * Save the context.
    */
   fill_mcontext(tst, &oucp->uc_mcontext);
   oucp->uc_mcontext.rax = 0;
   oucp->uc_mcontext.rdx = 0;
   oucp->uc_mcontext.rflags &= ~0x0001;   /* PSL_C */
   oucp->uc_sigmask = tst->sig_mask;
   VG_(memset)(oucp->__spare__, 0, sizeof(oucp->__spare__));

   /*
    * Switch to new one.
    */
   restore_mcontext(tst, &ucp->uc_mcontext);
   tst->sig_mask = ucp->uc_sigmask;
   tst->tmp_sig_mask = ucp->uc_sigmask;

   /* Tell the driver not to update the guest state with the "result",
      and set a bogus result to keep it happy. */
   *flags |= SfNoWriteResult;
   SET_STATUS_Success(0);

   /* Check to see if some any signals arose as a result of this. */
   *flags |= SfPollAfter;
}

// SYS_thr_new 455
// int thr_new(struct thr_param *param, int param_size);
PRE(sys_thr_new)
{
   static const Bool debug = False;

   ThreadId     ctid = VG_(alloc_ThreadState)();
   ThreadState* ptst = VG_(get_ThreadState)(tid);
   ThreadState* ctst = VG_(get_ThreadState)(ctid);
   SysRes       res;
   vki_sigset_t blockall, savedmask;
   struct vki_thr_param tp;
   Addr stk;

   PRINT("thr_new ( %#" FMT_REGWORD "x, %" FMT_REGWORD "u )",ARG1,ARG2);
   PRE_REG_READ2(int, "thr_new",
                 struct thr_param *, param,
                 int, param_size);

   PRE_MEM_READ( "thr_new(param)", ARG1, offsetof(struct vki_thr_param, spare));
   if (!ML_(safe_to_deref)( (void*)ARG1, offsetof(struct vki_thr_param, spare))) {
      SET_STATUS_Failure( VKI_EFAULT );
      return;
   }
   VG_(memset)(&tp, 0, sizeof(tp));
   VG_(memcpy)(&tp, (void *)ARG1, offsetof(struct vki_thr_param, spare));
   PRE_MEM_WRITE("thr_new(parent_tidptr)", (Addr)tp.parent_tid, sizeof(long));
   PRE_MEM_WRITE("thr_new(child_tidptr)", (Addr)tp.child_tid, sizeof(long));

   VG_(sigfillset)(&blockall);

   vg_assert(VG_(is_running_thread)(tid));
   vg_assert(VG_(is_valid_tid)(ctid));

   /* Copy register state

      On linux, both parent and child return to the same place, and the code
      following the clone syscall works out which is which, so we
      don't need to worry about it.
      On FreeBSD, thr_new arranges a direct call.  We don't actually need any
      of this gunk.

      The parent gets the child's new tid returned from clone, but the
      child gets 0.

      If the clone call specifies a NULL rsp for the new thread, then
      it actually gets a copy of the parent's rsp.
   */
   /* We inherit our parent's guest state. */
   ctst->arch.vex = ptst->arch.vex;
   ctst->arch.vex_shadow1 = ptst->arch.vex_shadow1;
   ctst->arch.vex_shadow2 = ptst->arch.vex_shadow2;

   /* Make thr_new appear to have returned Success(0) in the
      child. */
   ctst->arch.vex.guest_RAX = 0;
   ctst->arch.vex.guest_RDX = 0;
   LibVEX_GuestAMD64_put_rflag_c(0, &ctst->arch.vex);

   ctst->os_state.parent = tid;

   /* inherit signal mask */
   ctst->sig_mask = ptst->sig_mask;
   ctst->tmp_sig_mask = ptst->sig_mask;

   /* Linux has to guess, we don't */
   ctst->client_stack_highest_byte = (Addr)tp.stack_base + tp.stack_size;
   ctst->client_stack_szB = tp.stack_size;
   ctst->os_state.stk_id = VG_(register_stack)((Addr)tp.stack_base, (Addr)tp.stack_base + tp.stack_size);

   /* Assume the thr_new will succeed, and tell any tool that wants to
      know that this thread has come into existence.  If the thr_new
      fails, we'll send out a ll_exit notification for it at the out:
      label below, to clean up. */
   VG_TRACK ( pre_thread_ll_create, tid, ctid );

   if (debug)
      VG_(printf)("clone child has SETTLS: tls at %#lx\n", (Addr)tp.tls_base);
   ctst->arch.vex.guest_FS_CONST = (UWord)tp.tls_base;
   tp.tls_base = 0;  /* Don't have the kernel do it too */

   /* start the thread with everything blocked */
   VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);

   /* Set the client state for scheduler to run libthr's trampoline */
   ctst->arch.vex.guest_RDI = (Addr)tp.arg;
   /* XXX: align on 16-byte boundary? */
   ctst->arch.vex.guest_RSP = (Addr)tp.stack_base + tp.stack_size - 8;
   ctst->arch.vex.guest_RIP = (Addr)tp.start_func;

   /* But this is for thr_new() to run valgrind's trampoline */
   tp.start_func = (void *)ML_(start_thread_NORETURN);
   tp.arg = &VG_(threads)[ctid];

   /* And valgrind's trampoline on its own stack */
   stk = ML_(allocstack)(ctid);
   if (stk == (Addr)NULL) {
      res = VG_(mk_SysRes_Error)( VKI_ENOMEM );
      goto fail;
   }
   tp.stack_base = (void *)ctst->os_state.valgrind_stack_base;
   tp.stack_size = (Addr)stk - (Addr)tp.stack_base;

   /* Create the new thread */
   res = VG_(do_syscall2)(__NR_thr_new, (UWord)&tp, sizeof(tp));

   VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);

fail:
   if (sr_isError(res)) {
      /* thr_new failed */
      VG_(cleanup_thread)(&ctst->arch);
      ctst->status = VgTs_Empty;
      /* oops.  Better tell the tool the thread exited in a hurry :-) */
      VG_TRACK( pre_thread_ll_exit, ctid );
   } else {

      POST_MEM_WRITE((Addr)tp.parent_tid, sizeof(long));
      POST_MEM_WRITE((Addr)tp.child_tid, sizeof(long));

      /* Thread creation was successful; let the child have the chance
         to run */
      *flags |= SfYieldAfter;
   }

   /* "Complete" the syscall so that the wrapper doesn't call the kernel again. */
   SET_STATUS_from_SysRes(res);
}

// SYS_pread   475
// ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
PRE(sys_pread)
{
   *flags |= SfMayBlock;
   PRINT("sys_pread ( %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %" FMT_REGWORD "u )", ARG1, ARG2, ARG3, ARG4);
   PRE_REG_READ4(ssize_t, "pread",
                 unsigned int, fd, char *, buf, vki_size_t, count,
                 unsigned long, off);

   if (!ML_(fd_allowed)(ARG1, "read", tid, False))
      SET_STATUS_Failure( VKI_EBADF );
   else
      PRE_MEM_WRITE( "pread(buf)", ARG2, ARG3 );
}

POST(sys_pread)
{
   vg_assert(SUCCESS);
   POST_MEM_WRITE( ARG2, RES );
}

// SYS_pwrite  476
// ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
PRE(sys_pwrite)
{
   Bool ok;
   *flags |= SfMayBlock;
   PRINT("sys_pwrite ( %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %" FMT_REGWORD "u )", ARG1, ARG2, ARG3, ARG4);
   PRE_REG_READ4(ssize_t, "pwrite",
                 int, fd, const char *, buf, vki_size_t, nbytes,
                 vki_off_t, offset);
   /* check to see if it is allowed.  If not, try for an exemption from
      --sim-hints=enable-outer (used for self hosting). */
   ok = ML_(fd_allowed)(ARG1, "pwrite", tid, False);
   if (!ok && ARG1 == 2/*stderr*/
         && SimHintiS(SimHint_enable_outer, VG_(clo_sim_hints)))
      ok = True;
   if (!ok)
      SET_STATUS_Failure( VKI_EBADF );
   else
      PRE_MEM_READ( "pwrite(buf)", ARG2, ARG3 );
}

// SYS_mmap 477
/* FreeBSD-7 introduces a "regular" version of mmap etc. */
// void * mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
PRE(sys_mmap)
{
   SysRes r;

   PRINT("sys_mmap ( %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "u, 0x%" FMT_REGWORD "x)",
         ARG1, (UWord)ARG2, ARG3, ARG4, ARG5, ARG6 );
   PRE_REG_READ6(void *, "mmap",
                 void *, addr, size_t, len, int, prot,  int, flags,
                 int, fd,  off_t, offset);

   r = ML_(generic_PRE_sys_mmap)( tid, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 );
   SET_STATUS_from_SysRes(r);
}

// SYS_lseek 478
// off_t lseek(int fildes, off_t offset, int whence);
PRE(sys_lseek)
{
   PRINT("sys_lseek ( %" FMT_REGWORD "u, 0x%" FMT_REGWORD "x, %" FMT_REGWORD "u )", ARG1,ARG2,ARG3);
   PRE_REG_READ3(long, "lseek",
                 unsigned int, fd, unsigned long, offset,
                 unsigned int, whence);
}

// SYS_truncate   479
// int truncate(const char *path, off_t length);
PRE(sys_truncate)
{
   *flags |= SfMayBlock;
   PRINT("sys_truncate ( %#" FMT_REGWORD "x(%s), %" FMT_REGWORD "u )", ARG1,(char *)ARG1,ARG2);
   PRE_REG_READ2(long, "truncate",
                 const char *, path, unsigned long, length);
   PRE_MEM_RASCIIZ( "truncate(path)", ARG1 );
}

// SYS_ftruncate  480
// int ftruncate(int fd, off_t length);
PRE(sys_ftruncate)
{
   *flags |= SfMayBlock;
   PRINT("sys_ftruncate ( %" FMT_REGWORD "u, %" FMT_REGWORD "u )", ARG1,ARG2);
   PRE_REG_READ2(long, "ftruncate", unsigned int, fd,
                 unsigned long, length);
}

// SYS_cpuset_setid  485
// int cpuset_setid(cpuwhich_t which, id_t id, cpusetid_t setid);
PRE(sys_cpuset_setid)
{
   PRINT("sys_cpuset_setid ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %#" FMT_REGWORD "x )",
         SARG1, SARG2, ARG3);
   PRE_REG_READ3(int, "cpuset_setid", vki_cpuwhich_t, which, vki_id_t, id,
                 vki_cpusetid_t *,setid);
}

// SYS_cpuset_getid  486
// int cpuset_getid(cpulevel_t level, cpuwhich_t which, id_t id,
//                  cpusetid_t *setid);
PRE(sys_cpuset_getid)
{
   PRINT("sys_cpuset_getid ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD "d, %#" FMT_REGWORD "x )",
         SARG1, SARG2, SARG3, ARG4);
   PRE_REG_READ4(int, "cpuset_getid", vki_cpulevel_t, level,
                 vki_cpuwhich_t, which, vki_id_t, id,
                 vki_cpusetid_t, setid);
   PRE_MEM_WRITE("cpuset_getid(setid)", ARG4, sizeof(vki_cpusetid_t));
}

POST(sys_cpuset_getid)
{
   POST_MEM_WRITE(ARG4, sizeof(vki_cpusetid_t));
}

// SYS_cpuset_getaffinity  487
// int cpuset_getaffinity(cpulevel_t level, cpuwhich_t which, id_t id,
//                        size_t setsize, cpuset_t *mask);
PRE(sys_cpuset_getaffinity)
{
   PRINT("sys_cpuset_getaffinity ( %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "d, %" FMT_REGWORD "u, %#" FMT_REGWORD "x )",
         ARG1, ARG2, SARG3, ARG4, ARG5);
   PRE_REG_READ5(int, "cpuset_getaffinity",
                 vki_cpulevel_t, level, vki_cpuwhich_t, which, vki_id_t, id,
                 size_t, setsize, void *, mask);
   PRE_MEM_WRITE("cpuset_getaffinity", ARG5, ARG4);
}

POST(sys_cpuset_getaffinity)
{
   vg_assert(SUCCESS);
   if (RES == 0)
      POST_MEM_WRITE( ARG5, ARG4 );
}

// SYS_cpuset_setaffinity  488
// int cpuset_setaffinity(cpulevel_t level, cpuwhich_t which, id_t id,
//                        size_t setsize, const cpuset_t *mask);
PRE(sys_cpuset_setaffinity)
{

   PRINT("sys_cpuset_setaffinity ( %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "d, %" FMT_REGWORD "u, %#" FMT_REGWORD "x )",
         ARG1, ARG2, SARG3, ARG4, ARG5);
   PRE_REG_READ5(int, "cpuset_setaffinity",
                 vki_cpulevel_t, level, vki_cpuwhich_t, which, vki_id_t, id,
                 size_t, setsize, void *, mask);
   PRE_MEM_READ("cpuset_setaffinity", ARG5, ARG4);
}

// SYS_posix_fallocate 530
// int posix_fallocate(int fd, off_t offset, off_t len);
PRE(sys_posix_fallocate)
{
   PRINT("sys_posix_fallocate ( %" FMT_REGWORD "d, %" FMT_REGWORD "u, %" FMT_REGWORD "u )",
         SARG1, ARG2, ARG3);
   PRE_REG_READ3(long, "posix_fallocate",
                 int, fd, vki_off_t, offset,
                 vki_off_t, len);
}

// SYS_posix_fadvise 531
// int posix_fadvise(int fd, off_t offset, off_t len, int advice);
PRE(sys_posix_fadvise)
{
   PRINT("sys_posix_fadvise ( %" FMT_REGWORD "d, %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" FMT_REGWORD "d )",
         SARG1, ARG2, ARG3, SARG4);
   PRE_REG_READ4(long, "posix_fadvise",
                 int, fd, off_t, offset,
                 off_t, len,
                 int, advice);
   // @todo PJF advice can be 0 to 5 inclusive
}

// SYS_wait6   532
// pid_t wait6(idtype_t idtype, id_t id, int *status, int options,
//             struct __wrusage *wrusage, siginfo_t *infop);
PRE(sys_wait6)
{
   PRINT("sys_wait6 ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %#" FMT_REGWORD "x )",
         SARG1, SARG2, ARG3, SARG4, ARG5, ARG6);
   PRE_REG_READ6(pid_t, "wait6", vki_idtype_t, idtype, vki_id_t, id, int *, status, int, options,
                 struct vki___wrusage *, wrusage, vki_siginfo_t *,infop);
   PRE_MEM_WRITE("wait6(status)", ARG3, sizeof(int));
   if (ARG5) {
      PRE_MEM_WRITE("wait6(wrusage)", ARG5, sizeof(struct vki___wrusage));
   }
   if (ARG6) {
      PRE_MEM_WRITE("wait6(infop)", ARG6, sizeof(vki_siginfo_t));
   }
}

POST(sys_wait6)
{
   POST_MEM_WRITE(ARG3, sizeof(int));
   if (ARG5) {
      POST_MEM_WRITE(ARG5, sizeof(struct vki___wrusage));
   }

   if (ARG6) {
      POST_MEM_WRITE(ARG5, sizeof(vki_siginfo_t));
   }
}

// the man page is inconsistent for the last argument
// See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247386
// will stick to 'arg' for simplicity

// SYS_procctl 544
// int procctl(idtype_t idtype, id_t id, int cmd, void *arg);
PRE(sys_procctl)
{
   PRINT("sys_procctl ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD"d, %#" FMT_REGWORD "x )",
         SARG1, SARG2, SARG3, ARG4);
   PRE_REG_READ4(int, "procctl", vki_idtype_t, idtype, vki_id_t, id, int, cmd, void *, arg);
   switch (ARG3) {
   case PROC_ASLR_CTL:
   case PROC_SPROTECT:
   case PROC_TRACE_CTL:
   case PROC_TRAPCAP_CTL:
   case PROC_PDEATHSIG_CTL:
   case PROC_STACKGAP_CTL:
      PRE_MEM_READ("procctl(arg)", ARG4, sizeof(int));
      break;
   case PROC_REAP_STATUS:
      PRE_MEM_READ("procctl(arg)", ARG4, sizeof(struct vki_procctl_reaper_status));
      break;
   case PROC_REAP_GETPIDS:
      PRE_MEM_READ("procctl(arg)", ARG4, sizeof(struct vki_procctl_reaper_pids));
      break;
   case PROC_REAP_KILL:
      /* The first three fields are reads
       * int rk_sig;
       * u_int rk_flags;
       * pid_t rk_subtree;
       *
       * The last two fields are writes
       * u_int rk_killed;
       * pid_t rk_fpid;
       *
       * There is also a pad field
       */
      PRE_MEM_READ("procctl(arg)", ARG4, sizeof(int) + sizeof(u_int) + sizeof(vki_pid_t));
      PRE_MEM_WRITE("procctl(arg)", ARG4+offsetof(struct vki_procctl_reaper_kill, rk_killed), sizeof(u_int) + sizeof(vki_pid_t));
      break;
   case PROC_ASLR_STATUS:
   case PROC_PDEATHSIG_STATUS:
   case PROC_STACKGAP_STATUS:
   case PROC_TRAPCAP_STATUS:
   case PROC_TRACE_STATUS:
      PRE_MEM_WRITE("procctl(arg)", ARG4, sizeof(int));
   case PROC_REAP_ACQUIRE:
   case PROC_REAP_RELEASE:
   default:
      break;
   }
}

POST(sys_procctl)
{
   switch (ARG3) {
   case PROC_REAP_KILL:
      POST_MEM_WRITE(ARG4+offsetof(struct vki_procctl_reaper_kill, rk_killed), sizeof(u_int) + sizeof(vki_pid_t));
      break;
   case PROC_ASLR_STATUS:
   case PROC_PDEATHSIG_STATUS:
   case PROC_STACKGAP_STATUS:
   case PROC_TRAPCAP_STATUS:
   case PROC_TRACE_STATUS:
      POST_MEM_WRITE(ARG4, sizeof(int));
   default:
      break;
   }
}

#if (FREEBSD_VERS >= FREEBSD_12)

// SYS_cpuset_getdomain 561
// int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id,
//                      size_t setsize, domainset_t *mask, int *policy);
PRE(sys_cpuset_getdomain)
{
   PRINT("sys_cpuset_getdomain ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %#" FMT_REGWORD "x )",
         SARG1, SARG2, SARG3, ARG4, ARG5, ARG6);
   PRE_REG_READ6(int, "cpuset_getdomain",
                 cpulevel_t, level, cpuwhich_t, which, id_t, id,
                 size_t, setsize, vki_domainset_t *, mask, int *, policy);
   // man page says that setsize (ARG4) "is usually provided by calling sizeof(mask)"
   PRE_MEM_WRITE( "cpuset_getdomain(mask)", ARG5, ARG4 );
   PRE_MEM_WRITE( "cpuset_getdomain(policy)", ARG6, sizeof(int) );
}

POST(sys_cpuset_getdomain)
{
   POST_MEM_WRITE(ARG5, ARG4 );
   POST_MEM_WRITE(ARG6, sizeof(int) );
}

// SYS_cpuset_setdomain 562
// int cuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id,
//                     size_t setsize, const domainset_t *mask, int policy);
PRE(sys_cpuset_setdomain)
{
   PRINT("sys_cpuget_getdomain ( %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD "d, %" FMT_REGWORD "u, %#" FMT_REGWORD "x, %" FMT_REGWORD "d )",
         SARG1, SARG2, SARG3, ARG4, ARG5, SARG6);
   PRE_REG_READ6(int, "cpuset_getdomain",
                 cpulevel_t, level, cpuwhich_t, which, id_t, id,
                 size_t, setsize, vki_domainset_t *, mask, int, policy);
   // man page says that setsize (ARG4) "is usually provided by calling sizeof(mask)"
   PRE_MEM_READ( "cpuset_getdomain(mask)", ARG5, ARG4 );
}

#endif

PRE(sys_fake_sigreturn)
{
   ThreadState* tst;
   struct vki_ucontext *uc;
   int rflags;

   PRINT("sys_sigreturn ( %#" FMT_REGWORD "x )", ARG1);
   PRE_REG_READ1(long, "sigreturn",
                 struct vki_ucontext *, ucp);

   PRE_MEM_READ( "sigreturn(ucp)", ARG1, sizeof(struct vki_ucontext) );
   PRE_MEM_WRITE( "sigreturn(ucp)", ARG1, sizeof(struct vki_ucontext) );

   vg_assert(VG_(is_valid_tid)(tid));
   vg_assert(tid >= 1 && tid < VG_N_THREADS);
   vg_assert(VG_(is_running_thread)(tid));

   /* Adjust esp to point to start of frame; skip back up over handler
      ret addr */
   tst = VG_(get_ThreadState)(tid);
   tst->arch.vex.guest_RSP -= sizeof(Addr);

   uc = (struct vki_ucontext *)ARG1;
   if (uc == NULL || uc->uc_mcontext.len != sizeof(uc->uc_mcontext)) {
      SET_STATUS_Failure(VKI_EINVAL);
      return;
   }

   /* This is only so that the EIP is (might be) useful to report if
      something goes wrong in the sigreturn */
   ML_(fixup_guest_state_to_restart_syscall)(&tst->arch);

   VG_(sigframe_destroy)(tid);

   /* For unclear reasons, it appears we need the syscall to return
      without changing %RAX.  Since %RAX is the return value, and can
      denote either success or failure, we must set up so that the
      driver logic copies it back unchanged.  Also, note %RAX is of
      the guest registers written by VG_(sigframe_destroy). */
   rflags = LibVEX_GuestAMD64_get_rflags(&tst->arch.vex);
   SET_STATUS_from_SysRes( VG_(mk_SysRes_amd64_freebsd)( tst->arch.vex.guest_RAX,
                           tst->arch.vex.guest_RDX, (rflags & 1) != 0 ? True : False) );

   /*
    * Signal handler might have changed the signal mask.  Respect that.
    */
   tst->sig_mask = uc->uc_sigmask;
   tst->tmp_sig_mask = uc->uc_sigmask;

   /* Tell the driver not to update the guest state with the "result",
      and set a bogus result to keep it happy. */
   *flags |= SfNoWriteResult;
   SET_STATUS_Success(0);

   /* Check to see if some any signals arose as a result of this. */
   *flags |= SfPollAfter;
}


#undef PRE
#undef POST

#endif /* defined(VGP_amd64_freebsd) */

/*--------------------------------------------------------------------*/
/*--- end                                                          ---*/
/*--------------------------------------------------------------------*/
