unit machdep;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 signal,
 signalvar,
 ucontext,
 kern_thr;

const
 _ucodesel=(8 shl 3) or 3;
 _udatasel=(7 shl 3) or 3;
 _ufssel  =(2 shl 3) or 3;
 _ugssel  =(3 shl 3) or 3;

procedure cpu_set_syscall_retval(td:p_kthread;error:Integer);
procedure sendsig(catcher:sig_t;ksi:p_ksiginfo;mask:p_sigset_t);
function  sys_sigreturn(sigcntxp:p_ucontext_t):Integer;

implementation

uses
 errno,
 systm,
 md_psl,
 kern_sig,
 trap;

procedure cpu_set_syscall_retval(td:p_kthread;error:Integer);
begin
 Case error of
  0:With td^.td_frame^ do
    begin
     tf_rax:=td^.td_retval[0];
     tf_rdx:=td^.td_retval[1];
     tf_rflags:=tf_rflags and (not PSL_C);
    end;
  ERESTART:
    With td^.td_frame^ do
    begin
     //tf_err = size of syscall cmd
     tf_rip:=tf_rip-td^.td_frame^.tf_err;
     tf_r10:=tf_rcx;
     set_pcb_flags(td,PCB_FULL_IRET);
    end;
  EJUSTRETURN:; //nothing
  else
    With td^.td_frame^ do
    begin
     tf_rax:=error;
     tf_rflags:=tf_rflags or PSL_C;
    end;
 end;
end;

function get_fpcontext(td:p_kthread;mcp:p_mcontext_t;xstate:Pointer):Integer;
begin
 Result:=0;

 mcp^.mc_flags   :=mcp^.mc_flags or _MC_HASFPXSTATE;
 mcp^.mc_fpformat:=_MC_FPFMT_XMM;
 mcp^.mc_ownedfp :=_MC_FPOWNED_FPU;

 Move(td^.td_fpstate,xstate^,SizeOf(mcp^.mc_fpstate));
end;

function set_fpcontext(td:p_kthread;mcp:p_mcontext_t;xstate:Pointer):Integer;
begin
 Result:=0;

 if (mcp^.mc_fpformat=_MC_FPFMT_NODEV) then Exit(0);

 if (mcp^.mc_fpformat<>_MC_FPFMT_XMM) then Exit(EINVAL);

 if (mcp^.mc_ownedfp=_MC_FPOWNED_NONE) then Exit(0);

 if (mcp^.mc_ownedfp=_MC_FPOWNED_FPU) or
    (mcp^.mc_ownedfp=_MC_FPOWNED_PCB) then
 begin
  Move(xstate^,td^.td_fpstate,SizeOf(mcp^.mc_fpstate));
  Exit(0);
 end;

 Exit(EINVAL);
end;

procedure sendsig(catcher:sig_t;ksi:p_ksiginfo;mask:p_sigset_t);
var
 td:p_kthread;
 sf:sigframe;
 sfp:p_sigframe;
 regs:p_trapframe;
 sp:QWORD;
 sig,oonstack:Integer;
