[RFC 4/6] KVM: arm64: Emulate TLBIDIDR_EL1 with virtual domain counts

Jinqian Yang yangjinqian1 at huawei.com
Thu Oct 29 23:56:15 PDT 2026


Guest-visible TLBIDIDR_EL1 must carry the virtual domain counts
rather than the physical ones. Compute a synthesized tlbididr_val at
init time that remaps the NVIS and NVOS field values into the NIS
and NOS bit positions. Add an access handler that returns this
synthesized value on read and silently drops writes. If vTLBID is
not enabled for the VM, the handler injects an UNDEFINED exception
into the guest.

Signed-off-by: Jinqian Yang <yangjinqian1 at huawei.com>
---
 arch/arm64/kvm/arm.c      |  7 +++++++
 arch/arm64/kvm/sys_regs.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 4dfea015eaaa..ddf7c25f9ed9 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -230,6 +230,13 @@ static int kvm_arm_init_tlbidomain(struct kvm *kvm)
 	vdomain->nvis = min_t(u8, FIELD_GET(TLBIDIDR_EL1_NVIS_MASK, tlbididr),
 			      KVM_VTLBID_MAX_NVIS);
 	vdomain->num_domains = -1;
+	/*
+	 * Guest-visible TLBIDIDR: the NIS/NOS fields carry the virtual
+	 * domain counts (NVIS/NVOS), since the guest uses vDomains.
+	 */
+	vdomain->tlbididr_val = FIELD_PREP(TLBIDIDR_EL1_NIS_MASK, vdomain->nvis) |
+				FIELD_PREP(TLBIDIDR_EL1_NOS_MASK,
+					   FIELD_GET(TLBIDIDR_EL1_NVOS_MASK, tlbididr));
 	/*
 	 * The four VTLBIDn registers hold at most 4 * 64 / pdomain_bits
 	 * vDomain fields. Disable vTLBID altogether when NIS is outside
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6a025e1f3161..cf9804e54ff1 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -615,6 +615,34 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+#ifdef CONFIG_KVM_ARM_VTLBID
+static bool access_tlbididr(struct kvm_vcpu *vcpu,
+			    struct sys_reg_params *p,
+			    const struct sys_reg_desc *r)
+{
+	struct tlbidomain *vdomain = &vcpu->kvm->arch.tlbidomain;
+
+	if (p->is_write)
+		return ignore_write(vcpu, p);
+
+	if (!vdomain->guest_tlbid_enabled) {
+		kvm_inject_undefined(vcpu);
+		return false;
+	}
+
+	p->regval = vdomain->tlbididr_val;
+	return true;
+}
+#else
+static bool access_tlbididr(struct kvm_vcpu *vcpu,
+			    struct sys_reg_params *p,
+			    const struct sys_reg_desc *r)
+{
+	kvm_inject_undefined(vcpu);
+	return false;
+}
+#endif
+
 static bool access_actlr(struct kvm_vcpu *vcpu,
 			 struct sys_reg_params *p,
 			 const struct sys_reg_desc *r)
@@ -3562,6 +3590,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_LORN_EL1), trap_loregion },
 	{ SYS_DESC(SYS_LORC_EL1), trap_loregion },
 	{ SYS_DESC(SYS_MPAMIDR_EL1), undef_access },
+	{ SYS_DESC(SYS_TLBIDIDR_EL1), access_tlbididr },
 	{ SYS_DESC(SYS_LORID_EL1), trap_loregion },
 
 	{ SYS_DESC(SYS_MPAM1_EL1), undef_access },
-- 
2.33.0




More information about the linux-arm-kernel mailing list