You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/11/06 12:39:40 UTC

[incubator-nuttx] 03/03: arch:xtensa: add setjmp xtensa function

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 5a4140f020e3f00ac45f7084ec2bdac765b26f1e
Author: zhuyanlin <zh...@xiaomi.com>
AuthorDate: Tue Nov 2 17:25:03 2021 +0800

    arch:xtensa: add setjmp xtensa function
    
    N/A
    
    Signed-off-by: zhuyanlin <zh...@xiaomi.com>
---
 arch/Kconfig                           |   1 +
 libs/libc/machine/xtensa/Make.defs     |   4 +
 libs/libc/machine/xtensa/arch_setjmp.S | 374 +++++++++++++++++++++++++++++++++
 3 files changed, 379 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index 73a37d2..8720240 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -103,6 +103,7 @@ config ARCH_XTENSA
 	select ARCH_HAVE_CUSTOMOPT
 	select ARCH_HAVE_TESTSET
 	select ARCH_HAVE_STDARG_H
+	select ARCH_HAVE_SETJMP if ARCH_TOOLCHAIN_GNU
 	---help---
 		Cadence® Tensilica® Xtensa® actictures.
 
diff --git a/libs/libc/machine/xtensa/Make.defs b/libs/libc/machine/xtensa/Make.defs
index 379c7da..78fb412 100644
--- a/libs/libc/machine/xtensa/Make.defs
+++ b/libs/libc/machine/xtensa/Make.defs
@@ -34,6 +34,10 @@ ifeq ($(CONFIG_XTENSA_MEMSET),y)
 ASRCS += arch_memset.S
 endif
 
+ifeq ($(CONFIG_ARCH_SETJMP_H),y)
+ASRCS += arch_setjmp.S
+endif
+
 ifeq ($(CONFIG_XTENSA_STRCPY),y)
 ASRCS += arch_strcpy.S
 endif
