All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marc Zyngier <maz@kernel.org>
To: kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	kvm@vger.kernel.org
Cc: Joey Gouly <joey.gouly@arm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Oliver Upton <oliver.upton@linux.dev>,
	Zenghui Yu <yuzenghui@huawei.com>,
	Christoffer Dall <christoffer.dall@arm.com>,
	Volodymyr Babchuk <Volodymyr_Babchuk@epam.com>
Subject: [PATCH 31/33] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
Date: Mon,  3 Nov 2025 16:55:15 +0000	[thread overview]
Message-ID: <20251103165517.2960148-32-maz@kernel.org> (raw)
In-Reply-To: <20251103165517.2960148-1-maz@kernel.org>

Add the plumbing of GICv2 interrupt deactivation via GICV_DIR.
This requires adding a new device so that we can easily decode
the DIR address.

The deactivation itself is very similar to the GICv3 version.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-mmio-v2.c |  24 +++++++
 arch/arm64/kvm/vgic/vgic-mmio.h    |   1 +
 arch/arm64/kvm/vgic/vgic-v2.c      | 110 +++++++++++++++++++++++++++++
 arch/arm64/kvm/vgic/vgic.h         |   1 +
 include/kvm/arm_vgic.h             |   1 +
 5 files changed, 137 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
index f25fccb1f8e63..406845b3117cf 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
@@ -359,6 +359,16 @@ static void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
 	vgic_set_vmcr(vcpu, &vmcr);
 }
 
+static void vgic_mmio_write_dir(struct kvm_vcpu *vcpu,
+				gpa_t addr, unsigned int len,
+				unsigned long val)
+{
+	if (kvm_vgic_global_state.type == VGIC_V2)
+		vgic_v2_deactivate(vcpu, val);
+	else
+		vgic_v3_deactivate(vcpu, val);
+}
+
 static unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
 					gpa_t addr, unsigned int len)
 {
@@ -482,6 +492,10 @@ static const struct vgic_register_region vgic_v2_cpu_registers[] = {
 	REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
 		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
 		VGIC_ACCESS_32bit),
+	REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_CPU_DEACTIVATE,
+		vgic_mmio_read_raz, vgic_mmio_write_dir,
+		vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi,
+		4, VGIC_ACCESS_32bit),
 };
 
 unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
@@ -494,6 +508,16 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
 	return SZ_4K;
 }
 
+unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev)
+{
+	dev->regions = vgic_v2_cpu_registers;
+	dev->nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
+
+	kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
+
+	return KVM_VGIC_V2_CPU_SIZE;
+}
+
 int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
 {
 	const struct vgic_register_region *region;
diff --git a/arch/arm64/kvm/vgic/vgic-mmio.h b/arch/arm64/kvm/vgic/vgic-mmio.h
index 5b490a4dfa5e9..50dc80220b0f3 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio.h
+++ b/arch/arm64/kvm/vgic/vgic-mmio.h
@@ -213,6 +213,7 @@ void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid,
 				    const u32 val);
 
 unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
+unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev);
 
 unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
 
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 0159da5a94412..5ab29c6755ade 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -9,6 +9,7 @@
 #include <kvm/arm_vgic.h>
 #include <asm/kvm_mmu.h>
 
+#include "vgic-mmio.h"
 #include "vgic.h"
 
 static inline void vgic_v2_write_lr(int lr, u32 val)
@@ -147,6 +148,104 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 	cpuif->used_lrs = 0;
 }
 
