/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
#include "libc/str/str.h"

/**
 * @fileoverview XNU kernel callback normalization.
 */

union __darwin_sigval {
  int32_t sival_int;
  void *sival_ptr;
};

struct __darwin_siginfo {
  int32_t si_signo;
  int32_t si_errno;
  int32_t si_code;
  int32_t si_pid;
  uint32_t si_uid;
  int32_t si_status;
  void *si_addr;
  union __darwin_sigval si_value;
  int64_t si_band;
  uint64_t __pad[7];
};

struct __darwin_sigaltstack {
  void *ss_sp;
  uint64_t ss_size;
  int32_t ss_flags;
};

struct __darwin_mmst_reg {
  char __mmst_reg[10];
  char __mmst_rsrv[6];
};

struct __darwin_xmm_reg {
  char __xmm_reg[16];
};

struct __darwin_ymm_reg {
  char __ymm_reg[32];
};

struct __darwin_zmm_reg {
  char __zmm_reg[64];
};

struct __darwin_opmask_reg {
  char __opmask_reg[8];
};

struct __darwin_x86_thread_state64 {
  uint64_t __rax;
  uint64_t __rbx;
  uint64_t __rcx;
  uint64_t __rdx;
  uint64_t __rdi;
  uint64_t __rsi;
  uint64_t __rbp;
  uint64_t __rsp;
  uint64_t __r8;
  uint64_t __r9;
  uint64_t __r10;
  uint64_t __r11;
  uint64_t __r12;
  uint64_t __r13;
  uint64_t __r14;
  uint64_t __r15;
  uint64_t __rip;
  uint64_t __rflags;
  uint64_t __cs;
  uint64_t __fs;
  uint64_t __gs;
};

struct __darwin_x86_thread_full_state64 {
  struct __darwin_x86_thread_state64 ss64;
  uint64_t __ds;
  uint64_t __es;
  uint64_t __ss;
  uint64_t __gsbase;
};

struct __darwin_x86_float_state64 {
  int32_t __fpu_reserved[2];
  uint16_t __fpu_fcw;
  uint16_t __fpu_fsw;
  uint8_t __fpu_ftw;
  uint8_t __fpu_rsrv1;
  uint16_t __fpu_fop;
  uint32_t __fpu_ip;
  uint16_t __fpu_cs;
  uint16_t __fpu_rsrv2;
  uint32_t __fpu_dp;
  uint16_t __fpu_ds;
  uint16_t __fpu_rsrv3;
  uint32_t __fpu_mxcsr;
  uint32_t __fpu_mxcsrmask;
  struct __darwin_mmst_reg __fpu_stmm0;
  struct __darwin_mmst_reg __fpu_stmm1;
  struct __darwin_mmst_reg __fpu_stmm2;
  struct __darwin_mmst_reg __fpu_stmm3;
  struct __darwin_mmst_reg __fpu_stmm4;
  struct __darwin_mmst_reg __fpu_stmm5;
  struct __darwin_mmst_reg __fpu_stmm6;
  struct __darwin_mmst_reg __fpu_stmm7;
  struct __darwin_xmm_reg __fpu_xmm0;
  struct __darwin_xmm_reg __fpu_xmm1;
  struct __darwin_xmm_reg __fpu_xmm2;
  struct __darwin_xmm_reg __fpu_xmm3;
  struct __darwin_xmm_reg __fpu_xmm4;
  struct __darwin_xmm_reg __fpu_xmm5;
  struct __darwin_xmm_reg __fpu_xmm6;
  struct __darwin_xmm_reg __fpu_xmm7;
  struct __darwin_xmm_reg __fpu_xmm8;
  struct __darwin_xmm_reg __fpu_xmm9;
  struct __darwin_xmm_reg __fpu_xmm10;
  struct __darwin_xmm_reg __fpu_xmm11;
  struct __darwin_xmm_reg __fpu_xmm12;
  struct __darwin_xmm_reg __fpu_xmm13;
  struct __darwin_xmm_reg __fpu_xmm14;
  struct __darwin_xmm_reg __fpu_xmm15;
  char __fpu_rsrv4[96];
  int32_t __fpu_reserved1;
};

