unit machdep;

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

interface

uses
 signal,
 signalvar,
 ucontext,
 kern_thr,
 kern_proc;

const
 GUFS32_SEL = 2; // User 32 bit %fs Descriptor
 GUGS32_SEL = 3; // User 32 bit %gs Descriptor
 GUDATA_SEL = 7; // User 32/64 bit Data Descriptor
 GUCODE_SEL = 8; // User 64 bit Code Descriptor

 _ucodesel=(GUCODE_SEL shl 3) or 3;
 _udatasel=(GUDATA_SEL shl 3) or 3;
 _ufssel  =(GUFS32_SEL shl 3) or 3;
 _ugssel  =(GUGS32_SEL shl 3) or 3;

procedure bzero(ptr:Pointer;size:ptrint);
Procedure bmove(src,dst:Pointer;size:ptrint);

function  cpu_getstack(td:p_kthread):QWORD;
procedure cpu_set_fsbase(td:p_kthread;base:Pointer);
procedure cpu_set_gsbase(td:p_kthread;base:Pointer);
procedure cpu_init_jit(td:p_kthread);
procedure cpu_fini_jit(td:p_kthread);
procedure cpu_fetch_syscall_args(td:p_kthread);
procedure cpu_set_syscall_retval(td:p_kthread;error:Integer);
procedure cpu_set_upcall_kse(td:p_kthread;entry,arg:Pointer;stack:p_stack_t);

function  get_mcontext(td:p_kthread;mcp:p_mcontext_t;flags:Integer):Integer;
function  get_mcontext2(td:p_kthread;mcp:p_mcontext_t;flags:Integer):Integer;
function  set_mcontext(td:p_kthread;mcp:p_mcontext_t):Integer;

procedure sendsig(catcher:sig_t;ksi:p_ksiginfo;mask:p_sigset_t);

//

function  sys_sigreturn(sigcntxp:Pointer):Integer;

procedure exec_setregs(td:p_kthread;entry,stack_base,stack_top:QWORD);

implementation

uses
 errno,
 systm,
 kern_psl,
 vm_map,
 md_context;

//clearing memory without AVX optimizations
procedure bzero(ptr:Pointer;size:ptrint);
begin
 while (size>0) do
 begin
  PByte(ptr)^:=0;
  Inc(ptr);
  Dec(size);
 end;
end;

//move memory without AVX optimizations
Procedure bmove(src,dst:Pointer;size:ptrint);
begin
 while (size>0) do
 begin
  PByte(dst)^:=PByte(src)^;
  Inc(src);
  Inc(dst);
  Dec(size);
 end;
end;

function cpu_getstack(td:p_kthread):QWORD;
begin
 Result:=td^.td_frame.tf_rsp;
end;

procedure cpu_set_fsbase(td:p_kthread;base:Pointer);
begin
 td^.pcb_fsbase:=base;
 td^.td_teb^.fsbase:=base;
 set_pcb_flags(td,PCB_FULL_IRET);
end;

procedure cpu_set_gsbase(td:p_kthread;base:Pointer);
begin
 td^.pcb_gsbase:=base;
 td^.td_teb^.gsbase:=base;
 set_pcb_flags(td,PCB_FULL_IRET);
end;

procedure cpu_init_jit(td:p_kthread);
begin
 //teb stack
 teb_set_kernel(td);
 //teb stack
end;

procedure cpu_fini_jit(td:p_kthread);
begin
 //teb stack
 teb_set_user(td);
 //teb stack
end;

procedure cpu_fetch_syscall_args(td:p_kthread);
begin
 td^.td_retval[0]:=0;
 td^.td_retval[1]:=td^.td_frame.tf_rdx;

 //teb stack
 teb_set_kernel(td);
 //teb stack
end;

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;

 //teb stack
 teb_set_user(td);
 //teb stack
end;

