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

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

   Copyright (C) 2014-2015 Petr Pavlu
      setup@dagobah.cz

   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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

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

#if defined(VGP_amd64_solaris)

#include "libvex_guest_offsets.h"
#include "pub_core_basics.h"
#include "pub_core_debuglog.h"
#include "pub_core_vki.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcsignal.h"
#include "pub_core_tooliface.h"
#include "pub_core_syswrap.h"

#include "priv_types_n_macros.h"
#include "priv_syswrap-generic.h"
#include "priv_syswrap-solaris.h"


/* 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,             /* %rdi */
                                Addr retaddr,           /* %rsi */
                                void (*f)(Word),        /* %rdx */
                                Word arg1);             /* %rcx */
__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 */
"   movq  %rcx, %rdi\n"         /* set arg1 */
"   pushq %rsi\n"               /* retaddr to stack */
"   pushq %rdx\n"               /* f to stack */
"   movq  $0, %rax\n"           /* zero all GP regs (except %rdi) */
"   movq  $0, %rbx\n"
"   movq  $0, %rcx\n"
"   movq  $0, %rdx\n"
"   movq  $0, %rsi\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"
"   ret\n"                      /* jump to f */
"   ud2\n"                      /* should never get here */
".previous\n"
);

/* This function is called to setup a context of a new Valgrind thread (which
   will run the client code). */
void ML_(setup_start_thread_context)(ThreadId tid, vki_ucontext_t *uc)
{
   ThreadState *tst = VG_(get_ThreadState)(tid);
   UWord *stack = (UWord*)tst->os_state.valgrind_stack_init_SP;

   VG_(memset)(uc, 0, sizeof(*uc));
   uc->uc_flags = VKI_UC_CPU | VKI_UC_SIGMASK;

   /* Start the thread with everything blocked. */
   VG_(sigfillset)(&uc->uc_sigmask);

   /* Set up the stack, it should be always 16-byte aligned before doing
      a function call, i.e. the first parameter is also 16-byte aligned. */
   vg_assert(VG_IS_16_ALIGNED(stack));
   stack -= 1;
   stack[0] = 0; /* bogus return value */

   /* Set up the registers. */
   uc->uc_mcontext.gregs[VKI_REG_RDI] = (UWord)tst; /* the parameter */
   uc->uc_mcontext.gregs[VKI_REG_RIP] = (UWord)ML_(start_thread_NORETURN);
   uc->uc_mcontext.gregs[VKI_REG_RSP] = (UWord)stack;
}

