From: Oliver Upton <oliver.upton@linux.dev>
To: Sascha Bischoff <Sascha.Bischoff@arm.com>
Cc: "linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>,
"kvmarm@lists.linux.dev" <kvmarm@lists.linux.dev>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"kvm@vger.kernel.org" <kvm@vger.kernel.org>, nd <nd@arm.com>,
"maz@kernel.org" <maz@kernel.org>,
Joey Gouly <Joey.Gouly@arm.com>,
Suzuki Poulose <Suzuki.Poulose@arm.com>,
"yuzenghui@huawei.com" <yuzenghui@huawei.com>,
"will@kernel.org" <will@kernel.org>,
"tglx@linutronix.de" <tglx@linutronix.de>,
"lpieralisi@kernel.org" <lpieralisi@kernel.org>,
Timothy Hayes <Timothy.Hayes@arm.com>
Subject: Re: [PATCH 4/5] KVM: arm64: gic-v5: Support GICv3 compat
Date: Fri, 20 Jun 2025 13:20:36 -0700 [thread overview]
Message-ID: <aFXClKQRG3KNAD2y@linux.dev> (raw)
In-Reply-To: <20250620160741.3513940-5-sascha.bischoff@arm.com>
Hi Sascha,
Thank you for posting this. Very excited to see the GICv5 enablement get
started.
On Fri, Jun 20, 2025 at 04:07:51PM +0000, Sascha Bischoff wrote:
> Add support for GICv3 compat mode (FEAT_GCIE_LEGACY) which allows a
> GICv5 host to run GICv3-based VMs. This change enables the
> VHE/nVHE/hVHE/protected modes, but does not support nested
> virtualization.
Can't we just load the shadow state into the compat VGICv3? I'm worried
this has sharp edges on the UAPI side as well as users wanting to
migrate VMs to new hardware.
The guest hypervisor should only see GICv3-only or GICv5-only, we can
pretend FEAT_GCIE_LEGACY never existed :)
> Co-authored-by: Timothy Hayes <timothy.hayes@arm.com>
> Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
> Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> ---
> arch/arm64/include/asm/kvm_asm.h | 2 ++
> arch/arm64/include/asm/kvm_hyp.h | 2 ++
> arch/arm64/kvm/Makefile | 3 +-
> arch/arm64/kvm/hyp/nvhe/hyp-main.c | 12 +++++++
> arch/arm64/kvm/hyp/vgic-v3-sr.c | 51 +++++++++++++++++++++++++-----
> arch/arm64/kvm/sys_regs.c | 10 +++++-
> arch/arm64/kvm/vgic/vgic-init.c | 6 ++--
> arch/arm64/kvm/vgic/vgic-v3.c | 6 ++++
> arch/arm64/kvm/vgic/vgic-v5.c | 14 ++++++++
> arch/arm64/kvm/vgic/vgic.h | 2 ++
> include/kvm/arm_vgic.h | 9 +++++-
> 11 files changed, 104 insertions(+), 13 deletions(-)
> create mode 100644 arch/arm64/kvm/vgic/vgic-v5.c
>
> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
> index bec227f9500a..ad1ef0460fd6 100644
> --- a/arch/arm64/include/asm/kvm_asm.h
> +++ b/arch/arm64/include/asm/kvm_asm.h
> @@ -81,6 +81,8 @@ enum __kvm_host_smccc_func {
> __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff,
> __KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs,
> __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
> + __KVM_HOST_SMCCC_FUNC___vgic_v3_compat_mode_enable,
> + __KVM_HOST_SMCCC_FUNC___vgic_v3_compat_mode_disable,
> __KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
> __KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
> __KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,
> diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> index e6be1f5d0967..9c8adc5186ec 100644
> --- a/arch/arm64/include/asm/kvm_hyp.h
> +++ b/arch/arm64/include/asm/kvm_hyp.h
> @@ -85,6 +85,8 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if);
> void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if);
> void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if);
> int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu);
> +void __vgic_v3_compat_mode_enable(void);
> +void __vgic_v3_compat_mode_disable(void);
>
> #ifdef __KVM_NVHE_HYPERVISOR__
> void __timer_enable_traps(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 7c329e01c557..3ebc0570345c 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -23,7 +23,8 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
> vgic/vgic-v3.o vgic/vgic-v4.o \
> vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \
> vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
> - vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o
> + vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \
> + vgic/vgic-v5.o
>
> kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o
> kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o
> diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> index e9198e56e784..61af55df60a9 100644
> --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> @@ -475,6 +475,16 @@ static void handle___vgic_v3_restore_vmcr_aprs(struct kvm_cpu_context *host_ctxt
> __vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if));
> }
>
> +static void handle___vgic_v3_compat_mode_enable(struct kvm_cpu_context *host_ctxt)
> +{
> + __vgic_v3_compat_mode_enable();
> +}
> +
> +static void handle___vgic_v3_compat_mode_disable(struct kvm_cpu_context *host_ctxt)
> +{
> + __vgic_v3_compat_mode_disable();
> +}
> +
> static void handle___pkvm_init(struct kvm_cpu_context *host_ctxt)
> {
> DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
> @@ -603,6 +613,8 @@ static const hcall_t host_hcall[] = {
> HANDLE_FUNC(__kvm_timer_set_cntvoff),
> HANDLE_FUNC(__vgic_v3_save_vmcr_aprs),
> HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs),
> + HANDLE_FUNC(__vgic_v3_compat_mode_enable),
> + HANDLE_FUNC(__vgic_v3_compat_mode_disable),
> HANDLE_FUNC(__pkvm_init_vm),
> HANDLE_FUNC(__pkvm_init_vcpu),
> HANDLE_FUNC(__pkvm_teardown_vm),
> diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> index f162b0df5cae..b03b5f012226 100644
> --- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
> +++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> @@ -257,6 +257,18 @@ void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
> }
> }
>
> +void __vgic_v3_compat_mode_enable(void)
> +{
> + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, 0, ICH_VCTLR_EL2_V3);
> + isb();
> +}
> +
> +void __vgic_v3_compat_mode_disable(void)
> +{
> + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, ICH_VCTLR_EL2_V3, 0);
> + isb();
> +}
> +
It isn't clear to me what these ISBs are synchonizing against. AFAICT,
the whole compat thing is always visible and we can restore the rest of
the VGICv3 context before guaranteeing the enable bit has been observed.
Can we consolidate this into a single hyp call along with
__vgic_v3_*_vmcr_aprs()?
Last bit as an FYI, kvm_call_hyp() has an implied context synchronization upon
return, either because of ERET in nVHE or an explicit ISB on VHE.
> void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
> {
> /*
> @@ -296,12 +308,19 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
> }
>
> /*
> - * Prevent the guest from touching the ICC_SRE_EL1 system
> - * register. Note that this may not have any effect, as
> - * ICC_SRE_EL2.Enable being RAO/WI is a valid implementation.
> + * GICv5 BET0 FEAT_GCIE_LEGACY doesn't include ICC_SRE_EL2. This is due
> + * to be relaxed in a future spec release, likely BET1, at which point
> + * this in condition can be dropped again.
> */
> - write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
> - ICC_SRE_EL2);
> + if (!static_branch_unlikely(&kvm_vgic_global_state.gicv5_cpuif)) {
> + /*
> + * Prevent the guest from touching the ICC_SRE_EL1 system
> + * register. Note that this may not have any effect, as
> + * ICC_SRE_EL2.Enable being RAO/WI is a valid implementation.
> + */
> + write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
> + ICC_SRE_EL2);
> + }
>
> /*
> * If we need to trap system registers, we must write
> @@ -322,8 +341,14 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
> cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
> }
>
> - val = read_gicreg(ICC_SRE_EL2);
> - write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
> + /*
> + * Can be dropped in the future when GICv5 BET1 is released. See
> + * comment above.
> + */
> + if (!static_branch_unlikely(&kvm_vgic_global_state.gicv5_cpuif)) {
Can we use the GCIE cpucap instead, possibly via a shared helper with
the driver?
> - if (kvm_vgic_global_state.type == VGIC_V3) {
> + if (kvm_vgic_global_state.type == VGIC_V3 || kvm_vgic_in_v3_compat_mode()) {
Can we do a helper for this too?
> val &= ~ID_AA64PFR0_EL1_GIC_MASK;
> val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
> }
> @@ -1953,6 +1953,14 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> (vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val)))
> return -EINVAL;
>
> + /*
> + * If we are running on a GICv5 host and support FEAT_GCIE_LEGACY, then
> + * we support GICv3. Fail attempts to do anything but set that to IMP.
> + */
> + if (kvm_vgic_in_v3_compat_mode() &&
> + FIELD_GET(ID_AA64PFR0_EL1_GIC_MASK, user_val) != ID_AA64PFR0_EL1_GIC_IMP)
> + return -EINVAL;
> +
> return set_id_reg(vcpu, rd, user_val);
> }
>
> diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> index eb1205654ac8..5f6506e297c1 100644
> --- a/arch/arm64/kvm/vgic/vgic-init.c
> +++ b/arch/arm64/kvm/vgic/vgic-init.c
> @@ -674,10 +674,12 @@ void kvm_vgic_init_cpu_hardware(void)
> * We want to make sure the list registers start out clear so that we
> * only have the program the used registers.
> */
> - if (kvm_vgic_global_state.type == VGIC_V2)
> + if (kvm_vgic_global_state.type == VGIC_V2) {
> vgic_v2_init_lrs();
> - else
> + } else if (kvm_vgic_global_state.type == VGIC_V3 ||
> + kvm_vgic_in_v3_compat_mode()) {
> kvm_call_hyp(__vgic_v3_init_lrs);
> + }
> }
>
> /**
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index b9ad7c42c5b0..b5df4d36821d 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -734,6 +734,9 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)
> {
> struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
>
> + if (static_branch_unlikely(&kvm_vgic_global_state.gicv5_cpuif))
> + kvm_call_hyp(__vgic_v3_compat_mode_enable);
> +
> /* If the vgic is nested, perform the full state loading */
> if (vgic_state_is_nested(vcpu)) {
> vgic_v3_load_nested(vcpu);
> @@ -764,4 +767,7 @@ void vgic_v3_put(struct kvm_vcpu *vcpu)
>
> if (has_vhe())
> __vgic_v3_deactivate_traps(cpu_if);
> +
> + if (static_branch_unlikely(&kvm_vgic_global_state.gicv5_cpuif))
> + kvm_call_hyp(__vgic_v3_compat_mode_disable);
> }
> diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
> new file mode 100644
> index 000000000000..57199449ca0f
> --- /dev/null
> +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> @@ -0,0 +1,14 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <kvm/arm_vgic.h>
> +
> +#include "vgic.h"
> +
> +inline bool kvm_vgic_in_v3_compat_mode(void)a
nit: we're generally trusting of the compiler to 'do the right thing'
and avoid explicit inline specifiers unless necessary.
> +{
> + if (static_branch_unlikely(&kvm_vgic_global_state.gicv5_cpuif) &&
> + kvm_vgic_global_state.has_gcie_v3_compat)
> + return true;
> +
> + return false;
> +}
This should be a per-VM thing once KVM support for GICv5 lands. Can you
get ahead of that and take a KVM pointer that goes unused. Maybe rename
it:
bool vgic_is_v3_compat(struct kvm *kvm)
Or something similar.
Thanks,
Oliver
next prev parent reply other threads:[~2025-06-20 20:21 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-20 16:07 [PATCH 0/5] KVM: arm64: Enable GICv3 guests on GICv5 hosts using FEAT_GCIE_LEGACY Sascha Bischoff
2025-06-20 16:07 ` [PATCH 1/5] irqchip/gic-v5: Skip deactivate for forwarded PPI interrupts Sascha Bischoff
2025-06-23 15:21 ` Lorenzo Pieralisi
2025-06-27 9:49 ` Sascha Bischoff
2025-06-20 16:07 ` [PATCH 2/5] irqchip/gic-v5: Populate struct gic_kvm_info Sascha Bischoff
2025-06-23 15:14 ` Lorenzo Pieralisi
2025-06-27 9:49 ` Sascha Bischoff
2025-06-20 16:07 ` [PATCH 3/5] arm64/sysreg: Add ICH_VCTLR_EL2 Sascha Bischoff
2025-06-20 16:07 ` [PATCH 4/5] KVM: arm64: gic-v5: Support GICv3 compat Sascha Bischoff
2025-06-20 20:20 ` Oliver Upton [this message]
2025-06-20 23:02 ` Oliver Upton
2025-06-23 13:11 ` Sascha Bischoff
2025-06-22 12:19 ` Marc Zyngier
2025-06-22 12:37 ` Oliver Upton
2025-06-23 13:02 ` Sascha Bischoff
2025-06-20 16:07 ` [PATCH 5/5] KVM: arm64: gic-v5: Probe for GICv5 Sascha Bischoff
2025-06-20 20:25 ` Oliver Upton
2025-06-23 13:12 ` Sascha Bischoff
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aFXClKQRG3KNAD2y@linux.dev \
--to=oliver.upton@linux.dev \
--cc=Joey.Gouly@arm.com \
--cc=Sascha.Bischoff@arm.com \
--cc=Suzuki.Poulose@arm.com \
--cc=Timothy.Hayes@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=maz@kernel.org \
--cc=nd@arm.com \
--cc=tglx@linutronix.de \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.