procedure cpu_set_upcall_kse(td:p_kthread;entry,arg:Pointer;stack:p_stack_t);
begin
 {
  * Set the trap frame to point at the beginning of the uts
  * function.
  }
 td^.td_frame.tf_rbp:=0;
 td^.td_frame.tf_rsp:=(ptruint(stack^.ss_sp) + stack^.ss_size) and (not $F);
 Dec(td^.td_frame.tf_rsp,8);
 td^.td_frame.tf_rip:=ptruint(entry);
 td^.td_frame.tf_ds:=_udatasel;
 td^.td_frame.tf_es:=_udatasel;
 td^.td_frame.tf_fs:=_ufssel;
 td^.td_frame.tf_gs:=_ugssel;
 td^.td_frame.tf_flags:=TF_HASSEGS;

 {
  * Pass the address of the mailbox for this kse to the uts
  * function as a parameter on the stack.
  }
 td^.td_frame.tf_rdi:=ptruint(arg);
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;

 bmove(@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
  bmove(xstate,@td^.td_fpstate,SizeOf(mcp^.mc_fpstate));
  td^.td_frame.tf_flags:=td^.td_frame.tf_flags or TF_HASFPXSTATE;
  Exit(0);
 end;

 Exit(EINVAL);
end;

function get_mcontext(td:p_kthread;mcp:p_mcontext_t;flags:Integer):Integer;
var
 tp:p_trapframe;
begin
 tp:=@td^.td_frame;

 PROC_LOCK();
 mcp^.mc_onstack:=sigonstack(tp^.tf_rsp);
 PROC_UNLOCK();

 mcp^.mc_r15   :=tp^.tf_r15;
 mcp^.mc_r14   :=tp^.tf_r14;
 mcp^.mc_r13   :=tp^.tf_r13;
 mcp^.mc_r12   :=tp^.tf_r12;
 mcp^.mc_r11   :=tp^.tf_r11;
 mcp^.mc_r10   :=tp^.tf_r10;
 mcp^.mc_r9    :=tp^.tf_r9;
 mcp^.mc_r8    :=tp^.tf_r8;
 mcp^.mc_rdi   :=tp^.tf_rdi;
 mcp^.mc_rsi   :=tp^.tf_rsi;
 mcp^.mc_rbp   :=tp^.tf_rbp;
 mcp^.mc_rbx   :=tp^.tf_rbx;
 mcp^.mc_rcx   :=tp^.tf_rcx;
 mcp^.mc_rflags:=tp^.tf_rflags;

 if ((flags and GET_MC_CLEAR_RET)<>0) then
 begin
  mcp^.mc_rax:=0;
  mcp^.mc_rdx:=0;
  mcp^.mc_rflags:=tp^.tf_rflags and (not PSL_C);
 end else
 begin
  mcp^.mc_rax:=tp^.tf_rax;
  mcp^.mc_rdx:=tp^.tf_rdx;
 end;

 mcp^.mc_rip  :=tp^.tf_rip;
 mcp^.mc_cs   :=tp^.tf_cs;
 mcp^.mc_rsp  :=tp^.tf_rsp;
 mcp^.mc_ss   :=tp^.tf_ss;
 mcp^.mc_ds   :=tp^.tf_ds;
 mcp^.mc_es   :=tp^.tf_es;
 mcp^.mc_fs   :=tp^.tf_fs;
 mcp^.mc_gs   :=tp^.tf_gs;
 mcp^.mc_flags:=tp^.tf_flags;
 mcp^.mc_len  :=sizeof(mcontext_t);

 //xmm,ymm
 if ((tp^.tf_flags and TF_HASFPXSTATE)<>0) then
 begin
  Result:=get_fpcontext(td,mcp,@mcp^.mc_fpstate);
 end else
 begin
  Result:=md_get_fpcontext(td,mcp,@mcp^.mc_fpstate);
 end;
 if (Result<>0) then Exit;
 //xmm,ymm

 mcp^.mc_fsbase:=ptruint(td^.pcb_fsbase);
 mcp^.mc_gsbase:=ptruint(td^.pcb_gsbase);

 bzero(@mcp^.mc_spare,sizeof(mcp^.mc_spare));

 Result:=0;
end;

//sce ext

function get_mcontext2(td:p_kthread;mcp:p_mcontext_t;flags:Integer):Integer;
var
 tp:p_trapframe;
begin
 tp:=@td^.td_frame;

 mcp^.mc_onstack:=sigonstack(tp^.tf_rsp);

 mcp^.mc_r15   :=tp^.tf_r15;
 mcp^.mc_r14   :=tp^.tf_r14;
 mcp^.mc_r13   :=tp^.tf_r13;
 mcp^.mc_r12   :=tp^.tf_r12;
 mcp^.mc_r11   :=tp^.tf_r11;
 mcp^.mc_r10   :=tp^.tf_r10;
 mcp^.mc_r9    :=tp^.tf_r9;
 mcp^.mc_r8    :=tp^.tf_r8;
 mcp^.mc_rdi   :=tp^.tf_rdi;
 mcp^.mc_rsi   :=tp^.tf_rsi;
 mcp^.mc_rbp   :=tp^.tf_rbp;
 mcp^.mc_rbx   :=tp^.tf_rbx;
 mcp^.mc_rcx   :=tp^.tf_rcx;
 mcp^.mc_rflags:=tp^.tf_rflags;
 mcp^.mc_rax   :=tp^.tf_rax;
 mcp^.mc_rdx   :=tp^.tf_rdx;
 mcp^.mc_rip   :=tp^.tf_rip;
 mcp^.mc_cs    :=tp^.tf_cs;
 mcp^.mc_rsp   :=tp^.tf_rsp;
 mcp^.mc_ss    :=tp^.tf_ss;
 mcp^.mc_ds    :=tp^.tf_ds;
 mcp^.mc_es    :=tp^.tf_es;
 mcp^.mc_fs    :=tp^.tf_fs;
 mcp^.mc_gs    :=tp^.tf_gs;
 mcp^.mc_flags :=tp^.tf_flags;
 mcp^.mc_len   :=sizeof(mcontext_t);

 //xmm,ymm
 get_fpcontext(td,mcp,@mcp^.mc_fpstate);
 //xmm,ymm

 mcp^.mc_fsbase:=ptruint(td^.pcb_fsbase);
 mcp^.mc_gsbase:=ptruint(td^.pcb_gsbase);

 bzero(@mcp^.mc_spare,sizeof(mcp^.mc_spare));

 Result:=0;
end;

//sce ext

function set_mcontext(td:p_kthread;mcp:p_mcontext_t):Integer;
var
 tp:p_trapframe;
 rflags:QWORD;
begin
 tp:=@td^.td_frame;

 if (mcp^.mc_len<>sizeof(mcontext_t)) or
    ((mcp^.mc_flags and (not _MC_FLAG_MASK))<>0) then
 begin
  Exit(EINVAL);
 end;

 //xmm,ymm
 if ((mcp^.mc_rflags and _MC_HASFPXSTATE)<>0) then
 begin
  Result:=set_fpcontext(td,mcp,@mcp^.mc_fpstate);
  if (Result<>0) then Exit;
 end;
 //xmm,ymm

 rflags:=(mcp^.mc_rflags and PSL_USERCHANGE) or
         (tp^.tf_rflags and (not PSL_USERCHANGE));

 tp^.tf_r15   :=mcp^.mc_r15;
 tp^.tf_r14   :=mcp^.mc_r14;
 tp^.tf_r13   :=mcp^.mc_r13;
 tp^.tf_r12   :=mcp^.mc_r12;
 tp^.tf_r11   :=mcp^.mc_r11;
 tp^.tf_r10   :=mcp^.mc_r10;
 tp^.tf_r9    :=mcp^.mc_r9;
 tp^.tf_r8    :=mcp^.mc_r8;
 tp^.tf_rdi   :=mcp^.mc_rdi;
 tp^.tf_rsi   :=mcp^.mc_rsi;
 tp^.tf_rbp   :=mcp^.mc_rbp;
 tp^.tf_rbx   :=mcp^.mc_rbx;
 tp^.tf_rdx   :=mcp^.mc_rdx;
 tp^.tf_rcx   :=mcp^.mc_rcx;
 tp^.tf_rax   :=mcp^.mc_rax;
 tp^.tf_rip   :=mcp^.mc_rip;
 tp^.tf_rflags:=rflags;
 tp^.tf_rsp   :=mcp^.mc_rsp;
 tp^.tf_ss    :=mcp^.mc_ss;
 tp^.tf_flags :=mcp^.mc_flags;

 if ((tp^.tf_flags and TF_HASSEGS)<>0) then
 begin
  tp^.tf_ds:=mcp^.mc_ds;
  tp^.tf_es:=mcp^.mc_es;
  tp^.tf_fs:=mcp^.mc_fs;
  tp^.tf_gs:=mcp^.mc_gs;
 end;

 if ((mcp^.mc_flags and _MC_HASBASES)<>0) then
 begin
  cpu_set_fsbase(td,Pointer(mcp^.mc_fsbase));
  cpu_set_gsbase(td,Pointer(mcp^.mc_gsbase));
 end;

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

procedure host_sigcode; external;

function IS_SYSTEM_STACK(td:p_kthread;rsp:qword):Boolean; inline;
begin
 Result:=(rsp<=QWORD(td^.td_kstack.stack)) and (rsp>(QWORD(td^.td_kstack.sttop)));
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;

 Writeln('sendsig');

 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

 bmove(@regs^.tf_rdi,@sf.sf_uc.uc_mcontext.mc_rdi,tf_copy_1);
 bmove(@regs^.tf_err,@sf.sf_uc.uc_mcontext.mc_err,tf_copy_2);

 sf.sf_uc.uc_mcontext.mc_lbrfrom:=QWORD(-1);
 sf.sf_uc.uc_mcontext.mc_lbrto  :=QWORD(-1);

 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);

  //reset fpcontext usage
  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;

 if IS_SYSTEM_STACK(td,sp) then
 begin
  Assert(false,'System stack on guest context?');
 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);

 if ((td^.pcb_flags and PCB_IS_JIT)=0) or
    ((td^.pcb_flags and PCB_IS_HLE)<>0) then
 begin
  regs^.tf_rip:=QWORD(@host_sigcode);
 end else
 begin
  regs^.tf_rip:=QWORD(p_vmspace(p_proc.p_vmspace)^.sv_usrstack);
 end;

 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:Pointer):Integer;
