/* When a client clones, we need to keep track of the new thread. This means:
   1. allocate a ThreadId+ThreadState+stack for the thread

   2. initialize the thread's new VCPU state

   3. create the thread using the same args as the client requested, but using
      the scheduler entrypoint for IP, and a separate stack for SP. */
static SysRes do_clone ( ThreadId ptid,
                         UInt flags, Addr sp,
                         Int* parent_tidptr,
                         Int* child_tidptr,
                         Addr child_tls )
{
   const Bool debug = False;
   ThreadId ctid = VG_ (alloc_ThreadState) ();
   ThreadState * ptst = VG_ (get_ThreadState) (ptid);
   ThreadState * ctst = VG_ (get_ThreadState) (ctid);
   UInt ret = 0;
   UWord * stack;
   SysRes res;
   vki_sigset_t blockall, savedmask;

   VG_(sigfillset)(&blockall);
   vg_assert(VG_(is_running_thread)(ptid));
   vg_assert(VG_(is_valid_tid)(ctid));
   stack = (UWord *)ML_(allocstack)(ctid);
   if (stack == NULL) {
      res = VG_(mk_SysRes_Error)(VKI_ENOMEM);
      goto out;
   }
   setup_child(&ctst->arch, &ptst->arch);

   /* on MIPS we need to set V0 and A3 to zero */
   ctst->arch.vex.guest_r2 = 0;
   ctst->arch.vex.guest_r7 = 0;
   if (sp != 0)
      ctst->arch.vex.guest_r29 = sp;

   ctst->os_state.parent = ptid;
   ctst->sig_mask = ptst->sig_mask;
   ctst->tmp_sig_mask = ptst->sig_mask;

   ctst->os_state.threadgroup = ptst->os_state.threadgroup;

   ML_(guess_and_register_stack) (sp, ctst);

   VG_TRACK(pre_thread_ll_create, ptid, ctid);
   if (flags & VKI_CLONE_SETTLS) {
       if (debug)
         VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
       res = sys_set_tls(ctid, child_tls);
       if (sr_isError(res))
          goto out;
       ctst->arch.vex.guest_r27 = child_tls;
   }

   flags &= ~VKI_CLONE_SETTLS;
   VG_ (sigprocmask) (VKI_SIG_SETMASK, &blockall, &savedmask);
   /* Create the new thread */
   ret = do_syscall_clone_mips64_linux(ML_(start_thread_NORETURN),
                                       stack, flags, &VG_(threads)[ctid],
                                       parent_tidptr, NULL /*child_tls*/,
                                       child_tidptr);
   if (debug)
     VG_(printf)("ret: 0x%x\n", ret);

   res = VG_(mk_SysRes_mips64_linux)( /* val */ ret, 0, /* errflag */ 0);

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

   out:
   if (sr_isError (res)) {
      VG_ (cleanup_thread) (&ctst->arch);
      ctst->status = VgTs_Empty;
      VG_TRACK (pre_thread_ll_exit, ctid);
   }
   ptst->arch.vex.guest_r2 = 0;

   return res;
}
/* When a client clones, we need to keep track of the new thread. This means:
   1. allocate a ThreadId+ThreadState+stack for the the thread

   2. initialize the thread's new VCPU state

   3. create the thread using the same args as the client requested, but using
      the scheduler entrypoint for IP, and a separate stack for SP. */
static SysRes do_clone ( ThreadId ptid,
                         UInt flags, Addr sp,
                         Int* parent_tidptr,
                         Int* child_tidptr,
                         Addr child_tls )
{
   const Bool debug = False;
   ThreadId ctid = VG_ (alloc_ThreadState) ();
   ThreadState * ptst = VG_ (get_ThreadState) (ptid);
   ThreadState * ctst = VG_ (get_ThreadState) (ctid);
   UInt ret = 0;
   UWord * stack;
   NSegment const *seg;
   SysRes res;
   vki_sigset_t blockall, savedmask;

   VG_(sigfillset)(&blockall);
   vg_assert(VG_(is_running_thread)(ptid));
   vg_assert(VG_(is_valid_tid)(ctid));
   stack = (UWord *)ML_(allocstack)(ctid);
   if (stack == NULL) {
      res = VG_(mk_SysRes_Error)(VKI_ENOMEM);
      goto out;
   }
   setup_child(&ctst->arch, &ptst->arch);

   /* on MIPS we need to set V0 and A3 to zero */
   ctst->arch.vex.guest_r2 = 0;
   ctst->arch.vex.guest_r7 = 0;
   if (sp != 0)
      ctst->arch.vex.guest_r29 = sp;

   ctst->os_state.parent = ptid;
   ctst->sig_mask = ptst->sig_mask;
   ctst->tmp_sig_mask = ptst->sig_mask;

   ctst->os_state.threadgroup = ptst->os_state.threadgroup;
   seg = VG_(am_find_nsegment)((Addr)sp);

   if (seg && seg->kind != SkResvn) {
      ctst->client_stack_highest_word = sp;
      ctst->client_stack_szB = ctst->client_stack_highest_word - seg->start;
      VG_(register_stack)(seg->start, ctst->client_stack_highest_word);
      if (debug)
        VG_(printf)("tid %d: guessed client stack range %#lx-%#lx\n",
                    ctid, seg->start, sp /* VG_PGROUNDUP (sp) */ );
   } else {
      VG_(message)(Vg_UserMsg,
                    "!? New thread %d starts with sp+%#lx) unmapped\n",
                    ctid, sp);
      ctst->client_stack_szB = 0;
   }

   VG_TRACK(pre_thread_ll_create, ptid, ctid);
   if (flags & VKI_CLONE_SETTLS) {
       if (debug)
         VG_(printf)("clone child has SETTLS: tls at %#lx\n", child_tls);
       res = sys_set_tls(ctid, child_tls);
       if (sr_isError(res))
          goto out;
       ctst->arch.vex.guest_r27 = child_tls;
   }

   flags &= ~VKI_CLONE_SETTLS;
   VG_ (sigprocmask) (VKI_SIG_SETMASK, &blockall, &savedmask);
   /* Create the new thread */
   ret = do_syscall_clone_mips64_linux(ML_(start_thread_NORETURN),
                                       stack, flags, &VG_(threads)[ctid],
                                       parent_tidptr, NULL /*child_tls*/,
                                       child_tidptr);
   if (debug)
     VG_(printf)("ret: 0x%x\n", ret);

   res = VG_(mk_SysRes_mips64_linux)( /* val */ ret, 0, /* errflag */ 0);

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

   out:
   if (sr_isError (res)) {
      VG_ (cleanup_thread) (&ctst->arch);
      ctst->status = VgTs_Empty;
      VG_TRACK (pre_thread_ll_exit, ctid);
   }
   ptst->arch.vex.guest_r2 = 0;

   return res;
}