Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
From: Yao Yuan
Date: Fri Sep 04 2026 - 06:27:28 EST
On Thu, Sep 03, 2026 at 05:08:19PM +0800, Fuad Tabba wrote:
> finalise_el2_state() clears the EL2 MPAM traps whenever the ID registers
> advertise MPAM, but KVM sets them only when ARM64_MPAM is set, which
> also requires MPAMEN. Without EL3 the enable is EL2's own and nothing
> sets it, so the cap stays off and a guest reaches the MPAM registers
> while ID_AA64PFR0_EL1.MPAM reads 0 for it.
>
> Gate the traps on the ID registers alone. MPAMEN is not a term in any
> MPAM accessor, so they take effect without it. finalise_el2_state
> already wrote MPAM2_EL2 under the same condition, so MPAM3_EL3.TRAPLOWER
> is clear wherever the cap is set, and arm64.nompam still clears it.
>
> Fixes: 31ff96c38ea3 ("KVM: arm64: Fix missing traps of guest accesses to the MPAM registers")
> Signed-off-by: Fuad Tabba <fuad.tabba@xxxxxxxxx>
> ---
> Found this while working on the other MPAM thread [1].
>
> [1] https://lore.kernel.org/all/CA+EHjTxeWxZiuSmnKLGLxTBXP4oJT7-LuffbPAyCSZZ5TW=5Ew@xxxxxxxxxxxxxx/
>
> arch/arm64/include/asm/cpufeature.h | 5 +++++
> arch/arm64/kernel/cpufeature.c | 13 +++++++++++++
> arch/arm64/kvm/hyp/include/hyp/switch.h | 4 ++--
> arch/arm64/tools/cpucaps | 1 +
> 4 files changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 7404a6e83a930..8863ae99596bc 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -873,6 +873,11 @@ static __always_inline bool system_supports_mpam_hcr(void)
> return alternative_has_cap_unlikely(ARM64_MPAM_HCR);
> }
>
> +static __always_inline bool system_supports_mpam_sysregs(void)
> +{
> + return alternative_has_cap_unlikely(ARM64_MPAM_SYSREGS);
> +}
> +
> static inline bool system_supports_pmuv3(void)
> {
> return cpus_have_final_cap(ARM64_HAS_PMUV3);
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 17b83a2518a8f..36a27692e5cf7 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -2501,6 +2501,13 @@ test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope)
> return (read_sysreg_s(SYS_MPAM1_EL1) & MPAM1_EL1_MPAMEN);
> }
>
Hi Tabba,
> +static bool
> +test_has_mpam_sysregs(const struct arm64_cpu_capabilities *entry, int __unused)
> +{
> + /* The registers exist whether or not firmware enabled MPAM. */
> + return detect_ftr_has_mpam();
> +}
My understanding: arm64.nompam affects detect_ftr_has_mpam(),
thus when arm64.nompam = 1 w/ MPAM is supported in hardware,
the KVM's trap setting is skipped yet, and it's possible that
SYS_MPAM2_EL2 and SYS_MPAMHCR_EL2 are configured not trap anything
by firmware, thus guest can still access MPAM registers.
Do we need check the raw id register values for the real support
state of MPAM here ?
> +
> static void
> cpu_enable_mpam(const struct arm64_cpu_capabilities *entry)
> {
> @@ -3116,6 +3123,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
> .matches = test_has_mpam,
> .cpu_enable = cpu_enable_mpam,
> },
> + {
> + .desc = "Memory Partitioning And Monitoring system registers",
> + .type = ARM64_CPUCAP_SYSTEM_FEATURE,
> + .capability = ARM64_MPAM_SYSREGS,
> + .matches = test_has_mpam_sysregs,
> + },
> {
> .desc = "Memory Partitioning And Monitoring Virtualisation",
> .type = ARM64_CPUCAP_SYSTEM_FEATURE,
> diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
> index 1ce7130e25490..8941335724f6b 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/switch.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
> @@ -298,7 +298,7 @@ static inline void __activate_traps_mpam(struct kvm_vcpu *vcpu)
> u64 clr = MPAM2_EL2_EnMPAMSM;
> u64 set = MPAM2_EL2_TRAPMPAM0EL1 | MPAM2_EL2_TRAPMPAM1EL1;
>
> - if (!system_supports_mpam())
> + if (!system_supports_mpam_sysregs())
> return;
>
> /* trap guest access to MPAMIDR_EL1 */
> @@ -317,7 +317,7 @@ static inline void __deactivate_traps_mpam(void)
> u64 clr = MPAM2_EL2_TRAPMPAM0EL1 | MPAM2_EL2_TRAPMPAM1EL1 | MPAM2_EL2_TIDR;
> u64 set = MPAM2_EL2_EnMPAMSM;
>
> - if (!system_supports_mpam())
> + if (!system_supports_mpam_sysregs())
> return;
>
> sysreg_clear_set_s(SYS_MPAM2_EL2, clr, set);
> diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
> index 2775ba3359cfe..aa5be51385f68 100644
> --- a/arch/arm64/tools/cpucaps
> +++ b/arch/arm64/tools/cpucaps
> @@ -78,6 +78,7 @@ KVM_PROTECTED_MODE
> MISMATCHED_CACHE_TYPE
> MPAM
> MPAM_HCR
> +MPAM_SYSREGS
> MTE
> MTE_ASYMM
> MTE_FAR
>
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> --
> 2.39.5
>