/* Architecture-specific part of VG_(save_context). */
void ML_(save_machine_context)(ThreadId tid, vki_ucontext_t *uc,
                               CorePart part)
{
   ThreadState *tst = VG_(get_ThreadState)(tid);
   struct vki_fpchip_state *fs
      = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state;
   SizeT i;

   /* CPU */
   /* Common registers */
   uc->uc_mcontext.gregs[VKI_REG_RIP] = tst->arch.vex.guest_RIP;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RIP,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RIP], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RAX] = tst->arch.vex.guest_RAX;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RAX,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RAX], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RBX] = tst->arch.vex.guest_RBX;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RBX,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RBX], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RCX] = tst->arch.vex.guest_RCX;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RCX,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RCX], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RDX] = tst->arch.vex.guest_RDX;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RDX,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RDX], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RBP] = tst->arch.vex.guest_RBP;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RBP,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RBP], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RSI] = tst->arch.vex.guest_RSI;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RSI,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RSI], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RDI] = tst->arch.vex.guest_RDI;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RDI,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RDI], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R8] = tst->arch.vex.guest_R8;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R8,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R8], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R9] = tst->arch.vex.guest_R9;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R9,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R9], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R10] = tst->arch.vex.guest_R10;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R10,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R10], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R11] = tst->arch.vex.guest_R11;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R11,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R11], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R12] = tst->arch.vex.guest_R12;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R12,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R12], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R13] = tst->arch.vex.guest_R13;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R13,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R13], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R14] = tst->arch.vex.guest_R14;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R14,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R14], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_R15] = tst->arch.vex.guest_R15;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_R15,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_R15], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_RSP] = tst->arch.vex.guest_RSP;
   VG_TRACK(copy_reg_to_mem, part, tid, OFFSET_amd64_RSP,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_RSP], sizeof(UWord));

   /* ERR and TRAPNO */
   uc->uc_mcontext.gregs[VKI_REG_ERR] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_ERR], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_TRAPNO] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_TRAPNO], sizeof(UWord));

   /* Segment registers */
   /* Valgrind does not support moves from/to segment registers on AMD64.  The
      values returned below are the ones that are set by the kernel when
      a program is started. */
   uc->uc_mcontext.gregs[VKI_REG_CS] = VKI_UCS_SEL;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_CS], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_DS] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_DS], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_SS] = VKI_UDS_SEL;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_SS], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_ES] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_ES], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_FS] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_FS], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_GS] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_GS], sizeof(UWord));

   /* Segment bases */
   uc->uc_mcontext.gregs[VKI_REG_FSBASE] = tst->arch.vex.guest_FS_CONST;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_FSBASE], sizeof(UWord));
   uc->uc_mcontext.gregs[VKI_REG_GSBASE] = 0;
   VG_TRACK(post_mem_write, part, tid,
            (Addr)&uc->uc_mcontext.gregs[VKI_REG_GSBASE], sizeof(UWord));

   /* Handle rflags.  Refer to the x86-solaris variant of this code for
      a detailed description. */
   uc->uc_mcontext.gregs[VKI_REG_RFL] =
      LibVEX_GuestAMD64_get_rflags(&tst->arch.vex);
   VG_TRACK(post_mem_write, part, tid,
         (Addr)&uc->uc_mcontext.gregs[VKI_REG_RFL], sizeof(UWord));
   VKI_UC_GUEST_CC_OP(uc) = tst->arch.vex.guest_CC_OP;
   VKI_UC_GUEST_CC_NDEP(uc) = tst->arch.vex.guest_CC_NDEP;
   VKI_UC_GUEST_CC_DEP1(uc) = tst->arch.vex.guest_CC_DEP1;
   VG_TRACK(copy_reg_to_mem, part, tid,
            offsetof(VexGuestAMD64State, guest_CC_DEP1),
            (Addr)&VKI_UC_GUEST_CC_DEP1(uc), sizeof(UWord));
   VKI_UC_GUEST_CC_DEP2(uc) = tst->arch.vex.guest_CC_DEP2;
   VG_TRACK(copy_reg_to_mem, part, tid,
            offsetof(VexGuestAMD64State, guest_CC_DEP2),
            (Addr)&VKI_UC_GUEST_CC_DEP2(uc), sizeof(UWord));
   VKI_UC_GUEST_RFLAGS_NEG(uc) = ~uc->uc_mcontext.gregs[VKI_REG_RFL];
   /* Calculate a checksum. */
   {
      ULong buf[5];
      ULong checksum;

      buf[0] = VKI_UC_GUEST_CC_OP(uc);
      buf[1] = VKI_UC_GUEST_CC_NDEP(uc);
      buf[2] = VKI_UC_GUEST_CC_DEP1(uc);
      buf[3] = VKI_UC_GUEST_CC_DEP2(uc);
      buf[4] = uc->uc_mcontext.gregs[VKI_REG_RFL];
      checksum = ML_(fletcher64)((UInt*)&buf, sizeof(buf) / sizeof(UInt));
      VKI_UC_GUEST_RFLAGS_CHECKSUM(uc) = checksum;
   }

   /* FPU */
   /* The fpregset_t structure on amd64 follows the layout that is used by the
      FXSAVE instruction, therefore it is only necessary to call a VEX
      function that simulates this instruction. */
   LibVEX_GuestAMD64_fxsave(&tst->arch.vex, (HWord)fs);

   /* Control word */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->cw, sizeof(fs->cw));
   /* Status word */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->sw, sizeof(fs->sw));
   /* Compressed tag word */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->fctw, sizeof(fs->fctw));
   /* Unused */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->__fx_rsvd,
            sizeof(fs->__fx_rsvd));
   vg_assert(fs->__fx_rsvd == 0);
   /* Last x87 opcode */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->fop, sizeof(fs->fop));
   vg_assert(fs->fop == 0);
   /* Last x87 instruction pointer */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->rip, sizeof(fs->rip));
   vg_assert(fs->rip == 0);
   /* Last x87 data pointer */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->rdp, sizeof(fs->rdp));
   vg_assert(fs->rdp == 0);
   /* Media-instruction control and status register */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->mxcsr, sizeof(fs->mxcsr));
   /* Supported features in MXCSR */
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->mxcsr_mask,
            sizeof(fs->mxcsr_mask));

   /* ST registers */
   for (i = 0; i < 8; i++) {
      Addr addr = (Addr)&fs->st[i];
      /* x87 uses 80b FP registers but VEX uses only 64b registers, thus we
         have to lie here. :< */
      VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
               guest_FPREG[i]), addr, sizeof(ULong));
      VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
               guest_FPREG[i]), addr + 8, sizeof(UShort));
   }

   /* XMM registers */
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM0), (Addr)&fs->xmm[0], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM1), (Addr)&fs->xmm[1], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM2), (Addr)&fs->xmm[2], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM3), (Addr)&fs->xmm[3], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM4), (Addr)&fs->xmm[4], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM5), (Addr)&fs->xmm[5], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM6), (Addr)&fs->xmm[6], sizeof(U128));
   VG_TRACK(copy_reg_to_mem, part, tid, offsetof(VexGuestAMD64State,
            guest_YMM7), (Addr)&fs->xmm[7], sizeof(U128));

   /* Status word (sw) at exception */
   fs->status = 0;
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->status, sizeof(fs->status));

   /* MXCSR at exception */
   fs->xstatus = 0;
   VG_TRACK(post_mem_write, part, tid, (Addr)&fs->xstatus,
            sizeof(fs->xstatus));
}

