This patch adds support for {get,set,swap,make}context to the x86_64
architecture. Files are imported from glibc.

Signed-off-by: Natanael Copa <nc...@alpinelinux.org>
Signed-off-by: Florian Fainelli <flor...@openwrt.org>
---
 extra/Configs/Config.x86_64                 |    1 +
 libc/sysdeps/linux/x86_64/Makefile.arch     |    6 ++
 libc/sysdeps/linux/x86_64/__start_context.S |   49 +++++++++++
 libc/sysdeps/linux/x86_64/getcontext.S      |   88 +++++++++++++++++++
 libc/sysdeps/linux/x86_64/makecontext.c     |  121 +++++++++++++++++++++++++++
 libc/sysdeps/linux/x86_64/setcontext.S      |  103 +++++++++++++++++++++++
 libc/sysdeps/linux/x86_64/swapcontext.S     |  121 +++++++++++++++++++++++++++
 libc/sysdeps/linux/x86_64/ucontext_i.sym    |   37 ++++++++
 8 files changed, 526 insertions(+)
 create mode 100644 libc/sysdeps/linux/x86_64/__start_context.S
 create mode 100644 libc/sysdeps/linux/x86_64/getcontext.S
 create mode 100644 libc/sysdeps/linux/x86_64/makecontext.c
 create mode 100644 libc/sysdeps/linux/x86_64/setcontext.S
 create mode 100644 libc/sysdeps/linux/x86_64/swapcontext.S
 create mode 100644 libc/sysdeps/linux/x86_64/ucontext_i.sym

diff --git a/extra/Configs/Config.x86_64 b/extra/Configs/Config.x86_64
index 1b28088..4c8c3a9 100644
--- a/extra/Configs/Config.x86_64
+++ b/extra/Configs/Config.x86_64
@@ -12,3 +12,4 @@ config FORCE_OPTIONS_FOR_ARCH
        default y
        select ARCH_LITTLE_ENDIAN
        select ARCH_HAS_MMU
+       select ARCH_HAS_UCONTEXT
diff --git a/libc/sysdeps/linux/x86_64/Makefile.arch 
b/libc/sysdeps/linux/x86_64/Makefile.arch
index 7491d92..455a6c1 100644
--- a/libc/sysdeps/linux/x86_64/Makefile.arch
+++ b/libc/sysdeps/linux/x86_64/Makefile.arch
@@ -20,3 +20,9 @@ ifeq ($(UCLIBC_HAS_TLS),y)
 SSRC += sched_getcpu.S
 endif
 endif
