[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