struct __darwin_x86_avx_state64 {
  int32_t __fpu_reserved[2];
  uint16_t __fpu_fcw;
  uint16_t __fpu_fsw;
  uint8_t __fpu_ftw;
  uint8_t __fpu_rsrv1;
  uint16_t __fpu_fop;
  uint32_t __fpu_ip;
  uint16_t __fpu_cs;
  uint16_t __fpu_rsrv2;
  uint32_t __fpu_dp;
  uint16_t __fpu_ds;
  uint16_t __fpu_rsrv3;
  uint32_t __fpu_mxcsr;
  uint32_t __fpu_mxcsrmask;
  struct __darwin_mmst_reg __fpu_stmm0;
  struct __darwin_mmst_reg __fpu_stmm1;
  struct __darwin_mmst_reg __fpu_stmm2;
  struct __darwin_mmst_reg __fpu_stmm3;
  struct __darwin_mmst_reg __fpu_stmm4;
  struct __darwin_mmst_reg __fpu_stmm5;
  struct __darwin_mmst_reg __fpu_stmm6;
  struct __darwin_mmst_reg __fpu_stmm7;
  struct __darwin_xmm_reg __fpu_xmm0;
  struct __darwin_xmm_reg __fpu_xmm1;
  struct __darwin_xmm_reg __fpu_xmm2;
  struct __darwin_xmm_reg __fpu_xmm3;
  struct __darwin_xmm_reg __fpu_xmm4;
  struct __darwin_xmm_reg __fpu_xmm5;
  struct __darwin_xmm_reg __fpu_xmm6;
  struct __darwin_xmm_reg __fpu_xmm7;
  struct __darwin_xmm_reg __fpu_xmm8;
  struct __darwin_xmm_reg __fpu_xmm9;
  struct __darwin_xmm_reg __fpu_xmm10;
  struct __darwin_xmm_reg __fpu_xmm11;
  struct __darwin_xmm_reg __fpu_xmm12;
  struct __darwin_xmm_reg __fpu_xmm13;
  struct __darwin_xmm_reg __fpu_xmm14;
  struct __darwin_xmm_reg __fpu_xmm15;
  char __fpu_rsrv4[6 * 16];
  int32_t __fpu_reserved1;
  char __avx_reserved1[64];
  struct __darwin_xmm_reg __fpu_ymmh0;
  struct __darwin_xmm_reg __fpu_ymmh1;
  struct __darwin_xmm_reg __fpu_ymmh2;
  struct __darwin_xmm_reg __fpu_ymmh3;
  struct __darwin_xmm_reg __fpu_ymmh4;
  struct __darwin_xmm_reg __fpu_ymmh5;
  struct __darwin_xmm_reg __fpu_ymmh6;
  struct __darwin_xmm_reg __fpu_ymmh7;
  struct __darwin_xmm_reg __fpu_ymmh8;
  struct __darwin_xmm_reg __fpu_ymmh9;
  struct __darwin_xmm_reg __fpu_ymmh10;
  struct __darwin_xmm_reg __fpu_ymmh11;
  struct __darwin_xmm_reg __fpu_ymmh12;
  struct __darwin_xmm_reg __fpu_ymmh13;
  struct __darwin_xmm_reg __fpu_ymmh14;
  struct __darwin_xmm_reg __fpu_ymmh15;
};

