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"
