[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