[PATCH v2 1/4] KVM: arm64: Allow userspace to write GICD_TYPER.num_LPIs

Zhou Wang wangzhou1 at hisilicon.com
Sun Aug 24 19:39:51 PDT 2025


Allow userspace to write GICD_TYPER.num_LPIs. If GICD_TYPER.num_LPIs is
0, number of LPIs is indicated by GICD_TYPER.IDbits, so the default
value of GICD_TYPER.num_LPIs is still set as 0.

Signed-off-by: Zhou Wang <wangzhou1 at hisilicon.com>
---
 arch/arm64/kvm/vgic/vgic-init.c       |  2 ++
 arch/arm64/kvm/vgic/vgic-its.c        |  9 +++++++--
 arch/arm64/kvm/vgic/vgic-kvm-device.c |  1 +
 arch/arm64/kvm/vgic/vgic-mmio-v3.c    | 16 ++++++++++++++++
 include/kvm/arm_vgic.h                |  1 +
 include/linux/irqchip/arm-gic-v3.h    |  1 +
 6 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 1e680ad6e863..46468cb97536 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -389,6 +389,7 @@ int vgic_init(struct kvm *kvm)
 	/* freeze the number of spis */
 	if (!dist->nr_spis)
 		dist->nr_spis = VGIC_NR_IRQS_LEGACY - VGIC_NR_PRIVATE_IRQS;
+	dist->nr_lpis = 0;
 
 	ret = kvm_vgic_dist_init(kvm, dist->nr_spis);
 	if (ret)
@@ -428,6 +429,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
 	kfree(dist->spis);
 	dist->spis = NULL;
 	dist->nr_spis = 0;
+	dist->nr_lpis = 0;
 	dist->vgic_dist_base = VGIC_ADDR_UNDEF;
 
 	if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 7368c13f16b7..d8ee617cfa6f 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -377,9 +377,14 @@ static void update_affinity_collection(struct kvm *kvm, struct vgic_its *its,
 	}
 }
 
-static u32 max_lpis_propbaser(u64 propbaser)
+static u32 max_lpis_propbaser(struct vgic_dist *dist)
 {
+	u64 propbaser = dist->propbaser;
 	int nr_idbits = (propbaser & 0x1f) + 1;
+	int nr_lpis = dist->nr_lpis;
+
+	if (nr_lpis)
+		return min(8192 + nr_lpis, 1 << nr_idbits);
 
 	return 1U << min(nr_idbits, INTERRUPT_ID_BITS_ITS);
 }
@@ -1047,7 +1052,7 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
 	else
 		lpi_nr = event_id;
 	if (lpi_nr < GIC_LPI_OFFSET ||
-	    lpi_nr >= max_lpis_propbaser(kvm->arch.vgic.propbaser))
+	    lpi_nr >= max_lpis_propbaser(&kvm->arch.vgic))
 		return E_ITS_MAPTI_PHYSICALID_OOR;
 
 	/* If there is an existing mapping, behavior is UNPREDICTABLE. */
diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c
index 3d1a776b716d..f4268b60c4dd 100644
--- a/arch/arm64/kvm/vgic/vgic-kvm-device.c
+++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c
@@ -514,6 +514,7 @@ static bool reg_allowed_pre_init(struct kvm_device_attr *attr)
 		return false;
 
 	switch (attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK) {
+	case GICD_TYPER:
 	case GICD_IIDR:
 	case GICD_TYPER2:
 		return true;
diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
index a3ef185209e9..3a53b63f7b20 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
@@ -90,6 +90,8 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
 		if (vgic_has_its(vcpu->kvm)) {
 			value |= (INTERRUPT_ID_BITS_ITS - 1) << 19;
 			value |= GICD_TYPER_LPIS;
+			if (vgic->nr_lpis)
+				value |= (ilog2(vgic->nr_lpis) - 1) << 11;
 		} else {
 			value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
 		}
@@ -167,6 +169,20 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
 	u32 reg;
 
 	switch (addr & 0x0c) {
+	case GICD_TYPER:
+		reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
+
+		if (reg == val)
+			return 0;
+		if (vgic_initialized(vcpu->kvm))
+			return -EBUSY;
+		if ((reg ^ val) & ~GICD_TYPER_NUM_LPIS_MASK)
+			return -EINVAL;
+		if (GICD_TYPER_NUM_LPIS(val) > INTERRUPT_ID_BITS_ITS)
+			return -EINVAL;
+
+		dist->nr_lpis = 2 ^ GICD_TYPER_NUM_LPIS(val);
+		return 0;
 	case GICD_TYPER2:
 		reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
 
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 404883c7af6e..774bfd008230 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -303,6 +303,7 @@ struct vgic_dist {
 	 * else.
 	 */
 	struct its_vm		its_vm;
+	int			nr_lpis;
 };
 
 struct vgic_v2_cpu_if {
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 70c0948f978e..517d9f2edc44 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -85,6 +85,7 @@
 #define GICD_TYPER_ESPI			(1U << 8)
 
 #define GICD_TYPER_ID_BITS(typer)	((((typer) >> 19) & 0x1f) + 1)
+#define GICD_TYPER_NUM_LPIS_MASK	GENMASK(15, 11)
 #define GICD_TYPER_NUM_LPIS(typer)	((((typer) >> 11) & 0x1f) + 1)
 #define GICD_TYPER_SPIS(typer)		((((typer) & 0x1f) + 1) * 32)
 #define GICD_TYPER_ESPIS(typer)						\
-- 
2.33.0




More information about the linux-arm-kernel mailing list