/* Architecture-specific part of VG_(restore_context). */
void ML_(restore_machine_context)(ThreadId tid, vki_ucontext_t *uc,
                                  CorePart part, Bool esp_is_thrptr)
{
   ThreadState *tst = VG_(get_ThreadState)(tid);
   struct vki_fpchip_state *fs
      = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state;

   /* CPU */
   if (uc->uc_flags & VKI_UC_CPU) {
      /* Common registers */
      tst->arch.vex.guest_RIP = uc->uc_mcontext.gregs[VKI_REG_RIP];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RIP], OFFSET_amd64_RIP,
               sizeof(UWord));
      tst->arch.vex.guest_RAX = uc->uc_mcontext.gregs[VKI_REG_RAX];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RAX], OFFSET_amd64_RAX,
               sizeof(UWord));
      tst->arch.vex.guest_RBX = uc->uc_mcontext.gregs[VKI_REG_RBX];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RBX], OFFSET_amd64_RBX,
               sizeof(UWord));
      tst->arch.vex.guest_RCX = uc->uc_mcontext.gregs[VKI_REG_RCX];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RCX], OFFSET_amd64_RCX,
               sizeof(UWord));
      tst->arch.vex.guest_RDX = uc->uc_mcontext.gregs[VKI_REG_RDX];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RDX], OFFSET_amd64_RDX,
               sizeof(UWord));
      tst->arch.vex.guest_RBP = uc->uc_mcontext.gregs[VKI_REG_RBP];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RBP], OFFSET_amd64_RBP,
               sizeof(UWord));
      tst->arch.vex.guest_RSI = uc->uc_mcontext.gregs[VKI_REG_RSI];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RSI], OFFSET_amd64_RSI,
               sizeof(UWord));
      tst->arch.vex.guest_RDI = uc->uc_mcontext.gregs[VKI_REG_RDI];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RDI], OFFSET_amd64_RDI,
               sizeof(UWord));
      tst->arch.vex.guest_R8 = uc->uc_mcontext.gregs[VKI_REG_R8];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R8], OFFSET_amd64_R8,
               sizeof(UWord));
      tst->arch.vex.guest_R9 = uc->uc_mcontext.gregs[VKI_REG_R9];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R9], OFFSET_amd64_R9,
               sizeof(UWord));
      tst->arch.vex.guest_R10 = uc->uc_mcontext.gregs[VKI_REG_R10];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R10], OFFSET_amd64_R10,
               sizeof(UWord));
      tst->arch.vex.guest_R11 = uc->uc_mcontext.gregs[VKI_REG_R11];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R11], OFFSET_amd64_R11,
               sizeof(UWord));
      tst->arch.vex.guest_R12 = uc->uc_mcontext.gregs[VKI_REG_R12];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R12], OFFSET_amd64_R12,
               sizeof(UWord));
      tst->arch.vex.guest_R13 = uc->uc_mcontext.gregs[VKI_REG_R13];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R13], OFFSET_amd64_R13,
               sizeof(UWord));
      tst->arch.vex.guest_R14 = uc->uc_mcontext.gregs[VKI_REG_R14];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R14], OFFSET_amd64_R14,
               sizeof(UWord));
      tst->arch.vex.guest_R15 = uc->uc_mcontext.gregs[VKI_REG_R15];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_R15], OFFSET_amd64_R15,
               sizeof(UWord));
      tst->arch.vex.guest_RSP = uc->uc_mcontext.gregs[VKI_REG_RSP];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_RSP], OFFSET_amd64_RSP,
               sizeof(UWord));

      /* Ignore ERR and TRAPNO. */

      /* Ignore segment registers. */

      /* Segment bases */
      tst->arch.vex.guest_FS_CONST = uc->uc_mcontext.gregs[VKI_REG_FSBASE];
      VG_TRACK(copy_mem_to_reg, part, tid,
               (Addr)&uc->uc_mcontext.gregs[VKI_REG_FSBASE],
               offsetof(VexGuestAMD64State, guest_FS_CONST), sizeof(UWord));

      /* Rflags.  Refer to the x86-solaris variant of this code for a detailed
         description. */
      {
         ULong rflags;
         ULong orig_rflags;
         ULong new_rflags;
         Bool ok_restore = False;

         VG_TRACK(pre_mem_read, part, tid,
                  "restore_machine_context(uc->uc_mcontext.gregs[VKI_REG_RFL])",
                  (Addr)&uc->uc_mcontext.gregs[VKI_REG_RFL], sizeof(UWord));
         rflags = uc->uc_mcontext.gregs[VKI_REG_RFL];
         orig_rflags = LibVEX_GuestAMD64_get_rflags(&tst->arch.vex);
         new_rflags = rflags;
         /* The kernel disallows the ID flag to be changed via the setcontext
            call, thus do the same. */
         if (orig_rflags & VKI_RFLAGS_ID_BIT)
            new_rflags |= VKI_RFLAGS_ID_BIT;
         else
            new_rflags &= ~VKI_RFLAGS_ID_BIT;
         LibVEX_GuestAMD64_put_rflags(new_rflags, &tst->arch.vex);
         VG_TRACK(post_reg_write, part, tid,
                  offsetof(VexGuestAMD64State, guest_CC_DEP1), sizeof(UWord));
         VG_TRACK(post_reg_write, part, tid,
                  offsetof(VexGuestAMD64State, guest_CC_DEP2), sizeof(UWord));

         if (rflags != ~VKI_UC_GUEST_RFLAGS_NEG(uc)) {
            VG_(debugLog)(1, "syswrap-solaris",
                             "The rflags value was restored from an "
                             "explicitly set value in thread %u.\n", tid);
            ok_restore = True;
         }
         else {
            ULong buf[5];
            ULong checksum;

            buf[0] = VKI_UC_GUEST_CC_OP(uc);
            buf[1] = VKI_UC_GUEST_CC_NDEP(uc);
            buf[2] = VKI_UC_GUEST_CC_DEP1(uc);
            buf[3] = VKI_UC_GUEST_CC_DEP2(uc);
            buf[4] = rflags;
            checksum = ML_(fletcher64)((UInt*)&buf,
                                       sizeof(buf) / sizeof(UInt));
            if (checksum == VKI_UC_GUEST_RFLAGS_CHECKSUM(uc)) {
               /* Check ok, the full restoration is possible. */
               VG_(debugLog)(1, "syswrap-solaris",
                                "The CC_* guest state values were fully "
                                "restored in thread %u.\n", tid);
               ok_restore = True;

               tst->arch.vex.guest_CC_OP = VKI_UC_GUEST_CC_OP(uc);
               tst->arch.vex.guest_CC_NDEP = VKI_UC_GUEST_CC_NDEP(uc);
               tst->arch.vex.guest_CC_DEP1 = VKI_UC_GUEST_CC_DEP1(uc);
               VG_TRACK(copy_mem_to_reg, part, tid,
                        (Addr)&VKI_UC_GUEST_CC_DEP1(uc),
                        offsetof(VexGuestAMD64State, guest_CC_DEP1),
                        sizeof(UWord));
               tst->arch.vex.guest_CC_DEP2 = VKI_UC_GUEST_CC_DEP2(uc);
               VG_TRACK(copy_mem_to_reg, part, tid,
                        (Addr)&VKI_UC_GUEST_CC_DEP2(uc),
                        offsetof(VexGuestAMD64State, guest_CC_DEP2),
                        sizeof(UWord));
            }
         }

         if (!ok_restore)
            VG_(debugLog)(1, "syswrap-solaris",
                             "Cannot fully restore the CC_* guest state "
                             "values, using approximate rflags in thread "
                             "%u.\n", tid);
      }
   }

   if (uc->uc_flags & VKI_UC_FPU) {
      /* FPU */
      VexEmNote note;
      SizeT i;

      /* x87 */
      /* Control word */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..cw)",
               (Addr)&fs->cw, sizeof(fs->cw));
      /* Status word */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..sw)",
               (Addr)&fs->sw, sizeof(fs->sw));
      /* Compressed tag word */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..fctw)",
               (Addr)&fs->fctw, sizeof(fs->fctw));
      /* Last x87 opcode */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..fop)",
               (Addr)&fs->fop, sizeof(fs->fop));
      /* Last x87 instruction pointer */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..rip)",
               (Addr)&fs->rip, sizeof(fs->rip));
      /* Last x87 data pointer */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..rdp)",
               (Addr)&fs->rdp, sizeof(fs->rdp));
      /* Media-instruction control and status register */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..mxcsr)",
               (Addr)&fs->mxcsr, sizeof(fs->mxcsr));
      /* Supported features in MXCSR */
      VG_TRACK(pre_mem_read, part, tid,
               "restore_machine_context(uc->uc_mcontext.fpregs..mxcsr_mask)",
               (Addr)&fs->mxcsr_mask, sizeof(fs->mxcsr_mask));

      /* ST registers */
      for (i = 0; i < 8; i++) {
         Addr addr = (Addr)&fs->st[i];
         VG_TRACK(copy_mem_to_reg, part, tid, addr,
                  offsetof(VexGuestAMD64State, guest_FPREG[i]), sizeof(ULong));
      }

      /* XMM registers */
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[0],
               offsetof(VexGuestAMD64State, guest_YMM0), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[1],
               offsetof(VexGuestAMD64State, guest_YMM1), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[2],
               offsetof(VexGuestAMD64State, guest_YMM2), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[3],
               offsetof(VexGuestAMD64State, guest_YMM3), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[4],
               offsetof(VexGuestAMD64State, guest_YMM4), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[5],
               offsetof(VexGuestAMD64State, guest_YMM5), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[6],
               offsetof(VexGuestAMD64State, guest_YMM6), sizeof(U128));
      VG_TRACK(copy_mem_to_reg, part, tid, (Addr)&fs->xmm[7],
               offsetof(VexGuestAMD64State, guest_YMM7), sizeof(U128));

      note = LibVEX_GuestAMD64_fxrstor((HWord)fs, &tst->arch.vex);
      if (note != EmNote_NONE)
         VG_(message)(Vg_UserMsg,
                      "Error restoring FP state in thread %u: %s.\n",
                      tid, LibVEX_EmNote_string(note));
   }
}


/* ---------------------------------------------------------------------
   PRE/POST wrappers for AMD64/Solaris-specific syscalls
   ------------------------------------------------------------------ */

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

/* implementation */

#undef PRE
#undef POST

#endif // defined(VGP_amd64_solaris)

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