[PATCH] LoongArch: detect and disable sc.q if erratic

Xi Ruoyao posted 1 patch 2 months, 1 week ago
There is a newer version of this series
arch/loongarch/kernel/cpu-probe.c | 32 ++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
[PATCH] LoongArch: detect and disable sc.q if erratic
Posted by Xi Ruoyao 2 months, 1 week ago
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@xry111.site>
---
 arch/loongarch/kernel/cpu-probe.c | 32 ++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 657bbae6c1c7..943e5826c71b 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -132,6 +132,36 @@ 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 (
+		"1:ll.d\t$r0, %[mem]\n\t"
+		"move\t%[tmp], $r0\n\t"
+		"sc.q\t%[tmp], %[one], %[mem]\n\t"
+		"beqz\t%[tmp], 1b"
+		: [mem] "=ZB" (mem), [tmp] "=&r" (tmp)
+		: [one] "r" (1));
+
+	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 +207,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;
 	}
-- 
2.53.0
Re: [PATCH] LoongArch: detect and disable sc.q if erratic
Posted by kernel test robot 2 months, 1 week ago
Hi Xi,

kernel test robot noticed the following build errors:

[auto build test ERROR on soc/for-next]
[also build test ERROR on linus/master v7.0-rc7 next-20260406]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xi-Ruoyao/LoongArch-detect-and-disable-sc-q-if-erratic/20260407-193425
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/20260402044732.164294-2-xry111%40xry111.site
patch subject: [PATCH] LoongArch: detect and disable sc.q if erratic
config: loongarch-allnoconfig (https://download.01.org/0day-ci/archive/20260407/202604072314.9J2SQEFo-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project c80443cd37b2e2788cba67ffa180a6331e5f0791)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260407/202604072314.9J2SQEFo-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604072314.9J2SQEFo-lkp@intel.com/

All errors (new ones prefixed by >>):

>> arch/loongarch/kernel/cpu-probe.c:151:23: error: invalid operand for instruction
     151 |                 "move\t%[tmp], $r0\n\t"
         |                                     ^
   <inline asm>:3:22: note: instantiated into assembly here
       3 |         sc.q    $a2, $a0, $a1, 0
         |                                ^
   1 error generated.


vim +151 arch/loongarch/kernel/cpu-probe.c

   134	
   135	/*
   136	 * Some LoongArch has broken sc.q which incorrectly handles the upper word
   137	 * when the lower word is zero. Newer firmware versions (such as the 202602
   138	 * release from Loongson) seem to contain a workaround for this issue.
   139	 *
   140	 * Disable sc.q if erratic to ensure reliability and compatibility.
   141	 */
   142	static bool sc_q_is_sane(void)
   143	{
   144		struct {
   145			long word[2];
   146		} __aligned(16) mem;
   147		register long tmp;
   148	
   149		asm (
   150			"1:ll.d\t$r0, %[mem]\n\t"
 > 151			"move\t%[tmp], $r0\n\t"
   152			"sc.q\t%[tmp], %[one], %[mem]\n\t"
   153			"beqz\t%[tmp], 1b"
   154			: [mem] "=ZB" (mem), [tmp] "=&r" (tmp)
   155			: [one] "r" (1));
   156	
   157		if (mem.word[1] != 1) {
   158			pr_warn_once("Warning: sc.q is erratic on this platform, disabling for both kernel and HWCAP. Please try a firmware update.");
   159			return false;
   160		}
   161	
   162		return true;
   163	}
   164	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] LoongArch: detect and disable sc.q if erratic
Posted by Thomas Weißschuh 2 months, 1 week ago
On 2026-04-02 12:47:30+0800, Xi Ruoyao wrote:

(...)

> +/*
> + * 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 (
> +		"1:ll.d\t$r0, %[mem]\n\t"
> +		"move\t%[tmp], $r0\n\t"
> +		"sc.q\t%[tmp], %[one], %[mem]\n\t"

This probably needs to be gated behind CONFIG_AS_HAS_SCQ_EXTENSION to
avoid errors on older toolchains.

> +		"beqz\t%[tmp], 1b"
> +		: [mem] "=ZB" (mem), [tmp] "=&r" (tmp)
> +		: [one] "r" (1));
> +
> +	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;
> +}
Re: [PATCH] LoongArch: detect and disable sc.q if erratic
Posted by Xi Ruoyao 2 months, 1 week ago
On Thu, 2026-04-02 at 08:39 +0200, Thomas Weißschuh wrote:
> On 2026-04-02 12:47:30+0800, Xi Ruoyao wrote:
> 
> (...)
> 
> > +/*
> > + * 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 (
> > +		"1:ll.d\t$r0, %[mem]\n\t"
> > +		"move\t%[tmp], $r0\n\t"
> > +		"sc.q\t%[tmp], %[one], %[mem]\n\t"
> 
> This probably needs to be gated behind CONFIG_AS_HAS_SCQ_EXTENSION to
> avoid errors on older toolchains.

I guess I'd code the insn with .word instead...

This also affects HWCAP, and I don't want the userspace to see neither a
false positive nor a false negative simply because the kernel was built
with an old toolchain.

-- 
Xi Ruoyao <xry111@xry111.site>
Re: [PATCH] LoongArch: detect and disable sc.q if erratic
Posted by Mingcong Bai 2 months, 1 week ago
Hi Ruoyao,

在 2026/4/2 12:47, Xi Ruoyao 写道:
> 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@xry111.site>
> ---
>   arch/loongarch/kernel/cpu-probe.c | 32 ++++++++++++++++++++++++++++++-
>   1 file changed, 31 insertions(+), 1 deletion(-)

This patch was tested on a Loongson XB6MXC0_V1.0 motherboard (Loongson 
3B6000M) and an OrangePi Nova V1.1 (Loongson 2K3000), each against 
firmware versions stable202511 (without workaround) and stable202602 
(with workaround). Both devices had sc.q disabled correctly with 
stable202511, and enabled with the latter, no instability or 
functionality issue was observed.

With that:

Tested-by: Mingcong Bai <jeffbai@aosc.io>

> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index 657bbae6c1c7..943e5826c71b 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -132,6 +132,36 @@ 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 (
> +		"1:ll.d\t$r0, %[mem]\n\t"
> +		"move\t%[tmp], $r0\n\t"
> +		"sc.q\t%[tmp], %[one], %[mem]\n\t"
> +		"beqz\t%[tmp], 1b"
> +		: [mem] "=ZB" (mem), [tmp] "=&r" (tmp)
> +		: [one] "r" (1));
> +
> +	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 +207,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;
>   	}