From f8ebc827f2b0aed23295886543c0823c908dcee1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 10 Sep 2026 21:05:39 +0100 Subject: [PATCH 1/3] ARM: 9482/5: decompressor: always set endianess on xscale In BE32 configurations, endianess is always set to BE in Control Register C1, originally to allow booting BE kernels from a LE bootloader on IXP12xx. To also allow the opposite and allow booting LE kernels from a BE IXP4xx bootloader, moving this code into head-xscale.S along with the recommended serialization sequence and flip the B bit into the correct position regardless of its original state. Note that both the endianess switch sequence and the cp15wait I add here should be harmless on all supported ARMv4/v4t/v5 CPUs, but the existing cache flush code is not actually defined on ARM720 and may cause problems in multiplatform kernels that include both Xscale and CLPS711x. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/boot/compressed/Makefile | 8 -------- arch/arm/boot/compressed/big-endian.S | 14 -------------- arch/arm/boot/compressed/head-xscale.S | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 arch/arm/boot/compressed/big-endian.S diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index e3f550d62857..5993dd1d0ee9 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -41,14 +41,6 @@ ifeq ($(CONFIG_PXA_SHARPSL_DETECT_MACH_ID),y) OBJS += head-sharpsl.o endif -ifeq ($(CONFIG_CPU_ENDIAN_BE32),y) -ifeq ($(CONFIG_CPU_CP15),y) -OBJS += big-endian.o -else -# The endian should be set by h/w design. -endif -endif - # # We now have a PIC decompressor implementation. Decompressors running # from RAM should not define ZTEXTADDR. Decompressors running directly diff --git a/arch/arm/boot/compressed/big-endian.S b/arch/arm/boot/compressed/big-endian.S deleted file mode 100644 index 0e092c36da2f..000000000000 --- a/arch/arm/boot/compressed/big-endian.S +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * linux/arch/arm/boot/compressed/big-endian.S - * - * Switch CPU into big endian mode. - * Author: Nicolas Pitre - */ - - .section ".start", "ax" - - mrc p15, 0, r0, c1, c0, 0 @ read control reg - orr r0, r0, #(1 << 7) @ enable big endian mode - mcr p15, 0, r0, c1, c0, 0 @ write control reg - diff --git a/arch/arm/boot/compressed/head-xscale.S b/arch/arm/boot/compressed/head-xscale.S index 20fa44d59f82..d6c3d0a76550 100644 --- a/arch/arm/boot/compressed/head-xscale.S +++ b/arch/arm/boot/compressed/head-xscale.S @@ -7,6 +7,7 @@ */ #include +#include .section ".start", "ax" @@ -14,6 +15,23 @@ __XScale_start: @ Preserve r8/r7 i.e. kernel entry values + @ set correct endianness for ixp4xx regardless of bootloader + mrc p15, 0, r0, c1, c0, 0 +#ifdef CONFIG_CPU_BIG_ENDIAN + orr r0, r0, #CR_B @ enable big endian mode +#else + bic r0, r0, #CR_B +#endif + mcr p15, 0, r0, c1, c0, 0 +0: mrc p15, 0, r0, c1, c0, 0 + tst r0, #0x80 +#ifdef CONFIG_CPU_BIG_ENDIAN + beq 0b +#else + bne 0b +#endif + sub pc, pc, #4 @ flush instruction pipeline + @ Data cache might be active. @ Be sure to flush kernel binary out of the cache, @ whatever state it is, before it is turned off. From 358515f9f951e3140adda3795d0d0441db3f7436 Mon Sep 17 00:00:00 2001 From: Onkarnath Date: Tue, 11 Aug 2026 10:52:19 +0100 Subject: [PATCH 2/3] ARM: 9490/1: backtrace-clang: fix wrong sp usage for unwinding show_stack() can be called for any task, but c_backtrace is always unwinding frames based on "sp" register. Which leads to printing backtrace of current task instead of required task. Try with normal TC: ================== for_each_process(p){ sched_show_task(p); } [7.433271] task:kthreadd state:S stack:0 pid:2 tgid:2 ppid:0 task_flags:0x208040 flags:0x00000000 [7.433633] Call trace: [7.433640] [<80113418>] (dump_backtrace) from [<80113510>] (show_stack+0x14/0x18) .. [7.433676] [<8016749c>] (sched_show_task) from [<803cb324>] (meminfo_proc_show+0x6c/0x930) [7.434019] r5:8158e300 r4:8b4882d0 [7.434024] [<803cb324>] (meminfo_proc_show) from [<80365788>] (seq_read_iter+0x148/0x4bc) [7.434045] [<80365788>] (seq_read_iter) from [<803c044c>] (proc_reg_read_iter+0xb8/0xc4) [7.434060] [<803c044c>] (proc_reg_read_iter) from [<80377104>] (copy_splice_read+0x228/0x308) It should unwind frame based on passed "fp". (CONFIG_UNWINDER_FRAME_POINTER=y) Extra sp manipulation is needed only when c_backtrace() is called from dump_backtrace() with "frame == fp" i.e. NULL regs and not current task. (Suggested by Ard Biesheuvel) Otherwise passed "fp" should be used to unwind backtraces. With fix: ========= [13.933732] task:kthreadd state:S stack:0 pid:2 tgid:2 ppid:0 task_flags:0x208040 flags:0x00000000 [13.934165] Call trace: [13.934604] [<80afa1a8>] (schedule) from [<8015426c>] (kthreadd+0x124/0x208) [13.934654] r10:8100bbf0 r4:8116f440 [13.934664] [<8015426c>] (kthreadd) from [<8010010c>] (ret_from_fork+0x14/0x28) [13.934691] Exception stack(0xf081df9c to 0xf081dfe4) Suggested-by: Ard Biesheuvel Reviewed-by: Nick Desaulniers Signed-off-by: Onkarnath Signed-off-by: Maninder Singh Signed-off-by: Russell King --- arch/arm/lib/backtrace-clang.S | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/lib/backtrace-clang.S b/arch/arm/lib/backtrace-clang.S index 290c52a60fc6..9a10828992af 100644 --- a/arch/arm/lib/backtrace-clang.S +++ b/arch/arm/lib/backtrace-clang.S @@ -107,8 +107,10 @@ ENDPROC(c_backtrace) /* * Switches the current frame to be the frame for dump_stack. + * (Only in case of current task using current registers) */ - add frame, sp, #24 @ switch to false frame + cmp frame, fp + addeq frame, sp, #24 @ switch to false frame for_each_frame: tst frame, mask @ Check for address exceptions bne no_frame From c2bbeff6952a84282333ebcff59f9fde449f60dd Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Sat, 29 Aug 2026 18:37:35 +0100 Subject: [PATCH 3/3] ARM: 9493/1: asid: Do not replace active_asids if already 0 Under some uncommon timing conditions, a generation check and xchg(active_asids, A1) in check_and_switch_context() on P1 can race with an ASID roll-over on P2. If P2 has not seen the update to active_asids[P1], it can re-allocate A1 to a new task T2 on P2. P1 ends up waiting on the spinlock since the xchg() returned 0 while P2 can go through a second ASID roll-over with (T2,A1,G2) active on P2. This roll-over copies active_asids[P1] == A1,G1 into reserved_asids[P1] and active_asids[P2] == A1,G2 into reserved_asids[P2]. A subsequent scheduling of T1 on P1 and T2 on P2 would match reserved_asids and get their generation bumped to G3: P1 P2 -- -- TTBR0.BADDR = T0 TTBR0.ASID = A0 asid_generation = G1 check_and_switch_context(T1,A1,G1) generation match check_and_switch_context(T2,A0,G0) new_context() ASID roll-over asid_generation = G2 flush_context() active_asids[P1] = 0 asid_map[A1] = 0 reserved_asids[P1] = A0,G0 xchg(active_asids, A1) active_asids[P1] = A1,G1 xchg returns 0 spin_lock_irqsave() allocated ASID (T2,A1,G2) asid_map[A1] = 1 active_asids[P2] = A1,G2 ... check_and_switch_context(T3,A0,G0) new_context() ASID roll-over asid_generation = G3 flush_context() active_asids[P1] = 0 asid_map[A1] = 1 reserved_asids[P1] = A1,G1 reserved_asids[P2] = A1,G2 allocated ASID (T3,A2,G3) asid_map[A2] = 1 active_asids[P2] = A2,G3 new_context() check_update_reserved_asid(A1,G1) matches reserved_asid[P1] reserved_asid[P1] = A1,G3 updated T1 ASID to (T1,A1,G3) check_and_switch_context(T2,A1,G2) new_context() check_update_reserved_asid(A1,G2) matches reserved_asids[P2] reserved_asids[P2] = A1,G3 updated T2 ASID to (T2,A1,G3) At this point, we have two tasks, T1 and T2 both using ASID A1 with the latest generation G3. Any of them is allowed to be scheduled on the other CPU leading to two different tasks with the same ASID on the same CPU. This patch changes the xchg to cmpxchg so that the active_asids is only updated if non-zero to avoid a race with an ASID roll-over on a different CPU. Cc: Will Deacon Signed-off-by: Catalin Marinas Tested-by: Karl Mehltretter Signed-off-by: Karl Mehltretter Signed-off-by: Russell King --- arch/arm/mm/context.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 4204ffa2d104..7c4e1e4ba77b 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -238,7 +238,7 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) { unsigned long flags; unsigned int cpu = smp_processor_id(); - u64 asid; + u64 asid, old_active_asid; check_vmalloc_seq(mm); @@ -250,8 +250,17 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) cpu_set_reserved_ttbr0(); asid = atomic64_read(&mm->context.id); - if (!((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) - && atomic64_xchg(&per_cpu(active_asids, cpu), asid)) + + /* + * If our active_asids is zero, we are racing with an ASID roll-over + * on a different CPU, so skip the update (using cmpxchg if non-zero) + * and take the slow path. + */ + old_active_asid = atomic64_read(&per_cpu(active_asids, cpu)); + if (old_active_asid && + !((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) && + atomic64_cmpxchg(&per_cpu(active_asids, cpu), + old_active_asid, asid)) goto switch_mm_fastpath; raw_spin_lock_irqsave(&cpu_asid_lock, flags);