User context control for C6x: {get,make,set,swap}context.

* the registers (A0-A15,B0-B16) are saved and restored. I probably could leave out some of them? * mcontext_t specifies a 'PC' register, but its offset is too large to use it conveniently, so it is saved elsewhere (in ucontext_regspace). Is that a problem? * register B16 seems to hold the program counter, but that is fairly undocumented. * C6000 EABI specifies 10 registers to hold function arguments, but Linux assumes (and exposes) only 6 of them. The implementation below also only uses 6 registers for this. Am I getting into trouble when makecontext targets a function with more than 6 arguments? * I use the callee-preserved register A14 to hold the pointer to the next context (uc_link). Any issues here?

Signed-off by: Timon ter Braak <[email protected]>
---
 extra/Configs/Config.c6x              |   1 +
 libc/sysdeps/linux/c6x/Makefile.arch  |   4 +
 libc/sysdeps/linux/c6x/getcontext.S   |  93 +++++++++++++++++++++
 libc/sysdeps/linux/c6x/makecontext.c  |  87 ++++++++++++++++++++
libc/sysdeps/linux/c6x/setcontext.S | 148 ++++++++++++++++++++++++++++++++++
 libc/sysdeps/linux/c6x/swapcontext.c  |  25 ++++++
 libc/sysdeps/linux/c6x/sys/ucontext.h |  32 ++++++++
 libc/sysdeps/linux/c6x/ucontext_i.sym |  40 +++++++++
 8 files changed, 430 insertions(+)
 create mode 100644 libc/sysdeps/linux/c6x/getcontext.S
 create mode 100644 libc/sysdeps/linux/c6x/makecontext.c
 create mode 100644 libc/sysdeps/linux/c6x/setcontext.S
 create mode 100644 libc/sysdeps/linux/c6x/swapcontext.c
 create mode 100644 libc/sysdeps/linux/c6x/ucontext_i.sym

diff --git a/extra/Configs/Config.c6x b/extra/Configs/Config.c6x
index 96adfb3..1c3a838 100644
--- a/extra/Configs/Config.c6x
+++ b/extra/Configs/Config.c6x
@@ -11,6 +11,7 @@ config FORCE_OPTIONS_FOR_ARCH
        default y
        select ARCH_ANY_ENDIAN
        select ARCH_HAS_NO_MMU
+       select ARCH_HAS_UCONTEXT

 choice
        prompt "Target Processor Type"
diff --git a/libc/sysdeps/linux/c6x/Makefile.arch b/libc/sysdeps/linux/c6x/Makefile.arch
index 29c3b5d..223e30e 100644
--- a/libc/sysdeps/linux/c6x/Makefile.arch
+++ b/libc/sysdeps/linux/c6x/Makefile.arch
@@ -8,3 +8,7 @@
 CSRC-y := brk.c syscall.c prctl.c

 SSRC-y := __longjmp.S bsd-_setjmp.S bsd-setjmp.S clone.S setjmp.S _vfork.S
