unit trap; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sysutils, ucontext, kern_thr; const T_PRIVINFLT = 1; // privileged instruction T_BPTFLT = 3; // breakpoint instruction T_ARITHTRAP = 6; // arithmetic trap T_PROTFLT = 9; // protection fault T_TRCTRAP =10; // debug exception (sic) T_PAGEFLT =12; // page fault T_ALIGNFLT =14; // alignment fault T_DIVIDE =18; // integer divide fault T_NMI =19; // non-maskable trap T_OFLOW =20; // overflow trap T_BOUND =21; // bound instruction fault T_DNA =22; // device not available fault T_DOUBLEFLT =23; // double fault T_FPOPFLT =24; // fp coprocessor operand fetch fault T_TSSFLT =25; // invalid tss fault T_SEGNPFLT =26; // segment not present fault T_STKFLT =27; // stack fault T_MCHK =28; // machine check trap T_XMMFLT =29; // SIMD floating-point exception T_RESERVED =30; // reserved (unknown) T_DTRACE_RET=32; // DTrace pid return // XXX most of the following codes aren't used, but could be. // definitions for ILL_PRIVIN_FAULT=T_PRIVINFLT; ILL_ALIGN_FAULT =T_ALIGNFLT ; ILL_FPOP_FAULT =T_FPOPFLT ; // coprocessor operand fault // codes for SIGBUS BUS_PAGE_FAULT =T_PAGEFLT ; // page fault protection base BUS_SEGNP_FAULT=T_SEGNPFLT; // segment not present BUS_STK_FAULT =T_STKFLT ; // stack segment BUS_SEGM_FAULT =T_RESERVED; // segment protection base // Trap's coming from user mode T_USER=$100; MAX_TRAP_MSG=32; trap_msg:array[0..MAX_TRAP_MSG] of PChar=( '', // 0 unused 'privileged instruction fault', // 1 T_PRIVINFLT '', // 2 unused 'breakpoint instruction fault', // 3 T_BPTFLT '', // 4 unused '', // 5 unused 'arithmetic trap', // 6 T_ARITHTRAP '', // 7 unused '', // 8 unused 'general protection fault', // 9 T_PROTFLT 'trace trap', // 10 T_TRCTRAP '', // 11 unused 'page fault', // 12 T_PAGEFLT '', // 13 unused 'alignment fault', // 14 T_ALIGNFLT '', // 15 unused '', // 16 unused '', // 17 unused 'integer divide fault', // 18 T_DIVIDE 'non-maskable interrupt trap', // 19 T_NMI 'overflow trap', // 20 T_OFLOW 'FPU bounds check fault', // 21 T_BOUND 'FPU device not available', // 22 T_DNA 'double fault', // 23 T_DOUBLEFLT 'FPU operand fetch fault', // 24 T_FPOPFLT 'invalid TSS fault', // 25 T_TSSFLT 'segment not present fault', // 26 T_SEGNPFLT 'stack fault', // 27 T_STKFLT 'machine check trap', // 28 T_MCHK 'SIMD floating-point exception', // 29 T_XMMFLT 'reserved (unknown) fault', // 30 T_RESERVED '', // 31 unused (reserved) 'DTrace pid return trap' // 32 T_DTRACE_RET ); const PCB_FULL_IRET=1; SIG_ALTERABLE=$80000000; SIG_STI_LOCK =$40000000; procedure set_pcb_flags(td:p_kthread;f:Integer); procedure _sig_lock; procedure _sig_unlock; procedure sig_lock; procedure sig_unlock; procedure sig_sta; procedure sig_cla; procedure sig_sti; procedure sig_cli; procedure fast_syscall; procedure sigcode; procedure sigipi; implementation uses errno, vmparam, machdep, md_context, kern_sig, sysent; const NOT_PCB_FULL_IRET=not PCB_FULL_IRET; NOT_SIG_ALTERABLE=not SIG_ALTERABLE; NOT_SIG_STI_LOCK =not SIG_STI_LOCK; TDF_AST=TDF_ASTPENDING or TDF_NEEDRESCHED; procedure _sig_lock; assembler; nostackframe; asm pushf lock incl %gs:teb.iflag //lock interrupt popf end; procedure _sig_unlock; assembler; nostackframe; asm pushf lock decl %gs:teb.iflag //unlock interrupt popf end; procedure sig_lock; assembler; nostackframe; label _exit; asm //prolog (debugger) pushq %rbp movq %rsp,%rbp pushq %rax pushf movq $1,%rax lock xadd %rax,%gs:teb.iflag //lock interrupt test %rax,%rax jnz _exit movqq %gs:teb.thread,%rax //curkthread testl TDF_AST,kthread.td_flags(%rax) je _exit mov $0,%rax call fast_syscall _exit: //epilog (debugger) popf popq %rax popq %rbp end; procedure sig_unlock; assembler; nostackframe; label _exit; asm //prolog (debugger) pushq %rbp movq %rsp,%rbp pushq %rax pushf lock decl %gs:teb.iflag //unlock interrupt jnz _exit movqq %gs:teb.thread,%rax //curkthread testl TDF_AST,kthread.td_flags(%rax) je _exit mov $0,%rax call fast_syscall _exit: //epilog (debugger) popf popq %rax popq %rbp end; procedure sig_sta; assembler; nostackframe; asm lock orl SIG_ALTERABLE,%gs:teb.iflag end; procedure sig_cla; assembler; nostackframe; asm lock andl NOT_SIG_ALTERABLE,%gs:teb.iflag end; procedure sig_sti; assembler; nostackframe; asm lock orl SIG_STI_LOCK,%gs:teb.iflag end; procedure sig_cli; assembler; nostackframe; asm lock andl NOT_SIG_STI_LOCK,%gs:teb.iflag end; procedure set_pcb_flags(td:p_kthread;f:Integer); begin td^.pcb_flags:=f; end; type tsyscall=function(rdi,rsi,rdx,rcx,r8,r9:QWORD):Integer; procedure amd64_syscall; var td:p_kthread; td_frame:p_trapframe; scall:tsyscall; error:Integer; begin //Call directly to the address or make an ID table? td:=curkthread; td_frame:=@td^.td_frame; cpu_fetch_syscall_args(td); error:=0; scall:=nil; if (td_frame^.tf_rax<=High(sysent_table)) then begin scall:=tsyscall(sysent_table[td_frame^.tf_rax].sy_call); if (scall=nil) then begin Writeln('Unhandled syscall:',td_frame^.tf_rax,':',sysent_table[td_frame^.tf_rax].sy_name); Writeln(' [1]:0x',HexStr(td_frame^.tf_rdi,16)); Writeln(' [2]:0x',HexStr(td_frame^.tf_rsi,16)); Writeln(' [3]:0x',HexStr(td_frame^.tf_rdx,16)); Writeln(' [4]:0x',HexStr(td_frame^.tf_r10,16)); Writeln(' [5]:0x',HexStr(td_frame^.tf_r8 ,16)); Writeln(' [6]:0x',HexStr(td_frame^.tf_r9 ,16)); Assert(false,sysent_table[td_frame^.tf_rax].sy_name); end; end else if (td_frame^.tf_rax<=$1000) then begin Writeln('Unhandled syscall:',td_frame^.tf_rax); Writeln(' [1]:0x',HexStr(td_frame^.tf_rdi,16)); Writeln(' [2]:0x',HexStr(td_frame^.tf_rsi,16)); Writeln(' [3]:0x',HexStr(td_frame^.tf_rdx,16)); Writeln(' [4]:0x',HexStr(td_frame^.tf_r10,16)); Writeln(' [5]:0x',HexStr(td_frame^.tf_r8 ,16)); Writeln(' [6]:0x',HexStr(td_frame^.tf_r9 ,16)); Assert(false,IntToStr(td_frame^.tf_rax)); end else begin scall:=tsyscall(td_frame^.tf_rax); end; if (scall=nil) then begin error:=ENOSYS; end else begin if (td_frame^.tf_rax<=High(sysent_table)) then if is_guest_addr(td_frame^.tf_rip) then begin Writeln('Guest syscall:',sysent_table[td_frame^.tf_rax].sy_name); Writeln(' [1]:0x',HexStr(td_frame^.tf_rdi,16)); Writeln(' [2]:0x',HexStr(td_frame^.tf_rsi,16)); Writeln(' [3]:0x',HexStr(td_frame^.tf_rdx,16)); Writeln(' [4]:0x',HexStr(td_frame^.tf_r10,16)); Writeln(' [5]:0x',HexStr(td_frame^.tf_r8 ,16)); Writeln(' [6]:0x',HexStr(td_frame^.tf_r9 ,16)); end; error:=scall(td_frame^.tf_rdi, td_frame^.tf_rsi, td_frame^.tf_rdx, td_frame^.tf_r10, td_frame^.tf_r8 , td_frame^.tf_r9 ); end; if ((td^.td_pflags and TDP_NERRNO)=0) then begin td^.td_errno:=error; end; cpu_set_syscall_retval(td,error); end; procedure fast_syscall; assembler; nostackframe; label _after_call, _doreti, _fail, _ast, _doreti_exit; asm //prolog (debugger) pushq %rbp movq %rsp,%rbp movqq %rax,%r11 //save rax movqq %rcx,%r10 //save rcx lahf //load to AH shr $8,%rax andl $0xFF,%rax //filter movqq %rax,%rcx //save flags movqq %gs:teb.thread,%rax //curkthread test %rax,%rax jz _fail movqq kthread.td_kstack.stack(%rax),%rsp //td_kstack (Implicit lock interrupt) andq $-32,%rsp //align stack andl NOT_PCB_FULL_IRET,kthread.pcb_flags(%rax) //clear PCB_FULL_IRET movqq %rdi,kthread.td_frame.tf_rdi (%rax) movqq %rsi,kthread.td_frame.tf_rsi (%rax) movqq %rdx,kthread.td_frame.tf_rdx (%rax) movqq $0,kthread.td_frame.tf_rcx (%rax) movqq %r8 ,kthread.td_frame.tf_r8 (%rax) movqq %r9 ,kthread.td_frame.tf_r9 (%rax) movqq %r11,kthread.td_frame.tf_rax (%rax) movqq %rbx,kthread.td_frame.tf_rbx (%rax) movqq %r10,kthread.td_frame.tf_r10 (%rax) movqq $0,kthread.td_frame.tf_r11 (%rax) movqq %r12,kthread.td_frame.tf_r12 (%rax) movqq %r13,kthread.td_frame.tf_r13 (%rax) movqq %r14,kthread.td_frame.tf_r14 (%rax) movqq %r15,kthread.td_frame.tf_r15 (%rax) movqq %rcx,kthread.td_frame.tf_rflags(%rax) movqq $0 ,kthread.td_frame.tf_trapno(%rax) movqq $0 ,kthread.td_frame.tf_addr (%rax) movqq $0 ,kthread.td_frame.tf_flags (%rax) movqq $5 ,kthread.td_frame.tf_err (%rax) //sizeof(call $32) movqq (%rbp),%r11 //get prev rbp movqq %r11,kthread.td_frame.tf_rbp(%rax) lea 16(%rbp),%r11 //get prev rsp movqq %r11,kthread.td_frame.tf_rsp(%rax) movqq 8(%rbp),%r11 //get prev rip movqq %r11,kthread.td_frame.tf_rip(%rax) call amd64_syscall _after_call: movqq %gs:teb.thread,%rcx //curkthread //Requested full context restore testl PCB_FULL_IRET,kthread.pcb_flags(%rcx) jnz _doreti testl TDF_AST,kthread.td_flags(%rcx) jne _ast //Restore preserved registers. movqq kthread.td_frame.tf_rflags(%rcx),%rax shl $8,%rax sahf //restore flags movqq kthread.td_frame.tf_rdi(%rcx),%rdi movqq kthread.td_frame.tf_rsi(%rcx),%rsi movqq kthread.td_frame.tf_rdx(%rcx),%rdx movqq kthread.td_frame.tf_rax(%rcx),%rax movqq kthread.td_frame.tf_rsp(%rcx),%r11 lea -16(%r11),%r11 movqq %r11,%rsp //restore rsp (Implicit unlock interrupt) movqq $0,%rcx movqq $0,%r11 //epilog (debugger) popq %rbp ret //fail: _fail: movqq %rcx,%rax //get flags shl $8,%rax or $1,%ah //CF sahf //restore flags movqq $14,%rax //EFAULT movqq $0,%rdx movqq $0,%rcx movqq $0,%r11 popq %rbp ret //ast _ast: call ast jmp _after_call //doreti _doreti: //%rcx=curkthread testl TDF_AST,kthread.td_flags(%rcx) je _doreti_exit call ast jmp _doreti _doreti_exit: //Restore full. call ipi_sigreturn hlt end; procedure sigcode; assembler; nostackframe; asm call sigframe.sf_ahu(%rsp) lea sigframe.sf_uc(%rsp),%rdi pushq $0 movqq sys_sigreturn,%rax call fast_syscall hlt end; procedure sigipi; assembler; nostackframe; label _ast, _ast_exit; asm lea sigframe.sf_uc(%rsp),%rdi call sys_sigreturn //ast _ast: movqq %gs:teb.thread,%rax //curkthread testl TDF_AST,kthread.td_flags(%rax) je _ast_exit call ast jmp _ast _ast_exit: call ipi_sigreturn hlt end; end.