struct __darwin_x86_avx512_state64 {
  int32_t __fpu_reserved[2];
  uint16_t __fpu_fcw;
  uint16_t __fpu_fsw;
  uint8_t __fpu_ftw;
  uint8_t __fpu_rsrv1;
  uint16_t __fpu_fop;
  uint32_t __fpu_ip;
  uint16_t __fpu_cs;
  uint16_t __fpu_rsrv2;
  uint32_t __fpu_dp;
  uint16_t __fpu_ds;
  uint16_t __fpu_rsrv3;
  uint32_t __fpu_mxcsr;
  uint32_t __fpu_mxcsrmask;
  struct __darwin_mmst_reg __fpu_stmm0;
  struct __darwin_mmst_reg __fpu_stmm1;
  struct __darwin_mmst_reg __fpu_stmm2;
  struct __darwin_mmst_reg __fpu_stmm3;
  struct __darwin_mmst_reg __fpu_stmm4;
  struct __darwin_mmst_reg __fpu_stmm5;
  struct __darwin_mmst_reg __fpu_stmm6;
  struct __darwin_mmst_reg __fpu_stmm7;
  struct __darwin_xmm_reg __fpu_xmm0;
  struct __darwin_xmm_reg __fpu_xmm1;
  struct __darwin_xmm_reg __fpu_xmm2;
  struct __darwin_xmm_reg __fpu_xmm3;
  struct __darwin_xmm_reg __fpu_xmm4;
  struct __darwin_xmm_reg __fpu_xmm5;
  struct __darwin_xmm_reg __fpu_xmm6;
  struct __darwin_xmm_reg __fpu_xmm7;
  struct __darwin_xmm_reg __fpu_xmm8;
  struct __darwin_xmm_reg __fpu_xmm9;
  struct __darwin_xmm_reg __fpu_xmm10;
  struct __darwin_xmm_reg __fpu_xmm11;
  struct __darwin_xmm_reg __fpu_xmm12;
  struct __darwin_xmm_reg __fpu_xmm13;
  struct __darwin_xmm_reg __fpu_xmm14;
  struct __darwin_xmm_reg __fpu_xmm15;
  char __fpu_rsrv4[6 * 16];
  int32_t __fpu_reserved1;
  char __avx_reserved1[64];
  struct __darwin_xmm_reg __fpu_ymmh0;
  struct __darwin_xmm_reg __fpu_ymmh1;
  struct __darwin_xmm_reg __fpu_ymmh2;
  struct __darwin_xmm_reg __fpu_ymmh3;
  struct __darwin_xmm_reg __fpu_ymmh4;
  struct __darwin_xmm_reg __fpu_ymmh5;
  struct __darwin_xmm_reg __fpu_ymmh6;
  struct __darwin_xmm_reg __fpu_ymmh7;
  struct __darwin_xmm_reg __fpu_ymmh8;
  struct __darwin_xmm_reg __fpu_ymmh9;
  struct __darwin_xmm_reg __fpu_ymmh10;
  struct __darwin_xmm_reg __fpu_ymmh11;
  struct __darwin_xmm_reg __fpu_ymmh12;
  struct __darwin_xmm_reg __fpu_ymmh13;
  struct __darwin_xmm_reg __fpu_ymmh14;
  struct __darwin_xmm_reg __fpu_ymmh15;
  struct __darwin_opmask_reg __fpu_k0;
  struct __darwin_opmask_reg __fpu_k1;
  struct __darwin_opmask_reg __fpu_k2;
  struct __darwin_opmask_reg __fpu_k3;
  struct __darwin_opmask_reg __fpu_k4;
  struct __darwin_opmask_reg __fpu_k5;
  struct __darwin_opmask_reg __fpu_k6;
  struct __darwin_opmask_reg __fpu_k7;
  struct __darwin_ymm_reg __fpu_zmmh0;
  struct __darwin_ymm_reg __fpu_zmmh1;
  struct __darwin_ymm_reg __fpu_zmmh2;
  struct __darwin_ymm_reg __fpu_zmmh3;
  struct __darwin_ymm_reg __fpu_zmmh4;
  struct __darwin_ymm_reg __fpu_zmmh5;
  struct __darwin_ymm_reg __fpu_zmmh6;
  struct __darwin_ymm_reg __fpu_zmmh7;
  struct __darwin_ymm_reg __fpu_zmmh8;
  struct __darwin_ymm_reg __fpu_zmmh9;
  struct __darwin_ymm_reg __fpu_zmmh10;
  struct __darwin_ymm_reg __fpu_zmmh11;
  struct __darwin_ymm_reg __fpu_zmmh12;
  struct __darwin_ymm_reg __fpu_zmmh13;
  struct __darwin_ymm_reg __fpu_zmmh14;
  struct __darwin_ymm_reg __fpu_zmmh15;
  struct __darwin_zmm_reg __fpu_zmm16;
  struct __darwin_zmm_reg __fpu_zmm17;
  struct __darwin_zmm_reg __fpu_zmm18;
  struct __darwin_zmm_reg __fpu_zmm19;
  struct __darwin_zmm_reg __fpu_zmm20;
  struct __darwin_zmm_reg __fpu_zmm21;
  struct __darwin_zmm_reg __fpu_zmm22;
  struct __darwin_zmm_reg __fpu_zmm23;
  struct __darwin_zmm_reg __fpu_zmm24;
  struct __darwin_zmm_reg __fpu_zmm25;
  struct __darwin_zmm_reg __fpu_zmm26;
  struct __darwin_zmm_reg __fpu_zmm27;
  struct __darwin_zmm_reg __fpu_zmm28;
  struct __darwin_zmm_reg __fpu_zmm29;
  struct __darwin_zmm_reg __fpu_zmm30;
  struct __darwin_zmm_reg __fpu_zmm31;
};

struct __darwin_x86_exception_state64 {
  uint16_t __trapno;
  uint16_t __cpu;
  uint32_t __err;
  uint64_t __faultvaddr;
};

struct __darwin_x86_debug_state64 {
  uint64_t __dr0;
  uint64_t __dr1;
  uint64_t __dr2;
  uint64_t __dr3;
  uint64_t __dr4;
  uint64_t __dr5;
  uint64_t __dr6;
  uint64_t __dr7;
};

