/* Tests that the process can exit even if daemon thread is still running.
   This test does *not* use any libc; it interfaces only with kernel. */

#include <sys/lwp.h>
#include <sys/mman.h>
#include <sys/regset.h>
#include <sys/syscall.h>
#include <sys/ucontext.h>

#if defined(__amd64) || defined(__i386)
#include <sys/segments.h>
#endif

extern void bzero(void *ptr, size_t n);

#if defined(VGP_x86_solaris)
asm("\n"
".text\n"
".globl bzero\n"
"bzero:\n"
"   push %edi\n"
"   movl $0, %eax\n"
"   movl 12(%esp), %ecx\n"
"   movl 8(%esp), %edi\n"
"   rep  stosb\n"
"   pop  %edi\n"
"   ret\n"
);
#elif defined(VGP_amd64_solaris)
asm("\n"
".text\n"
".globl bzero\n"
"bzero:\n"
"   push %rdi\n"
"   movq %rsi, %rcx\n"
"   movq $0, %rax\n"
"   rep  stosb\n"
"   pop  %rdi\n"
"   ret\n"
);
#else
#  error "Unknown platform"
#endif

static void sleep(unsigned int sec) {
   timespec_t ts;
   ts.tv_sec = (time_t)sec;
   ts.tv_nsec = 0;

#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "pushl $0\n"
      "pushl %[TS]\n"
      "pushl $0xdeadbeef\n"
      "movl  %[SYSNO], %%eax\n"
      "int   $0x91\n"
      "addl  $12, %%esp\n"
      :
      : [TS] "g" (&ts), [SYSNO] "n" (SYS_nanosleep)
      : "eax", "edx", "cc", "memory");
#elif defined(VGP_amd64_solaris)
   __asm__ __volatile__ (
      "movq %[SYSNO], %%rax\n"
      "movq %[TS], %%rdi\n"
      "movq $0, %%rsi\n"
      "syscall\n"
      :
      : [TS] "g" (&ts), [SYSNO] "n" (SYS_nanosleep)
      : "rax", "rdx", "rdi", "rsi", "cc", "memory");
#else
#  error "Unknown platform"
#endif
}

static void lwp_exit(void) {
#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "movl %[SYSNO], %%eax\n"
      "int  $0x91\n"
      :
      : [SYSNO] "n" (SYS_lwp_exit)
      : "eax", "edx", "cc", "memory");
#elif defined(VGP_amd64_solaris)
   __asm__ __volatile__ (
      "movq %[SYSNO], %%rax\n"
      "syscall\n"
      :
      : [SYSNO] "n" (SYS_lwp_exit)
      : "rax", "rdx", "cc", "memory");
#else
#  error "Unknown platform"
#endif
}

#define STACK_FLAGS (MAP_PRIVATE | MAP_NORESERVE | MAP_ANON)
#define STACK_PROT  (PROT_READ | PROT_WRITE)
static void *allocate_stack(size_t stacksize) {
   void *address = NULL;

#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "pushl $0\n"
      "pushl $-1\n"
      "pushl %[FLAGS]\n"
      "pushl %[PROT]\n"
      "pushl %[SIZE]\n"
      "pushl $0\n"
      "pushl $0xdeadbeef\n"
      "movl  %[SYSNO], %%eax\n"
      "int   $0x91\n"
      "addl  $28, %%esp\n"
      "movl %%eax, %[ADDRESS]\n"
      : [ADDRESS] "=r" (address)
      : [FLAGS] "n" (STACK_FLAGS), [PROT] "n" (STACK_PROT),
        [SIZE] "g" (stacksize), [SYSNO] "n" (SYS_mmap)
      : "eax", "edx", "cc", "memory");
#elif defined(VGP_amd64_solaris)
   __asm__ __volatile__ (
      "movq %[SYSNO], %%rax\n"
      "movq $0, %%rdi\n"
      "movq %[SIZE], %%rsi\n"
      "movq %[PROT], %%rdx\n"
      "movq %[FLAGS], %%r10\n"
      "movq $-1, %%r8\n"
      "movq $0, %%r9\n"
      "syscall\n"
      "movq %%rax, %[ADDRESS]\n"
      : [ADDRESS] "=r" (address)
      : [FLAGS] "n" (STACK_FLAGS), [PROT] "n" (STACK_PROT),
        [SIZE] "g" (stacksize), [SYSNO] "n" (SYS_mmap)
      : "rax", "rdx", "rdi", "rsi", "r10", "r8", "r9", "cc", "memory");
