[PATCH] riscv: cpufeature: Validate H extension via CSR_HGATP probe

Chen Pei posted 1 patch 4 days, 21 hours ago
arch/riscv/kernel/cpufeature.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
[PATCH] riscv: cpufeature: Validate H extension via CSR_HGATP probe
Posted by Chen Pei 4 days, 21 hours ago
The H extension is advertised to S-mode through three channels --
the DT "riscv,isa" string, the DT "riscv,isa-extensions" list, and
the ACPI RHCT ISA string -- all of which converge in
riscv_resolve_isa().  The H entry has no validate callback today,
so the kernel trusts the description unconditionally.

When firmware advertises H but the hardware does not actually
implement it (common during early bring-up, e.g. QEMU), kvm.ko
reaches kvm_riscv_gstage_mode_detect() and oopses on the first
CSR_HGATP access with an illegal instruction.

Add a validate callback for H that probes CSR_HGATP under an
exception-table fixup.  If the read traps, the H bit is cleared
during ISA resolution and the rest of the kernel sees H as
unavailable.  Because the probe runs in riscv_resolve_isa(), it
covers all three advertisement sources.

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 arch/riscv/kernel/cpufeature.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74..b34dd789e94e 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -17,9 +17,11 @@
 #include <linux/of.h>
 #include <asm/acpi.h>
 #include <asm/alternative.h>
+#include <asm/asm-extable.h>
 #include <asm/bugs.h>
 #include <asm/cacheflush.h>
 #include <asm/cpufeature.h>
+#include <asm/csr.h>
 #include <asm/hwcap.h>
 #include <asm/text-patching.h>
 #include <asm/hwprobe.h>
@@ -163,6 +165,32 @@ static int riscv_ext_d_validate(const struct riscv_isa_ext_data *data,
 	return 0;
 }
 