var
 td:p_kthread;
 uc:ucontext_t;
 regs:p_trapframe;
 ucp:p_ucontext_t;
begin
 td:=curkthread;

 Writeln('sys_sigreturn');

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

 if IS_SYSTEM_STACK(td,uc.uc_mcontext.mc_rsp) then
 begin
  Assert(false,'System stack on guest context?');
 end;

 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;
 end;
 //xmm,ymm

 bmove(@ucp^.uc_mcontext.mc_rdi,@regs^.tf_rdi,tf_copy_1);
 bmove(@ucp^.uc_mcontext.mc_err,@regs^.tf_err,tf_copy_2);

 if ((ucp^.uc_mcontext.mc_flags and _MC_HASBASES)<>0) then
 begin
  cpu_set_fsbase(td,Pointer(ucp^.uc_mcontext.mc_fsbase));
  cpu_set_gsbase(td,Pointer(ucp^.uc_mcontext.mc_gsbase));
 end;

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

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

procedure exec_setregs(td:p_kthread;entry,stack_base,stack_top:QWORD);
var
 regs:p_trapframe;
begin
 regs:=@td^.td_frame;

 cpu_set_fsbase(td,nil);
 cpu_set_gsbase(td,nil);

 set_pcb_flags(td,PCB_FULL_IRET);

 bzero(regs, sizeof(trapframe));

 regs^.tf_rip   :=entry;
 regs^.tf_rsp   :=((stack_base - 8) and (not $F)) + 8;
 regs^.tf_rdi   :=stack_base; // argv
 regs^.tf_rflags:=PSL_USER or (regs^.tf_rflags and PSL_T);
 regs^.tf_ss    :=_udatasel;
 regs^.tf_cs    :=_ucodesel;
 regs^.tf_ds    :=_udatasel;
 regs^.tf_es    :=_udatasel;
 regs^.tf_fs    :=_ufssel;
 regs^.tf_gs    :=_ugssel;
 regs^.tf_flags :=TF_HASSEGS;

 //init FPU
 fpuinit(td);

 Set8087CW(__INITIAL_FPUCW__);
 SetMXCSR (__INITIAL_MXCSR__);

 //teb stack
 td^.td_ustack.stack:=Pointer(stack_top );
 td^.td_ustack.sttop:=Pointer(stack_base);
 //teb stack

 td^.td_retval[1]:=0;
end;



end.

