[PATCH] RFC: user context control for TI C6X
Timon ter Braak
timon at terbraak.org
Thu Feb 14 14:11:08 UTC 2013
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 <timon at terbraak.org>
---
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
More information about the uClibc
mailing list