+
+CSRC-$(UCLIBC_HAS_CONTEXT_FUNCS) += makecontext.c
+SSRC-$(UCLIBC_HAS_CONTEXT_FUNCS) += getcontext.S setcontext.S swapcontext.S
+
diff --git a/libc/sysdeps/linux/c6x/getcontext.S b/libc/sysdeps/linux/c6x/getcontext.S
new file mode 100644
index 0000000..20d23ee
--- /dev/null
+++ b/libc/sysdeps/linux/c6x/getcontext.S
@@ -0,0 +1,93 @@
+/* Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   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 <ucontext_i.h>
+
+/* int getcontext (ucontext_t *ucp) */
+
+.global __getcontext
+.type __getcontext,%function
+.align 2
+__getcontext:
+       STW  .D1T1      A0,*+A4(MCONTEXT_C6X_A0)
+       STW  .D1T1      A1,*+A4(MCONTEXT_C6X_A1)
+       STW  .D1T1      A2,*+A4(MCONTEXT_C6X_A2)
+       STW  .D1T1      A3,*+A4(MCONTEXT_C6X_A3)
+       STW  .D1T1      A4,*+A4(MCONTEXT_C6X_A4)
+       STW  .D1T1      A5,*+A4(MCONTEXT_C6X_A5)
+       STW  .D1T1      A6,*+A4(MCONTEXT_C6X_A6)
+       STW  .D1T1      A7,*+A4(MCONTEXT_C6X_A7)
+       STW  .D1T1      A8,*+A4(MCONTEXT_C6X_A8)
+       STW  .D1T1      A9,*+A4(MCONTEXT_C6X_A9)
+
+       STW  .D1T2      B0,*+A4(MCONTEXT_C6X_B0)
+       STW  .D1T2      B1,*+A4(MCONTEXT_C6X_B1)
+       STW  .D1T2      B2,*+A4(MCONTEXT_C6X_B2)
+       STW  .D1T2      B3,*+A4(MCONTEXT_C6X_B3)
+       STW  .D1T2      B4,*+A4(MCONTEXT_C6X_B4)
+       STW  .D1T2      B5,*+A4(MCONTEXT_C6X_B5)
+       STW  .D1T2      B6,*+A4(MCONTEXT_C6X_B6)
+       STW  .D1T2      B7,*+A4(MCONTEXT_C6X_B7)
+       STW  .D1T2      B8,*+A4(MCONTEXT_C6X_B8)
+       STW  .D1T2      B9,*+A4(MCONTEXT_C6X_B9)
+
+       MV   .D2X       A4,B6
+||     MV   .S1        A4,A6
+
+       ADDK .S1        UCONTEXT_REGSPACE,A6
+||     ADDK .S2        UCONTEXT_REGSPACE,B6
+
+        STW .D1T1       A10,*+A6(0)
+||      STW .D2T2       B10,*+B6(4)
+        STW .D1T1       A11,*+A6(8)
+||      STW .D2T2       B11,*+B6(12)
+        STW .D1T1       A12,*+A6(16)
+||      STW .D2T2       B12,*+B6(20)
+        STW .D1T1       A13,*+A6(24)
+||      STW .D2T2       B13,*+B6(28)
+        STW .D1T1       A14,*+A6(32)
+||      STW .D2T2       B14,*+B6(36)
+        STW .D1T1       A15,*+A6(40)
+||      STW .D2T2       B15,*+B6(44)
+        STW .D1T1       A16,*+A6(48)
+||      STW .D2T2       B16,*+B6(52)
+
+       ; Save ucontext_t* across the next call
+       MV  .D1         A4,A8
+||     MVK .S1         UCONTEXT_SIGMASK,A6             ; uc_sigmask address
+
+       ; int sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask))
+       ; A4: SIG_BLOCK
+       ; B4: 0
+       ; A6: uc_sigmask
+       MVK  .D1        SIG_BLOCK,A4
+||     ZERO .S2        B4
+||     ADD  .S1        A8,A6,A6
+
+        B    .S2       sigprocmask
+        mvkl .S2       1f, B3
+        mvkh .S2       1f, B3
+        nop             3
+1:
+       ; Restore clobbered link register
+       LDW  .D1T2      *+A8(MCONTEXT_C6X_B3),B3
+
+       ; Return with value 0
+       ZERO .L1        A4
+||     RET  .S2        B3
+
+       NOP             5                       ; Delay slots for branch
+
+.size __getcontext,.-__getcontext
+weak_alias(__getcontext, getcontext)
diff --git a/libc/sysdeps/linux/c6x/makecontext.c b/libc/sysdeps/linux/c6x/makecontext.c
new file mode 100644
index 0000000..935f866
--- /dev/null
+++ b/libc/sysdeps/linux/c6x/makecontext.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   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 <stdarg.h>
+#include <ucontext.h>
+
+/* Number of arguments that go in registers.
+   Note: convention says 10!?
+   Linux sigcontext.h only exposes 6.
+*/
+#define NREG_ARGS  6
+
+/* Take a context previously prepared via getcontext() and set to
+   call func() with the given int only args.  */
+void
+__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
+{
+  extern void __startcontext (void);
+  unsigned long *funcstack;
+  va_list vl;
+  int misaligned;
+  int i;
+
+  /* Start at the top of stack.  */
+ funcstack = (unsigned long *) (ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
+
+  /* Ensure the stack stays eight byte aligned.  */
+  misaligned = ((unsigned long) funcstack & 4) != 0;
+
+  if ((argc > NREG_ARGS) && (argc & 1) != 0)
+    misaligned = !misaligned;
+
+  if (misaligned)
+    funcstack -= 1;
+
+  /* Reserve space for the on-stack arguments.  */
+  if (argc > NREG_ARGS)
+    funcstack -= (argc - NREG_ARGS);
+
+  /* Exit to startcontext() with the next context in A14 */
+  ucp->uc_regspace[A14] = (unsigned long) ucp->uc_link;
+  ucp->uc_mcontext.sc_b3 = (unsigned long) __startcontext;
+
+  ucp->uc_regspace[B15] = (unsigned long) funcstack;
+  ucp->uc_regspace[B16] = (unsigned long) func;
+
+  va_start (vl, argc);
+  for (i = 0; i < argc ; i++) {
+       /* The first ten arguments go into registers.  */
+       switch (i) {
+       case 0:
+               ucp->uc_mcontext.sc_a4 = va_arg(vl, unsigned long);
+               break;
+       case 1:
+               ucp->uc_mcontext.sc_b4 = va_arg(vl, unsigned long);
+               break;
+       case 2:
+               ucp->uc_mcontext.sc_a6 = va_arg(vl, unsigned long);
+               break;
+       case 3:
+               ucp->uc_mcontext.sc_b6 = va_arg(vl, unsigned long);
+               break;
+       case 4:
+               ucp->uc_mcontext.sc_a8 = va_arg(vl, unsigned long);
+               break;
+       case 5:
+               ucp->uc_mcontext.sc_b8 = va_arg(vl, unsigned long);
+               break;
+       default:
+               /* And the remainder on the stack.  */
+               *funcstack++ = va_arg (vl, unsigned long);
+       }
+  }
+  va_end (vl);
+}
+weak_alias (__makecontext, makecontext)
diff --git a/libc/sysdeps/linux/c6x/setcontext.S b/libc/sysdeps/linux/c6x/setcontext.S
new file mode 100644
index 0000000..a9641b9
--- /dev/null
+++ b/libc/sysdeps/linux/c6x/setcontext.S
@@ -0,0 +1,148 @@
+/* Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   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 <ucontext_i.h>
+
+.text
+.macro do_call fn
+#ifdef _TMS320C6400_PLUS
+        callp   .s2     (\fn), B3
+#elif defined(_TMS320C6400)
+        call    .s2     (\fn)
+        addkpc  .s2     9f, B3, 0
+        nop             4
+9:
+#else
+        call    .s2     (\fn)
+        mhkl    .s2     9f, B3
+        mhkh    .s2     9f, B3
+        nop             3
+9:
+#endif
+.endm
+
+/* int setcontext (const ucontext_t *ucp) */
+
+.global __setcontext
+.type __setcontext,%function
+.align 2
+__setcontext:
+       MV   .D1        A4,A8                   ; save ucontext_t* across next 
call
+
+       ; int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
+       ; A4: Set signal mask
+       ; B4: Signal mask pointer
+       ; A6: 0 -> do not store current signal mask
+       MV   .D2X       A4,B4                   ; ucontext_t address
+||     MVK  .D1        SIG_SETMASK,A4
+
+       ADDK .S2        UCONTEXT_SIGMASK,B4     ; uc_sigmask address
+||     ZERO .S1        A6
+
+        B    .S2        sigprocmask
+        mvkl .S2        1f, B3
+        mvkh .S2        1f, B3
+        nop             3
+1:
+
+       MV   .D1        A8,A4
+||     MV   .D2X       A8,B4
+
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A0),A0
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B0),B0
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A1),A1
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B1),B1
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A2),A2
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B2),B2
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A3),A3
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B3),B3
+       ; Base registers are loaded later
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A5),A5
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B5),B5
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A6),A6
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B6),B6
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A7),A7
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B7),B7
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A8),A8
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B8),B8
+       LDW  .D1T1      *+A4(MCONTEXT_C6X_A9),A9
+||      LDW  .D2T2     *+B4(MCONTEXT_C6X_B9),B9
+
+       MV   .D1        A4,A10                  ; Keep copy of ucontext_t*
+||     MV   .D2X       A4,B10                  ; Keep copy of ucontext_t*
+
+       ADDK .S1        UCONTEXT_REGSPACE,A10   ; uc_regspace address
+||     ADDK .S2        UCONTEXT_REGSPACE,B10   ; uc_regspace address
+
+        LDW  .D1T1     *+A10(8),A11
+||      LDW  .D2T2     *+B10(12),B11
+
+        LDW  .D1T1     *+A10(16),A12
+||      LDW  .D2T2     *+B10(20),B12
+
+        LDW  .D1T1     *+A10(24),A13
+||      LDW  .D2T2     *+B10(28),B13
+
+       ; Load PC into B10 so that it is ready for the branch
+        LDW  .D1T1     *+A10(40), A15
+||     LDW  .D2T2      *+B10(52),B10
+
+        LDW  .D1T1     *+A10(32),A14
+||      LDW  .D2T2     *+B10(36),B14
+
+        ;; Loads have 4 delay slots.  Take advantage of this to restore the
+        ;; scratch registers and stack pointer before the base registers
+        ;; disappear.  We also need to make sure no interrupts occur,
+        ;; so put the whole thing in the delay slots of a dummy branch
+        ;; We can not move the ret earlier as that would cause it to occur
+        ;; before the last load completes
+        B    .S1       (2f)
+
+        LDW  .D1T1     *+A4(MCONTEXT_C6X_A4), A4
+||     LDW  .D2T2      *+B4(MCONTEXT_C6X_B4), B4
+
+       LDW  .D2T2      *+B10(44),B15
+
+       NOP             1
+
+        RET  .S2       B10
+
+        LDW  .D1T1     *+A10(0), A10
+||     LDW  .D2T2      *+B10(4), B10
+
+        NOP             1
+2:
+        NOP             3
+
+.size __setcontext,.-__setcontext
+weak_alias(__setcontext, setcontext)
+
+/* 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.  */
+.global __startcontext
+.type __startcontext,%function
+.align 2
+__startcontext:
+       MV   .D1        A14,A0
+
+   [A0]        B    .S1        __setcontext
+|| [A0] MV   .D1       A0,A4
+
+  [!A0]        B    .S2        _exit
+
+       NOP             6
+
diff --git a/libc/sysdeps/linux/c6x/swapcontext.c b/libc/sysdeps/linux/c6x/swapcontext.c
new file mode 100644
index 0000000..cc9d3b8
--- /dev/null
+++ b/libc/sysdeps/linux/c6x/swapcontext.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   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 <stdarg.h>
+#include <ucontext.h>
+
+int
+swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
+{
+        if(getcontext(oucp) == 0)
+                setcontext(ucp);
+        // TODO: set errno if we know why it failed
+        return -1;
+}
diff --git a/libc/sysdeps/linux/c6x/sys/ucontext.h b/libc/sysdeps/linux/c6x/sys/ucontext.h
index 476fc73..a2b2be9 100644
--- a/libc/sysdeps/linux/c6x/sys/ucontext.h
+++ b/libc/sysdeps/linux/c6x/sys/ucontext.h
@@ -22,6 +22,37 @@
 #include <signal.h>
 #include <bits/sigcontext.h>