+void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
+	struct kvm_vcpu *target_vcpu = NULL;
+	bool mmio = false;
+	struct vgic_irq *irq;
+	unsigned long flags;
+	u64 lr = 0;
+	u8 cpuid;
+
+	/* Snapshot CPUID, and remove it from the INTID */
+	cpuid = FIELD_GET(GENMASK_ULL(12, 10), val);
+	val &= ~GENMASK_ULL(12, 10);
+
+	/* We only deal with DIR when EOIMode==1 */
+	if (!(cpuif->vgic_vmcr & GICH_VMCR_EOI_MODE_MASK))
+		return;
+
+	/* Make sure we're in the same context as LR handling */
+	local_irq_save(flags);
+
+	irq = vgic_get_vcpu_irq(vcpu, val);
+	if (WARN_ON_ONCE(!irq))
+		goto out;
+
+	/*
+	 * Four possibities:
+	 *
+	 * - The irq is not queued on any CPU, and there is nothing to
+	 *   do,
+	 *
+	 * - Or the irq is in an LR, meaning that its state is not
+	 *   directly observable. Treat it bluntly by making it as if
+	 *   this was a write to GICD_ICACTIVER, which will force an
+	 *   exit on all vcpus. If it hurts, don't do that.
+	 *
+	 * - Or the irq is active, but not in an LR, and we can
+	 *   directly deactivate it by building a pseudo-LR, fold it,
+	 *   and queue a request to prune the resulting ap_list,
+	 *
+	 * - Or the irq is not active, and there is nothing to do.
+	 *
+	 * Special care must be taken to match the source CPUID when
+	 * deactivating a GICv2 SGI.
+	 */
+	scoped_guard(raw_spinlock, &irq->irq_lock) {
+		target_vcpu = vgic_target_oracle(irq);
+
+		/* Not on any ap_list? */
+		if (!target_vcpu)
+			goto put;
+
+		/*
+		 * Urgh. We're deactivating something that we cannot
+		 * observe yet... Big hammer time.
+		 */
+		if (irq->on_lr) {
+			mmio = true;
+			goto put;
+		}
+
+		/* Not active: relax!  */
+		if (!irq->active) {
+			target_vcpu = NULL;
+			goto put;
+		}
+
+		/* SGI: check that the cpuid matches */
+		if (val < VGIC_NR_SGIS && irq->active_source != cpuid) {
+			target_vcpu = NULL;
+			goto put;
+		}
+
+		/* (with a Dalek voice) DEACTIVATE!!!! */
+		lr = vgic_v2_compute_lr(vcpu, irq) & ~GICH_LR_ACTIVE_BIT;
+	}
+
+	if (lr & GICH_LR_HW)
+		writel_relaxed(FIELD_GET(GICH_LR_PHYSID_CPUID, lr),
+			       kvm_vgic_global_state.gicc_base + GIC_CPU_DEACTIVATE);
+
+	vgic_v2_fold_lr(vcpu, lr);
+
+put:
+	vgic_put_irq(vcpu->kvm, irq);
+
+out:
+	local_irq_restore(flags);
+
+	if (mmio)
+		vgic_mmio_write_cactive(vcpu, val / 32, 4, BIT(val % 32));
+
+	/* Force the ap_list to be pruned */
+	if (target_vcpu)
+		kvm_make_request(KVM_REQ_VGIC_PROCESS_UPDATE, target_vcpu);
+}
+
 static u32 vgic_v2_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
 {
 	u32 val = irq->intid;
@@ -349,6 +448,7 @@ static bool vgic_v2_check_base(gpa_t dist_base, gpa_t cpu_base)
 int vgic_v2_map_resources(struct kvm *kvm)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
+	unsigned int len;
 	int ret = 0;
 
 	if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
@@ -372,6 +472,16 @@ int vgic_v2_map_resources(struct kvm *kvm)
 		return ret;
 	}
 
+	len = vgic_v2_init_cpuif_iodev(&dist->cpuif_iodev);
+	dist->cpuif_iodev.base_addr = dist->vgic_cpu_base;
+	dist->cpuif_iodev.iodev_type = IODEV_CPUIF;
+	dist->cpuif_iodev.redist_vcpu = NULL;
+
+	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist->vgic_cpu_base,
+				      len, &dist->cpuif_iodev.dev);
+	if (ret)
+		return ret;
+
 	if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
 		ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
 					    kvm_vgic_global_state.vcpu_base,
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 39589e1fbbb55..cf41864736204 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -261,6 +261,7 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
 
 void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
+void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val);
 void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
 void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 3c528c74bc6bf..caa72a92cb5d9 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -284,6 +284,7 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
+	struct vgic_io_device	cpuif_iodev;
 
 	bool			has_its;
 	bool			table_write_in_progress;