struct __darwin_x86_cpmu_state64 {
  uint64_t __ctrs[16];
};

struct __darwin_mcontext64_full {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_full_state64 __ss;
  struct __darwin_x86_float_state64 __fs;
};

struct __darwin_mcontext_avx64 {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_state64 __ss;
  struct __darwin_x86_avx_state64 __fs;
};

struct __darwin_mcontext_avx64_full {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_full_state64 __ss;
  struct __darwin_x86_avx_state64 __fs;
};

struct __darwin_mcontext_avx512_64 {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_state64 __ss;
  struct __darwin_x86_avx512_state64 __fs;
};

struct __darwin_mcontext_avx512_64_full {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_full_state64 __ss;
  struct __darwin_x86_avx512_state64 __fs;
};

struct __darwin_mcontext64 {
  struct __darwin_x86_exception_state64 __es;
  struct __darwin_x86_thread_state64 __ss;
  struct __darwin_x86_float_state64 __fs;
};

struct __darwin_ucontext {
  int32_t uc_onstack;
  uint32_t uc_sigmask;
  struct __darwin_sigaltstack uc_stack;
  struct __darwin_ucontext *uc_link;
  uint64_t uc_mcsize;
  struct __darwin_mcontext64 *uc_mcontext;
};

noasan static void xnuexceptionstate2linux(
    mcontext_t *mc, struct __darwin_x86_exception_state64 *xnues) {
  mc->trapno = xnues->__trapno;
  mc->err = xnues->__err;
}

noasan static void linuxexceptionstate2xnu(
    struct __darwin_x86_exception_state64 *xnues, mcontext_t *mc) {
  xnues->__trapno = mc->trapno;
  xnues->__err = mc->err;
}

noasan static void xnuthreadstate2linux(
    ucontext_t *uc, mcontext_t *mc, struct __darwin_x86_thread_state64 *xnuss) {
  mc->rdi = xnuss->__rdi;
  mc->rsi = xnuss->__rsi;
  mc->rbp = xnuss->__rbp;
  mc->rbx = xnuss->__rbx;
  mc->rdx = xnuss->__rdx;
  mc->rax = xnuss->__rax;
  mc->rcx = xnuss->__rcx;
  mc->rsp = xnuss->__rsp;
  mc->rip = xnuss->__rip;
  mc->cs = xnuss->__cs;
  mc->gs = xnuss->__gs;
  mc->fs = xnuss->__fs;
  mc->eflags = xnuss->__rflags;
  uc->uc_flags = xnuss->__rflags;
  memcpy(&mc->r8, &xnuss->__r8, 8 * sizeof(int64_t));
}

noasan static void linuxthreadstate2xnu(
    struct __darwin_x86_thread_state64 *xnuss, ucontext_t *uc, mcontext_t *mc) {
  xnuss->__rdi = mc->rdi;
  xnuss->__rsi = mc->rsi;
  xnuss->__rbp = mc->rbp;
  xnuss->__rbx = mc->rbx;
  xnuss->__rdx = mc->rdx;
  xnuss->__rax = mc->rax;
  xnuss->__rcx = mc->rcx;
  xnuss->__rsp = mc->rsp;
  xnuss->__rip = mc->rip;
  xnuss->__cs = mc->cs;
  xnuss->__gs = mc->gs;
  xnuss->__fs = mc->fs;
  xnuss->__rflags = mc->eflags;
  xnuss->__rflags = uc->uc_flags;
  memcpy(&xnuss->__r8, &mc->r8, 8 * sizeof(int64_t));
}

noasan static void xnussefpustate2linux(
    struct FpuState *fs, struct __darwin_x86_float_state64 *xnufs) {
  fs->cwd = xnufs->__fpu_fcw;
  fs->swd = xnufs->__fpu_fsw;
  fs->ftw = xnufs->__fpu_ftw;
  fs->fop = xnufs->__fpu_fop;
  fs->rip = xnufs->__fpu_ip;
  fs->rdp = xnufs->__fpu_dp;
  fs->mxcsr = xnufs->__fpu_mxcsr;
  fs->mxcr_mask = xnufs->__fpu_mxcsrmask;
  /* copy st0-st7 as well as xmm0-xmm15 */
  memcpy(fs->st, &xnufs->__fpu_stmm0, (8 + 16) * sizeof(uint128_t));
}