+
+ifeq ($(UCLIBC_HAS_CONTEXT_FUNCS),y)
+CSRC += makecontext.c
+SSRC += setcontext.S getcontext.S swapcontext.S __start_context.S
+endif
+
diff --git a/libc/sysdeps/linux/x86_64/__start_context.S 
b/libc/sysdeps/linux/x86_64/__start_context.S
new file mode 100644
index 0000000..9f2ee23
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/__start_context.S
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <a...@suse.de>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+/* This is the helper code which gets called if a function which is
+   registered with 'makecontext' returns.  In this case we have to
+   install the context listed in the uc_link element of the context
+   'makecontext' manipulated at the time of the 'makecontext' call.
+   If the pointer is NULL the process must terminate.  */
+
+
+ENTRY(__start_context)
+       /* This removes the parameters passed to the function given to
+          'makecontext' from the stack.  RBX contains the address
+          on the stack pointer for the next context.  */
+       movq    %rbx, %rsp
+
+       popq    %rdi                    /* This is the next context.  */
+       cfi_adjust_cfa_offset(-8)
+       testq   %rdi, %rdi
+       je      2f                      /* If it is zero exit.  */
+
+       call    JUMPTARGET(__setcontext)
+       /* If this returns (which can happen if the syscall fails) we'll
+          exit the program with the return error value (-1).  */
+       movq    %rax,%rdi
+
+2:
+       call    HIDDEN_JUMPTARGET(exit)
+       /* The 'exit' call should never return.  In case it does cause
+          the process to terminate.  */
+       hlt
+END(__start_context)
diff --git a/libc/sysdeps/linux/x86_64/getcontext.S 
b/libc/sysdeps/linux/x86_64/getcontext.S
new file mode 100644
index 0000000..dcebc4f
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/getcontext.S
@@ -0,0 +1,88 @@
+/* Save current context.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <a...@suse.de>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+/*  int __getcontext (ucontext_t *ucp)
+
+  Saves the machine context in UCP such that when it is activated,
+  it appears as if __getcontext() returned again.
+
+  This implementation is intended to be used for *synchronous* context
+  switches only.  Therefore, it does not have to save anything
+  other than the PRESERVED state.  */
+
+
+ENTRY(__getcontext)
+       /* Save the preserved registers, the registers used for passing
+          args, and the return address.  */
+       movq    %rbx, oRBX(%rdi)
+       movq    %rbp, oRBP(%rdi)
+       movq    %r12, oR12(%rdi)
+       movq    %r13, oR13(%rdi)
+       movq    %r14, oR14(%rdi)
+       movq    %r15, oR15(%rdi)
+
+       movq    %rdi, oRDI(%rdi)
+       movq    %rsi, oRSI(%rdi)
+       movq    %rdx, oRDX(%rdi)
+       movq    %rcx, oRCX(%rdi)
+       movq    %r8, oR8(%rdi)
+       movq    %r9, oR9(%rdi)
+
+       movq    (%rsp), %rcx
+       movq    %rcx, oRIP(%rdi)
+       leaq    8(%rsp), %rcx           /* Exclude the return address.  */
+       movq    %rcx, oRSP(%rdi)
+
+       /* We have separate floating-point register content memory on the
+          stack.  We use the __fpregs_mem block in the context.  Set the
+          links up correctly.  */
+
+       leaq    oFPREGSMEM(%rdi), %rcx
+       movq    %rcx, oFPREGS(%rdi)
+       /* Save the floating-point environment.  */
+       fnstenv (%rcx)
+       fldenv  (%rcx)
+       stmxcsr oMXCSR(%rdi)
+
+       /* Save the current signal mask with
+          rt_sigprocmask (SIG_BLOCK, NULL, set,_NSIG/8).  */
+       leaq    oSIGMASK(%rdi), %rdx
+       xorl    %esi,%esi
+#if SIG_BLOCK == 0
+       xorl    %edi, %edi
+#else
+       movl    $SIG_BLOCK, %edi
+#endif
+       movl    $_NSIG8,%r10d
+       movl    $__NR_rt_sigprocmask, %eax
+       syscall
+       cmpq    $-4095, %rax            /* Check %rax for error.  */
+       jae     SYSCALL_ERROR_LABEL     /* Jump to error handler if error.  */
+
+       /* All done, return 0 for success.  */
+       xorl    %eax, %eax
+L(pseudo_end):
+       ret
+PSEUDO_END(__getcontext)
+
+weak_alias (__getcontext, getcontext)
diff --git a/libc/sysdeps/linux/x86_64/makecontext.c 
b/libc/sysdeps/linux/x86_64/makecontext.c
new file mode 100644
index 0000000..5473031
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/makecontext.c
@@ -0,0 +1,121 @@
+/* Create new context.
+   Copyright (C) 2002, 2004, 2005, 2008 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <a...@suse.de>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ucontext.h>
+
+#include "ucontext_i.h"
+
+/* This implementation can handle any ARGC value but only
+   normal integer parameters.
+   makecontext sets up a stack and the registers for the
+   user context. The stack looks like this:
+               +-----------------------+
+               | next context          |
+               +-----------------------+
+               | parameter 7-n         |
+              +-----------------------+
+              | trampoline address    |
+    %rsp ->    +-----------------------+
+
+   The registers are set up like this:
+     %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6
+     %rbx   : address of next context
+     %rsp   : stack pointer.
+*/
+
+/* XXX: This implementation currently only handles integer arguments.
+   To handle long int and pointer arguments the va_arg arguments needs
+   to be changed to long and also the stdlib/tst-setcontext.c file needs
+   to be changed to pass long arguments to makecontext.  */
+
+
+void
+__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
+{
+  extern void __start_context (void);
+  greg_t *sp;
+  unsigned int idx_uc_link;
+  va_list ap;
+  int i;
+
+  /* Generate room on stack for parameter if needed and uc_link.  */
+  sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp
+                  + ucp->uc_stack.ss_size);
+  sp -= (argc > 6 ? argc - 6 : 0) + 1;
+  /* Align stack and make space for trampoline address.  */
+  sp = (greg_t *) ((((uintptr_t) sp) & -16L) - 8);
+
+  idx_uc_link = (argc > 6 ? argc - 6 : 0) + 1;
+
+  /* Setup context ucp.  */
+  /* Address to jump to.  */
+  ucp->uc_mcontext.gregs[REG_RIP] = (uintptr_t) func;
+  /* Setup rbx.*/
+  ucp->uc_mcontext.gregs[REG_RBX] = (uintptr_t) &sp[idx_uc_link];
+  ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
+
+  /* Setup stack.  */
+  sp[0] = (uintptr_t) &__start_context;
+  sp[idx_uc_link] = (uintptr_t) ucp->uc_link;
+
+  va_start (ap, argc);
+  /* Handle arguments.
+
+     The standard says the parameters must all be int values.  This is
+     an historic accident and would be done differently today.  For
+     x86-64 all integer values are passed as 64-bit values and
+     therefore extending the API to copy 64-bit values instead of
+     32-bit ints makes sense.  It does not break existing
+     functionality and it does not violate the standard which says
+     that passing non-int values means undefined behavior.  */
+  for (i = 0; i < argc; ++i)
+    switch (i)
+      {
+      case 0:
+       ucp->uc_mcontext.gregs[REG_RDI] = va_arg (ap, greg_t);
+       break;
+      case 1:
+       ucp->uc_mcontext.gregs[REG_RSI] = va_arg (ap, greg_t);
+       break;
+      case 2:
+       ucp->uc_mcontext.gregs[REG_RDX] = va_arg (ap, greg_t);
+       break;
+      case 3:
+       ucp->uc_mcontext.gregs[REG_RCX] = va_arg (ap, greg_t);
+       break;
+      case 4:
+       ucp->uc_mcontext.gregs[REG_R8] = va_arg (ap, greg_t);
+       break;
+      case 5:
+       ucp->uc_mcontext.gregs[REG_R9] = va_arg (ap, greg_t);
+       break;
+      default:
+       /* Put value on stack.  */
+       sp[i - 5] = va_arg (ap, greg_t);
+       break;
+      }
+  va_end (ap);
+
+}
+
+
+weak_alias (__makecontext, makecontext)
diff --git a/libc/sysdeps/linux/x86_64/setcontext.S 
b/libc/sysdeps/linux/x86_64/setcontext.S
new file mode 100644
index 0000000..561ab9f
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/setcontext.S
@@ -0,0 +1,103 @@
+/* Install given context.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <a...@suse.de>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+
+/*  int __setcontext (const ucontext_t *ucp)
+
+  Restores the machine context in UCP and thereby resumes execution
+  in that context.
+
+  This implementation is intended to be used for *synchronous* context
+  switches only.  Therefore, it does not have to restore anything
+  other than the PRESERVED state.  */
+
+ENTRY(__setcontext)
+       /* Save argument since syscall will destroy it.  */
+       pushq   %rdi
+       cfi_adjust_cfa_offset(8)
+
+       /* Set the signal mask with
+          rt_sigprocmask (SIG_SETMASK, mask, NULL, _NSIG/8).  */
+       leaq    oSIGMASK(%rdi), %rsi
+       xorl    %edx, %edx
+       movl    $SIG_SETMASK, %edi
+       movl    $_NSIG8,%r10d
+       movl    $__NR_rt_sigprocmask, %eax
+       syscall
+       popq    %rdi                    /* Reload %rdi, adjust stack.  */
+       cfi_adjust_cfa_offset(-8)
+       cmpq    $-4095, %rax            /* Check %rax for error.  */
+       jae     SYSCALL_ERROR_LABEL     /* Jump to error handler if error.  */
+
+       /* Restore the floating-point context.  Not the registers, only the
+          rest.  */
+       movq    oFPREGS(%rdi), %rcx
+       fldenv  (%rcx)
+       ldmxcsr oMXCSR(%rdi)
+
+
+       /* Load the new stack pointer, the preserved registers and
+          registers used for passing args.  */
+       cfi_def_cfa(%rdi, 0)
+       cfi_offset(%rbx,oRBX)
+       cfi_offset(%rbp,oRBP)
+       cfi_offset(%r12,oR12)
+       cfi_offset(%r13,oR13)
+       cfi_offset(%r14,oR14)
+       cfi_offset(%r15,oR15)
+       cfi_offset(%rsp,oRSP)
+       cfi_offset(%rip,oRIP)
+
+       movq    oRSP(%rdi), %rsp
+       movq    oRBX(%rdi), %rbx
+       movq    oRBP(%rdi), %rbp
+       movq    oR12(%rdi), %r12
+       movq    oR13(%rdi), %r13
+       movq    oR14(%rdi), %r14
+       movq    oR15(%rdi), %r15
+
+       /* The following ret should return to the address set with
+       getcontext.  Therefore push the address on the stack.  */
+       movq    oRIP(%rdi), %rcx
+       pushq   %rcx
+
+       movq    oRSI(%rdi), %rsi
+       movq    oRDX(%rdi), %rdx
+       movq    oRCX(%rdi), %rcx
+       movq    oR8(%rdi), %r8
+       movq    oR9(%rdi), %r9
+
+       /* Setup finally  %rdi.  */
+       movq    oRDI(%rdi), %rdi
+
+       /* End FDE here, we fall into another context.  */
+       cfi_endproc
+       cfi_startproc
+
+       /* Clear rax to indicate success.  */
+       xorl    %eax, %eax
+L(pseudo_end):
+       ret
+PSEUDO_END(__setcontext)
+
+weak_alias (__setcontext, setcontext)
diff --git a/libc/sysdeps/linux/x86_64/swapcontext.S 
b/libc/sysdeps/linux/x86_64/swapcontext.S
new file mode 100644
index 0000000..6d2ebb8
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/swapcontext.S
@@ -0,0 +1,121 @@
+/* Save current context and install the given one.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <a...@suse.de>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+
+/* int __swapcontext (ucontext_t *oucp, const ucontext_t *ucp);
+
+  Saves the machine context in oucp such that when it is activated,
+  it appears as if __swapcontextt() returned again, restores the
+  machine context in ucp and thereby resumes execution in that
+  context.
+
+  This implementation is intended to be used for *synchronous* context
+  switches only.  Therefore, it does not have to save anything
+  other than the PRESERVED state.  */
+
+ENTRY(__swapcontext)
+       /* Save the preserved registers, the registers used for passing args,
+          and the return address.  */
+       movq    %rbx, oRBX(%rdi)
+       movq    %rbp, oRBP(%rdi)
+       movq    %r12, oR12(%rdi)
+       movq    %r13, oR13(%rdi)
+       movq    %r14, oR14(%rdi)
+       movq    %r15, oR15(%rdi)
+
+       movq    %rdi, oRDI(%rdi)
+       movq    %rsi, oRSI(%rdi)
+       movq    %rdx, oRDX(%rdi)
+       movq    %rcx, oRCX(%rdi)
+       movq    %r8, oR8(%rdi)
+       movq    %r9, oR9(%rdi)
+
+       movq    (%rsp), %rcx
+       movq    %rcx, oRIP(%rdi)
+       leaq    8(%rsp), %rcx           /* Exclude the return address.  */
+       movq    %rcx, oRSP(%rdi)
+
+       /* We have separate floating-point register content memory on the
+          stack.  We use the __fpregs_mem block in the context.  Set the
+          links up correctly.  */
+       leaq    oFPREGSMEM(%rdi), %rcx
+       movq    %rcx, oFPREGS(%rdi)
+       /* Save the floating-point environment.  */
+       fnstenv (%rcx)
+       stmxcsr oMXCSR(%rdi)
+
+
+       /* The syscall destroys some registers, save them.  */
+       movq    %rsi, %r12
+
+       /* Save the current signal mask and install the new one with
+          rt_sigprocmask (SIG_BLOCK, newset, oldset,_NSIG/8).  */
+       leaq    oSIGMASK(%rdi), %rdx
+       leaq    oSIGMASK(%rsi), %rsi
+       movl    $SIG_SETMASK, %edi
+       movl    $_NSIG8,%r10d
+       movl    $__NR_rt_sigprocmask, %eax
+       syscall
+       cmpq    $-4095, %rax            /* Check %rax for error.  */
+       jae     SYSCALL_ERROR_LABEL     /* Jump to error handler if error.  */
+
+       /* Restore destroyed registers.  */
+       movq    %r12, %rsi
+
+       /* Restore the floating-point context.  Not the registers, only the
+          rest.  */
+       movq    oFPREGS(%rsi), %rcx
+       fldenv  (%rcx)
+       ldmxcsr oMXCSR(%rsi)
+
+       /* Load the new stack pointer and the preserved registers.  */
+       movq    oRSP(%rsi), %rsp
+       movq    oRBX(%rsi), %rbx
+       movq    oRBP(%rsi), %rbp
+       movq    oR12(%rsi), %r12
+       movq    oR13(%rsi), %r13
+       movq    oR14(%rsi), %r14
+       movq    oR15(%rsi), %r15
+
+       /* The following ret should return to the address set with
+       getcontext.  Therefore push the address on the stack.  */
+       movq    oRIP(%rsi), %rcx
+       pushq   %rcx
+
+       /* Setup registers used for passing args.  */
+       movq    oRDI(%rsi), %rdi
+       movq    oRDX(%rsi), %rdx
+       movq    oRCX(%rsi), %rcx
+       movq    oR8(%rsi), %r8
+       movq    oR9(%rsi), %r9
+
+       /* Setup finally  %rsi.  */
+       movq    oRSI(%rsi), %rsi
+
+       /* Clear rax to indicate success.  */
+       xorl    %eax, %eax
+L(pseudo_end):
+       ret
+PSEUDO_END(__swapcontext)
+
+weak_alias (__swapcontext, swapcontext)
diff --git a/libc/sysdeps/linux/x86_64/ucontext_i.sym 
b/libc/sysdeps/linux/x86_64/ucontext_i.sym
new file mode 100644
index 0000000..af3e0e5
--- /dev/null
+++ b/libc/sysdeps/linux/x86_64/ucontext_i.sym
@@ -0,0 +1,37 @@
+#include <stddef.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+
+--
+
+SIG_BLOCK
+SIG_SETMASK
+
+_NSIG8         (_NSIG / 8)
+
+#define ucontext(member)       offsetof (ucontext_t, member)
+#define mcontext(member)       ucontext (uc_mcontext.member)
+#define mreg(reg)              mcontext (gregs[REG_##reg])
+
+oRBP           mreg (RBP)
+oRSP           mreg (RSP)
+oRBX           mreg (RBX)
+oR8            mreg (R8)
+oR9            mreg (R9)
+oR10           mreg (R10)
+oR11           mreg (R11)
+oR12           mreg (R12)
+oR13           mreg (R13)
+oR14           mreg (R14)
+oR15           mreg (R15)
+oRDI           mreg (RDI)
+oRSI           mreg (RSI)
+oRDX           mreg (RDX)
+oRAX           mreg (RAX)
+oRCX           mreg (RCX)
+oRIP           mreg (RIP)
+oEFL           mreg (EFL)
+oFPREGS                mcontext (fpregs)
+oSIGMASK       ucontext (uc_sigmask)
+oFPREGSMEM     ucontext (__fpregs_mem)
+oMXCSR         ucontext (__fpregs_mem.mxcsr)
-- 
1.7.10.4

_______________________________________________
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to