From nobody Sun Apr 5 18:20:39 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 894AF41325C; Fri, 6 Mar 2026 17:10:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772817040; cv=none; b=pOAzE6CQfSCytzA4OX8dACitnFk0qAxXhmppgZqlCCEPOUvddxhOTQjNWGgPr2valdC0vmeB9S8xeG74JvWnIsLRysyfmQ4r/26OBiegjmPkk1taP+ymZb1LLfENfKNB0H3f5Hm1fm5fDv3u7PaUfwoh5vZH0dEVqdzZdLXn/v8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772817040; c=relaxed/simple; bh=J5EO1xviE9VFn8+583CTCxibzcT4/ZQErwlFsDHecNo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=D8QazppkRo2so5o5w7GgDRCEFG49SFhnKnkWWD7T/y4C39B1tf2dkQaUc9wgMCMiT7Bb0MzZ0/VKt6KxEkWRC5uVyr1txTcV757T/xVEpgrz1ieUj0WQFZN27s7AcLgIJRzYEV2BOSMarGxwt9sJlQJebenuJq+Cv9MBUx+viPs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=N3CYEuul; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="N3CYEuul" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5D6B4C4CEF7; Fri, 6 Mar 2026 17:10:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772817040; bh=J5EO1xviE9VFn8+583CTCxibzcT4/ZQErwlFsDHecNo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=N3CYEuulJ177qjcAiexiehYsjLv27xJdXImip3kWt1Ls0CIgth/rFiO4/E0074UlE TawWME1xl2L0dv2aCw0UKe3vMwFIJiDGvBHnkOxKUrwzEJGBXMf41eRh0RTipU5uso ZKiSuoq4wMyoYQ2/vBl8MwWElpUrgOIereqoYjsooJIWuOayFaS8tOifUJGXToyJs6 /bSIMqWScyOWV+/4jq9StHbcjQKnB/99RpzvHIzyRCrbcXJJUkvpeWW/Ya4ija3K5n JBHquZmUV6KsUuSeEbQCrd9VKgsNFIwaAnMjltAJji/z/HNONpHSnWahod+XtLuy95 /fIgG3PlGXliw== From: Mark Brown Date: Fri, 06 Mar 2026 17:01:09 +0000 Subject: [PATCH v10 17/30] KVM: arm64: Support SME identification registers for guests Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260306-kvm-arm64-sme-v10-17-43f7683a0fb7@kernel.org> References: <20260306-kvm-arm64-sme-v10-0-43f7683a0fb7@kernel.org> In-Reply-To: <20260306-kvm-arm64-sme-v10-0-43f7683a0fb7@kernel.org> To: Marc Zyngier , Joey Gouly , Catalin Marinas , Suzuki K Poulose , Will Deacon , Paolo Bonzini , Jonathan Corbet , Shuah Khan , Oliver Upton Cc: Dave Martin , Fuad Tabba , Mark Rutland , Ben Horgan , linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, Peter Maydell , Eric Auger , Mark Brown X-Mailer: b4 0.15-dev-6ac23 X-Developer-Signature: v=1; a=openpgp-sha256; l=9360; i=broonie@kernel.org; h=from:subject:message-id; bh=J5EO1xviE9VFn8+583CTCxibzcT4/ZQErwlFsDHecNo=; b=owEBbQGS/pANAwAKASTWi3JdVIfQAcsmYgBpqwo1Th/KwCxjeHYiBvJe4lfMxXEt4BCdSWywn Gls9WVy/BOJATMEAAEKAB0WIQSt5miqZ1cYtZ/in+ok1otyXVSH0AUCaasKNQAKCRAk1otyXVSH 0IEWB/9G92L2v8rdlgm6PYPvbFHf2la7m/pZwFrtfTC1OhfGoak0JPTVgs2nORdQGwyTRdMYIqf 06yOw4MF4zclyeOSUIlbf9al7pe3Ix8Qg6YrMmqB/IIe6iW3ThkpMCUJLz+asI6/e7Xx0M4rNT0 h1r89kDaD2swaL5rcKws9Q+HR2OwjFytZAGlH74Dt5EgXjfyPqx+q+Sl/l/m6j/nq1rgV5a5UI8 TeyLSItp9rTtYng4Hg2qP3aRqi+cCg9c8cL5HDwhqP/2lQAXVewi2bkdE9Ix2vsA86tNvqb9mL9 7nBETCjD1vJuUk1dG8qxzvSz6fw81BVNsu/gkCF0iznAm8ga X-Developer-Key: i=broonie@kernel.org; a=openpgp; fpr=3F2568AAC26998F9E813A1C5C3F436CA30F5D8EB The primary register for identifying SME is ID_AA64PFR1_EL1.SME. This is hidden from guests unless SME is enabled by the VMM. When it is visible it is writable and can be used to control the availability of SME2. There is also a new register ID_AA64SMFR0_EL1 which we make writable, forcing it to all bits 0 if SME is disabled. This includes the field SMEver giving the SME version, userspace is responsible for ensuring the value is consistent with ID_AA64PFR1_EL1.SME. It also includes FA64, a separately enableable extension which provides the full FPSIMD and SVE instruction set including FFR in streaming mode. Userspace can control the availability of FA64 by writing to this field. The other features enumerated there only add new instructions, there are no architectural controls for these. There is a further identification register SMIDR_EL1 which provides a basic description of the SME microarchitecture, in a manner similar to MIDR_EL1 for the PE. It also describes support for priority management and a basic affinity description for shared SME units, plus some RES0 space. We do not support priority management for guests so this is hidden from guests, along with any new fields. As for MIDR_EL1 and REVIDR_EL1 we expose the implementer and revision information to guests with the raw value from the CPU we are running on, this may present issues for asymmetric systems or for migration as it does for the existing registers. Signed-off-by: Mark Brown --- arch/arm64/include/asm/kvm_host.h | 3 ++ arch/arm64/kvm/config.c | 8 +----- arch/arm64/kvm/hyp/nvhe/pkvm.c | 4 ++- arch/arm64/kvm/sys_regs.c | 60 +++++++++++++++++++++++++++++++++++= ---- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index ec1ede0c3c12..b8f9ab8fadd4 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -397,6 +397,7 @@ struct kvm_arch { u64 revidr_el1; u64 aidr_el1; u64 ctr_el0; + u64 smidr_el1; =20 /* Masks for VNCR-backed and general EL2 sysregs */ struct kvm_sysreg_masks *sysreg_masks; @@ -1568,6 +1569,8 @@ static inline u64 *__vm_id_reg(struct kvm_arch *ka, u= 32 reg) return &ka->revidr_el1; case SYS_AIDR_EL1: return &ka->aidr_el1; + case SYS_SMIDR_EL1: + return &ka->smidr_el1; default: WARN_ON_ONCE(1); return NULL; diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index d9f553cbf9df..57df8d0c38c4 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -281,14 +281,8 @@ static bool feat_anerr(struct kvm *kvm) =20 static bool feat_sme_smps(struct kvm *kvm) { - /* - * Revists this if KVM ever supports SME -- this really should - * look at the guest's view of SMIDR_EL1. Funnily enough, this - * is not captured in the JSON file, but only as a note in the - * ARM ARM. - */ return (kvm_has_feat(kvm, FEAT_SME) && - (read_sysreg_s(SYS_SMIDR_EL1) & SMIDR_EL1_SMPS)); + (kvm_read_vm_id_reg(kvm, SYS_SMIDR_EL1) & SMIDR_EL1_SMPS)); } =20 static bool feat_spe_fds(struct kvm *kvm) diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 399968cf570e..2757833c4396 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -348,8 +348,10 @@ static void pkvm_init_features_from_host(struct pkvm_h= yp_vm *hyp_vm, const struc host_kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES); =20 - if (test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &host_arch_flags)) + if (test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &host_arch_flags)) { hyp_vm->kvm.arch.midr_el1 =3D host_kvm->arch.midr_el1; + hyp_vm->kvm.arch.smidr_el1 =3D host_kvm->arch.smidr_el1; + } =20 return; } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 66248fd48a7d..15854947de61 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1893,7 +1893,11 @@ static unsigned int id_visibility(const struct kvm_v= cpu *vcpu, =20 switch (id) { case SYS_ID_AA64ZFR0_EL1: - if (!vcpu_has_sve(vcpu)) + if (!vcpu_has_sve(vcpu) && !vcpu_has_sme(vcpu)) + return REG_RAZ; + break; + case SYS_ID_AA64SMFR0_EL1: + if (!vcpu_has_sme(vcpu)) return REG_RAZ; break; } @@ -1923,6 +1927,18 @@ static unsigned int raz_visibility(const struct kvm_= vcpu *vcpu, =20 /* cpufeature ID register access trap handlers */ =20 +static bool hidden_id_reg(struct kvm_vcpu *vcpu, + struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + switch (reg_to_encoding(r)) { + case SYS_SMIDR_EL1: + return !vcpu_has_sme(vcpu); + default: + return false; + } +} + static bool access_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) @@ -2015,7 +2031,9 @@ static u64 sanitise_id_aa64pfr1_el1(const struct kvm_= vcpu *vcpu, u64 val) SYS_FIELD_GET(ID_AA64PFR0_EL1, RAS, pfr0) =3D=3D ID_AA64PFR0_EL1_RA= S_IMP)) val &=3D ~ID_AA64PFR1_EL1_RAS_frac; =20 - val &=3D ~ID_AA64PFR1_EL1_SME; + if (!kvm_has_sme(vcpu->kvm)) + val &=3D ~ID_AA64PFR1_EL1_SME; + val &=3D ~ID_AA64PFR1_EL1_RNDR_trap; val &=3D ~ID_AA64PFR1_EL1_NMI; val &=3D ~ID_AA64PFR1_EL1_GCS; @@ -3026,6 +3044,9 @@ static bool access_imp_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { + if (hidden_id_reg(vcpu, p, r)) + return bad_trap(vcpu, p, r, "write to hidden ID register"); + if (p->is_write) return write_to_read_only(vcpu, p, r); =20 @@ -3037,8 +3058,11 @@ static bool access_imp_id_reg(struct kvm_vcpu *vcpu, return access_id_reg(vcpu, p, r); =20 /* - * Otherwise, fall back to the old behavior of returning the value of - * the current CPU. + * Otherwise, fall back to the old behavior of returning the + * value of the current CPU for REVIDR_EL1 and AIDR_EL1, or + * use whatever the sanitised reset value we have is for other + * registers not exposed prior to writability support for + * these registers. */ switch (reg_to_encoding(r)) { case SYS_REVIDR_EL1: @@ -3047,6 +3071,9 @@ static bool access_imp_id_reg(struct kvm_vcpu *vcpu, case SYS_AIDR_EL1: p->regval =3D read_sysreg(aidr_el1); break; + case SYS_SMIDR_EL1: + p->regval =3D r->val; + break; default: WARN_ON_ONCE(1); } @@ -3057,12 +3084,15 @@ static bool access_imp_id_reg(struct kvm_vcpu *vcpu, static u64 __ro_after_init boot_cpu_midr_val; static u64 __ro_after_init boot_cpu_revidr_val; static u64 __ro_after_init boot_cpu_aidr_val; +static u64 __ro_after_init boot_cpu_smidr_val; =20 static void init_imp_id_regs(void) { boot_cpu_midr_val =3D read_sysreg(midr_el1); boot_cpu_revidr_val =3D read_sysreg(revidr_el1); boot_cpu_aidr_val =3D read_sysreg(aidr_el1); + if (system_supports_sme()) + boot_cpu_smidr_val =3D read_sysreg_s(SYS_SMIDR_EL1); } =20 static u64 reset_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_de= sc *r) @@ -3074,6 +3104,8 @@ static u64 reset_imp_id_reg(struct kvm_vcpu *vcpu, co= nst struct sys_reg_desc *r) return boot_cpu_revidr_val; case SYS_AIDR_EL1: return boot_cpu_aidr_val; + case SYS_SMIDR_EL1: + return boot_cpu_smidr_val; default: KVM_BUG_ON(1, vcpu->kvm); return 0; @@ -3122,6 +3154,16 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, con= st struct sys_reg_desc *r, .val =3D mask, \ } =20 +#define IMPLEMENTATION_ID_FILTERED(reg, mask, reg_visibility) { \ + SYS_DESC(SYS_##reg), \ + .access =3D access_imp_id_reg, \ + .get_user =3D get_id_reg, \ + .set_user =3D set_imp_id_reg, \ + .reset =3D reset_imp_id_reg, \ + .visibility =3D reg_visibility, \ + .val =3D mask, \ + } + static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) { __vcpu_assign_sys_reg(vcpu, r->reg, vcpu->kvm->arch.nr_pmu_counters); @@ -3238,7 +3280,6 @@ static const struct sys_reg_desc sys_reg_descs[] =3D { ID_AA64PFR1_EL1_MTE_frac | ID_AA64PFR1_EL1_NMI | ID_AA64PFR1_EL1_RNDR_trap | - ID_AA64PFR1_EL1_SME | ID_AA64PFR1_EL1_RES0 | ID_AA64PFR1_EL1_MPAM_frac | ID_AA64PFR1_EL1_MTE)), @@ -3248,7 +3289,7 @@ static const struct sys_reg_desc sys_reg_descs[] =3D { ID_AA64PFR2_EL1_MTESTOREONLY), ID_UNALLOCATED(4,3), ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0), - ID_HIDDEN(ID_AA64SMFR0_EL1), + ID_WRITABLE(ID_AA64SMFR0_EL1, ~ID_AA64SMFR0_EL1_RES0), ID_UNALLOCATED(4,6), ID_WRITABLE(ID_AA64FPFR0_EL1, ~ID_AA64FPFR0_EL1_RES0), =20 @@ -3454,6 +3495,13 @@ static const struct sys_reg_desc sys_reg_descs[] =3D= { { SYS_DESC(SYS_CCSIDR_EL1), access_ccsidr }, { SYS_DESC(SYS_CLIDR_EL1), access_clidr, reset_clidr, CLIDR_EL1, .set_user =3D set_clidr, .val =3D ~CLIDR_EL1_RES0 }, + IMPLEMENTATION_ID_FILTERED(SMIDR_EL1, + (SMIDR_EL1_NSMC | SMIDR_EL1_HIP | + SMIDR_EL1_AFFINITY2 | + SMIDR_EL1_IMPLEMENTER | + SMIDR_EL1_REVISION | SMIDR_EL1_SH | + SMIDR_EL1_AFFINITY), + sme_visibility), IMPLEMENTATION_ID(AIDR_EL1, GENMASK_ULL(63, 0)), { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 }, ID_FILTERED(CTR_EL0, ctr_el0, --=20 2.47.3