+enum {
+        A10,
+#define A10 A10
+        B10,
+#define B10 B10
+        A11,
+#define A11 A11
+        B11,
+#define B11 B11
+        A12,
+#define A12 A12
+        B12,
+#define B12 B12
+        A13,
+#define A13 A13
+        B13,
+#define B13 B13
+        A14,
+#define A14 A14
+        B14,
+#define B14 B14
+        A15,
+#define A15 A15
+        B15,
+#define B15 B15
+        A16,
+#define A16 A16
+        B16
+#define B16 B16
+};
+
 /* A machine context is exactly a sigcontext.  */
 typedef struct sigcontext mcontext_t;

@@ -33,6 +64,7 @@ typedef struct ucontext
        stack_t          uc_stack;
        mcontext_t       uc_mcontext;
        __sigset_t       uc_sigmask;
+        unsigned long    uc_regspace[14] __attribute__((__aligned__(8)));
 } ucontext_t;

 #endif /* sys/ucontext.h */
diff --git a/libc/sysdeps/linux/c6x/ucontext_i.sym b/libc/sysdeps/linux/c6x/ucontext_i.sym
new file mode 100644
index 0000000..ff4c252
--- /dev/null
+++ b/libc/sysdeps/linux/c6x/ucontext_i.sym
@@ -0,0 +1,40 @@
+#include <inttypes.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/ucontext.h>
+
+SIG_BLOCK
+SIG_SETMASK
+
+-- Offsets of the fields in the ucontext_t structure.
+#define ucontext(member)        offsetof (ucontext_t, member)
+#define mcontext(member)        ucontext (uc_mcontext.member)
+
+UCONTEXT_SIGMASK                ucontext (uc_sigmask)
+UCONTEXT_REGSPACE               ucontext (uc_regspace)
+
+MCONTEXT_C6X_SP                        mcontext(sc_sp)
+MCONTEXT_C6X_PC                        mcontext(sc_pc)
+
+MCONTEXT_C6X_A0                        mcontext(sc_a0)
+MCONTEXT_C6X_A1                        mcontext(sc_a1)
+MCONTEXT_C6X_A2                        mcontext(sc_a2)
+MCONTEXT_C6X_A3                        mcontext(sc_a3)
+MCONTEXT_C6X_A4                        mcontext(sc_a4)
+MCONTEXT_C6X_A5                        mcontext(sc_a5)
+MCONTEXT_C6X_A6                        mcontext(sc_a6)
+MCONTEXT_C6X_A7                        mcontext(sc_a7)
+MCONTEXT_C6X_A8                        mcontext(sc_a8)
+MCONTEXT_C6X_A9                        mcontext(sc_a9)
+
+MCONTEXT_C6X_B0                        mcontext(sc_b0)
+MCONTEXT_C6X_B1                        mcontext(sc_b1)
+MCONTEXT_C6X_B2                        mcontext(sc_b2)
+MCONTEXT_C6X_B3                        mcontext(sc_b3)
+MCONTEXT_C6X_B4                        mcontext(sc_b4)
+MCONTEXT_C6X_B5                        mcontext(sc_b5)
+MCONTEXT_C6X_B6                        mcontext(sc_b6)
+MCONTEXT_C6X_B7                        mcontext(sc_b7)
+MCONTEXT_C6X_B8                        mcontext(sc_b8)
+MCONTEXT_C6X_B9                        mcontext(sc_b9)
+
--
1.8.1.2

_______________________________________________
uClibc mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to