Ping: [PATCH v2] LoongArch: detect and disable sc.q if erratic
From: Xi Ruoyao
Date: Sun Apr 26 2026 - 22:18:00 EST
Ping.
On Thu, 2026-04-09 at 20:19 +0800, Xi Ruoyao wrote:
> We've observed that, on some Loongson 2K3000/3B6000M systems with earlier
> firmware revisions, the sc.q instruction may write incorrect data into
> the upper half of the written 128-bit datum.
>
> It seems upgrading the firmware (for example, the 202602 release from
> Loongson [1]) will resolve the issue. But since not all systems may be
> running the most up-to-date firmware, based on firmware update avail-
> ability and the environment in which they are running in.
>
> To help with system compatibility and ensure correct behavior, check if
> sc.q behaves erratically and disable if so.
>
> Link: https://github.com/loongson/Firmware/pull/156 [1]
> Signed-off-by: Xi Ruoyao <xry111@xxxxxxxxxxx>
> ---
> arch/loongarch/kernel/cpu-probe.c | 39 ++++++++++++++++++++++++++++++-
> 1 file changed, 38 insertions(+), 1 deletion(-)
>
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index 657bbae6c1c7..5fcf2672172f 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -132,6 +132,43 @@ static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
> }
> }
>
> +/*
> + * Some LoongArch has broken sc.q which incorrectly handles the upper word
> + * when the lower word is zero. Newer firmware versions (such as the 202602
> + * release from Loongson) seem to contain a workaround for this issue.
> + *
> + * Disable sc.q if erratic to ensure reliability and compatibility.
> + */
> +static bool sc_q_is_sane(void)
> +{
> + struct {
> + long word[2];
> + } __aligned(16) mem;
> + register long tmp asm("t0");
> + register long one asm("t1") = 1;
> + register long *ptr asm("t2") = &mem.word[0];
> +
> + /*
> + * The sc.q instruction is hard coded with .word so the HWCAP bit
> + * exported to the userspace won't depend on the assembler version
> + * used to build the kernel.
> + */
> + asm (
> + "1:ll.d\t$r0, %[ptr], 0\n\t"
> + "move\t%[tmp], $r0\n\t"
> + ".word\t0x385735cc\n\t" /* sc.q %[tmp], %[one], %[ptr]*/
> + "beqz\t%[tmp], 1b"
> + : [tmp] "=&r" (tmp), "=m" (mem)
> + : [ptr] "r" (ptr), [one] "r" (one));
> +
> + if (mem.word[1] != 1) {
> + pr_warn_once("Warning: sc.q is erratic on this platform, disabling for both kernel and HWCAP. Please try a firmware update.");
> + return false;
> + }
> +
> + return true;
> +}
> +
> static void cpu_probe_common(struct cpuinfo_loongarch *c)
> {
> unsigned int config;
> @@ -177,7 +214,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
> c->options |= LOONGARCH_CPU_LAM;
> elf_hwcap |= HWCAP_LOONGARCH_LAM;
> }
> - if (config & CPUCFG2_SCQ) {
> + if ((config & CPUCFG2_SCQ) && sc_q_is_sane()) {
> c->options |= LOONGARCH_CPU_SCQ;
> elf_hwcap |= HWCAP_LOONGARCH_SCQ;
> }
--
Xi Ruoyao <xry111@xxxxxxxxxxx>