-- 
2.47.3


  parent reply	other threads:[~2025-11-03 16:55 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-03 16:54 [PATCH 00/33] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
2025-11-03 16:54 ` [PATCH 01/33] irqchip/gic: Add missing GICH_HCR control bits Marc Zyngier
2025-11-03 16:54 ` [PATCH 02/33] irqchip/gic: Expose CPU interface VA to KVM Marc Zyngier
2025-11-03 16:54 ` [PATCH 03/33] irqchip/apple-aic: Spit out ICH_MIDR_EL2 value on spurious vGIC MI Marc Zyngier
2025-11-04 11:13   ` Zenghui Yu
2025-11-03 16:54 ` [PATCH 04/33] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
2025-11-03 16:54 ` [PATCH 05/33] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping Marc Zyngier
2025-11-04  8:50   ` Yao Yuan
2025-11-04  9:04     ` Marc Zyngier
2025-11-04  9:40       ` Yao Yuan
2025-11-05  2:01   ` kernel test robot
2025-11-05 11:31     ` Marc Zyngier
2025-11-03 16:54 ` [PATCH 06/33] KVM: arm64: Repack struct vgic_irq fields Marc Zyngier
2025-11-03 16:54 ` [PATCH 07/33] KVM: arm64: Add tracking of vgic_irq being present in a LR Marc Zyngier
2025-11-03 16:54 ` [PATCH 08/33] KVM: arm64: Add LR overflow handling documentation Marc Zyngier
2025-11-03 16:54 ` [PATCH 09/33] KVM: arm64: GICv3: Drop LPI active state when folding LRs Marc Zyngier
2025-11-03 16:54 ` [PATCH 10/33] KVM: arm64: GICv3: Preserve EOIcount on exit Marc Zyngier
2025-11-03 16:54 ` [PATCH 11/33] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs Marc Zyngier
2025-11-03 16:54 ` [PATCH 12/33] KVM: arm64: GICv3: Extract LR folding primitive Marc Zyngier
2025-11-03 16:54 ` [PATCH 13/33] KVM: arm64: GICv3: Extract LR computing primitive Marc Zyngier
2025-11-03 16:54 ` [PATCH 14/33] KVM: arm64: GICv2: Preserve EOIcount on exit Marc Zyngier
2025-11-03 16:54 ` [PATCH 15/33] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded Marc Zyngier
2025-11-03 16:55 ` [PATCH 16/33] KVM: arm64: GICv2: Extract LR folding primitive Marc Zyngier
2025-11-03 16:55 ` [PATCH 17/33] KVM: arm64: GICv2: Extract LR computing primitive Marc Zyngier
2025-11-03 16:55 ` [PATCH 18/33] KVM: arm64: Compute vgic state irrespective of the number of interrupts Marc Zyngier
2025-11-03 16:55 ` [PATCH 19/33] KVM: arm64: Eagerly save VMCR on exit Marc Zyngier
2025-11-03 16:55 ` [PATCH 20/33] KVM: arm64: Revamp vgic maintenance interrupt configuration Marc Zyngier
2025-11-03 16:55 ` [PATCH 21/33] KVM: arm64: Make vgic_target_oracle() globally available Marc Zyngier
2025-11-03 16:55 ` [PATCH 22/33] KVM: arm64: Invert ap_list sorting to push active interrupts out Marc Zyngier
2025-11-03 16:55 ` [PATCH 23/33] KVM: arm64: Move undeliverable interrupts to the end of ap_list Marc Zyngier
2025-11-03 16:55 ` [PATCH 24/33] KVM: arm64: Use MI to detect groups being enabled/disabled Marc Zyngier
2025-11-03 16:55 ` [PATCH 25/33] KVM: arm64: Add AP-list overflow split/splice Marc Zyngier
2025-11-03 16:55 ` [PATCH 26/33] KVM: arm64: GICv3: Handle LR overflow when EOImode==0 Marc Zyngier
2025-11-03 16:55 ` [PATCH 27/33] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps Marc Zyngier
2025-11-03 16:55 ` [PATCH 28/33] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive Marc Zyngier
2025-11-03 16:55 ` [PATCH 29/33] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity Marc Zyngier
2025-11-03 16:55 ` [PATCH 30/33] KVM: arm64: GICv2: Handle LR overflow when EOImode==0 Marc Zyngier
2025-11-03 16:55 ` Marc Zyngier [this message]
2025-11-03 16:55 ` [PATCH 32/33] KVM: arm64: GICv2: Always trap GICV_DIR register Marc Zyngier
2025-11-03 16:55 ` [PATCH 33/33] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation Marc Zyngier

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=20251103165517.2960148-32-maz@kernel.org \
    --to=maz@kernel.org \
    --cc=Volodymyr_Babchuk@epam.com \
    --cc=christoffer.dall@arm.com \
    --cc=joey.gouly@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=oliver.upton@linux.dev \
    --cc=suzuki.poulose@arm.com \
    --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.