Thread (8 messages) flat view 8 messages, 3 authors, 9d ago

Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented

From: Fuad Tabba <fuad.tabba@linux.dev>
Date: 2026-09-07 10:29:18
Also in: kvmarm, lkml

Hi Ben,

On Mon, 7 Sept 2026 at 10:12, Ben Horgan [off-list ref] wrote:
...
quoted
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/ (local)

Thanks,

Ben

quoted
+
 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
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help