#else
#  error "Unknown platform"
#endif

   return address;
}
#undef STACK_FLAGS
#undef STACK_PROT

static void thread_func(void) {
   sleep(10000);
}

#define LWP_FLAGS (LWP_SUSPENDED | LWP_DETACHED | LWP_DAEMON)
static id_t lwp_create(void *stack) {
   id_t tid;

   ucontext_t ucontext;
   bzero(&ucontext, sizeof(ucontext));
   ucontext.uc_flags = UC_CPU;

#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "mov %%ss, %[STACK_SEG]\n"
      : [STACK_SEG] "=r" (ucontext.uc_mcontext.gregs[SS])
      :
      :);
   ucontext.uc_mcontext.gregs[EIP] = (greg_t) thread_func;
   ucontext.uc_mcontext.gregs[UESP] = (greg_t) stack;
   ucontext.uc_mcontext.gregs[EBP] = (greg_t) stack;
#elif defined(VGP_amd64_solaris)
   ucontext.uc_mcontext.gregs[REG_SS] = UDS_SEL;
   ucontext.uc_mcontext.gregs[REG_RIP] = (greg_t) thread_func;
   ucontext.uc_mcontext.gregs[REG_RSP] = (greg_t) stack;
   ucontext.uc_mcontext.gregs[REG_RBP] = (greg_t) stack;
#else
#  error "Unknown platform"
#endif

#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "pushl $0\n"
      "pushl %[FLAGS]\n"
      "pushl %[UCONTEXT]\n"
      "pushl $0xdeadbeef\n"
      "movl  %[SYSNO], %%eax\n"
      "int   $0x91\n"
      "addl  $16, %%esp\n"
      "movl %%eax, %[TID]\n"
      : [TID] "=r" (tid)
      : [FLAGS] "n" (LWP_FLAGS), [UCONTEXT] "g" (&ucontext),
        [SYSNO] "n" (SYS_lwp_create)
      : "eax", "edx", "cc", "memory");
#elif defined(VGP_amd64_solaris)
   __asm__ __volatile__ (
      "movq %[SYSNO], %%rax\n"
      "movq %[UCONTEXT], %%rdi\n"
      "movq %[FLAGS], %%rsi\n"
      "movq $0, %%rdx\n"
      "syscall\n"
      "movl %%eax, %[TID]\n"
      : [TID] "=r" (tid)
      : [FLAGS] "n" (LWP_FLAGS), [UCONTEXT] "g" (&ucontext),
        [SYSNO] "n" (SYS_lwp_create)
      : "rax", "rdx", "rdi", "rsi", "cc", "memory");
#else
#  error "Unknown platform"
#endif

   return tid;
}

static void lwp_continue(id_t tid) {
#if defined(VGP_x86_solaris)
   __asm__ __volatile__ (
      "pushl %[TID]\n"
      "pushl $0xdeadbeef\n"
      "movl  %[SYSNO], %%eax\n"
      "int   $0x91\n"
      "addl  $8, %%esp\n"
      :
      : [TID] "m" (tid), [SYSNO] "n" (SYS_lwp_continue)
      : "eax", "edx", "cc", "memory");
#elif defined(VGP_amd64_solaris)
   __asm__ __volatile__ (
      "movq %[SYSNO], %%rax\n"
      "xor %%rdi, %%rdi\n"
      "movl %[TID], %%edi\n"
      "syscall\n"
      :
      : [TID] "r" (tid), [SYSNO] "n" (SYS_lwp_continue)
      : "rax", "rdx", "rdi", "cc", "memory");
#else
#  error "Unknown platform"
#endif
}

#define STACK_SIZE 16384
void _start(void) {
   void *stack = allocate_stack(STACK_SIZE);
   id_t tid = lwp_create((char *) stack + STACK_SIZE);
   lwp_continue(tid);
   sleep(5);
   lwp_exit();
   return; /* not reached */
}

