Index: sys/amd64/linux/linux_sysvec.c =================================================================== --- sys/amd64/linux/linux_sysvec.c +++ sys/amd64/linux/linux_sysvec.c @@ -207,19 +207,49 @@ static void linux_set_syscall_retval(struct thread *td, int error) { - struct trapframe *frame = td->td_frame; + struct trapframe *frame; + frame = td->td_frame; + /* * On Linux only %rcx and %r11 values are not preserved across * the syscall. So, do not clobber %rdx and %r10. */ - td->td_retval[1] = frame->tf_rdx; - if (error != EJUSTRETURN) + + if (__predict_true(error == 0)) { + frame->tf_rax = td->td_retval[0]; frame->tf_r10 = frame->tf_rcx; + /* XXX: This shouldn't be needed. */ + set_pcb_flags(td->td_pcb, PCB_FULL_IRET); + return; + } - cpu_set_syscall_retval(td, error); + switch (error) { + case ERESTART: + /* + * Reconstruct pc, we know that 'syscall' is 2 bytes, + * lcall $X,y is 7 bytes, int 0x80 is 2 bytes. + * We saved this in tf_err. + * + * Require full context restore to get the arguments + * in the registers reloaded at return to usermode. + */ + frame->tf_rip -= frame->tf_err; + frame->tf_r10 = frame->tf_rcx; + set_pcb_flags(td->td_pcb, PCB_FULL_IRET); + break; - /* Restore all registers. */ + case EJUSTRETURN: + break; + + default: + frame->tf_rax = SV_ABI_ERRNO(td->td_proc, error); + frame->tf_r10 = frame->tf_rcx; + set_pcb_flags(td->td_pcb, PCB_FULL_IRET); + break; + } + + /* Restore all registers. XXX: This shouldn't be needed. */ set_pcb_flags(td->td_pcb, PCB_FULL_IRET); }