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

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

commit 09b1ed44d7fdaa499ba58aa1bdfd0b67fb34de28
Author: shenjunyuan <[email protected]>
AuthorDate: Tue Jun 2 20:52:14 2026 +0800

    libs/libc/machine/tricore: restore PSW and repair PRS/IO in longjmp
    
    setjmp saves PSW into env->psw at offset 4 but longjmp never wrote it
    back, leaving env->psw effectively unused and the PSW after the jump
    indeterminate (it ends up holding whatever the last popped UC carried).
    
    Worse, on PROTECTED builds where the jmp_buf lives in kernel-protected
    memory, the unwind loop itself can fault: each `ret` in .Lupper releases
    one Upper Context, and as a hardware side-effect the popped UC's PSW
    becomes the active PSW.  If that UC was pushed in user mode (PSW.IO !=
    Supervisor or PSW.PRS pointing at a user protection set), the very next
    load from [a4] in the loop traps on the MPU even though longjmp itself
    was entered in supervisor mode.
    
    Fix this in two places:
    
    1. Insert a PSW repair sequence at the head of .Lloop that forces
       PSW.IO=Supervisor and PSW.PRS=0 on every iteration before any
       access to env.  Two `insert` instructions write bits 10-13 and
       bit 15 independently so the S bit (bit 14, alternate master tag
       selector) is left untouched, and other PSW fields (CDC/CDE/IS/
       arithmetic flags) are preserved.  An `isync` after `mtcr` drains
       the pipeline so the following loads execute under the new PSW.
    
    2. After the unwind loop terminates and all callee-saved registers
       are reloaded from env, restore env->psw via mtcr + isync before
       the indirect jump back to the setjmp call site, so the user-
       visible PSW after longjmp matches the one captured at setjmp time.
    
    Both the TASKING and GCC inline-asm branches are updated.  d3 is used
    as the scratch register since d2 holds the longjmp return value at the
    recovery tail and must not be clobbered.
    
    Verified on TC4xx (TriCore 1.8) hardware: the previously reproducible
    MPU trap during longjmp unwind no longer occurs, and existing task
    entry / TerminateTask / ChainTask paths remain functional.
    
    Signed-off-by: shenjunyuan <[email protected]>
---
 libs/libc/machine/tricore/arch_setjmp.c | 40 +++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libs/libc/machine/tricore/arch_setjmp.c 
b/libs/libc/machine/tricore/arch_setjmp.c
index 5ddaceef4b6..41072d240f0 100644
--- a/libs/libc/machine/tricore/arch_setjmp.c
+++ b/libs/libc/machine/tricore/arch_setjmp.c
@@ -94,12 +94,39 @@ int setjmp(jmp_buf env)
   return 0;
 }
 
+/* The CSA unwind loop below uses `ret` in .Lupper to release one Upper
+ * Context per iteration.  The hardware side-effect of `ret` is that the
+ * popped UC's PSW becomes the active PSW, which may put the CPU into
+ * user mode / wrong PRS even though longjmp itself runs in supervisor
+ * mode.  The next access to env (a4) would then fault on the MPU, since
+ * jmp_buf may live in kernel-protected memory.
+ *
+ * To keep the unwind loop running in a known-safe context, the head of
+ * .Lloop forces PSW.IO=Supervisor and PSW.PRS=0 on every iteration.
+ * Once the loop terminates, the original PSW saved at setjmp time
+ * (env->psw) is restored before the indirect jump back to the setjmp
+ * call site.
+ *
+ * PSW layout on TC1.6.2 / TC1.8:
+ *   bit 15    = PRS[2]
+ *   bit 14    = S    (alternate master tag selector, must preserve)
+ *   bit 13:12 = PRS[1:0]
+ *   bit 11:10 = IO   (0b10 = Supervisor)
+ */
+
 void longjmp(jmp_buf env, int val)
 {
   __asm__ __volatile__ (
 #ifdef CONFIG_TRICORE_TOOLCHAIN_TASKING
     "ld.w   d5, [a4]0\n\t"
     ".Lloop:\n\t"
+    "mfcr   d3, #0xfe04\n\t"
+    "mov    d2, #2\n\t"
+    "insert d3, d3, d2, #10, #4\n\t"
+    "mov    d2, #0\n\t"
+    "insert d3, d3, d2, #15, #1\n\t"
+    "mtcr   #0xfe04, d3\n\t"
+    "isync\n\t"
     "mfcr   d2, #0xfe00\n\t"
     "jne    d2, d5, .Lrelease\n\t"
     "max.u  d2, d4, #1\n\t"
@@ -117,6 +144,9 @@ void longjmp(jmp_buf env, int val)
     "ld.w   d13, [a4]52\n\t"
     "ld.w   d14, [a4]56\n\t"
     "ld.w   d15, [a4]60\n\t"
+    "ld.w   d3, [a4]4\n\t"
+    "mtcr   #0xfe04, d3\n\t"
+    "isync\n\t"
     "ji     a2\n\t"
     ".Lrelease:\n\t"
     "jnz.t  d2, #20, .Lupper\n\t"
@@ -135,6 +165,13 @@ void longjmp(jmp_buf env, int val)
 #else
     "ld.w   %%d5, [%%a4]0\n\t"
     ".Lloop:\n\t"
+    "mfcr   %%d3, 0xfe04\n\t"
+    "mov    %%d2, 2\n\t"
+    "insert %%d3, %%d3, %%d2, 10, 4\n\t"
+    "mov    %%d2, 0\n\t"
+    "insert %%d3, %%d3, %%d2, 15, 1\n\t"
+    "mtcr   0xfe04, %%d3\n\t"
+    "isync\n\t"
     "mfcr   %%d2, 0xfe00\n\t"
     "jne    %%d2, %%d5, .Lrelease\n\t"
     "max.u  %%d2, %%d4, 1\n\t"
@@ -152,6 +189,9 @@ void longjmp(jmp_buf env, int val)
     "ld.w   %%d13, [%%a4]52\n\t"
     "ld.w %%d14, [%%a4]56\n\t"
     "ld.w   %%d15, [%%a4]60\n\t"
+    "ld.w   %%d3, [%%a4]4\n\t"
+    "mtcr   0xfe04, %%d3\n\t"
+    "isync\n\t"
     "ji     %%a2\n\t"
     ".Lrelease:\n\t"
     "jnz.t  %%d2, 20, .Lupper\n\t"

Reply via email to