noasan static void linuxssefpustate2xnu(
    struct __darwin_x86_float_state64 *xnufs, struct FpuState *fs) {
  xnufs->__fpu_fcw = fs->cwd;
  xnufs->__fpu_fsw = fs->swd;
  xnufs->__fpu_ftw = fs->ftw;
  xnufs->__fpu_fop = fs->fop;
  xnufs->__fpu_ip = fs->rip;
  xnufs->__fpu_dp = fs->rdp;
  xnufs->__fpu_mxcsr = fs->mxcsr;
  xnufs->__fpu_mxcsrmask = fs->mxcr_mask;
  /* copy st0-st7 as well as xmm0-xmm15 */
  memcpy(&xnufs->__fpu_stmm0, fs->st, (8 + 16) * sizeof(uint128_t));
}

noasan void __sigenter_xnu(void *fn, int infostyle, int sig,
                           struct __darwin_siginfo *xnuinfo,
                           struct __darwin_ucontext *xnuctx) {
  int rva;
  intptr_t ax;
  struct Goodies {
    ucontext_t uc;
    siginfo_t si;
  } g;
  rva = __sighandrvas[sig & (NSIG - 1)];
  if (rva >= kSigactionMinRva) {
    memset(&g, 0, sizeof(g));
    if (xnuctx) {
      memcpy(&g.uc.uc_sigmask, &xnuctx->uc_sigmask,
             MIN(sizeof(g.uc.uc_sigmask), sizeof(xnuctx->uc_sigmask)));
      g.uc.uc_stack.ss_sp = xnuctx->uc_stack.ss_sp;
      g.uc.uc_stack.ss_flags = xnuctx->uc_stack.ss_flags;
      g.uc.uc_stack.ss_size = xnuctx->uc_stack.ss_size;
      g.uc.uc_mcontext.fpregs = &g.uc.__fpustate;
      if (xnuctx->uc_mcontext) {
        if (xnuctx->uc_mcsize >=
            sizeof(struct __darwin_x86_exception_state64)) {
          xnuexceptionstate2linux(&g.uc.uc_mcontext,
                                  &xnuctx->uc_mcontext->__es);
        }
        if (xnuctx->uc_mcsize >=
            (sizeof(struct __darwin_x86_exception_state64) +
             sizeof(struct __darwin_x86_thread_state64))) {
          xnuthreadstate2linux(&g.uc, &g.uc.uc_mcontext,
                               &xnuctx->uc_mcontext->__ss);
        }
        if (xnuctx->uc_mcsize >= sizeof(struct __darwin_mcontext64)) {
          xnussefpustate2linux(&g.uc.__fpustate, &xnuctx->uc_mcontext->__fs);
        }
      }
    }
    if (xnuinfo) {
      g.si.si_signo = xnuinfo->si_signo;
      g.si.si_errno = xnuinfo->si_errno;
      g.si.si_code = xnuinfo->si_code;
      if (xnuinfo->si_pid) {
        g.si.si_pid = xnuinfo->si_pid;
        g.si.si_uid = xnuinfo->si_uid;
        g.si.si_status = xnuinfo->si_status;
      } else {
        g.si.si_addr = (void *)xnuinfo->si_addr;
      }
    }
    ((sigaction_f)(_base + rva))(sig, &g.si, &g.uc);
    if (xnuctx) {
      xnuctx->uc_stack.ss_sp = g.uc.uc_stack.ss_sp;
      xnuctx->uc_stack.ss_flags = g.uc.uc_stack.ss_flags;
      xnuctx->uc_stack.ss_size = g.uc.uc_stack.ss_size;
      if (xnuctx->uc_mcontext) {
        if (xnuctx->uc_mcsize >=
            sizeof(struct __darwin_x86_exception_state64)) {
          linuxexceptionstate2xnu(&xnuctx->uc_mcontext->__es,
                                  &g.uc.uc_mcontext);
        }
        if (xnuctx->uc_mcsize >=
            (sizeof(struct __darwin_x86_exception_state64) +
             sizeof(struct __darwin_x86_thread_state64))) {
          linuxthreadstate2xnu(&xnuctx->uc_mcontext->__ss, &g.uc,
                               &g.uc.uc_mcontext);
        }
        if (xnuctx->uc_mcsize >= sizeof(struct __darwin_mcontext64)) {
          linuxssefpustate2xnu(&xnuctx->uc_mcontext->__fs, &g.uc.__fpustate);
        }
      }
    }
  }
  asm volatile("syscall"
               : "=a"(ax)
               : "0"(0x20000b8 /* sigreturn */), "D"(xnuctx), "S"(infostyle)
               : "rcx", "r11", "memory", "cc");
  unreachable;
}
