svn commit: r366810 - head/sys/amd64/linux
Edward Tomasz Napierala
trasz at FreeBSD.org
Sun Oct 18 16:16:23 UTC 2020
Author: trasz
Date: Sun Oct 18 16:16:22 2020
New Revision: 366810
URL: https://svnweb.freebsd.org/changeset/base/366810
Log:
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
Modified:
head/sys/amd64/linux/linux_sysvec.c
Modified: head/sys/amd64/linux/linux_sysvec.c
==============================================================================
--- head/sys/amd64/linux/linux_sysvec.c Sun Oct 18 15:58:16 2020 (r366809)
+++ head/sys/amd64/linux/linux_sysvec.c Sun Oct 18 16:16:22 2020 (r366810)
@@ -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->tf_r10 = frame->tf_rcx;
+ frame = td->td_frame;
- cpu_set_syscall_retval(td, error);
+ switch (error) {
+ case 0:
+ frame->tf_rax = td->td_retval[0];
+ frame->tf_r10 = frame->tf_rcx;
+ break;
- if (__predict_false(error != 0)) {
- if (error != ERESTART && error != EJUSTRETURN)
- frame->tf_rax = linux_to_bsd_errno(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.
+ *
+ */
+ frame->tf_rip -= frame->tf_err;
+ frame->tf_r10 = frame->tf_rcx;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ 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);
}
More information about the svn-src-all
mailing list