Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
From: Fuad Tabba
Date: Mon Sep 07 2026 - 06:34:52 EST
Hi Ben,
On Mon, 7 Sept 2026 at 10:12, Ben Horgan <ben.horgan@xxxxxxx> wrote:
...
> > 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);
> > +}
>
> The sashiko comments reminded me about the possibility of mismatched systems. I see two cases to
> consider here. One is if the firmware doesn't touch the MPAM system registers and leaves
> MPAM3_EL3.TRAPLOWER set to 1. In which case the user is required to add arm64.nompam to the cmdline
> as the MPAM registers can't be accessed from EL2. The second is if the f/w clears
> MPAM3_EL3.TRAPLOWER, in which case arm64.nompam can't be used without making MPAM1_EL1 etc, shared
> between guests. Perhaps for these mismatched systems we need to unconditionally enable the EL2 traps
> for the cpus that support MPAM and not advertise any support for MPAM. Furthermore, if we, before
> kvm gets involved, unconditionally enable the EL2 traps on systems where MPAM can't be enabled then
> I'm not sure that we need to distinguish system_supports_mpam_sysregs() and system_supports_mpam()
> in the kvm code. What do you think? Does that fit in with the pattern of how cpu features are
> generally handled?
This patch misses that case. check_override reads the CPU's own ID
registers, detect_ftr_has_mpam() the sanitised ones, so
finalise_el2_state clears the traps and neither cap is set.
Not advertising MPAM already happens, since a mismatch lowers the ID
field to 0. The trap default is the missing half, and I agree it
belongs in finalise_el2_state, which already computes whether
MPAM2_EL2 can be touched. KVM then needs only system_supports_mpam(),
and the cap goes.
The one host left is a forced nVHE, where test_has_mpam()'s MPAM1_EL1
read would trap before cpu_enable_mpam(). The respin makes the caps
false when is_hyp_nvhe(), so MPAM is off there rather than the boot
hanging [1].
I'll respin along those lines.
Cheers,
/fuad
[1] https://lore.kernel.org/all/apqrky27mJmUV9UA@willie-the-truck/
>
> Thanks,
>
> Ben
>
>
> > +
> > 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);
> > }
> >
> > +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();
> > +}
> > +
> > 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
>