[PATCH 05/33] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping

Yao Yuan yaoyuan at linux.alibaba.com
Tue Nov 4 00:50:26 PST 2025


On Mon, Nov 03, 2025 at 04:54:49PM +0800, Marc Zyngier wrote:
> A long time ago, an unsuspecting architect forgot to add a trap
> bit for ICV_DIR_EL1 in ICH_HCR_EL2. Which was unfortunate, but
> what's a bit of spec between friends? Thankfully, this was fixed
> in a later revision, and ARM "deprecates" the lack of trapping
> ability.
>
> Unfortuantely, a few (billion) CPUs went out with that defect,
> anything ARMv8.0 from ARM, give or take. And on these CPUs,
> you can't trap DIR on its own, full stop.
>
> As the next best thing, we can trap everything in the common group,
> which is a tad expensive, but hey ho, that's what you get. You can
> otherwise recycle the HW in the neaby bin.
>
> Signed-off-by: Marc Zyngier <maz at kernel.org>
> ---
>  arch/arm64/include/asm/virt.h  |  7 ++++++-
>  arch/arm64/kernel/cpufeature.c | 34 ++++++++++++++++++++++++++++++++++
>  arch/arm64/kernel/hyp-stub.S   |  5 +++++
>  arch/arm64/kvm/vgic/vgic-v3.c  |  3 +++
>  arch/arm64/tools/cpucaps       |  1 +
>  5 files changed, 49 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
> index aa280f356b96a..8eb63d3294974 100644
> --- a/arch/arm64/include/asm/virt.h
> +++ b/arch/arm64/include/asm/virt.h
> @@ -40,8 +40,13 @@
>   */
>  #define HVC_FINALISE_EL2	3
>
> +/*
> + * HVC_GET_ICH_VTR_EL2 - Retrieve the ICH_VTR_EL2 value
> + */
> +#define HVC_GET_ICH_VTR_EL2	4
> +
>  /* Max number of HYP stub hypercalls */
> -#define HVC_STUB_HCALL_NR 4
> +#define HVC_STUB_HCALL_NR 5
>
>  /* Error returned when an invalid stub number is passed into x0 */
>  #define HVC_STUB_ERR	0xbadca11
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 5ed401ff79e3e..44103ad98805d 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -2303,6 +2303,31 @@ static bool has_gic_prio_relaxed_sync(const struct arm64_cpu_capabilities *entry
>  }
>  #endif
>
> +static bool can_trap_icv_dir_el1(const struct arm64_cpu_capabilities *entry,
> +				 int scope)
> +{
> +	struct arm_smccc_res res = {};
> +
> +	BUILD_BUG_ON(ARM64_HAS_ICH_HCR_EL2_TDS <= ARM64_HAS_GICV3_CPUIF);
> +	BUILD_BUG_ON(ARM64_HAS_ICH_HCR_EL2_TDS <= ARM64_HAS_GICV5_LEGACY);
> +	if (!cpus_have_cap(ARM64_HAS_GICV3_CPUIF) ||
> +	    !cpus_have_cap(ARM64_HAS_GICV3_CPUIF))

Duplicated checking ?

> +		return false;
> +
> +	if (!is_hyp_mode_available())
> +		return false;
> +
> +	if (is_kernel_in_hyp_mode())
> +		res.a1 = read_sysreg_s(SYS_ICH_VTR_EL2);
> +	else
> +		arm_smccc_1_1_hvc(HVC_GET_ICH_VTR_EL2, &res);
> +
> +	if (res.a0 == HVC_STUB_ERR)
> +		return false;
> +
> +	return res.a1 & ICH_VTR_EL2_TDS;
> +}
> +
>  #ifdef CONFIG_ARM64_BTI
>  static void bti_enable(const struct arm64_cpu_capabilities *__unused)
>  {
> @@ -2814,6 +2839,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
>  		.matches = has_gic_prio_relaxed_sync,
>  	},
>  #endif
> +	{
> +		/*
> +		 * Depends on having GICv3
> +		 */
> +		.desc = "ICV_DIR_EL1 trapping",
> +		.capability = ARM64_HAS_ICH_HCR_EL2_TDS,
> +		.type = ARM64_CPUCAP_SYSTEM_FEATURE,
> +		.matches = can_trap_icv_dir_el1,
> +	},
>  #ifdef CONFIG_ARM64_E0PD
>  	{
>  		.desc = "E0PD",
> diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S
> index 36e2d26b54f5c..ab60fa685f6d8 100644
> --- a/arch/arm64/kernel/hyp-stub.S
> +++ b/arch/arm64/kernel/hyp-stub.S
> @@ -54,6 +54,11 @@ SYM_CODE_START_LOCAL(elx_sync)
>  1:	cmp	x0, #HVC_FINALISE_EL2
>  	b.eq	__finalise_el2
>
> +	cmp	x0, #HVC_GET_ICH_VTR_EL2
> +	b.ne	2f
> +	mrs	x1, ich_vtr_el2
> +	b	9f
> +
>  2:	cmp	x0, #HVC_SOFT_RESTART
>  	b.ne	3f
>  	mov	x0, x2
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index 236d0beef561d..e0c6e03bf9411 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -648,6 +648,9 @@ void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
>  		dir_trap = true;
>  	}
>
> +	if (!cpus_have_cap(ARM64_HAS_ICH_HCR_EL2_TDS))
> +		common_trap = true;
> +
>  	if (group0_trap)
>  		hcr |= ICH_HCR_EL2_TALL0;
>  	if (group1_trap)
> diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
> index 1b32c1232d28d..77f1bd230722d 100644
> --- a/arch/arm64/tools/cpucaps
> +++ b/arch/arm64/tools/cpucaps
> @@ -40,6 +40,7 @@ HAS_GICV5_CPUIF
>  HAS_GICV5_LEGACY
>  HAS_GIC_PRIO_MASKING
>  HAS_GIC_PRIO_RELAXED_SYNC
> +HAS_ICH_HCR_EL2_TDS
>  HAS_HCR_NV1
>  HAS_HCX
>  HAS_LDAPR
> --
> 2.47.3
>



More information about the linux-arm-kernel mailing list