[PATCH v5 16/36] KVM: arm64: gic-v5: Implement direct injection of PPIs

Marc Zyngier maz at kernel.org
Wed Mar 4 01:35:30 PST 2026


On Thu, 26 Feb 2026 15:59:33 +0000,
Sascha Bischoff <Sascha.Bischoff at arm.com> wrote:
> 
> GICv5 is able to directly inject PPI pending state into a guest using
> a mechanism called DVI whereby the pending bit for a paticular PPI is
> driven directly by the physically-connected hardware. This mechanism
> itself doesn't allow for any ID translation, so the host interrupt is
> directly mapped into a guest with the same interrupt ID.
> 
> When mapping a virtual interrupt to a physical interrupt via
> kvm_vgic_map_irq for a GICv5 guest, check if the interrupt itself is a
> PPI or not. If it is, and the host's interrupt ID matches that used
> for the guest DVI is enabled, and the interrupt itself is marked as
> directly_injected.
> 
> When the interrupt is unmapped again, this process is reversed, and
> DVI is disabled for the interrupt again.
> 
> Note: the expectation is that a directly injected PPI is disabled on
> the host while the guest state is loaded. The reason is that although
> DVI is enabled to drive the guest's pending state directly, the host
> pending state also remains driven. In order to avoid the same PPI
> firing on both the host and the guest, the host's interrupt must be
> disabled (masked). This is left up to the code that owns the device
> generating the PPI as this needs to be handled on a per-VM basis. One
> VM might use DVI, while another might not, in which case the physical
> PPI should be enabled for the latter.
> 
> Co-authored-by: Timothy Hayes <timothy.hayes at arm.com>
> Signed-off-by: Timothy Hayes <timothy.hayes at arm.com>
> Signed-off-by: Sascha Bischoff <sascha.bischoff at arm.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron at huawei.com>
> ---
>  arch/arm64/kvm/vgic/vgic-v5.c | 15 +++++++++++++++
>  arch/arm64/kvm/vgic/vgic.c    | 10 ++++++++++
>  arch/arm64/kvm/vgic/vgic.h    |  1 +
>  include/kvm/arm_vgic.h        |  1 +
>  4 files changed, 27 insertions(+)
> 
> diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
> index 5b35c756887a9..f5cd9decfc26e 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> @@ -86,6 +86,21 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
>  	return 0;
>  }
>  
> +/*
> + * Sets/clears the corresponding bit in the ICH_PPI_DVIR register.
> + */
> +int vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, u32 irq, bool dvi)
> +{
> +	struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5;
> +	u32 ppi = FIELD_GET(GICV5_HWIRQ_ID, irq);
> +	unsigned long *p;
> +
> +	p = (unsigned long *)&cpu_if->vgic_ppi_dvir[ppi / 64];
> +	__assign_bit(ppi % 64, p, dvi);
> +
> +	return 0;
> +}
> +
>  void vgic_v5_load(struct kvm_vcpu *vcpu)
>  {
>  	struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5;
> diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
> index 1005ff5f36235..62e58fdf611d3 100644
> --- a/arch/arm64/kvm/vgic/vgic.c
> +++ b/arch/arm64/kvm/vgic/vgic.c
> @@ -577,12 +577,22 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
>  	irq->host_irq = host_irq;
>  	irq->hwintid = data->hwirq;
>  	irq->ops = ops;
> +
> +	if (vgic_is_v5(vcpu->kvm) &&
> +	    __irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, irq->intid))
> +		irq->directly_injected = !vgic_v5_set_ppi_dvi(vcpu, irq->hwintid,
> +							      true);
> +

Huh. A couple of things here:

- under what conditions would irq->directly_injected not be set to
  true for a PPI? That can never happen here AFAICT.

- we have per-IRQ operations, and PPIs do have such ops attached to
  them. Why can't this be moved to such a callback?

>  	return 0;
>  }
>  
>  /* @irq->irq_lock must be held */
>  static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq)
>  {
> +	if (irq->directly_injected && vgic_is_v5(irq->target_vcpu->kvm))
> +		WARN_ON(vgic_v5_set_ppi_dvi(irq->target_vcpu, irq->hwintid, false));
> +
> +	irq->directly_injected = false;
>  	irq->hw = false;
>  	irq->hwintid = 0;
>  	irq->ops = NULL;
> diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
> index 81d464d26534f..d7fe867a27b64 100644
> --- a/arch/arm64/kvm/vgic/vgic.h
> +++ b/arch/arm64/kvm/vgic/vgic.h
> @@ -364,6 +364,7 @@ void vgic_debug_init(struct kvm *kvm);
>  void vgic_debug_destroy(struct kvm *kvm);
>  
>  int vgic_v5_probe(const struct gic_kvm_info *info);
> +int vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, u32 irq, bool dvi);

Doing the above would keep these things private to the vgic-v5
implementation.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.



More information about the linux-arm-kernel mailing list