Stop calling set_syscall_retval() from linux_set_syscall_retval().

The former clobbers some registers that shouldn't be touched.

Reviewed by:	kib (earlier version)
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D26406
This commit is contained in:
Edward Tomasz Napierala 2020-10-18 16:16:22 +00:00
parent 54669eb779
commit 6221ec6064
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=366810

View File

@ -206,24 +206,45 @@ linux_fetch_syscall_args(struct thread *td)
static void
linux_set_syscall_retval(struct thread *td, int error)
{
struct trapframe *frame = td->td_frame;
struct trapframe *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)
frame = td->td_frame;
switch (error) {
case 0:
frame->tf_rax = td->td_retval[0];
frame->tf_r10 = frame->tf_rcx;
break;
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.
*
*/
frame->tf_rip -= frame->tf_err;
frame->tf_r10 = frame->tf_rcx;
break;
case EJUSTRETURN:
break;
cpu_set_syscall_retval(td, error);
if (__predict_false(error != 0)) {
if (error != ERESTART && error != EJUSTRETURN)
frame->tf_rax = linux_to_bsd_errno(error);
default:
frame->tf_rax = linux_to_bsd_errno(error);
frame->tf_r10 = frame->tf_rcx;
break;
}
/* Restore all registers. */
/*
* Differently from FreeBSD native ABI, on Linux only %rcx
* and %r11 values are not preserved across the syscall.
* Require full context restore to get all registers except
* those two restored at return to usermode.
*
* XXX: Would be great to be able to avoid PCB_FULL_IRET
* for the error == 0 case.
*/
set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
}