begin
 td:=curkthread;

 sig:=ksi^.ksi_info.si_signo;

 regs:=td^.td_frame;
 oonstack:=sigonstack(regs^.tf_rsp);

 // Save user context.
 sf:=Default(sigframe);

 sf.sf_uc.uc_sigmask:=mask^;
 sf.sf_uc.uc_stack  :=td^.td_sigstk;

 if ((td^.td_pflags and TDP_ALTSTACK)<>0) then
 begin
  if (oonstack<>0) then
  begin
   sf.sf_uc.uc_stack.ss_flags:=SS_ONSTACK;
  end else
  begin
   sf.sf_uc.uc_stack.ss_flags:=0;
  end;
 end else
 begin
  sf.sf_uc.uc_stack.ss_flags:=SS_DISABLE;
 end;

 sf.sf_uc.uc_mcontext.mc_onstack:=oonstack;

 //set segs
 regs^.tf_cs:=_ucodesel;
 regs^.tf_ds:=_udatasel;
 regs^.tf_ss:=_udatasel;
 regs^.tf_es:=_udatasel;
 regs^.tf_fs:=_ufssel;
 regs^.tf_gs:=_ugssel;
 regs^.tf_flags:=regs^.tf_flags or TF_HASSEGS;
 //set segs

 Move(regs^,sf.sf_uc.uc_mcontext.mc_rdi,SizeOf(trapframe));

 sf.sf_uc.uc_mcontext.mc_len:=sizeof(mcontext_t);

 //xmm,ymm
 if ((regs^.tf_flags and TF_HASFPXSTATE)<>0) then
 begin
  get_fpcontext(td,@sf.sf_uc.uc_mcontext,@sf.sf_uc.uc_mcontext.mc_fpstate);

  regs^.tf_flags:=regs^.tf_flags and (not TF_HASFPXSTATE);
 end;
 //xmm,ymm

 sf.sf_uc.uc_mcontext.mc_flags :=sf.sf_uc.uc_mcontext.mc_flags or _MC_HASBASES;
 sf.sf_uc.uc_mcontext.mc_fsbase:=QWORD(td^.pcb_fsbase);
 sf.sf_uc.uc_mcontext.mc_gsbase:=QWORD(td^.pcb_gsbase);

 if ((td^.td_pflags and TDP_ALTSTACK)<>0) and
    (oonstack=0) and
    SIGISMEMBER(@p_sigacts.ps_sigonstack,sig) then
 begin
  sp:=QWORD(td^.td_sigstk.ss_sp)+td^.td_sigstk.ss_size;
 end else
 begin
  sp:=regs^.tf_rsp-128;
 end;

 sp:=sp-sizeof(sigframe);

 sfp:=p_sigframe(sp and (not $1F));

 regs^.tf_rdi:=sig;
 regs^.tf_rdx:=QWORD(@sfp^.sf_uc);

 if (SIGISMEMBER(@p_sigacts.ps_siginfo,sig)) then
 begin
  regs^.tf_rsi:=QWORD(@sfp^.sf_si);
  sf.sf_ahu:=Pointer(catcher);

  sf.sf_si:=ksi^.ksi_info;
  sf.sf_si.si_signo:=sig;

  regs^.tf_rcx:=QWORD(ksi^.ksi_info.si_addr);
 end else
 begin
  regs^.tf_rsi:=ksi^.ksi_info.si_code;
  regs^.tf_rcx:=QWORD(ksi^.ksi_info.si_addr);
  sf.sf_ahu:=Pointer(catcher);
 end;

 ps_mtx_unlock;
 PROC_UNLOCK;

 if (copyout(@sf,sfp,sizeof(sigframe))<>0) then
 begin
  PROC_LOCK;
  sigexit(td,SIGILL);
 end;

 regs^.tf_rsp:=QWORD(sfp);
 regs^.tf_rip:=QWORD(@sigcode);
 regs^.tf_rflags:=regs^.tf_rflags and (not (PSL_T or PSL_D));

 set_pcb_flags(td,PCB_FULL_IRET);
 PROC_LOCK;
 ps_mtx_lock;
end;

function sys_sigreturn(sigcntxp:p_ucontext_t):Integer;
var
 td:p_kthread;
 uc:ucontext_t;
 regs:p_trapframe;
 ucp:p_ucontext_t;
begin
 td:=curkthread;

 Result:=copyin(sigcntxp,@uc,sizeof(ucontext_t));
 if (Result<>0) then Exit;

 ucp:=@uc;
 if ((ucp^.uc_mcontext.mc_flags and (not _MC_FLAG_MASK))<>0) then
 begin
  Exit(EINVAL);
 end;

 regs:=td^.td_frame;

 //xmm,ymm
 if ((uc.uc_mcontext.mc_flags and _MC_HASFPXSTATE)<>0) then
 begin
  Result:=set_fpcontext(td,@ucp^.uc_mcontext,@ucp^.uc_mcontext.mc_fpstate);
  if (Result<>0) then Exit;

  regs^.tf_flags:=regs^.tf_flags or TF_HASFPXSTATE;
 end;
 //xmm,ymm

 Move(ucp^.uc_mcontext.mc_rdi,regs^,sizeof(trapframe));

 //pcb^.pcb_fsbase := ucp^.uc_mcontext.mc_fsbase;
 //pcb^.pcb_gsbase := ucp^.uc_mcontext.mc_gsbase;

 kern_sigprocmask(td,SIG_SETMASK,@ucp^.uc_sigmask,nil,0);

 set_pcb_flags(td,PCB_FULL_IRET);
 Result:=EJUSTRETURN;
end;


end.