+static int riscv_ext_h_validate(const struct riscv_isa_ext_data *data,
+				const unsigned long *isa_bitmap)
+{
+	unsigned long val, ret = 1;
+
+	/*
+	 * The H extension may be advertised by firmware (DT/ACPI) even
+	 * when the underlying hardware does not implement it, or when
+	 * M-mode firmware has not delegated hypervisor CSR access to
+	 * S-mode.  Probe CSR_HGATP under an exception-table fixup: if
+	 * the read traps with an illegal instruction, ret stays 1.
+	 */
+	asm volatile(
+		"1:	csrr	%0, " __stringify(CSR_HGATP) "\n"
+		"	li	%1, 0\n"
+		"2:\n"
+		_ASM_EXTABLE(1b, 2b)
+		: "=r" (val), "+r" (ret));
+
+	if (ret) {
+		pr_err("H detected in ISA string, disabling as CSR_HGATP is not accessible\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int riscv_ext_vector_x_validate(const struct riscv_isa_ext_data *data,
 				       const unsigned long *isa_bitmap)
 {
@@ -498,7 +526,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__RISCV_ISA_EXT_DATA(q, RISCV_ISA_EXT_q),
 	__RISCV_ISA_EXT_SUPERSET(c, RISCV_ISA_EXT_c, riscv_c_exts),
 	__RISCV_ISA_EXT_SUPERSET_VALIDATE(v, RISCV_ISA_EXT_v, riscv_v_exts, riscv_ext_vector_float_validate),
-	__RISCV_ISA_EXT_DATA(h, RISCV_ISA_EXT_h),
+	__RISCV_ISA_EXT_DATA_VALIDATE(h, RISCV_ISA_EXT_h, riscv_ext_h_validate),
 	__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicbom, RISCV_ISA_EXT_ZICBOM, riscv_xlinuxenvcfg_exts, riscv_ext_zicbom_validate),
 	__RISCV_ISA_EXT_DATA_VALIDATE(zicbop, RISCV_ISA_EXT_ZICBOP, riscv_ext_zicbop_validate),
 	__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts, riscv_ext_zicboz_validate),
-- 
2.50.1
Re: [PATCH] riscv: cpufeature: Validate H extension via CSR_HGATP probe
Posted by Conor Dooley 4 days, 20 hours ago
On Wed, Jun 03, 2026 at 05:33:01PM +0800, Chen Pei wrote:
> The H extension is advertised to S-mode through three channels --
> the DT "riscv,isa" string, the DT "riscv,isa-extensions" list, and
> the ACPI RHCT ISA string -- all of which converge in
> riscv_resolve_isa().  The H entry has no validate callback today,
> so the kernel trusts the description unconditionally.
> 
> When firmware advertises H but the hardware does not actually
> implement it (common during early bring-up, e.g. QEMU), kvm.ko
> reaches kvm_riscv_gstage_mode_detect() and oopses on the first
> CSR_HGATP access with an illegal instruction.
> 
> Add a validate callback for H that probes CSR_HGATP under an
> exception-table fixup.  If the read traps, the H bit is cleared
> during ISA resolution and the rest of the kernel sees H as
> unavailable.  Because the probe runs in riscv_resolve_isa(), it
> covers all three advertisement sources.

The point of validate callbacks is twofold: checking that the kernel
configuration supports the extension and that extensions that the kernel
depends on to support the one in question are present.
It is not the kernel's job to check that the devicetree matches the
hardware. I can only assume that ACPI has the same policy. Firmware must
not report supporting extensions that hardware does not actually support.

NAK, fix your firmware instead please.

Thanks,
Conor.


> 
> Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
> ---
>  arch/riscv/kernel/cpufeature.c | 30 +++++++++++++++++++++++++++++-
>  1 file changed, 29 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index f46aa5602d74..b34dd789e94e 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -17,9 +17,11 @@
>  #include <linux/of.h>
>  #include <asm/acpi.h>
>  #include <asm/alternative.h>
> +#include <asm/asm-extable.h>
>  #include <asm/bugs.h>
>  #include <asm/cacheflush.h>
>  #include <asm/cpufeature.h>
> +#include <asm/csr.h>
>  #include <asm/hwcap.h>
>  #include <asm/text-patching.h>
>  #include <asm/hwprobe.h>
> @@ -163,6 +165,32 @@ static int riscv_ext_d_validate(const struct riscv_isa_ext_data *data,
>  	return 0;
>  }
>  
> +static int riscv_ext_h_validate(const struct riscv_isa_ext_data *data,
> +				const unsigned long *isa_bitmap)
> +{
> +	unsigned long val, ret = 1;
> +
> +	/*
> +	 * The H extension may be advertised by firmware (DT/ACPI) even
> +	 * when the underlying hardware does not implement it, or when
> +	 * M-mode firmware has not delegated hypervisor CSR access to
> +	 * S-mode.  Probe CSR_HGATP under an exception-table fixup: if
> +	 * the read traps with an illegal instruction, ret stays 1.
> +	 */
> +	asm volatile(
> +		"1:	csrr	%0, " __stringify(CSR_HGATP) "\n"
> +		"	li	%1, 0\n"
> +		"2:\n"
> +		_ASM_EXTABLE(1b, 2b)
> +		: "=r" (val), "+r" (ret));
> +
> +	if (ret) {
> +		pr_err("H detected in ISA string, disabling as CSR_HGATP is not accessible\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
>  static int riscv_ext_vector_x_validate(const struct riscv_isa_ext_data *data,
>  				       const unsigned long *isa_bitmap)
>  {
> @@ -498,7 +526,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
>  	__RISCV_ISA_EXT_DATA(q, RISCV_ISA_EXT_q),
>  	__RISCV_ISA_EXT_SUPERSET(c, RISCV_ISA_EXT_c, riscv_c_exts),
>  	__RISCV_ISA_EXT_SUPERSET_VALIDATE(v, RISCV_ISA_EXT_v, riscv_v_exts, riscv_ext_vector_float_validate),
> -	__RISCV_ISA_EXT_DATA(h, RISCV_ISA_EXT_h),
> +	__RISCV_ISA_EXT_DATA_VALIDATE(h, RISCV_ISA_EXT_h, riscv_ext_h_validate),
>  	__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicbom, RISCV_ISA_EXT_ZICBOM, riscv_xlinuxenvcfg_exts, riscv_ext_zicbom_validate),
>  	__RISCV_ISA_EXT_DATA_VALIDATE(zicbop, RISCV_ISA_EXT_ZICBOP, riscv_ext_zicbop_validate),
>  	__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts, riscv_ext_zicboz_validate),
> -- 
> 2.50.1
>