diff --git a/libs/libc/machine/xtensa/arch_setjmp.S b/libs/libc/machine/xtensa/arch_setjmp.S
new file mode 100644
index 0000000..99b83af
--- /dev/null
+++ b/libs/libc/machine/xtensa/arch_setjmp.S
@@ -0,0 +1,374 @@
+/****************************************************************************
+ * libs/libc/machine/xtensa/arch_setjmp.S
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <arch/chip/core-isa.h>
+#include <arch/xtensa/xtensa_abi.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
+
+/* Windowed ABI:
+
+   This implementation relies heavily on the Xtensa register window
+   mechanism.  Setjmp flushes all the windows except its own to the
+   stack and then copies registers from the save areas on the stack
+   into the jmp_buf structure, along with the return address of the call
+   to setjmp.  Longjmp invalidates all the windows except its own, and
+   then sets things up so that it will return to the right place,
+   using a window underflow to automatically restore the registers.
+
+   Note that it would probably be sufficient to only copy the
+   registers from setjmp's caller into jmp_buf.  However, we also copy
+   the save area located at the stack pointer of setjmp's caller.
+   This save area will typically remain intact until the longjmp call.
+   The one exception is when there is an intervening alloca in
+   setjmp's caller.  This is certainly an unusual situation and is
+   likely to cause problems in any case (the storage allocated on the
+   stack cannot be safely accessed following the longjmp).  As bad as
+   it is, on most systems this situation would not necessarily lead to
+   a catastrophic failure.  If we did not preserve the extra save area
+   on Xtensa, however, it would.  When setjmp's caller returns after a
+   longjmp, there will be a window underflow; an invalid return
+   address or stack pointer in the save area will almost certainly
+   lead to a crash.  Keeping a copy of the extra save area in the
+   jmp_buf avoids this with only a small additional cost.  If setjmp
+   and longjmp are ever time-critical, this could be removed.
+*/
+
+  .text
+  .align  4
+  .literal_position
+  .global setjmp
+  .type setjmp, @function
+setjmp:
+
+# if XCHAL_HAVE_XEA3
+/*
+  a2 points to the jmp_buf structure of 68 bytes length:
+  8 * 4 to save the regester save area of setjmp that contains the callers resgisters
+  8 * 4 to save the caller's register save area which is pottentally
+  clobbered by an alloca() in the caller
+*/
+
+  entry sp, 32
+
+  /* Flush all registers.  */
+  ssai  0
+  spillw
+
+  addi  a7, a1, -32   # find the destination save area
+  s32i  a0, a2, 64
+
+/* Copy the callee register save area to jmp_buf */
+  l32i  a3, a7, 0
+  l32i  a4, a7, 4
+  s32i  a3, a2, 0
+  s32i  a4, a2, 4
+  l32i  a3, a7, 8
+  l32i  a4, a7, 12
+  s32i  a3, a2, 8
+  s32i  a4, a2, 12
+  l32i  a3, a7, 16
+  l32i  a4, a7, 20
+  s32i  a3, a2, 16
+  s32i  a4, a2, 20
+  l32i  a3, a7, 24
+  l32i  a4, a7, 28
+  s32i  a3, a2, 24
+  s32i  a4, a2, 28
+
+/* keep copy of callee register save area to protect against an
+   alloca() (after the setjmp) clobbering the registers needed to return from
+   the caller of setjmp */
+
+  l32i  a3, a1, 0
+  l32i  a4, a1, 4
+  s32i  a3, a2, 32
+  s32i  a4, a2, 36
+  l32i  a3, a1, 8
+  l32i  a4, a1, 12
+  s32i  a3, a2, 40
+  s32i  a4, a2, 44
+  l32i  a3, a1, 16
+  l32i  a4, a1, 20
+  s32i  a3, a2, 48
+  s32i  a4, a2, 52
+  l32i  a3, a1, 24
+  l32i  a4, a1, 28
+  s32i  a3, a2, 56
+  s32i  a4, a2, 60
+# else
+  entry sp, 16
+
+  /* Flush registers.  */
+  mov a4, a2      # save a2 (jmp_buf)
+  movi  a2, 0
+  syscall
+  mov a2, a4      # restore a2
+
+  /* Copy the register save area at (sp - 16).  */
+  addi  a5, a1, -16
+  l32i  a3, a5, 0
+  l32i  a4, a5, 4
+  s32i  a3, a2, 0
+  s32i  a4, a2, 4
+  l32i  a3, a5, 8
+  l32i  a4, a5, 12
+  s32i  a3, a2, 8
+  s32i  a4, a2, 12
+
+  /* Copy 0-8 words from the register overflow area.  */
+  extui a3, a0, 30, 2
+  blti  a3, 2, .Lendsj
+  l32i  a7, a1, 4
+  slli  a4, a3, 4
+  sub a5, a7, a4
+  addi  a6, a2, 16
+  addi  a7, a7, -16   # a7 = end of register overflow area
+.Lsjloop:
+  l32i  a3, a5, 0
+  l32i  a4, a5, 4
+  s32i  a3, a6, 0
+  s32i  a4, a6, 4
+  l32i  a3, a5, 8
+  l32i  a4, a5, 12
+  s32i  a3, a6, 8
+  s32i  a4, a6, 12
+  addi  a5, a5, 16
+  addi  a6, a6, 16
+  blt a5, a7, .Lsjloop
+.Lendsj:
+
+  /* Copy the register save area at sp.  */
+  l32i  a3, a1, 0
+  l32i  a4, a1, 4
+  s32i  a3, a2, 48
+  s32i  a4, a2, 52
+  l32i  a3, a1, 8
+  l32i  a4, a1, 12
+  s32i  a3, a2, 56
+  s32i  a4, a2, 60
+
+  /* Save the return address, including the window size bits.  */
+  s32i  a0, a2, 64
+# endif
+
+  movi  a2, 0
+  retw
+  .size setjmp, . - setjmp
+
+/* void longjmp (jmp_buf env, int val) */
+
+  .align  4
+  .literal_position
+  .global longjmp
+  .type longjmp, @function
+longjmp:
+  /*  a2 == &env, a3 == val  */
+#if XCHAL_HAVE_XEA3
+  entry sp, 32
+  ssai  0
+  tossw
+
+  l32i a0, a2, 64
+
+  addi  a7, a1, -32   # find the destination save area
+  l32i  a4, a2, 0
+  l32i  a5, a2, 4
+  s32i  a4, a7, 0
+  s32i  a5, a7, 4
+  l32i  a4, a2, 8
+  l32i  a5, a2, 12
+  s32i  a4, a7, 8
+  s32i  a5, a7, 12
+  l32i  a4, a2, 16
+  l32i  a5, a2, 20
+  s32i  a4, a7, 16
+  s32i  a5, a7, 20
+  l32i  a4, a2, 24
+  l32i  a5, a2, 28
+  s32i  a4, a7, 24
+  s32i  a5, a7, 28
+
+  /* The 8 words saved from the register save area at the target's
+     sp are copied back to the target procedure's save area.  The
+     only point of this is to prevent a catastrophic failure in
+     case the contents were moved by an alloca after calling
+     setjmp.  This is a bit paranoid but it doesn't cost much.
+   */
+
+  l32i  a7, a2, 4 /* get the stack pointer as it was at the call to setjmp() ...
+                before any changed due to alloca() */
+      addi  a7, a7, -32
+  l32i  a4, a2, 32 /* copy the register values from the jmp_buf to the
+                possibly clobbered register save area */
+  l32i  a5, a2, 36
+  s32i  a4, a7, 0
+  s32i  a5, a7, 4
+  l32i  a4, a2, 40
+  l32i  a5, a2, 44
+  s32i  a4, a7, 8
+  s32i  a5, a7, 12
+  l32i  a4, a2, 48
+  l32i  a5, a2, 52
+  s32i  a4, a7, 16
+  s32i  a5, a7, 20
+  l32i  a4, a2, 56
+  l32i  a5, a2, 60
+  s32i  a4, a7, 24
+  s32i  a5, a7, 28
+
+#else
+  entry sp, 16
+
+# if XCHAL_MAYHAVE_ERRATUM_XEA1KWIN
+  /* Using this register triggers early any overflow that a kernel-mode
+     level-one interrupt might otherwise cause.  */
+#  define AR_WB a15
+# else
+  /* Using this register is more efficient; it triggers less overflows.  */
+#  define AR_WB a5
+# endif
+  /* Invalidate all but the current window;
+     set WindowStart to (1 << WindowBase).  */
+  rsr AR_WB, WINDOWBASE
+  movi  a4, 1
+  ssl AR_WB
+  sll a4, a4
+  wsr a4, WINDOWSTART
+  rsync
+
+  /* Return to the return address of the setjmp, using the
+     window size bits from the setjmp call so that the caller
+     will be able to find the return value that we put in a2.  */
+
+  l32i  a0, a2, 64
+
+  /* Copy the first 4 saved registers from jmp_buf into the save area
+     at the current sp so that the values will be restored to registers
+     when longjmp returns.  */
+
+  addi  a7, a1, -16
+  l32i  a4, a2, 0
+  l32i  a5, a2, 4
+  s32i  a4, a7, 0
+  s32i  a5, a7, 4
+  l32i  a4, a2, 8
+  l32i  a5, a2, 12
+  s32i  a4, a7, 8
+  s32i  a5, a7, 12
+
+  /* Copy the remaining 0-8 saved registers.  */
+  extui a7, a0, 30, 2
+  blti  a7, 2, .Lendlj
+  l32i  a8, a2, 52
+  slli  a4, a7, 4
+  sub a6, a8, a4
+  addi  a5, a2, 16
+  addi  a8, a8, -16   # a8 = end of register overflow area
+.Lljloop:
+  l32i  a7, a5, 0
+  l32i  a4, a5, 4
+  s32i  a7, a6, 0
+  s32i  a4, a6, 4
+  l32i  a7, a5, 8
+  l32i  a4, a5, 12
+  s32i  a7, a6, 8
+  s32i  a4, a6, 12
+  addi  a5, a5, 16
+  addi  a6, a6, 16
+  blt a6, a8, .Lljloop
+.Lendlj:
+
+  /* The 4 words saved from the register save area at the target's
+     sp are copied back to the target procedure's save area.  The
+     only point of this is to prevent a catastrophic failure in
+     case the contents were moved by an alloca after calling
+     setjmp.  This is a bit paranoid but it doesn't cost much.  */
+
+  l32i  a7, a2, 4   # load the target stack pointer
+  addi  a7, a7, -16   # find the destination save area
+  l32i  a4, a2, 48
+  l32i  a5, a2, 52
+  s32i  a4, a7, 0
+  s32i  a5, a7, 4
+  l32i  a4, a2, 56
+  l32i  a5, a2, 60
+  s32i  a4, a7, 8
+  s32i  a5, a7, 12
+#endif
+
+  /* Return val ? val : 1.  */
+  movi  a2, 1
+  movnez  a2, a3, a3
+
+  retw
+  .size longjmp, . - longjmp
+
+#else
+
+  /*
+   Call0 ABI:
+   Much like other ABIs, this version just saves the necessary registers
+   to the stack and restores them later.  Much less needs to be done.
+  */
+
+  .text
+  .align  4
+  .literal_position
+  .global setjmp
+  .type setjmp, @function
+setjmp:
+  s32i  a0, a2, 0
+  s32i  a1, a2, 4
+  s32i  a12, a2, 8
+  s32i  a13, a2, 12
+  s32i  a14, a2, 16
+  s32i  a15, a2, 20
+  movi  a2, 0
+  ret
+  .size setjmp, . - setjmp
+
+  .align  4
+  .literal_position
+  .global longjmp
+  .type longjmp, @function
+longjmp:
+  l32i  a0, a2, 0
+  l32i  a12, a2, 8
+  l32i  a13, a2, 12
+  l32i  a14, a2, 16
+  l32i  a15, a2, 20
+  l32i  a1, a2, 4
+  /* Return val ? val : 1.  */
+  movi  a2, 1
+  movnez  a2, a3, a3
+
+  ret
+  .size longjmp, .-longjmp
+
+#endif /* CALL0 ABI */