* [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
@ 2026-09-03 16:08 Fuad Tabba
2026-09-04 9:58 ` Yao Yuan
2026-09-07 9:12 ` Ben Horgan
0 siblings, 2 replies; 8+ messages in thread
From: Fuad Tabba @ 2026-09-03 16:08 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon
Cc: James Morse, Ben Horgan, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
Fuad Tabba, linux-arm-kernel, kvmarm, linux-kernel
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@linux.dev>
---
Found this while working on the other MPAM thread [1].
[1] https://lore.kernel.org/all/CA+EHjTxeWxZiuSmnKLGLxTBXP4oJT7-LuffbPAyCSZZ5TW=5Ew@mail.gmail.com/
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);
}
+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
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-03 16:08 [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented Fuad Tabba
@ 2026-09-04 9:58 ` Yao Yuan
2026-09-04 11:18 ` Fuad Tabba
2026-09-07 9:12 ` Ben Horgan
1 sibling, 1 reply; 8+ messages in thread
From: Yao Yuan @ 2026-09-04 9:58 UTC (permalink / raw)
To: Fuad Tabba
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Ben Horgan, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
Fuad Tabba, linux-arm-kernel, kvmarm, linux-kernel
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@linux.dev>
> ---
> Found this while working on the other MPAM thread [1].
>
> [1] https://lore.kernel.org/all/CA+EHjTxeWxZiuSmnKLGLxTBXP4oJT7-LuffbPAyCSZZ5TW=5Ew@mail.gmail.com/
>
> 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
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-04 9:58 ` Yao Yuan
@ 2026-09-04 11:18 ` Fuad Tabba
2026-09-04 23:07 ` Yao Yuan
0 siblings, 1 reply; 8+ messages in thread
From: Fuad Tabba @ 2026-09-04 11:18 UTC (permalink / raw)
To: Yao Yuan
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Ben Horgan, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
linux-arm-kernel, kvmarm, linux-kernel
Hi Yuan,
> > +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 ?
Under arm64.nompam, finalise_el2_state skips the MPAM2_EL2 and
MPAMHCR_EL2 writes too, via the same check_override, so the kernel
does not clear the traps either. What a guest reaches there is
whatever EL3 left, UNKNOWN when EL3 is implemented. This patch is for
the case where the kernel cleared the traps itself and never set them
again.
Gating on the raw ID registers would make __activate_traps_mpam()
write MPAM2_EL2 on guest entry there. AFAICT that traps to EL3
wherever MPAM3_EL3.TRAPLOWER is still set, which is the firmware the
option is for: 10f885d63a0e ("arm64: Add override for MPAM") added it
for firmware that leaves the trap set and does not emulate it.
The arm64.nompam hazard is documented separately [1].
Cheers,
/fuad
[1] https://lore.kernel.org/all/20260903084809.2027326-1-fuad.tabba@linux.dev/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-04 11:18 ` Fuad Tabba
@ 2026-09-04 23:07 ` Yao Yuan
0 siblings, 0 replies; 8+ messages in thread
From: Yao Yuan @ 2026-09-04 23:07 UTC (permalink / raw)
To: Fuad Tabba
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Ben Horgan, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
linux-arm-kernel, kvmarm, linux-kernel
On Fri, Sep 04, 2026 at 12:18:00PM +0800, Fuad Tabba wrote:
> Hi Yuan,
>
> > > +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 ?
>
> Under arm64.nompam, finalise_el2_state skips the MPAM2_EL2 and
> MPAMHCR_EL2 writes too, via the same check_override, so the kernel
> does not clear the traps either. What a guest reaches there is
> whatever EL3 left, UNKNOWN when EL3 is implemented.
That's what I was worried before w/ nompam = 1 for guest can still access
the MPAM registers. Now it's fine to me w/ limitation on usage of nompam
described in [1], the limitation on guest behavior described there yet.
Thanks for the reply! I just not aware [1] before.
> This patch is for the case where the kernel cleared the traps itself and
> never set them again.
Yes next time I will explicitly say want to discuss something may
related to the patch's main purpose.
>
> Gating on the raw ID registers would make __activate_traps_mpam()
> write MPAM2_EL2 on guest entry there. AFAICT that traps to EL3
> wherever MPAM3_EL3.TRAPLOWER is still set, which is the firmware the
> option is for: 10f885d63a0e ("arm64: Add override for MPAM") added it
> for firmware that leaves the trap set and does not emulate it.
Make sense, it break the nompam actually on platform need it,
this isn't good idea.
For the purpose of this patch:
Reviewed-by: Yuan Yao <yaoyuan@linux.alibaba.com>
>
> The arm64.nompam hazard is documented separately [1].
>
> Cheers,
> /fuad
>
> [1] https://lore.kernel.org/all/20260903084809.2027326-1-fuad.tabba@linux.dev/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-03 16:08 [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented Fuad Tabba
2026-09-04 9:58 ` Yao Yuan
@ 2026-09-07 9:12 ` Ben Horgan
2026-09-07 10:28 ` Fuad Tabba
1 sibling, 1 reply; 8+ messages in thread
From: Ben Horgan @ 2026-09-07 9:12 UTC (permalink / raw)
To: Fuad Tabba, Marc Zyngier, Oliver Upton, Catalin Marinas,
Will Deacon
Cc: James Morse, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
Fuad Tabba, linux-arm-kernel, kvmarm, linux-kernel
Hi Fuad,
On 03/09/2026 17:08, 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.
Good spot.
>
> 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@linux.dev>
> ---
> Found this while working on the other MPAM thread [1].
>
> [1] https://lore.kernel.org/all/CA+EHjTxeWxZiuSmnKLGLxTBXP4oJT7-LuffbPAyCSZZ5TW=5Ew@mail.gmail.com/
>
> 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);
> +}
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?
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
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-07 9:12 ` Ben Horgan
@ 2026-09-07 10:28 ` Fuad Tabba
2026-09-07 15:00 ` Fuad Tabba
0 siblings, 1 reply; 8+ messages in thread
From: Fuad Tabba @ 2026-09-07 10:28 UTC (permalink / raw)
To: Ben Horgan
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
linux-arm-kernel, kvmarm, linux-kernel
Hi Ben,
On Mon, 7 Sept 2026 at 10:12, Ben Horgan <ben.horgan@arm.com> 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
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-07 10:28 ` Fuad Tabba
@ 2026-09-07 15:00 ` Fuad Tabba
2026-09-08 13:15 ` Ben Horgan
0 siblings, 1 reply; 8+ messages in thread
From: Fuad Tabba @ 2026-09-07 15:00 UTC (permalink / raw)
To: Ben Horgan
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
linux-arm-kernel, kvmarm, linux-kernel
Hi Ben,
I built the respin first, and it didn't hold up.
On Mon, 7 Sept 2026 at 11:28, Fuad Tabba <fuad.tabba@linux.dev> wrote:
...
> 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.
finalise_el2_state can't set that default. Under nVHE and hVHE the
host is the trapped party, and cpufeature reads MPAMIDR_EL1 and
MPAM1_EL1 from EL1 before KVM exists, so the only way out is to stop
the host detecting MPAM.
> 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].
is_hyp_nvhe() is true on an hVHE host too, since
is_kernel_in_hyp_mode() reads CurrentEL. So that turns MPAM off for
every pKVM host on VHE hardware, not for a forced-nVHE corner. On the
AEM FVP with MPAM, a host booted kvm-arm.mode=protected detects MPAM
without the change and not with it.
> I'll respin along those lines.
Not along those lines :) I think that v2 would be v1 with a clearer
commit message, keeping both predicates. Merging them would give
resctrl the wrong answer, since it needs firmware to have enabled MPAM
where KVM's trapping needs only that the registers are implemented.
The mismatched machine stays uncovered, with this patch at least.
Closing it means deciding per CPU at EL2 from the CPU's own registers
rather than from a system-wide cap.
Cheers,
/fuad
> 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
> >
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
2026-09-07 15:00 ` Fuad Tabba
@ 2026-09-08 13:15 ` Ben Horgan
0 siblings, 0 replies; 8+ messages in thread
From: Ben Horgan @ 2026-09-08 13:15 UTC (permalink / raw)
To: Fuad Tabba
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
James Morse, Xi Ruoyao, Mark Rutland, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Steffen Eiden, Gavin Shan,
linux-arm-kernel, kvmarm, linux-kernel
Hi Fuad,
On 07/09/2026 16:00, Fuad Tabba wrote:
> Hi Ben,
>
> I built the respin first, and it didn't hold up.
>
> On Mon, 7 Sept 2026 at 11:28, Fuad Tabba <fuad.tabba@linux.dev> wrote:
> ...
>> 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.
>
> finalise_el2_state can't set that default. Under nVHE and hVHE the
> host is the trapped party, and cpufeature reads MPAMIDR_EL1 and
> MPAM1_EL1 from EL1 before KVM exists, so the only way out is to stop
> the host detecting MPAM.
>
>> 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].
>
> is_hyp_nvhe() is true on an hVHE host too, since
> is_kernel_in_hyp_mode() reads CurrentEL. So that turns MPAM off for
> every pKVM host on VHE hardware, not for a forced-nVHE corner. On the
> AEM FVP with MPAM, a host booted kvm-arm.mode=protected detects MPAM
> without the change and not with it.
>
>> I'll respin along those lines.
>
> Not along those lines :) I think that v2 would be v1 with a clearer
> commit message, keeping both predicates. Merging them would give
> resctrl the wrong answer, since it needs firmware to have enabled MPAM
> where KVM's trapping needs only that the registers are implemented.
>
> The mismatched machine stays uncovered, with this patch at least.
> Closing it means deciding per CPU at EL2 from the CPU's own registers
> rather than from a system-wide cap.
Ok, there seems no way to properly handle mismatched systems anyway as CPUs can come online at any
time. I don't know of any mismatched MPAM systems and they would be strange as the interface to the
interconnect would on the CPUS that support MPAM require providing PARTID and PMG and the others not.
Thanks,
Ben
>
> Cheers,
> /fuad
>
>> 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
>>>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-08 13:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 16:08 [PATCH] KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented Fuad Tabba
2026-09-04 9:58 ` Yao Yuan
2026-09-04 11:18 ` Fuad Tabba
2026-09-04 23:07 ` Yao Yuan
2026-09-07 9:12 ` Ben Horgan
2026-09-07 10:28 ` Fuad Tabba
2026-09-07 15:00 ` Fuad Tabba
2026-09-08 13:15 ` Ben Horgan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox